Repository: cobisja/tad-php
Branch: master
Commit: fed8050bcb65
Files: 20
Total size: 141.4 KB
Directory structure:
gitextract_4ybaawxs/
├── .gitignore
├── LICENSE
├── README.md
├── composer.json
├── lib/
│ ├── Exceptions/
│ │ ├── ConnectionError.php
│ │ ├── FilterArgumentError.php
│ │ ├── UnrecognizedArgument.php
│ │ └── UnrecognizedCommand.php
│ ├── Providers/
│ │ ├── TADSoap.php
│ │ └── TADZKLib.php
│ ├── TAD.php
│ ├── TADFactory.php
│ └── TADResponse.php
├── phpunit.xml
└── test/
├── helpers/
│ └── ClassReflection.php
└── lib/
├── Providers/
│ ├── TADSoapTest.php
│ └── TADZKLibTest.php
├── RealTADTest.php
├── TADResponseTest.php
└── TADTest.php
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
index.php
coverage/**/*
nbproject/**/*
vendor/**/*
================================================
FILE: LICENSE
================================================
Copyright (c) 2014-2015 Jorge Cobis <jcobis@gmail.com>
MIT License
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.
================================================
FILE: README.md
================================================
# TAD-PHP
A simple PHP class to interacts with ZK Time & Attendance Devices.
##About
TAD: A class that implements an interface to interacts with ZK Time & Attendance devices.
Documentation found about ZK SOAP api is very limited or poor, however TAD class implements most SOAP functions supported by ZK devices. Specifically TAD class exposes the following 35 methods:
```
get_date, get_att_log, get_user_info, get_all_user_info, get_user_template, get_combination, get_option, get_free_sizes, get_platform, get_fingerprint_algorithm, get_serial_number, get_oem_vendor, get_mac_address, get_device_name, get_manufacture_time, get_antipassback_mode, get_workcode, get_ext_format_mode, get_encrypted_mode, get_pin2_width, get_ssr_mode, get_firmware_version, set_date, set_user_info, set_user_template, delete_user, delete_template, delete_data, delete_user_password, delete_admin, enable, disable, refresh_db, restart, and poweroff.
```
All methods above are implemented by 2 classes: **Providers\TADSoap** and **Providers\TADZKLib**.
There are some SOAP functions supported by ZK devices that it's suppossed, according to the official docs (which incidentally it's very limited and so poor!!!) must show an expected behaviour, but when they are invoked don't work like is expected, so they become useless (e.g. Restart SOAP call). For these situations, TAD class implement them by **Providers\TADZKLib** class ([PHP_ZKLib] - http://dnaextrim.github.io/php_zklib/). This class takes a different approach to "talk to the device": it uses UDP protocol at device standard port 4370.
PHP_ZKLib class it's been fully integrated, after a refactoring process taking out all duplicated code (DRY).
For practical purposes, you don't have to be worried about when to use TAD class or PHP_ZKLib class because you only have to get a TAD instance (as shown below) and call any of its methods available. The class decides about when runs the method invoked using TAD class or PHP_ZKLib class.
##Requirements
* Any flavour of PHP 5.4+
* PHPUnit to execute the test suite (optional).
## Supported devices
* All ZK Time & Attendance devices with web server built-in (with ZEM600 or less).
##Getting started
###Setting up the environment
After download TAD-PHP, you have 2 ways to get your enviroment configured to use the classes:
####Composer
[Composer](https://getcomposer.org) is the PHP's package manager and is the recommended way to get packages for your projects. It's also able to build automatically ***autoloaders*** if you wrote down your code using PSR-0 and/or PSR-4 standards, avoiding you headaches about everything related to loading classes.
**TADPHP** is built follows PSR-4 standard and comes with a specific file named **composer.json** that allows **Composer** to generate a file named **autoload.php** (beside others files of course). This files generated is the only one you need to include in your project to get all classes required by TADPHP loaded in memory:
1. Install Composer:
```
curl -s https://getcomposer.org/installer | php
```
2. Get inside TADPHP root folder and generate the **autoload.php** file:
```
php composer.phar dump-autoload
```
The command above will generate a folder called **vendor**. Inside of it, you'll see the **autoload.php**
3. Require/Include **autoload.php** file in the **index.php** of your project or whatever file you need to use **TAD-PHP** classes:
```php
<?php
require 'vendor/autoload.php';
...
```
####Loading TAD-PHP classes by hand
Even if Composer it's the preferred method to generate the files needed to get all classes loaded, maybe you want to do the task by hand:
1. Copy and paste TAD-PHP folder in your project root.
2. Rename TAD-PHP folder to use a shorter name (for example 'tad').
3. Require/Include all classes required by TAD-PHP using the relative TAD-PHP path
```php
<?php
require 'tad/lib/TADFactory.php';
require 'tad/lib/TAD.php';
require 'tad/lib/TADResponse.php';
require 'tad/lib/Providers/TADSoap.php';
require 'tad/lib/Providers/TADZKLib.php';
require 'tad/lib/Exceptions/ConnectionError.php';
require 'tad/lib/Exceptions/FilterArgumentError.php';
require 'tad/lib/Exceptions/UnrecognizedArgument.php';
require 'tad/lib/Exceptions/UnrecognizedCommand.php';
```
####Handling namespaces
All TAD-PHP classes are under the namespace named **TADPHP**. So, to use any class you need to use the **Fully qualified class name**. For example, to get a new instance of **TADFactory class** you need to use:
```php
<?php
...
$tad_factory = new TADPHP\TADFactory();
...
```
However, as your project grows up using fully qualified class names becomes annoying, so it's better to use PHP **USE** sentence:
```php
<?php
...
use TADPHP\TADFactory;
use TADPHP\TAD;
...
$comands = TAD::commands_available();
$tad = (new TADFactory(['ip'=>'192.168.100.156', 'com_key'=>0]))->get_instance();
...
```
###Class instantiation
First, instantiate a TADFactory object, then use it to create a TAD object.
```php
<?php
...
$tad_factory = new TADFactory(['ip'=>'192.168.0.1']);
$tad = $tad_factory->get_instance();
...
```
Or you can get a TAD object in one single step (valid only in PHP 5.4+):
```php
<?php
$tad = (new TADFactory(['ip'=>'192.168.0.1']))->get_instance();
```
You can customize TAD object traits passing an options array:
```php
<?php
$options = [
'ip' => '192.168.0.1', // '169.254.0.1' by default (totally useless!!!).
'internal_id' => 100, // 1 by default.
'com_key' => 123, // 0 by default.
'description' => 'TAD1', // 'N/A' by default.
'soap_port' => 8080, // 80 by default,
'udp_port' => 20000 // 4370 by default.
'encoding' => 'utf-8' // iso8859-1 by default.
];
$tad_factory = new TADFactory($options);
$tad = $tad_factory->get_instance();
```
##TAD API
SOAP API is implemented by **TADSoap class**. All methods that use UDP Protocol are implemented by **PHP_ZKLib class**. Even though you have 2 classes, you do not have to be worried about which method is been calling using SOAP api or through PHP_ZKLib. You've got a single interface.
Some methods need that you set up some parameters prior you can call them. TAD class uses associative arrays as way to pass params to the methods. Using associative arrays is a "more verbose way" that helps you to remember which params you have to pass.
Valid params supported by TAD class are:
```
com_key, pin, time, template, name, password, group, privilege, card, pin2, tz1, tz2, tz3, finger_id, option_name, date, size, valid, value
```
As you can see, params names are so intuitive and easy to remember.
######Note: All examples shown below asusmes $tad variable holds a TAD object.
###Getting a list of commands available
```php
// Get a full list of commands supported by TADPHP\TAD class.
$commands_list = TAD::commands_available();
// Get a list of commands implemented via TADPHP\TADSoap.
$soap_commands = = TAD::soap_commands_available();
// Get a list of commands implemented via TAD\PHP\TADSoap.
$zklib_commands = TAD::zklib_commands_available();
```
###Getting and Setting Date and Time
```php
// Getting current time and date
$dt = $tad->get_date();
// Setting device's date to '2014-01-01' (time will be set to now!)
$response = $tad->set_date(['date'=>'2014-01-01']);
// Setting device's time to '12:30:15' (date will be set to today!)
$response = $tad->set_date(['time'=>'12:30:15']);
// Setting device's date & time
$response = $tad->set_date(['date'=>'2014-01-01', 'time'=>'12:30:15']);
// Setting device's date & time to now.
$response = $tad->set_date();
```
###Getting attendance logs
You can retrieve attendance logs for all user or just for one:
```php
// Getting attendance logs from all users.
$logs = $tad->get_att_log();
// Getting attendance logs from one user.
$logs = $tad->get_att_log(['pin'=>123]);
```
###Getting information about users.
You can get all information about a single user or all users. The information you can get include:
* PIN: Internal user's ID (this is an id generated by the device).
* Name: User's name.
* Password: Password used to check in/out.
* Card: Card number (relevant if you device supports RFID technology).
* Privilege: User's role (1: regular user, 2: enroller, 6: admin and 14: superadmin)
* Group: User's group privilege.
* PIN2: Personal identity number (this is an id you can set according your needs).
* TZ1: User's time zone 1.
* TZ2: User's time zone 2.
* TZ3: User's time zone 3:
```php
// Getting info from a specific user.
$user_info = $tad->get_user_info(['pin'=>123]);
// Getting info from all users.
$all_user_info = $tad->get_all_user_info();
```
###Creating / Updating users
TAD class allows you to register new users in the device or even you can update (change) information about an user already registered. However to achieve this, TAD class needs to delete the user (of course this applies when you are updating user's information) and then creates the user. Maybe this is not the best way to do that, but if TAD just calls the method to create a user, it will be created as many times as you call it.
If you look into PHP_ZKLib code, you'll see a method to create / update users. However, when you call that method, it generates a PIN code (not PIN2 code) in a way that if that code already exists in the device, it refuses to create the user. This is a method that should be modified to make it working properly but the way how PIN code is created is unknown.
In the meantime, TAD class uses delete and create SOAP calls. Of course, to make things easy for you, you have to call just 1 method.
```php
// We are creating a superadmin user named 'Foo Bar' with a PIN = 123 and password = 4321.
$r = $tad->set_user_info([
'pin' => 123,
'name'=> 'Foo Bar',
'privilege'=> 14,
'password' => 4321
]);
```
######Note: The way TAD class creates / updates users has one big concern you have to be aware. The user's fingerprints stored (templates) are lost!!!, so it is necessary to save them prior you call set_user_info() method.
###Uploading user's fingerprints
The device uses an algorithm to encode fingerprints called "BioBridge" and it has 2 flavors: VX 9.0 and the new one VX 10.0. According the documentation, VX 10.0 generates shorter encoded fingerprints and it's faster when the device has to make searchings for a fingerprint match process. However, TAD class exposes a method to upload fingerprints but it works only when device is configured to use the old BioBridge VX 9.0 algorithm. When device uses VX 10.0 algorithm, the machine freezes!!!. When asked to ZK Software forum, the answer got was: "It has to work with any biobridge version. Check your code!". Any help about this, would be appreciated.
```php
/** Setting a user template (fingerprint).
*
* You can upload until 10 templates per user. You have to use 'finger_id' param to indicate
* which fingerprint you are uploading to.
*
* Till now, this method only works with VX 9.0 BioBridge algorithm :-(. Any help
* arround this issue will be appreciated.
*/
// The folowing string represents a fingerprint encoded using BioBridge algorithm VX 9.0
$template1_vx9 = "ocosgoulTUEdNKVRwRQ0I27BDTEkdMEONK9KQQunMVSBK6VPLEENk9MwgQ+DP3PBC1FTXEEG4ihpQQQ3vFQBO4K+WwERYilHAQ8ztktBEBbKQ0ELDtJrwQ7dqCiBCz+/IgEGKrBjQQhEO0zBFQNDQYEKFbhrQQdLF1wBDxclfUELMNFXwQRvvmHBCslKUAEZfU1OQRzmIU5BXRW0eoEKPMltgQnQGUyBJQSfRIEUSzIdAQ45l3gBByHUTMEJ5yVhQQmi0UZBFHvYPUEGeKxTAQ6rFGNBCIYURoEOZS9VwR+1M4RoE5m0DRUTF8DHd6HdqxHAxWmj393M28DDX2FkanKi/t7LGsDCWqGarmt1BaL/25nAwVaiipu/cgcQGKG6mcDBU6KYmr5wChQcobmJIsDBUKKJmZ1uExyi+ZaYwMFMgU2CQCSinYdnJsDBR4Ghl3Q4owa3dnfAwUamdlZlR5p2Zi7AwUSndERlfOpWZlfAwUOiQzVkLDhDopRUVTLAwT2iQ0ZjIzVMolNFRcDBN6I0ZlQebVaiEjRVwMEyolVVUxVxXKEBRUTAwS+iZVYyD3JhoQJFTMDBLKJlVUIKcWShBVVTwMIkoWVkFQhyaaEVZ1rAwh6hVlUPAW+iNGd3wMIToWdlBnWiRWZ3aMDDCqRmZjRpZmrAxASjd2Vnh2/gAA==";
$template1_data = [
'pin' => 123,
'finger_id' => 0, // First fingerprint has 0 as index.
'size' => 514, // Be careful, this is not string length of $template1_vx9 var.
'valid' => 1,
'template' => $template1_vx9
];
$tad->set_user_template( $template1_data );
```
###Deleting user's fingerprints
When you have to delete user's fingerprints, you delete all of them. You cannot delete fingerprint one by one.
```php
// Delete all fingerprints (template) for user with PIN = 123.
$tad->delete_template(['pin'=>123]);
```
###Deleting user's passwords
```php
// Delete password for user with PIN = 123.
$tad->delete_user_password(['pin'=>123]);
```
###Deleting users
```php
/// Delete user with PIN = 123.
$tad->delete_user(['pin'=>123]);
```
###Deleting admin users
From the device point of view, users that have a privilege level not equal to 0, are considered admin users, so this method enables you wipe them out with one single step.
```php
$tad->delete_admin();
```
###Clearing out data from device
You can clear information from device according a code you specify.
Code meaning:
1: clear all device data, 2: clear all users templates and 3: clear all attendance logs.
```php
// Delete all attendance logs stored.
$tad->delete_data(['value'=>3]);
```
###Getting some statistics from the device.
You can get some valuable statistics about your device including:
* Space available for templates.
* Space available for attendance logs.
* Total storage capacity for attendance logs.
* Total storage capacity for user templates.
* Total users stored
* Total user passwords stored.
* Total attendance logs stored.
* Total templates stored.
```php
// Get some device statistics.
$fs = $tad->get_free_sizes();
```
###Disabling / Enabling the device
Sometimes you need to lock device's screen and keypad to prevent users can use it. You can disable the device and when you want to get it back working, just enable it!
```php
// Disabling device.
$tad->disable();
...
// Enabling device.
$tad->enable();
```
###Restarting / Shutting down the device.
```php
// Restart the device!!!
$tad->restart();
...
// Shut down the device!!!
$tad->poweroff();
```
##TADResponse API
Every command executed via TAD class will return an object that is an instance of **TADResponse** class. This object contains all information about the device's response received. This way you can get full flexibility to manipulate the responses that you got from the device: you can transform it in XML, JSON or even you can get an array. Also you can set some criterias to make a filtering process on the response.
**TADResponse** class offers 14 methods:
```
get_response, set_response, get_encoding, set_encoding, get_header, set_header, get_response_body, to_xml, to_json, to_array, count, is_empty_response, filter_xml and filter_by
```
### Getting and Setting responses
When you call any TAD method, all responses (including those empty ones) are returned as an **TADResponse object**, so you can invoke any of methods mentioned above:
```php
$r= $tad->get_date();
// Get response in XML format.
$xml_date = $r->get_response();
// Or you can specify the format.
$json_date = $r->get_response(['format'=>'json']);
// Or you want to get just response's body (without XML header).
$raw_response = $r->get_response_body(); // This always will be in XML format!!!
```
Sometimes you would like to build a TADResponse object by hand:
```php
$response = '<Response><Name>Foo</Name><Address>Bar</Address></Response>';
$encoding = 'iso8859-1';
$tr = new TADResponse($response, $encoding);
...
// Maybe later you want to change the response you set before
$new_response = '<CrazyResponse><Joke>foo bar taz</Joke></CrazyResponse>';
$tr->set_response($new_response);
...
```
###Getting and Setting response's encoding
```php
$r = $tad->get_date();
// Get current response's encoding.
$encoding = $r->get_encoding();
// Perhaps you want to change response's encoding.
$r->set_encoding('utf-8');
```
###Getting and Setting response's header
Instead of getting a full XML response, you can get just the header's response and you can change it even:
```php
$r = $tad->get_date();
$header = $r->get_header();
// Method above returns '<?xml version="1.0" encoding="iso8859-1" standalone="no"?>'
$new_header = '<?xml version="1.1" encoding="utf-8" standalone="yes"?>';
// Lets set a new response's header.
$r->set_header($new_header);
```
###Transform device's responses in XML, JSON or Array format.
As you seen above, you can get device's responses in different formats using **get_response() method** and specifying the format you want. However, you can use the following methods too:
```php
// Get attendance logs for all users.
$att_logs = $tad->get_att_logs(); // $att_logs is an TADResponse object.
// Get response in XML format.
$xml_att_logs = $att_logs->to_xml();
// Get response in JSON format.
$json_att_logs = $att_logs->to_json();
// Get an array from response.
$array_att_logs = $att_logs->to_array().
// Lets get an XML response in one single step.
$xml = $tad->get_att_logs()->to_xml();
```
###Counting how many items has the response
When you are interested just in how many items has the response, just count them:
```php
$att_logs = $tad->get_att_logs();
// Get just the number of logs you retrived.
$logs_number = $att_logs->count();
```
###Dealing with empty responses
Sometimes some queries to the device returns an empty answer. Because the original response from the device is in XML format, to know if you got any relevant data, you should have to parse the responses. That's not very handy:
```php
$r = $tad->get_att_logs(['pin'=>123]); // This employee does not have any logs!!!
if ($r->is_empty_response()) {
echo 'The employee does not have logs recorded';
}
...
```
###Filtering response according your needs!!!
As you saw above, all device's responses are handled by **TADResponse class**. You get the raw XML but you always get the whole set. What if you you'd like to do some kind of processing on reponses? Now, you can process the whole XML response by applying filters. This way, you can get just XML responses that really needs.
```php
// Get attendance logs for all users;
$att_logs = $tad->get_att_logs();
// Now, you want filter the resulset to get att logs between '2014-01-10' and '2014-03-20'.
$filtered_att_logs = $att_logs->filter_by_date(
['start' => '2014-01-10','end' => '2014-03-20']
);
// Maybe you need to be more specific: get logs of users with privilege of Enroller
// that generated a 'check-out' event after '2014-08-01'.
$filtered_att_logs = $att_logs->filter_by_privilege_and_status_and_date(
2, // 2 = Enroller role.
1, // 1 = Check-out.
['start'] => '2014-08-01'
);
// You can do specific string searching too!!!
$users = $tad->get_all_user_info();
// Searches for all employees with the string 'Foo' as part of its name.
$foo_employees = $users->filter_by_name(['like'=>'Foo']);
```
Notes:
* The original response is lost! because it is replaced with the filtered response.
* If you do a **filter_by** using a non exists tag, you'll always get an **empty response**.
* When you want to specify specific ranges you have to use an associative array with keys **'start'** (indicates where range begins), and **'end'** (where range ends).
* **greater than** ranges are indicated by passing only **'start'** key.
* **less than** ranges are indicated by passing only **'end'** key.
* To perform searches (filtering) to match just partial strings, you have to use the key **'like'** as you saw in the example above.
* To perform a full match search (filtering) you have to pass the string directly, without use an array.
* To filter by 1 exact value you have to pass just the value (not an array!). However, if by any reason you decide to use an array, both keys have to have the same value.
* If you want to build a very specific filter, you have to use **filter_xml()** method. Using it, you are able to built a customized regex to define how the XML have to be processed.
##Todo
**TADPHP** is not perfect!!!. As mentioned at the beggining, it's been developed after hours, and hours, and hours of googling and it's been tested using just Fingertec Q2i Time & attendance device (that it's I have in my work), so it's possible that you can find errors when you use it with others devices or even you can find better ways to do the things. For that reason, there are some things to do:
* Make TAD-PHP works perfectly on devices with ZEM greater than 600 (with ZEM800 almost everything works as expected, but there are still some bugs).
* Make set_user_template() method works with BioBridge VX 10.0 algorithm.
* Find out how to customize the PIN code generation in the PHP_ZKLib zk_set_user_info() method.
* Test TAD method get_option(). This method allows you getting detailed information about the device, but it's necessary to set its argument to a valid option name. However, these names are not available, and according to documentation ZK Software can give you all options names but you have to pay for them.
* Enhance PHP_ZKLib to allows more sophisticated functions like uploading user's photo for example.
##Author
[Jorge Cobis](<mailto:jcobis@gmail.com>) - <http://twitter.com/cobisja>.
By the way, I'm from Bolivarian Republic of Venezuela :-D
##Contributing
Feel free to contribute!!!. Welcome aboard!!!
##Misc
###Version history
***0.4.2** (Saturday, 17th January 2015)
* Improved directions to get TAD-PHP running.
* Some minor documentation changes.
**0.4.1** (Friday, 16th January 2015)
* Enhanced **TADZKLib class** by adding 14 new methods to get operating information about the device.
* Some minor bug fixes.
* Some fixes in documentation.
**0.4.0** (Sunday, 11th January 2015)
* Wiped out **TADHelpers class**. All its behavior it's been implemented into **TADResponse class**.
* All classes has been refactored according the new behavior associated to **TADResponse class**.
* All test suite has been reviewed and upgraded.
* Changed global **Provider namespace** to get a consistent namespacing schema.
* Some minor bugs fixes.
* Improved **TADZKLib class** documentation.
* Some fixes in documentation.
**0.3.2** (Saturday, 3rd January 2015)
* Implemented a ***dynamic xml filter*** that allows you to build single or multiple filtering criterias based on XML tags of response.
* Refactoring of tests.
* Some bug fixes in documentation.
**0.3.1** (Monday, 29th December 2014)
* Some improvements in README.md.
* Refactored XML response generated by Providers\TADZKLib class.
* Some refactoring in tests to ajust them to the refactored XML response generated by Providers\TADZKLib class.
* Tests DRYed.
* Some bug fixtures.
**0.3.0** (Saturday, 27th December 2014)
* Encoding option added to options set used when you instantiate the TADPHP\TADFactory class. With this options you can customized encoding for both SOAP requests and responses.
* Add a new Test class (RealTADTest) the allows you to run tests against a real Time & Attendance device.
* General refactoring according the new encoding options added.
* Some bug fixes.
* General code formatting to ajust it to PSR-1 and PSR-2 (it is not completed yet!).
* Some improvements in README.md
**0.2.0** (Wednesday, 24th December 2014)
* Some refactoring to make TADPHP\TADSAP, Providers\TADSOAP and Providers\TADZKLib classes simpler.
* TADPHP\TADHelpers refactored to contains just methods related with TADPHP\TAD class responses.
* Some bug fixes in test suite.
* Some improvements in README.md
**0.1.0** (Monday, 22nd December 2014)
* Initial public release.
##License
Copyright (c) 2014 Jorge Cobis (<jcobis@gmail.com>)
MIT License
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.
================================================
FILE: composer.json
================================================
{
"name": "TAD-PHP",
"type": "library",
"description": "PHP class that allows you interact with ZK Time & Attendance devices.",
"keywords": [
"attendance",
"time",
"ZK",
"tad"
],
"license": "MIT",
"authors": [
{
"name": "Jorge Cobis",
"email": "jcobis@gmail.com"
}
],
"require": {
"php": ">=5.3.0",
"ext-soap": "*",
"ext-sockets": "*"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
},
"autoload": {
"psr-4": {
"TADPHP\\": "lib/",
"TADPHP\\Exceptions\\": "lib/Exceptions/",
"TADPHP\\Providers\\": "lib/Providers/",
"Test\\Helpers\\": "test/helpers/"
}
}
}
================================================
FILE: lib/Exceptions/ConnectionError.php
================================================
<?php
/*
* tad-php
*
* (The MIT License)
*
* Copyright (c) 2014 Jorge Cobis <jcobis@gmail.com / http://twitter.com/cobisja>.
*
* 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.
*/
namespace TADPHP\Exceptions;
class ConnectionError extends \Exception
{
}
================================================
FILE: lib/Exceptions/FilterArgumentError.php
================================================
<?php
/*
* tad-php
*
* (The MIT License)
*
* Copyright (c) 2014 Jorge Cobis <jcobis@gmail.com / http://twitter.com/cobisja>.
*
* 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.
*/
namespace TADPHP\Exceptions;
class FilterArgumentError extends \Exception
{
}
================================================
FILE: lib/Exceptions/UnrecognizedArgument.php
================================================
<?php
/*
* tad-php
*
* (The MIT License)
*
* Copyright (c) 2014 Jorge Cobis <jcobis@gmail.com / http://twitter.com/cobisja>.
*
* 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.
*/
namespace TADPHP\Exceptions;
class UnrecognizedArgument extends \Exception
{
}
================================================
FILE: lib/Exceptions/UnrecognizedCommand.php
================================================
<?php
/*
* tad-php
*
* (The MIT License)
*
* Copyright (c) 2014 Jorge Cobis <jcobis@gmail.com / http://twitter.com/cobisja>.
*
* 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.
*/
namespace TADPHP\Exceptions;
class UnrecognizedCommand extends \Exception
{
}
================================================
FILE: lib/Providers/TADSoap.php
================================================
<?php
/*
* tad-php
*
* (The MIT License)
*
* Copyright (c) 2014 Jorge Cobis <jcobis@gmail.com / http://twitter.com/cobisja>.
*
* 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.
*/
namespace TADPHP\Providers;
use TADPHP\TADResponse;
/**
* TADSoap: class that allows to interact with a Time & Attendance device using SOAP.
*/
class TADSoap
{
const XML_FAIL_RESPONSE = 'Fail!';
const XML_SUCCESS_RESPONSE = 'Succeed!';
const SOAP_VERSION = SOAP_1_1;
/**
* @var array SOAP commands array supported by the class.
*/
static private $soap_commands_available = [
'get_date' => '<GetDate><ArgComKey>%com_key%</ArgComKey></GetDate>',
'get_att_log' => '<GetAttLog><ArgComKey>%com_key%</ArgComKey><Arg><PIN>%pin%</PIN></Arg></GetAttLog>',
'get_user_info' => '<GetUserInfo><ArgComKey>%com_key%</ArgComKey><Arg><PIN>%pin%</PIN></Arg></GetUserInfo>',
'get_all_user_info' => '<GetAllUserInfo><ArgComKey>%com_key%</ArgComKey></GetAllUserInfo>',
'get_user_template' => '<GetUserTemplate><ArgComKey>0</ArgComKey><Arg><PIN>%pin%</PIN><FingerID>%finger_id%</FingerID></Arg></GetUserTemplate>',
'get_combination' => '<GetCombination><ArgComKey>%com_key%</ArgComKey></GetCombination>',
'get_option' => '<GetOption><ArgComKey>%com_key%</ArgComKey><Arg><Name>%option_name%</Name></Arg></GetOption>',
'set_user_info' => [ '<DeleteUser><ArgComKey>%com_key%</ArgComKey><Arg><PIN>%pin%</PIN></Arg></DeleteUser>', '<SetUserInfo><ArgComKey>%com_key%</ArgComKey><Arg><Name>%name%</Name><Password>%password%</Password><Group>%group%</Group><Privilege>%privilege%</Privilege><Card>%card%</Card><PIN2>%pin%</PIN2><TZ1>%tz1%</TZ1><TZ2>%tz2%</TZ2><TZ3>%tz3%</TZ3></Arg></SetUserInfo>'],
'set_user_template' => '<SetUserTemplate><ArgComKey>%com_key%</ArgComKey><Arg><PIN>%pin%</PIN><FingerID>%finger_id%</FingerID><Size>%size%</Size><Valid>%valid%</Valid><Template>%template%</Template></Arg></SetUserTemplate>',
'delete_user' => '<DeleteUser><ArgComKey>%com_key%</ArgComKey><Arg><PIN>%pin%</PIN></Arg></DeleteUser>',
'delete_template' => '<DeleteTemplate><ArgComKey>%com_key%</ArgComKey><Arg><PIN>%pin%</PIN></Arg></DeleteTemplate>',
'delete_user_password'=> '<ClearUserPassword><ArgComKey>%com_key%</ArgComKey><Arg><PIN>%pin%</PIN></Arg></ClearUserPassword>',
'delete_data' => '<ClearData><ArgComKey>%com_key%</ArgComKey><Arg><Value>%value%</Value></Arg></ClearData>',
'refresh_db' => '<RefreshDB><ArgComKey>%com_key%</ArgComKey></RefreshDB>',
];
/**
* @var SOAPClient Holds a <code>\SoapClient</code> instance.
*/
private $soap_client;
/**
* @var array Options array required by <code>SoapClient</code> class.
*/
private $soap_client_options;
/**
* Returns commands available by the class.
*
* @param array $options options to define the information level about commands available by the class.
* @return array commands available list.
*/
static public function get_commands_available(array $options = [])
{
return (isset($options['include_command_string']) && $options['include_command_string']) ?
self::$soap_commands_available : array_keys(self::$soap_commands_available);
}
/**
* Build a <code>TADSoap</code> instance to allow communication with the device via SOAP api.
*
* @param \SoapClient $soap_client <code>SoapClient</code> instance
* @param array $soap_client_options options required by <code>SoapClient</code> class.
*/
public function __construct(\SoapClient $soap_client, array $soap_client_options)
{
$this->soap_client = $soap_client;
$this->soap_client_options = $soap_client_options;
}
/**
* Get a command, build the SOAP request and send it to device.
*
* @param mixed $soap_command command to be executed.
* @param array $soap_command_args command arguments.
* @return string response.
*/
public function execute_soap_command($soap_command, array $soap_command_args, $encoding)
{
$soap_location = $this->get_soap_provider_options()['location'];
$soap_request = $this->build_soap_request($soap_command, $soap_command_args, $encoding);
$response = !is_array($soap_request) ?
$this->execute_single_soap_request($soap_request, $soap_location) :
$this->execute_multiple_soap_requests($soap_request, $soap_location);
return new TADResponse($response, $encoding);
}
/**
* Returns params required by <b><code>SoapClient</code></b> class.
*
* @return array params list.
*/
public function get_soap_provider_options()
{
return $this->soap_client_options;
}
/**
* Returns the SOAP request based on command.
*
* @param string $command command requested.
* @param array $args command params.
* @return string SOAP request.
*/
public function build_soap_request($command, array $args, $encoding)
{
$command_string = $this->get_command_string($command);
$soap_request = $this->parse_command_string($command_string, $args);
if (!is_array($soap_request)) {
$soap_request = $this->normalize_xml_string($soap_request, $encoding);
} else {
$soap_request = array_map(
function ($soap_request) use ($encoding) {
return $this->normalize_xml_string($soap_request, $encoding);
},
$soap_request
);
}
return $soap_request;
}
/**
* Returns command SOAP definition.
*
* @param string $key SOAP command requested.
* @return string SOAP definition.
*/
private function get_command_string($key)
{
return self::$soap_commands_available[$key];
}
/**
* Sends a SOAP command to device.
*
* @param mixed $soap_request SOAP command.
* @param string $soap_location URI required by SOAP service.
* @return string device response.
*/
private function execute_single_soap_request($soap_request, $soap_location)
{
return $this->soap_client->__doRequest($soap_request, $soap_location, '', self::SOAP_VERSION);
}
/**
* Sends multiple SOAP commands to the device.
*
* @param mixed $soap_requests SOAP commands array.
* @param string $soap_location URI required by SOAP service.
* @return string device response (Always returns the last command response.)
*/
private function execute_multiple_soap_requests(array $soap_requests, $soap_location)
{
foreach ($soap_requests as $soap_request) {
$result = $this->execute_single_soap_request($soap_request, $soap_location);
}
return $result;
}
/**
* Parses SOAP request, replacing formal params with actual params.
*
* @param string $command_string SOAP request.
* @param array $command_args actual args values required by SOAP request.
* @return string SOAP request parsed.
*/
private function parse_command_string($command_string, array $command_args)
{
$parseable_args = array_map(
function($item) {
return '%' . $item . '%';
},
array_keys($command_args)
);
$parsed_command = str_replace($parseable_args, array_values($command_args), $command_string);
return $parsed_command;
}
/**
* Build an XML header.
*
* @param string $xml XML string which header will be added to.
* @param string $encoding encoding
* @return string full XML string (Header + Body).
*/
public static function normalize_xml_string($xml, $encoding = 'utf-8')
{
$xml ='<?xml version="1.0" encoding="' . $encoding . '" standalone="no"?>' . $xml;
return trim(str_replace([ "\n", "\r" ], '', $xml));
}
}
================================================
FILE: lib/Providers/TADZKLib.php
================================================
<?php
/*
* tad-php
*
* (The MIT License)
*
* Copyright (c) 2014 Jorge Cobis <jcobis@gmail.com / http://twitter.com/cobisja>.
*
* 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.
*/
namespace TADPHP\Providers;
use TADPHP\TADResponse;
/**
* TADZKlib: class that allows to interact with a Time & Attendance device using UDP protocol.
*
* This is a modified class of PHP_ZKLib (@link http://dnaextrim.github.io/php_zklib/ )
*
* This class has been modified by refactoring most of methods, taking out all duplicated code. The
* original behavior it's been kept.
*/
class TADZKLib
{
const USHRT_MAX = 65535;
const CMD_CONNECT = 1000;
const CMD_EXIT = 1001;
const CMD_ENABLEDEVICE = 1002;
const CMD_DISABLEDEVICE = 1003;
const CMD_RESTART = 1004;
const CMD_POWEROFF = 1005;
const CMD_ACK_OK = 2000;
const CMD_ACK_ERROR = 2001;
const CMD_ACK_DATA = 2002;
const CMD_PREPARE_DATA = 1500;
const CMD_DATA = 1501;
const CMD_USERTEMP_RRQ = 9;
const CMD_ATTLOG_RRQ = 13;
const CMD_CLEAR_DATA = 14;
const CMD_CLEAR_ATTLOG = 15;
const CMD_WRITE_LCD = 66;
const CMD_GET_TIME = 201;
const CMD_SET_TIME = 202;
const CMD_VERSION = 1100;
const CMD_AUTH = 1102;
const CMD_DEVICE = 11;
const CMD_CLEAR_ADMIN = 20;
const CMD_SET_USER = 8;
const CMD_GET_FREE_SIZES = 50;
const EMPTY_STRING = '';
const CUSTOMIZED_COMMAND_STRING = null;
const DEVICE_GENERAL_INFO_STRING_LENGTH = 184;
const XML_FAIL_RESPONSE = 'Fail!';
const XML_SUCCESS_RESPONSE = 'Successfully!';
/**
* @var string Device's ip address.
*/
private $ip;
/**
* @var int Device's UDP port.
*/
private $port;
/**
* @var TADZKlib holds a class instance.
*/
private $zkclient;
/**
* @var string device's response (low level format).
*/
private $data_recv = '';
/**
* @var int session id associated to UDP transaction.
*/
private $session_id = 0;
/**
* @var boolean tells if result was successfully (<code>true</code>) or fail (<code>false</code>).
*/
private $result;
/**
* @var array commands set supported by <code>TADZKLib</code> class.
*/
static private $zklib_commands = [
'get_platform' => [
'command_id' => self::CMD_DEVICE,
'command_string' => '~Platform',
'should_disconnect' => true,
'result_filter_string'=>'~Platform='
],
'get_fingerprint_algorithm' => [
'command_id' => self::CMD_DEVICE,
'command_string' => '~ZKFPVersion',
'should_disconnect' => true,
'result_filter_string'=>'~ZKFPVersion='
],
'get_serial_number' => [
'command_id' => self::CMD_DEVICE,
'command_string' => '~SerialNumber',
'should_disconnect' => true,
'result_filter_string'=>'~SerialNumber='
],
'get_oem_vendor' => [
'command_id' => self::CMD_DEVICE,
'command_string' => '~OEMVendor',
'should_disconnect' => true,
'result_filter_string'=>'~OEMVendor='
],
'get_mac_address' => [
'command_id' => self::CMD_DEVICE,
'command_string' => 'MAC',
'should_disconnect' => true,
'result_filter_string'=>'MAC='
],
'get_device_name' => [
'command_id' => self::CMD_DEVICE,
'command_string' => '~DeviceName',
'should_disconnect' => true,
'result_filter_string'=>'~DeviceName='
],
'get_manufacture_time' => [
'command_id' => self::CMD_DEVICE,
'command_string' => '~ProductTime',
'should_disconnect' => true,
'result_filter_string'=>'~ProductTime='
],
'get_antipassback_mode' => [
'command_id' => self::CMD_DEVICE,
'command_string' => '~APBFO',
'should_disconnect' => true,
'result_filter_string'=>'~APBFO='
],
'get_workcode' => [
'command_id' => self::CMD_DEVICE,
'command_string' => '~WCFO',
'should_disconnect' => true,
'result_filter_string'=>'~WCFO='
],
'get_ext_format_mode' => [
'command_id' => self::CMD_DEVICE,
'command_string' => '~ExtendFmt',
'should_disconnect' => true,
'result_filter_string'=>'~ExtendFmt='
],
'get_encrypted_mode' => [
'command_id' => self::CMD_DEVICE,
'command_string' => 'encrypt_out',
'should_disconnect' => true,
'result_filter_string'=>'encrypt_out='
],
'get_pin2_width' => [
'command_id' => self::CMD_DEVICE,
'command_string' => '~PIN2Width',
'should_disconnect' => true,
'result_filter_string'=>'~PIN2Width='
],
'get_ssr_mode' => [
'command_id' => self::CMD_DEVICE,
'command_string' => '~SSR',
'should_disconnect' => true,
'result_filter_string'=>'~SSR='
],
'get_firmware_version' => [
'command_id' => self::CMD_VERSION,
'command_string' => self::EMPTY_STRING,
'should_disconnect' => true,
'result_filter_string'=>false
],
'get_free_sizes' => [
'command_id' => self::CMD_GET_FREE_SIZES,
'command_string' => self::EMPTY_STRING,
'should_disconnect' => true,
'result_filter_string'=>false
],
'set_date' => [
'command_id' => self::CMD_SET_TIME,
'command_string' => self::CUSTOMIZED_COMMAND_STRING,
'should_disconnect' => true,
'result_filter_string'=>false
],
'delete_admin' => [
'command_id' => self::CMD_CLEAR_ADMIN,
'command_string' => self::EMPTY_STRING,
'should_disconnect' => true,
'result_filter_string'=>false
],
'enable' => [
'command_id' => self::CMD_ENABLEDEVICE,
'command_string' => self::EMPTY_STRING,
'should_disconnect' => true,
'result_filter_string'=>false
],
'disable' => [
'command_id' => self::CMD_DISABLEDEVICE,
'command_string' => self::EMPTY_STRING,
'should_disconnect' => false,
'result_filter_string'=>false
],
'restart' => [
'command_id' => self::CMD_RESTART,
'command_string' => self::EMPTY_STRING,
'should_disconnect' => true,
'result_filter_string'=>false
],
'poweroff' => [
'command_id' => self::CMD_POWEROFF,
'command_string' => self::EMPTY_STRING,
'should_disconnect' => true,
'result_filter_string'=>false
]
];
/**
* Returns commands available by the class.
*
* @return array commands list.
*/
static public function get_commands_available()
{
return array_keys(self::$zklib_commands);
}
/**
* Iniatialize TADZKLib class and sets its attributes.
*
* @param array $options options (ip, udp port and connection timeout).
*/
public function __construct(array $options)
{
$this->ip = $options['ip'];
$this->port = $options['udp_port'];
$this->zkclient = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
$timeout = ['sec' => $options['connection_timeout'], 'usec' => 500000];
socket_set_option($this->zkclient, SOL_SOCKET, SO_RCVTIMEO, $timeout);
}
/**
* Magic call to implement dynamic method calling.
*
* @param string $command method invoked.
* @param array $args arguments passed.
* @return TADResponse
*/
public function __call($command, array $args)
{
$should_disconnect = true;
$args = count($args) === 0 ? [] : array_shift($args);
$encoding = $args['encoding'];
unset($args['encoding']);
$this->connect();
switch($command){
case 'set_date':
$response = $this->zk_set_date($args);
break;
case 'get_free_sizes':
$response = $this->zk_get_free_sizes();
break;
default:
$should_disconnect = self::$zklib_commands[$command]['should_disconnect'];
$response = $this->send_command_to_device(
self::$zklib_commands[$command]['command_id'],
self::$zklib_commands[$command]['command_string']
);
}
$result_filter_string = self::$zklib_commands[$command]['result_filter_string'];
$response = $this->build_command_response($command, $this->result, $response, $encoding, $result_filter_string);
$should_disconnect && $this->disconnect();
return new TADResponse($response, $encoding);
}
/**
* Establish a connection to the device.
*
* @return boolean <b><code>true</code></b> on successfully conection, otherwise returns <b><code>false</code></b>.
*/
private function connect()
{
$command = self::CMD_CONNECT;
$command_string = self::EMPTY_STRING;
$chksum = 0;
$session_id = 0;
$reply_id = -1 + self::USHRT_MAX;
$buf = $this->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);
if (strlen($this->data_recv) > 0) {
$u = unpack('H2h1/H2h2/H2h3/H2h4/H2h5/H2h6', substr($this->data_recv, 0, 8));
$this->session_id = hexdec($u['h6'].$u['h5']);
return $this->checkValid($this->data_recv);
} else {
return false;
}
} catch (ErrorException $e) {
return false;
} catch (exception $e) {
return false;
}
}
/**
* Disconnects from the device.
*
* @return boolean <b><code>true</code></b> on successfully, otherwise returns <b><code>false</code></b>.
*/
private function disconnect()
{
$command = self::CMD_EXIT;
$command_string = '';
$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 = $this->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);
return $this->checkValid($this->data_recv);
} catch (ErrorException $e) {
return false;
} catch (Exception $e) {
return false;
}
}
/**
* Sets device's time and date.
*
* @param array $dt date and time data.
* @return boolean <b><code>true</code></b> on successfully, otherwise returns <b><code>false</code></b>.
*/
private function zk_set_date(array $dt = [])
{
$normalized_datetime = $this->setup_datetime($dt);
$encoded_time = $this->encode_time($normalized_datetime);
return $this->send_command_to_device(self::CMD_SET_TIME, pack('I', $encoded_time));
}
/**
* Gets device's information about current device's storage.
*
* @return array device's storage information.
*/
private function zk_get_free_sizes()
{
$fs = [];
$free_sizes_info = $this->reverse_hex(bin2hex($this->send_command_to_device(self::CMD_GET_FREE_SIZES)));
if (!$free_sizes_info) {
$fs = false;
} else {
if (self::DEVICE_GENERAL_INFO_STRING_LENGTH > strlen($free_sizes_info)) {
$free_sizes_info = '000000000000000000000000' . $free_sizes_info;
}
$fs['att_logs_available'] = hexdec(substr($free_sizes_info, 27, 5));
$fs['templates_available'] = hexdec(substr($free_sizes_info, 44, 4));
$fs['att_logs_capacity'] = hexdec(substr($free_sizes_info, 51, 5));
$fs['templates_capacity'] = hexdec(substr($free_sizes_info, 60, 4));
$fs['passwords_stored'] = hexdec(substr($free_sizes_info, 76, 4));
$fs['admins_stored'] = hexdec(substr($free_sizes_info, 84, 4));
$fs['att_logs_stored'] = hexdec(substr($free_sizes_info, 116, 4));
$fs['templates_stored'] = hexdec(substr($free_sizes_info, 132, 4));
$fs['users_stored'] = hexdec(substr($free_sizes_info, 148, 4));
}
return $fs;
}
/**
* Helper that allows sending command to device.
*
* @param integer $command command code.
* @param string $command_string subcommand.
* @param int $reply_id device's reply.
* @return boolean <b><code>true</code></b> on successfully, otherwise returns <b><code>false</code></b>.
*/
private function send_command_to_device($command, $command_string = '', $reply_id =null)
{
$chksum = 0;
$session_id = $this->session_id;
$u = unpack('H2h1/H2h2/H2h3/H2h4/H2h5/H2h6/H2h7/H2h8', substr($this->data_recv, 0, 8));
if (is_null($reply_id)) {
$reply_id = hexdec($u['h8'].$u['h7']);
}
$buf = $this->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));
$this->session_id = hexdec($u['h6'].$u['h5']);
$this->result = $this->checkValid($this->data_recv);
return substr($this->data_recv, 8);
} catch (ErrorException $e) {
return false;
} catch (exception $e) {
return false;
}
}
/**
* Calculates the chksum of the packet to be sent to the device.
*
* @param string $p packed sent to the device.
* @return string checksum calculated.
*/
private function createChkSum($p)
{
/*This function
Copied from zkemsdk.c*/
$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);
}
/**
* Creates UDP header to be sent to the device.
*
* @param int $command command id.
* @param string $chksum checksum associated.
* @param int $session_id session id associated.
* @param int $reply_id reply id associated.
* @param string $command_string subcomand.
* @return string UDP header.
*/
private function createHeader($command, $chksum, $session_id, $reply_id, $command_string)
{
/*This function puts a the parts that make up a packet together and
packs them into a byte string*/
$buf = pack('SSSS', $command, $chksum, $session_id, $reply_id).$command_string;
$buf = unpack('C'.(8+strlen($command_string)).'c', $buf);
$u = unpack('S', $this->createChkSum($buf));
if (is_array($u)) {
while (list($key) = each($u)) {
$u = $u[$key];
break;
}
}
$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 CMD_ACK_OK, indicating success.
*
* @param string $reply packet received from the device.
*
* @return boolean <b><code>true</code></b> on valid packet, otherwise returns <b><code>false</code></b>.
*/
private function checkValid($reply)
{
$u = unpack('H2h1/H2h2', substr($reply, 0, 8));
$command = hexdec($u['h2'].$u['h1']);
if ($command == self::CMD_ACK_OK) {
return true;
} else {
return false;
}
}
/**
* Builds a command response with a XML format to keep TAD behavior.
*
* @param string $command command executed.
* @param mixed $result command result.
* @return string XML response.
*/
private function build_command_response($command, $result_code, $result, $encoding, $result_filter_string=false)
{
$response_data = [];
$xml_tag = str_replace('_', ' ', $command);
$base_xml_tag = join('', explode(' ', ucwords($xml_tag))) . 'Response';
if (is_array($result)) {
if (0 === count($result)) {
$xml_header = '';
$response = $xml_header . '<' . $base_xml_tag . '>' . '</' . $base_xml_tag . '>';
return $response;
}
$response_data = ['Row'=>$result];
} else {
if (!is_bool($result) && true === $result_code) {
$result_filter_string = $result_filter_string ? $result_filter_string : null;
$result_data = str_replace($result_filter_string, '', $result);
} else {
$result_data = ($result_code ? self::XML_SUCCESS_RESPONSE : self::XML_FAIL_RESPONSE);
}
$result_code = $result_code ? '1' : '0';
$response_data = ['Row'=>['Result'=> $result_code, 'Information'=> $result_data]];
}
return $this->array_to_xml(new \SimpleXMLElement('<' . $base_xml_tag . '/>'), $response_data, $encoding);
}
/**
* Take an array in the form of <code>['date'=>date_value, 'time'=>time_value]</code> y returns
* another array with the following form:
*
* <code>
* ['year'=>foo_year, 'month'=>bar_month, 'day'=>taz_day,
* 'hour'=>foo_hour, 'minute=>bar_minute, 'second'=>taz_minute]
* </code>
*
* Any missing item from input array is replaced by corresponding element generated from
* current date and time.
*
* @param array $dt input 'datetime' array.
* @return array array generated.
*/
private function setup_datetime(array $dt=[])
{
$now = explode(' ', date("Y-m-d H:i:s"));
$dt = array_filter($dt, 'strlen');
!isset($dt['date']) ? $dt['date'] = $now[0] : null;
!isset($dt['time']) ? $dt['time'] = $now[1] : null;
$date = explode('-', $dt['date']);
$time = explode(':', $dt['time']);
return [
'year'=>$date[0], 'month'=>$date[1], 'day'=>$date[2],
'hour'=>$time[0], 'minute'=>$time[1], 'second'=>$time[2]];
}
/**
* Method taken from PHPLib @link http://dnaextrim.github.io/php_zklib/ project.
*
* @param string $hexstr hex string.
* @return string hex string reversed.
*/
private function reverse_hex($hexstr)
{
$tmp = '';
for ($i=strlen($hexstr); $i>=0; $i--) {
$tmp .= substr($hexstr, $i, 2);
$i--;
}
return $tmp;
}
/**
* Method taken from PHPZKLib @link http://dnaextrim.github.io/php_zklib/ project.
*
* It's been modified to accept an associative array as input.
*
* @param array $t array with a timestamp data.
* @return int timestamp encoded.
*/
private function encode_time(array $t)
{
/*Encode a timestamp send at the timeclock
copied from zkemsdk.c - EncodeTime*/
$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;
}
/**
* Transforms an array into an XML string.
*
* @param \SimpleXMLElement $object <code>SimpleXMLElement</code> instance.
* @param array $data input array to be transformed.
* @return string XML string generated.
*/
private function array_to_xml(\SimpleXMLElement $object, array $data)
{
foreach ($data as $key => $value) {
if (is_array($value)) {
$new_object = $object->addChild($key);
$this->array_to_xml($new_object, $value);
} else {
$object->addChild($key, $value);
}
}
$xml = trim(str_replace("<?xml version=\"1.0\"?>", '', $object->asXML()));
return $xml;
}
}
================================================
FILE: lib/TAD.php
================================================
<?php
/*
* tad-php
*
* (The MIT License)
*
* Copyright (c) 2014 Jorge Cobis <jcobis@gmail.com / http://twitter.com/cobisja>.
*
* 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.
*/
namespace TADPHP;
use TADPHP\Providers\TADSoap;
use TADPHP\Providers\TADZKLib;
use TADPHP\Exceptions\ConnectionError;
use TADPHP\Exceptions\UnrecognizedArgument;
use TADPHP\Exceptions\UnrecognizedCommand;
/**
* TAD: Time & Attendance Device is a class that implements some function presents in time and attendance device.
*
*
* For developing purposes, it's been used Fingertec Q2i device (that it's been using were I work).
*
* Methods exposed by TAD class have been tested using Q2i deviced only , that has a Linux 2.6.21 kernel (ZEM-600).
* However, it's possible that TAD class works with similar devices, since most of them use the same SOAP API from
* ZK Software.
*
* There are some SOAP functions that it's suppossed, according to the official docs (which incidentally it's
* very limited and so poor!!!) must show a expected behaviour, but when they are invoked don't work like
* it's expected, so they become useless (e.g. Restart SOAP call). For these situations, I found (after googling for
* hours, and hours, and hours!!!), a PHP class named PHP_ZKLib (@link http://dnaextrim.github.io/php_zklib/ ) that
* take a different approach to "talk to device": it uses UDP protocol at device standard port 4370.
*
* PHP_ZKLib class it's been fully integrated, after a refactoring process to take out all duplicated code (DRY)
*
* TAD Class has been tested as far as I could. If you check the test code coverage you'll notice that it does not
* reach 100%. The reason is that it was not possible to write some tests fof PHP_ZKLib class. I have to admit that
* I not have fully understanding about what it's done by some methods. I could not find technical information
* about UDP protocol for ZK devices.
*
* For practical purposes, you don't have to be worried about when to use TAD class or PHP_ZKLib class because
* you only have to get a TAD instance (as shown below) and call any of methods available. The class decides
* about when run the method invoked using TAD class or PHP_ZKLib class.
*
* To get a TAD instance, call <code><b>get_instance()</b></code> method from <code><b>TADFactory</b></code> class.
*
* Please note that all device's responses are handled by TAD through <b><code>TADResponse</code></b> class. For
* that reason all responses are returned embedded in <code>TADResponse</code> object.
*
* You can get responses in XML, JSON or Array using the respective methods exposed by <code>TADResponse</code> class.
* (<code>to_xml(), to_json(), to_array(), get_reponse()</code>)
*
* Some examples:
*
* - Get a TAD instance for device with ip = '192.168.100.156':
* <code>
* $b1 = (new TADFactory(['ip'=>'192.168.100.156']))->get_instance();
* </code>
*
* Getting device time and date:
* <code>
* $dt = $b1->get_date(); // method executed via TAD class.
* </code>
*
* Setting device date and time:
* <code>
* $r = $b1->set_date(['date'=>'2014-12-31', 'time'=>'23:59:59']); // method executed via PHP_ZKLib.
* </code>
*
* All device responses are <code>TADResponse</code> objects. You can transform them as shown below:
*
* Get an array with logs of user with pin = 99999999:
* <code>
* $logs = $b1->get_att_log(['pin'=>'99999999'])->to_array();
* </code>
*
* If you want to filter logs by date:
* <code>
* $logs = $b1->get_att_log(['pin'=>'99999999']);
* $logs = $logs->filter_by_date(['start_date'=>'2014-11-27', 'end_date'=>'2014-12-02']);
* </code>
*
* For more information see README.md
*
* @author Jorge Cobis - email: jcobis@gmail.com / twitter: @cobisja
*/
class TAD
{
/**
* Valid commands args array.
*
* @var array
*/
static private $parseable_args = [
'com_key', 'pin', 'time', 'template',
'name', 'password', 'group', 'privilege',
'card', 'pin2', 'tz1', 'tz2', 'tz3',
'finger_id', 'option_name', 'date',
'size', 'valid', 'value'
];
/**
* @var string Device ip address.
*/
private $ip;
/**
* @var mixed Device internal id.
*/
private $internal_id;
/**
* @var string Device description (just for info purposes).
*/
private $description;
/**
* Security communication code (required for SOAP functions calls).
*
* @var mixed
*/
private $com_key;
/**
* @var int Connection timeout in seconds.
*/
private $connection_timeout;
/**
* @var string Encoding for XML commands and responses.
*/
private $encoding;
/**
* @var int UDP port number.
*/
private $udp_port;
/**
* Holds a <code>TADSoap</code> instance to talk to device via SOAP.
*
* @var object
*/
private $tad_soap;
/**
* Holds <code>PHP_ZKLib</code> instance to talk to device via UDP.
*
* @var object
*/
private $zklib;
/**
* Returns an array with a full list of commands available.
*
* @return array list of commands available.
*/
public static function commands_available()
{
return array_merge(static::soap_commands_available(), static::zklib_commands_available());
}
/**
* Returns an array with SOAP commands list available.
*
* @return array SOAP commands list.
*/
public static function soap_commands_available(array $options = [])
{
return TADSoap::get_commands_available($options);
}
/**
* Returns an array with PHP_ZKLib commands available.
*
* @return array PHP_ZHLib commands list.
*/
public static function zklib_commands_available()
{
return TADZKLib::get_commands_available();
}
/**
* Returns valid commands arguments list.
*
* @return array arguments list.
*/
public static function get_valid_commands_args()
{
return self::$parseable_args;
}
/**
* Tells if device is "online" to process commands requests.
*
* @param string $ip device ip address
* @param int $timeout seconds to wait for device.
* @return boolean <b>true</b> if device is alive, <b>false</b> otherwise.
*/
public static function is_device_online($ip, $timeout = 1)
{
$handler = curl_init($ip);
curl_setopt_array($handler, [ CURLOPT_TIMEOUT => $timeout, CURLOPT_RETURNTRANSFER => true ]);
$response = curl_exec($handler);
curl_close($handler);
return (boolean)$response;
}
/**
* Get a new TAD class instance.
*
* @param TADSoap $soap_provider code><b>TADSoap</b></code> class instance.
* @param TADZKLib $zklib_provider <code><b>ZKLib</b></code> class instance.
* @param array $options device parameters.
*/
public function __construct(TADSoap $soap_provider, TADZKLib $zklib_provider, array $options = [])
{
$this->ip = $options['ip'];
$this->internal_id = (integer) $options['internal_id'];
$this->com_key = (integer) $options['com_key'];
$this->description = $options['description'];
$this->connection_timeout = (integer) $options['connection_timeout'];
$this->encoding = strtolower($options['encoding']);
$this->udp_port = (integer) $options['udp_port'];
$this->tad_soap = $soap_provider;
$this->zklib = $zklib_provider;
}
/**
* Magic __call method overriding to define in runtime the methods should be called based on method invoked.
* (Something like Ruby metaprogramming :-P). In this way, we decrease the number of methods required
* (usually should be one method per SOAP or PHP_ZKLib command exposed).
*
* Note:
*
* Those methods that add, update o delete device information, call SOAP method <b><code>refresh_db()</code></b>
* to properly update device database.
*
* @param string $command command to be invoked.
* @param array $args commands args.
* @return string device response in XML format.
* @throws ConnectionError.
* @throws UnrecognizedCommand.
* @throws UnrecognizedArgument.
*/
public function __call($command, array $args)
{
$command_args = count($args) === 0 ? [] : array_shift($args);
$this->check_for_connection() &&
$this->check_for_valid_command($command) &&
$this->check_for_unrecognized_args($command_args);
if (in_array($command, TADSoap::get_commands_available())) {
$response = $this->execute_command_via_tad_soap($command, $command_args);
} else {
$response = $this->execute_command_via_zklib($command, $command_args);
}
$this->check_for_refresh_tad_db($command);
return $response;
}
/**
* Send a command to device using a <code>TADSoap</code> instance class.
*
* @param string $command command to be sending.
* @param array $args command args.
* @return string device response.
*/
public function execute_command_via_tad_soap($command, array $args = [])
{
$command_args = $this->config_array_items(array_merge(['com_key' => $this->get_com_key()], $args));
return $this->tad_soap->execute_soap_command($command, $command_args, $this->encoding);
}
/**
* Send a command to device using <code>PHP_ZKLib</code> class.
*
* All responses generate by PHP_ZKLib class are not in XML format, it is used <code>build_command_response</code>
* to build an XML response, just to keep the TAD class behavior. For this purpose, the method uses class constans
* <code>ZKLib::XML_SUCCESS_RESPONSE</code> and <code>ZKLib::XML_FAIL_RESPONSE</code>.
*
* @param string $command command to be sending.
* @param array $args command args.
* @return string string device response.
*/
public function execute_command_via_zklib($command, array $args = [])
{
$command_args = $this->config_array_items($args);
$response = $this->zklib->{$command}(array_merge(['encoding'=>$this->encoding], $command_args));
return $response;
}
/**
* Returns device's IP address.
*
* @return string IP address.
*/
public function get_ip()
{
return $this->ip;
}
/**
* Returns device's internal code.
*
* @return int internal code.
*/
public function get_internal_id()
{
return $this->internal_id;
}
/**
* Returns device's comm code.
*
* @return int code.
*/
public function get_com_key()
{
return $this->com_key;
}
/**
* Returns device's string description.
*
* @return string device description.
*/
public function get_description()
{
return $this->description;
}
/**
* Returns device's connection timeout.
*
* @return int connection timeout.
*/
public function get_connection_timeout()
{
return $this->connection_timeout;
}
/**
* Returns device's encoding (used for SOAP requests and responses).
*
* @return string encoding.
*/
public function get_encoding()
{
return $this->encoding;
}
/**
* Return device's UDP port number.
*
* @return int port number.
*/
public function get_udp_port()
{
return $this->udp_port;
}
/**
* Tells if device is ready (alive) to process requests.
*
* @return boolean <b>true</b> if device is alive, <b>false</b> otherwise.
*/
public function is_alive()
{
return static::is_device_online($this->get_ip(), $this->connection_timeout);
}
/**
* Throws an Exception when device is not alive.
*
* @return boolean <b><code>true</code></b> if there is a connection with the device.
* @throws ConnectionError
*/
private function check_for_connection()
{
if (!$this->is_alive()) {
throw new ConnectionError('Imposible iniciar conexión con dispositivo ' . $this->get_ip());
}
return true;
}
/**
* Tells if the command requested is in valid commands set.
*
* @param string $command command requested.
* @return boolean <code><b>true</b></code> if the command es known by the class.
* @throws UnrecognizedCommand
*/
private function check_for_valid_command($command)
{
$tad_commands = static::commands_available();
if (!in_array($command, $tad_commands)) {
throw new UnrecognizedCommand("Comando $command no reconocido!");
}
return true;
}
/**
* Tells if the arguments supplied are in valid args set.
*
* @param array $args args array to be verified.
* @return <b><code>true</code></b> if all args supplied are valid (known by the class).
* @throws TAD\Exceptions\UnrecognizedArgument
*/
private function check_for_unrecognized_args(array $args)
{
if (0 !== count($unrecognized_args = array_diff(array_keys($args), static::get_valid_commands_args()))) {
throw new UnrecognizedArgument('Parámetro(s) desconocido(s): ' . join(', ', $unrecognized_args));
}
return true;
}
/**
* Tells if it's necessary to do a device database update. To do this, the method verified the command
* executed to see if it did any adding, deleting or updating of database device. In that case, a
* <code>refesh_db</code> command is executed.
*
* @param string $command_executed command executed.
*/
private function check_for_refresh_tad_db($command_executed)
{
preg_match('/^(set_|delete_)/', $command_executed) && $this->execute_command_via_tad_soap('refresh_db', []);
}
/**
* Returns an array with all parseable_args, allowed by the class, initialized with specific values
* passed through $values array. Those args not passed in method param will be set to null.
*
* @param array $values array values to be analized.
* @return array array generated.
*/
private function config_array_items(array $values)
{
$normalized_args = [];
foreach (static::get_valid_commands_args() as $parseable_arg_key) {
$normalized_args[$parseable_arg_key] =
isset($values[$parseable_arg_key]) ? $values[$parseable_arg_key] : null;
}
return $normalized_args;
}
}
================================================
FILE: lib/TADFactory.php
================================================
<?php
/*
* tad-php
*
* (The MIT License)
*
* Copyright (c) 2014 Jorge Cobis <jcobis@gmail.com / http://twitter.com/cobisja>.
*
* 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.
*/
namespace TADPHP;
use TADPHP\Providers\TADSoap;
use TADPHP\Providers\TADZKLib;
use TADPHP\TAD;
class TADFactory
{
private $options;
/**
* Registers attributes values for <code>TAD\TAD</code> and <code>Providers\ZKLib</code> classes.
*
* @param array $options attributes values.
*/
public function __construct(array $options = [])
{
$this->options = $options;
}
/**
* Returns an <code><b>TAD\TAD</b></code> class instance.
*
* @return TAD class instance.
*/
public function get_instance()
{
$options = $this->options;
$this->set_options($this->get_default_options(), $options);
$soap_options = [
'location' => "http://{$options['ip']}/iWsService",
'uri' => 'http://www.zksoftware/Service/message/',
'connection_timeout' => $options['connection_timeout'],
'exceptions' => false,
'trace' => true
];
$soap_client = new \SoapClient(null, $soap_options);
return new TAD(
new TADSoap($soap_client, $soap_options),
new TADZKLib($options),
$options
);
}
/**
* Returns a default values array used by <code>TAD\TAD</code> y <code>Providers\ZKLib</code> classes.
*
* @return array default values.
*/
private function get_default_options()
{
$default_options['ip'] = '169.254.0.1';
$default_options['internal_id'] = 1;
$default_options['com_key'] = 0;
$default_options['description'] = 'N/A';
$default_options['connection_timeout'] = 5;
$default_options['soap_port'] = 80;
$default_options['udp_port'] = 4370;
$default_options['encoding'] = 'iso8859-1';
return $default_options;
}
/**
* Set all array items to a known default values.
*
* @param array $base_options default values
* @param array $options default values to be changed to a known values.
*/
private function set_options(array $base_options, array &$options)
{
foreach ($base_options as $key => $default) {
!isset($options[$key]) ? $options[$key] = $default : null;
}
}
}
================================================
FILE: lib/TADResponse.php
================================================
<?php
/*
* tad-php
*
* (The MIT License)
*
* Copyright (c) 2015 Jorge Cobis <jcobis@gmail.com / http://twitter.com/cobisja>.
*
* 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.
*/
namespace TADPHP;
use TADPHP\Exceptions\FilterArgumentError;
/**
* Class to handle reponses generated by Time & Attendance devices.
*/
class TADResponse
{
const XML_NO_DATA_FOUND = '<Row><Result>0</Result><Information>No data!</Information></Row>';
const DEFAULT_XML_HEADER = '<?xml version="1.0" encoding="iso8859-1" standalone="no"?>';
/**
* @var string Response's XML header.
*/
private $response_header;
/**
* @var string Response's XML body.
*/
private $response_body;
/**
* @var string Response's encoding.
*/
private $encoding;
/**
* @var boolean Indicates if Response's has no data (empty).
*/
private $is_empty_response;
/**
* Initialize the class.
*
* @param string $response XML string that represents the TAD response.
*/
public function __construct($response, $encoding)
{
$header = $this->extract_xml_header($response);
if ('' === $header) {
$this->set_header(self::DEFAULT_XML_HEADER);
}
$this->set_encoding($encoding);
$this->set_response($response);
}
/**
* Returns response formatted according $options.
*
* @param array $options format to apply on the response.
* @return mixed Response formatted.
*/
public function get_response(array $options = [])
{
if (!isset($options['format'])) {
$options['format'] = 'xml';
}
return $this->{'to_' . $options['format']}();
}
/**
* Sets response.
*
* @param string $response XML string.
*/
public function set_response($response='')
{
if ($this->is_there_no_data($response)) {
!$this->is_no_data_response($response) && $response = $this->build_no_data_response($response);
$this->is_empty_response = true;
} else {
$this->is_empty_response = false;
}
$xml_header = $this->extract_xml_header($response);
if ('' !== $xml_header && 0!== strcmp($this->response_header, $xml_header)) {
$this->response_header = $xml_header;
}
$this->response_body = $response;
}
/**
* Gets response's encoding.
*/
public function get_encoding()
{
return $this->encoding;
}
/**
* Sets response's encoding
*
* @param string $encoding encoding.
*/
public function set_encoding($encoding)
{
$this->encoding = $encoding;
$this->set_response_header_encoding($encoding);
}
/**
* Returns response's header.
*
* @return string header.
*/
public function get_header()
{
return $this->response_header;
}
/**
* Sets response's header
*
* @param string $header header to be set.
*/
public function set_header($header)
{
$this->response_header = $header;
}
/**
* Returns a not sanitized response without header.
*
* @return string XML string.
*/
public function get_response_body()
{
return $this->response_body;
}
/**
* Tells if response stored by the class is empty (with no data).
*
* @return boolean <b><code>true</code></b> response is empty otherwise returns <b><code>false</code></b>.
*/
public function is_empty_response()
{
return $this->is_empty_response;
}
/**
* Returns response in XML format.
*
* @return string XML string.
*/
public function to_xml()
{
return (string) $this;
}
/**
* Returns response in JSON format.
*
* @return string JSON string generated.
*/
public function to_json()
{
return $this->is_empty_response() ? '{}' : json_encode(simplexml_load_string((string) $this));
}
/**
* Returns response in array format.
*
* @return array array generated.
*/
public function to_array()
{
return $this->is_empty_response() ? [] : json_decode($this->to_json((string) $this), true);
}
/**
* Return the numbers of nodes that the response has.
*
* @return int number of nodes.
*/
public function count()
{
return $this->get_items_number($this->to_xml());
}
/**
* Magic method to define a dynamic filter of type 'filter_by'. This filter allows you define
* multiple filtering criterias.
*
* @param string $method method invoked.
* @param array $args arguments passed.
* @return string filtered XML string.
* @throws FilterArgumentError
* @throws \Exception
*/
public function __call($method, $args)
{
$xml = $this->to_xml();
if (preg_match('/filter_by_([a-zA-Z]+)/', $method)) {
$filters = preg_split('/(by_|_and_)/i', $method, -1);
unset($filters[0]);
if (count($filters) !== count($args)) {
throw new FilterArgumentError(
'Invalid number of arguments: '
. count($filters) . ' expected, '
. count($args) . ' given.'
);
}
foreach ($filters as $filter) {
$filter_args = $this->normalize_filter_args(array_shift($args));
switch ($filter) {
case 'date':
$filter_regex = '/<DateTime>([0-9]{4}-[0-9]{2}-[0-9]{2})/';
break;
case 'time':
$filter_regex = '/([0-9]{2}:[0-9]{2}:[0-9]{2})<\/DateTime>/';
break;
case 'datetime':
$filter_regex = '/'
. '<DateTime>([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2})<\/DateTime>'
. '/';
break;
default:
$filter_regex = '/<' . ucwords($filter) . '>(.*?)<\/' . ucwords($filter) . '>/si';
break;
}
$xml = $this->filter_xml($xml, $filter_regex, $filter_args);
}
$this->set_response($xml);
return $this;
} else {
throw new \Exception('Unknown method ' . $method);
}
}
/**
* Magic method to get a TADResponse object in string format.
*
* @return type
*/
public function __toString()
{
return $this->response_header . $this->sanitize_xml_string($this->response_body);
}
/**
* Parses an XML string applying an specific filter.
*
* @param string $xml input XML string.
* @param string $filter regex to be applied on input.
* @param array $range boundaries searching criteria.
* @param string $xml_init_row_tag XML root tag.
* @return string XML string filtered.
*/
public function filter_xml($xml, $filter, array $range=[], $xml_init_row_tag='<Row>')
{
$xml_header = $this->extract_xml_header($xml);
$matches = [];
$filtered_xml = self::XML_NO_DATA_FOUND;
$rows = explode($xml_init_row_tag, $xml);
$main_xml_init_tag = trim(array_shift($rows));
$main_xml_end_tag = '' !== $main_xml_init_tag ? '</' . str_replace('<', '', $main_xml_init_tag) : '';
if ('' !== $main_xml_end_tag) {
$rows[] = str_replace($main_xml_end_tag, '', array_pop($rows));
}
if (preg_match_all($filter, $xml, $matches)) {
$indexes = array_keys(
array_filter(
$matches[1],
function($data) use ($range) {
switch (count($range)) {
case 1:
if (isset($range['like'])) {
$result = false === strpos($data, $range['like']) ? false : true;
} elseif (isset($range['start'])) {
$result = is_numeric($data) ?
$data >= $range['start'] :
0 <= strcmp($data, $range['start']);
} else {
$result = is_numeric($data) ?
$data <= $range['end'] :
0 >= strcmp($data, $range['end']);
}
break;
case 2:
$result = is_numeric($data) ?
$data >= $range['start'] && $data <= $range['end'] :
!(strcmp($data, $range['start']) < 0 || strcmp($data, $range['end']) > 0);
break;
default:
$result = false;
}
return $result;
}
)
);
$filtered_xml = (
0 === count($indexes) ?
self::XML_NO_DATA_FOUND :
join(
'',
array_map(
function($index) use ($rows, $xml_init_row_tag) {
return $xml_init_row_tag . $rows[$index];
},
$indexes
)
)
);
}
$xml = $xml_header . $main_xml_init_tag . trim($filtered_xml) . $main_xml_end_tag;
return $this->sanitize_xml_string($xml);
}
/**
* Gets response's XML header.
*
* @param string $xml XML string.
* @return string XML header.
*/
private function extract_xml_header(&$xml)
{
$xml_header = '';
if (false !== strpos($xml, '?>')) {
$xml_items = explode('?>', $xml);
$xml_header = $xml_items[0] . '?>';
$xml = $xml_items[1];
}
$xml = $this->sanitize_xml_string($xml);
return trim($xml_header);
}
/**
* Adds encoding to XML header.
*
* @param string $encoding encoding to be set in XML header.
* @return string XML header with encoding.
*/
private function set_response_header_encoding($encoding = 'utf-8')
{
$header = $this->response_header;
if (is_null($header) || '' === $header) {
$header ='<?xml version="1.0" encoding="' . $encoding . '" standalone="no"?>';
} else {
$header = preg_filter('/encoding="([^"]+)"/', 'encoding="' . $encoding . '"', $header);
}
$this->response_header = $header;
}
/**
* Cleans a XML string from undesired chars (in this case EOL by default).
*
* @param string $xml string to be cleaned out.
* @return string XML string cleaned out.
*/
private function sanitize_xml_string($xml, array $undesired_chars = [ "\n", "\r", "\t" ])
{
return trim(str_replace($undesired_chars, '', $xml));
}
/**
* Sets boundaries to be used as filter criteria.
*
* @param mixed $args boundaries.
* @return array boundaries validated.
*/
private function normalize_filter_args($args)
{
$normalized_filter_args = [];
$valid_range_filter = ['start', 'end', 'like'];
if (is_array($args)) {
$args_keys = array_keys($args);
array_walk(
$args_keys,
function ($item) use ($valid_range_filter) {
if (!in_array($item, $valid_range_filter)) {
throw new FilterArgumentError('Invalid range key ' . $item);
}
}
);
isset($args['start']) ? $normalized_filter_args['start'] = $args['start'] : null;
isset($args['end']) ? $normalized_filter_args['end'] = $args['end'] : null;
isset($args['like']) ? $normalized_filter_args['like'] = $args['like'] : null;
} else {
$normalized_filter_args['start'] = $normalized_filter_args['end'] = $args;
}
return $normalized_filter_args;
}
/**
* Tells if device's response returns an empty response, represented by an empty XML string
* (a string with just an open and end tags).
*
* @param string $response device response.
* @return boolean <b><code>true</code></b> if it is a empty response.
*/
private function is_there_no_data($response)
{
return ($this->is_no_data_response($response) || 0 === $this->get_items_number($response));
}
/**
* Returns the numbers of nodes that the XML response has.
*
* @param type $response XML string.
* @return int number of nodes.
*/
private function get_items_number($response)
{
if (is_null($response) || '' === trim($response)) {
return 0;
}
$response = $this->sanitize_xml_string($response);
$xml = new \SimpleXMLElement($response);
$items_number = $xml->count();
return $items_number;
}
/**
* Generates a modified XML response with a NO DATA text.
*
* @param string $response device response.
* @return string modified XML response.
*/
private function build_no_data_response($response)
{
(is_null($response) || '' === trim($response)) ? $response = '<Response></Response>' : null;
$response = $this->get_header() . $response;
$pos = strpos($response, '>', strpos($response, '>') + 1);
$no_data_response = substr_replace($response, self::XML_NO_DATA_FOUND, $pos + 1, false);
return $no_data_response;
}
/**
* Tells if response passed represents an empty XML response.
*
* @param type $response XML string to be evaluated.
* @return boolean <b><code>true</code></b> if it is a empty response.
*/
private function is_no_data_response($response)
{
return false !== strpos($response, self::XML_NO_DATA_FOUND);
}
}
================================================
FILE: phpunit.xml
================================================
<phpunit bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsotalion="false"
stopOnFailure="false"
syntaxCheck="false"
verbose="true"
>
<testsuites>
<testsuite name="TAD-PHP test suite">
<directory>test</directory>
</testsuite>
</testsuites>
</phpunit>
================================================
FILE: test/helpers/ClassReflection.php
================================================
<?php
/*
* tad-php
*
* (The MIT License)
*
* Copyright (c) 2014 Jorge Cobis <jcobis@gmail.com / http://twitter.com/cobisja>.
*
* 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.
*/
namespace Test\Helpers;
abstract class ClassReflection
{
/**
* Allows you to invoking protected or privated methods of class given.
*
* Source code taken from website:
* @link https://jtreminio.com/2013/03/unit-testing-tutorial-part-3-testing-protected-private-methods-coverage-reports-and-crap/
*
* @param object &$object object from where you want to invoke protected or private methods.
* @param string $method_name method's name
* @param array $parameters method's params.
*
* @return mixed method's result.
*/
static public function invoke_method(&$object, $method_name, array $parameters = [])
{
$reflection = new \ReflectionClass(get_class($object));
$method = $reflection->getMethod($method_name);
$method->setAccessible(true);
return $method->invokeArgs($object, $parameters);
}
}
================================================
FILE: test/lib/Providers/TADSoapTest.php
================================================
<?php
namespace Test\Providers;
use TADPHP\TAD;
use TADPHP\Providers\TADSoap;
use Test\Helpers\ClassReflection;
class TADSoapTest extends \PHPUnit_Framework_TestCase
{
public function testBuildTADSoap()
{
$soap_options = $this->get_soap_options();
$soap_client = new \SoapClient( null, $soap_options );
$tad_soap = new TADSoap($soap_client, $soap_options);
$this->assertNotNull($tad_soap);
$this->assertInstanceOf('TADPHP\Providers\TADSoap', $tad_soap);
return $tad_soap;
}
/**
* @depends testBuildTADSoap
*/
public function testGetSoapProviderOptions($tad_soap_instance)
{
$soap_providers_options = $tad_soap_instance->get_soap_provider_options();
$this->assertInternalType('array', $soap_providers_options);
$this->assertArrayHasKey('location', $soap_providers_options);
$this->assertArrayHasKey('uri', $soap_providers_options);
$this->assertEquals('http://127.0.0.1/iWsService', $soap_providers_options['location']);
$this->assertEquals('http://www.zksoftware/Service/message/', $soap_providers_options['uri']);
}
/**
* @depends testBuildTADSoap
* @dataProvider soapCommandsFixtures
*/
public function testBuildSoapRequest($command, array $args, $expected_soap_string, $encoding, TADSoap $tad_soap)
{
$args += array_fill_keys( TAD::get_valid_commands_args(), null );
$soap_request = $tad_soap->build_soap_request( $command, $args, $encoding );
$this->assertEquals( $expected_soap_string, $soap_request );
}
/**
* @depends testBuildTADSoap
*/
public function testBuildMultipleSoapRequest(TADSoap $tad_soap)
{
$args = array_fill_keys( TAD::get_valid_commands_args(), null );
// We uses 'set_user_info' command defined in TADSoap class.
// Maybe there is a better way to test this. :-P
$soap_request = $tad_soap->build_soap_request('set_user_info', $args, 'iso8859-1');
$this->assertInternalType('array', $soap_request);
}
public function testExecuteSoapRequest()
{
$mock_response = '<GetUserInfoResponse></GetUserInfoResponse>';
$encoding = 'iso8859-1';
$soap_options = $this->get_soap_options();
$soap_client = $this->getMockBuilder('\SoapClient')
->setConstructorArgs( [ null, [ 'location'=>$soap_options['location'], 'uri'=>$soap_options['uri'] ] ] )
->setMethods( [ '__doRequest' ] )
->getMock();
$soap_client->expects( $this->any() )
->method( '__doRequest' )
->with( $this->anything(), $soap_options['location'], '', SOAP_1_1 )
->will( $this->returnValue( $mock_response ) );
$tad_soap = $this->getMockBuilder('TADPHP\Providers\TADSoap')
->setConstructorArgs( [ $soap_client, $soap_options ] )
->setMethods( null )
->getMock();
$args = array_fill_keys( TAD::get_valid_commands_args(), null );
$args['com_key'] = 0;
$args['pin'] = 123;
$response = $tad_soap->execute_soap_command( 'get_user_info', $args, $encoding );
$this->assertNotEmpty( $response );
}
public function testExecuteMultipleSoapRequests()
{
$soap_requests = [
'<GetDate><ArgComKey>0</ArgComKey></GetDate>',
'<Restart><ArgComKey>0</ArgComKey></Restart>'
];
$mock_response = '<RestartResponse><Row><Result>1</Result><Information>Success!</Information></Row></RestartResponse>';
$soap_options = $this->get_soap_options();
$soap_client = $this->getMockBuilder('\SoapClient')
->setConstructorArgs( [ null, [ 'location'=>$soap_options['location'], 'uri'=>$soap_options['uri'] ] ] )
->setMethods( [ '__doRequest' ] )
->getMock();
$soap_client->expects( $this->any() )
->method( '__doRequest' )
->with( $this->anything(), $soap_options['location'], '', SOAP_1_1 )
->will( $this->returnValue( $mock_response ) );
$tad_soap = $this->getMockBuilder('TADPHP\Providers\TADSoap')
->setConstructorArgs( [ $soap_client, $soap_options ] )
->setMethods( null )
->getMock();
$result = ClassReflection::invoke_method(
$tad_soap,
'execute_multiple_soap_requests',
[ $soap_requests, $soap_options['location'] ] );
$this->assertNotEmpty( $result );
}
public function soapCommandsFixtures()
{
$encoding = 'iso8859-1';
return [
[ 'get_date', ['com_key'=>0], '<?xml version="1.0" encoding="'. $encoding .'" standalone="no"?><GetDate><ArgComKey>0</ArgComKey></GetDate>', $encoding ],
[ 'get_att_log', ['com_key'=>0], '<?xml version="1.0" encoding="' . $encoding. '" standalone="no"?><GetAttLog><ArgComKey>0</ArgComKey><Arg><PIN></PIN></Arg></GetAttLog>', $encoding ],
[ 'get_att_log', ['com_key'=>0, 'pin'=>'99999999'], '<?xml version="1.0" encoding="' . $encoding . '" standalone="no"?><GetAttLog><ArgComKey>0</ArgComKey><Arg><PIN>99999999</PIN></Arg></GetAttLog>', $encoding ],
[ 'set_user_template', [
'com_key' => 0,
'pin' => '999',
'finger_id' => '0',
'size' => '514',
'valid' => '1',
'template' => 'foobartaz123'
],
'<?xml version="1.0" encoding="' . $encoding . '" standalone="no"?><SetUserTemplate><ArgComKey>0</ArgComKey><Arg><PIN>999</PIN><FingerID>0</FingerID><Size>514</Size><Valid>1</Valid><Template>foobartaz123</Template></Arg></SetUserTemplate>',
$encoding
]
];
}
private function get_soap_options()
{
$soap_options = [
'location' => "http://127.0.0.1/iWsService",
'uri' => 'http://www.zksoftware/Service/message/',
'connection_timeout' => 1,
'exceptions' => false,
'trace' => true
];
return $soap_options;
}
}
================================================
FILE: test/lib/Providers/TADZKLibTest.php
================================================
<?php
namespace Test\Providers;
use TADPHP\Providers\TADZKLib;
use Test\Helpers\ClassReflection;
class TADZKLibTest extends \PHPUnit_Framework_TestCase
{
public function testBuildTADZKLibIsOk()
{
$options = ['ip' => '127.0.0.1', 'udp_port' => 4370, 'connection_timeout'=>1];
$zk = new TADZKLib( $options );
$this->assertNotNull($zk);
$this->assertInstanceOf('TADPHP\Providers\TADZKLib', $zk);
return $zk;
}
/**
* @depends testBuildTADZKLibIsOk
* @dataProvider build_commands_fixtures
*/
public function testBuildCommandResponse($command, $result_code, $result, $expected_xml, $encoding, TADZKLib $zk)
{
$result = ClassReflection::invoke_method($zk, 'build_command_response', [ $command, $result_code, $result, $encoding ]);
$this->assertEquals($expected_xml, $result);
}
/**
* @depends testBuildTADZKLibIsOk
* @dataProvider datetime_fixtures
*/
public function testSettingUpDateIsOk($datetime, TADZKLib $zk)
{
$valid_datetime_keys = ['year', 'month', 'day', 'hour', 'minute', 'second'];
$result = ClassReflection::invoke_method( $zk, 'setup_datetime', [$datetime] );
$result_keys = array_keys($result);
$this->assertEmpty( array_diff( $valid_datetime_keys, $result_keys ), 'invalid keys found!');
}
/**
* @depends testBuildTADZKLibIsOk
*/
public function testReverseHex(TADZKLib $zk)
{
$hex_data = "000000000000000000000000000000002202000000000000420400000000000043000000000000004a0a00000000000002000000020000001027000010270000400d0300ce220000ee240000fd0c0300000000000000000000000000";
$reversed_hex = ClassReflection::invoke_method($zk, 'reverse_hex', [$hex_data]);
$reversed_reversed_hex = ClassReflection::invoke_method($zk, 'reverse_hex', [$reversed_hex]);
$this->assertEquals( strlen($hex_data), strlen($reversed_hex));
$this->assertEquals( $hex_data, $reversed_reversed_hex );
}
/**
* @depends testBuildTADZKLibIsOk
*/
public function testEncodeTime(TADZKLib $zk)
{
$expected_encoded_time = 480003771; // This integer represents '2014-12-07 14:22:51' timestamp.
$dt = ['date'=>'2014-12-07', 'time'=>'14:22:51'];
$t = ClassReflection::invoke_method($zk, 'setup_datetime', [$dt]);
$encoded_time = ClassReflection::invoke_method($zk, 'encode_time', [$t]);
$this->assertInternalType('integer', $encoded_time);
$this->assertEquals($expected_encoded_time, $encoded_time);
}
public function build_commands_fixtures()
{
$encoding = 'iso8859-1';
return [
[ 'restart', true, true, '<RestartResponse><Row><Result>1</Result><Information>Successfully!</Information></Row></RestartResponse>', $encoding],
[ 'poweroff', false, false, '<PoweroffResponse><Row><Result>0</Result><Information>Fail!</Information></Row></PoweroffResponse>', $encoding],
[ 'foo', true, ['bar'=>0, 'taz'=>0], '<FooResponse><Row><bar>0</bar><taz>0</taz></Row></FooResponse>', $encoding],
[ 'foo', true, [], '<FooResponse></FooResponse>', $encoding]
];
}
public function datetime_fixtures()
{
return [
'empty_args' => [ [] ],
'only_date' => [ ['date'=>'2014-12-06'] ],
'only_time' => [ ['time'=>'08:38:23'] ],
'valid_args' => [ ['date'=>'2014-12-06', 'time'=>'08:38:23'] ] ,
'crazy_args' => [ ['foo'=>'123', 'bar'=>'abc', 'baz'=>'#$%'] ]
];
}
}
================================================
FILE: test/lib/RealTADTest.php
================================================
<?php
/*
* tad-php
*
* (The MIT License)
*
* Copyright (c) 2014 Jorge Cobis <jcobis@gmail.com / http://twitter.com/cobisja>.
*
* 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.
*/
/**
* RealTADTest: Tests about real interaction between TAD-PHP classes and
* ZK Time & Attendance Devices.
*/
namespace Test;
use TADPHP\TAD;
use TADPHP\TADFactory;
class RealTADTest extends \PHPUnit_Framework_TestCase
{
/**
* @var string TAD real ip address.
*/
private $tad_ip;
/**
* @var string TAD encoding.
*/
private $tad_encoding;
public function setUp()
{
$this->tad_ip = '192.168.100.156';
$this->tad_encoding = 'iso8859-1';
if (!TAD::is_device_online($this->tad_ip)) {
$this->markTestSkipped("Real TAD tests disabled. Device in {$this->tad_ip} is offline!");
}
}
public function testDeviceIsOnLine()
{
$tad_options = [ 'ip' => $this->tad_ip, 'encoding' => $this->tad_encoding ];
$tad = (new TADFactory($tad_options))->get_instance();
$this->assertNotNull($tad);
$this->assertInstanceOf('TADPHP\TAD', $tad);
return $tad;
}
/**
* @depends testDeviceIsOnLine
*/
public function testGetDate(TAD $tad)
{
$date = $tad->get_date();
$xml_object = new \SimpleXMLElement($date);
$this->assertNotNull($xml_object->Row->Date);
$this->assertNotNull($xml_object->Row->Time);
$this->assertRegExp('/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/', (string) $xml_object->Row->Date);
$this->assertRegExp('/^[0-9]{2}:[0-9]{2}:[0-9]{2}$/', (string) $xml_object->Row->Time);
}
/**
* @depends testDeviceIsOnLine
*/
public function testSetDate(TAD $tad)
{
$date = '2000-01-01';
$time = '12:15:30';
$expected_response = $this->build_expected_response('SetDateResponse');
$response = $tad->set_date(['date'=>$date, 'time'=>$time])->to_xml();
$this->assertEquals($expected_response, $response);
$dt = $tad->get_date();
$xml_object = new \SimpleXMLElement($dt);
$tad->set_date();
$this->assertEquals($date, (string)$xml_object->Row->Date);
$this->assertRegExp('/12:15:[0-9]{2}/', (string)$xml_object->Row->Time);
}
/**
* @depends testDeviceIsOnLine
*/
public function testGetFreeSizes(TAD $tad)
{
$expected_free_sizes_keys = [
'att_logs_available', 'templates_available', 'att_logs_capacity',
'templates_capacity', 'passwords_stored', 'admins_stored',
'att_logs_stored', 'templates_stored', 'users_stored'
];
$expected_free_sizes_items = count($expected_free_sizes_keys);
$free_sizes = $tad->get_free_sizes();
$xml_object = new \SimpleXMLElement($free_sizes->to_xml());
$this->assertEquals($expected_free_sizes_items, $xml_object->Row->children()->count());
$free_sizes_array = $free_sizes->to_array()['Row'];
$free_sizes_keys = array_keys($free_sizes_array);
$this->assertTrue($expected_free_sizes_keys === $free_sizes_keys);
}
/**
* @depends testDeviceIsOnLine
*/
public function testSetUserInfoAndDeleteUser(TAD $tad)
{
$user_info = [
'pin' => 123,
'name' => 'Foo Bar',
'password' => 8888,
'privilege' => 0,
];
$expected_set_user_info_response = $this->build_expected_response('SetUserInfoResponse');
$expected_delete_user_info_response = $this->build_expected_response('DeleteUserResponse');
$fs = $tad->get_free_sizes();
$total_users_before = (integer) $fs->to_array()['Row']['users_stored'];
$set_user_info_response = $tad->set_user_info($user_info)->to_xml();
$fs = $tad->get_free_sizes();
$total_users_after = (integer) $fs->to_array()['Row']['users_stored'];
$delete_user_response = $tad->delete_user([ 'pin' => $user_info['pin'] ])->to_xml();
$this->assertEquals($expected_set_user_info_response, $set_user_info_response);
$this->assertEquals($expected_delete_user_info_response, $delete_user_response);
$this->assertTrue($total_users_after === $total_users_before + 1);
}
/**
* @depends testDeviceIsOnLine
*/
public function testGetUserInfo(TAD $tad)
{
$user_info = [
'pin' => 123,
'name' => 'Foo Bar',
'password' => 8888,
'privilege' => 0,
];
$tad->set_user_info($user_info);
$response = $tad->get_user_info(['pin'=>123]);
$user_info_response = $response->to_array()['Row'];
$tad->delete_user(['pin'=>123]);
$this->assertEquals($user_info_response['PIN2'], $user_info['pin']);
$this->assertEquals($user_info_response['Name'], $user_info['name']);
$this->assertEquals($user_info_response['Password'], $user_info['password']);
$this->assertEquals($user_info_response['Privilege'], $user_info['privilege']);
}
/**
* @depends testDeviceIsOnLine
*/
public function testGetAllUserInfo(TAD $tad)
{
$fs = $tad->get_free_sizes();
$total_users = (integer) $fs->to_array()['Row']['users_stored'];
$all_user_info_items = $tad->get_all_user_info()->count();
$this->assertEquals($total_users, $all_user_info_items);
}
/**
* @depends testDeviceIsOnLine
*/
public function testDeleteUserPassword(TAD $tad)
{
$user_info = [
'pin' => 123,
'name' => 'Foo Bar',
'password' => 8888,
'privilege' => 0,
];
$expected_response = $this->build_expected_response('ClearUserPasswordResponse');
$tad->set_user_info($user_info);
$before_password = $tad->get_user_info(['pin'=>$user_info['pin']])->to_array()['Row']['Password'];
$response = $tad->delete_user_password(['pin'=>$user_info['pin']])->to_xml();
$after_password = $tad->get_user_info(['pin'=>$user_info['pin']])->to_array()['Row']['Password'];
$tad->delete_user(['pin'=>$user_info['pin']]);
$this->assertEquals($expected_response, $response);
$this->assertNotEquals($after_password, $before_password);
$this->assertEmpty($after_password);
}
/**
* @depends testDeviceIsOnLine
*/
public function testGetAndSetAndDeleteUserTemplate(TAD $tad)
{
$user_info = [
'pin' => 123,
'name' => 'Foo Bar',
'password' => 8888,
'privilege' => 0,
];
$template1_vx9 = "ocosgoulTUEdNKVRwRQ0I27BDTEkdMEONK9KQQunMVSBK6VPLEENk9MwgQ+DP3PBC1FTXEEG4ihpQQQ3vFQBO4K+WwERYilHAQ8ztktBEBbKQ0ELDtJrwQ7dqCiBCz+/IgEGKrBjQQhEO0zBFQNDQYEKFbhrQQdLF1wBDxclfUELMNFXwQRvvmHBCslKUAEZfU1OQRzmIU5BXRW0eoEKPMltgQnQGUyBJQSfRIEUSzIdAQ45l3gBByHUTMEJ5yVhQQmi0UZBFHvYPUEGeKxTAQ6rFGNBCIYURoEOZS9VwR+1M4RoE5m0DRUTF8DHd6HdqxHAxWmj393M28DDX2FkanKi/t7LGsDCWqGarmt1BaL/25nAwVaiipu/cgcQGKG6mcDBU6KYmr5wChQcobmJIsDBUKKJmZ1uExyi+ZaYwMFMgU2CQCSinYdnJsDBR4Ghl3Q4owa3dnfAwUamdlZlR5p2Zi7AwUSndERlfOpWZlfAwUOiQzVkLDhDopRUVTLAwT2iQ0ZjIzVMolNFRcDBN6I0ZlQebVaiEjRVwMEyolVVUxVxXKEBRUTAwS+iZVYyD3JhoQJFTMDBLKJlVUIKcWShBVVTwMIkoWVkFQhyaaEVZ1rAwh6hVlUPAW+iNGd3wMIToWdlBnWiRWZ3aMDDCqRmZjRpZmrAxASjd2Vnh2/gAA==";
$template1_data = [
'pin' => $user_info['pin'],
'finger_id' => 0,
'size' => 514,
'valid' => 1,
'template' => $template1_vx9
];
$no_template_response = $this->build_expected_response('GetUserTemplateResponse', false, 'No data!');
$expected_set_user_template_response = $this->build_expected_response('SetUserTemplateResponse');
$expected_delete_template_response = $this->build_expected_response('DeleteTemplateResponse');
// Create a test user.
$tad->set_user_info($user_info);
// GetUserTemplate test section.
$before_set_template = $tad->get_user_template(['pin'=>$user_info['pin']]);
$this->assertEquals($no_template_response, $before_set_template->to_xml());
// SetUerTemplate test section.
$response = $tad->set_user_template($template1_data);
$this->assertEquals($expected_set_user_template_response, $response->to_xml());
$after_set_template = $tad->get_user_template(['pin'=>$user_info['pin']]);
$raw_after_template = $after_set_template->to_array($after_set_template)['Row']['Template'];
$this->assertNotEmpty($after_set_template);
$this->assertEquals($template1_vx9, $raw_after_template);
// DeleteTemplate test section.
$response = $tad->delete_template(['pin'=>$user_info['pin']]);
$this->assertEquals($expected_delete_template_response, $response->to_xml());
$after_delete_template = $tad->get_user_template(['pin'=>$user_info['pin']]);
$this->assertEquals($no_template_response, $after_delete_template->to_xml());
// Delete the test user created above.
$tad->delete_user(['pin'=>$user_info['pin']]);
}
/**
* @depends testDeviceIsOnLine
*/
public function testDeleteAdmin(TAD $tad)
{
$user_info = [
'pin' => 123,
'name' => 'Foo Bar',
'password' => 8888,
'privilege' => 14, // Superadmin.
];
$expected_response = $this->build_expected_response('DeleteAdminResponse');
$tad->set_user_info($user_info);
$response = $tad->delete_admin();
$fs = $tad->get_free_sizes();
$total_admins = (integer) $fs->to_array()['Row']['admins_stored'];
$tad->delete_user(['pin'=>$user_info['pin']]);
$this->assertEquals($expected_response, $response);
$this->assertTrue(0 === $total_admins);
}
/**
* @depends testDeviceIsOnLine
*/
public function testRestartDevice(TAD $tad)
{
$expected_response = $this->build_expected_response('RestartResponse');
$device_ip = $tad->get_ip();
$response = $tad->restart();
sleep(2); // Lets give it a few seconds to test if it's online.
$is_device_online = TAD::is_device_online($device_ip);
$this->assertEquals($expected_response, $response);
$this->assertNotTrue($is_device_online);
}
private function build_expected_response($base_tag, $result = true, $information = 'Successfully!')
{
$result_code = (int) $result;
$xml_header = '<?xml version="1.0" encoding="' . $this->tad_encoding. '" standalone="no"?>';
$base_response = "<Row><Result>$result_code</Result><Information>$information</Information></Row>";
$open_tag = "<$base_tag>";
$end_tag = "</$base_tag>";
$expected_response = $xml_header . $open_tag . $base_response . $end_tag;
return $expected_response;
}
}
================================================
FILE: test/lib/TADResponseTest.php
================================================
<?php
/*
* tad-php
*
* (The MIT License)
*
* Copyright (c) 2015 Jorge Cobis <jcobis@gmail.com / http://twitter.com/cobisja>.
*
* 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.
*/
namespace Test;
use TADPHP\TADResponse;
class TADResponseTest extends \PHPUnit_Framework_TestCase
{
public function testTADResponseIsInstantiatedCorrectly()
{
$header = '<?xml version="1.0" encoding="utf-8" standalone="no"?>';
$response = '<FooResponse><value_1>Bar</value_1><value_2>Taz</value_2></FooResponse>';
$encoding = 'utf-8';
$tr = new TADResponse($header . $response, $encoding);
$this->assertInstanceOf('TADPHP\TADResponse', $tr);
$this->assertEquals($encoding, $tr->get_encoding());
$this->assertEquals($header, $tr->get_header());
$this->assertEquals($response, $tr->get_response_body());
$this->assertEquals($header . $response, $tr->get_response());
return $tr;
}
/**
* @depends testTADResponseIsInstantiatedCorrectly
*/
public function testGetResponseInDifferentFormats(TADResponse $tr)
{
$xml_response = $tr->get_response(['format'=>'xml']);
libxml_use_internal_errors(true);
$valid_xml = simplexml_load_string($xml_response);
libxml_use_internal_errors(false);
$this->assertNotFalse($valid_xml);
$json_response = $tr->get_response(['format'=>'json']);
$this->assertNotNull(json_decode($json_response));
$array_response = $tr->get_response(['format'=>'array']);
$this->assertTrue(is_array($array_response) && 0 !== count($array_response));
}
/**
* @depends testTADResponseIsInstantiatedCorrectly
*/
public function testSetEmptyResponse(TADResponse $tr)
{
$tr->set_response('');
$expected_empty_response = ''
. '<?xml version="1.0" encoding="utf-8" standalone="no"?>'
. '<Response>'
. '<Row><Result>0</Result><Information>No data!</Information></Row>'
. '</Response>';
$this->assertEquals(1, $tr->count());
$this->assertTrue($tr->is_empty_response());
$this->assertEquals($expected_empty_response, $tr->get_response());
}
/**
* @depends testTADResponseIsInstantiatedCorrectly
*/
public function testChangeAnAlreadySetResponse(TADResponse $tr)
{
$tr = new TADResponse('<Response><data>Foo</data></Response>', 'iso8859-1');
$first_response = $tr->to_xml();
$tr->set_response(''
. '<?xml version="1.0" encoding="utf-8" standalone="no"?>'
. '<Response><data>Foo</data></Response>'
);
$last_response = $tr->to_xml();
$this->assertNotEquals($last_response, $first_response);
}
/**
* @depends testTADResponseIsInstantiatedCorrectly
*/
public function testGetHeader(TADResponse $tr)
{
$expected_header = '<?xml version="1.0" encoding="utf-8" standalone="no"?>';
$this->assertEquals($expected_header, $tr->get_header());
}
/**
* @depends testTADResponseIsInstantiatedCorrectly
*/
public function testSetHeader(TADResponse $tr)
{
$expected_header = '<?xml version="1.1" encoding="iso8859-1" standalone="yes"?>';
$tr->set_header($expected_header);
$this->assertEquals($expected_header, $tr->get_header());
}
public function testGetResponseBody()
{
$tr = new TADResponse('<Response><data>Foo</data></Response>', 'iso8859-1');
$response_body = '<Response><data>Foo</data></Response>';
$this->assertEquals($response_body, $tr->get_response_body());
}
public function testIsEmptyResponse()
{
$tr = new TADResponse('<Response></Response>', 'iso8859-1');
$this->assertTrue($tr->is_empty_response());
}
public function testCount()
{
$tr = new TADResponse('<Response></Response>', 'iso8859-1');
$this->assertTrue(0 === $tr->count()-1);
}
/**
* @expectedException \Exception
*/
public function testExceptionIsThrownWhenUnknownMethodIsInvoked()
{
$tr = new TADResponse('<Response></Response>', 'iso8859-1');
$tr->foo();
}
/**
* @expectedException TADPHP\Exceptions\FilterArgumentError
*/
public function testFilterArgumentExecptionisThrownWhenWrongArgumentNumber()
{
$tr = new TADResponse('<Response></Response>', 'iso8859-1');
$tr->filter_by_date_and_pin(123);
}
/**
* @depends testTADResponseIsInstantiatedCorrectly
* @dataProvider xmlAttLogFixture
* @expectedException TADPHP\Exceptions\FilterArgumentError
*/
public function testFilterResponseByDateThrowsFilterArgumentExceptionWithInvalidRangeKey($xml, TADResponse $tr)
{
$tr->set_response($xml);
$date_range = ['foo'=>'2014-01-01', 'end'=>'2014-11-29'];
$tr->filter_by_date($date_range);
}
/**
* @depends testTADResponseIsInstantiatedCorrectly
* @dataProvider xmlAttLogFixture
*/
public function testFilterResponseByDate($xml, TADResponse $tr)
{
$tr->set_response($xml);
$this->assertEquals(9, $tr->filter_by_date(['start'=>'2014-01-01'])->count());
$this->assertEquals(1, $tr->filter_by_date(['start'=>'2014-12-04'])->count());
$this->assertTrue($tr->filter_by_date(['start'=>'2014-12-05'])->is_empty_response());
$tr->set_response($xml);
$this->assertEquals(9, $tr->filter_by_date(['end'=>'2014-12-31'])->count());
// $this->assertEquals(1, $tr->filter_by_date(['end'=>'2014-11-30'])->count());
$this->assertTrue($tr->filter_by_date(['end'=>'2014-11-29'])->is_empty_response());
$tr->set_response($xml);
$this->assertEquals(9, $tr->filter_by_date(['start'=>'2014-11-01', 'end'=>'2014-12-31'])->count());
$this->assertTrue($tr->filter_by_date(['start'=>'2015-01-01', 'end'=>'2015-12-31'])->is_empty_response());
}
/**
* @depends testTADResponseIsInstantiatedCorrectly
* @dataProvider xmlAttLogFixture
*/
public function testFilterResponseByTime($xml, TADResponse $tr)
{
$tr->set_response($xml);
$this->assertEquals(5, $tr->filter_by_time(['start'=>'18:00:00'])->count());
$this->assertEquals(2, $tr->filter_by_time(['end'=>'19:00:00'])->count());
$this->assertEquals(1, $tr->filter_by_time(['start'=>'00:00:00', 'end'=>'02:00:00'])->count());
$this->assertTrue($tr->filter_by_date(['start'=>'00:00:00', 'end'=>'01:00:00'])->is_empty_response());
}
/**
* @depends testTADResponseIsInstantiatedCorrectly
* @dataProvider xmlAttLogFixture
*/
public function testFilterResponseByDateTime($xml, TADResponse $tr)
{
$tr->set_response($xml);
$this->assertEquals(9, $tr->filter_by_datetime(['start'=>'2014-11-30 18:00:00'])->count());
$this->assertEquals(9, $tr->filter_by_datetime(['end'=>'2014-12-31 19:00:00'])->count());
$this->assertEquals(1, $tr->filter_by_datetime('2014-12-02 08:01:32')->count());
$this->assertTrue($tr->filter_by_datetime('2015-01-01 00:00:00')->is_empty_response());
}
/**
* @depends testTADResponseIsInstantiatedCorrectly
* @dataProvider xmlAttLogFixture
*/
public function testFilterResponseByStatus($xml, TADResponse $tr)
{
$tr->set_response($xml);
$this->assertEquals(9, $tr->filter_by_status(0)->count());
$this->assertTrue($tr->filter_by_status(1)->is_empty_response());
}
/**
* @depends testTADResponseIsInstantiatedCorrectly
* @dataProvider xmlUserInfoFixture
*/
public function testFilterResponseByPin($xml, TADResponse $tr)
{
$tr->set_response($xml);
$this->assertEquals(9, $tr->filter_by_pin(['start'=>5])->count());
$tr->set_response($xml);
$this->assertEquals(9, $tr->filter_by_pin(['end'=>9])->count());
$tr->set_response($xml);
$this->assertEquals(1, $tr->filter_by_pin(1)->count());
$tr->set_response($xml);
$this->assertTrue($tr->filter_by_pin(0)->is_empty_response());
}
/**
* @depends testTADResponseIsInstantiatedCorrectly
* @dataProvider xmlUserInfoFixture
*/
public function testFilterResponseByPrivilege($xml, TADResponse $tr)
{
$tr->set_response($xml);
$this->assertEquals(1, $tr->filter_by_privilege(14)->count());
$tr->set_response($xml);
$this->assertTrue($tr->filter_by_privilege(2)->is_empty_response());
}
/**
* @depends testTADResponseIsInstantiatedCorrectly
* @dataProvider xmlUserInfoFixture
*/
public function testFilterResponseByCard($xml, TADResponse $tr)
{
$tr->set_response($xml);
$this->assertEquals(1, $tr->filter_by_card(55555)->count());
$tr->set_response($xml);
$this->assertTrue($tr->filter_by_card(999999)->is_empty_response());
}
/**
* @depends testTADResponseIsInstantiatedCorrectly
* @dataProvider xmlUserInfoFixture
*/
public function testFilterResponseUsingLikeOperator($xml, TADResponse $tr)
{
$tr->set_response($xml);
$this->assertEquals(2, $tr->filter_by_name(['like'=>'Dolor'])->count());
$tr->set_response($xml);
$this->assertTrue($tr->filter_by_name(['like'=>'ultricies'])->is_empty_response());
}
/**
* @depends testTADResponseIsInstantiatedCorrectly
* @dataProvider xmlUserInfoFixture
*/
public function testFilterResponseUsingTooManyFilterArguments($xml, TADResponse $tr)
{
$tr->set_response($xml);
$this->assertTrue($tr->filter_by_name(['like'=>'Dolor', 'start'=>'Foo', 'end'=>'Bar' ])->is_empty_response());
}
public function xmlAttLogFixture()
{
return [
['
<?xml version="1.0" encoding="iso8859-1" standalone="no"?>
<GetAttLogResponse>
<Row><PIN>10610805</PIN><DateTime>2014-11-30 18:36:49</DateTime><Verified>0</Verified><Status>0</Status><WorkCode>0</WorkCode></Row>
<Row><PIN>2</PIN><DateTime>2014-11-30 18:43:27</DateTime><Verified>0</Verified><Status>0</Status><WorkCode>0</WorkCode></Row>
<Row><PIN>10610805</PIN><DateTime>2014-11-30 20:52:44</DateTime><Verified>0</Verified><Status>0</Status><WorkCode>0</WorkCode></Row>
<Row><PIN>0</PIN><DateTime>2014-11-30 20:52:54</DateTime><Verified>0</Verified><Status>0</Status><WorkCode>0</WorkCode></Row>
<Row><PIN>10610805</PIN><DateTime>2014-11-30 21:24:46</DateTime><Verified>0</Verified><Status>0</Status><WorkCode>0</WorkCode></Row>
<Row><PIN>0</PIN><DateTime>2014-12-02 08:01:11</DateTime><Verified>0</Verified><Status>0</Status><WorkCode>0</WorkCode></Row>
<Row><PIN>10610805</PIN><DateTime>2014-12-02 08:01:23</DateTime><Verified>0</Verified><Status>0</Status><WorkCode>0</WorkCode></Row>
<Row><PIN>0</PIN><DateTime>2014-12-02 08:01:32</DateTime><Verified>0</Verified><Status>0</Status><WorkCode>0</WorkCode></Row>
<Row><PIN>10610805</PIN><DateTime>2014-12-04 01:06:35</DateTime><Verified>0</Verified><Status>0</Status><WorkCode>0</WorkCode></Row>
</GetAttLogResponse>
']
];
}
public function xmlUserInfoFixture()
{
return [
[
''
. '<?xml version="1.0" encoding="iso8859-1" standalone="no"?>'
. '<GetAllUserInfoResponse>'
. '<Row><PIN>1</PIN><Name>Lorem</Name><Password>1234</Password><Group>1</Group><Privilege>14</Privilege><Card>55555</Card><PIN2>1001</PIN2><TZ1>0</TZ1><TZ2>0</TZ2><TZ3>0</TZ3></Row>'
. '<Row><PIN>2</PIN><Name>Ipsum</Name><Password></Password><Group>1</Group><Privilege>0</Privilege><Card>0</Card><PIN2>1002</PIN2><TZ1>0</TZ1><TZ2>0</TZ2><TZ3>0</TZ3></Row>'
. '<Row><PIN>3</PIN><Name>Dolor Sed</Name><Password></Password><Group>1</Group><Privilege>0</Privilege><Card>0</Card><PIN2>1003</PIN2><TZ1>0</TZ1><TZ2>0</TZ2><TZ3>0</TZ3></Row>'
. '<Row><PIN>4</PIN><Name>Sit</Name><Password></Password><Group>1</Group><Privilege>0</Privilege><Card>0</Card><PIN2>1004</PIN2><TZ1>0</TZ1><TZ2>0</TZ2><TZ3>0</TZ3></Row>'
. '<Row><PIN>5</PIN><Name>Amet</Name><Password></Password><Group>1</Group><Privilege>0</Privilege><Card>0</Card><PIN2>1005</PIN2><TZ1>0</TZ1><TZ2>0</TZ2><TZ3>0</TZ3></Row>'
. '<Row><PIN>6</PIN><Name>Consectetur</Name><Password></Password><Group>1</Group><Privilege>0</Privilege><Card>0</Card><PIN2>1006</PIN2><TZ1>0</TZ1><TZ2>0</TZ2><TZ3>0</TZ3></Row>'
. '<Row><PIN>7</PIN><Name>Adipiscing</Name><Password></Password><Group>1</Group><Privilege>0</Privilege><Card>0</Card><PIN2>1007</PIN2><TZ1>0</TZ1><TZ2>0</TZ2><TZ3>0</TZ3></Row>'
. '<Row><PIN>8</PIN><Name>Elit</Name><Password></Password><Group>1</Group><Privilege>0</Privilege><Card>0</Card><PIN2>1008</PIN2><TZ1>0</TZ1><TZ2>0</TZ2><TZ3>0</TZ3></Row>'
. '<Row><PIN>9</PIN><Name>Nulla</Name><Password></Password><Group>1</Group><Privilege>0</Privilege><Card>0</Card><PIN2>1009</PIN2><TZ1>0</TZ1><TZ2>0</TZ2><TZ3>0</TZ3></Row>'
. '<Row><PIN>10</PIN><Name>Imperdiet</Name><Password></Password><Group>1</Group><Privilege>0</Privilege><Card>0</Card><PIN2>1010</PIN2><TZ1>0</TZ1><TZ2>0</TZ2><TZ3>0</TZ3></Row>'
. '<Row><PIN>11</PIN><Name>Molestie</Name><Password></Password><Group>1</Group><Privilege>0</Privilege><Card>0</Card><PIN2>1011</PIN2><TZ1>0</TZ1><TZ2>0</TZ2><TZ3>0</TZ3></Row>'
. '<Row><PIN>12</PIN><Name>Ante</Name><Password></Password><Group>1</Group><Privilege>0</Privilege><Card>0</Card><PIN2>1012</PIN2><TZ1>0</TZ1><TZ2>0</TZ2><TZ3>0</TZ3></Row>'
. '<Row><PIN>13</PIN><Name>Elit Luctus Dolor</Name><Password></Password><Group>1</Group><Privilege>0</Privilege><Card>0</Card><PIN2>1013</PIN2><TZ1>0</TZ1><TZ2>0</TZ2><TZ3>0</TZ3></Row>'
. '</GetAllUserInfoResponse>'
]
];
}
}
================================================
FILE: test/lib/TADTest.php
================================================
<?php
/*
* tad-php
*
* (The MIT License)
*
* Copyright (c) 2014 Jorge Cobis <jcobis@gmail.com / http://twitter.com/cobisja>.
*
* 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.
*/
namespace Test;
use TADPHP\TAD;
use TADPHP\TADFactory;
use TADPHP\Providers\TADSoap;
use TADPHP\Providers\TADZKLib;
class TADTest extends \PHPUnit_Framework_TestCase
{
public function testSoapCommandsAvailable()
{
$commands = TAD::soap_commands_available(['include_command_string'=>true]);
$this->assertInternalType('array', $commands);
$this->assertTrue($commands !== array_values($commands));
}
public function testZKLibCommandsAvailable()
{
$commands = TAD::zklib_commands_available();
$this->assertInternalType('array', $commands);
$this->assertFalse($commands !== array_values($commands));
}
public function testGetValidCommandsArgs()
{
$valid_args = TAD::get_valid_commands_args();
$this->assertInternalType('array', $valid_args);
$this->assertFalse($valid_args !== array_values($valid_args));
}
/**
* @dataProvider get_options
*/
public function testGetOptions(array $options)
{
$method = array_keys($options)[0];
$expected_value = array_values($options)[0];
$tad_options = array_merge(['ip'=>'127.0.0.1'], $options);
$tad = (new TADFactory($tad_options))->get_instance();
$option_value = $tad->{"get_$method"}();
$this->assertEquals($expected_value, $option_value);
}
public function testDeviceWithInvalidIPAddressIsNotAlive()
{
$tad = (new TADFactory(['ip'=>'1.2.3.4', 'connection_timeout'=>1]))->get_instance();
$this->assertFalse($tad->is_alive());
}
/**
* @expectedException TADPHP\Exceptions\ConnectionError
*/
public function testTADThrowsConnectionErrorExceptionWithInvalidDeviceIPAddress()
{
$tad = (new TADFactory(['ip'=>'1.2.3.4', 'connection_timeout'=>1]))->get_instance();
$tad->get_date();
}
/**
* @expectedException TADPHP\Exceptions\UnrecognizedCommand
*/
public function testTADThrowsUnrecognizedCommandExceptionWithInvalidCommand()
{
$tad = (new TADFactory(['ip'=>'127.0.0.1']))->get_instance();
$tad->foo();
}
/**
* @expectedException TADPHP\Exceptions\UnrecognizedArgument
*/
public function testTADThrowsUnrecognizedArgumentExceptionWithValidCommand()
{
$tad = (new TADFactory(['ip'=>'127.0.0.1']))->get_instance();
$tad->get_user_info(['foo'=>'bar']);
}
public function testTAD()
{
$options = $this->get_tad_and_soap_options();
$tad_soap_provider = new TADSoap(new \SoapClient(null, $options['soap']), $options['soap']);
$zklib_provider = new TADZKLib($options['tad']);
$tad = $this->getMockBuilder('\TADPHP\TAD')
->setConstructorArgs([ $tad_soap_provider, $zklib_provider, $options['tad']])
->setMethods(['is_alive', 'execute_command_via_tad_soap', 'execute_command_via_zklib'])
->getMock();
$tad->expects($this->any())
->method('is_alive')
->will($this->returnValue(true));
$tad->expects($this->any())
->method('execute_command_via_tad_soap')
->will($this->returnValue('<CommandResponse>Executed via SOAP</CommandResponse>'));
$tad->expects($this->once())
->method('execute_command_via_zklib')
->will($this->returnValue('<CommandResponse>Executed via ZKLib</CommandResponse>'));
$this->assertEquals(
'<CommandResponse>Executed via SOAP</CommandResponse>',
$tad->get_date()
);
$this->assertEquals(
'<CommandResponse>Executed via ZKLib</CommandResponse>',
$tad->set_date()
);
}
public function testExecuteCommandViaTADSoap()
{
$options = $this->get_tad_and_soap_options();
$mock_response = '<GetDateResponse><row><date>2014-01-01</date><time>12:00:00</time></row></GetDateResponse>';
$soap_client = new \SoapClient(null, $options['soap']);
$tad_soap = $this->getMockBuilder('TADPHP\Providers\TADSoap')
->setConstructorArgs([ $soap_client, $options['soap'] ])
->setMethods(['execute_soap_command'])
->getMock();
$tad_soap->expects($this->once())
->method('execute_soap_command')
->will($this->returnValue($mock_response));
$zklib_provider = new TADZKLib($options['tad']);
$tad = $this->getMockBuilder('\TADPHP\TAD')
->setConstructorArgs([ $tad_soap, $zklib_provider, $options['tad'] ])
->setMethods(null)
->getMock();
$response = $tad->execute_command_via_tad_soap('get_date', []);
$this->assertNotEmpty($response);
}
public function testExecuteCommandViaZKLib()
{
$options = $this->get_tad_and_soap_options();
$mock_response = ''
. '<SetDateResponse>'
. '<Result>1</Result>'
. '<Information>Successfully!</Information>'
. '</SetDateResponse>';
$soap_client = new \SoapClient(null, $options['soap']);
$tad_soap = new TADSoap($soap_client, $options['soap']);
$zk = $this->getMockBuilder('TADPHP\Providers\TADZKLib')
->setConstructorArgs([ $options['tad'] ])
->setMethods(['__call'])
->getMock();
$zk->expects($this->once())
->method('__call')
->will($this->returnValue($mock_response));
$tad = $this->getMockBuilder('\TADPHP\TAD')
->setConstructorArgs([ $tad_soap, $zk, $options['tad'] ])
->setMethods(null)
->getMock();
$response = $tad->set_date();
$this->assertNotEmpty($response);
}
protected function soap_options()
{
return [
[]
];
}
protected function get_tad_and_soap_options()
{
$options = array_reduce(array_reduce($this->get_options(), 'array_merge', []), 'array_merge', []);
$soap_options = [
'location' => "http://{$options['ip']}/iWsService",
'uri' => 'http://www.zksoftware/Service/message/'
];
return ['tad' => $options, 'soap' => $soap_options];
}
public function get_options()
{
$options = [
[['ip' => '127.0.0.1']],
[['com_key' => 0]],
[['internal_id' => 100]],
[['description' => 'Foo']],
[['connection_timeout'=> 1]],
[['udp_port' => 4370]],
[['encoding'=>'iso8859-1']]
];
return $options;
}
}
gitextract_4ybaawxs/
├── .gitignore
├── LICENSE
├── README.md
├── composer.json
├── lib/
│ ├── Exceptions/
│ │ ├── ConnectionError.php
│ │ ├── FilterArgumentError.php
│ │ ├── UnrecognizedArgument.php
│ │ └── UnrecognizedCommand.php
│ ├── Providers/
│ │ ├── TADSoap.php
│ │ └── TADZKLib.php
│ ├── TAD.php
│ ├── TADFactory.php
│ └── TADResponse.php
├── phpunit.xml
└── test/
├── helpers/
│ └── ClassReflection.php
└── lib/
├── Providers/
│ ├── TADSoapTest.php
│ └── TADZKLibTest.php
├── RealTADTest.php
├── TADResponseTest.php
└── TADTest.php
SYMBOL INDEX (157 symbols across 15 files)
FILE: lib/Exceptions/ConnectionError.php
class ConnectionError (line 30) | class ConnectionError extends \Exception
FILE: lib/Exceptions/FilterArgumentError.php
class FilterArgumentError (line 30) | class FilterArgumentError extends \Exception
FILE: lib/Exceptions/UnrecognizedArgument.php
class UnrecognizedArgument (line 30) | class UnrecognizedArgument extends \Exception
FILE: lib/Exceptions/UnrecognizedCommand.php
class UnrecognizedCommand (line 30) | class UnrecognizedCommand extends \Exception
FILE: lib/Providers/TADSoap.php
class TADSoap (line 35) | class TADSoap
method get_commands_available (line 78) | static public function get_commands_available(array $options = [])
method __construct (line 90) | public function __construct(\SoapClient $soap_client, array $soap_clie...
method execute_soap_command (line 103) | public function execute_soap_command($soap_command, array $soap_comman...
method get_soap_provider_options (line 120) | public function get_soap_provider_options()
method build_soap_request (line 132) | public function build_soap_request($command, array $args, $encoding)
method get_command_string (line 157) | private function get_command_string($key)
method execute_single_soap_request (line 169) | private function execute_single_soap_request($soap_request, $soap_loca...
method execute_multiple_soap_requests (line 181) | private function execute_multiple_soap_requests(array $soap_requests, ...
method parse_command_string (line 197) | private function parse_command_string($command_string, array $command_...
method normalize_xml_string (line 218) | public static function normalize_xml_string($xml, $encoding = 'utf-8')
FILE: lib/Providers/TADZKLib.php
class TADZKLib (line 40) | class TADZKLib
method get_commands_available (line 243) | static public function get_commands_available()
method __construct (line 253) | public function __construct(array $options)
method __call (line 271) | public function __call($command, array $args)
method connect (line 310) | private function connect()
method disconnect (line 344) | private function disconnect()
method zk_set_date (line 374) | private function zk_set_date(array $dt = [])
method zk_get_free_sizes (line 386) | private function zk_get_free_sizes()
method send_command_to_device (line 420) | private function send_command_to_device($command, $command_string = ''...
method createChkSum (line 456) | private function createChkSum($p)
method createHeader (line 511) | private function createHeader($command, $chksum, $session_id, $reply_i...
method checkValid (line 547) | private function checkValid($reply)
method build_command_response (line 567) | private function build_command_response($command, $result_code, $resul...
method setup_datetime (line 612) | private function setup_datetime(array $dt=[])
method reverse_hex (line 634) | private function reverse_hex($hexstr)
method encode_time (line 654) | private function encode_time(array $t)
method array_to_xml (line 672) | private function array_to_xml(\SimpleXMLElement $object, array $data)
FILE: lib/TAD.php
class TAD (line 105) | class TAD
method commands_available (line 177) | public static function commands_available()
method soap_commands_available (line 187) | public static function soap_commands_available(array $options = [])
method zklib_commands_available (line 197) | public static function zklib_commands_available()
method get_valid_commands_args (line 207) | public static function get_valid_commands_args()
method is_device_online (line 219) | public static function is_device_online($ip, $timeout = 1)
method __construct (line 236) | public function __construct(TADSoap $soap_provider, TADZKLib $zklib_pr...
method __call (line 267) | public function __call($command, array $args)
method execute_command_via_tad_soap (line 293) | public function execute_command_via_tad_soap($command, array $args = [])
method execute_command_via_zklib (line 311) | public function execute_command_via_zklib($command, array $args = [])
method get_ip (line 324) | public function get_ip()
method get_internal_id (line 334) | public function get_internal_id()
method get_com_key (line 344) | public function get_com_key()
method get_description (line 354) | public function get_description()
method get_connection_timeout (line 364) | public function get_connection_timeout()
method get_encoding (line 374) | public function get_encoding()
method get_udp_port (line 384) | public function get_udp_port()
method is_alive (line 394) | public function is_alive()
method check_for_connection (line 405) | private function check_for_connection()
method check_for_valid_command (line 421) | private function check_for_valid_command($command)
method check_for_unrecognized_args (line 439) | private function check_for_unrecognized_args(array $args)
method check_for_refresh_tad_db (line 455) | private function check_for_refresh_tad_db($command_executed)
method config_array_items (line 467) | private function config_array_items(array $values)
FILE: lib/TADFactory.php
class TADFactory (line 34) | class TADFactory
method __construct (line 43) | public function __construct(array $options = [])
method get_instance (line 53) | public function get_instance()
method get_default_options (line 80) | private function get_default_options()
method set_options (line 100) | private function set_options(array $base_options, array &$options)
FILE: lib/TADResponse.php
class TADResponse (line 35) | class TADResponse
method __construct (line 65) | public function __construct($response, $encoding)
method get_response (line 83) | public function get_response(array $options = [])
method set_response (line 97) | public function set_response($response='')
method get_encoding (line 117) | public function get_encoding()
method set_encoding (line 127) | public function set_encoding($encoding)
method get_header (line 138) | public function get_header()
method set_header (line 148) | public function set_header($header)
method get_response_body (line 158) | public function get_response_body()
method is_empty_response (line 168) | public function is_empty_response()
method to_xml (line 178) | public function to_xml()
method to_json (line 188) | public function to_json()
method to_array (line 198) | public function to_array()
method count (line 208) | public function count()
method __call (line 223) | public function __call($method, $args)
method __toString (line 275) | public function __toString()
method filter_xml (line 289) | public function filter_xml($xml, $filter, array $range=[], $xml_init_r...
method extract_xml_header (line 363) | private function extract_xml_header(&$xml)
method set_response_header_encoding (line 384) | private function set_response_header_encoding($encoding = 'utf-8')
method sanitize_xml_string (line 403) | private function sanitize_xml_string($xml, array $undesired_chars = [ ...
method normalize_filter_args (line 414) | private function normalize_filter_args($args)
method is_there_no_data (line 448) | private function is_there_no_data($response)
method get_items_number (line 459) | private function get_items_number($response)
method build_no_data_response (line 478) | private function build_no_data_response($response)
method is_no_data_response (line 495) | private function is_no_data_response($response)
FILE: test/helpers/ClassReflection.php
class ClassReflection (line 30) | abstract class ClassReflection
method invoke_method (line 44) | static public function invoke_method(&$object, $method_name, array $pa...
FILE: test/lib/Providers/TADSoapTest.php
class TADSoapTest (line 9) | class TADSoapTest extends \PHPUnit_Framework_TestCase
method testBuildTADSoap (line 11) | public function testBuildTADSoap()
method testGetSoapProviderOptions (line 27) | public function testGetSoapProviderOptions($tad_soap_instance)
method testBuildSoapRequest (line 42) | public function testBuildSoapRequest($command, array $args, $expected_...
method testBuildMultipleSoapRequest (line 53) | public function testBuildMultipleSoapRequest(TADSoap $tad_soap)
method testExecuteSoapRequest (line 64) | public function testExecuteSoapRequest()
method testExecuteMultipleSoapRequests (line 95) | public function testExecuteMultipleSoapRequests()
method soapCommandsFixtures (line 131) | public function soapCommandsFixtures()
method get_soap_options (line 153) | private function get_soap_options()
FILE: test/lib/Providers/TADZKLibTest.php
class TADZKLibTest (line 8) | class TADZKLibTest extends \PHPUnit_Framework_TestCase
method testBuildTADZKLibIsOk (line 11) | public function testBuildTADZKLibIsOk()
method testBuildCommandResponse (line 25) | public function testBuildCommandResponse($command, $result_code, $resu...
method testSettingUpDateIsOk (line 36) | public function testSettingUpDateIsOk($datetime, TADZKLib $zk)
method testReverseHex (line 49) | public function testReverseHex(TADZKLib $zk)
method testEncodeTime (line 63) | public function testEncodeTime(TADZKLib $zk)
method build_commands_fixtures (line 77) | public function build_commands_fixtures()
method datetime_fixtures (line 88) | public function datetime_fixtures()
FILE: test/lib/RealTADTest.php
class RealTADTest (line 37) | class RealTADTest extends \PHPUnit_Framework_TestCase
method setUp (line 49) | public function setUp()
method testDeviceIsOnLine (line 59) | public function testDeviceIsOnLine()
method testGetDate (line 73) | public function testGetDate(TAD $tad)
method testSetDate (line 87) | public function testSetDate(TAD $tad)
method testGetFreeSizes (line 107) | public function testGetFreeSizes(TAD $tad)
method testSetUserInfoAndDeleteUser (line 131) | public function testSetUserInfoAndDeleteUser(TAD $tad)
method testGetUserInfo (line 161) | public function testGetUserInfo(TAD $tad)
method testGetAllUserInfo (line 186) | public function testGetAllUserInfo(TAD $tad)
method testDeleteUserPassword (line 199) | public function testDeleteUserPassword(TAD $tad)
method testGetAndSetAndDeleteUserTemplate (line 226) | public function testGetAndSetAndDeleteUserTemplate(TAD $tad)
method testDeleteAdmin (line 279) | public function testDeleteAdmin(TAD $tad)
method testRestartDevice (line 305) | public function testRestartDevice(TAD $tad)
method build_expected_response (line 319) | private function build_expected_response($base_tag, $result = true, $i...
FILE: test/lib/TADResponseTest.php
class TADResponseTest (line 34) | class TADResponseTest extends \PHPUnit_Framework_TestCase
method testTADResponseIsInstantiatedCorrectly (line 36) | public function testTADResponseIsInstantiatedCorrectly()
method testGetResponseInDifferentFormats (line 57) | public function testGetResponseInDifferentFormats(TADResponse $tr)
method testSetEmptyResponse (line 75) | public function testSetEmptyResponse(TADResponse $tr)
method testChangeAnAlreadySetResponse (line 91) | public function testChangeAnAlreadySetResponse(TADResponse $tr)
method testGetHeader (line 107) | public function testGetHeader(TADResponse $tr)
method testSetHeader (line 116) | public function testSetHeader(TADResponse $tr)
method testGetResponseBody (line 124) | public function testGetResponseBody()
method testIsEmptyResponse (line 132) | public function testIsEmptyResponse()
method testCount (line 138) | public function testCount()
method testExceptionIsThrownWhenUnknownMethodIsInvoked (line 147) | public function testExceptionIsThrownWhenUnknownMethodIsInvoked()
method testFilterArgumentExecptionisThrownWhenWrongArgumentNumber (line 156) | public function testFilterArgumentExecptionisThrownWhenWrongArgumentNu...
method testFilterResponseByDateThrowsFilterArgumentExceptionWithInvalidRangeKey (line 167) | public function testFilterResponseByDateThrowsFilterArgumentExceptionW...
method testFilterResponseByDate (line 179) | public function testFilterResponseByDate($xml, TADResponse $tr)
method testFilterResponseByTime (line 200) | public function testFilterResponseByTime($xml, TADResponse $tr)
method testFilterResponseByDateTime (line 213) | public function testFilterResponseByDateTime($xml, TADResponse $tr)
method testFilterResponseByStatus (line 226) | public function testFilterResponseByStatus($xml, TADResponse $tr)
method testFilterResponseByPin (line 237) | public function testFilterResponseByPin($xml, TADResponse $tr)
method testFilterResponseByPrivilege (line 256) | public function testFilterResponseByPrivilege($xml, TADResponse $tr)
method testFilterResponseByCard (line 269) | public function testFilterResponseByCard($xml, TADResponse $tr)
method testFilterResponseUsingLikeOperator (line 282) | public function testFilterResponseUsingLikeOperator($xml, TADResponse ...
method testFilterResponseUsingTooManyFilterArguments (line 295) | public function testFilterResponseUsingTooManyFilterArguments($xml, TA...
method xmlAttLogFixture (line 302) | public function xmlAttLogFixture()
method xmlUserInfoFixture (line 322) | public function xmlUserInfoFixture()
FILE: test/lib/TADTest.php
class TADTest (line 35) | class TADTest extends \PHPUnit_Framework_TestCase
method testSoapCommandsAvailable (line 37) | public function testSoapCommandsAvailable()
method testZKLibCommandsAvailable (line 45) | public function testZKLibCommandsAvailable()
method testGetValidCommandsArgs (line 53) | public function testGetValidCommandsArgs()
method testGetOptions (line 64) | public function testGetOptions(array $options)
method testDeviceWithInvalidIPAddressIsNotAlive (line 77) | public function testDeviceWithInvalidIPAddressIsNotAlive()
method testTADThrowsConnectionErrorExceptionWithInvalidDeviceIPAddress (line 87) | public function testTADThrowsConnectionErrorExceptionWithInvalidDevice...
method testTADThrowsUnrecognizedCommandExceptionWithInvalidCommand (line 96) | public function testTADThrowsUnrecognizedCommandExceptionWithInvalidCo...
method testTADThrowsUnrecognizedArgumentExceptionWithValidCommand (line 105) | public function testTADThrowsUnrecognizedArgumentExceptionWithValidCom...
method testTAD (line 111) | public function testTAD()
method testExecuteCommandViaTADSoap (line 145) | public function testExecuteCommandViaTADSoap()
method testExecuteCommandViaZKLib (line 173) | public function testExecuteCommandViaZKLib()
method soap_options (line 204) | protected function soap_options()
method get_tad_and_soap_options (line 211) | protected function get_tad_and_soap_options()
method get_options (line 223) | public function get_options()
Condensed preview — 20 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (150K chars).
[
{
"path": ".gitignore",
"chars": 52,
"preview": "index.php\n\ncoverage/**/*\nnbproject/**/*\nvendor/**/*\n"
},
{
"path": "LICENSE",
"chars": 1091,
"preview": "Copyright (c) 2014-2015 Jorge Cobis <jcobis@gmail.com>\n\nMIT License\n\nPermission is hereby granted, free of charge, to an"
},
{
"path": "README.md",
"chars": 24709,
"preview": "# TAD-PHP\n\nA simple PHP class to interacts with ZK Time & Attendance Devices.\n\n##About\n\nTAD: A class that implements an "
},
{
"path": "composer.json",
"chars": 641,
"preview": "{\n \"name\": \"TAD-PHP\",\n \"type\": \"library\",\n \"description\": \"PHP class that allows you interact with ZK Time & Attendan"
},
{
"path": "lib/Exceptions/ConnectionError.php",
"chars": 1285,
"preview": "<?php\n/*\n * tad-php\n *\n * (The MIT License)\n *\n * Copyright (c) 2014 Jorge Cobis <jcobis@gmail.com / http://twitter.com/"
},
{
"path": "lib/Exceptions/FilterArgumentError.php",
"chars": 1289,
"preview": "<?php\n/*\n * tad-php\n *\n * (The MIT License)\n *\n * Copyright (c) 2014 Jorge Cobis <jcobis@gmail.com / http://twitter.com/"
},
{
"path": "lib/Exceptions/UnrecognizedArgument.php",
"chars": 1290,
"preview": "<?php\n/*\n * tad-php\n *\n * (The MIT License)\n *\n * Copyright (c) 2014 Jorge Cobis <jcobis@gmail.com / http://twitter.com/"
},
{
"path": "lib/Exceptions/UnrecognizedCommand.php",
"chars": 1289,
"preview": "<?php\n/*\n * tad-php\n *\n * (The MIT License)\n *\n * Copyright (c) 2014 Jorge Cobis <jcobis@gmail.com / http://twitter.com/"
},
{
"path": "lib/Providers/TADSoap.php",
"chars": 9123,
"preview": "<?php\n/*\n * tad-php\n *\n * (The MIT License)\n *\n * Copyright (c) 2014 Jorge Cobis <jcobis@gmail.com / http://twitter.com/"
},
{
"path": "lib/Providers/TADZKLib.php",
"chars": 22670,
"preview": "<?php\n/*\n * tad-php\n *\n * (The MIT License)\n *\n * Copyright (c) 2014 Jorge Cobis <jcobis@gmail.com / http://twitter.com/"
},
{
"path": "lib/TAD.php",
"chars": 15672,
"preview": "<?php\n/*\n * tad-php\n *\n * (The MIT License)\n *\n * Copyright (c) 2014 Jorge Cobis <jcobis@gmail.com / http://twitter.com/"
},
{
"path": "lib/TADFactory.php",
"chars": 3456,
"preview": "<?php\n/*\n * tad-php\n *\n * (The MIT License)\n *\n * Copyright (c) 2014 Jorge Cobis <jcobis@gmail.com / http://twitter.com/"
},
{
"path": "lib/TADResponse.php",
"chars": 15521,
"preview": "<?php\n/*\n * tad-php\n *\n * (The MIT License)\n *\n * Copyright (c) 2015 Jorge Cobis <jcobis@gmail.com / http://twitter.com/"
},
{
"path": "phpunit.xml",
"chars": 447,
"preview": "<phpunit bootstrap=\"vendor/autoload.php\"\n colors=\"true\"\n convertErrorsToExceptions=\"true\"\n conve"
},
{
"path": "test/helpers/ClassReflection.php",
"chars": 2106,
"preview": "<?php\n/*\n * tad-php\n *\n * (The MIT License)\n *\n * Copyright (c) 2014 Jorge Cobis <jcobis@gmail.com / http://twitter.com/"
},
{
"path": "test/lib/Providers/TADSoapTest.php",
"chars": 5712,
"preview": "<?php\nnamespace Test\\Providers;\n\nuse TADPHP\\TAD;\nuse TADPHP\\Providers\\TADSoap;\nuse Test\\Helpers\\ClassReflection;\n\n\nclass"
},
{
"path": "test/lib/Providers/TADZKLibTest.php",
"chars": 3373,
"preview": "<?php\nnamespace Test\\Providers;\n\nuse TADPHP\\Providers\\TADZKLib;\nuse Test\\Helpers\\ClassReflection;\n\n\nclass TADZKLibTest e"
},
{
"path": "test/lib/RealTADTest.php",
"chars": 11977,
"preview": "<?php\n/*\n * tad-php\n *\n * (The MIT License)\n *\n * Copyright (c) 2014 Jorge Cobis <jcobis@gmail.com / http://twitter.com/"
},
{
"path": "test/lib/TADResponseTest.php",
"chars": 15279,
"preview": "<?php\n\n/*\n * tad-php\n *\n * (The MIT License)\n *\n * Copyright (c) 2015 Jorge Cobis <jcobis@gmail.com / http://twitter.com"
},
{
"path": "test/lib/TADTest.php",
"chars": 7793,
"preview": "<?php\n/*\n * tad-php\n *\n * (The MIT License)\n *\n * Copyright (c) 2014 Jorge Cobis <jcobis@gmail.com / http://twitter.com/"
}
]
About this extraction
This page contains the full source code of the cobisja/tad-php GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 20 files (141.4 KB), approximately 37.6k tokens, and a symbol index with 157 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.