From b476db93cf378950fe0c7d9f169f84457b999345 Mon Sep 17 00:00:00 2001 From: Daniel Melani Date: Sun, 27 Mar 2016 21:46:21 +0200 Subject: [PATCH 1/4] Add chassis serial to brocade ironware devices --- includes/polling/os/ironware.inc.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/includes/polling/os/ironware.inc.php b/includes/polling/os/ironware.inc.php index e7a2406f05..210fe56ed5 100644 --- a/includes/polling/os/ironware.inc.php +++ b/includes/polling/os/ironware.inc.php @@ -17,3 +17,5 @@ $version = trim( $version = str_replace('V', '', $version); $version = str_replace('"', '', $version); + +$serial = trim(snmp_get($device, 'snChasSerNum.0', '-Ovq', 'FOUNDRY-SN-AGENT-MIB'), '"'); From c2a292789418b7feba81796d721955fce9c4a8b4 Mon Sep 17 00:00:00 2001 From: Daniel Melani Date: Tue, 29 Mar 2016 16:14:53 +0200 Subject: [PATCH 2/4] Make toner overview line up with the rest --- html/pages/device/overview/toner.inc.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/html/pages/device/overview/toner.inc.php b/html/pages/device/overview/toner.inc.php index 7014d74c69..d77b395ade 100644 --- a/html/pages/device/overview/toner.inc.php +++ b/html/pages/device/overview/toner.inc.php @@ -5,7 +5,8 @@ $graph_type = 'toner_usage'; $toners = dbFetchRows('SELECT * FROM `toner` WHERE device_id = ?', array($device['device_id'])); if (count($toners)) { - echo '
+ echo '
+
'; @@ -57,6 +58,7 @@ if (count($toners)) { echo '
'; echo '
'; echo '
'; + echo '
'; }//end if unset($toner_rows); From 51cf4ca0a38d4a35c2d5a53caa30e253934910d5 Mon Sep 17 00:00:00 2001 From: vectr0n Date: Tue, 29 Mar 2016 15:24:45 -0400 Subject: [PATCH 3/4] Create Installation-Nginx-(Debian-Ubuntu).md --- .../Installation-Nginx-(Debian-Ubuntu).md | 235 ++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 doc/Installation/Installation-Nginx-(Debian-Ubuntu).md diff --git a/doc/Installation/Installation-Nginx-(Debian-Ubuntu).md b/doc/Installation/Installation-Nginx-(Debian-Ubuntu).md new file mode 100644 index 0000000000..581aada57a --- /dev/null +++ b/doc/Installation/Installation-Nginx-(Debian-Ubuntu).md @@ -0,0 +1,235 @@ + +> NOTE: These instructions have been tested on a fresh install of Ubuntu 12.04 and 14.04. + +> NOTE: These instructions assume you are the root user. If you are not, prepend `sudo` to the shell commands (the ones that aren't at `mysql>` prompts) or temporarily become a user with root privileges with `sudo -s` or `sudo -i`. + +### On the DB Server ### + +This host is where the MySQL database runs. It could be the same machine as your network management server (this is the most common initial deployment scenario). + +> ** Whilst we are working on ensuring LibreNMS is compatible with MySQL strict mode, for now, please disable this after mysql is installed. + +You are free to choose between using MySQL or MariaDB: + +**MySQL** +```bash +apt-get install mysql-server mysql-client +mysql -uroot -p +``` + +**MariaDB** +```bash +apt-get install mariadb-server mariadb-client +mysql -uroot -p +``` + +Input the MySQL root password to enter the MySQL command-line interface. + +Create the database: + +```sql +CREATE DATABASE librenms; +GRANT ALL PRIVILEGES ON librenms.* + TO 'librenms'@'' + IDENTIFIED BY '' +; +FLUSH PRIVILEGES; +exit +``` + +Replace `` above with the IP or DNS name of the server running LibreNMS. If your database is on the same server as LibreNMS, you can use `localhost`. + +If you are deploying a separate database server, you need to change the `bind-address`. If your MySQL database resides on the same server as LibreNMS, you should skip this step. + + vim /etc/mysql/my.cnf + +Within the [mysqld] section please add: + + innodb_file_per_table=1 + +Find the line: `bind-address = 127.0.0.1` + +Change `127.0.0.1` to the IP address that your MySQL server should listen on. Restart MySQL: + +If you see a line that starts `sql-mode` then change this to `sql-mode=""`. + + service mysql restart + +### On the NMS ### + +This host is where the web server and SNMP poller run. It could be the same machine as your database server. + +Install the required software: + + apt-get install nginx-full php5-fpm php5-cli php5-mysql php5-gd php5-snmp php-pear php5-curl php5-mcrypt php5-json php-net-ipv4 php-net-ipv6 snmp snmpd graphviz fping imagemagick whois mtr-tiny nmap python-mysqldb rrdtool git + +The packages listed above are an all-inclusive list of packages that were necessary on a clean install of Ubuntu 12.04/14.04. + +You need to configure snmpd appropriately if you have not already done so. An absolute minimal config for snmpd is: + + rocommunity public 127.0.0.1 + +Adding the above line to `/etc/snmp/snmpd.conf` and running `service snmpd restart` will activate this config. + +In `/etc/php5/apache2/php.ini` and `/etc/php5/cli/php.ini`, ensure date.timezone is set to your preferred time zone. See http://php.net/manual/en/timezones.php for a list of supported timezones. Valid +examples are: "America/New York", "Australia/Brisbane", "Etc/UTC". +Please also ensure that `allow_url_fopen` is enabled. Other functions needed for LibreNMS include `exec,passthru,shell_exec,escapeshellarg,escapeshellcmd,proc_close,proc_open,popen`. + +### Adding the librenms-user ### + + useradd librenms -d /opt/librenms -M -r + usermod -a -G librenms www-data + +### Cloning ### + +LibreNMS is installed using git. If you're not familiar with git, check out the [git book][2] or the tips at [git ready][3]. The initial install from github.com is called a `git clone`; subsequent updates are done through `git pull`. + +You can clone the repository via HTTPS or SSH. In either case, you need to ensure that the appropriate port (443 for HTTPS, 22 for SSH) is open in the outbound direction for your server. + + cd /opt + git clone https://github.com/librenms/librenms.git librenms + cd /opt/librenms + +The recommended method of cloning a git repository is HTTPS. If you would like to clone via SSH instead, use the command `git clone git@github.com:librenms/librenms.git librenms` instead. + +Sometimes the initial clone can take quite a while (nearly 3 minutes on a 10 Mbps fibre connection in Australia is a recent example). If it's a big problem to you, you can save about 50% of the bandwidth by not pulling down the full git history. This comes with some limitations (namely that you can't use it as the basis for further git repos), but if you're not planning to develop for LibreNMS it's an acceptable option. To perform the initial clone without full history, run the following instead: + + cd /opt + git clone --depth 1 https://github.com/librenms/librenms.git librenms + cd /opt/librenms + + +### Web Interface ### + +To prepare the web interface (and adding devices shortly), you'll need to create and chown a directory as well as create an Apache vhost. + +First, create and chown the `rrd` directory and create the `logs` directory: + + mkdir rrd logs + chown -R librenms:librenms /opt/librenms + chmod 775 rrd + +> NOTE: If you're not running Ubuntu or Debian, you will need to change `www-data` to the user and group which run the Nginx web server. +> If you're planing on running rrdcached, make sure that the path is also chmod'ed to 775 and chown'ed to librenms:librenms. + +Add configuration for `nginx` at `/etc/nginx/conf.d/librenms.conf` with the following content: + +```nginx +server { + listen 80; + server_name librenms.example.com; + root /opt/librenms/html; + index index.php; + access_log /opt/librenms/logs/access_log; + error_log /opt/librenms/logs/error_log; + location / { + try_files $uri $uri/ @librenms; + } + location ~ \.php { + fastcgi_param PATH_INFO $fastcgi_path_info; + include fastcgi.conf; + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass unix:/var/run/php5-fpm.sock; + } + location ~ /\.ht { + deny all; + } + location @librenms { + rewrite api/v0(.*)$ /api_v0.php/$1 last; + rewrite ^(.+)$ /index.php/$1 last; + } +} +``` +On at least Ubuntu 14.04 (and possibly other distributions and versions as well), mcrypt is not enabled on install. Run the following to enable it: + + php5enmod mcrypt + +(To get to your LibreNMS install externally, you'll also need add it to your DNS or hosts file.) + +### Start the web-server: ### + +Restart nginx: + + service nginx restart + +Restart php5-fpm: + + service php5-fpm restart + +### Manual vs. web installer ### + +At this stage you can either launch the web installer by going to http://librenms.example.com/install.php, follow the onscreen instructions then skip to the 'Add localhost' section. Alternatively if you want to continue the setup manually then just keep following these instructions. + + cp config.php.default config.php + vim config.php + +Change the values to the right of the equal sign for lines beginning with `$config[db_]` to match your database information as setup above. + +Change the value of `$config['snmp']['community']` from `public` to whatever your read-only SNMP community is. If you have multiple communities, set it to the most common. + +** Be sure you have no characters (including whitespace like: newlines, spaces, tabs, etc) outside of the `` blocks. Your graphs will break otherwise. ** + +### Initialise the database ### + +Initiate the follow database with the following command: + + php build-base.php + +### Create admin user ### + +Create the admin user - priv should be 10 + + php adduser.php 10 + +Substitute your desired username, password and email address--and leave the angled brackets off. + +### Validate your install ### + +Run validate.php as root in the librenms directory + + php validate.php + +This will check your install to verify it is set up correctly. + +### Add localhost ### + + php addhost.php localhost public v2c + +This assumes you haven't made community changes--if you have, replace `public` with your community. It also assumes SNMP v2c. If you're using v3, there are additional steps (NOTE: instructions for SNMPv3 to come). + +Discover localhost:: + + php discovery.php -h all + +### Create cronjob ### + +LibreNMS uses Job Snijders' [poller-wrapper.py][1]. By default, the cron job runs `poller-wrapper.py` with 16 threads. The current recommendation is to use 4 threads per core as a rule of thumb. If the thread count needs to be changed, you can do so by editing the cron file (`/etc/cron.d/librenms`). Just add a number after `poller-wrapper.py`, as in the example below: + + /opt/librenms/poller-wrapper.py 12 >> /dev/null 2>&1 + +Create the cronjob + + cp librenms.nonroot.cron /etc/cron.d/librenms + +### Daily Updates ### + +LibreNMS performs daily updates by default. At 00:15 system time every day, a `git pull --no-edit --quiet` is performed. You can override this default by editing your `config.php` file. Remove the comment (the `#` mark) on the line: + + #$config['update'] = 0; + +so that it looks like this: + + $config['update'] = 0; + +### Install complete ### + +Please allow for 2-3 runs of the poller-wrapper for data to start appearing in the WebUI. +If you don't see data after this, please refer to the [FAQ](http://docs.librenms.org/Support/FAQ/) for assistance. + +That's it! You now should be able to log in to http://librenms.example.com/. Please note that we have not covered HTTPS setup in this example, so your LibreNMS install is not secure by default. Please do not expose it to the public Internet unless you have configured HTTPS and taken appropriate web server hardening steps. + +It would be great if you would consider opting into the stats system we have, please see [this page](http://docs.librenms.org/General/Callback-Stats-and-Privacy/) on what it is and how to enable it. + +[1]: https://github.com/Atrato/observium-poller-wrapper +[2]: http://git-scm.com/book +[3]: http://gitready.com/ From 4430658932762ead3661231492b8074f144ea4b6 Mon Sep 17 00:00:00 2001 From: Dave Bell Date: Tue, 29 Mar 2016 23:05:25 +0100 Subject: [PATCH 4/4] Squashed 'lib/influxdb-php/' changes from 9698292..5facc95 5facc95 Merge pull request #42 from tuupola/no-dev-master b52d2cb Do not courage installing dev-master 0b88830 Merge branch 'release/1.3.0' ce4a39b Changelog 1.3.0 22ff83e Merge pull request #38 from influxdata/feature/travis 0448a6d Fixed order contributors 1934531 Travis container-infra and php 7 42ba3a0 Merge pull request #37 from influxdata/feature/fix-testsuite 18f1c3a Fixed wrong orderby tests 9dc71c0 Merge pull request #35 from skazi0/order-by f20ec57 Merge pull request #36 from skazi0/reserved-dbnames-fix 2232e47 Updated dependencies, added testscript 110ae56 added explicit testing of reserved keywords quoting fa4b7e7 added test a8cffeb - fixed tests - added more name quoting 223dc0f Update README.md 8375b67 added orderBy to query builder 07ce381 added quoting of dbname in queries a2d6301 Fixed issue with listUsers, added getColumns and added unit tests 430d3e2 Fixed issue with intval function on 32bit php ac12b98 Updated travis/codeclimate links after org rename to influxdata 5b2b53f Added support for 32 bit systems and added setters/getters for Point fields 5da903f Bumped version to 1.1.3; added support for symfony 3 9109fa2 Merge pull request #32 from sc0rp10/master 2fc58a6 change symfony/event-dispatcher version to 2.*|3.* b1201e9 Merge pull request #27 from mermetbt/master 351c1a1 Leave booleans lowercase. 44b1fbc Merge pull request #26 from andig/fix-24 20bd2e7 Fix check timestamp format and add unit tests. 5ddab42 The Guzzle driver now throw an exception when the response has an invalid return code. 7c0f681 Fix multi-point encoding on windows 82b7540 Merge pull request #20 from iGusev/patch-2 2f83ba0 Improved test coverage cb45f82 Modified readme for 1.1.2 99cf644 Merge pull request #23 from influxdb/ISSUE-21 b0ee796 ISSUE-21 - Fixed issue with authentication not working for write operations a0d4215 Added groupBy method to QueryBuilder b3cfd74 Added support for 0.9.4; added getlastQuery; added database if not exists support 22fda13 Merge pull request #19 from iGusev/patch-1 e96bb6d Code formatting 03b4cea Merge pull request #18 from cnelissen/escape-invalid-characters 6bb8e25 Merge pull request #17 from cnelissen/write-falsey-values dddf3c7 Escape invalid characters 7e23035 Allow falsey value 29b13ed Changed version from 1.0.2 to 1.1.0 87e284c Updated changelog df5d181 Added support for 0.9.3rc2 4f3f99c Merge pull request #16 from erwinw/patch-1 b1eb0b2 Update README.md git-subtree-dir: lib/influxdb-php git-subtree-split: 5facc95ef61ae66b365e878f69a9e8185693da89 --- .gitignore | 6 +- .travis.yml | 6 +- README.md | 352 ++--- Vagrantfile | 43 + ansible/main.yml | 10 + ansible/requirements.txt | 1 + composer.json | 9 +- composer.lock | 1163 +++++++++++++++-- requirements.sh | 14 + src/InfluxDB/Client.php | 54 +- src/InfluxDB/Database.php | 37 +- src/InfluxDB/Driver/DriverInterface.php | 5 + src/InfluxDB/Driver/Guzzle.php | 24 +- src/InfluxDB/Driver/UDP.php | 8 + src/InfluxDB/Point.php | 150 ++- src/InfluxDB/Query/Builder.php | 32 +- src/InfluxDB/ResultSet.php | 8 + test_library.php | 68 + tests/unit/AbstractTest.php | 26 +- tests/unit/AdminTest.php | 2 +- tests/unit/ClientTest.php | 108 +- tests/unit/DatabaseTest.php | 141 +- tests/unit/PointTest.php | 127 +- tests/unit/ResultSetTest.php | 9 +- tests/unit/json/databases.example.json | 25 + tests/unit/{ => json}/input.example.json | 0 .../{ => json}/result-no-tags.example.json | 0 .../{ => json}/result-test-users.example.json | 0 tests/unit/{ => json}/result.example.json | 0 tests/unit/json/users.example.json | 14 + 30 files changed, 2093 insertions(+), 349 deletions(-) create mode 100644 Vagrantfile create mode 100644 ansible/main.yml create mode 100644 ansible/requirements.txt create mode 100755 requirements.sh create mode 100644 test_library.php create mode 100644 tests/unit/json/databases.example.json rename tests/unit/{ => json}/input.example.json (100%) rename tests/unit/{ => json}/result-no-tags.example.json (100%) rename tests/unit/{ => json}/result-test-users.example.json (100%) rename tests/unit/{ => json}/result.example.json (100%) create mode 100644 tests/unit/json/users.example.json diff --git a/.gitignore b/.gitignore index 2826a22d79..fe65e120ee 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ -# Created by .gitignore support plugin (hsz.mobi) - +.vagrant +.DS_Store vendor/ .idea build/ -test.php \ No newline at end of file +test.php diff --git a/.travis.yml b/.travis.yml index c71c55677b..c0865e17ee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,18 @@ language: php +sudo: false + php: - 5.5 - 5.6 + - 7.0 install: + - composer selfupdate - composer install addons: code_climate: - repo_token: 5371d86d298d66eb9007cc8de462d7063e58f6dd85e430928834736edee479a9 + repo_token: 303a289e61e8e11d8bcae115860c4fffc6e1e7fe2d504d20a773e69bd7641284 after_script: - vendor/bin/test-reporter diff --git a/README.md b/README.md index 06f60943af..e1b81e6754 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,23 @@ # influxdb-php ## InfluxDB client library for PHP -[![Build Status](https://travis-ci.org/influxdb/influxdb-php.svg?branch=master)](https://travis-ci.org/influxdb/influxdb-php) -[![Code Climate](https://codeclimate.com/github/influxdb/influxdb-php/badges/gpa.svg)](https://codeclimate.com/github/influxdb/influxdb-php) -[![Test Coverage](https://codeclimate.com/github/influxdb/influxdb-php/badges/coverage.svg)](https://codeclimate.com/github/influxdb/influxdb-php/coverage) +[![Build Status](https://travis-ci.org/influxdata/influxdb-php.svg?branch=master)](https://travis-ci.org/influxdata/influxdb-php) +[![Code Climate](https://codeclimate.com/github/influxdata/influxdb-php/badges/gpa.svg)](https://codeclimate.com/github/influxdata/influxdb-php) +[![Test Coverage](https://codeclimate.com/github/influxdata/influxdb-php/badges/coverage.svg)](https://codeclimate.com/github/influxdata/influxdb-php/coverage) ### Overview -A easy to use library for using InfluxDB with PHP. +A easy to use library for using InfluxDB with PHP. Maintained by [@thecodeassassin](https://github.com/thecodeassassin), [@gianarb](https://github.com/gianarb). -The influxdb-php library was created to have php port of the python influxdb client. +The influxdb-php library was created to have php port of the python influxdb client. This way there will be a common abstraction library between different programming languages. ### Installation Installation can be done with composer: -composer require influxdb/influxdb-php:dev-master +``` bash +$ composer require influxdb/influxdb-php +``` ### NOTE for PHP 5.3 and PHP 5.4 users @@ -27,10 +29,7 @@ The 0.1.x branch will work on PHP 5.3 and PHP 5.4 but doesn't contain all the fe Initialize a new client object: ```php - $client = new InfluxDB\Client($host, $port); - - ``` This will create a new client object which you can use to read and write points to InfluxDB. @@ -38,13 +37,11 @@ This will create a new client object which you can use to read and write points It's also possible to create a client from a DSN (Data Source Name): ```php - - // directly get the database object - $database = InfluxDB\Client::fromDSN(sprintf('influxdb://user:pass@%s:%s/%s', $host, $port, $dbname)); - - // get the client to retrieve other databases - $client = $database->getClient(); - +// directly get the database object +$database = InfluxDB\Client::fromDSN(sprintf('influxdb://user:pass@%s:%s/%s', $host, $port, $dbname)); + +// get the client to retrieve other databases +$client = $database->getClient(); ``` ### Reading @@ -52,37 +49,45 @@ It's also possible to create a client from a DSN (Data Source Name): To fetch records from InfluxDB you can do a query directly on a database: ```php - - // fetch the database - $database = $client->selectDB('influx_test_db'); - - // executing a query will yield a resultset object - $result = $database->query('select * from test_metric LIMIT 5'); - - // get the points from the resultset yields an array - $points = $result->getPoints(); - +// fetch the database +$database = $client->selectDB('influx_test_db'); + +// executing a query will yield a resultset object +$result = $database->query('select * from test_metric LIMIT 5'); + +// get the points from the resultset yields an array +$points = $result->getPoints(); ``` It's also possible to use the QueryBuilder object. This is a class that simplifies the process of building queries. ```php +// retrieve points with the query builder +$result = $database->getQueryBuilder() + ->select('cpucount') + ->from('test_metric') + ->limit(2) + ->getResultSet() + ->getPoints(); - // retrieve points with the query builder - $result = $database->getQueryBuilder() - ->select('cpucount') - ->from('test_metric') - ->limit(2) - ->getResultSet() - ->getPoints(); - - - // get the query from the QueryBuilder - $query = $database->getQueryBuilder() - ->select('cpucount') - ->from('test_metric') - ->getQuery(); - +// get the query from the QueryBuilder +$query = $database->getQueryBuilder() + ->select('cpucount') + ->from('test_metric') + ->where(["region = 'us-west'"]) + ->getQuery(); +``` + +Make sure that you enter single quotes when doing a where query on strings; otherwise InfluxDB will return an empty result. + +You can get the last executed query from the client: + +```php +// use the getLastQuery() method +$lastQuery = $client->getLastQuery(); + +// or access the static variable directly: +$lastQuery = Client::lastQuery; ``` ### Writing data @@ -90,28 +95,26 @@ It's also possible to use the QueryBuilder object. This is a class that simplifi Writing data is done by providing an array of points to the writePoints method on a database: ```php +// create an array of points +$points = array( + new Point( + 'test_metric', // name of the measurement + 0.64, // the measurement value + ['host' => 'server01', 'region' => 'us-west'], // optional tags + ['cpucount' => 10], // optional additional fields + 1435255849 // Time precision has to be set to seconds! + ), + new Point( + 'test_metric', // name of the measurement + 0.84, // the measurement value + ['host' => 'server01', 'region' => 'us-west'], // optional tags + ['cpucount' => 10], // optional additional fields + 1435255849 // Time precision has to be set to seconds! + ) +); - // create an array of points - $points = array( - new Point( - 'test_metric', // name of the measurement - 0.64, // the measurement value - ['host' => 'server01', 'region' => 'us-west'], // optional tags - ['cpucount' => 10], // optional additional fields - 1435255849 // Time precision has to be set to seconds! - ), - new Point( - 'test_metric', // name of the measurement - 0.84, // the measurement value - ['host' => 'server01', 'region' => 'us-west'], // optional tags - ['cpucount' => 10], // optional additional fields - 1435255849 // Time precision has to be set to seconds! - ) - ); - - // we are writing unix timestamps, which have a second precision - $result = $database->writePoints($points, Database::PRECISION_SECONDS); - +// we are writing unix timestamps, which have a second precision +$result = $database->writePoints($points, Database::PRECISION_SECONDS); ``` It's possible to add multiple [fields](https://influxdb.com/docs/v0.9/concepts/key_concepts.html) when writing @@ -123,22 +126,22 @@ InfluxDB takes the current time as the default timestamp. You can also write multiple fields to a measurement without specifying a value: ```php - $points = [ - new Point( - 'instance', // the name of the measurement - null, // measurement value - ['host' => 'server01', 'region' => 'us-west'], // measurement tags - ['cpucount' => 10, 'free' => 1], // measurement fields - exec('date +%s%N') // timestamp in nanoseconds - ), - new Point( - 'instance', // the name of the measurement - null, // measurement value - ['host' => 'server01', 'region' => 'us-west'], // measurement tags - ['cpucount' => 10, 'free' => 2], // measurement fields - exec('date +%s%N') // timestamp in nanoseconds - ) - ]; +$points = [ + new Point( + 'instance', // the name of the measurement + null, // measurement value + ['host' => 'server01', 'region' => 'us-west'], // measurement tags + ['cpucount' => 10, 'free' => 1], // measurement fields + exec('date +%s%N') // timestamp in nanoseconds on Linux ONLY + ), + new Point( + 'instance', // the name of the measurement + null, // measurement value + ['host' => 'server01', 'region' => 'us-west'], // measurement tags + ['cpucount' => 10, 'free' => 2], // measurement fields + exec('date +%s%N') // timestamp in nanoseconds on Linux ONLY + ) +]; ``` @@ -150,39 +153,37 @@ First, set your InfluxDB host to support incoming UDP sockets: [udp] enabled = true bind-address = ":4444" - database = "test_db" + database = "test_db" ``` Then, configure the UDP driver in the client: ```php - - // set the UDP driver in the client - $client->setDriver(new \InfluxDB\Driver\UDP($client->getHost(), 4444)); - - $points = [ - new Point( - 'test_metric', - 0.84, - ['host' => 'server01', 'region' => 'us-west'], - ['cpucount' => 10], - exec('date +%s%N') // this will produce a nanosecond timestamp in Linux operating systems - ) - ]; - - // now just write your points like you normally would - $result = $database->writePoints($points); +// set the UDP driver in the client +$client->setDriver(new \InfluxDB\Driver\UDP($client->getHost(), 4444)); + +$points = [ + new Point( + 'test_metric', + 0.84, + ['host' => 'server01', 'region' => 'us-west'], + ['cpucount' => 10], + exec('date +%s%N') // this will produce a nanosecond timestamp on Linux ONLY + ) +]; + +// now just write your points like you normally would +$result = $database->writePoints($points); ``` Or simply use a DSN (Data Source Name) to send metrics using UDP: ```php +// get a database object using a DSN (Data Source Name) +$database = \InfluxDB\Client::fromDSN('udp+influxdb://username:pass@localhost:4444/test123'); - // get a database object using a DSN (Data Source Name) - $database = \InfluxDB\Client::fromDSN('udp+influxdb://username:pass@localhost:4444/test123'); - - // write your points - $result = $database->writePoints($points); +// write your points +$result = $database->writePoints($points); ``` *Note:* It is import to note that precision will be *ignored* when you use UDP. You should always use nanosecond @@ -194,59 +195,64 @@ It's important to provide the correct precision when adding a timestamp to a Poi if you specify a timestamp in seconds and the default (nanosecond) precision is set; the entered timestamp will be invalid. ```php - // Points will require a nanosecond precision (this is default as per influxdb standard) - $newPoints = $database->writePoints($points); +// Points will require a nanosecond precision (this is default as per influxdb standard) +$newPoints = $database->writePoints($points); - // Points will require second precision - $newPoints = $database->writePoints($points, Database::PRECISION_SECONDS); - - // Points will require microsecond precision - $newPoints = $database->writePoints($points, Database::PRECISION_MICROSECONDS); +// Points will require second precision +$newPoints = $database->writePoints($points, Database::PRECISION_SECONDS); + +// Points will require microsecond precision +$newPoints = $database->writePoints($points, Database::PRECISION_MICROSECONDS); +``` + +Please note that `exec('date + %s%N')` does NOT work under MacOS; you can use PHP's `microtime` to get a timestamp with microsecond precision, like such: + +```php +list($usec, $sec) = explode(' ', microtime()); +$timestamp = sprintf('%d%06d', $sec, $usec*1000000); ``` ### Creating databases When creating a database a default retention policy is added. This retention policy does not have a duration -so the data will be flushed with the memory. +so the data will be flushed with the memory. This library makes it easy to provide a retention policy when creating a database: ```php - - // create the client - $client = new \InfluxDB\Client($host, $port, '', ''); +// create the client +$client = new \InfluxDB\Client($host, $port, '', ''); - // select the database - $database = $client->selectDB('influx_test_db'); +// select the database +$database = $client->selectDB('influx_test_db'); - // create the database with a retention policy - $result = $database->create(new RetentionPolicy('test', '5d', 1, true)); - - // check if a database exists then create it if it doesn't - $database = $client->selectDB('test_db'); - - if (!$database->exists()) { - $database->create(new RetentionPolicy('test', '1d', 2, true)); - } - +// create the database with a retention policy +$result = $database->create(new RetentionPolicy('test', '5d', 1, true)); + +// check if a database exists then create it if it doesn't +$database = $client->selectDB('test_db'); + +if (!$database->exists()) { + $database->create(new RetentionPolicy('test', '1d', 2, true)); +} ``` You can also alter retention policies: ```php - $database->alterRetentionPolicy(new RetentionPolicy('test', '2d', 5, true)); +$database->alterRetentionPolicy(new RetentionPolicy('test', '2d', 5, true)); ``` and list them: ```php - $result = $database->listRetentionPolicies(); +$result = $database->listRetentionPolicies(); ``` You can add more retention policies to a database: ```php - $result = $database->createRetentionPolicy(new RetentionPolicy('test2', '30d', 1, true)); +$result = $database->createRetentionPolicy(new RetentionPolicy('test2', '30d', 1, true)); ``` ### Client functions @@ -254,12 +260,11 @@ You can add more retention policies to a database: Some functions are too general for a database. So these are available in the client: ```php +// list users +$result = $client->listUsers(); - // list users - $result = $client->listUsers(); - - // list databases - $result = $client->listDatabases(); +// list databases +$result = $client->listDatabases(); ``` ### Admin functionality @@ -267,49 +272,47 @@ Some functions are too general for a database. So these are available in the cli You can use the client's $client->admin functionality to administer InfluxDB via the API. ```php - // add a new user without privileges - $client->admin->createUser('testuser123', 'testpassword'); +// add a new user without privileges +$client->admin->createUser('testuser123', 'testpassword'); - // add a new user with ALL cluster-wide privileges - $client->admin->createUser('admin_user', 'password', \InfluxDB\Client\Admin::PRIVILEGE_ALL); - - // drop user testuser123 - $client->admin->dropUser('testuser123'); +// add a new user with ALL cluster-wide privileges +$client->admin->createUser('admin_user', 'password', \InfluxDB\Client\Admin::PRIVILEGE_ALL); + +// drop user testuser123 +$client->admin->dropUser('testuser123'); ``` List all the users: ```php - // show a list of all users - $results = $client->admin->showUsers(); - - // show users returns a ResultSet object - $users = $results->getPoints(); +// show a list of all users +$results = $client->admin->showUsers(); + +// show users returns a ResultSet object +$users = $results->getPoints(); ``` #### Granting and revoking privileges -Granting permissions can be done on both the database level and cluster-wide. +Granting permissions can be done on both the database level and cluster-wide. To grant a user specific privileges on a database, provide a database object or a database name. ```php +// grant permissions using a database object +$database = $client->selectDB('test_db'); +$client->admin->grant(\InfluxDB\Client\Admin::PRIVILEGE_READ, 'testuser123', $database); - // grant permissions using a database object - $database = $client->selectDB('test_db'); - $client->admin->grant(\InfluxDB\Client\Admin::PRIVILEGE_READ, 'testuser123', $database); +// give user testuser123 read privileges on database test_db +$client->admin->grant(\InfluxDB\Client\Admin::PRIVILEGE_READ, 'testuser123', 'test_db'); - // give user testuser123 read privileges on database test_db - $client->admin->grant(\InfluxDB\Client\Admin::PRIVILEGE_READ, 'testuser123', 'test_db'); - - // revoke user testuser123's read privileges on database test_db - $client->admin->revoke(\InfluxDB\Client\Admin::PRIVILEGE_READ, 'testuser123', 'test_db'); +// revoke user testuser123's read privileges on database test_db +$client->admin->revoke(\InfluxDB\Client\Admin::PRIVILEGE_READ, 'testuser123', 'test_db'); - // grant a user cluster-wide privileges - $client->admin->grant(\InfluxDB\Client\Admin::PRIVILEGE_READ, 'testuser123'); - - // Revoke an admin's cluster-wide privileges - $client->admin->revoke(\InfluxDB\Client\Admin::PRIVILEGE_ALL, 'admin_user'); - +// grant a user cluster-wide privileges +$client->admin->grant(\InfluxDB\Client\Admin::PRIVILEGE_READ, 'testuser123'); + +// Revoke an admin's cluster-wide privileges +$client->admin->revoke(\InfluxDB\Client\Admin::PRIVILEGE_ALL, 'admin_user'); ``` ## Todo @@ -322,7 +325,38 @@ To grant a user specific privileges on a database, provide a database object or ## Changelog -####1.0.1 +###1.3.0 +* Added quoting of dbname in queries +* Added orderBy to query builder +* Fixed wrong orderby tests +* Travis container-infra and php 7 + +###1.2.2 +* Fixed issue with listUsers() method +* Added more unit tests +* Added getColumns method to \InfluxDB\ResultSet + +###1.2.0 +* Added support for 32 bit systems +* Added setters/getters for Point fields + +###1.1.3 +* Added support for symfony3 + +###1.1.2 +* Fixed issue with authentication when writing data + +###1.1.1 +* Added support for 0.9.4 +* Added if not exists support to database->create() +* Added getLastQuery method + +###1.1.0 +* Added support for 0.9.3 rc2 +* Changed the way we handle the datatypes of values +* Changed list retention policies to reflect the changes in 0.9.3 + +####1.0.1 * Added support for authentication in the guzzle driver * Added admin functionality diff --git a/Vagrantfile b/Vagrantfile new file mode 100644 index 0000000000..93995aeefd --- /dev/null +++ b/Vagrantfile @@ -0,0 +1,43 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# All Vagrant configuration is done below. The "2" in Vagrant.configure +# configures the configuration version (we support older styles for +# backwards compatibility). Please don't change it unless you know what +# you're doing. +#system(" +# if [ #{ARGV[0]} = 'up' ]; then +# ./requirements.sh +# fi +#") + + +Vagrant.configure(2) do |config| + + # The most common configuration options are documented and commented below. + # For a complete reference, please see the online documentation at + # https://docs.vagrantup.com. + + # Every Vagrant development environment requires a box. You can search for + # boxes at https://atlas.hashicorp.com/search. + config.vm.box = "ubuntu/trusty64" + + config.vm.define :node1 do |node1| + # + # Networking + # + node1.vm.network 'private_network', ip: '192.168.33.10' + # + # VM Setup + # + # Set the hostname to something useful + node1.vm.hostname = 'influxdb-node1' + node1.vm.define :influxdb_node1, {} + + node1.vm.provision 'ansible' do |ansible| + ansible.playbook = 'ansible/main.yml' + ansible.tags = ENV['ANSIBLE_TAGS'] unless ENV['ANSIBLE_TAGS'].to_s.empty? + ansible.sudo = true + end + end +end diff --git a/ansible/main.yml b/ansible/main.yml new file mode 100644 index 0000000000..912260e633 --- /dev/null +++ b/ansible/main.yml @@ -0,0 +1,10 @@ +--- +- hosts: all + sudo: yes + + vars: + influxdb_udp_enabled: "true" + influxdb_udp_bind_address: "0.0.0.0:8090" + + roles: + - { role: mtchavez.influxdb } diff --git a/ansible/requirements.txt b/ansible/requirements.txt new file mode 100644 index 0000000000..49f8dff75d --- /dev/null +++ b/ansible/requirements.txt @@ -0,0 +1 @@ +mtchavez.influxdb diff --git a/composer.json b/composer.json index 7af0fafe8f..1164d72faf 100644 --- a/composer.json +++ b/composer.json @@ -19,14 +19,19 @@ { "name": "Daniel Martinez", "email": "danimartcas@hotmail.com" + }, + { + "name": "Gianluca Arbezzano", + "email": "gianarb92@gmail.com" } ], "require": { - "php": ">=5.5", + "php": "^5.5 | ^7.0", "guzzlehttp/guzzle": "6.*", - "symfony/event-dispatcher": "2.*" + "symfony/event-dispatcher": "2.*|3.*" }, "require-dev": { + "phpunit/phpunit": "^4.0", "codeclimate/php-test-reporter": "0.*", "symfony/config": "~2.8", "symfony/console": "~2.8", diff --git a/composer.lock b/composer.lock index a2fb5ba284..7186e8f19b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "ee3f2e2a4eb50f8de36c6efbff37fe71", + "hash": "b8e1dc9e3375bc55cfa2bc06647e5453", + "content-hash": "62f314d125890d134f461ba0bed7c33e", "packages": [ { "name": "guzzlehttp/guzzle", @@ -12,12 +13,12 @@ "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "1879fbe853b0c64d109e369c7aeff09849e62d1e" + "reference": "8c033e192fdcddb3067ca77e0b6829e910ecdd7d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/1879fbe853b0c64d109e369c7aeff09849e62d1e", - "reference": "1879fbe853b0c64d109e369c7aeff09849e62d1e", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/8c033e192fdcddb3067ca77e0b6829e910ecdd7d", + "reference": "8c033e192fdcddb3067ca77e0b6829e910ecdd7d", "shasum": "" }, "require": { @@ -33,7 +34,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "6.0-dev" + "dev-master": "6.2-dev" } }, "autoload": { @@ -66,7 +67,7 @@ "rest", "web service" ], - "time": "2015-07-10 20:04:21" + "time": "2016-01-31 00:19:49" }, { "name": "guzzlehttp/promises", @@ -74,12 +75,12 @@ "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "f596be052ef429a16b2f640812fcf84392dd38f7" + "reference": "b59321a17f3e3ebc14f2bbbf73b4d065a88cf3f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/f596be052ef429a16b2f640812fcf84392dd38f7", - "reference": "f596be052ef429a16b2f640812fcf84392dd38f7", + "url": "https://api.github.com/repos/guzzle/promises/zipball/b59321a17f3e3ebc14f2bbbf73b4d065a88cf3f6", + "reference": "b59321a17f3e3ebc14f2bbbf73b4d065a88cf3f6", "shasum": "" }, "require": { @@ -117,20 +118,20 @@ "keywords": [ "promise" ], - "time": "2015-06-30 16:39:54" + "time": "2016-02-10 14:20:03" }, { "name": "guzzlehttp/psr7", - "version": "1.1.0", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "af0e1758de355eb113917ad79c3c0e3604bce4bd" + "reference": "f5d04bdd2881ac89abde1fb78cc234bce24327bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/af0e1758de355eb113917ad79c3c0e3604bce4bd", - "reference": "af0e1758de355eb113917ad79c3c0e3604bce4bd", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5d04bdd2881ac89abde1fb78cc234bce24327bb", + "reference": "f5d04bdd2881ac89abde1fb78cc234bce24327bb", "shasum": "" }, "require": { @@ -154,7 +155,7 @@ "GuzzleHttp\\Psr7\\": "src/" }, "files": [ - "src/functions.php" + "src/functions_include.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -175,7 +176,7 @@ "stream", "uri" ], - "time": "2015-06-24 19:55:15" + "time": "2016-01-23 01:23:02" }, { "name": "psr/http-message", @@ -228,28 +229,27 @@ }, { "name": "symfony/event-dispatcher", - "version": "2.8.x-dev", + "version": "dev-master", "source": { "type": "git", - "url": "https://github.com/symfony/EventDispatcher.git", - "reference": "d7246885b7fe4cb5a2786bda34362d2f0e40b730" + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "9d7930726e060b0c45ae10022a4cb307e502500b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/d7246885b7fe4cb5a2786bda34362d2f0e40b730", - "reference": "d7246885b7fe4cb5a2786bda34362d2f0e40b730", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9d7930726e060b0c45ae10022a4cb307e502500b", + "reference": "9d7930726e060b0c45ae10022a4cb307e502500b", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": ">=5.5.9" }, "require-dev": { "psr/log": "~1.0", - "symfony/config": "~2.0,>=2.0.5|~3.0.0", - "symfony/dependency-injection": "~2.6|~3.0.0", - "symfony/expression-language": "~2.6|~3.0.0", - "symfony/phpunit-bridge": "~2.7|~3.0.0", - "symfony/stopwatch": "~2.3|~3.0.0" + "symfony/config": "~2.8|~3.0", + "symfony/dependency-injection": "~2.8|~3.0", + "symfony/expression-language": "~2.8|~3.0", + "symfony/stopwatch": "~2.8|~3.0" }, "suggest": { "symfony/dependency-injection": "", @@ -258,13 +258,16 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev" + "dev-master": "3.1-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\EventDispatcher\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -282,7 +285,7 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2015-06-24 15:32:32" + "time": "2016-02-04 12:57:09" } ], "packages-dev": [ @@ -292,12 +295,12 @@ "source": { "type": "git", "url": "https://github.com/codeclimate/php-test-reporter.git", - "reference": "418ae782307841ac50fe26daa4cfe04520b0de9c" + "reference": "02790d733f9eb43d0a5981962c41846092fb387f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/codeclimate/php-test-reporter/zipball/418ae782307841ac50fe26daa4cfe04520b0de9c", - "reference": "418ae782307841ac50fe26daa4cfe04520b0de9c", + "url": "https://api.github.com/repos/codeclimate/php-test-reporter/zipball/02790d733f9eb43d0a5981962c41846092fb387f", + "reference": "02790d733f9eb43d0a5981962c41846092fb387f", "shasum": "" }, "require": { @@ -316,7 +319,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "0.1.x-dev" + "dev-master": "0.2.x-dev" } }, "autoload": { @@ -342,26 +345,80 @@ "codeclimate", "coverage" ], - "time": "2015-04-18 14:43:54" + "time": "2015-12-17 00:52:54" }, { - "name": "guzzle/guzzle", + "name": "doctrine/instantiator", "version": "dev-master", "source": { "type": "git", - "url": "https://github.com/guzzle/guzzle3.git", - "reference": "b3f5050cb6270c7a728a0b74ac2de50a262b3e02" + "url": "https://github.com/doctrine/instantiator.git", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle3/zipball/b3f5050cb6270c7a728a0b74ac2de50a262b3e02", - "reference": "b3f5050cb6270c7a728a0b74ac2de50a262b3e02", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "shasum": "" + }, + "require": { + "php": ">=5.3,<8.0-DEV" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2015-06-14 21:17:01" + }, + { + "name": "guzzle/guzzle", + "version": "v3.8.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "4de0618a01b34aa1c8c33a3f13f396dcd3882eba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/4de0618a01b34aa1c8c33a3f13f396dcd3882eba", + "reference": "4de0618a01b34aa1c8c33a3f13f396dcd3882eba", "shasum": "" }, "require": { "ext-curl": "*", "php": ">=5.3.3", - "symfony/event-dispatcher": "~2.1" + "symfony/event-dispatcher": ">=2.1" }, "replace": { "guzzle/batch": "self.version", @@ -388,21 +445,18 @@ "guzzle/stream": "self.version" }, "require-dev": { - "doctrine/cache": "~1.3", - "monolog/monolog": "~1.0", + "doctrine/cache": "*", + "monolog/monolog": "1.*", "phpunit/phpunit": "3.7.*", - "psr/log": "~1.0", - "symfony/class-loader": "~2.1", - "zendframework/zend-cache": "2.*,<2.3", - "zendframework/zend-log": "2.*,<2.3" - }, - "suggest": { - "guzzlehttp/guzzle": "Guzzle 5 has moved to a new package name. The package you have installed, Guzzle 3, is deprecated." + "psr/log": "1.0.*", + "symfony/class-loader": "*", + "zendframework/zend-cache": "<2.3", + "zendframework/zend-log": "<2.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.9-dev" + "dev-master": "3.8-dev" } }, "autoload": { @@ -426,7 +480,7 @@ "homepage": "https://github.com/guzzle/guzzle/contributors" } ], - "description": "PHP HTTP client. This library is deprecated in favor of https://packagist.org/packages/guzzlehttp/guzzle", + "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", "homepage": "http://guzzlephp.org/", "keywords": [ "client", @@ -437,7 +491,486 @@ "rest", "web service" ], - "time": "2015-04-29 17:06:53" + "time": "2014-01-28 22:29:15" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8", + "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "dflydev/markdown": "~1.0", + "erusev/parsedown": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "phpDocumentor": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "mike.vanriel@naenius.com" + } + ], + "time": "2015-02-03 12:10:50" + }, + { + "name": "phpspec/prophecy", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "e55e3e32a870bd4f05425fa4f717b52bd40e5659" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/e55e3e32a870bd4f05425fa4f717b52bd40e5659", + "reference": "e55e3e32a870bd4f05425fa4f717b52bd40e5659", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": "^5.3|^7.0", + "phpdocumentor/reflection-docblock": "~2.0", + "sebastian/comparator": "~1.1", + "sebastian/recursion-context": "~1.0" + }, + "require-dev": { + "phpspec/phpspec": "~2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.5.x-dev" + } + }, + "autoload": { + "psr-0": { + "Prophecy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "time": "2015-12-28 13:26:33" + }, + { + "name": "phpunit/php-code-coverage", + "version": "2.2.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979", + "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-file-iterator": "~1.3", + "phpunit/php-text-template": "~1.2", + "phpunit/php-token-stream": "~1.3", + "sebastian/environment": "^1.3.2", + "sebastian/version": "~1.0" + }, + "require-dev": { + "ext-xdebug": ">=2.1.4", + "phpunit/phpunit": "~4" + }, + "suggest": { + "ext-dom": "*", + "ext-xdebug": ">=2.2.1", + "ext-xmlwriter": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2015-10-06 15:47:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0", + "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2015-06-21 13:08:43" + }, + { + "name": "phpunit/php-text-template", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2015-06-21 13:50:34" + }, + { + "name": "phpunit/php-timer", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3e82f4e9fc92665fafd9157568e4dcb01d014e5b", + "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2015-06-21 08:01:12" + }, + { + "name": "phpunit/php-token-stream", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "cab6c6fefee93d7b7c3a01292a0fe0884ea66644" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/cab6c6fefee93d7b7c3a01292a0fe0884ea66644", + "reference": "cab6c6fefee93d7b7c3a01292a0fe0884ea66644", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2015-09-23 14:46:55" + }, + { + "name": "phpunit/phpunit", + "version": "4.8.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "6e351261f9cd33daf205a131a1ba61c6d33bd483" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6e351261f9cd33daf205a131a1ba61c6d33bd483", + "reference": "6e351261f9cd33daf205a131a1ba61c6d33bd483", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=5.3.3", + "phpspec/prophecy": "^1.3.1", + "phpunit/php-code-coverage": "~2.1", + "phpunit/php-file-iterator": "~1.4", + "phpunit/php-text-template": "~1.2", + "phpunit/php-timer": ">=1.0.6", + "phpunit/phpunit-mock-objects": "~2.3", + "sebastian/comparator": "~1.1", + "sebastian/diff": "~1.2", + "sebastian/environment": "~1.3", + "sebastian/exporter": "~1.2", + "sebastian/global-state": "~1.0", + "sebastian/version": "~1.0", + "symfony/yaml": "~2.1|~3.0" + }, + "suggest": { + "phpunit/php-invoker": "~1.1" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.8.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2016-02-11 14:56:33" + }, + { + "name": "phpunit/phpunit-mock-objects", + "version": "2.3.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983", + "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": ">=5.3.3", + "phpunit/php-text-template": "~1.2", + "sebastian/exporter": "~1.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "suggest": { + "ext-soap": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Mock Object library for PHPUnit", + "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", + "keywords": [ + "mock", + "xunit" + ], + "time": "2015-10-02 06:51:40" }, { "name": "psr/log", @@ -545,27 +1078,396 @@ ], "time": "2013-05-04 08:07:33" }, + { + "name": "sebastian/comparator", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "937efb279bd37a375bcadf584dec0726f84dbf22" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/937efb279bd37a375bcadf584dec0726f84dbf22", + "reference": "937efb279bd37a375bcadf584dec0726f84dbf22", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/diff": "~1.2", + "sebastian/exporter": "~1.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "http://www.github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2015-07-26 15:48:44" + }, + { + "name": "sebastian/diff", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e", + "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff" + ], + "time": "2015-12-08 07:14:41" + }, + { + "name": "sebastian/environment", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "6e7133793a8e5a5714a551a8324337374be209df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6e7133793a8e5a5714a551a8324337374be209df", + "reference": "6e7133793a8e5a5714a551a8324337374be209df", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "time": "2015-12-02 08:37:27" + }, + { + "name": "sebastian/exporter", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "f88f8936517d54ae6d589166810877fb2015d0a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/f88f8936517d54ae6d589166810877fb2015d0a2", + "reference": "f88f8936517d54ae6d589166810877fb2015d0a2", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/recursion-context": "~1.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "time": "2015-08-09 04:23:41" + }, + { + "name": "sebastian/global-state", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "time": "2015-10-12 03:26:01" + }, + { + "name": "sebastian/recursion-context", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "7ff5b1b3dcc55b8ab8ae61ef99d4730940856ee7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/7ff5b1b3dcc55b8ab8ae61ef99d4730940856ee7", + "reference": "7ff5b1b3dcc55b8ab8ae61ef99d4730940856ee7", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "time": "2016-01-28 05:39:29" + }, + { + "name": "sebastian/version", + "version": "1.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", + "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", + "shasum": "" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2015-06-21 13:59:46" + }, { "name": "symfony/config", "version": "2.8.x-dev", "source": { "type": "git", - "url": "https://github.com/symfony/Config.git", - "reference": "358ec929e494b6f12d8508d88357cbd7383a10ca" + "url": "https://github.com/symfony/config.git", + "reference": "f4d8e2236db888c708d934975070ff6f0a332a24" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Config/zipball/358ec929e494b6f12d8508d88357cbd7383a10ca", - "reference": "358ec929e494b6f12d8508d88357cbd7383a10ca", + "url": "https://api.github.com/repos/symfony/config/zipball/f4d8e2236db888c708d934975070ff6f0a332a24", + "reference": "f4d8e2236db888c708d934975070ff6f0a332a24", "shasum": "" }, "require": { "php": ">=5.3.9", "symfony/filesystem": "~2.3|~3.0.0" }, - "require-dev": { - "symfony/phpunit-bridge": "~2.7|~3.0.0" - }, "type": "library", "extra": { "branch-alias": { @@ -575,7 +1477,10 @@ "autoload": { "psr-4": { "Symfony\\Component\\Config\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -593,29 +1498,29 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2015-07-09 16:11:14" + "time": "2016-02-04 14:44:13" }, { "name": "symfony/console", "version": "2.8.x-dev", "source": { "type": "git", - "url": "https://github.com/symfony/Console.git", - "reference": "fd85e7517e79a2bceafcee8f7e8b7bbd0919a90a" + "url": "https://github.com/symfony/console.git", + "reference": "d59b01b03451dfc1497b9736120ac6bcb422ff10" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Console/zipball/fd85e7517e79a2bceafcee8f7e8b7bbd0919a90a", - "reference": "fd85e7517e79a2bceafcee8f7e8b7bbd0919a90a", + "url": "https://api.github.com/repos/symfony/console/zipball/d59b01b03451dfc1497b9736120ac6bcb422ff10", + "reference": "d59b01b03451dfc1497b9736120ac6bcb422ff10", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": ">=5.3.9", + "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { "psr/log": "~1.0", "symfony/event-dispatcher": "~2.1|~3.0.0", - "symfony/phpunit-bridge": "~2.7|~3.0.0", "symfony/process": "~2.1|~3.0.0" }, "suggest": { @@ -632,7 +1537,10 @@ "autoload": { "psr-4": { "Symfony\\Component\\Console\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -650,28 +1558,25 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2015-07-16 12:22:14" + "time": "2016-02-03 18:01:35" }, { "name": "symfony/filesystem", "version": "2.8.x-dev", "source": { "type": "git", - "url": "https://github.com/symfony/Filesystem.git", - "reference": "9f70c5625a32b2f1e6fc37222f52b4e0eb437b0e" + "url": "https://github.com/symfony/filesystem.git", + "reference": "cbfaa8a24c38911d2d14ef12118514a504c09dd0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Filesystem/zipball/9f70c5625a32b2f1e6fc37222f52b4e0eb437b0e", - "reference": "9f70c5625a32b2f1e6fc37222f52b4e0eb437b0e", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/cbfaa8a24c38911d2d14ef12118514a504c09dd0", + "reference": "cbfaa8a24c38911d2d14ef12118514a504c09dd0", "shasum": "" }, "require": { "php": ">=5.3.9" }, - "require-dev": { - "symfony/phpunit-bridge": "~2.7|~3.0.0" - }, "type": "library", "extra": { "branch-alias": { @@ -681,7 +1586,10 @@ "autoload": { "psr-4": { "Symfony\\Component\\Filesystem\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -699,28 +1607,84 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2015-07-09 16:11:14" + "time": "2016-01-27 11:34:40" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "1289d16209491b584839022f29257ad859b8532d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/1289d16209491b584839022f29257ad859b8532d", + "reference": "1289d16209491b584839022f29257ad859b8532d", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2016-01-20 09:13:37" }, { "name": "symfony/stopwatch", "version": "2.8.x-dev", "source": { "type": "git", - "url": "https://github.com/symfony/Stopwatch.git", - "reference": "cd5f0dc1d3d0e2c83461dad77e20a9186beb6146" + "url": "https://github.com/symfony/stopwatch.git", + "reference": "e3bc8e2a984f4382690a438c8bb650f3ffd71e73" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Stopwatch/zipball/cd5f0dc1d3d0e2c83461dad77e20a9186beb6146", - "reference": "cd5f0dc1d3d0e2c83461dad77e20a9186beb6146", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/e3bc8e2a984f4382690a438c8bb650f3ffd71e73", + "reference": "e3bc8e2a984f4382690a438c8bb650f3ffd71e73", "shasum": "" }, "require": { "php": ">=5.3.9" }, - "require-dev": { - "symfony/phpunit-bridge": "~2.7|~3.0.0" - }, "type": "library", "extra": { "branch-alias": { @@ -730,7 +1694,10 @@ "autoload": { "psr-4": { "Symfony\\Component\\Stopwatch\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -748,28 +1715,25 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2015-07-01 18:24:26" + "time": "2016-01-03 15:33:41" }, { "name": "symfony/yaml", "version": "2.8.x-dev", "source": { "type": "git", - "url": "https://github.com/symfony/Yaml.git", - "reference": "000e7fc2653335cd42c6d21405dac1c74224a387" + "url": "https://github.com/symfony/yaml.git", + "reference": "33fd1b2cfdde601704665fad343014bba04b0753" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Yaml/zipball/000e7fc2653335cd42c6d21405dac1c74224a387", - "reference": "000e7fc2653335cd42c6d21405dac1c74224a387", + "url": "https://api.github.com/repos/symfony/yaml/zipball/33fd1b2cfdde601704665fad343014bba04b0753", + "reference": "33fd1b2cfdde601704665fad343014bba04b0753", "shasum": "" }, "require": { "php": ">=5.3.9" }, - "require-dev": { - "symfony/phpunit-bridge": "~2.7|~3.0.0" - }, "type": "library", "extra": { "branch-alias": { @@ -779,7 +1743,10 @@ "autoload": { "psr-4": { "Symfony\\Component\\Yaml\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -797,7 +1764,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2015-07-01 14:16:54" + "time": "2016-02-02 09:49:18" } ], "aliases": [], @@ -806,7 +1773,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=5.5" + "php": "^5.5 | ^7.0" }, "platform-dev": [] } diff --git a/requirements.sh b/requirements.sh new file mode 100755 index 0000000000..f8acc1ba4d --- /dev/null +++ b/requirements.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +#!/bin/bash + +GLEXEC=$(which ansible-galaxy) + +if [ $? = 1 ]; then + echo + echo "Ansible-galaxy not found, please make sure you have ansible 1.9+ installed." + echo + exit 1 +fi + +$GLEXEC install -f -r ansible/requirements.txt diff --git a/src/InfluxDB/Client.php b/src/InfluxDB/Client.php index b724c63f04..51b137ef22 100644 --- a/src/InfluxDB/Client.php +++ b/src/InfluxDB/Client.php @@ -88,6 +88,14 @@ class Client */ protected $driver; + + /** + * Stores the last query that ran + * + * @var null + */ + public static $lastQuery = null; + /** * @param string $host * @param int $port @@ -103,7 +111,7 @@ class Client $username = '', $password = '', $ssl = false, - $verifySSL = true, + $verifySSL = false, $timeout = 0 ) { $this->host = (string) $host; @@ -183,6 +191,9 @@ class Client $driver->setParameters($parameters); try { + // store the last query sent + static::$lastQuery = $query; + // perform the query and return the resultset return $driver->query(); @@ -191,6 +202,33 @@ class Client } } + /** + * Write data + * + * @param array $parameters + * @param string $payload + * + * @return bool + */ + public function write(array $parameters, $payload) + { + // retrive the driver + $driver = $this->getDriver(); + + // add authentication to the driver if needed + if (!empty($this->username) && !empty($this->password)) { + $parameters += ['auth' => [$this->username, $this->password]]; + } + + // set the given parameters + $driver->setParameters($parameters); + + // send the points to influxDB + $driver->write(implode("\n", $payload)); + + return $driver->isSuccess(); + } + /** * List all the databases */ @@ -209,9 +247,9 @@ class Client */ public function listUsers() { - $result = $this->query(null, 'SHOW USERS')->getPoints(); + $result = $this->query(null, 'SHOW USERS')->getColumns(); - return $this->pointsToArray($result); + return (array) $result; } /** @@ -306,6 +344,16 @@ class Client return $this->host; } + /** + * Returns the last executed query + * + * @return null|string + */ + public function getLastQuery() + { + return static::$lastQuery; + } + /** * @param Point[] $points * @return array diff --git a/src/InfluxDB/Database.php b/src/InfluxDB/Database.php index 3ca7163f85..5c094fde68 100644 --- a/src/InfluxDB/Database.php +++ b/src/InfluxDB/Database.php @@ -77,14 +77,21 @@ class Database * Create this database * * @param RetentionPolicy $retentionPolicy + * @param bool $createIfNotExists Only create the database if it does not yet exist + * * @return ResultSet * @throws DatabaseException - * @throws Exception */ - public function create(RetentionPolicy $retentionPolicy = null) + public function create(RetentionPolicy $retentionPolicy = null, $createIfNotExists = true) { try { - $this->query(sprintf('CREATE DATABASE %s', $this->name)); + $query = sprintf( + 'CREATE DATABASE %s"%s"', + ($createIfNotExists ? 'IF NOT EXISTS ' : ''), + $this->name + ); + + $this->query($query); if ($retentionPolicy) { $this->createRetentionPolicy($retentionPolicy); @@ -125,25 +132,13 @@ class Database ); try { - $driver = $this->client->getDriver(); - $parameters = [ 'url' => sprintf('write?db=%s&precision=%s', $this->name, $precision), 'database' => $this->name, 'method' => 'post' ]; - // add authentication to the driver if needed - if (!empty($this->username) && !empty($this->password)) { - $parameters += ['auth' => [$this->username, $this->password]]; - } - - $driver->setParameters($parameters); - - // send the points to influxDB - $driver->write(implode(PHP_EOL, $payload)); - - return $driver->isSuccess(); + return $this->client->write($parameters, $payload); } catch (\Exception $e) { throw new Exception($e->getMessage(), $e->getCode()); @@ -174,7 +169,7 @@ class Database */ public function listRetentionPolicies() { - return $this->query(sprintf('SHOW RETENTION POLICIES %s', $this->name))->getPoints(); + return $this->query(sprintf('SHOW RETENTION POLICIES ON "%s"', $this->name))->getPoints(); } /** @@ -182,7 +177,7 @@ class Database */ public function drop() { - $this->query(sprintf('DROP DATABASE %s', $this->name)); + $this->query(sprintf('DROP DATABASE "%s"', $this->name)); } /** @@ -210,12 +205,8 @@ class Database */ protected function getRetentionPolicyQuery($method, RetentionPolicy $retentionPolicy) { - if (!in_array($method, ['CREATE', 'ALTER'])) { - throw new \InvalidArgumentException(sprintf('%s is not a valid method')); - } - $query = sprintf( - '%s RETENTION POLICY %s ON %s DURATION %s REPLICATION %s', + '%s RETENTION POLICY "%s" ON "%s" DURATION %s REPLICATION %s', $method, $retentionPolicy->name, $this->name, diff --git a/src/InfluxDB/Driver/DriverInterface.php b/src/InfluxDB/Driver/DriverInterface.php index fc3a2c2f96..317099644d 100644 --- a/src/InfluxDB/Driver/DriverInterface.php +++ b/src/InfluxDB/Driver/DriverInterface.php @@ -30,6 +30,11 @@ interface DriverInterface */ public function setParameters(array $parameters); + /** + * @return array + */ + public function getParameters(); + /** * Send the data * diff --git a/src/InfluxDB/Driver/Guzzle.php b/src/InfluxDB/Driver/Guzzle.php index a3f1d91112..c3c2952eed 100644 --- a/src/InfluxDB/Driver/Guzzle.php +++ b/src/InfluxDB/Driver/Guzzle.php @@ -68,6 +68,14 @@ class Guzzle implements DriverInterface, QueryDriverInterface $this->parameters = $parameters; } + /** + * @return array + */ + public function getParameters() + { + return $this->parameters; + } + /** * Send the data * @@ -87,17 +95,10 @@ class Guzzle implements DriverInterface, QueryDriverInterface */ public function query() { - $response = $this->httpClient->get($this->parameters['url'], $this->getRequestParameters()); $raw = (string) $response->getBody(); - $responseJson = json_encode($raw); - - if (isset($responseJson->error)) { - throw new Exception($responseJson->error); - } - return new ResultSet($raw); } @@ -109,7 +110,14 @@ class Guzzle implements DriverInterface, QueryDriverInterface */ public function isSuccess() { - return in_array($this->response->getStatusCode(), ['200', '204']); + $statuscode = $this->response->getStatusCode(); + + if(!in_array($statuscode, ['200', '204'])) + { + throw new Exception('HTTP Code ' . $statuscode . ' ' . $this->response->getBody()); + } + + return true; } /** diff --git a/src/InfluxDB/Driver/UDP.php b/src/InfluxDB/Driver/UDP.php index ac2be0ebb0..9aa6c50045 100644 --- a/src/InfluxDB/Driver/UDP.php +++ b/src/InfluxDB/Driver/UDP.php @@ -58,6 +58,14 @@ class UDP implements DriverInterface $this->parameters = $parameters; } + /** + * @return array + */ + public function getParameters() + { + return $this->parameters; + } + /** * Send the data * diff --git a/src/InfluxDB/Point.php b/src/InfluxDB/Point.php index cd757e209b..f9cf8db17f 100644 --- a/src/InfluxDB/Point.php +++ b/src/InfluxDB/Point.php @@ -14,22 +14,22 @@ class Point /** * @var string */ - private $measurement; + protected $measurement; /** * @var array */ - private $tags = []; + protected $tags = []; /** * @var array */ - private $fields = []; + protected $fields = []; /** * @var string */ - private $timestamp; + protected $timestamp; /** * The timestamp is optional. If you do not specify a timestamp the server’s @@ -55,12 +55,14 @@ class Point $this->measurement = (string) $measurement; $this->tags = $tags; - $this->fields = $additionalFields; + $fields = $additionalFields; - if ($value) { - $this->fields['value'] = $value; + if ($value !== null) { + $fields['value'] = $value; } + $this->setFields($fields); + if ($timestamp && !$this->isValidTimeStamp($timestamp)) { throw new DatabaseException(sprintf('%s is not a valid timestamp', $timestamp)); } @@ -80,10 +82,10 @@ class Point $string = $this->measurement; if (count($this->tags) > 0) { - $string .= ',' . $this->arrayToString($this->tags); + $string .= ',' . $this->arrayToString($this->escapeCharacters($this->tags)); } - $string .= ' ' . $this->arrayToString($this->fields); + $string .= ' ' . $this->arrayToString($this->escapeCharacters($this->fields)); if ($this->timestamp) { $string .= ' '.$this->timestamp; @@ -92,6 +94,116 @@ class Point return $string; } + /** + * @return string + */ + public function getMeasurement() + { + return $this->measurement; + } + + /** + * @param string $measurement + */ + public function setMeasurement($measurement) + { + $this->measurement = $measurement; + } + + /** + * @return array + */ + public function getTags() + { + return $this->tags; + } + + /** + * @param array $tags + */ + public function setTags($tags) + { + $this->tags = $tags; + } + + /** + * @return array + */ + public function getFields() + { + return $this->fields; + } + + /** + * @param array $fields + */ + public function setFields($fields) + { + foreach ($fields as &$field) { + if (is_integer($field)) { + $field = sprintf('%di', $field); + } elseif (is_string($field)) { + $field = sprintf("\"%s\"", $field); + } elseif (is_bool($field)) { + $field = ($field ? "true" : "false"); + } + } + + $this->fields = $fields; + } + + /** + * @return string + */ + public function getTimestamp() + { + return $this->timestamp; + } + + /** + * @param string $timestamp + */ + public function setTimestamp($timestamp) + { + $this->timestamp = $timestamp; + } + + /** + * Escapes invalid characters in both the array key and array value + * + * @param array $arr + * @return array + */ + private function escapeCharacters(array $arr) + { + $returnArr = []; + + foreach ($arr as $key => $value) { + $returnArr[$this->addSlashes($key)] = $this->addSlashes($value); + } + + return $returnArr; + } + + /** + * Returns strings with space, comma, or equals sign characters backslashed per Influx write protocol syntax + * + * @param string $value + * @return string + */ + private function addSlashes($value) + { + return str_replace([ + ' ', + ',', + '=' + ],[ + '\ ', + '\,', + '\=' + ], $value); + } + /** * @param array $arr * @return string @@ -113,14 +225,26 @@ class Point */ private function isValidTimeStamp($timestamp) { - if ((int) $timestamp === $timestamp) { + + // if the code is run on a 32bit system, loosely check if the timestamp is a valid numeric + if (PHP_INT_SIZE == 4 && is_numeric($timestamp)) { return true; } - if ($timestamp <= PHP_INT_MAX && $timestamp >= ~PHP_INT_MAX) { - return true; + if (!is_numeric($timestamp)) { + return false; } - return false; + if (intval($timestamp) != $timestamp) { + return false; + } + + if (!($timestamp <= PHP_INT_MAX && $timestamp >= ~PHP_INT_MAX)) { + return false; + } + + return true; + + } } diff --git a/src/InfluxDB/Query/Builder.php b/src/InfluxDB/Query/Builder.php index 1060bc39d4..6f1de91968 100644 --- a/src/InfluxDB/Query/Builder.php +++ b/src/InfluxDB/Query/Builder.php @@ -60,6 +60,16 @@ class Builder */ protected $limitClause = ''; + /** + * @var array + */ + protected $groupBy; + + /** + * @var array + */ + protected $orderBy; + /** * @param Database $db */ @@ -178,6 +188,18 @@ class Builder return $this; } + public function groupBy($field = 'type') { + $this->groupBy[] = $field; + + return $this; + } + + public function orderBy($field = 'type', $order = 'ASC') { + $this->orderBy[] = "$field $order"; + + return $this; + } + /** * Set's the time range to select data from * @@ -244,7 +266,7 @@ class Builder */ protected function parseQuery() { - $query = sprintf("SELECT %s FROM %s", $this->selection, $this->metric); + $query = sprintf('SELECT %s FROM "%s"', $this->selection, $this->metric); if (! $this->metric) { throw new \InvalidArgumentException('No metric provided to from()'); @@ -262,6 +284,14 @@ class Builder } + if (!empty($this->groupBy)) { + $query .= ' GROUP BY ' . implode(',', $this->groupBy); + } + + if (!empty($this->orderBy)) { + $query .= ' ORDER BY ' . implode(',', $this->orderBy); + } + if ($this->limitClause) { $query .= $this->limitClause; } diff --git a/src/InfluxDB/ResultSet.php b/src/InfluxDB/ResultSet.php index 66330ab42d..b9faa977d7 100644 --- a/src/InfluxDB/ResultSet.php +++ b/src/InfluxDB/ResultSet.php @@ -89,6 +89,14 @@ class ResultSet return array_shift($series); } + /** + * @return mixed + */ + public function getColumns() + { + return $this->getSeries()[0]['columns']; + } + /** * @param array $serie * @return array diff --git a/test_library.php b/test_library.php new file mode 100644 index 0000000000..02978e16ff --- /dev/null +++ b/test_library.php @@ -0,0 +1,68 @@ +selectDB('test'); + +if ($database->exists()) { + $database->drop(); +} + +$database->create(new \InfluxDB\Database\RetentionPolicy('test', '12w', 1, true)); + + +$start = microtime(true); + +$countries = ['nl', 'gb', 'us', 'be', 'th', 'jp', 'nl', 'sa']; +$colors = ['orange', 'black', 'yellow', 'white', 'red', 'purple']; +$points = []; + +for ($i=0; $i < 1000; $i++) { + $points[] = new \InfluxDB\Point( + 'flags', + randFloat(1, 999), + ['country' => $countries[array_rand($countries)]], + ['color' => $colors[array_rand($colors)]], + (int) shell_exec('date +%s%N')+mt_rand(1,1000) + ); +}; + +// insert the points +$database->writePoints($points); + +$end = microtime(true); + +$country = $countries[array_rand($countries)]; +$color = $colors[array_rand($colors)]; + +$results = $database->query("SELECT * FROM flags WHERE country = '$country' LIMIT 5")->getPoints(); +$results2 = $database->query("SELECT * FROM flags WHERE color = '$color' LIMIT 5")->getPoints(); + +echo "Showing top 5 flags from country $country:" . PHP_EOL; +print_r($results); +echo PHP_EOL; + +echo "Showing top 5 flags with color $color:" . PHP_EOL; +print_r($results2); + + +echo PHP_EOL; +echo sprintf('Executed 1000 inserts in %.2f seconds', $end - $start); diff --git a/tests/unit/AbstractTest.php b/tests/unit/AbstractTest.php index 160f6aea66..7afcc49be5 100644 --- a/tests/unit/AbstractTest.php +++ b/tests/unit/AbstractTest.php @@ -10,7 +10,7 @@ use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\HandlerStack; use InfluxDB\Client; use InfluxDB\Database; -use InfluxDB\Driver\Guzzle; +use InfluxDB\Driver\Guzzle as GuzzleDriver; use InfluxDB\ResultSet; use PHPUnit_Framework_MockObject_MockObject; use GuzzleHttp\Client as GuzzleClient; @@ -44,7 +44,7 @@ abstract class AbstractTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); - $this->resultData = file_get_contents(dirname(__FILE__) . '/result.example.json'); + $this->resultData = file_get_contents(dirname(__FILE__) . '/json/result.example.json'); $this->mockClient->expects($this->any()) ->method('getBaseURI') @@ -52,9 +52,17 @@ abstract class AbstractTest extends \PHPUnit_Framework_TestCase $this->mockClient->expects($this->any()) ->method('query') - ->will($this->returnValue(new ResultSet($this->resultData))); + ->will($this->returnCallback(function ($db, $query) { + Client::$lastQuery = $query; - $httpMockClient = new Guzzle($this->buildHttpMockClient('')); + return new ResultSet($this->resultData); + })); + + $this->mockClient->expects($this->any()) + ->method('write') + ->will($this->returnValue(true)); + + $httpMockClient = new GuzzleDriver($this->buildHttpMockClient('')); // make sure the client has a valid driver $this->mockClient->expects($this->any()) @@ -88,7 +96,13 @@ abstract class AbstractTest extends \PHPUnit_Framework_TestCase public function buildHttpMockClient($body) { // Create a mock and queue two responses. - $mock = new MockHandler([new Response(200, array(), $body)]); + $mock = new MockHandler([ + new Response(200, array(), $body), + new Response(200, array(), $body), + new Response(400, array(), 'fault{'), + new Response(400, array(), $body), + new Response(400, array(), $body), + ]); $handler = HandlerStack::create($mock); return new GuzzleClient(['handler' => $handler]); @@ -114,7 +128,7 @@ abstract class AbstractTest extends \PHPUnit_Framework_TestCase ->getMock(); if ($emptyResult) { - $mockClient->expects($this->once()) + $mockClient->expects($this->any()) ->method('query') ->will($this->returnValue(new ResultSet($this->getEmptyResult()))); } diff --git a/tests/unit/AdminTest.php b/tests/unit/AdminTest.php index 889874f4c6..835e9869a1 100644 --- a/tests/unit/AdminTest.php +++ b/tests/unit/AdminTest.php @@ -47,7 +47,7 @@ class AdminTest extends AbstractTest public function testShowUsers() { - $testJson = file_get_contents(dirname(__FILE__) . '/result-test-users.example.json'); + $testJson = file_get_contents(dirname(__FILE__) . '/json/result-test-users.example.json'); $clientMock = $this->getClientMock(); $testResult = new ResultSet($testJson); diff --git a/tests/unit/ClientTest.php b/tests/unit/ClientTest.php index 09f420d1bf..a75b979d1e 100644 --- a/tests/unit/ClientTest.php +++ b/tests/unit/ClientTest.php @@ -4,6 +4,7 @@ namespace InfluxDB\Test; use InfluxDB\Client; use InfluxDB\Driver\Guzzle; +use InfluxDB\Point; class ClientTest extends AbstractTest { @@ -19,16 +20,26 @@ class ClientTest extends AbstractTest /** @var Client $client */ protected $client = null; + public function testGetters() + { + $client = $this->getClient(); + + $this->assertEquals('http://localhost:8086', $client->getBaseURI()); + $this->assertInstanceOf('InfluxDB\Driver\Guzzle', $client->getDriver()); + $this->assertEquals('localhost', $client->getHost()); + $this->assertEquals('0', $client->getTimeout()); + } + public function testBaseURl() { - $client = new Client('localhost', 8086); + $client = $this->getClient(); $this->assertEquals($client->getBaseURI(), 'http://localhost:8086'); } public function testSelectDbShouldReturnDatabaseInstance() { - $client = new Client('localhost', 8086); + $client = $this->getClient(); $dbName = 'test-database'; $database = $client->selectDB($dbName); @@ -38,23 +49,110 @@ class ClientTest extends AbstractTest $this->assertEquals($dbName, $database->getName()); } + public function testSecureInstance() + { + $client = $this->getClient('test', 'test', true); + $urlParts = parse_url($client->getBaseURI()); + + $this->assertEquals('https', $urlParts['scheme']); + } /** */ public function testGuzzleQuery() { - $client = new Client('localhost', 8086); + $client = $this->getClient('test', 'test'); $query = "some-bad-query"; - $bodyResponse = file_get_contents(dirname(__FILE__) . '/result.example.json'); + $bodyResponse = file_get_contents(dirname(__FILE__) . '/json/result.example.json'); $httpMockClient = $this->buildHttpMockClient($bodyResponse); $client->setDriver(new Guzzle($httpMockClient)); /** @var \InfluxDB\ResultSet $result */ - $result = $client->query(null, $query); + $result = $client->query('somedb', $query); + $parameters = $client->getDriver()->getParameters(); + + $this->assertEquals(['test', 'test'], $parameters['auth']); + $this->assertEquals('somedb', $parameters['database']); $this->assertInstanceOf('\InfluxDB\ResultSet', $result); + + $this->assertEquals( + true, + $client->write( + [ + 'url' => 'http://localhost', + 'database' => 'influx_test_db', + 'method' => 'post' + ], + [new Point('test', 1.0)] + ) + ); + + $this->setExpectedException('\InvalidArgumentException'); + $client->query('test', 'bad-query'); + + $this->setExpectedException('\InfluxDB\Driver\Exception'); + $client->query('test', 'bad-query'); + } + + public function testGetLastQuery() + { + $this->mockClient->query('test', 'SELECT * from test_metric'); + $this->assertEquals($this->getClient()->getLastQuery(), 'SELECT * from test_metric'); + } + + public function testListDatabases() + { + $this->doTestResponse('databases.example.json', ['test', 'test1', 'test2'], 'listDatabases'); + } + public function testListUsers() + { + $this->doTestResponse('users.example.json', ['user', 'admin'], 'listUsers'); + } + + public function testFactoryMethod() + { + $client = $this->getClient('test', 'test', true); + + $staticClient = \InfluxDB\Client::fromDSN('https+influxdb://test:test@localhost:8086/'); + + $this->assertEquals($client, $staticClient); + + $db = $client->selectDB('testdb'); + $staticDB = \InfluxDB\Client::fromDSN('https+influxdb://test:test@localhost:8086/testdb'); + + $this->assertEquals($db, $staticDB); + + } + + /** + * @param string $responseFile + * @param array $result + * @param string $method + */ + protected function doTestResponse($responseFile, array $result, $method) + { + $client = $this->getClient(); + $bodyResponse = file_get_contents(dirname(__FILE__) . '/json/'. $responseFile); + $httpMockClient = $this->buildHttpMockClient($bodyResponse); + + $client->setDriver(new Guzzle($httpMockClient)); + + $this->assertEquals($result, $client->$method()); + } + + /** + * @param string $username + * @param string $password + * @param bool|false $ssl + * + * @return Client + */ + protected function getClient($username = '', $password = '', $ssl = false) + { + return new Client('localhost', 8086, $username, $password, $ssl); } } \ No newline at end of file diff --git a/tests/unit/DatabaseTest.php b/tests/unit/DatabaseTest.php index e410385476..b90fe33180 100644 --- a/tests/unit/DatabaseTest.php +++ b/tests/unit/DatabaseTest.php @@ -27,34 +27,54 @@ class DatabaseTest extends AbstractTest { parent::setUp(); - $this->resultData = file_get_contents(dirname(__FILE__) . '/result.example.json'); + $this->resultData = file_get_contents(dirname(__FILE__) . '/json/result.example.json'); $this->mockClient->expects($this->any()) ->method('listDatabases') ->will($this->returnValue(array('test123', 'test'))); - $this->dataToInsert = file_get_contents(dirname(__FILE__) . '/input.example.json'); + $this->dataToInsert = file_get_contents(dirname(__FILE__) . '/json/input.example.json'); } + public function testGetters() + { + $this->assertInstanceOf('InfluxDB\Client', $this->database->getClient()); + $this->assertInstanceOf('InfluxDB\Query\Builder', $this->database->getQueryBuilder()); + } + + /** * */ - public function testQuery() + public function testQueries() { $testResultSet = new ResultSet($this->resultData); $this->assertEquals($this->database->query('SELECT * FROM test_metric'), $testResultSet); + + $this->database->drop(); + $this->assertEquals('DROP DATABASE "influx_test_db"', Client::$lastQuery); + } - public function testCreateRetentionPolicy() + + public function testRetentionPolicyQueries() { - $retentionPolicy = new Database\RetentionPolicy('test', '1d', 1, true); + $retentionPolicy = $this->getTestRetentionPolicy(); - $mockClient = $this->getClientMock(true); + $this->assertEquals( + $this->getTestDatabase()->createRetentionPolicy($retentionPolicy), + new ResultSet($this->getEmptyResult()) + ); - $database = new Database('test', $mockClient); + $this->database->listRetentionPolicies(); + $this->assertEquals('SHOW RETENTION POLICIES ON "influx_test_db"', Client::$lastQuery); - $this->assertEquals($database->createRetentionPolicy($retentionPolicy), new ResultSet($this->getEmptyResult())); + $this->database->alterRetentionPolicy($this->getTestRetentionPolicy()); + $this->assertEquals( + 'ALTER RETENTION POLICY "test" ON "influx_test_db" DURATION 1d REPLICATION 1 DEFAULT', + Client::$lastQuery + ); } /** @@ -65,6 +85,38 @@ class DatabaseTest extends AbstractTest new Database(null, $this->mockClient); } + public function testCreate() + { + // test create with retention policy + $this->database->create($this->getTestRetentionPolicy('influx_test_db'), true); + $this->assertEquals( + 'CREATE RETENTION POLICY "influx_test_db" ON "influx_test_db" DURATION 1d REPLICATION 1 DEFAULT', + Client::$lastQuery + ); + + // test creating a database without create if not exists + $this->database->create(null, true); + $this->assertEquals('CREATE DATABASE IF NOT EXISTS "influx_test_db"', Client::$lastQuery); + + // test creating a database without create if not exists + $this->database->create(null, false); + $this->assertEquals('CREATE DATABASE "influx_test_db"', Client::$lastQuery); + + + $this->mockClient->expects($this->any()) + ->method('query') + ->will($this->returnCallback(function () { + throw new \Exception('test exception'); + })); + + + // test an exception being handled correctly + $this->setExpectedException('\InfluxDB\Database\Exception'); + $this->database->create($this->getTestRetentionPolicy('influx_test_db'), false); + + } + + public function testExists() { $database = new Database('test', $this->mockClient); @@ -96,5 +148,76 @@ class DatabaseTest extends AbstractTest ); $this->assertEquals(true, $this->database->writePoints(array($point1, $point2))); + + $this->mockClient->expects($this->once()) + ->method('write') + ->will($this->throwException(new \Exception('Test exception'))); + + $this->setExpectedException('InfluxDB\Exception'); + + $this->database->writePoints(array($point1, $point2)); + } -} \ No newline at end of file + + public function testQueryBuilderOrderBy() + { + $this->assertEquals( + $this->database->getQueryBuilder() + ->from('test_metric') + ->orderBy('time', 'DESC')->getQuery(), + 'SELECT * FROM "test_metric" ORDER BY time DESC'); + + $this->assertEquals( + $this->database->getQueryBuilder() + ->from('test_metric') + ->orderBy('time', 'DESC') + ->orderBy('some_field', 'ASC') + ->getQuery(), + 'SELECT * FROM "test_metric" ORDER BY time DESC,some_field ASC'); + } + + /** + * @see https://github.com/influxdata/influxdb-php/pull/36 + */ + public function testReservedNames() + { + $database = new Database('stats', $this->mockClient); + + // test handling of reserved keywords in database name + $database->create(); + $this->assertEquals('CREATE DATABASE IF NOT EXISTS "stats"', Client::$lastQuery); + + $database->listRetentionPolicies(); + $this->assertEquals('SHOW RETENTION POLICIES ON "stats"', Client::$lastQuery); + + // test handling of reserved keywords in retention policy names + $database->create($this->getTestRetentionPolicy('default')); + $this->assertEquals( + 'CREATE RETENTION POLICY "default" ON "stats" DURATION 1d REPLICATION 1 DEFAULT', + Client::$lastQuery + ); + + // test handling of reserved keywords in measurement names + $this->assertEquals($database->getQueryBuilder()->from('server')->getQuery(), 'SELECT * FROM "server"'); + } + + /** + * @param string $name + * + * @return Database + */ + protected function getTestDatabase($name = 'test') + { + return new Database($name, $this->getClientMock(true)); + } + + /** + * @param string $name + * + * @return Database\RetentionPolicy + */ + protected function getTestRetentionPolicy($name = 'test') + { + return new Database\RetentionPolicy($name, '1d', 1, true); + } +} diff --git a/tests/unit/PointTest.php b/tests/unit/PointTest.php index eaee63fe75..1b757cc63e 100644 --- a/tests/unit/PointTest.php +++ b/tests/unit/PointTest.php @@ -1,10 +1,4 @@ 'server01', 'region' => 'us-west'), - array('cpucount' => 10), - 1435222310 - ); + $point = $this->getPoint('1440494531376778481'); $this->assertEquals($expected, (string) $point); } -} \ No newline at end of file + + /** + * Check if the Point class throw an exception when invalid timestamp are given. + * + * @dataProvider wrongTimestampProvider + * @expectedException \InfluxDB\Database\Exception + */ + public function testPointWrongTimestamp($timestamp) + { + $this->getPoint($timestamp); + } + + /** + * Check if the Point class accept all valid timestamp given. + * + * @dataProvider validTimestampProvider + */ + public function testPointValidTimestamp($timestamp) + { + $expected = 'instance,host=server01,region=us-west cpucount=10i,free=1i,test="string",bool=false,value=1.11' . (($timestamp) ? ' ' . $timestamp : ''); + + $point = $this->getPoint($timestamp); + + $this->assertEquals($expected, (string) $point); + } + + public function testGettersAndSetters() + { + $timestamp = time(); + $timestamp2 = time() + 3600; + $point = $this->getPoint($timestamp); + + $this->assertEquals($timestamp, $point->getTimestamp()); + $point->setTimestamp($timestamp2); + $this->assertEquals($timestamp2, $point->getTimestamp()); + + $this->assertEquals('instance', $point->getMeasurement()); + $point->setMeasurement('test'); + $this->assertEquals('test', $point->getMeasurement()); + + $fields = $point->getFields(); + $this->assertEquals(1.11, $fields['value']); + $this->assertEquals([ + 'cpucount' => '10i', + 'free' => '1i', + 'test' => "\"string\"", + 'bool' => 'false', + 'value' => '1.1100000000000001' + ], $fields); + + $point->setFields(['cpucount' => 11]); + $this->assertEquals(['cpucount' => '11i'], $point->getFields()); + + $this->assertEquals(['host' => 'server01', 'region' => 'us-west'], $point->getTags()); + $point->setTags(['test' => 'value']); + $this->assertEquals(['test' => 'value'], $point->getTags()); + + } + + + /** + * Provide wrong timestamp value for testing. + */ + public function wrongTimestampProvider() + { + return [ + ['2015-10-27 14:17:40'], + ['INVALID'], + ['aa778481'], + ['1477aee'], + ['15.258'], + ['15,258'], + [15.258], + [true] + ]; + } + + /** + * Provide valid timestamp value for testing. + */ + public function validTimestampProvider() + { + return [ + [time()], // Current time returned by the PHP time function. + [0], // Day 0 + [~PHP_INT_MAX], // Minimum value integer + [PHP_INT_MAX], // Maximum value integer + ['1440494531376778481'] // Text timestamp + ]; + } + + /** + * Returns an instance of \InfluxDB\Point + * + * @param int $timestamp + * + * @return Point + */ + private function getPoint($timestamp) + { + return new Point( + 'instance', // the name of the measurement + 1.11, // measurement value + ['host' => 'server01', 'region' => 'us-west'], + ['cpucount' => 10, 'free' => 1, 'test' => "string", 'bool' => false], + $timestamp + ); + } + +} diff --git a/tests/unit/ResultSetTest.php b/tests/unit/ResultSetTest.php index 4c04bf1e9b..f1a3669eea 100644 --- a/tests/unit/ResultSetTest.php +++ b/tests/unit/ResultSetTest.php @@ -10,7 +10,7 @@ class ResultSetTest extends \PHPUnit_Framework_TestCase public function setUp() { - $resultJsonExample = file_get_contents(dirname(__FILE__) . '/result.example.json'); + $resultJsonExample = file_get_contents(dirname(__FILE__) . '/json/result.example.json'); $this->resultSet = new ResultSet($resultJsonExample); } @@ -65,7 +65,7 @@ EOD; */ public function testGetPointsFromNameWithoudTags() { - $resultJsonExample = file_get_contents(dirname(__FILE__) . '/result-no-tags.example.json'); + $resultJsonExample = file_get_contents(dirname(__FILE__) . '/json/result-no-tags.example.json'); $this->resultSet = new ResultSet($resultJsonExample); $measurementName = 'cpu_load_short'; @@ -95,6 +95,11 @@ EOD; } + public function testGetSeries() + { + $this->assertEquals(['time', 'value'], $this->resultSet->getColumns()); + } + /** * We can get points from measurement */ diff --git a/tests/unit/json/databases.example.json b/tests/unit/json/databases.example.json new file mode 100644 index 0000000000..fae386bb95 --- /dev/null +++ b/tests/unit/json/databases.example.json @@ -0,0 +1,25 @@ +{ + "results":[ + { + "series":[ + { + "name":"databases", + "columns":[ + "name" + ], + "values":[ + [ + "test" + ], + [ + "test1" + ], + [ + "test2" + ] + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/unit/input.example.json b/tests/unit/json/input.example.json similarity index 100% rename from tests/unit/input.example.json rename to tests/unit/json/input.example.json diff --git a/tests/unit/result-no-tags.example.json b/tests/unit/json/result-no-tags.example.json similarity index 100% rename from tests/unit/result-no-tags.example.json rename to tests/unit/json/result-no-tags.example.json diff --git a/tests/unit/result-test-users.example.json b/tests/unit/json/result-test-users.example.json similarity index 100% rename from tests/unit/result-test-users.example.json rename to tests/unit/json/result-test-users.example.json diff --git a/tests/unit/result.example.json b/tests/unit/json/result.example.json similarity index 100% rename from tests/unit/result.example.json rename to tests/unit/json/result.example.json diff --git a/tests/unit/json/users.example.json b/tests/unit/json/users.example.json new file mode 100644 index 0000000000..272794e6c0 --- /dev/null +++ b/tests/unit/json/users.example.json @@ -0,0 +1,14 @@ +{ + "results":[ + { + "series":[ + { + "columns":[ + "user", + "admin" + ] + } + ] + } + ] +} \ No newline at end of file