Add ZK library classes for device interaction and user management

- Implemented Os class for retrieving OS information.
- Added Pin class for getting PIN width.
- Created Platform class for fetching platform details and version.
- Developed SerialNumber class to retrieve device serial number.
- Introduced Ssr class for SSR information retrieval.
- Implemented Time class for setting and getting device time.
- Added User class for user management including setting, getting, clearing, and removing users.
- Created Util class with various utility functions for command handling and data processing.
- Implemented Version class for fetching device version.
- Added WorkCode class for retrieving work code information.
- Set up Composer autoloading for the ZK library.
This commit is contained in:
2025-06-25 17:25:23 +07:00
commit 5bec63d9cd
39 changed files with 10171 additions and 0 deletions

347
zklib/ZKLib.php Normal file
View File

@ -0,0 +1,347 @@
<?php
require(__DIR__ . '/vendor/autoload.php');
use ZK\Util;
class ZKLib
{
public $_ip;
public $_port;
public $_zkclient;
public $_data_recv = '';
public $_session_id = 0;
public $_section = '';
/**
* ZKLib constructor.
* @param string $ip Device IP
* @param integer $port Default: 4370
*/
public function __construct($ip, $port = 4370)
{
$this->_ip = $ip;
$this->_port = $port;
$this->_zkclient = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
$timeout = ['sec' => 60, 'usec' => 500000];
socket_set_option($this->_zkclient, SOL_SOCKET, SO_RCVTIMEO, $timeout);
}
/**
* Create and send command to device
*
* @param string $command
* @param string $command_string
* @param string $type
* @return bool|mixed
*/
public function _command($command, $command_string, $type = Util::COMMAND_TYPE_GENERAL)
{
$chksum = 0;
$session_id = $this->_session_id;
$u = unpack('H2h1/H2h2/H2h3/H2h4/H2h5/H2h6/H2h7/H2h8', substr($this->_data_recv, 0, 8));
$reply_id = hexdec($u['h8'] . $u['h7']);
$buf = Util::createHeader($command, $chksum, $session_id, $reply_id, $command_string);
socket_sendto($this->_zkclient, $buf, strlen($buf), 0, $this->_ip, $this->_port);
try {
@socket_recvfrom($this->_zkclient, $this->_data_recv, 1024, 0, $this->_ip, $this->_port);
$u = unpack('H2h1/H2h2/H2h3/H2h4/H2h5/H2h6', substr($this->_data_recv, 0, 8));
$ret = false;
$session = hexdec($u['h6'] . $u['h5']);
if ($type === Util::COMMAND_TYPE_GENERAL && $session_id === $session) {
$ret = substr($this->_data_recv, 8);
} else if ($type === Util::COMMAND_TYPE_DATA && !empty($session)) {
$ret = $session;
}
return $ret;
} catch (ErrorException $e) {
return false;
} catch (Exception $e) {
return false;
}
}
/**
* Connect to device
*
* @return bool
*/
public function connect()
{
return (new ZK\Connect())->connect($this);
}
/**
* Disconnect from device
*
* @return bool
*/
public function disconnect()
{
return (new ZK\Connect())->disconnect($this);
}
/**
* Get device version
*
* @return bool|mixed
*/
public function version()
{
return (new ZK\Version())->get($this);
}
/**
* Get OS version
*
* @return bool|mixed
*/
public function osVersion()
{
return (new ZK\Os())->get($this);
}
/**
* Get platform
*
* @return bool|mixed
*/
public function platform()
{
return (new ZK\Platform())->get($this);
}
/**
* Get firmware version
*
* @return bool|mixed
*/
public function fmVersion()
{
return (new ZK\Platform())->getVersion($this);
}
/**
* Get work code
*
* @return bool|mixed
*/
public function workCode()
{
return (new ZK\WorkCode())->get($this);
}
/**
* Get SSR
*
* @return bool|mixed
*/
public function ssr()
{
return (new ZK\Ssr())->get($this);
}
/**
* Get pin width
*
* @return bool|mixed
*/
public function pinWidth()
{
return (new ZK\Pin())->width($this);
}
/**
* @return bool|mixed
*/
public function faceFunctionOn()
{
return (new ZK\Face())->on($this);
}
/**
* Get device serial number
*
* @return bool|mixed
*/
public function serialNumber()
{
return (new ZK\SerialNumber())->get($this);
}
/**
* Get device name
*
* @return bool|mixed
*/
public function deviceName()
{
return (new ZK\Device())->name($this);
}
/**
* Disable device
*
* @return bool|mixed
*/
public function disableDevice()
{
return (new ZK\Device())->disable($this);
}
/**
* Enable device
*
* @return bool|mixed
*/
public function enableDevice()
{
return (new ZK\Device())->enable($this);
}
/**
* Get users data
*
* @return array [userid, name, cardno, uid, role, password]
*/
public function getUser()
{
return (new ZK\User())->get($this);
}
/**
* Set user data
*
* @param int $uid Unique ID (max 65535)
* @param int|string $userid ID in DB (same like $uid, max length = 9, only numbers - depends device setting)
* @param string $name (max length = 24)
* @param int|string $password (max length = 8, only numbers - depends device setting)
* @param int $role Default Util::LEVEL_USER
* @param int $cardno Default 0 (max length = 10, only numbers)
* @return bool|mixed
*/
public function setUser($uid, $userid, $name, $password, $role = Util::LEVEL_USER, $cardno = 0)
{
return (new ZK\User())->set($this, $uid, $userid, $name, $password, $role, $cardno);
}
/**
* Get fingerprint data array by UID
* TODO: Can get data, but don't know how to parse the data. Need more documentation about it...
*
* @param integer $uid Unique ID (max 65535)
* @return array Binary fingerprint data array (where key is finger ID (0-9))
*/
public function getFingerprint($uid)
{
return (new ZK\Fingerprint())->get($this, $uid);
}
/**
* Set fingerprint data array
* TODO: Still can not set fingerprint. Need more documentation about it...
*
* @param integer $uid Unique ID (max 65535)
* @param array $data Binary fingerprint data array (where key is finger ID (0-9) same like returned array from 'getFingerprint' method)
* @return int Count of added fingerprints
*/
public function setFingerprint($uid, array $data)
{
return (new ZK\Fingerprint())->set($this, $uid, $data);
}
/**
* Remove fingerprint by UID and fingers ID array
*
* @param integer $uid Unique ID (max 65535)
* @param array $data Fingers ID array (0-9)
* @return int Count of deleted fingerprints
*/
public function removeFingerprint($uid, array $data)
{
return (new ZK\Fingerprint())->remove($this, $uid, $data);
}
/**
* Remove All users
*
* @return bool|mixed
*/
public function clearUsers()
{
return (new ZK\User())->clear($this);
}
/**
* Remove admin
*
* @return bool|mixed
*/
public function clearAdmin()
{
return (new ZK\User())->clearAdmin($this);
}
/**
* Remove user by UID
*
* @param integer $uid
* @return bool|mixed
*/
public function removeUser($uid)
{
return (new ZK\User())->remove($this, $uid);
}
/**
* Get attendance log
*
* @return array [uid, id, state, timestamp]
*/
public function getAttendance()
{
return (new ZK\Attendance())->get($this);
}
/**
* Clear attendance log
*
* @return bool|mixed
*/
public function clearAttendance()
{
return (new ZK\Attendance())->clear($this);
}
/**
* Set device time
*
* @param string $t Format: "Y-m-d H:i:s"
* @return bool|mixed
*/
public function setTime($t)
{
return (new ZK\Time())->set($this, $t);
}
/**
* Get device time
*
* @return bool|mixed Format: "Y-m-d H:i:s"
*/
public function getTime()
{
return (new ZK\Time())->get($this);
}
}

Binary file not shown.

2
zklib/logs/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/*
!/.gitignore

91
zklib/src/Attendance.php Normal file
View File

@ -0,0 +1,91 @@
<?php
namespace ZK;
use ZKLib;
class Attendance
{
/**
* @param ZKLib $self
* @return array [uid, id, state, timestamp]
*/
public function get(ZKLib $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_ATT_LOG_RRQ;
$command_string = '';
$session = $self->_command($command, $command_string, Util::COMMAND_TYPE_DATA);
if ($session === false) {
return [];
}
$attData = Util::recData($self);
$attendance = [];
if (!empty($attData)) {
$attData = substr($attData, 10);
while (strlen($attData) > 40) {
$u = unpack('H78', substr($attData, 0, 39));
$u1 = hexdec(substr($u[1], 4, 2));
$u2 = hexdec(substr($u[1], 6, 2));
$uid = $u1 + ($u2 * 256);
$id = hex2bin(substr($u[1], 8, 18));
$id = str_replace(chr(0), '', $id);
$state = hexdec(substr($u[1], 56, 2));
$timestamp = Util::decodeTime(hexdec(Util::reverseHex(substr($u[1], 58, 8))));
$type = hexdec(Util::reverseHex(substr($u[1], 66, 2 )));
// $timestamp_date = date('Y-m-d', $timestamp);
date_default_timezone_set('Asia/Jakarta');
$timestamp_date = date('Y-m-d', strtotime($timestamp));
$current_date = date('Y-m-d');
// if ($timestamp_date == $current_date) {
// $attendance[] = [
// 'uid' => $uid,
// 'id' => $id,
// 'state' => $state,
// 'timestamp' => $timestamp,
// 'type' => $type
// ];
// }
$attendance[] = [
'uid' => $uid,
'id' => $id,
'state' => $state,
'timestamp' => $timestamp,
'type' => $type
];
$attData = substr($attData, 40);
}
}
// var_dump($current_date);
// die();
return $attendance;
}
/**
* @param ZKLib $self
* @return bool|mixed
*/
public function clear(ZKLib $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_CLEAR_ATT_LOG;
$command_string = '';
return $self->_command($command, $command_string);
}
}

82
zklib/src/Connect.php Normal file
View File

@ -0,0 +1,82 @@
<?php
namespace ZK;
use ZKLib;
use ErrorException;
use Exception;
class Connect
{
/**
* @param ZKLib $self
* @return bool
*/
public function connect(ZKLib $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_CONNECT;
$command_string = '';
$chksum = 0;
$session_id = 0;
$reply_id = -1 + Util::USHRT_MAX;
$buf = Util::createHeader($command, $chksum, $session_id, $reply_id, $command_string);
socket_sendto($self->_zkclient, $buf, strlen($buf), 0, $self->_ip, $self->_port);
try {
@socket_recvfrom($self->_zkclient, $self->_data_recv, 1024, 0, $self->_ip, $self->_port);
if (strlen($self->_data_recv) > 0) {
$u = unpack('H2h1/H2h2/H2h3/H2h4/H2h5/H2h6', substr($self->_data_recv, 0, 8));
$session = hexdec($u['h6'] . $u['h5']);
if (empty($session)) {
return false;
}
$self->_session_id = $session;
return Util::checkValid($self->_data_recv);
} else {
return false;
}
} catch (ErrorException $e) {
return false;
} catch (Exception $e) {
return false;
}
}
/**
* @param ZKLib $self
* @return bool
*/
public function disconnect(ZKLib $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_EXIT;
$command_string = '';
$chksum = 0;
$session_id = $self->_session_id;
$u = unpack('H2h1/H2h2/H2h3/H2h4/H2h5/H2h6/H2h7/H2h8', substr($self->_data_recv, 0, 8));
$reply_id = hexdec($u['h8'] . $u['h7']);
$buf = Util::createHeader($command, $chksum, $session_id, $reply_id, $command_string);
socket_sendto($self->_zkclient, $buf, strlen($buf), 0, $self->_ip, $self->_port);
try {
@socket_recvfrom($self->_zkclient, $self->_data_recv, 1024, 0, $self->_ip, $self->_port);
$self->_session_id = 0;
return Util::checkValid($self->_data_recv);
} catch (ErrorException $e) {
return false;
} catch (Exception $e) {
return false;
}
}
}

50
zklib/src/Device.php Normal file
View File

@ -0,0 +1,50 @@
<?php
namespace ZK;
use ZKLib;
class Device
{
/**
* @param ZKLib $self
* @return bool|mixed
*/
public function name(ZKLib $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_DEVICE;
$command_string = '~DeviceName';
return $self->_command($command, $command_string);
}
/**
* @param ZKLib $self
* @return bool|mixed
*/
public function enable(ZKLib $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_ENABLE_DEVICE;
$command_string = '';
return $self->_command($command, $command_string);
}
/**
* @param ZKLib $self
* @return bool|mixed
*/
public function disable(ZKLib $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_DISABLE_DEVICE;
$command_string = chr(0) . chr(0);
return $self->_command($command, $command_string);
}
}

23
zklib/src/Face.php Normal file
View File

@ -0,0 +1,23 @@
<?php
namespace ZK;
use ZKLib;
class Face
{
/**
* @param ZKLib $self
* @return bool|mixed
*/
public function on(ZKLib $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_DEVICE;
$command_string = 'FaceFunOn';
return $self->_command($command, $command_string);
}
}

162
zklib/src/Fingerprint.php Normal file
View File

@ -0,0 +1,162 @@
<?php
namespace ZK;
use ZKLib;
class Fingerprint
{
/**
* TODO: Can get data, but don't know how to parse the data. Need more documentation about it...
*
* @param ZKLib $self
* @param integer $uid Unique Employee ID in ZK device
* @return array Binary fingerprint data array (where key is finger ID (0-9))
*/
public function get(ZKLib $self, $uid)
{
$self->_section = __METHOD__;
$data = [];
//fingers of the hands
for ($i = 0; $i <= 9; $i++) {
$tmp = $this->_getFinger($self, $uid, $i);
if ($tmp['size'] > 0) {
$data[$i] = $tmp['tpl'];
}
unset($tmp);
}
return $data;
}
/**
* @param ZKLib $self
* @param integer $uid Unique Employee ID in ZK device
* @param integer $finger Finger ID (0-9)
* @return array
*/
private function _getFinger(ZKLib $self, $uid, $finger)
{
$command = Util::CMD_USER_TEMP_RRQ;
$byte1 = chr((int)($uid % 256));
$byte2 = chr((int)($uid >> 8));
$command_string = $byte1 . $byte2 . chr($finger);
$ret = [
'size' => 0,
'tpl' => ''
];
$session = $self->_command($command, $command_string, Util::COMMAND_TYPE_DATA);
if ($session === false) {
return $ret;
}
$data = Util::recData($self, 10, false);
if (!empty($data)) {
$templateSize = strlen($data);
$prefix = chr($templateSize % 256) . chr(round($templateSize / 256)) . $byte1 . $byte2 . chr($finger) . chr(1);
$data = $prefix . $data;
if (strlen($templateSize) > 0) {
$ret['size'] = $templateSize;
$ret['tpl'] = $data;
}
}
return $ret;
}
/**
* TODO: Still can not set fingerprint. Need more documentation about it...
*
* @param ZKLib $self
* @param int $uid Unique Employee ID in ZK device
* @param array $data Binary fingerprint data array (where key is finger ID (0-9) same like returned array from 'get' method)
* @return int Count of added fingerprints
*/
public function set(ZKLib $self, $uid, array $data)
{
$self->_section = __METHOD__;
$count = 0;
foreach ($data as $finger => $item) {
$allowSet = true;
if ($this->_checkFinger($self, $uid, $finger) === true) {
$allowSet = $this->_removeFinger($self, $uid, $finger);
}
if ($allowSet === true && $this->_setFinger($self, $item) === true) {
$count++;
}
}
return $count;
}
/**
* @param ZKLib $self
* @param string $data Binary fingerprint data item
* @return bool|mixed
*/
private function _setFinger(ZKLib $self, $data)
{
$command = Util::CMD_USER_TEMP_WRQ;
$command_string = $data;
return $self->_command($command, $command_string);
}
/**
* @param ZKLib $self
* @param int $uid Unique Employee ID in ZK device
* @param array $data Fingers ID array (0-9)
* @return int Count of deleted fingerprints
*/
public function remove(ZKLib $self, $uid, array $data)
{
$self->_section = __METHOD__;
$count = 0;
foreach ($data as $finger) {
if ($this->_checkFinger($self, $uid, $finger) === true) {
if ($this->_removeFinger($self, $uid, $finger) === true) {
$count++;
}
}
}
return $count;
}
/**
* @param ZKLib $self
* @param int $uid Unique Employee ID in ZK device
* @param int $finger Finger ID (0-9)
* @return bool
*/
private function _removeFinger(ZKLib $self, $uid, $finger)
{
$command = Util::CMD_DELETE_USER_TEMP;
$byte1 = chr((int)($uid % 256));
$byte2 = chr((int)($uid >> 8));
$command_string = ($byte1 . $byte2) . chr($finger);
$self->_command($command, $command_string);
return !($this->_checkFinger($self, $uid, $finger));
}
/**
* @param ZKLib $self
* @param int $uid Unique Employee ID in ZK device
* @param int $finger Finger ID (0-9)
* @return bool Returned true if exist
*/
private function _checkFinger(ZKLib $self, $uid, $finger)
{
$res = $this->_getFinger($self, $uid, $finger);
return (bool)($res['size'] > 0);
}
}

22
zklib/src/Os.php Normal file
View File

@ -0,0 +1,22 @@
<?php
namespace ZK;
use ZKLib;
class Os
{
/**
* @param ZKLib $self
* @return bool|mixed
*/
public function get(ZKLib $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_DEVICE;
$command_string = '~OS';
return $self->_command($command, $command_string);
}
}

22
zklib/src/Pin.php Normal file
View File

@ -0,0 +1,22 @@
<?php
namespace ZK;
use ZKLib;
class Pin
{
/**
* @param ZKLib $self
* @return bool|mixed
*/
public function width(ZKLib $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_DEVICE;
$command_string = '~PIN2Width';
return $self->_command($command, $command_string);
}
}

36
zklib/src/Platform.php Normal file
View File

@ -0,0 +1,36 @@
<?php
namespace ZK;
use ZKLib;
class Platform
{
/**
* @param ZKLib $self
* @return bool|mixed
*/
public function get(ZKLib $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_DEVICE;
$command_string = '~Platform';
return $self->_command($command, $command_string);
}
/**
* @param ZKLib $self
* @return bool|mixed
*/
public function getVersion(ZKLib $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_DEVICE;
$command_string = '~ZKFPVersion';
return $self->_command($command, $command_string);
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace ZK;
use ZKLib;
class SerialNumber
{
/**
* @param ZKLib $self
* @return bool|mixed
*/
public function get(ZKLib $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_DEVICE;
$command_string = '~SerialNumber';
return $self->_command($command, $command_string);
}
}

22
zklib/src/Ssr.php Normal file
View File

@ -0,0 +1,22 @@
<?php
namespace ZK;
use ZKLib;
class Ssr
{
/**
* @param ZKLib $self
* @return bool|mixed
*/
public function get(ZKLib $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_DEVICE;
$command_string = '~SSR';
return $self->_command($command, $command_string);
}
}

43
zklib/src/Time.php Normal file
View File

@ -0,0 +1,43 @@
<?php
namespace ZK;
use ZKLib;
class Time
{
/**
* @param ZKLib $self
* @param string $t Format: "Y-m-d H:i:s"
* @return bool|mixed
*/
public function set(ZKLib $self, $t)
{
$self->_section = __METHOD__;
$command = Util::CMD_SET_TIME;
$command_string = pack('I', Util::encodeTime($t));
return $self->_command($command, $command_string);
}
/**
* @param ZKLib $self
* @return bool|mixed
*/
public function get(ZKLib $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_GET_TIME;
$command_string = '';
$ret = $self->_command($command, $command_string);
if ($ret) {
return Util::decodeTime(hexdec(Util::reverseHex(bin2hex($ret))));
} else {
return false;
}
}
}

161
zklib/src/User.php Normal file
View File

@ -0,0 +1,161 @@
<?php
namespace ZK;
use ZKLib;
class User
{
/**
* @param ZKLib $self
* @param int $uid Unique ID (max 65535)
* @param int|string $userid (max length = 9, only numbers - depends device setting)
* @param string $name (max length = 24)
* @param int|string $password (max length = 8, only numbers - depends device setting)
* @param int $role Default Util::LEVEL_USER
* @param int $cardno Default 0 (max length = 10, only numbers)
* @return bool|mixed
*/
public function set(ZKLib $self, $uid, $userid, $name, $password, $role = Util::LEVEL_USER, $cardno = 0)
{
$self->_section = __METHOD__;
if (
(int)$uid === 0 ||
(int)$uid > Util::USHRT_MAX ||
strlen($userid) > 9 ||
strlen($name) > 24 ||
strlen($password) > 8 ||
strlen($cardno) > 10
) {
return false;
}
$command = Util::CMD_SET_USER;
$byte1 = chr((int)($uid % 256));
$byte2 = chr((int)($uid >> 8));
$cardno = hex2bin(Util::reverseHex(dechex($cardno)));
$command_string = implode('', [
$byte1,
$byte2,
chr($role),
str_pad($password, 8, chr(0)),
str_pad($name, 24, chr(0)),
str_pad($cardno, 4, chr(0)),
str_pad(chr(1), 9, chr(0)),
str_pad($userid, 9, chr(0)),
str_repeat(chr(0), 15)
]);
return $self->_command($command, $command_string);
}
/**
* @param ZKLib $self
* @return array [userid, name, cardno, uid, role, password]
*/
public function get(ZKLib $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_USER_TEMP_RRQ;
$command_string = chr(Util::FCT_USER);
$session = $self->_command($command, $command_string, Util::COMMAND_TYPE_DATA);
if ($session === false) {
return [];
}
$userData = Util::recData($self);
$users = [];
if (!empty($userData)) {
$userData = substr($userData, 11);
while (strlen($userData) > 72) {
$u = unpack('H144', substr($userData, 0, 72));
$u1 = hexdec(substr($u[1], 2, 2));
$u2 = hexdec(substr($u[1], 4, 2));
$uid = $u1 + ($u2 * 256);
$cardno = hexdec(substr($u[1], 78, 2) . substr($u[1], 76, 2) . substr($u[1], 74, 2) . substr($u[1], 72, 2)) . ' ';
$role = hexdec(substr($u[1], 6, 2)) . ' ';
$password = hex2bin(substr($u[1], 8, 16)) . ' ';
$name = hex2bin(substr($u[1], 24, 74)) . ' ';
$userid = hex2bin(substr($u[1], 98, 72)) . ' ';
//Clean up some messy characters from the user name
$password = explode(chr(0), $password, 2);
$password = $password[0];
$userid = explode(chr(0), $userid, 2);
$userid = $userid[0];
$name = explode(chr(0), $name, 3);
$name = utf8_encode($name[0]);
$cardno = str_pad($cardno, 11, '0', STR_PAD_LEFT);
if ($name == '') {
$name = $userid;
}
$users[$userid] = [
'userid' => $userid,
'name' => $name,
'cardno' => $cardno,
'uid' => $uid,
'role' => intval($role),
'password' => $password
];
$userData = substr($userData, 72);
}
}
return $users;
}
/**
* @param ZKLib $self
* @return bool|mixed
*/
public function clear(ZKLib $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_CLEAR_DATA;
$command_string = '';
return $self->_command($command, $command_string);
}
/**
* @param ZKLib $self
* @return bool|mixed
*/
public function clearAdmin(ZKLib $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_CLEAR_ADMIN;
$command_string = '';
return $self->_command($command, $command_string);
}
/**
* @param ZKLib $self
* @param integer $uid
* @return bool|mixed
*/
public function remove(ZKLib $self, $uid)
{
$self->_section = __METHOD__;
$command = Util::CMD_DELETE_USER;
$byte1 = chr((int)($uid % 256));
$byte2 = chr((int)($uid >> 8));
$command_string = ($byte1 . $byte2);
return $self->_command($command, $command_string);
}
}

415
zklib/src/Util.php Normal file
View File

@ -0,0 +1,415 @@
<?php
namespace ZK;
use ZKLib;
class Util
{
const USHRT_MAX = 65535;
const CMD_CONNECT = 1000; # Connections requests
const CMD_EXIT = 1001; # Disconnection requests
const CMD_ENABLE_DEVICE = 1002; # Ensure the machine to be at the normal work condition
const CMD_DISABLE_DEVICE = 1003; # Make the machine to be at the shut-down condition, generally demonstrates in the work ...on LCD
const CMD_ACK_OK = 2000; # Return value for order perform successfully
const CMD_ACK_ERROR = 2001; # Return value for order perform failed
const CMD_ACK_DATA = 2002; # Return data
const CMD_ACK_UNAUTH = 2005; # Connection unauthorized
const CMD_PREPARE_DATA = 1500; # Prepares to transmit the data
const CMD_DATA = 1501; # Transmit a data packet
const CMD_FREE_DATA = 1502; # Clear machines open buffer
const CMD_USER_TEMP_RRQ = 9; # Read some fingerprint template or some kind of data entirely
const CMD_ATT_LOG_RRQ = 13; # Read all attendance record
const CMD_CLEAR_DATA = 14; # Clear Data
const CMD_CLEAR_ATT_LOG = 15; # Clear attendance records
const CMD_GET_TIME = 201; # Obtain the machine time
const CMD_SET_TIME = 202; # Set machines time
const CMD_VERSION = 1100; # Obtain the firmware edition
const CMD_DEVICE = 11; # Read in the machine some configuration parameter
const CMD_SET_USER = 8; # Upload the user information (from PC to terminal).
const CMD_USER_TEMP_WRQ = 10; # Upload some fingerprint template
const CMD_DELETE_USER = 18; # Delete some user
const CMD_DELETE_USER_TEMP = 19; # Delete some fingerprint template
const CMD_CLEAR_ADMIN = 20; # Cancel the manager
const LEVEL_USER = 0;
const LEVEL_ADMIN = 14;
const FCT_ATTLOG = 1;
const FCT_WORKCODE = 8;
const FCT_FINGERTMP = 2;
const FCT_OPLOG = 4;
const FCT_USER = 5;
const FCT_SMS = 6;
const FCT_UDATA = 7;
const COMMAND_TYPE_GENERAL = 'general';
const COMMAND_TYPE_DATA = 'data';
const ATT_STATE_FINGERPRINT = 1;
const ATT_STATE_PASSWORD = 0;
const ATT_STATE_CARD = 2;
const ATT_TYPE_CHECK_IN = 0;
const ATT_TYPE_CHECK_OUT = 1;
const ATT_TYPE_OVERTIME_IN = 4;
const ATT_TYPE_OVERTIME_OUT = 5;
/**
* Encode a timestamp send at the timeclock
* copied from zkemsdk.c - EncodeTime
*
* @param string $t Format: "Y-m-d H:i:s"
* @return int
*/
static public function encodeTime($t)
{
$timestamp = strtotime($t);
$t = (object)[
'year' => (int)date('Y', $timestamp),
'month' => (int)date('m', $timestamp),
'day' => (int)date('d', $timestamp),
'hour' => (int)date('H', $timestamp),
'minute' => (int)date('i', $timestamp),
'second' => (int)date('s', $timestamp),
];
$d = (($t->year % 100) * 12 * 31 + (($t->month - 1) * 31) + $t->day - 1) *
(24 * 60 * 60) + ($t->hour * 60 + $t->minute) * 60 + $t->second;
return $d;
}
/**
* Decode a timestamp retrieved from the timeclock
* copied from zkemsdk.c - DecodeTime
*
* @param int|string $t
* @return false|string Format: "Y-m-d H:i:s"
*/
static public function decodeTime($t)
{
$second = $t % 60;
$t = $t / 60;
$minute = $t % 60;
$t = $t / 60;
$hour = $t % 24;
$t = $t / 24;
$day = $t % 31 + 1;
$t = $t / 31;
$month = $t % 12 + 1;
$t = $t / 12;
$year = floor($t + 2000);
$d = date('Y-m-d H:i:s', strtotime(
$year . '-' . $month . '-' . $day . ' ' . $hour . ':' . $minute . ':' . $second
));
return $d;
}
/**
* @param string $hex
* @return string
*/
static public function reverseHex($hex)
{
$tmp = '';
for ($i = strlen($hex); $i >= 0; $i--) {
$tmp .= substr($hex, $i, 2);
$i--;
}
return $tmp;
}
/**
* Checks a returned packet to see if it returned self::CMD_PREPARE_DATA,
* indicating that data packets are to be sent
* Returns the amount of bytes that are going to be sent
*
* @param ZKLib $self
* @return bool|number
*/
static public function getSize(ZKLib $self)
{
$u = unpack('H2h1/H2h2/H2h3/H2h4/H2h5/H2h6/H2h7/H2h8', substr($self->_data_recv, 0, 8));
$command = hexdec($u['h2'] . $u['h1']);
if ($command == self::CMD_PREPARE_DATA) {
$u = unpack('H2h1/H2h2/H2h3/H2h4', substr($self->_data_recv, 8, 4));
$size = hexdec($u['h4'] . $u['h3'] . $u['h2'] . $u['h1']);
return $size;
} else {
return false;
}
}
/**
* This function calculates the chksum of the packet to be sent to the
* time clock
* Copied from zkemsdk.c
*
* @inheritdoc
*/
static public function createChkSum($p)
{
$l = count($p);
$chksum = 0;
$i = $l;
$j = 1;
while ($i > 1) {
$u = unpack('S', pack('C2', $p['c' . $j], $p['c' . ($j + 1)]));
$chksum += $u[1];
if ($chksum > self::USHRT_MAX) {
$chksum -= self::USHRT_MAX;
}
$i -= 2;
$j += 2;
}
if ($i) {
$chksum = $chksum + $p['c' . strval(count($p))];
}
while ($chksum > self::USHRT_MAX) {
$chksum -= self::USHRT_MAX;
}
if ($chksum > 0) {
$chksum = -($chksum);
} else {
$chksum = abs($chksum);
}
$chksum -= 1;
while ($chksum < 0) {
$chksum += self::USHRT_MAX;
}
return pack('S', $chksum);
}
/**
* This function puts a the parts that make up a packet together and
* packs them into a byte string
*
* @inheritdoc
*/
static public function createHeader($command, $chksum, $session_id, $reply_id, $command_string)
{
$buf = pack('SSSS', $command, $chksum, $session_id, $reply_id) . $command_string;
$buf = unpack('C' . (8 + strlen($command_string)) . 'c', $buf);
$u = unpack('S', self::createChkSum($buf));
if (is_array($u)) {
$u = reset($u);
}
$chksum = $u;
$reply_id += 1;
if ($reply_id >= self::USHRT_MAX) {
$reply_id -= self::USHRT_MAX;
}
$buf = pack('SSSS', $command, $chksum, $session_id, $reply_id);
return $buf . $command_string;
}
/**
* Checks a returned packet to see if it returned Util::CMD_ACK_OK,
* indicating success
*
* @inheritdoc
*/
static public function checkValid($reply)
{
$u = unpack('H2h1/H2h2', substr($reply, 0, 8));
$command = hexdec($u['h2'] . $u['h1']);
/** TODO: Some device can return 'Connection unauthorized' then should check also */
if ($command == self::CMD_ACK_OK || $command == self::CMD_ACK_UNAUTH) {
return true;
} else {
return false;
}
}
/**
* Get User Role string
* @param integer $role
* @return string
*/
static public function getUserRole($role)
{
switch ($role) {
case self::LEVEL_USER:
$ret = 'User';
break;
case self::LEVEL_ADMIN:
$ret = 'Admin';
break;
default:
$ret = 'Unknown';
}
return $ret;
}
/**
* Get Attendance State string
* @param integer $state
* @return string
*/
static public function getAttState($state)
{
switch ($state) {
case self::ATT_STATE_FINGERPRINT:
$ret = 'Fingerprint';
break;
case self::ATT_STATE_PASSWORD:
$ret = 'Password';
break;
case self::ATT_STATE_CARD:
$ret = 'Card';
break;
default:
$ret = 'Unknown';
}
return $ret;
}
/**
* Get Attendance Type string
* @param integer $type
* @return string
*/
static public function getAttType($type)
{
switch ($type) {
case self::ATT_TYPE_CHECK_IN:
$ret = 'Check-in';
break;
case self::ATT_TYPE_CHECK_OUT:
$ret = 'Check-out';
break;
case self::ATT_TYPE_OVERTIME_IN:
$ret = 'Overtime-in';
break;
case self::ATT_TYPE_OVERTIME_OUT:
$ret = 'Overtime-out';
break;
default:
$ret = 'Undefined';
}
return $ret;
}
/**
* Receive data from device
* @param ZKLib $self
* @param int $maxErrors
* @param bool $first if 'true' don't remove first 4 bytes for first row
* @return string
*/
static public function recData(ZKLib $self, $maxErrors = 10, $first = true)
{
$data = '';
$bytes = self::getSize($self);
if ($bytes) {
$received = 0;
$errors = 0;
while ($bytes > $received) {
$ret = @socket_recvfrom($self->_zkclient, $dataRec, 1032, 0, $self->_ip, $self->_port);
if ($ret === false) {
if ($errors < $maxErrors) {
//try again if false
$errors++;
sleep(1);
continue;
} else {
//return empty if has maximum count of errors
self::logReceived($self, $received, $bytes);
unset($data);
return '';
}
}
if ($first === false) {
//The first 4 bytes don't seem to be related to the user
$dataRec = substr($dataRec, 8);
}
$data .= $dataRec;
$received += strlen($dataRec);
unset($dataRec);
$first = false;
}
//flush socket
@socket_recvfrom($self->_zkclient, $dataRec, 1024, 0, $self->_ip, $self->_port);
unset($dataRec);
}
return $data;
}
/**
* @param ZKLib $self
* @param int $received
* @param int $bytes
*/
static private function logReceived(ZKLib $self, $received, $bytes)
{
self::logger($self, 'Received: ' . $received . ' of ' . $bytes . ' bytes');
}
/**
* Write log
* @param ZKLib $self
* @param string $str
*/
static private function logger(ZKLib $self, $str)
{
if (defined('ZK_LIB_LOG')) {
//use constant if defined
$log = ZK_LIB_LOG;
} else {
$dir = dirname(dirname(__FILE__));
$log = $dir . '/logs/error.log';
}
$row = '<' . $self->_ip . '> [' . date('d.m.Y H:i:s') . '] ';
$row .= (empty($self->_section) ? '' : '(' . $self->_section . ') ');
$row .= $str;
$row .= PHP_EOL;
file_put_contents($log, $row, FILE_APPEND);
}
}

22
zklib/src/Version.php Normal file
View File

@ -0,0 +1,22 @@
<?php
namespace ZK;
use ZKLib;
class Version
{
/**
* @param ZKLib $self
* @return bool|mixed
*/
public function get(ZKLib $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_VERSION;
$command_string = '';
return $self->_command($command, $command_string);
}
}

22
zklib/src/WorkCode.php Normal file
View File

@ -0,0 +1,22 @@
<?php
namespace ZK;
use ZKLib;
class WorkCode
{
/**
* @param ZKLib $self
* @return bool|mixed
*/
public function get(ZKLib $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_DEVICE;
$command_string = 'WorkCode';
return $self->_command($command, $command_string);
}
}

7
zklib/vendor/autoload.php vendored Normal file
View File

@ -0,0 +1,7 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer' . '/autoload_real.php';
return ComposerAutoloaderInit20e92c6b69b73d8b1e592132f9d729fb::getLoader();

413
zklib/vendor/composer/ClassLoader.php vendored Normal file
View File

@ -0,0 +1,413 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see http://www.php-fig.org/psr/psr-0/
* @see http://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
// PSR-4
private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
private $fallbackDirsPsr4 = array();
// PSR-0
private $prefixesPsr0 = array();
private $fallbackDirsPsr0 = array();
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoritative = false;
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
}
return array();
}
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
/**
* Unregisters this instance as an autoloader.
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
if ('\\' == $class[0]) {
$class = substr($class, 1);
}
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative) {
return false;
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if ($file === null && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if ($file === null) {
// Remember that this class does not exist.
return $this->classMap[$class] = false;
}
return $file;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
if (0 === strpos($class, $prefix)) {
foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}

View File

@ -0,0 +1,9 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

View File

@ -0,0 +1,9 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

10
zklib/vendor/composer/autoload_psr4.php vendored Normal file
View File

@ -0,0 +1,10 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'ZK\\' => array($baseDir . '/src'),
);

45
zklib/vendor/composer/autoload_real.php vendored Normal file
View File

@ -0,0 +1,45 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit20e92c6b69b73d8b1e592132f9d729fb
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit20e92c6b69b73d8b1e592132f9d729fb', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit20e92c6b69b73d8b1e592132f9d729fb', 'loadClassLoader'));
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
$loader->register(true);
return $loader;
}
}