Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Php Zkteco Laravel Package

taha-rafi/php-zkteco

A PHP library for communicating with ZKTeco biometric attendance and access control devices over the network. Connect to a device to read user records, attendance logs, and device info, sync time, and manage settings, giving PHP applications a simple way to pull and work with data from ZKTeco termin...

View on GitHub
Deep Wiki
Context7

Getting Started

Install and let Laravel 8–12 auto-discovery do the wiring:

composer require .../php-zkteco
php artisan vendor:publish --tag=zkteco-config

Then set your device credentials in .env and confirm they map correctly in config/zkteco.php (host, port, protocol, comm key). Run php artisan config:cache after any config change — the facade resolves the client from cached config, and stale config is the most common "it worked in tinker but not in the queue" cause.

Where to look first:

  1. config/zkteco.php — the entire surface area of tunables (protocol, timeouts, comm key, webhook secret, cloud sync endpoint) is visible here before you read a line of source.
  2. src/Laravel/ZKTecoServiceProvider.php and src/Laravel/Facades/ZKTeco.php — the provider tells you what is bound as singleton vs. factory, and the facade's @method docblock is your fastest API reference.
  3. The v2.0 feature set (ATT_LOG writer, selective deletion, dual TCP/UDP, CommKey, AdmsServer, webhooks) is documented in the README's v2 section — read it before designing anything destructive.

First use case: a scheduled command that pulls attendance from one device into an Eloquent table. That single flow exercises connection, CommKey auth, log retrieval, and your dedupe strategy — the four things that break in production. Get that working before touching writes or deletion.

Implementation Patterns

Inject the client, don't sprinkle the facade. The facade is fine in controllers and commands, but for anything you want to test, resolve the underlying client from the container and type-hint it. You can then swap it with a fake in tests instead of hitting real hardware in CI.

One connection per job, not per record. Open the device connection inside a queued job, process all records for that device, close it. Long-lived connections across a worker's lifetime will silently die and surface as timeouts on the next job.

Multi-device fleets: fan out by device. Keep a devices array in config (or a DB table), and dispatch one job per device with Bus::batch() so a single unreachable terminal doesn't block the rest:

Bus::batch(
    collect(config('zkteco.devices'))->map(fn ($d) => new SyncDeviceAttendance($d))
)->dispatch();

Add ->allowFailures() — one dead terminal is normal, not an incident.

Attendance ingest → idempotent upsert. Devices retain logs until explicitly cleared, so every poll re-reads the same rows. Use a unique index on (device_sn, user_id, timestamp) and upsert(). Never insert().

Writing ATT_LOG (CMD_SET_DATA 3001) is for backfill and migration — seeding a replacement terminal from your database, or reconciling after a device swap. Treat it as a one-way migration step in a command with a --dry-run flag, not something in a request path.

Selective deletion (CMD_TIME_ATTLOG_DELE 10005) is a two-phase operation: export and persist locally first, verify row counts match, then delete. Never delete on the same pass that reads — if the read is partial you will destroy data you never stored.

Protocol choice: TCP for anything transactional (writes, deletions) because you get delivery confirmation. UDP for high-volume read polling on legacy/firmware-limited devices where speed matters more than ordering. Make it per-device config, not global — mixed fleets are the norm.

Push-based devices → AdmsServer + webhooks. For terminals that can POST to you, run the built-in AdmsServer rather than polling. Verify every inbound webhook with HMAC-SHA256 over the raw request body and hash_equals(); store the secret in config/env, never in the DB. Register the webhook route outside web middleware so CSRF doesn't reject device traffic.

Cloud POST sync is the escape hatch for edge sites: let the on-prem Laravel app talk to the device, and have it forward normalized records to your central API. Keep the payload schema identical to your local table so the receiver can reuse validation.

Gotchas and Tips

  • Port 4370 and the firewall. Nearly every "connection refused" is network, not code. Verify reachability with nc -zv <host> 4370 from the queue worker's host, not your laptop.
  • CommKey mismatch fails quietly. Wrong key often looks like an empty result set rather than an auth error. Always assert on a known-good read (getDeviceInfo-style call) before concluding a device has no attendance.
  • Device clock drift is real. Attendance timestamps are device-local. Normalize to UTC on ingest and store the device timezone per terminal, or you'll get duplicated and out-of-order shifts at DST boundaries.
  • Deletion is unrecoverable. There is no undo on 10005. Log the exact user/timestamp range before issuing it, and gate the command behind an explicit --confirm flag.
  • Webhook replays. HMAC proves authenticity, not freshness. Check a timestamp header and reject anything older than a few minutes, plus dedupe on a device-supplied event ID.
  • Socket timeouts in workers. Set explicit connect/read timeouts in config/zkteco.php. A hung socket will eat a queue worker for the full default timeout and cascade into a backed-up queue.
  • PHP 8.4 in the CI matrix is new in v2.1.0. If you're on 8.0–8.3 you're covered, but run your own suite on 8.4 before upgrading production — implicit-nullable and dynamic-property deprecations are the usual casualties in socket-heavy code.
  • config:cache after publish. Editing config/zkteco.php on a cached deployment has no effect. Add config recaching to your deploy script, not your muscle memory.
  • Extension points: the service provider binds the client; if you need a custom transport or logging decorator, rebind it in your own AppServiceProvider rather than forking the package. The facade resolves whatever is bound, so a decorator gives you request/response logging for free during debugging.
  • Test with a fake, not a device. Hardware in CI is a flake generator. Fake the client binding for feature tests and reserve real-device runs for a manual smoke check after upgrades.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle