Download the PHP package msaied/zkteco without Composer

On this page you can find all versions of the php package msaied/zkteco. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package zkteco

zkteco

Packagist Version Packagist Downloads PHP Version GitHub Actions Workflow Status GitHub License

A PHP client for ZKTeco biometric attendance devices, with an optional, auto-discovered Laravel bridge. It speaks both directions of the wire:

The socket protocol is verified end-to-end against real hardware (read, write, template upload, realtime streaming, and interactive enrollment all work — see Tested hardware). The ADMS read path is fully implemented behind a trust-but-gate admission model; some outbound ADMS command layouts are still provisional (see Limitations).

Two ways to talk to a device

Socket client (ZkTeco\TCP) ADMS push (ZkTeco\ADMS)
Who initiates Your app dials the device The device dials your app
Transport TCP, port 4370 HTTP(S), device → your endpoint
Good for On-demand reads/writes, live streaming, enrollment Always-on fleets, NAT'd devices, push-on-punch
Entry point new Device(...) / ZkTeco::connection() Mounted routes + events / ZkTeco::push($serial)
Needs a daemon? Only for realtime()->live() No — devices push on their own schedule

You can use either or both. The two paths share the same domain (value objects & enums): a punch arriving over the socket stream and one pushed over ADMS both surface as the same AttendanceRecord and the same PunchReceived event.

Features

Requirements

Installation

The Laravel bridge is auto-discovered. To customise connections, publish the config:


Part 1 — Socket client (you dial the device)

Quick start

Connecting

The Device constructor only describes the connection — no socket is opened until you connect.

Non-ASCII names (Arabic, Chinese, …)

The device stores user names in a fixed byte field and renders them using its own configured language codepage, not UTF-8. Writing UTF-8 names to a device set to another codepage shows mojibake on the panel even though the device can display the script. Set nameEncoding to the device's codepage so names are converted on write and back on read:

Common values: Windows-1256 (Arabic), GB2312 (Chinese), Windows-1251 (Cyrillic). Leave it UTF-8 for ASCII-only or Unicode firmware. Encoding is done with iconv, so any encoding it supports is valid.

There are two ways to scope a connection:

Managed scope (preferred) — session() connects, disables the device while the callback runs, then re-enables and disconnects in a finally, even on exceptions. Use this for ordinary read/write work:

Explicit lifecycle — connect() / disconnect() for long-lived work such as realtime listening or interactive enrollment, where you do not want the device disabled:

Note: session() disables the device, which also locks the fingerprint sensor. Use connect()/disconnect() for realtime()->live() and templates()->enroll().

Working with the device

Users

uid is the device-local record slot (1..N); userId is the human-facing employee number string. They are distinct and must never be conflated.

Attendance

Templates & fingerprint enrollment

A Template is one biometric enrollment belonging to a user; a user may have several. data is the raw, opaque, firmware-specific template payload — this package does not interpret it.

Interactive enrollment triggers the device's fingerprint sensor and blocks while the person presses their finger (typically 3×). It returns true on a successful capture. Run it on an explicit connection, not inside session() (which would disable the sensor):

Because the enroll event stream is firmware-specific (see Tested hardware), enroll() accepts an optional $trace closure — fn (string $event, array $context) — invoked at each step of the handshake: the CMD_STARTENROLL payload and reply, every event packet (with its raw hex), the parsed completion, and the trailing records drained afterwards. On a terminal whose firmware differs from the verified one, pass it to record exactly what the device returns instead of guessing:

The fingerIndex (0–9) is the device's finger slot, used by enroll(), delete() and Template. It runs from the left pinky across to the right pinky, with the thumbs meeting in the middle — matching the device's on-screen Enroll layout:

Index Finger Index Finger
0 left pinky 5 right thumb
1 left ring 6 right index
2 left middle 7 right middle
3 left index 8 right ring
4 left thumb 9 right pinky

Face enrollment is not supported over the socket protocol — see Limitations.

Device info

Device control

Realtime punches

live() registers for live attendance events and returns a Generator that yields an AttendanceRecord per punch, or null on an idle heartbeat (so the loop never blocks forever). Run it on an explicit connection:


Part 2 — ADMS push (the device dials you)

ADMS is ZKTeco's device-initiated HTTP protocol — the inverse of the socket client. Instead of you dialing the device, the device is configured with your server's address and pushes to it: it handshakes, uploads attendance (and, on capable firmware, attendance photos, biometric templates, and audit logs), and polls for commands you've queued. This is the right fit for always-on fleets, devices behind NAT, or any case where you want push-on-punch without holding an open socket.

The package ships the whole HTTP surface as routes plus a controller; you wire your app in through events (for data the device uploads) and the ZkTeco::push() fluent API (for commands you send back).

Enabling the endpoints

The ADMS routes stay dormant by default so an app that only uses the socket client never exposes a push surface. Turn them on in config/zkteco.php (or via env):

With enabled = true, the bridge mounts these routes under the prefix (default iclock), which is the path ZKTeco firmware expects:

Method Path Purpose
GET /iclock/cdata handshake / config negotiation
POST /iclock/cdata data upload (attendance, photos, biodata, oplog)
GET /iclock/getrequest device polls for queued commands
POST /iclock/devicecmd device reports command results
GET /iclock/registry PUSH-SDK registration

On the device, point Comm → Cloud Server / ADMS Setup at your server's address and port (disable "Enable Domain Name" if you're using an IP). Deploy behind HTTPS — TLS is not terminated in-package.

You also need the device/command tables (see Persistence):

Device admission: trust but gate

Recording a device is never the same as trusting its data. Admission has two postures, set by auto_register:

A device is always in one of three states — pending, approved, or blocked (ZkTeco\ADMS\Registry\DeviceStatus).

Approving devices

Two artisan commands manage the fleet:

You can also approve programmatically through the registry contract (ZkTeco\ADMS\Registry\DeviceRegistry), resolvable from the container:

Reacting to uploaded data

Each kind of upload is parsed into a value object and dispatched as a Laravel event carrying the originating serial number as $connection. Listen to the ones you care about:

Event Fired for Payload
PunchReceived every attendance punch (ATTLOG / RTLOG) $record: AttendanceRecord, $connection: string
AttendancePhotoReceived punch-time photo (ATTPHOTO) $photo: AttendancePhoto, $connection: string
BiometricReceived biometric template (BIODATA, PUSH-SDK) $template: BiometricTemplate, $connection: string
UserReceived user synced from the device (USERINFO) $user: User, $connection: string
OperationLogged audit entry (enroll, delete, settings, power) $entry: OperationLog, $connection: string
DeviceRegistered a device registers for the first time $device: RegisteredDevice
CommandAcknowledged a queued command's outcome came back $command: QueuedCommand, $result: CommandResult

PunchReceived is the same event the socket listener fires (see zkteco:listen), so a single listener can absorb punches from both transports — telling them apart by $connection (a configured connection name from the socket path, a device serial from the ADMS path) if you need to.

Attendance photos and biometric blobs are handed to you as opaque bytes — the package never persists them for you:

Sending commands back to a device

ADMS is poll-based, so commands are asynchronous: you queue a typed command, the device drains it on its next getrequest poll, and the outcome arrives later as a CommandAcknowledged event. ZkTeco::push($serial) returns a fluent builder over an already-registered device (it throws CommandException::unknownDevice for an unknown serial):

Each call returns a QueuedCommand handle (its id correlates the later acknowledgement). The full set:

Method What it queues
reboot() / restart() reboot the device
powerOff() power the device off
enable() / disable() toggle device availability
clearData() wipe users + templates + attendance
clearLog() wipe the attendance log
clearPhoto() wipe stored photos
syncTime(?DateTimeImmutable $at = null) set the device clock
queryData(string $table) ask the device to re-upload a table
deleteUser(string $pin) delete a user by employee number
upsertUser(User $user) create or update a user
pushTemplate(BiometricTemplate $template) push a biometric template

Then react to outcomes:

Wire-format caveat: the power, SET OPTIONS, and data-write command layouts are provisional and not yet pinned against real hardware (the attendance/registration read path is). See Limitations.

Persistence (models & migrations)

These migrations are optional — the package never runs them for you. The service provider only publishes them; it does not auto-load them, so they exist only if you deliberately publish and migrate. Publishing zkteco-migrations copies three tables into your app, each with a matching Eloquent model under ZkTeco\Laravel\Models:

Table Model Holds
zkteco_devices Device registered devices: serial_number, protocol_generation, status, capabilities, stamps, last_seen_at
zkteco_commands Command queued/sent/acked commands: serial_number, command, status, return_code, sent_at, acknowledged_at
zkteco_attendance Attendance optional store for punches you choose to persist: uid, user_id, recorded_at, verify_mode, punch_state, connection

When do you actually need them?

How you use the package Migrations needed
Pure PHP core (ZkTeco\TCP / ZkTeco\ADMS, no Laravel) None — the core never touches a database.
Laravel + socket client only (ZkTeco::connection()) None — the TCP path doesn't persist anything.
Laravel + ADMS push endpoints zkteco_devices + zkteco_commands (see below)

zkteco_devices and zkteco_commands back the ADMS registry and command queue, so they are required only once you enable the push endpoints with the built-in Eloquent persistence. Bind your own DeviceRegistry / CommandQueue implementations (in-memory, Redis, …) and you can skip the tables entirely.

zkteco_attendance is always optional — it's an opt-in convenience store for your own listeners, and the package never writes to it itself.

Using the ADMS core without Laravel

The ADMS core (ZkTeco\ADMS) is framework-neutral. You can mount it on any HTTP stack by feeding requests to PushRouter and implementing the sink interfaces (AttendanceSink, AttendancePhotoSink, BiometricSink, UserSink, OperationLogSink) and the DeviceRegistry / CommandQueue contracts yourself. A runnable, dependency-free demo lives in examples/:


Value objects & enums

Value object Fields
User uid, userId, name, privilege, password, cardNumber, groupId
AttendanceRecord userId, recordedAt, verifyMode, punchState, uid
Template uid, fingerIndex, valid, data
OperationLog operation, code, operatorId, occurredAt, target, parameters
AttendancePhoto userId, capturedAt, image, contentType
BiometricTemplate userId, type, index, valid, data
Enum Cases
Privilege User (0), Enroller (2), Manager (6), Admin (14)
PunchState CheckIn, CheckOut, BreakOut, BreakIn, OvertimeIn, OvertimeOut, Undefined
VerifyMode Password, Fingerprint, Face, Card, Other
OperationType Startup, Shutdown, VerifyFailed, Alarm, MenuEntered, SettingsChanged, FingerprintEnrolled, PasswordEnrolled, CardEnrolled, UserDeleted, FingerprintDeleted, DataCleared, Other

The socket path fills AttendanceRecord->uid (the device slot); the ADMS path leaves it null and keys on userId, because the device doesn't send its internal slot over push.

Laravel integration reference

When installed inside a Laravel app the ZkTecoServiceProvider is auto-discovered. Configure socket connections in config/zkteco.php:

Resolve a configured socket Device through the facade:

Streaming socket punches (zkteco:listen)

The socket realtime stream is a blocking, infinite loop, so it needs its own long-running process — you can't run it inside an HTTP request. The zkteco:listen command is that daemon: it holds the connection open and fires a PunchReceived event for every punch.

Run it under a supervisor (Horizon, systemd, or supervisord) so it restarts after a dropped connection; it traps SIGINT/SIGTERM for graceful shutdown.

Reach for it only when you need to react the instant someone punches and you're dialing the device (socket path). If the device pushes to you over ADMS, you already get PunchReceived with no daemon. If periodic syncing is enough, schedule a job that calls attendance()->all() instead.

You want… Use
Push-on-punch from an always-on device ADMS endpoints + PunchReceived
Instant reaction while you hold the socket zkteco:listen
Periodic pull of the stored log A scheduled attendance()->all()

Artisan commands

Command Purpose
zkteco:listen {connection?} Stream live socket punches as PunchReceived events
zkteco:devices {--pending} List ADMS devices and their approval status
zkteco:approve {serial} {--block} Approve (or --block) an ADMS device by serial

Error handling

All failures derive from ZkTeco\Exceptions\ZkException (each carries a typed ErrorCode and a context array):

Localizing error messages

Every exception pairs a human-readable English getMessage() (for logs and developers) with two machine-stable fields that drive translation:

In a Laravel app the bridge resolves each code through zkteco::errors.<code>, passing context as the replacement bindings and falling back to the English getMessage() when no translation exists for the active locale. Any ZkException reaching the handler on a JSON request is rendered as { "message": "<localised>", "error_code": "<code>" } with HTTP 503.

To translate them:

This copies the catalogue to lang/vendor/zkteco/en/errors.php. Add a sibling locale folder using the same keys (the placeholders are filled from each exception's context):

Set the app locale and the JSON message switches accordingly. Outside Laravel, map errorCode->value to your own catalogue — the codes are stable, so no parsing of message strings is required.

The full list of keys lives in lang/en/errors.php, one per ErrorCode case.

Tested hardware

The socket protocol is verified end-to-end against a physical unit:

Property Value
Model MB2000/ID
Firmware Ver 6.60 May 14 2018
Transport TCP, port 4370, comm key 0

Verified on this device: handshake + metadata read, buffered user and attendance reads, clock set/read, user create/read/delete, fingerprint template upload (byte-for-byte round-trip), realtime event registration, and interactive fingerprint capture via CMD_STARTENROLL.

The enrollment event stream is firmware-specific and differs from pyzk's published sequence; enroll() was written against this firmware. Only the success path has been observed so far — failure result codes (e.g. duplicate finger) are not yet characterised.

A gated integration suite exercises all of the above against real hardware — see Testing.

Compatibility notes

The package targets the generic ZK protocol, not one firmware build, so other ZKTeco terminals are expected to interoperate. The following newer build has been checked at the protocol level but has not yet had a full end-to-end hardware run, so it is listed separately from the verified unit above:

Property Value
Model MB2000
Firmware Ver 8.0.4.5-20200729
Push Service Ver 2.0.30S (ADMS PushV2)

What this implies per path:

Limitations

Testing

The unit and Laravel suites use a fake transport and need no hardware. A separate Integration suite talks to a real device and is skipped unless ZKTECO_DEVICE_HOST is set:

Optional overrides: ZKTECO_DEVICE_COMM_KEY (default 0) and ZKTECO_DEVICE_TIMEOUT (seconds, default 5). The interactive enrollment test is additionally gated behind ZKTECO_ENROLL_INTERACTIVE=1 because it needs a person to physically press a finger on the sensor. The write-path tests are reversible by design (throwaway probe users that are cleaned up afterwards).

Design

Architecture decisions are recorded as ADRs in docs/adr/, and the ubiquitous language lives in CONTEXT.md.

License

MIT.


All versions of zkteco with dependencies

PHP Build Version
Package Version
Requires php Version ^8.3
ext-iconv Version *
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package msaied/zkteco contains the following files

Loading the files please wait ...