adding biometric device

This commit is contained in:
Flatlogic Bot 2026-04-04 07:17:13 +00:00
parent 79a5056e8e
commit 040f45e79d
45 changed files with 5512 additions and 8 deletions

87
api/iclock/cdata Normal file
View File

@ -0,0 +1,87 @@
<?php
require_once __DIR__ . '/../../db/config.php';
$method = $_SERVER['REQUEST_METHOD'];
if ($method === 'GET') {
$sn = $_GET['SN'] ?? '';
if (empty($sn)) {
http_response_code(400);
exit('Error: SN missing');
}
// Respond to device init
echo "GET OPTION FROM: {$sn}\n";
echo "Stamp=9999\n";
echo "OpStamp=9999\n";
echo "ErrorDelay=60\n";
echo "Delay=30\n";
echo "TransTimes=00:00;14:00\n";
echo "TransInterval=1\n";
echo "TransFlag=1111000000\n";
echo "TimeZone=74\n"; // local timezone config
echo "Realtime=1\n";
echo "Encrypt=0\n";
exit;
} elseif ($method === 'POST') {
$sn = $_GET['SN'] ?? '';
$table = $_GET['table'] ?? '';
if ($table === 'ATTLOG') {
$body = file_get_contents('php://input');
$lines = explode("\n", trim($body));
$empStmt = db()->query("SELECT id, zkteco_uid FROM hr_employees WHERE zkteco_uid IS NOT NULL AND zkteco_uid != '' AND status = 'active'");
$empMap = [];
while ($emp = $empStmt->fetch()) {
$empMap[(string)$emp['zkteco_uid']] = $emp['id'];
}
$insertedCount = 0;
foreach ($lines as $line) {
$line = trim($line);
if (empty($line)) continue;
$parts = explode("\t", $line);
if (count($parts) >= 2) {
$uid = trim($parts[0]);
$datetime = trim($parts[1]);
if (strlen($datetime) >= 19 && isset($empMap[$uid])) {
$emp_id = $empMap[$uid];
$date = substr($datetime, 0, 10);
$time = substr($datetime, 11, 8);
$chkStmt = db()->prepare("SELECT id, check_in, check_out FROM hr_attendance WHERE employee_id = ? AND date = ?");
$chkStmt->execute([$emp_id, $date]);
$existing = $chkStmt->fetch();
if ($existing) {
$upd_in = $existing['check_in'];
$upd_out = $existing['check_out'];
$changed = false;
if (empty($upd_in) || $time < $upd_in) { $upd_in = $time; $changed = true; }
if ($time > $upd_in && (empty($upd_out) || $time > $upd_out)) { $upd_out = $time; $changed = true; }
if ($changed) {
$uStmt = db()->prepare("UPDATE hr_attendance SET check_in = ?, check_out = ?, status = 'present' WHERE id = ?");
$uStmt->execute([$upd_in, $upd_out, $existing['id']]);
}
} else {
$iStmt = db()->prepare("INSERT INTO hr_attendance (employee_id, date, check_in, status) VALUES (?, ?, ?, 'present')");
$iStmt->execute([$emp_id, $date, $time]);
}
$insertedCount++;
}
}
}
echo "OK: " . $insertedCount;
exit;
} else {
// Acknowledge OPERLOG or other tables to keep device happy
echo "OK";
exit;
}
}

4
api/iclock/devicecmd Normal file
View File

@ -0,0 +1,4 @@
<?php
// ZKTeco devices return the result of executing commands here.
// We just acknowledge.
echo "OK";

4
api/iclock/getrequest Normal file
View File

@ -0,0 +1,4 @@
<?php
// ZKTeco devices call this to ask the server if there are any pending commands.
// We just return OK so it doesn't get stuck.
echo "OK";

1788
composer-setup.php Normal file

File diff suppressed because it is too large Load Diff

5
composer.json Normal file
View File

@ -0,0 +1,5 @@
{
"require": {
"rats/zkteco": "^002.0"
}
}

56
composer.lock generated Normal file
View File

@ -0,0 +1,56 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "b0f94fedb2e61e4b370f9be3a60b2340",
"packages": [
{
"name": "rats/zkteco",
"version": "V002",
"source": {
"type": "git",
"url": "https://github.com/raihanafroz/zkteco.git",
"reference": "6e17024370ec56c89d87c85a54a1c55079304834"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/raihanafroz/zkteco/zipball/6e17024370ec56c89d87c85a54a1c55079304834",
"reference": "6e17024370ec56c89d87c85a54a1c55079304834",
"shasum": ""
},
"type": "library",
"autoload": {
"psr-4": {
"Rats\\Zkteco\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Raihan Afroz",
"email": "raihanafroz9@gmail.com"
}
],
"description": "ZKTeco Laravel Library",
"support": {
"issues": "https://github.com/raihanafroz/zkteco/issues",
"source": "https://github.com/raihanafroz/zkteco/tree/V002"
},
"time": "2021-11-21T17:53:28+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {},
"platform-dev": {},
"plugin-api-version": "2.9.0"
}

BIN
composer.phar Executable file

Binary file not shown.

View File

@ -0,0 +1,12 @@
CREATE TABLE IF NOT EXISTS hr_zkteco_settings (
id INT AUTO_INCREMENT PRIMARY KEY,
ip_address VARCHAR(100) NOT NULL,
port INT DEFAULT 4370,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
INSERT INTO hr_zkteco_settings (ip_address, port)
SELECT '192.168.1.201', 4370
WHERE NOT EXISTS (SELECT 1 FROM hr_zkteco_settings);
ALTER TABLE hr_employees ADD COLUMN zkteco_uid VARCHAR(50) NULL DEFAULT NULL AFTER id;

View File

@ -71,7 +71,7 @@ $records = $stmt->fetchAll();
?>
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">سجل الحضور والانصراف</h1>
<h1 class="h2">سجل الحضور والانصراف <a href="hr_zkteco.php" class="btn btn-sm btn-outline-info ms-3"><i class="fas fa-fingerprint me-1"></i> ربط البصمة ZKTeco</a></h1>
<div class="btn-toolbar mb-2 mb-md-0">
<form class="d-flex gap-2 align-items-center" method="get">
<label class="col-form-label">التاريخ:</label>

View File

@ -19,7 +19,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$error = "لا تملك صلاحية التعديل.";
} else {
$id = !empty($_POST['id']) ? $_POST['id'] : null;
$first_name = trim($_POST['first_name']);
$first_name = trim($_POST["first_name"]);
$zkteco_uid = !empty($_POST["zkteco_uid"]) ? trim($_POST["zkteco_uid"]) : null;
$last_name = trim($_POST['last_name']);
$email = trim($_POST['email']);
$phone = trim($_POST['phone']);
@ -37,13 +38,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
try {
if ($id) {
// Update
$stmt = db()->prepare("UPDATE hr_employees SET first_name=?, last_name=?, email=?, phone=?, department_id=?, job_title=?, basic_salary=?, join_date=?, status=?, gender=?, birth_date=? WHERE id=?");
$stmt->execute([$first_name, $last_name, $email, $phone, $department_id, $job_title, $basic_salary, $join_date, $status, $gender, $birth_date, $id]);
$stmt = db()->prepare("UPDATE hr_employees SET first_name=?, last_name=?, email=?, phone=?, department_id=?, job_title=?, basic_salary=?, join_date=?, status=?, gender=?, birth_date=?, zkteco_uid=? WHERE id=?");
$stmt->execute([$first_name, $last_name, $email, $phone, $department_id, $job_title, $basic_salary, $join_date, $status, $gender, $birth_date, $zkteco_uid, $id]);
$success = "تم تحديث بيانات الموظف بنجاح.";
} else {
// Insert
$stmt = db()->prepare("INSERT INTO hr_employees (first_name, last_name, email, phone, department_id, job_title, basic_salary, join_date, status, gender, birth_date) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$first_name, $last_name, $email, $phone, $department_id, $job_title, $basic_salary, $join_date, $status, $gender, $birth_date]);
$stmt = db()->prepare("INSERT INTO hr_employees (first_name, last_name, email, phone, department_id, job_title, basic_salary, join_date, status, gender, birth_date, zkteco_uid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$first_name, $last_name, $email, $phone, $department_id, $job_title, $basic_salary, $join_date, $status, $gender, $birth_date, $zkteco_uid]);
$success = "تم إضافة الموظف بنجاح.";
}
} catch (PDOException $e) {
@ -207,7 +208,10 @@ $pagination = getPagination($page, $totalEmployees, $perPage);
</div>
<div class="col-md-6">
<label class="form-label">الراتب الأساسي</label>
<input type="number" step="0.01" name="basic_salary" id="empSalary" class="form-control">
<input type="number" step="0.01" name="basic_salary" id="empSalary" class="form-control"></div>
<div class="col-md-6">
<label class="form-label">ZKTeco UID</label>
<input type="text" name="zkteco_uid" id="empZktecoUid" class="form-control">
</div>
<div class="col-md-6">
@ -242,6 +246,7 @@ $pagination = getPagination($page, $totalEmployees, $perPage);
<th>المسمى الوظيفي</th>
<th>تاريخ التعيين</th>
<th>الحالة</th>
<th>بصمة (UID)</th>
<th>الإجراءات</th>
</tr>
</thead>
@ -286,6 +291,7 @@ $pagination = getPagination($page, $totalEmployees, $perPage);
?>
<span class="badge bg-<?= $status_cls ?>"><?= htmlspecialchars($row['status']) ?></span>
</td>
<td><span class="badge bg-light text-dark border"><?= htmlspecialchars($row['zkteco_uid'] ?: '-') ?></span></td>
<td>
<div class="btn-group">
<?php if (canEdit('hr_employees')): ?>
@ -346,7 +352,8 @@ $pagination = getPagination($page, $totalEmployees, $perPage);
document.getElementById('empDept').value = btn.dataset.dept;
document.getElementById('empJobTitle').value = btn.dataset.job;
document.getElementById('empJoinDate').value = btn.dataset.join;
document.getElementById('empSalary').value = btn.dataset.salary;
document.getElementById("empSalary").value = btn.dataset.salary;
document.getElementById("empZktecoUid").value = btn.dataset.zkteco;
document.getElementById('empStatus').value = btn.dataset.status;
}
</script>

283
hr_zkteco.php Normal file
View File

@ -0,0 +1,283 @@
<?php
require_once 'includes/header.php';
require 'vendor/autoload.php';
use Rats\Zkteco\Lib\ZKTeco;
if (!isAdmin()) {
echo "<div class='alert alert-danger'>ليس لديك صلاحية للوصول إلى هذه الصفحة.</div>";
require_once 'includes/footer.php';
exit;
}
$error = '';
$success = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['save_settings'])) {
$ip = trim($_POST['ip_address']);
$port = (int)$_POST['port'];
if ($ip && $port) {
$stmt = db()->prepare("UPDATE hr_zkteco_settings SET ip_address = ?, port = ? WHERE id = 1");
$stmt->execute([$ip, $port]);
$success = "تم حفظ الإعدادات بنجاح.";
} else {
$error = "يرجى تعبئة الحقول المطلوبة.";
}
}
$stmt = db()->query("SELECT * FROM hr_zkteco_settings LIMIT 1");
$settings = $stmt->fetch();
$ip = $settings['ip_address'] ?? '192.168.1.201';
$port = $settings['port'] ?? 4370;
if (isset($_POST['test_connection'])) {
try {
$zk = new ZKTeco($ip, $port);
if ($zk->connect()) {
$success = "تم الاتصال بالجهاز بنجاح (الإصدار: " . htmlspecialchars($zk->version() ?? 'غير معروف') . ")";
$zk->disconnect();
} else {
$error = "فشل الاتصال بالجهاز. يرجى التأكد من أن الجهاز على نفس الشبكة والمنفذ مفتوح (عادة 4370).";
}
} catch (Exception $e) {
$error = "خطأ أثناء الاتصال: " . $e->getMessage();
}
}
if (isset($_POST['sync_logs'])) {
try {
$zk = new ZKTeco($ip, $port);
if ($zk->connect()) {
$attendance = $zk->getAttendance();
$empStmt = db()->query("SELECT id, zkteco_uid FROM hr_employees WHERE zkteco_uid IS NOT NULL AND zkteco_uid != '' AND status = 'active'");
$empMap = [];
while ($emp = $empStmt->fetch()) {
$empMap[(string)$emp['zkteco_uid']] = $emp['id'];
}
$syncedCount = 0;
if (is_array($attendance)) {
$attData = [];
foreach ($attendance as $att) {
$uid = (string)$att['id'];
if (isset($empMap[$uid])) {
$emp_id = $empMap[$uid];
$timeStr = $att['timestamp'];
$date = date('Y-m-d', strtotime($timeStr));
$time = date('H:i:s', strtotime($timeStr));
if (!isset($attData[$emp_id][$date])) {
$attData[$emp_id][$date] = ['min' => $time, 'max' => $time];
} else {
if ($time < $attData[$emp_id][$date]['min']) $attData[$emp_id][$date]['min'] = $time;
if ($time > $attData[$emp_id][$date]['max']) $attData[$emp_id][$date]['max'] = $time;
}
}
}
foreach ($attData as $emp_id => $dates) {
foreach ($dates as $date => $times) {
$check_in = $times['min'];
$check_out = ($times['max'] != $times['min']) ? $times['max'] : null;
$chkStmt = db()->prepare("SELECT id, check_in, check_out FROM hr_attendance WHERE employee_id = ? AND date = ?");
$chkStmt->execute([$emp_id, $date]);
$existing = $chkStmt->fetch();
if ($existing) {
$upd_in = $existing['check_in'];
$upd_out = $existing['check_out'];
$changed = false;
if (empty($upd_in) || $check_in < $upd_in) { $upd_in = $check_in; $changed = true; }
if ($check_out && (empty($upd_out) || $check_out > $upd_out)) { $upd_out = $check_out; $changed = true; }
if ($changed) {
$uStmt = db()->prepare("UPDATE hr_attendance SET check_in = ?, check_out = ?, status = 'present' WHERE id = ?");
$uStmt->execute([$upd_in, $upd_out, $existing['id']]);
$syncedCount++;
}
} else {
$iStmt = db()->prepare("INSERT INTO hr_attendance (employee_id, date, check_in, check_out, status) VALUES (?, ?, ?, ?, 'present')");
$iStmt->execute([$emp_id, $date, $check_in, $check_out]);
$syncedCount++;
}
}
}
}
$zk->disconnect();
if ($syncedCount > 0) {
$success = "تم مزامنة $syncedCount سجل حضور (تحديث / إنشاء) بنجاح.";
} else {
$success = "تم الاتصال بنجاح ولكن لم يتم العثور على سجلات جديدة للموظفين المعرفين.";
}
} else {
$error = "فشل الاتصال بالجهاز.";
}
} catch (Exception $e) {
$error = "حدث خطأ أثناء المزامنة: " . $e->getMessage();
}
}
} else {
$stmt = db()->query("SELECT * FROM hr_zkteco_settings LIMIT 1");
$settings = $stmt->fetch();
$ip = $settings['ip_address'] ?? '192.168.1.201';
$port = $settings['port'] ?? 4370;
}
?>
<div class="container-fluid py-4">
<div class="row mb-4">
<div class="col-md-8">
<h2 class="h3 mb-0 text-gray-800">ربط جهاز البصمة ZKTeco</h2>
<p class="text-muted">اختر طريقة الربط المناسبة للشبكة الخاصة بك لسحب سجلات الحضور.</p>
</div>
<div class="col-md-4 text-end">
<a href="hr_attendance.php" class="btn btn-secondary"><i class="fas fa-arrow-left"></i> العودة لسجل الحضور</a>
</div>
</div>
<?php if ($error): ?>
<div class="alert alert-danger shadow-sm border-0 border-start border-danger border-4 alert-dismissible fade show" role="alert">
<i class="fas fa-exclamation-circle me-2"></i> <?= htmlspecialchars($error) ?>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php endif; ?>
<?php if ($success): ?>
<div class="alert alert-success shadow-sm border-0 border-start border-success border-4 alert-dismissible fade show" role="alert">
<i class="fas fa-check-circle me-2"></i> <?= htmlspecialchars($success) ?>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php endif; ?>
<!-- Nav tabs -->
<ul class="nav nav-tabs mb-4" id="zktecoTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active fw-bold text-dark" id="pull-tab" data-bs-toggle="tab" data-bs-target="#pull" type="button" role="tab" aria-controls="pull" aria-selected="true">
<i class="fas fa-network-wired me-1 text-primary"></i> الطريقة 1: الاتصال المباشر (TCP Pull)
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link fw-bold text-dark" id="push-tab" data-bs-toggle="tab" data-bs-target="#push" type="button" role="tab" aria-controls="push" aria-selected="false">
<i class="fas fa-cloud-upload-alt me-1 text-success"></i> الطريقة 2: الدفع التلقائي (ADMS Push)
</button>
</li>
</ul>
<!-- Tab panes -->
<div class="tab-content" id="zktecoTabsContent">
<!-- Tab 1: TCP Pull -->
<div class="tab-pane fade show active" id="pull" role="tabpanel" aria-labelledby="pull-tab">
<div class="alert alert-info border-0 border-start border-info border-4">
<i class="fas fa-info-circle me-2"></i> هذه الطريقة تتطلب أن يكون الخادم (السيرفر) قادراً على الوصول إلى عنوان IP الخاص بالجهاز. إذا كان الجهاز في شبكة محلية مختلفة ولا يوجد IP ثابت، استخدم <strong>الطريقة 2</strong>.
</div>
<div class="row">
<div class="col-xl-6 col-lg-6 mb-4">
<div class="card shadow-sm border-0 h-100">
<div class="card-header bg-white pb-0 border-bottom-0">
<h6 class="m-0 font-weight-bold text-primary"><i class="fas fa-cog me-2"></i> إعدادات الاتصال المباشر</h6>
</div>
<div class="card-body">
<form method="POST" action="">
<div class="mb-3">
<label class="form-label">IP Address (عنوان الجهاز)</label>
<input type="text" class="form-control" name="ip_address" value="<?= htmlspecialchars($ip) ?>" required placeholder="مثال: 192.168.1.201">
</div>
<div class="mb-3">
<label class="form-label">Port (المنفذ)</label>
<input type="number" class="form-control" name="port" value="<?= htmlspecialchars($port) ?>" required placeholder="الافتراضي: 4370">
</div>
<div class="d-grid gap-2 d-md-flex justify-content-md-start">
<button type="submit" name="save_settings" class="btn btn-primary"><i class="fas fa-save me-1"></i> حفظ الإعدادات</button>
</div>
</form>
</div>
</div>
</div>
<div class="col-xl-6 col-lg-6 mb-4">
<div class="card shadow-sm border-0 h-100">
<div class="card-header bg-white pb-0 border-bottom-0">
<h6 class="m-0 font-weight-bold text-info"><i class="fas fa-sync-alt me-2"></i> العمليات</h6>
</div>
<div class="card-body text-center py-5">
<p class="text-muted mb-4">اختبر الاتصال أو ابدأ بسحب البيانات الان.</p>
<form method="POST" action="">
<div class="d-grid gap-3 col-8 mx-auto">
<button type="submit" name="test_connection" class="btn btn-outline-info btn-lg shadow-sm">
<i class="fas fa-wifi me-2"></i> اختبار الاتصال
</button>
<button type="submit" name="sync_logs" class="btn btn-success btn-lg shadow-sm">
<i class="fas fa-download me-2"></i> مزامنة الحضور والانصراف
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- Tab 2: ADMS Push -->
<div class="tab-pane fade" id="push" role="tabpanel" aria-labelledby="push-tab">
<div class="card shadow-sm border-0 mb-4">
<div class="card-body">
<h5 class="card-title text-success mb-3"><i class="fas fa-check-circle me-2"></i> النظام جاهز لاستقبال البصمات تلقائياً (ADMS/WDMS)</h5>
<p class="card-text text-muted mb-4">من خلال هذه الطريقة، يقوم جهاز البصمة بإرسال سجلات الحضور تلقائياً إلى هذا النظام عبر الإنترنت. <strong>هذه الطريقة مثالية إذا كان جهاز البصمة في شبكة محلية بدون IP ثابت (Public IP).</strong></p>
<h6 class="font-weight-bold text-dark mb-3"><i class="fas fa-desktop me-2"></i> خطوات الإعداد على جهاز البصمة ZKTeco:</h6>
<div class="row">
<div class="col-md-8">
<ul class="list-group list-group-flush mb-4 shadow-sm rounded border">
<li class="list-group-item d-flex justify-content-between align-items-center py-3">
<div class="ms-2 me-auto">
<div class="fw-bold text-primary mb-1">1. إعدادات الشبكة (Comm.)</div>
افتح القائمة الرئيسية للجهاز واذهب إلى <kbd>COMM.</kbd> ثم <kbd>Cloud Server Setting</kbd> أو <kbd>ADMS</kbd>.
</div>
<span class="badge bg-primary rounded-pill"><i class="fas fa-arrow-left"></i></span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center py-3">
<div class="ms-2 me-auto">
<div class="fw-bold text-primary mb-1">2. عنوان السيرفر (Server Address)</div>
أدخل عنوان هذا النظام أو الـ IP الخاص به.
<br><small class="text-muted">مثال: إذا كان عنوان التطبيق <code>http://your-app.com</code> أدخل فقط <code>your-app.com</code></small>
</div>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center py-3">
<div class="ms-2 me-auto">
<div class="fw-bold text-primary mb-1">3. منفذ السيرفر (Server Port)</div>
أدخل المنفذ: <kbd>80</kbd> للاتصال العادي (أو 443 إذا كنت تستخدم HTTPS مدعوم).
</div>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center py-3">
<div class="ms-2 me-auto">
<div class="fw-bold text-primary mb-1">4. تفعيل المزامنة</div>
بمجرد الحفظ، سيبدأ الجهاز بإرسال بيانات الحضور تلقائياً إلى السيرفر. ستظهر السجلات في صفحة "سجل الحضور".
</div>
<span class="badge bg-success rounded-pill"><i class="fas fa-check"></i></span>
</li>
</ul>
</div>
<div class="col-md-4">
<div class="alert alert-warning border-0 border-start border-warning border-4 h-100 d-flex flex-column justify-content-center">
<div>
<h6 class="alert-heading font-weight-bold"><i class="fas fa-exclamation-triangle me-2"></i> خطوة هامة لربط الموظفين</h6>
<p class="mb-0 small">لكي يتم احتساب البصمات للموظف الصحيح، تأكد من الذهاب إلى <a href="hr_employees.php" class="alert-link fw-bold">قائمة الموظفين</a> وتعديل بيانات كل موظف وإضافة <strong>ZKTeco UID</strong> (رقم الموظف في جهاز البصمة).</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<?php require_once 'includes/footer.php'; ?>

1
iclock.php Normal file
View File

@ -0,0 +1 @@
<?php echo $_SERVER['PATH_INFO']; ?>

1
iclock/cdata.php Normal file
View File

@ -0,0 +1 @@
<?php echo 'cdata works'; ?>

1
iclock/cdata/index.php Normal file
View File

@ -0,0 +1 @@
<?php echo 'cdata works'; ?>

22
vendor/autoload.php vendored Normal file
View File

@ -0,0 +1,22 @@
<?php
// autoload.php @generated by Composer
if (PHP_VERSION_ID < 50600) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, $err);
} elseif (!headers_sent()) {
echo $err;
}
}
throw new RuntimeException($err);
}
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit6910b6f5ff1d98cde5c3ed7e3c06c3dd::getLoader();

579
vendor/composer/ClassLoader.php vendored Normal file
View File

@ -0,0 +1,579 @@
<?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 https://www.php-fig.org/psr/psr-0/
* @see https://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
/** @var \Closure(string):void */
private static $includeFile;
/** @var string|null */
private $vendorDir;
// PSR-4
/**
* @var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array();
/**
* @var array<string, list<string>>
*/
private $prefixDirsPsr4 = array();
/**
* @var list<string>
*/
private $fallbackDirsPsr4 = array();
// PSR-0
/**
* List of PSR-0 prefixes
*
* Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
*
* @var array<string, array<string, list<string>>>
*/
private $prefixesPsr0 = array();
/**
* @var list<string>
*/
private $fallbackDirsPsr0 = array();
/** @var bool */
private $useIncludePath = false;
/**
* @var array<string, string>
*/
private $classMap = array();
/** @var bool */
private $classMapAuthoritative = false;
/**
* @var array<string, bool>
*/
private $missingClasses = array();
/** @var string|null */
private $apcuPrefix;
/**
* @var array<string, self>
*/
private static $registeredLoaders = array();
/**
* @param string|null $vendorDir
*/
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
self::initializeIncludeClosure();
}
/**
* @return array<string, list<string>>
*/
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
}
return array();
}
/**
* @return array<string, list<string>>
*/
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
/**
* @return list<string>
*/
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
/**
* @return list<string>
*/
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
/**
* @return array<string, string> Array of classname => path
*/
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array<string, string> $classMap Class to filename map
*
* @return void
*/
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 list<string>|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*
* @return void
*/
public function add($prefix, $paths, $prepend = false)
{
$paths = (array) $paths;
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
$paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
$paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
$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 list<string>|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
$paths = (array) $paths;
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
$paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
$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] = $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
$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 list<string>|string $paths The PSR-0 base directories
*
* @return void
*/
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 list<string>|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
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
*
* @return void
*/
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
*
* @return void
*/
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;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*
* @return void
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*
* @return void
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
if (null === $this->vendorDir) {
return;
}
if ($prepend) {
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
} else {
unset(self::$registeredLoaders[$this->vendorDir]);
self::$registeredLoaders[$this->vendorDir] = $this;
}
}
/**
* Unregisters this instance as an autoloader.
*
* @return void
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
if (null !== $this->vendorDir) {
unset(self::$registeredLoaders[$this->vendorDir]);
}
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return true|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
$includeFile = self::$includeFile;
$includeFile($file);
return true;
}
return null;
}
/**
* 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)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
/**
* Returns the currently registered loaders keyed by their corresponding vendor directories.
*
* @return array<string, self>
*/
public static function getRegisteredLoaders()
{
return self::$registeredLoaders;
}
/**
* @param string $class
* @param string $ext
* @return string|false
*/
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
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;
}
return false;
}
/**
* @return void
*/
private static function initializeIncludeClosure()
{
if (self::$includeFile !== null) {
return;
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
*/
self::$includeFile = \Closure::bind(static function($file) {
include $file;
}, null, null);
}
}

396
vendor/composer/InstalledVersions.php vendored Normal file
View File

@ -0,0 +1,396 @@
<?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;
use Composer\Autoload\ClassLoader;
use Composer\Semver\VersionParser;
/**
* This class is copied in every Composer installed project and available to all
*
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
*
* To require its presence, you can require `composer-runtime-api ^2.0`
*
* @final
*/
class InstalledVersions
{
/**
* @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
* @internal
*/
private static $selfDir = null;
/**
* @var mixed[]|null
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
*/
private static $installed;
/**
* @var bool
*/
private static $installedIsLocalDir;
/**
* @var bool|null
*/
private static $canGetVendors;
/**
* @var array[]
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
private static $installedByVendor = array();
/**
* Returns a list of all package names which are present, either by being installed, replaced or provided
*
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackages()
{
$packages = array();
foreach (self::getInstalled() as $installed) {
$packages[] = array_keys($installed['versions']);
}
if (1 === \count($packages)) {
return $packages[0];
}
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
}
/**
* Returns a list of all package names with a specific type e.g. 'library'
*
* @param string $type
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackagesByType($type)
{
$packagesByType = array();
foreach (self::getInstalled() as $installed) {
foreach ($installed['versions'] as $name => $package) {
if (isset($package['type']) && $package['type'] === $type) {
$packagesByType[] = $name;
}
}
}
return $packagesByType;
}
/**
* Checks whether the given package is installed
*
* This also returns true if the package name is provided or replaced by another package
*
* @param string $packageName
* @param bool $includeDevRequirements
* @return bool
*/
public static function isInstalled($packageName, $includeDevRequirements = true)
{
foreach (self::getInstalled() as $installed) {
if (isset($installed['versions'][$packageName])) {
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
}
}
return false;
}
/**
* Checks whether the given package satisfies a version constraint
*
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
*
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
*
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
* @param string $packageName
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
* @return bool
*/
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints((string) $constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
}
/**
* Returns a version constraint representing all the range(s) which are installed for a given package
*
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
* whether a given version of a package is installed, and not just whether it exists
*
* @param string $packageName
* @return string Version constraint usable with composer/semver
*/
public static function getVersionRanges($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
$ranges = array();
if (isset($installed['versions'][$packageName]['pretty_version'])) {
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
}
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
}
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
}
if (array_key_exists('provided', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
}
return implode(' || ', $ranges);
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['version'])) {
return null;
}
return $installed['versions'][$packageName]['version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getPrettyVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
return null;
}
return $installed['versions'][$packageName]['pretty_version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
*/
public static function getReference($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['reference'])) {
return null;
}
return $installed['versions'][$packageName]['reference'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
*/
public static function getInstallPath($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @return array
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
*/
public static function getRootPackage()
{
$installed = self::getInstalled();
return $installed[0]['root'];
}
/**
* Returns the raw installed.php data for custom implementations
*
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
* @return array[]
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
*/
public static function getRawData()
{
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = include __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
return self::$installed;
}
/**
* Returns the raw data of all installed.php which are currently loaded for custom implementations
*
* @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
public static function getAllRawData()
{
return self::getInstalled();
}
/**
* Lets you reload the static array from another file
*
* This is only useful for complex integrations in which a project needs to use
* this class but then also needs to execute another project's autoloader in process,
* and wants to ensure both projects have access to their version of installed.php.
*
* A typical case would be PHPUnit, where it would need to make sure it reads all
* the data it needs from this class, then call reload() with
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
* the project in which it runs can then also use this class safely, without
* interference between PHPUnit's dependencies and the project's dependencies.
*
* @param array[] $data A vendor/composer/installed.php data set
* @return void
*
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
*/
public static function reload($data)
{
self::$installed = $data;
self::$installedByVendor = array();
// when using reload, we disable the duplicate protection to ensure that self::$installed data is
// always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not,
// so we have to assume it does not, and that may result in duplicate data being returned when listing
// all installed packages for example
self::$installedIsLocalDir = false;
}
/**
* @return string
*/
private static function getSelfDir()
{
if (self::$selfDir === null) {
self::$selfDir = strtr(__DIR__, '\\', '/');
}
return self::$selfDir;
}
/**
* @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
private static function getInstalled()
{
if (null === self::$canGetVendors) {
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
}
$installed = array();
$copiedLocalDir = false;
if (self::$canGetVendors) {
$selfDir = self::getSelfDir();
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
$vendorDir = strtr($vendorDir, '\\', '/');
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($vendorDir.'/composer/installed.php')) {
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require $vendorDir.'/composer/installed.php';
self::$installedByVendor[$vendorDir] = $required;
$installed[] = $required;
if (self::$installed === null && $vendorDir.'/composer' === $selfDir) {
self::$installed = $required;
self::$installedIsLocalDir = true;
}
}
if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) {
$copiedLocalDir = true;
}
}
}
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require __DIR__ . '/installed.php';
self::$installed = $required;
} else {
self::$installed = array();
}
}
if (self::$installed !== array() && !$copiedLocalDir) {
$installed[] = self::$installed;
}
return $installed;
}
}

21
vendor/composer/LICENSE vendored Normal file
View File

@ -0,0 +1,21 @@
Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

10
vendor/composer/autoload_classmap.php vendored Normal file
View File

@ -0,0 +1,10 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
);

View File

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

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

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

36
vendor/composer/autoload_real.php vendored Normal file
View File

@ -0,0 +1,36 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit6910b6f5ff1d98cde5c3ed7e3c06c3dd
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit6910b6f5ff1d98cde5c3ed7e3c06c3dd', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInit6910b6f5ff1d98cde5c3ed7e3c06c3dd', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit6910b6f5ff1d98cde5c3ed7e3c06c3dd::getInitializer($loader));
$loader->register(true);
return $loader;
}
}

36
vendor/composer/autoload_static.php vendored Normal file
View File

@ -0,0 +1,36 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInit6910b6f5ff1d98cde5c3ed7e3c06c3dd
{
public static $prefixLengthsPsr4 = array (
'R' =>
array (
'Rats\\Zkteco\\' => 12,
),
);
public static $prefixDirsPsr4 = array (
'Rats\\Zkteco\\' =>
array (
0 => __DIR__ . '/..' . '/rats/zkteco/src',
),
);
public static $classMap = array (
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit6910b6f5ff1d98cde5c3ed7e3c06c3dd::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit6910b6f5ff1d98cde5c3ed7e3c06c3dd::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit6910b6f5ff1d98cde5c3ed7e3c06c3dd::$classMap;
}, null, ClassLoader::class);
}
}

46
vendor/composer/installed.json vendored Normal file
View File

@ -0,0 +1,46 @@
{
"packages": [
{
"name": "rats/zkteco",
"version": "V002",
"version_normalized": "002.0.0.0",
"source": {
"type": "git",
"url": "https://github.com/raihanafroz/zkteco.git",
"reference": "6e17024370ec56c89d87c85a54a1c55079304834"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/raihanafroz/zkteco/zipball/6e17024370ec56c89d87c85a54a1c55079304834",
"reference": "6e17024370ec56c89d87c85a54a1c55079304834",
"shasum": ""
},
"time": "2021-11-21T17:53:28+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"Rats\\Zkteco\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Raihan Afroz",
"email": "raihanafroz9@gmail.com"
}
],
"description": "ZKTeco Laravel Library",
"support": {
"issues": "https://github.com/raihanafroz/zkteco/issues",
"source": "https://github.com/raihanafroz/zkteco/tree/V002"
},
"install-path": "../rats/zkteco"
}
],
"dev": true,
"dev-package-names": []
}

32
vendor/composer/installed.php vendored Normal file
View File

@ -0,0 +1,32 @@
<?php return array(
'root' => array(
'name' => '__root__',
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => '79a5056e8ec090b0861aa5c8dc1189870f3fe8eb',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev' => true,
),
'versions' => array(
'__root__' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => '79a5056e8ec090b0861aa5c8dc1189870f3fe8eb',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev_requirement' => false,
),
'rats/zkteco' => array(
'pretty_version' => 'V002',
'version' => '002.0.0.0',
'reference' => '6e17024370ec56c89d87c85a54a1c55079304834',
'type' => 'library',
'install_path' => __DIR__ . '/../rats/zkteco',
'aliases' => array(),
'dev_requirement' => false,
),
),
);

0
vendor/rats/zkteco/CHANGELOG.md vendored Normal file
View File

292
vendor/rats/zkteco/README.md vendored Normal file
View File

@ -0,0 +1,292 @@
# ZKTeco - Laravel Library #
[![Issues](https://img.shields.io/github/issues/raihanafroz/zkteco?style=flat-square)](https://github.com/raihanafroz/zkteco/issues)
[![Forks](https://img.shields.io/github/forks/raihanafroz/zkteco?style=flat-square)](https://github.com/raihanafroz/zkteco/network/members)
[![Stars](https://img.shields.io/github/stars/raihanafroz/zkteco?style=flat-square)](https://github.com/raihanafroz/zkteco/stargazers)
[![Total Downloads](https://img.shields.io/packagist/dt/rats/zkteco?style=flat-square)](https://packagist.org/packages/rats/zkteco)
[![License](https://poser.pugx.org/rats/zkteco/license.svg)](https://packagist.org/packages/rats/zkteco)
The `rats/zkteco` package provides easy to use functions to ZKTeco Device activities.
__Requires:__ **Laravel** >= **6.0**
__License:__ MIT or later
## Installation:
You can install the package via composer:
``` bash
composer require rats/zkteco
```
The package will automatically register itself.
You have to enable your php socket if it is not enable.
## Usage
1. Create a object of ZKTeco class.
```php
use Rats\Zkteco\Lib\ZKTeco;
// 1 s't parameter is string $ip Device IP Address
// 2 nd parameter is integer $port Default: 4370
$zk = new ZKTeco('192.168.1.201');
// or you can use with port
// $zk = new ZKTeco('192.168.1.201', 8080);
```
2. Call ZKTeco methods
* __Connect__
```php
// connect
// this return bool
$zk->connect();
```
* __Disconnect__
```php
// disconnect
// this return bool
$zk->disconnect();
```
* __Enable Device__
```php
// enable
// this return bool/mixed
$zk->enableDevice();
```
> **NOTE**: You have to call after read/write any info of Device.
* __Disable Device__
```php
// disable
// this return bool/mixed
$zk->disableDevice();
```
> **NOTE**: You have to call before read/write any info of Device.
* __Device Version__
```php
// get device version
// this return bool/mixed
$zk->version();
```
* __Device Os Version__
```php
// get device os version
// this return bool/mixed
$zk->osVersion();
```
* __Power Off__
```php
// turn off the device
// this return bool/mixed
$zk->shutdown();
```
* __Restart__
```php
// restart the device
// this return bool/mixed
$zk->restart();
```
* __Sleep__
```php
// sleep the device
// this return bool/mixed
$zk->sleep();
```
* __Resume__
```php
// resume the device from sleep
// this return bool/mixed
$zk->resume();
```
* __Voice Test__
```php
// voice test of the device "Thank you"
// this return bool/mixed
$zk->testVoice();
```
* __Platform__
```php
// get platform
// this return bool/mixed
$zk->platform();
```
* __Firmware Version__
```php
// get firmware version
// this return bool/mixed
$zk->fmVersion();
```
* __Work Code__
```php
// get work code
// this return bool/mixed
$zk->workCode();
```
* __SSR__
```php
// get SSR
// this return bool/mixed
$zk->ssr();
```
* __Pin Width__
```php
// get Pin Width
// this return bool/mixed
$zk->pinWidth();
```
* __Serial Number__
```php
// get device serial number
// this return bool/mixed
$zk->serialNumber();
```
* __Device Name__
```php
// get device name
// this return bool/mixed
$zk->deviceName();
```
* __Get Device Time__
```php
// get device time
// return bool/mixed bool|mixed Format: "Y-m-d H:i:s"
$zk->getTime();
```
* __Set Device Time__
```php
// set device time
// parameter string $t Format: "Y-m-d H:i:s"
// return bool/mixed
$zk->setTime();
```
* __Get Users__
```php
// get User
// this return array[]
$zk->getUser();
```
* __Set Users__
```php
// set user
// 1 s't parameter int $uid Unique ID (max 65535)
// 2 nd parameter int|string $userid ID in DB (same like $uid, max length = 9, only numbers - depends device setting)
// 3 rd parameter string $name (max length = 24)
// 4 th parameter int|string $password (max length = 8, only numbers - depends device setting)
// 5 th parameter int $role Default Util::LEVEL_USER
// 6 th parameter int $cardno Default 0 (max length = 10, only numbers
// return bool|mixed
$zk->setUser();
```
* __Clear All Admin__
```php
// remove all admin
// return bool|mixed
$zk->clearAdmin();
```
* __Clear All Users__
```php
// remove all users
// return bool|mixed
$zk->clearAdmin();
```
* __Remove A User__
```php
// remove a user by $uid
// parameter integer $uid
// return bool|mixed
$zk->removeUser();
```
* __Get Attendance Log__
```php
// get attendance log
// return array[]
// like as 0 => array:5 [▼
// "uid" => 1 /* serial number of the attendance */
// "id" => "1" /* user id of the application */
// "state" => 1 /* the authentication type, 1 for Fingerprint, 4 for RF Card etc */
// "timestamp" => "2020-05-27 21:21:06" /* time of attendance */
// "type" => 255 /* attendance type, like check-in, check-out, overtime-in, overtime-out, break-in & break-out etc. if attendance type is none of them, it gives 255. */
// ]
$zk->getAttendance();
```
* __Clear Attendance Log__
```php
// clear attendance log
// return bool/mixed
$zk->clearAttendance();
```
# end

19
vendor/rats/zkteco/composer.json vendored Normal file
View File

@ -0,0 +1,19 @@
{
"name": "rats/zkteco",
"description": "ZKTeco Laravel Library",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Raihan Afroz",
"email": "raihanafroz9@gmail.com"
}
],
"autoload": {
"psr-4":{
"Rats\\Zkteco\\": "src"
}
},
"minimum-stability": "dev",
"require": {}
}

View File

@ -0,0 +1,72 @@
<?php
namespace Rats\Zkteco\Lib\Helper;
use Rats\Zkteco\Lib\ZKTeco;
class Attendance
{
/**
* @param ZKTeco $self
* @return array [uid, id, state, timestamp]
*/
static public function get(ZKTeco $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 )));
$attendance[] = [
'uid' => $uid,
'id' => $id,
'state' => $state,
'timestamp' => $timestamp,
'type' => $type
];
$attData = substr($attData, 40);
}
}
return $attendance;
}
/**
* @param ZKTeco $self
* @return bool|mixed
*/
static public function clear(ZKTeco $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_CLEAR_ATT_LOG;
$command_string = '';
return $self->_command($command, $command_string);
}
}

View File

@ -0,0 +1,83 @@
<?php
namespace Rats\Zkteco\Lib\Helper;
use Rats\Zkteco\Lib\Helper\Util;
use Rats\Zkteco\Lib\ZKTeco;
use ErrorException;
use Exception;
class Connect
{
/**
* @param ZKTeco $self
* @return bool
*/
static public function connect(ZKTeco $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 ZKTeco $self
* @return bool
*/
static public function disconnect(ZKTeco $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;
}
}
}

View File

@ -0,0 +1,151 @@
<?php
namespace Rats\Zkteco\Lib\Helper;
use Rats\Zkteco\Lib\ZKTeco;
class Device
{
/**
* @param ZKTeco $self
* @return bool|mixed
*/
static public function name(ZKTeco $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_DEVICE;
$command_string = '~DeviceName';
return $self->_command($command, $command_string);
}
/**
* @param ZKTeco $self
* @return bool|mixed
*/
static public function enable(ZKTeco $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_ENABLE_DEVICE;
$command_string = '';
return $self->_command($command, $command_string);
}
/**
* @param ZKTeco $self
* @return bool|mixed
*/
static public function disable(ZKTeco $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_DISABLE_DEVICE;
$command_string = chr(0) . chr(0);
return $self->_command($command, $command_string);
}
/**
* @param ZKTeco $self
* @return bool|mixed *** this will turn off the device
*/
public static function powerOff(ZKTeco $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_POWEROFF;
$command_string = chr(0) . chr(0);
return $self->_command($command, $command_string);
}
/**
* @param ZKTeco $self
* @return bool|mixed *** this will restart the device
*/
public static function restart(ZKTeco $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_RESTART;
$command_string = chr(0) . chr(0);
return $self->_command($command, $command_string);
}
/**
* @param ZKTeco $self
* @return bool|mixed *** this will sleep the device
*/
public static function sleep(ZKTeco $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_SLEEP;
$command_string = chr(0) . chr(0);
return $self->_command($command, $command_string);
}
/**
* @param ZKTeco $self
* @return bool|mixed *** this will resume the device from sleep
*/
public static function resume(ZKTeco $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_RESUME;
$command_string = chr(0) . chr(0);
return $self->_command($command, $command_string);
}
/**
* @param ZKTeco $self
* @return bool|mixed *** this will play voice "Thank you"
*/
public static function testVoice(ZKTeco $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_TESTVOICE;
$command_string = chr(0) . chr(0);
return $self->_command($command, $command_string);
}
/**
* @param ZKTeco $self
* @return bool|mixed *** this will clear the LCD screen
*/
public static function clearLCD(ZKTeco $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_CLEAR_LCD;
return $self->_command($command, '');
}
/**
* @param ZKTeco $self
* @param $rank *** Line number of text
* @param $text *** Text which will display in the LCD screen
* @return bool|mixed *** this will write text into the LCD
*/
public static function writeLCD(ZKTeco $self, $rank, $text)
{
$self->_section = __METHOD__;
$command = Util::CMD_WRITE_LCD;
$byte1 = chr((int)($rank % 256));
$byte2 = chr((int)($rank >> 8));
$byte3 = chr(0);
$command_string = $byte1.$byte2.$byte3.' '.$text;
return $self->_command($command, $command_string);
}
}

View File

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

View File

@ -0,0 +1,166 @@
<?php
namespace Rats\Zkteco\Lib\Helper;
use Rats\Zkteco\Lib\ZKTeco;
class Fingerprint
{
/**
* TODO: Can get data, but don't know how to parse the data. Need more documentation about it...
*
* @param ZKTeco $self
* @param integer $uid Unique Employee ID in ZK device
* @return array Binary fingerprint data array (where key is finger ID (0-9))
*/
static public function get(ZKTeco $self, $uid)
{
$self->_section = __METHOD__;
$data = [];
//fingers of the hands
for ($i = 0; $i <= 9; $i++) {
$finger = new Fingerprint();
$tmp = $finger->_getFinger($self, $uid, $i);
if ($tmp['size'] > 0) {
$data[$i] = $tmp['tpl'];
}
unset($tmp);
}
return $data;
}
/**
* @param ZKTeco $self
* @param integer $uid Unique Employee ID in ZK device
* @param integer $finger Finger ID (0-9)
* @return array
*/
private function _getFinger(ZKTeco $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 ZKTeco $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
*/
static public function set(ZKTeco $self, $uid, array $data)
{
$self->_section = __METHOD__;
$count = 0;
foreach ($data as $finger => $item) {
$allowSet = true;
$fingerPrint = new Fingerprint();
if ($fingerPrint->_checkFinger($self, $uid, $finger) === true) {
$allowSet = $fingerPrint->_removeFinger($self, $uid, $finger);
}
if ($allowSet === true && $fingerPrint->_setFinger($self, $item) === true) {
$count++;
}
}
return $count;
}
/**
* @param ZKTeco $self
* @param string $data Binary fingerprint data item
* @return bool|mixed
*/
private function _setFinger(ZKTeco $self, $data)
{
$command = Util::CMD_USER_TEMP_WRQ;
$command_string = $data;
return $self->_command($command, $command_string);
}
/**
* @param ZKTeco $self
* @param int $uid Unique Employee ID in ZK device
* @param array $data Fingers ID array (0-9)
* @return int Count of deleted fingerprints
*/
static public function remove(ZKTeco $self, $uid, array $data)
{
$self->_section = __METHOD__;
$count = 0;
foreach ($data as $finger) {
$fingerPrint = new Fingerprint();
if ($fingerPrint->_checkFinger($self, $uid, $finger) === true) {
if ($fingerPrint->_removeFinger($self, $uid, $finger) === true) {
$count++;
}
}
}
return $count;
}
/**
* @param ZKTeco $self
* @param int $uid Unique Employee ID in ZK device
* @param int $finger Finger ID (0-9)
* @return bool
*/
private function _removeFinger(ZKTeco $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);
$fingerPrint = new Fingerprint();
return !($fingerPrint->_checkFinger($self, $uid, $finger));
}
/**
* @param ZKTeco $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(ZKTeco $self, $uid, $finger)
{
$fingerPrint = new Fingerprint();
$res = $fingerPrint->_getFinger($self, $uid, $finger);
return (bool)($res['size'] > 0);
}
}

View File

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

View File

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

View File

@ -0,0 +1,36 @@
<?php
namespace Rats\Zkteco\Lib\Helper;
use Rats\Zkteco\Lib\ZKTeco;
class Platform
{
/**
* @param ZKTeco $self
* @return bool|mixed
*/
static public function get(ZKTeco $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_DEVICE;
$command_string = '~Platform';
return $self->_command($command, $command_string);
}
/**
* @param ZKTeco $self
* @return bool|mixed
*/
static public function getVersion(ZKTeco $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 Rats\Zkteco\Lib\Helper;
use Rats\Zkteco\Lib\ZKTeco;
class SerialNumber
{
/**
* @param ZKTeco $self
* @return bool|mixed
*/
static public function get(ZKTeco $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_DEVICE;
$command_string = '~SerialNumber';
return $self->_command($command, $command_string);
}
}

View File

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

View File

@ -0,0 +1,44 @@
<?php
namespace Rats\Zkteco\Lib\Helper;
use Rats\Zkteco\Lib\ZKTeco;
class Time
{
/**
* @param ZKTeco $self
* @param string $t Format: "Y-m-d H:i:s"
* @return bool|mixed
*/
static public function set(ZKTeco $self, $t)
{
die($t);
$self->_section = __METHOD__;
$command = Util::CMD_SET_TIME;
$command_string = pack('I', Util::encodeTime($t));
return $self->_command($command, $command_string);
}
/**
* @param ZKTeco $self
* @return bool|mixed
*/
static public function get(ZKTeco $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;
}
}
}

View File

@ -0,0 +1,161 @@
<?php
namespace Rats\Zkteco\Lib\Helper;
use Rats\Zkteco\Lib\ZKTeco;
class User
{
/**
* @param ZKTeco $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
*/
static public function set(ZKTeco $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)
]);
// die($command_string);
return $self->_command($command, $command_string);
}
/**
* @param ZKTeco $self
* @return array [userid, name, cardno, uid, role, password]
*/
static public function get(ZKTeco $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] = [
'uid' => $uid,
'userid' => $userid,
'name' => $name,
'role' => intval($role),
'password' => $password,
'cardno' => $cardno,
];
$userData = substr($userData, 72);
}
}
return $users;
}
/**
* @param ZKTeco $self
* @return bool|mixed
*/
static public function clear(ZKTeco $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_CLEAR_DATA;
$command_string = '';
return $self->_command($command, $command_string);
}
/**
* @param ZKTeco $self
* @return bool|mixed
*/
static public function clearAdmin(ZKTeco $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_CLEAR_ADMIN;
$command_string = '';
return $self->_command($command, $command_string);
}
/**
* @param ZKTeco $self
* @param integer $uid
* @return bool|mixed
*/
static public function remove(ZKTeco $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);
}
}

View File

@ -0,0 +1,427 @@
<?php
namespace Rats\Zkteco\Lib\Helper;
use Rats\Zkteco\Lib\ZKTeco;
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_RESTART = 1004; # Restart the machine
const CMD_POWEROFF = 1005; # Turn Off the machine
const CMD_SLEEP = 1006; # Sleep the machine
const CMD_RESUME = 1007; # Resume the machine from Sleep
const CMD_TEST_TEMP = 1011;
const CMD_TESTVOICE = 1017; # Voice test to the device
const CMD_CHANGE_SPEED = 1101;
const CMD_WRITE_LCD = 66; # Write in LCD
const CMD_CLEAR_LCD = 67; # Clear 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; # User level as User
const LEVEL_ADMIN = 14; # User level as Admin
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 ZKTeco $self
* @return bool|number
*/
static public function getSize(ZKTeco $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 ZKTeco $self
* @param int $maxErrors
* @param bool $first if 'true' don't remove first 4 bytes for first row
* @return string
*/
static public function recData(ZKTeco $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 ZKTeco $self
* @param int $received
* @param int $bytes
*/
static private function logReceived(ZKTeco $self, $received, $bytes)
{
self::logger($self, 'Received: ' . $received . ' of ' . $bytes . ' bytes');
}
/**
* Write log
* @param ZKTeco $self
* @param string $str
*/
static private function logger(ZKTeco $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);
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace Rats\Zkteco\Lib\Helper;;
use Rats\Zkteco\Lib\Helper\Util;
use Rats\Zkteco\Lib\ZKTeco;
class Version
{
/**
* @param ZKTeco $self
* @return bool|mixed
*/
static public function get(ZKTeco $self)
{
$self->_section = __METHOD__;
$command = Util::CMD_VERSION;
$command_string = '';
return $self->_command($command, $command_string);
}
}

View File

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

435
vendor/rats/zkteco/src/Lib/ZKTeco.php vendored Normal file
View File

@ -0,0 +1,435 @@
<?php
namespace Rats\Zkteco\Lib;
use ErrorException;
use Exception;
use Rats\Zkteco\Lib\Helper\Attendance;
use Rats\Zkteco\Lib\Helper\Device;
use Rats\Zkteco\Lib\Helper\Face;
use Rats\Zkteco\Lib\Helper\Fingerprint;
use Rats\Zkteco\Lib\Helper\Os;
use Rats\Zkteco\Lib\Helper\Pin;
use Rats\Zkteco\Lib\Helper\Platform;
use Rats\Zkteco\Lib\Helper\SerialNumber;
use Rats\Zkteco\Lib\Helper\Ssr;
use Rats\Zkteco\Lib\Helper\Time;
use Rats\Zkteco\Lib\Helper\User;
use Rats\Zkteco\Lib\Helper\Util;
use Rats\Zkteco\Lib\Helper\Connect;
use Rats\Zkteco\Lib\Helper\Version;
use Rats\Zkteco\Lib\Helper\WorkCode;
class ZKTeco{
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 = array('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 Connect::connect($this);
}
/**
* Disconnect from device
*
* @return bool
*/
public function disconnect()
{
return Connect::disconnect($this);
}
/**
* Get device version
*
* @return bool|mixed
*/
public function version()
{
return Version::get($this);
}
/**
* Get OS version
*
* @return bool|mixed
*/
public function osVersion()
{
return Os::get($this);
}
/**
* Get platform
*
* @return bool|mixed
*/
public function platform()
{
return Platform::get($this);
}
/**
* Get firmware version
*
* @return bool|mixed
*/
public function fmVersion()
{
return Platform::getVersion($this);
}
/**
* Get work code
*
* @return bool|mixed
*/
public function workCode()
{
return WorkCode::get($this);
}
/**
* Get SSR
*
* @return bool|mixed
*/
public function ssr()
{
return Ssr::get($this);
}
/**
* Get pin width
*
* @return bool|mixed
*/
public function pinWidth()
{
return Pin::width($this);
}
/**
* @return bool|mixed
*/
public function faceFunctionOn()
{
return Face::on($this);
}
/**
* Get device serial number
*
* @return bool|mixed
*/
public function serialNumber()
{
return SerialNumber::get($this);
}
/**
* Get device name
*
* @return bool|mixed
*/
public function deviceName()
{
return Device::name($this);
}
/**
* Disable device
*
* @return bool|mixed
*/
public function disableDevice()
{
return Device::disable($this);
}
/**
* Enable device
*
* @return bool|mixed
*/
public function enableDevice()
{
return Device::enable($this);
}
/**
* Get users data
*
* @return array [userid, name, cardno, uid, role, password]
*/
public function getUser()
{
return 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 User::set($this, $uid, $userid, $name, $password, $role, $cardno);
}
/**
* Remove All users
*
* @return bool|mixed
*/
public function clearUsers()
{
return User::clear($this);
}
/**
* Remove admin
*
* @return bool|mixed
*/
public function clearAdmin()
{
return User::clearAdmin($this);
}
/**
* Remove user by UID
*
* @param integer $uid
* @return bool|mixed
*/
public function removeUser($uid)
{
return User::remove($this, $uid);
}
/**
* 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 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 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 Fingerprint::remove($this, $uid, $data);
}
/**
* Get attendance log
*
* @return array [uid, id, state, timestamp]
*/
public function getAttendance()
{
return Attendance::get($this);
}
/**
* Clear attendance log
*
* @return bool|mixed
*/
public function clearAttendance()
{
return Attendance::clear($this);
}
/**
* Set device time
*
* @param string $t Format: "Y-m-d H:i:s"
* @return bool|mixed
*/
public function setTime($t)
{
return Time::set($this, $t);
}
/**
* Get device time
*
* @return bool|mixed Format: "Y-m-d H:i:s"
*/
public function getTime()
{
return Time::get($this);
}
/**
* turn off the device
*
* @return bool|mixed
*/
public function shutdown()
{
return Device::powerOff($this);
}
/**
* restart the device
*
* @return bool|mixed
*/
public function restart()
{
return Device::restart($this);
}
/**
* make sleep mood the device
*
* @return bool|mixed
*/
public function sleep()
{
return Device::sleep($this);
}
/**
* resume the device from sleep
*
* @return bool|mixed
*/
public function resume()
{
return Device::resume($this);
}
/**
* voice test Sound will "Thank you"
*
* @return bool|mixed
*/
public function testVoice()
{
return Device::testVoice($this);
}
public function clearLCD()
{
return Device::clearLCD($this);
}
public function writeLCD()
{
return Device::writeLCD($this, 2, "RAIHAN Afroz Topu");
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace Rats\Zkteco\Providers;
use Illuminate\Support\ServiceProvider;
class ZktecoServiceProvider extends ServiceProvider {
public function boot(){
// dd("Zkteco is running");
}
public function register(){
}
}