├── zklib
├── logs
│ └── .gitignore
├── docs
│ └── ZK_Communication_protocol_manual_CMD.pdf
├── vendor
│ ├── autoload.php
│ └── composer
│ │ ├── autoload_classmap.php
│ │ ├── autoload_namespaces.php
│ │ ├── autoload_psr4.php
│ │ ├── autoload_real.php
│ │ └── ClassLoader.php
├── src
│ ├── Os.php
│ ├── Ssr.php
│ ├── Version.php
│ ├── Pin.php
│ ├── Face.php
│ ├── WorkCode.php
│ ├── SerialNumber.php
│ ├── Platform.php
│ ├── Time.php
│ ├── Device.php
│ ├── Attendance.php
│ ├── Connect.php
│ ├── Fingerprint.php
│ ├── User.php
│ └── Util.php
└── ZKLib.php
├── README.md
├── example.php
└── LICENSE
/zklib/logs/.gitignore:
--------------------------------------------------------------------------------
1 | /*
2 | !/.gitignore
3 |
--------------------------------------------------------------------------------
/zklib/docs/ZK_Communication_protocol_manual_CMD.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vodvud/php_zklib/HEAD/zklib/docs/ZK_Communication_protocol_manual_CMD.pdf
--------------------------------------------------------------------------------
/zklib/vendor/autoload.php:
--------------------------------------------------------------------------------
1 | array($baseDir . '/src'),
10 | );
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### *** Note: Abandoned library ***
2 |
3 | # PHP ZKLib #
4 |
5 | PHP library to interacts with ZK Time and Attendance Devices.
6 |
7 | Library for connecting under the network using the UDP protocol and port 4370
8 |
9 | See ZK communication protocol manual [here](zklib/docs/ZK_Communication_protocol_manual_CMD.pdf)
10 |
--------------------------------------------------------------------------------
/zklib/src/Os.php:
--------------------------------------------------------------------------------
1 | _section = __METHOD__;
16 |
17 | $command = Util::CMD_DEVICE;
18 | $command_string = '~OS';
19 |
20 | return $self->_command($command, $command_string);
21 | }
22 | }
--------------------------------------------------------------------------------
/zklib/src/Ssr.php:
--------------------------------------------------------------------------------
1 | _section = __METHOD__;
16 |
17 | $command = Util::CMD_DEVICE;
18 | $command_string = '~SSR';
19 |
20 | return $self->_command($command, $command_string);
21 | }
22 | }
--------------------------------------------------------------------------------
/zklib/src/Version.php:
--------------------------------------------------------------------------------
1 | _section = __METHOD__;
16 |
17 | $command = Util::CMD_VERSION;
18 | $command_string = '';
19 |
20 | return $self->_command($command, $command_string);
21 | }
22 | }
--------------------------------------------------------------------------------
/zklib/src/Pin.php:
--------------------------------------------------------------------------------
1 | _section = __METHOD__;
16 |
17 | $command = Util::CMD_DEVICE;
18 | $command_string = '~PIN2Width';
19 |
20 | return $self->_command($command, $command_string);
21 | }
22 | }
--------------------------------------------------------------------------------
/zklib/src/Face.php:
--------------------------------------------------------------------------------
1 | _section = __METHOD__;
16 |
17 | $command = Util::CMD_DEVICE;
18 | $command_string = 'FaceFunOn';
19 |
20 | return $self->_command($command, $command_string);
21 | }
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/zklib/src/WorkCode.php:
--------------------------------------------------------------------------------
1 | _section = __METHOD__;
16 |
17 | $command = Util::CMD_DEVICE;
18 | $command_string = 'WorkCode';
19 |
20 | return $self->_command($command, $command_string);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/zklib/src/SerialNumber.php:
--------------------------------------------------------------------------------
1 | _section = __METHOD__;
16 |
17 | $command = Util::CMD_DEVICE;
18 | $command_string = '~SerialNumber';
19 |
20 | return $self->_command($command, $command_string);
21 | }
22 | }
--------------------------------------------------------------------------------
/zklib/src/Platform.php:
--------------------------------------------------------------------------------
1 | _section = __METHOD__;
16 |
17 | $command = Util::CMD_DEVICE;
18 | $command_string = '~Platform';
19 |
20 | return $self->_command($command, $command_string);
21 | }
22 |
23 | /**
24 | * @param ZKLib $self
25 | * @return bool|mixed
26 | */
27 | public function getVersion(ZKLib $self)
28 | {
29 | $self->_section = __METHOD__;
30 |
31 | $command = Util::CMD_DEVICE;
32 | $command_string = '~ZKFPVersion';
33 |
34 | return $self->_command($command, $command_string);
35 | }
36 | }
--------------------------------------------------------------------------------
/zklib/src/Time.php:
--------------------------------------------------------------------------------
1 | _section = __METHOD__;
17 |
18 | $command = Util::CMD_SET_TIME;
19 | $command_string = pack('I', Util::encodeTime($t));
20 |
21 | return $self->_command($command, $command_string);
22 | }
23 |
24 | /**
25 | * @param ZKLib $self
26 | * @return bool|mixed
27 | */
28 | public function get(ZKLib $self)
29 | {
30 | $self->_section = __METHOD__;
31 |
32 | $command = Util::CMD_GET_TIME;
33 | $command_string = '';
34 |
35 | $ret = $self->_command($command, $command_string);
36 |
37 | if ($ret) {
38 | return Util::decodeTime(hexdec(Util::reverseHex(bin2hex($ret))));
39 | } else {
40 | return false;
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/zklib/src/Device.php:
--------------------------------------------------------------------------------
1 | _section = __METHOD__;
16 |
17 | $command = Util::CMD_DEVICE;
18 | $command_string = '~DeviceName';
19 |
20 | return $self->_command($command, $command_string);
21 | }
22 |
23 | /**
24 | * @param ZKLib $self
25 | * @return bool|mixed
26 | */
27 | public function enable(ZKLib $self)
28 | {
29 | $self->_section = __METHOD__;
30 |
31 | $command = Util::CMD_ENABLE_DEVICE;
32 | $command_string = '';
33 |
34 | return $self->_command($command, $command_string);
35 | }
36 |
37 | /**
38 | * @param ZKLib $self
39 | * @return bool|mixed
40 | */
41 | public function disable(ZKLib $self)
42 | {
43 | $self->_section = __METHOD__;
44 |
45 | $command = Util::CMD_DISABLE_DEVICE;
46 | $command_string = chr(0) . chr(0);
47 |
48 | return $self->_command($command, $command_string);
49 | }
50 | }
--------------------------------------------------------------------------------
/zklib/vendor/composer/autoload_real.php:
--------------------------------------------------------------------------------
1 | $path) {
28 | $loader->set($namespace, $path);
29 | }
30 |
31 | $map = require __DIR__ . '/autoload_psr4.php';
32 | foreach ($map as $namespace => $path) {
33 | $loader->setPsr4($namespace, $path);
34 | }
35 |
36 | $classMap = require __DIR__ . '/autoload_classmap.php';
37 | if ($classMap) {
38 | $loader->addClassMap($classMap);
39 | }
40 |
41 | $loader->register(true);
42 |
43 | return $loader;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/zklib/src/Attendance.php:
--------------------------------------------------------------------------------
1 | _section = __METHOD__;
16 |
17 | $command = Util::CMD_ATT_LOG_RRQ;
18 | $command_string = '';
19 |
20 | $session = $self->_command($command, $command_string, Util::COMMAND_TYPE_DATA);
21 | if ($session === false) {
22 | return [];
23 | }
24 |
25 | $attData = Util::recData($self);
26 |
27 | $attendance = [];
28 | if (!empty($attData)) {
29 | $attData = substr($attData, 10);
30 |
31 | while (strlen($attData) > 40) {
32 | $u = unpack('H78', substr($attData, 0, 39));
33 |
34 | $u1 = hexdec(substr($u[1], 4, 2));
35 | $u2 = hexdec(substr($u[1], 6, 2));
36 | $uid = $u1 + ($u2 * 256);
37 | $id = hex2bin(substr($u[1], 8, 18));
38 | $id = str_replace(chr(0), '', $id);
39 | $state = hexdec(substr($u[1], 56, 2));
40 | $timestamp = Util::decodeTime(hexdec(Util::reverseHex(substr($u[1], 58, 8))));
41 | $type = hexdec(Util::reverseHex(substr($u[1], 66, 2 )));
42 |
43 | $attendance[] = [
44 | 'uid' => $uid,
45 | 'id' => $id,
46 | 'state' => $state,
47 | 'timestamp' => $timestamp,
48 | 'type' => $type
49 | ];
50 |
51 | $attData = substr($attData, 40);
52 | }
53 |
54 | }
55 |
56 | return $attendance;
57 | }
58 |
59 | /**
60 | * @param ZKLib $self
61 | * @return bool|mixed
62 | */
63 | public function clear(ZKLib $self)
64 | {
65 | $self->_section = __METHOD__;
66 |
67 | $command = Util::CMD_CLEAR_ATT_LOG;
68 | $command_string = '';
69 |
70 | return $self->_command($command, $command_string);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/zklib/src/Connect.php:
--------------------------------------------------------------------------------
1 | _section = __METHOD__;
18 |
19 | $command = Util::CMD_CONNECT;
20 | $command_string = '';
21 | $chksum = 0;
22 | $session_id = 0;
23 | $reply_id = -1 + Util::USHRT_MAX;
24 |
25 | $buf = Util::createHeader($command, $chksum, $session_id, $reply_id, $command_string);
26 |
27 | socket_sendto($self->_zkclient, $buf, strlen($buf), 0, $self->_ip, $self->_port);
28 |
29 | try {
30 | @socket_recvfrom($self->_zkclient, $self->_data_recv, 1024, 0, $self->_ip, $self->_port);
31 | if (strlen($self->_data_recv) > 0) {
32 | $u = unpack('H2h1/H2h2/H2h3/H2h4/H2h5/H2h6', substr($self->_data_recv, 0, 8));
33 |
34 | $session = hexdec($u['h6'] . $u['h5']);
35 | if (empty($session)) {
36 | return false;
37 | }
38 |
39 | $self->_session_id = $session;
40 | return Util::checkValid($self->_data_recv);
41 | } else {
42 | return false;
43 | }
44 | } catch (ErrorException $e) {
45 | return false;
46 | } catch (Exception $e) {
47 | return false;
48 | }
49 | }
50 |
51 | /**
52 | * @param ZKLib $self
53 | * @return bool
54 | */
55 | public function disconnect(ZKLib $self)
56 | {
57 | $self->_section = __METHOD__;
58 |
59 | $command = Util::CMD_EXIT;
60 | $command_string = '';
61 | $chksum = 0;
62 | $session_id = $self->_session_id;
63 |
64 | $u = unpack('H2h1/H2h2/H2h3/H2h4/H2h5/H2h6/H2h7/H2h8', substr($self->_data_recv, 0, 8));
65 | $reply_id = hexdec($u['h8'] . $u['h7']);
66 |
67 | $buf = Util::createHeader($command, $chksum, $session_id, $reply_id, $command_string);
68 |
69 |
70 | socket_sendto($self->_zkclient, $buf, strlen($buf), 0, $self->_ip, $self->_port);
71 | try {
72 | @socket_recvfrom($self->_zkclient, $self->_data_recv, 1024, 0, $self->_ip, $self->_port);
73 |
74 | $self->_session_id = 0;
75 | return Util::checkValid($self->_data_recv);
76 | } catch (ErrorException $e) {
77 | return false;
78 | } catch (Exception $e) {
79 | return false;
80 | }
81 | }
82 | }
--------------------------------------------------------------------------------
/zklib/src/Fingerprint.php:
--------------------------------------------------------------------------------
1 | _section = __METHOD__;
19 |
20 | $data = [];
21 | //fingers of the hands
22 | for ($i = 0; $i <= 9; $i++) {
23 | $tmp = $this->_getFinger($self, $uid, $i);
24 | if ($tmp['size'] > 0) {
25 | $data[$i] = $tmp['tpl'];
26 | }
27 | unset($tmp);
28 | }
29 | return $data;
30 | }
31 |
32 |
33 | /**
34 | * @param ZKLib $self
35 | * @param integer $uid Unique Employee ID in ZK device
36 | * @param integer $finger Finger ID (0-9)
37 | * @return array
38 | */
39 | private function _getFinger(ZKLib $self, $uid, $finger)
40 | {
41 | $command = Util::CMD_USER_TEMP_RRQ;
42 | $byte1 = chr((int)($uid % 256));
43 | $byte2 = chr((int)($uid >> 8));
44 | $command_string = $byte1 . $byte2 . chr($finger);
45 |
46 | $ret = [
47 | 'size' => 0,
48 | 'tpl' => ''
49 | ];
50 |
51 | $session = $self->_command($command, $command_string, Util::COMMAND_TYPE_DATA);
52 | if ($session === false) {
53 | return $ret;
54 | }
55 |
56 | $data = Util::recData($self, 10, false);
57 |
58 | if (!empty($data)) {
59 | $templateSize = strlen($data);
60 | $prefix = chr($templateSize % 256) . chr(round($templateSize / 256)) . $byte1 . $byte2 . chr($finger) . chr(1);
61 | $data = $prefix . $data;
62 | if (strlen($templateSize) > 0) {
63 | $ret['size'] = $templateSize;
64 | $ret['tpl'] = $data;
65 | }
66 | }
67 |
68 | return $ret;
69 | }
70 |
71 | /**
72 | * TODO: Still can not set fingerprint. Need more documentation about it...
73 | *
74 | * @param ZKLib $self
75 | * @param int $uid Unique Employee ID in ZK device
76 | * @param array $data Binary fingerprint data array (where key is finger ID (0-9) same like returned array from 'get' method)
77 | * @return int Count of added fingerprints
78 | */
79 | public function set(ZKLib $self, $uid, array $data)
80 | {
81 | $self->_section = __METHOD__;
82 |
83 |
84 | $count = 0;
85 | foreach ($data as $finger => $item) {
86 | $allowSet = true;
87 | if ($this->_checkFinger($self, $uid, $finger) === true) {
88 | $allowSet = $this->_removeFinger($self, $uid, $finger);
89 | }
90 | if ($allowSet === true && $this->_setFinger($self, $item) === true) {
91 | $count++;
92 | }
93 | }
94 |
95 | return $count;
96 | }
97 |
98 | /**
99 | * @param ZKLib $self
100 | * @param string $data Binary fingerprint data item
101 | * @return bool|mixed
102 | */
103 | private function _setFinger(ZKLib $self, $data)
104 | {
105 | $command = Util::CMD_USER_TEMP_WRQ;
106 | $command_string = $data;
107 |
108 | return $self->_command($command, $command_string);
109 | }
110 |
111 | /**
112 | * @param ZKLib $self
113 | * @param int $uid Unique Employee ID in ZK device
114 | * @param array $data Fingers ID array (0-9)
115 | * @return int Count of deleted fingerprints
116 | */
117 | public function remove(ZKLib $self, $uid, array $data)
118 | {
119 | $self->_section = __METHOD__;
120 |
121 | $count = 0;
122 | foreach ($data as $finger) {
123 | if ($this->_checkFinger($self, $uid, $finger) === true) {
124 | if ($this->_removeFinger($self, $uid, $finger) === true) {
125 | $count++;
126 | }
127 | }
128 | }
129 |
130 | return $count;
131 | }
132 |
133 | /**
134 | * @param ZKLib $self
135 | * @param int $uid Unique Employee ID in ZK device
136 | * @param int $finger Finger ID (0-9)
137 | * @return bool
138 | */
139 | private function _removeFinger(ZKLib $self, $uid, $finger)
140 | {
141 | $command = Util::CMD_DELETE_USER_TEMP;
142 | $byte1 = chr((int)($uid % 256));
143 | $byte2 = chr((int)($uid >> 8));
144 | $command_string = ($byte1 . $byte2) . chr($finger);
145 |
146 | $self->_command($command, $command_string);
147 |
148 | return !($this->_checkFinger($self, $uid, $finger));
149 | }
150 |
151 | /**
152 | * @param ZKLib $self
153 | * @param int $uid Unique Employee ID in ZK device
154 | * @param int $finger Finger ID (0-9)
155 | * @return bool Returned true if exist
156 | */
157 | private function _checkFinger(ZKLib $self, $uid, $finger)
158 | {
159 | $res = $this->_getFinger($self, $uid, $finger);
160 | return (bool)($res['size'] > 0);
161 | }
162 | }
--------------------------------------------------------------------------------
/zklib/src/User.php:
--------------------------------------------------------------------------------
1 | _section = __METHOD__;
22 |
23 | if (
24 | (int)$uid === 0 ||
25 | (int)$uid > Util::USHRT_MAX ||
26 | strlen($userid) > 9 ||
27 | strlen($name) > 24 ||
28 | strlen($password) > 8 ||
29 | strlen($cardno) > 10
30 | ) {
31 | return false;
32 | }
33 |
34 | $command = Util::CMD_SET_USER;
35 | $byte1 = chr((int)($uid % 256));
36 | $byte2 = chr((int)($uid >> 8));
37 | $cardno = hex2bin(Util::reverseHex(dechex($cardno)));
38 |
39 | $command_string = implode('', [
40 | $byte1,
41 | $byte2,
42 | chr($role),
43 | str_pad($password, 8, chr(0)),
44 | str_pad($name, 24, chr(0)),
45 | str_pad($cardno, 4, chr(0)),
46 | str_pad(chr(1), 9, chr(0)),
47 | str_pad($userid, 9, chr(0)),
48 | str_repeat(chr(0), 15)
49 | ]);
50 |
51 | return $self->_command($command, $command_string);
52 | }
53 |
54 | /**
55 | * @param ZKLib $self
56 | * @return array [userid, name, cardno, uid, role, password]
57 | */
58 | public function get(ZKLib $self)
59 | {
60 | $self->_section = __METHOD__;
61 |
62 | $command = Util::CMD_USER_TEMP_RRQ;
63 | $command_string = chr(Util::FCT_USER);
64 |
65 | $session = $self->_command($command, $command_string, Util::COMMAND_TYPE_DATA);
66 | if ($session === false) {
67 | return [];
68 | }
69 |
70 | $userData = Util::recData($self);
71 |
72 | $users = [];
73 | if (!empty($userData)) {
74 | $userData = substr($userData, 11);
75 |
76 | while (strlen($userData) > 72) {
77 | $u = unpack('H144', substr($userData, 0, 72));
78 |
79 | $u1 = hexdec(substr($u[1], 2, 2));
80 | $u2 = hexdec(substr($u[1], 4, 2));
81 | $uid = $u1 + ($u2 * 256);
82 | $cardno = hexdec(substr($u[1], 78, 2) . substr($u[1], 76, 2) . substr($u[1], 74, 2) . substr($u[1], 72, 2)) . ' ';
83 | $role = hexdec(substr($u[1], 6, 2)) . ' ';
84 | $password = hex2bin(substr($u[1], 8, 16)) . ' ';
85 | $name = hex2bin(substr($u[1], 24, 74)) . ' ';
86 | $userid = hex2bin(substr($u[1], 98, 72)) . ' ';
87 |
88 | //Clean up some messy characters from the user name
89 | $password = explode(chr(0), $password, 2);
90 | $password = $password[0];
91 | $userid = explode(chr(0), $userid, 2);
92 | $userid = $userid[0];
93 | $name = explode(chr(0), $name, 3);
94 | $name = utf8_encode($name[0]);
95 | $cardno = str_pad($cardno, 11, '0', STR_PAD_LEFT);
96 |
97 | if ($name == '') {
98 | $name = $userid;
99 | }
100 |
101 | $users[$userid] = [
102 | 'userid' => $userid,
103 | 'name' => $name,
104 | 'cardno' => $cardno,
105 | 'uid' => $uid,
106 | 'role' => intval($role),
107 | 'password' => $password
108 | ];
109 |
110 | $userData = substr($userData, 72);
111 | }
112 | }
113 |
114 | return $users;
115 | }
116 |
117 | /**
118 | * @param ZKLib $self
119 | * @return bool|mixed
120 | */
121 | public function clear(ZKLib $self)
122 | {
123 | $self->_section = __METHOD__;
124 |
125 | $command = Util::CMD_CLEAR_DATA;
126 | $command_string = '';
127 |
128 | return $self->_command($command, $command_string);
129 | }
130 |
131 | /**
132 | * @param ZKLib $self
133 | * @return bool|mixed
134 | */
135 | public function clearAdmin(ZKLib $self)
136 | {
137 | $self->_section = __METHOD__;
138 |
139 | $command = Util::CMD_CLEAR_ADMIN;
140 | $command_string = '';
141 |
142 | return $self->_command($command, $command_string);
143 | }
144 |
145 | /**
146 | * @param ZKLib $self
147 | * @param integer $uid
148 | * @return bool|mixed
149 | */
150 | public function remove(ZKLib $self, $uid)
151 | {
152 | $self->_section = __METHOD__;
153 |
154 | $command = Util::CMD_DELETE_USER;
155 | $byte1 = chr((int)($uid % 256));
156 | $byte2 = chr((int)($uid >> 8));
157 | $command_string = ($byte1 . $byte2);
158 |
159 | return $self->_command($command, $command_string);
160 | }
161 | }
--------------------------------------------------------------------------------
/example.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | ZK Test
4 |
5 |
6 |
7 | connect();
19 | if ($ret) {
20 | $zk->disableDevice();
21 | $zk->setTime(date('Y-m-d H:i:s')); // Synchronize time
22 | ?>
23 |
24 |
25 |
26 | | Status |
27 | Connected |
28 | Version |
29 | version()); ?> |
30 | OS Version |
31 | osVersion()); ?> |
32 | Platform |
33 | platform()); ?> |
34 |
35 |
36 | | Firmware Version |
37 | fmVersion()); ?> |
38 | WorkCode |
39 | workCode()); ?> |
40 | SSR |
41 | ssr()); ?> |
42 | Pin Width |
43 | pinWidth()); ?> |
44 |
45 |
46 | | Face Function On |
47 | faceFunctionOn()); ?> |
48 | Serial Number |
49 | serialNumber()); ?> |
50 | Device Name |
51 | deviceName()); ?> |
52 | Get Time |
53 | getTime()); ?> |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | | Data User |
62 |
63 |
64 | | UID |
65 | ID |
66 | Name |
67 | Card # |
68 | Role |
69 | Password |
70 |
71 | setUser(1, '1', 'User1', '', ZK\Util::LEVEL_USER);
74 | //$zk->setUser(2, '2', 'User2', '', ZK\Util::LEVEL_USER);
75 | //$zk->setUser(3, '3', 'User3', '', ZK\Util::LEVEL_USER);
76 | //$zk->setUser(5, '5', 'Admin', '1234', ZK\Util::LEVEL_ADMIN);
77 | $users = $zk->getUser();
78 | sleep(1);
79 | foreach ($users as $uItem) {
80 | ?>
81 |
82 | |
83 | |
84 | |
85 | |
86 | |
87 | |
88 |
89 | clearAdmin();
96 | //$zk->clearUsers();
97 | //$zk->removeUser(1);
98 | ?>
99 |
100 |
101 |
102 |
103 |
104 | | Data Attendance |
105 |
106 |
107 | | UID |
108 | ID |
109 | Name |
110 | State |
111 | Date |
112 | Time |
113 | Type |
114 |
115 | getAttendance();
117 | if (count($attendance) > 0) {
118 | $attendance = array_reverse($attendance, true);
119 | sleep(1);
120 | foreach ($attendance as $attItem) {
121 | ?>
122 |
123 | |
124 | |
125 | |
126 | |
127 | |
128 | |
129 | |
130 |
131 |
135 |
136 | 0) {
138 | //$zk->clearAttendance(); // Remove attendance log only if not empty
139 | }
140 | ?>
141 |
142 | enableDevice();
144 | $zk->disconnect();
145 | }
146 | ?>
147 |
148 |
149 |
--------------------------------------------------------------------------------
/zklib/ZKLib.php:
--------------------------------------------------------------------------------
1 | _ip = $ip;
24 | $this->_port = $port;
25 |
26 | $this->_zkclient = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
27 |
28 | $timeout = ['sec' => 60, 'usec' => 500000];
29 | socket_set_option($this->_zkclient, SOL_SOCKET, SO_RCVTIMEO, $timeout);
30 |
31 | }
32 |
33 | /**
34 | * Create and send command to device
35 | *
36 | * @param string $command
37 | * @param string $command_string
38 | * @param string $type
39 | * @return bool|mixed
40 | */
41 | public function _command($command, $command_string, $type = Util::COMMAND_TYPE_GENERAL)
42 | {
43 | $chksum = 0;
44 | $session_id = $this->_session_id;
45 |
46 | $u = unpack('H2h1/H2h2/H2h3/H2h4/H2h5/H2h6/H2h7/H2h8', substr($this->_data_recv, 0, 8));
47 | $reply_id = hexdec($u['h8'] . $u['h7']);
48 |
49 | $buf = Util::createHeader($command, $chksum, $session_id, $reply_id, $command_string);
50 |
51 | socket_sendto($this->_zkclient, $buf, strlen($buf), 0, $this->_ip, $this->_port);
52 |
53 | try {
54 | @socket_recvfrom($this->_zkclient, $this->_data_recv, 1024, 0, $this->_ip, $this->_port);
55 |
56 | $u = unpack('H2h1/H2h2/H2h3/H2h4/H2h5/H2h6', substr($this->_data_recv, 0, 8));
57 |
58 | $ret = false;
59 | $session = hexdec($u['h6'] . $u['h5']);
60 |
61 | if ($type === Util::COMMAND_TYPE_GENERAL && $session_id === $session) {
62 | $ret = substr($this->_data_recv, 8);
63 | } else if ($type === Util::COMMAND_TYPE_DATA && !empty($session)) {
64 | $ret = $session;
65 | }
66 |
67 | return $ret;
68 | } catch (ErrorException $e) {
69 | return false;
70 | } catch (Exception $e) {
71 | return false;
72 | }
73 | }
74 |
75 | /**
76 | * Connect to device
77 | *
78 | * @return bool
79 | */
80 | public function connect()
81 | {
82 | return (new ZK\Connect())->connect($this);
83 | }
84 |
85 | /**
86 | * Disconnect from device
87 | *
88 | * @return bool
89 | */
90 | public function disconnect()
91 | {
92 | return (new ZK\Connect())->disconnect($this);
93 | }
94 |
95 | /**
96 | * Get device version
97 | *
98 | * @return bool|mixed
99 | */
100 | public function version()
101 | {
102 | return (new ZK\Version())->get($this);
103 | }
104 |
105 | /**
106 | * Get OS version
107 | *
108 | * @return bool|mixed
109 | */
110 | public function osVersion()
111 | {
112 | return (new ZK\Os())->get($this);
113 | }
114 |
115 | /**
116 | * Get platform
117 | *
118 | * @return bool|mixed
119 | */
120 | public function platform()
121 | {
122 | return (new ZK\Platform())->get($this);
123 | }
124 |
125 | /**
126 | * Get firmware version
127 | *
128 | * @return bool|mixed
129 | */
130 | public function fmVersion()
131 | {
132 | return (new ZK\Platform())->getVersion($this);
133 | }
134 |
135 | /**
136 | * Get work code
137 | *
138 | * @return bool|mixed
139 | */
140 | public function workCode()
141 | {
142 | return (new ZK\WorkCode())->get($this);
143 | }
144 |
145 | /**
146 | * Get SSR
147 | *
148 | * @return bool|mixed
149 | */
150 | public function ssr()
151 | {
152 | return (new ZK\Ssr())->get($this);
153 | }
154 |
155 | /**
156 | * Get pin width
157 | *
158 | * @return bool|mixed
159 | */
160 | public function pinWidth()
161 | {
162 | return (new ZK\Pin())->width($this);
163 | }
164 |
165 | /**
166 | * @return bool|mixed
167 | */
168 | public function faceFunctionOn()
169 | {
170 | return (new ZK\Face())->on($this);
171 | }
172 |
173 | /**
174 | * Get device serial number
175 | *
176 | * @return bool|mixed
177 | */
178 | public function serialNumber()
179 | {
180 | return (new ZK\SerialNumber())->get($this);
181 | }
182 |
183 | /**
184 | * Get device name
185 | *
186 | * @return bool|mixed
187 | */
188 | public function deviceName()
189 | {
190 | return (new ZK\Device())->name($this);
191 | }
192 |
193 | /**
194 | * Disable device
195 | *
196 | * @return bool|mixed
197 | */
198 | public function disableDevice()
199 | {
200 | return (new ZK\Device())->disable($this);
201 | }
202 |
203 | /**
204 | * Enable device
205 | *
206 | * @return bool|mixed
207 | */
208 | public function enableDevice()
209 | {
210 | return (new ZK\Device())->enable($this);
211 | }
212 |
213 | /**
214 | * Get users data
215 | *
216 | * @return array [userid, name, cardno, uid, role, password]
217 | */
218 | public function getUser()
219 | {
220 | return (new ZK\User())->get($this);
221 | }
222 |
223 | /**
224 | * Set user data
225 | *
226 | * @param int $uid Unique ID (max 65535)
227 | * @param int|string $userid ID in DB (same like $uid, max length = 9, only numbers - depends device setting)
228 | * @param string $name (max length = 24)
229 | * @param int|string $password (max length = 8, only numbers - depends device setting)
230 | * @param int $role Default Util::LEVEL_USER
231 | * @param int $cardno Default 0 (max length = 10, only numbers)
232 | * @return bool|mixed
233 | */
234 | public function setUser($uid, $userid, $name, $password, $role = Util::LEVEL_USER, $cardno = 0)
235 | {
236 | return (new ZK\User())->set($this, $uid, $userid, $name, $password, $role, $cardno);
237 | }
238 |
239 | /**
240 | * Get fingerprint data array by UID
241 | * TODO: Can get data, but don't know how to parse the data. Need more documentation about it...
242 | *
243 | * @param integer $uid Unique ID (max 65535)
244 | * @return array Binary fingerprint data array (where key is finger ID (0-9))
245 | */
246 | public function getFingerprint($uid)
247 | {
248 | return (new ZK\Fingerprint())->get($this, $uid);
249 | }
250 |
251 | /**
252 | * Set fingerprint data array
253 | * TODO: Still can not set fingerprint. Need more documentation about it...
254 | *
255 | * @param integer $uid Unique ID (max 65535)
256 | * @param array $data Binary fingerprint data array (where key is finger ID (0-9) same like returned array from 'getFingerprint' method)
257 | * @return int Count of added fingerprints
258 | */
259 | public function setFingerprint($uid, array $data)
260 | {
261 | return (new ZK\Fingerprint())->set($this, $uid, $data);
262 | }
263 |
264 | /**
265 | * Remove fingerprint by UID and fingers ID array
266 | *
267 | * @param integer $uid Unique ID (max 65535)
268 | * @param array $data Fingers ID array (0-9)
269 | * @return int Count of deleted fingerprints
270 | */
271 | public function removeFingerprint($uid, array $data)
272 | {
273 | return (new ZK\Fingerprint())->remove($this, $uid, $data);
274 | }
275 |
276 | /**
277 | * Remove All users
278 | *
279 | * @return bool|mixed
280 | */
281 | public function clearUsers()
282 | {
283 | return (new ZK\User())->clear($this);
284 | }
285 |
286 | /**
287 | * Remove admin
288 | *
289 | * @return bool|mixed
290 | */
291 | public function clearAdmin()
292 | {
293 | return (new ZK\User())->clearAdmin($this);
294 | }
295 |
296 | /**
297 | * Remove user by UID
298 | *
299 | * @param integer $uid
300 | * @return bool|mixed
301 | */
302 | public function removeUser($uid)
303 | {
304 | return (new ZK\User())->remove($this, $uid);
305 | }
306 |
307 | /**
308 | * Get attendance log
309 | *
310 | * @return array [uid, id, state, timestamp]
311 | */
312 | public function getAttendance()
313 | {
314 | return (new ZK\Attendance())->get($this);
315 | }
316 |
317 | /**
318 | * Clear attendance log
319 | *
320 | * @return bool|mixed
321 | */
322 | public function clearAttendance()
323 | {
324 | return (new ZK\Attendance())->clear($this);
325 | }
326 |
327 | /**
328 | * Set device time
329 | *
330 | * @param string $t Format: "Y-m-d H:i:s"
331 | * @return bool|mixed
332 | */
333 | public function setTime($t)
334 | {
335 | return (new ZK\Time())->set($this, $t);
336 | }
337 |
338 | /**
339 | * Get device time
340 | *
341 | * @return bool|mixed Format: "Y-m-d H:i:s"
342 | */
343 | public function getTime()
344 | {
345 | return (new ZK\Time())->get($this);
346 | }
347 | }
--------------------------------------------------------------------------------
/zklib/src/Util.php:
--------------------------------------------------------------------------------
1 | (int)date('Y', $timestamp),
77 | 'month' => (int)date('m', $timestamp),
78 | 'day' => (int)date('d', $timestamp),
79 | 'hour' => (int)date('H', $timestamp),
80 | 'minute' => (int)date('i', $timestamp),
81 | 'second' => (int)date('s', $timestamp),
82 | ];
83 |
84 | $d = (($t->year % 100) * 12 * 31 + (($t->month - 1) * 31) + $t->day - 1) *
85 | (24 * 60 * 60) + ($t->hour * 60 + $t->minute) * 60 + $t->second;
86 |
87 | return $d;
88 | }
89 |
90 | /**
91 | * Decode a timestamp retrieved from the timeclock
92 | * copied from zkemsdk.c - DecodeTime
93 | *
94 | * @param int|string $t
95 | * @return false|string Format: "Y-m-d H:i:s"
96 | */
97 | static public function decodeTime($t)
98 | {
99 | $second = $t % 60;
100 | $t = $t / 60;
101 |
102 | $minute = $t % 60;
103 | $t = $t / 60;
104 |
105 | $hour = $t % 24;
106 | $t = $t / 24;
107 |
108 | $day = $t % 31 + 1;
109 | $t = $t / 31;
110 |
111 | $month = $t % 12 + 1;
112 | $t = $t / 12;
113 |
114 | $year = floor($t + 2000);
115 |
116 | $d = date('Y-m-d H:i:s', strtotime(
117 | $year . '-' . $month . '-' . $day . ' ' . $hour . ':' . $minute . ':' . $second
118 | ));
119 |
120 | return $d;
121 | }
122 |
123 | /**
124 | * @param string $hex
125 | * @return string
126 | */
127 | static public function reverseHex($hex)
128 | {
129 | $tmp = '';
130 |
131 | for ($i = strlen($hex); $i >= 0; $i--) {
132 | $tmp .= substr($hex, $i, 2);
133 | $i--;
134 | }
135 |
136 | return $tmp;
137 | }
138 |
139 | /**
140 | * Checks a returned packet to see if it returned self::CMD_PREPARE_DATA,
141 | * indicating that data packets are to be sent
142 | * Returns the amount of bytes that are going to be sent
143 | *
144 | * @param ZKLib $self
145 | * @return bool|number
146 | */
147 | static public function getSize(ZKLib $self)
148 | {
149 | $u = unpack('H2h1/H2h2/H2h3/H2h4/H2h5/H2h6/H2h7/H2h8', substr($self->_data_recv, 0, 8));
150 | $command = hexdec($u['h2'] . $u['h1']);
151 |
152 | if ($command == self::CMD_PREPARE_DATA) {
153 | $u = unpack('H2h1/H2h2/H2h3/H2h4', substr($self->_data_recv, 8, 4));
154 | $size = hexdec($u['h4'] . $u['h3'] . $u['h2'] . $u['h1']);
155 | return $size;
156 | } else {
157 | return false;
158 | }
159 | }
160 |
161 | /**
162 | * This function calculates the chksum of the packet to be sent to the
163 | * time clock
164 | * Copied from zkemsdk.c
165 | *
166 | * @inheritdoc
167 | */
168 | static public function createChkSum($p)
169 | {
170 | $l = count($p);
171 | $chksum = 0;
172 | $i = $l;
173 | $j = 1;
174 | while ($i > 1) {
175 | $u = unpack('S', pack('C2', $p['c' . $j], $p['c' . ($j + 1)]));
176 |
177 | $chksum += $u[1];
178 |
179 | if ($chksum > self::USHRT_MAX) {
180 | $chksum -= self::USHRT_MAX;
181 | }
182 | $i -= 2;
183 | $j += 2;
184 | }
185 |
186 | if ($i) {
187 | $chksum = $chksum + $p['c' . strval(count($p))];
188 | }
189 |
190 | while ($chksum > self::USHRT_MAX) {
191 | $chksum -= self::USHRT_MAX;
192 | }
193 |
194 | if ($chksum > 0) {
195 | $chksum = -($chksum);
196 | } else {
197 | $chksum = abs($chksum);
198 | }
199 |
200 | $chksum -= 1;
201 | while ($chksum < 0) {
202 | $chksum += self::USHRT_MAX;
203 | }
204 |
205 | return pack('S', $chksum);
206 | }
207 |
208 | /**
209 | * This function puts a the parts that make up a packet together and
210 | * packs them into a byte string
211 | *
212 | * @inheritdoc
213 | */
214 | static public function createHeader($command, $chksum, $session_id, $reply_id, $command_string)
215 | {
216 | $buf = pack('SSSS', $command, $chksum, $session_id, $reply_id) . $command_string;
217 |
218 | $buf = unpack('C' . (8 + strlen($command_string)) . 'c', $buf);
219 |
220 | $u = unpack('S', self::createChkSum($buf));
221 |
222 | if (is_array($u)) {
223 | $u = reset($u);
224 | }
225 | $chksum = $u;
226 |
227 | $reply_id += 1;
228 |
229 | if ($reply_id >= self::USHRT_MAX) {
230 | $reply_id -= self::USHRT_MAX;
231 | }
232 |
233 | $buf = pack('SSSS', $command, $chksum, $session_id, $reply_id);
234 |
235 | return $buf . $command_string;
236 |
237 | }
238 |
239 | /**
240 | * Checks a returned packet to see if it returned Util::CMD_ACK_OK,
241 | * indicating success
242 | *
243 | * @inheritdoc
244 | */
245 | static public function checkValid($reply)
246 | {
247 | $u = unpack('H2h1/H2h2', substr($reply, 0, 8));
248 |
249 | $command = hexdec($u['h2'] . $u['h1']);
250 | /** TODO: Some device can return 'Connection unauthorized' then should check also */
251 | if ($command == self::CMD_ACK_OK || $command == self::CMD_ACK_UNAUTH) {
252 | return true;
253 | } else {
254 | return false;
255 | }
256 | }
257 |
258 | /**
259 | * Get User Role string
260 | * @param integer $role
261 | * @return string
262 | */
263 | static public function getUserRole($role)
264 | {
265 | switch ($role) {
266 | case self::LEVEL_USER:
267 | $ret = 'User';
268 | break;
269 | case self::LEVEL_ADMIN:
270 | $ret = 'Admin';
271 | break;
272 | default:
273 | $ret = 'Unknown';
274 | }
275 |
276 | return $ret;
277 | }
278 |
279 | /**
280 | * Get Attendance State string
281 | * @param integer $state
282 | * @return string
283 | */
284 | static public function getAttState($state)
285 | {
286 | switch ($state) {
287 | case self::ATT_STATE_FINGERPRINT:
288 | $ret = 'Fingerprint';
289 | break;
290 | case self::ATT_STATE_PASSWORD:
291 | $ret = 'Password';
292 | break;
293 | case self::ATT_STATE_CARD:
294 | $ret = 'Card';
295 | break;
296 | default:
297 | $ret = 'Unknown';
298 | }
299 |
300 | return $ret;
301 | }
302 |
303 | /**
304 | * Get Attendance Type string
305 | * @param integer $type
306 | * @return string
307 | */
308 | static public function getAttType($type)
309 | {
310 | switch ($type) {
311 | case self::ATT_TYPE_CHECK_IN:
312 | $ret = 'Check-in';
313 | break;
314 | case self::ATT_TYPE_CHECK_OUT:
315 | $ret = 'Check-out';
316 | break;
317 | case self::ATT_TYPE_OVERTIME_IN:
318 | $ret = 'Overtime-in';
319 | break;
320 | case self::ATT_TYPE_OVERTIME_OUT:
321 | $ret = 'Overtime-out';
322 | break;
323 | default:
324 | $ret = 'Undefined';
325 | }
326 |
327 | return $ret;
328 | }
329 |
330 | /**
331 | * Receive data from device
332 | * @param ZKLib $self
333 | * @param int $maxErrors
334 | * @param bool $first if 'true' don't remove first 4 bytes for first row
335 | * @return string
336 | */
337 | static public function recData(ZKLib $self, $maxErrors = 10, $first = true)
338 | {
339 | $data = '';
340 | $bytes = self::getSize($self);
341 |
342 | if ($bytes) {
343 | $received = 0;
344 | $errors = 0;
345 |
346 | while ($bytes > $received) {
347 | $ret = @socket_recvfrom($self->_zkclient, $dataRec, 1032, 0, $self->_ip, $self->_port);
348 |
349 | if ($ret === false) {
350 | if ($errors < $maxErrors) {
351 | //try again if false
352 | $errors++;
353 | sleep(1);
354 | continue;
355 | } else {
356 | //return empty if has maximum count of errors
357 | self::logReceived($self, $received, $bytes);
358 | unset($data);
359 | return '';
360 | }
361 | }
362 |
363 | if ($first === false) {
364 | //The first 4 bytes don't seem to be related to the user
365 | $dataRec = substr($dataRec, 8);
366 | }
367 |
368 | $data .= $dataRec;
369 | $received += strlen($dataRec);
370 |
371 | unset($dataRec);
372 | $first = false;
373 | }
374 |
375 | //flush socket
376 | @socket_recvfrom($self->_zkclient, $dataRec, 1024, 0, $self->_ip, $self->_port);
377 | unset($dataRec);
378 | }
379 |
380 | return $data;
381 | }
382 |
383 | /**
384 | * @param ZKLib $self
385 | * @param int $received
386 | * @param int $bytes
387 | */
388 | static private function logReceived(ZKLib $self, $received, $bytes)
389 | {
390 | self::logger($self, 'Received: ' . $received . ' of ' . $bytes . ' bytes');
391 | }
392 |
393 | /**
394 | * Write log
395 | * @param ZKLib $self
396 | * @param string $str
397 | */
398 | static private function logger(ZKLib $self, $str)
399 | {
400 | if (defined('ZK_LIB_LOG')) {
401 | //use constant if defined
402 | $log = ZK_LIB_LOG;
403 | } else {
404 | $dir = dirname(dirname(__FILE__));
405 | $log = $dir . '/logs/error.log';
406 | }
407 |
408 | $row = '<' . $self->_ip . '> [' . date('d.m.Y H:i:s') . '] ';
409 | $row .= (empty($self->_section) ? '' : '(' . $self->_section . ') ');
410 | $row .= $str;
411 | $row .= PHP_EOL;
412 |
413 | file_put_contents($log, $row, FILE_APPEND);
414 | }
415 | }
416 |
--------------------------------------------------------------------------------
/zklib/vendor/composer/ClassLoader.php:
--------------------------------------------------------------------------------
1 |
7 | * Jordi Boggiano
8 | *
9 | * For the full copyright and license information, please view the LICENSE
10 | * file that was distributed with this source code.
11 | */
12 |
13 | namespace Composer\Autoload;
14 |
15 | /**
16 | * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
17 | *
18 | * $loader = new \Composer\Autoload\ClassLoader();
19 | *
20 | * // register classes with namespaces
21 | * $loader->add('Symfony\Component', __DIR__.'/component');
22 | * $loader->add('Symfony', __DIR__.'/framework');
23 | *
24 | * // activate the autoloader
25 | * $loader->register();
26 | *
27 | * // to enable searching the include path (eg. for PEAR packages)
28 | * $loader->setUseIncludePath(true);
29 | *
30 | * In this example, if you try to use a class in the Symfony\Component
31 | * namespace or one of its children (Symfony\Component\Console for instance),
32 | * the autoloader will first look for the class under the component/
33 | * directory, and it will then fallback to the framework/ directory if not
34 | * found before giving up.
35 | *
36 | * This class is loosely based on the Symfony UniversalClassLoader.
37 | *
38 | * @author Fabien Potencier
39 | * @author Jordi Boggiano
40 | * @see http://www.php-fig.org/psr/psr-0/
41 | * @see http://www.php-fig.org/psr/psr-4/
42 | */
43 | class ClassLoader
44 | {
45 | // PSR-4
46 | private $prefixLengthsPsr4 = array();
47 | private $prefixDirsPsr4 = array();
48 | private $fallbackDirsPsr4 = array();
49 |
50 | // PSR-0
51 | private $prefixesPsr0 = array();
52 | private $fallbackDirsPsr0 = array();
53 |
54 | private $useIncludePath = false;
55 | private $classMap = array();
56 |
57 | private $classMapAuthoritative = false;
58 |
59 | public function getPrefixes()
60 | {
61 | if (!empty($this->prefixesPsr0)) {
62 | return call_user_func_array('array_merge', $this->prefixesPsr0);
63 | }
64 |
65 | return array();
66 | }
67 |
68 | public function getPrefixesPsr4()
69 | {
70 | return $this->prefixDirsPsr4;
71 | }
72 |
73 | public function getFallbackDirs()
74 | {
75 | return $this->fallbackDirsPsr0;
76 | }
77 |
78 | public function getFallbackDirsPsr4()
79 | {
80 | return $this->fallbackDirsPsr4;
81 | }
82 |
83 | public function getClassMap()
84 | {
85 | return $this->classMap;
86 | }
87 |
88 | /**
89 | * @param array $classMap Class to filename map
90 | */
91 | public function addClassMap(array $classMap)
92 | {
93 | if ($this->classMap) {
94 | $this->classMap = array_merge($this->classMap, $classMap);
95 | } else {
96 | $this->classMap = $classMap;
97 | }
98 | }
99 |
100 | /**
101 | * Registers a set of PSR-0 directories for a given prefix, either
102 | * appending or prepending to the ones previously set for this prefix.
103 | *
104 | * @param string $prefix The prefix
105 | * @param array|string $paths The PSR-0 root directories
106 | * @param bool $prepend Whether to prepend the directories
107 | */
108 | public function add($prefix, $paths, $prepend = false)
109 | {
110 | if (!$prefix) {
111 | if ($prepend) {
112 | $this->fallbackDirsPsr0 = array_merge(
113 | (array) $paths,
114 | $this->fallbackDirsPsr0
115 | );
116 | } else {
117 | $this->fallbackDirsPsr0 = array_merge(
118 | $this->fallbackDirsPsr0,
119 | (array) $paths
120 | );
121 | }
122 |
123 | return;
124 | }
125 |
126 | $first = $prefix[0];
127 | if (!isset($this->prefixesPsr0[$first][$prefix])) {
128 | $this->prefixesPsr0[$first][$prefix] = (array) $paths;
129 |
130 | return;
131 | }
132 | if ($prepend) {
133 | $this->prefixesPsr0[$first][$prefix] = array_merge(
134 | (array) $paths,
135 | $this->prefixesPsr0[$first][$prefix]
136 | );
137 | } else {
138 | $this->prefixesPsr0[$first][$prefix] = array_merge(
139 | $this->prefixesPsr0[$first][$prefix],
140 | (array) $paths
141 | );
142 | }
143 | }
144 |
145 | /**
146 | * Registers a set of PSR-4 directories for a given namespace, either
147 | * appending or prepending to the ones previously set for this namespace.
148 | *
149 | * @param string $prefix The prefix/namespace, with trailing '\\'
150 | * @param array|string $paths The PSR-4 base directories
151 | * @param bool $prepend Whether to prepend the directories
152 | *
153 | * @throws \InvalidArgumentException
154 | */
155 | public function addPsr4($prefix, $paths, $prepend = false)
156 | {
157 | if (!$prefix) {
158 | // Register directories for the root namespace.
159 | if ($prepend) {
160 | $this->fallbackDirsPsr4 = array_merge(
161 | (array) $paths,
162 | $this->fallbackDirsPsr4
163 | );
164 | } else {
165 | $this->fallbackDirsPsr4 = array_merge(
166 | $this->fallbackDirsPsr4,
167 | (array) $paths
168 | );
169 | }
170 | } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
171 | // Register directories for a new namespace.
172 | $length = strlen($prefix);
173 | if ('\\' !== $prefix[$length - 1]) {
174 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
175 | }
176 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
177 | $this->prefixDirsPsr4[$prefix] = (array) $paths;
178 | } elseif ($prepend) {
179 | // Prepend directories for an already registered namespace.
180 | $this->prefixDirsPsr4[$prefix] = array_merge(
181 | (array) $paths,
182 | $this->prefixDirsPsr4[$prefix]
183 | );
184 | } else {
185 | // Append directories for an already registered namespace.
186 | $this->prefixDirsPsr4[$prefix] = array_merge(
187 | $this->prefixDirsPsr4[$prefix],
188 | (array) $paths
189 | );
190 | }
191 | }
192 |
193 | /**
194 | * Registers a set of PSR-0 directories for a given prefix,
195 | * replacing any others previously set for this prefix.
196 | *
197 | * @param string $prefix The prefix
198 | * @param array|string $paths The PSR-0 base directories
199 | */
200 | public function set($prefix, $paths)
201 | {
202 | if (!$prefix) {
203 | $this->fallbackDirsPsr0 = (array) $paths;
204 | } else {
205 | $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
206 | }
207 | }
208 |
209 | /**
210 | * Registers a set of PSR-4 directories for a given namespace,
211 | * replacing any others previously set for this namespace.
212 | *
213 | * @param string $prefix The prefix/namespace, with trailing '\\'
214 | * @param array|string $paths The PSR-4 base directories
215 | *
216 | * @throws \InvalidArgumentException
217 | */
218 | public function setPsr4($prefix, $paths)
219 | {
220 | if (!$prefix) {
221 | $this->fallbackDirsPsr4 = (array) $paths;
222 | } else {
223 | $length = strlen($prefix);
224 | if ('\\' !== $prefix[$length - 1]) {
225 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
226 | }
227 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
228 | $this->prefixDirsPsr4[$prefix] = (array) $paths;
229 | }
230 | }
231 |
232 | /**
233 | * Turns on searching the include path for class files.
234 | *
235 | * @param bool $useIncludePath
236 | */
237 | public function setUseIncludePath($useIncludePath)
238 | {
239 | $this->useIncludePath = $useIncludePath;
240 | }
241 |
242 | /**
243 | * Can be used to check if the autoloader uses the include path to check
244 | * for classes.
245 | *
246 | * @return bool
247 | */
248 | public function getUseIncludePath()
249 | {
250 | return $this->useIncludePath;
251 | }
252 |
253 | /**
254 | * Turns off searching the prefix and fallback directories for classes
255 | * that have not been registered with the class map.
256 | *
257 | * @param bool $classMapAuthoritative
258 | */
259 | public function setClassMapAuthoritative($classMapAuthoritative)
260 | {
261 | $this->classMapAuthoritative = $classMapAuthoritative;
262 | }
263 |
264 | /**
265 | * Should class lookup fail if not found in the current class map?
266 | *
267 | * @return bool
268 | */
269 | public function isClassMapAuthoritative()
270 | {
271 | return $this->classMapAuthoritative;
272 | }
273 |
274 | /**
275 | * Registers this instance as an autoloader.
276 | *
277 | * @param bool $prepend Whether to prepend the autoloader or not
278 | */
279 | public function register($prepend = false)
280 | {
281 | spl_autoload_register(array($this, 'loadClass'), true, $prepend);
282 | }
283 |
284 | /**
285 | * Unregisters this instance as an autoloader.
286 | */
287 | public function unregister()
288 | {
289 | spl_autoload_unregister(array($this, 'loadClass'));
290 | }
291 |
292 | /**
293 | * Loads the given class or interface.
294 | *
295 | * @param string $class The name of the class
296 | * @return bool|null True if loaded, null otherwise
297 | */
298 | public function loadClass($class)
299 | {
300 | if ($file = $this->findFile($class)) {
301 | includeFile($file);
302 |
303 | return true;
304 | }
305 | }
306 |
307 | /**
308 | * Finds the path to the file where the class is defined.
309 | *
310 | * @param string $class The name of the class
311 | *
312 | * @return string|false The path if found, false otherwise
313 | */
314 | public function findFile($class)
315 | {
316 | // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
317 | if ('\\' == $class[0]) {
318 | $class = substr($class, 1);
319 | }
320 |
321 | // class map lookup
322 | if (isset($this->classMap[$class])) {
323 | return $this->classMap[$class];
324 | }
325 | if ($this->classMapAuthoritative) {
326 | return false;
327 | }
328 |
329 | $file = $this->findFileWithExtension($class, '.php');
330 |
331 | // Search for Hack files if we are running on HHVM
332 | if ($file === null && defined('HHVM_VERSION')) {
333 | $file = $this->findFileWithExtension($class, '.hh');
334 | }
335 |
336 | if ($file === null) {
337 | // Remember that this class does not exist.
338 | return $this->classMap[$class] = false;
339 | }
340 |
341 | return $file;
342 | }
343 |
344 | private function findFileWithExtension($class, $ext)
345 | {
346 | // PSR-4 lookup
347 | $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
348 |
349 | $first = $class[0];
350 | if (isset($this->prefixLengthsPsr4[$first])) {
351 | foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
352 | if (0 === strpos($class, $prefix)) {
353 | foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
354 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
355 | return $file;
356 | }
357 | }
358 | }
359 | }
360 | }
361 |
362 | // PSR-4 fallback dirs
363 | foreach ($this->fallbackDirsPsr4 as $dir) {
364 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
365 | return $file;
366 | }
367 | }
368 |
369 | // PSR-0 lookup
370 | if (false !== $pos = strrpos($class, '\\')) {
371 | // namespaced class name
372 | $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
373 | . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
374 | } else {
375 | // PEAR-like class name
376 | $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
377 | }
378 |
379 | if (isset($this->prefixesPsr0[$first])) {
380 | foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
381 | if (0 === strpos($class, $prefix)) {
382 | foreach ($dirs as $dir) {
383 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
384 | return $file;
385 | }
386 | }
387 | }
388 | }
389 | }
390 |
391 | // PSR-0 fallback dirs
392 | foreach ($this->fallbackDirsPsr0 as $dir) {
393 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
394 | return $file;
395 | }
396 | }
397 |
398 | // PSR-0 include paths.
399 | if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
400 | return $file;
401 | }
402 | }
403 | }
404 |
405 | /**
406 | * Scope isolated include.
407 | *
408 | * Prevents access to $this/self from included files.
409 | */
410 | function includeFile($file)
411 | {
412 | include $file;
413 | }
414 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 | {description}
294 | Copyright (C) {year} {fullname}
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | {signature of Ty Coon}, 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
--------------------------------------------------------------------------------