From 9d98200f0fbb45b2c11d4eaed0fbe7642fad4a07 Mon Sep 17 00:00:00 2001 From: Benjamin Carl Date: Sat, 15 Apr 2017 09:36:10 +0200 Subject: [PATCH 01/12] Beginning restructuring by new template. --- .editorconfig | 5 +- .scrutinizer.yml | 88 -------- .travis.yml | 19 +- Demo.php | 16 +- LICENSE | 43 ++-- README.md | 26 +-- composer.json | 37 +-- phpunit.xml.dist | 23 ++ src/Clickalicious/Memcached/Autoloader.php | 211 ------------------ src/Clickalicious/Memcached/Bootstrap.php | 122 ---------- src/Clickalicious/Memcached/Cache.php | 153 ------------- .../Compression/CompressionInterface.php | 96 -------- .../Memcached/Compression/Zlib.php | 106 --------- src/Clickalicious/Memcached/Exception.php | 74 ------ src/Clickalicious/Memcached/Php/Cache.php | 126 +++++++++++ .../Memcached/{ => Php}/Client.php | 88 +++----- .../Php/Compression/CompressionInterface.php | 59 +++++ .../Memcached/{ => Php}/Compression/Lzw.php | 98 +++----- .../Memcached/{ => Php}/Compression/Smaz.php | 95 +++----- .../Memcached/Php/Compression/Zlib.php | 69 ++++++ src/Clickalicious/Memcached/Php/Exception.php | 39 ++++ .../Memcached/{ => Php}/Util.php | 76 ++----- .../Memcached/{ => Php}/ClientTest.php | 83 ++----- .../Memcached/{ => Php}/UtilTest.php | 119 ++++------ tests/phpunit.xml | 37 --- update-gh-pages.sh | 2 +- 26 files changed, 569 insertions(+), 1341 deletions(-) delete mode 100644 .scrutinizer.yml create mode 100644 phpunit.xml.dist delete mode 100644 src/Clickalicious/Memcached/Autoloader.php delete mode 100644 src/Clickalicious/Memcached/Bootstrap.php delete mode 100644 src/Clickalicious/Memcached/Cache.php delete mode 100644 src/Clickalicious/Memcached/Compression/CompressionInterface.php delete mode 100644 src/Clickalicious/Memcached/Compression/Zlib.php delete mode 100644 src/Clickalicious/Memcached/Exception.php create mode 100644 src/Clickalicious/Memcached/Php/Cache.php rename src/Clickalicious/Memcached/{ => Php}/Client.php (95%) create mode 100644 src/Clickalicious/Memcached/Php/Compression/CompressionInterface.php rename src/Clickalicious/Memcached/{ => Php}/Compression/Lzw.php (62%) rename src/Clickalicious/Memcached/{ => Php}/Compression/Smaz.php (68%) create mode 100644 src/Clickalicious/Memcached/Php/Compression/Zlib.php create mode 100644 src/Clickalicious/Memcached/Php/Exception.php rename src/Clickalicious/Memcached/{ => Php}/Util.php (74%) rename tests/Clickalicious/Memcached/{ => Php}/ClientTest.php (84%) rename tests/Clickalicious/Memcached/{ => Php}/UtilTest.php (61%) delete mode 100644 tests/phpunit.xml diff --git a/.editorconfig b/.editorconfig index 2365f05..db25999 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,7 +4,10 @@ root = true [*] charset = utf-8 end_of_line = lf -indent_size = 4 +indent_size = 2 indent_style = space + +[*.php] +indent_size = 4 insert_final_newline = true trim_trailing_whitespace = true diff --git a/.scrutinizer.yml b/.scrutinizer.yml deleted file mode 100644 index 702693b..0000000 --- a/.scrutinizer.yml +++ /dev/null @@ -1,88 +0,0 @@ -# language: php -# .scrutinizer.yml -before_commands: - - "composer install --prefer-dist --no-interaction" - -filter: - paths: - - src/* - - tests/* - excluded_paths: - - docs/* - - src/Clickalicious/Memcached/Autoloader.php - - src/Clickalicious/Memcached/Bootstrap.php - - src/Clickalicious/Memcached/Cache.php - - src/Clickalicious/Memcached/Exception.php - - src/Clickalicious/Memcached/Compression/* - -tools: - external_code_coverage: true - php_code_sniffer: - enabled: true - config: - standard: PSR2 - php_cs_fixer: - enabled: true - filter: - paths: - - src/* - - tests/* - excluded_paths: - - docs/* - - src/Clickalicious/Memcached/Autoloader.php - - src/Clickalicious/Memcached/Bootstrap.php - - src/Clickalicious/Memcached/Cache.php - - src/Clickalicious/Memcached/Exception.php - - src/Clickalicious/Memcached/Compression/* - config: - level: all - -checks: - php: - code_rating: true - duplication: true - remove_extra_empty_lines: true - remove_php_closing_tag: true - remove_trailing_whitespace: true - fix_use_statements: - remove_unused: true - preserve_multiple: false - preserve_blanklines: true - order_alphabetically: true - fix_php_opening_tag: true - fix_linefeed: true - fix_line_ending: true - fix_identation_4spaces: true - fix_doc_comments: true - use_self_instead_of_fqcn: true - uppercase_constants: true - simplify_boolean_return: true - return_doc_comments: true - properties_in_camelcaps: true - prefer_while_loop_over_for_loop: true - phpunit_assertions: true - parameters_in_camelcaps: true - optional_parameters_at_the_end: true - no_short_variable_names: - minimum: '3' - no_short_method_names: - minimum: '3' - no_new_line_at_end_of_file: true - no_long_variable_names: - maximum: '20' - no_goto: true - newline_at_end_of_file: true - line_length: - max_length: '120' - function_in_camel_caps: true - encourage_single_quotes: true - encourage_postdec_operator: true - avoid_perl_style_comments: true - avoid_multiple_statements_on_same_line: true - avoid_fixme_comments: true - align_assignments: true - return_doc_comment_if_not_inferrable: true - parameter_doc_comments: true - param_doc_comment_if_not_inferrable: true - more_specific_types_in_doc_comments: true - avoid_unnecessary_concatenation: true diff --git a/.travis.yml b/.travis.yml index 4a09ad2..249c38d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,17 @@ language: php php: - - 5.4 - - 5.5 - 5.6 - 7.0 + - 7.1 + - nightly - hhvm sudo: false matrix: allow_failures: + - php: nightly - php: hhvm fast_finish: true @@ -30,17 +31,16 @@ before_script: - composer --optimize-autoloader --no-interaction script: - - bin/phpunit -c tests/ --coverage-clover=coverage.clover --coverage-html=./docs/coverage + - bin/phpunit --configuration . --coverage-clover=build/logs/clover.xml --coverage-html=build/html/coverage after_script: # We upload only for reference platform! This is our base for further code analyses and so on. - - if [ $(phpenv version-name) == "5.4" ] && [ "$PREFER_LOWEST" == "--prefer-lowest" ]; then wget https://scrutinizer-ci.com/ocular.phar; fi - - if [ $(phpenv version-name) == "5.4" ] && [ "$PREFER_LOWEST" == "--prefer-lowest" ]; then php ocular.phar code-coverage:upload --format=php-clover coverage.clover > /dev/null 2>&1; fi + - if [ $(phpenv version-name) == "5.6" ] && [ "$PREFER_LOWEST" == "--prefer-lowest" ]; then bin/codacycoverage clover build/logs/clover.xml > /dev/null 2>&1; fi after_success: # Push coverage to github pages branch - chmod +x ./update-gh-pages.sh - - if [ $(phpenv version-name) == "5.4" ] && [ "$PREFER_LOWEST" == "--prefer-lowest" ]; then bash ./update-gh-pages.sh; fi + - if [ $(phpenv version-name) == "5.6" ] && [ "$PREFER_LOWEST" == "--prefer-lowest" ]; then bash ./update-gh-pages.sh; fi # CREATE GIT TAG - git config --global user.email "builds@travis-ci.org" - git config --global user.name "Travis" @@ -48,7 +48,7 @@ after_success: - echo -n $GIT_TAG > public/version - git commit -m "Set build VERSION number" public/version - git tag $GIT_TAG -a -m "Generated tag from TravisCI build $TRAVIS_BUILD_NUMBER" - - git push --tags --quiet https://$GITHUBKEY@github.com/clickalicious/Memcached.php > /dev/null 2>&1 + - git push --tags --quiet https://$GITHUBKEY@github.com/clickalicious/memcached-php > /dev/null 2>&1 # Blacklist the pushed tag from above to prevent black hole branches: @@ -57,7 +57,8 @@ branches: # Who to notify? notifications: - slack: clickalicious:$SLACKKEY + slack: + secure: UFIXK2u7xalPWZppJ+X8aU9qjHWompS6qpBxyjYd8NyYIdTC8HvrovatS150F/b0QUnZXGaHe416vmooKRVDv6JgsR+v+sf/XAiAsc/vPSDO734zWZgubQCF3+jmKV2rD7OGnHgn/LBnREJhF26o10ox3A2UQ3G4kkFwP1q+i6g= email: recipients: - - opensource@clickalicious.de \ No newline at end of file + - opensource@clickalicious.de diff --git a/Demo.php b/Demo.php index 3fcdd6f..b0b6450 100644 --- a/Demo.php +++ b/Demo.php @@ -4,13 +4,13 @@ /** * Memcache.php * - * Demo.php - Demonstration of Memcached.php Memcached Client. + * Demo.php - Demonstration of memcached-php Memcached Client. * * * PHP versions 5.3 * * LICENSE: - * Memcached.php - Plain vanilla PHP Memcached client with full support of Memcached protocol. + * memcached-php - Plain vanilla PHP Memcached client with full support of Memcached protocol. * * Copyright (c) 2014 - 2015, Benjamin Carl * All rights reserved. @@ -25,7 +25,7 @@ * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - * - Neither the name of Memcached.php nor the names of its + * - Neither the name of memcached-php nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * @@ -49,7 +49,7 @@ * @copyright 2014 - 2015 Benjamin Carl * @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause * @version Git: $Id$ - * @link https://github.com/clickalicious/Memcached.php + * @link https://github.com/clickalicious/memcached-php */ /** @@ -62,9 +62,9 @@ use Clickalicious\Memcached\Client; /** - * Memcached.php + * memcached-php * - * Demonstration of Memcached.php Memcached Client. + * Demonstration of memcached-php Memcached Client. * * @category Clickalicious * @package Clickalicious_Memcached @@ -73,10 +73,10 @@ * @copyright 2014 - 2015 Benjamin Carl * @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause * @version Git: $Id$ - * @link https://github.com/clickalicious/Memcached.php + * @link https://github.com/clickalicious/memcached-php */ -// Create Memcached.php instance ... +// Create memcached-php instance ... $memcached = new Client( '127.0.0.1' ); diff --git a/LICENSE b/LICENSE index 988e504..be011f9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,27 +1,22 @@ -Copyright (c) 2014 - 2015, clickalicious GmbH, Benjamin Carl -All rights reserved. +(The MIT license) +Copyright 2017 clickalicious, Benjamin Carl -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of Memcached.php nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index bc28ce4..f1f443e 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,14 @@ --- -![Logo of Memcached.php](docs/logo-large.png) +![Logo of memcached-php](docs/logo-large.png) Plain vanilla PHP `Memcached` client library with full support of Memcached ASCII protocol -| [![Build Status](https://travis-ci.org/clickalicious/Memcached.php.svg?branch=master)](https://travis-ci.org/clickalicious/Memcached.php) | [![Scrutinizer](https://img.shields.io/scrutinizer/g/clickalicious/Memcached.php.svg)](https://scrutinizer-ci.com/g/clickalicious/Memcached.php/) | [![Scrutinizer Coverage](https://img.shields.io/scrutinizer/coverage/g/clickalicious/Memcached.php.svg?maxAge=2592000)](http://clickalicious.github.io/Memcached.php/) | [![clickalicious open-source](https://img.shields.io/badge/clickalicious-open--source-green.svg?style=flat)](https://www.clickalicious.de/) | +| [![Build Status](https://travis-ci.org/clickalicious/memcached-php.svg?branch=master)](https://travis-ci.org/clickalicious/memcached-php) | [![Scrutinizer](https://img.shields.io/scrutinizer/g/clickalicious/memcached-php.svg)](https://scrutinizer-ci.com/g/clickalicious/memcached-php/) | [![Scrutinizer Coverage](https://img.shields.io/scrutinizer/coverage/g/clickalicious/memcached-php.svg?maxAge=2592000)](http://clickalicious.github.io/memcached-php/) | [![clickalicious open-source](https://img.shields.io/badge/clickalicious-open--source-green.svg?style=flat)](https://www.clickalicious.de/) | |--- |--- |--- |--- | -| [![GitHub release](https://img.shields.io/github/release/clickalicious/Memcached.php.svg?style=flat)](https://github.com/clickalicious/Memcached.php/releases) | [![Waffle.io](https://img.shields.io/waffle/label/clickalicious/Memcached.php/in%20progress.svg)](https://waffle.io/clickalicious/Memcached.php) | [![SensioLabsInsight](https://insight.sensiolabs.com/projects/57efa79c-5ece-4296-abab-5c3eb9053c2c/mini.png)](https://insight.sensiolabs.com/projects/57efa79c-5ece-4296-abab-5c3eb9053c2c) | [![Packagist](https://img.shields.io/packagist/l/clickalicious/memcached.php.svg?style=flat)](http://opensource.org/licenses/BSD-3-Clause) | +| [![GitHub release](https://img.shields.io/github/release/clickalicious/memcached-php.svg?style=flat)](https://github.com/clickalicious/memcached-php/releases) | [![Waffle.io](https://img.shields.io/waffle/label/clickalicious/memcached-php/in%20progress.svg)](https://waffle.io/clickalicious/memcached-php) | [![SensioLabsInsight](https://insight.sensiolabs.com/projects/57efa79c-5ece-4296-abab-5c3eb9053c2c/mini.png)](https://insight.sensiolabs.com/projects/57efa79c-5ece-4296-abab-5c3eb9053c2c) | [![Packagist](https://img.shields.io/packagist/l/clickalicious/memcached-php.svg?style=flat)](http://opensource.org/licenses/BSD-3-Clause) | ## Table of Contents @@ -34,7 +34,7 @@ Plain vanilla PHP `Memcached` client library with full support of Memcached ASCI - Clean & well documented code - Unit-Tested -**Memcached.php** covers almost 100% of the `Memcached` protocol specification. The code is clean, full documented and developed following the PSR coding standards (PSR-0/4, PSR-1, PSR-2). The code is unit-tested (PHPUnit) and the coverage is high. The library supports \ and \ command on stored integers (strings) and the [connection handling is done like recommended](https://github.com/memcached/memcached/blob/master/doc/protocol.txt#L10 "Keep connections open and share them via a pool across instances.") in the `Memcached` protocol specification. Last but not least it supports seven of [PHP's eight variable types](http://php.net/manual/en/language.types.intro.php "PHP's variable types") - in detail four scalar types: +**memcached-php** covers almost 100% of the `Memcached` protocol specification. The code is clean, full documented and developed following the PSR coding standards (PSR-0/4, PSR-1, PSR-2). The code is unit-tested (PHPUnit) and the coverage is high. The library supports \ and \ command on stored integers (strings) and the [connection handling is done like recommended](https://github.com/memcached/memcached/blob/master/doc/protocol.txt#L10 "Keep connections open and share them via a pool across instances.") in the `Memcached` protocol specification. Last but not least it supports seven of [PHP's eight variable types](http://php.net/manual/en/language.types.intro.php "PHP's variable types") - in detail four scalar types: boolean integer @@ -68,7 +68,7 @@ $client->set('foo', 1.00); // Returns 1.00 as PHP's type float! $client->get('foo'); ``` -You will find a demonstration `Demo.php` showing in detail how to use the **Memcached.php** `client`. +You will find a demonstration `Demo.php` showing in detail how to use the **memcached-php** `client`. ## Requirements @@ -92,22 +92,22 @@ For a consistent versioning i decided to make use of `Semantic Versioning 2.0.0` - [x] `>= 90%` test coverage - [ ] Security check through 3rd-Party (Please get in contact with me) -[![Throughput Graph](https://graphs.waffle.io/clickalicious/Memcached.php/throughput.svg)](https://waffle.io/clickalicious/Memcached.php/metrics) +[![Throughput Graph](https://graphs.waffle.io/clickalicious/memcached-php/throughput.svg)](https://waffle.io/clickalicious/memcached-php/metrics) ## Installation -The recommended way to install this library is through [Composer](http://getcomposer.org/). Require the `clickalicious/memcached.php` package into your `composer.json` file: +The recommended way to install this library is through [Composer](http://getcomposer.org/). Require the `clickalicious/memcached-php` package into your `composer.json` file: ```json { "require": { - "clickalicious/memcached.php": "~0.1" + "clickalicious/memcached-php": "~0.1" } } ``` -**Memcached.php** is also available as [download from github packed as zip-file](https://github.com/clickalicious/Memcached.php/archive/master.zip "zip package containing library for download") or via `git clone https://github.com/clickalicious/Memcached.php.git .` +**memcached-php** is also available as [download from github packed as zip-file](https://github.com/clickalicious/memcached-php/archive/master.zip "zip package containing library for download") or via `git clone https://github.com/clickalicious/memcached-php.git .` ## Data @@ -122,17 +122,17 @@ The recommended way to install this library is through [Composer](http://getcomp of 16, but you might want to restrict yourself to 16 bits for compatibility with older versions. -**Memcached.php** uses this field for its meta data. The meta data is required to mark data for serialization and stuff like this. This meta data is stored via the clients` flags field. The lower first **8 Bits** (*lowest Byte*) are reserved by **Memcached.php**. The other 8 Bits (half of the 16 Bits) can be used by your app. +**memcached-php** uses this field for its meta data. The meta data is required to mark data for serialization and stuff like this. This meta data is stored via the clients` flags field. The lower first **8 Bits** (*lowest Byte*) are reserved by **memcached-php**. The other 8 Bits (half of the 16 Bits) can be used by your app. ## Documentation -The best and currently only existing documentation is the inline documentation of this project. So please have a look at the source to understand how **Memcached.php** works internally. +The best and currently only existing documentation is the inline documentation of this project. So please have a look at the source to understand how **memcached-php** works internally. ## Tests -**Memcached.php** is unit-tested and the code coverage is high. For an in-detail view have a look at this always up to date [Code Coverage report](http://clickalicious.github.io/Memcached.php/dashboard.html "Code Coverage"). +**memcached-php** is unit-tested and the code coverage is high. For an in-detail view have a look at this always up to date [Code Coverage report](http://clickalicious.github.io/memcached-php/dashboard.html "Code Coverage"). Running the Tests You will find a PHPUnit configuration including testsuites in directory `tests/`. To run those configuration execute the following command on `cli`: @@ -167,7 +167,7 @@ If you are interested in any of these features too - please let me know. Maybe w ## Participate & share ... yeah. If you're a code monkey too - maybe we can build a force ;) If you would like to participate in either **Code**, **Comments**, **Documentation**, **Wiki**, **Bug-Reports**, **Unit-Tests**, **Bug-Fixes**, **Feedback** and/or **Critic** then please let me know as well! - + diff --git a/composer.json b/composer.json index e3f1607..2ad5914 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { - "name": "clickalicious/memcached.php", - "description": "Memcached.php - Plain vanilla PHP Memcached client with full support of Memcached protocol.", + "name": "clickalicious/memcached-php", + "description": "Plain vanilla PHP Memcached client library with full support of Memcached ASCII protocol.", "type": "library", "authors": [ { @@ -13,19 +13,25 @@ "minimum-stability": "stable", "prefer-stable": true, "require": { - "php": ">=5.4.0", - "gpupo/cache": "^1.2" + "php": ">=5.6.0", + "roave/security-advisories": "dev-master as 1.0.x-dev", + "gpupo/cache": "^1.3" }, "require-dev": { - "satooshi/php-coveralls": "^1.0", - "phpunit/phpunit": "^4.8" + "phpunit/phpunit": "^5.7", + "codacy/coverage": "^1.0.5", + "symfony/var-dumper": "^3.2" }, "autoload": { - "psr-4": { - "Clickalicious\\": "src/Clickalicious/" - } + "psr-4": { + "Clickalicious\\": "src/Clickalicious/" + } + }, + "autoload-dev": { + "psr-4": { + "Clickalicious\\": "src/Clickalicious/" + } }, - "include-path": ["src/"], "keywords": [ "Memcached", "Client", @@ -35,15 +41,16 @@ "inmemory", "nosql" ], - "homepage": "https://github.com/clickalicious/Memcached.php", - "license": "BSD-3-Clause", + "homepage": "https://github.com/clickalicious/memcached-php", + "license": "MIT", "support": { "email": "opensource@clickalicious.de", - "issues": "https://github.com/clickalicious/Memcached.php/issues", - "wiki": "https://github.com/clickalicious/Memcached.php/wiki" + "issues": "https://github.com/clickalicious/memcached-php/issues", + "wiki": "https://github.com/clickalicious/memcached-php/wiki", + "tests": "https://clickalicious.github.io/memcached-php" }, "config" : { "bin-dir" : "bin", - "preferred-install": "dist" + "preferred-install": "source" } } diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..04d644a --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,23 @@ + + + + + + ./tests + + + + + + ./src + + ./bin + ./docs + ./vendor + ./tests + + + + diff --git a/src/Clickalicious/Memcached/Autoloader.php b/src/Clickalicious/Memcached/Autoloader.php deleted file mode 100644 index d7d003d..0000000 --- a/src/Clickalicious/Memcached/Autoloader.php +++ /dev/null @@ -1,211 +0,0 @@ - - * @copyright 2015 Benjamin Carl - * @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause - * @version Git: $Id$ - * @link https://github.com/clickalicious/Memcached.php - */ - -/** - * Memcached.php - * - * Autoloader.php - Autoloader of Memcached.php. - * - * @category Clickalicious - * @package Clickalicious_Memcached - * @subpackage Clickalicious_Memcached_Autoloader - * @author Benjamin Carl - * @copyright 2015 Benjamin Carl - * @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause - * @version Git: $Id$ - * @link https://github.com/clickalicious/Memcached.php - */ -class Autoloader -{ - /** - * An associative array where the key is a namespace prefix and the value - * is an array of base directories for classes in that namespace. - * - * @var array - */ - protected $prefixes = array(); - - /** - * Register loader with SPL autoloader stack. - * - * @return void - */ - public function register() - { - spl_autoload_register(array($this, 'loadClass')); - } - - /** - * Adds a base directory for a namespace prefix. - * - * @param string $prefix The namespace prefix. - * @param string $base_dir A base directory for class files in the - * namespace. - * @param bool $prepend If true, prepend the base directory to the stack - * instead of appending it; this causes it to be searched first rather - * than last. - * @return void - */ - public function addNamespace($prefix, $base_dir, $prepend = false) - { - // normalize namespace prefix - $prefix = trim($prefix, '\\') . '\\'; - - // normalize the base directory with a trailing separator - $base_dir = rtrim($base_dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; - - // initialize the namespace prefix array - if (isset($this->prefixes[$prefix]) === false) { - $this->prefixes[$prefix] = array(); - } - - // retain the base directory for the namespace prefix - if ($prepend === true) { - array_unshift($this->prefixes[$prefix], $base_dir); - } else { - array_push($this->prefixes[$prefix], $base_dir); - } - } - - /** - * Loads the class file for a given class name. - * - * @param string $class The fully-qualified class name. - * @return mixed The mapped file name on success, or boolean false on - * failure. - */ - public function loadClass($class) - { - // the current namespace prefix - $prefix = $class; - - // work backwards through the namespace names of the fully-qualified - // class name to find a mapped file name - while (false !== $pos = strrpos($prefix, '\\')) { - - // retain the trailing namespace separator in the prefix - $prefix = substr($class, 0, $pos + 1); - - // the rest is the relative class name - $relative_class = substr($class, $pos + 1); - - // try to load a mapped file for the prefix and relative class - $mapped_file = $this->loadMappedFile($prefix, $relative_class); - - if ($mapped_file !== false) { - return $mapped_file; - } - - // remove the trailing namespace separator for the next iteration - // of strrpos() - $prefix = rtrim($prefix, '\\'); - } - - // never found a mapped file - return false; - } - - /** - * Load the mapped file for a namespace prefix and relative class. - * - * @param string $prefix The namespace prefix. - * @param string $relative_class The relative class name. - * - * @return mixed Boolean false if no mapped file can be loaded, or the name of the mapped file that was loaded. - */ - protected function loadMappedFile($prefix, $relative_class) - { - // are there any base directories for this namespace prefix? - if (isset($this->prefixes[$prefix]) === false) { - return false; - } - - // look through base directories for this namespace prefix - foreach ($this->prefixes[$prefix] as $base_dir) { - - // replace the namespace prefix with the base directory, - // replace namespace separators with directory separators - // in the relative class name, append with .php - $file = $base_dir - . str_replace('\\', '/', $relative_class) - . '.php'; - - // if the mapped file exists, require it - if ($this->requireFile($file)) { - // yes, we're done - return $file; - } - } - - // never found it - return false; - } - - /** - * If a file exists, require it from the file system. - * - * @param string $file The file to require. - * @return bool True if the file exists, false if not. - */ - protected function requireFile($file) - { - if (file_exists($file)) { - require $file; - return true; - } - return false; - } -} diff --git a/src/Clickalicious/Memcached/Bootstrap.php b/src/Clickalicious/Memcached/Bootstrap.php deleted file mode 100644 index 666f395..0000000 --- a/src/Clickalicious/Memcached/Bootstrap.php +++ /dev/null @@ -1,122 +0,0 @@ - - * @copyright 2014 - 2015 Benjamin Carl - * @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause - * @version Git: $Id$ - * @link https://github.com/clickalicious/Memcached.php - */ - -// Include autoloader -require_once 'Autoloader.php'; - -/** - * Detects composer in global scope - * - * @author Benjamin Carl - * @return bool TRUE if composer is active, otherwise FALSE - * @access public - */ -function composer_running() -{ - $result = false; - $classes = get_declared_classes(); - natsort($classes); - foreach ($classes as $class) { - if (stristr($class, 'ComposerAutoloaderInit')) { - $result = true; - break; - } - } - - return $result; -} - - -// The base path to /src/ if we don't have Composer we need to know root path -define( - 'CLICKALICIOUS_MEMCACHED_BASE_PATH', - realpath(dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR) . - DIRECTORY_SEPARATOR -); - -// Root node -$root = realpath(CLICKALICIOUS_MEMCACHED_BASE_PATH . '../'); - -// Check for composer existence -if (true === $composerExist = $composerRunning = file_exists($root . '/vendor/autoload.php')) { - include_once $root . '/vendor/autoload.php'; - -} else { - $composerExist = $composerRunning = composer_running(); -} - -// No need to double detect and so on ... -define( - 'CLICKALICIOUS_MEMCACHED_COMPOSER_EXISTS', - $composerExist -); - -define( - 'CLICKALICIOUS_MEMCACHED_COMPOSER_RUNNING', - $composerRunning -); - -// Force reporting of all errors ... -error_reporting(-1); - -// Init autoloading -$loader = new Autoloader(); - -// register the autoloader -$loader->register(); - -// register the base directories for the namespace prefix -$loader->addNamespace('Clickalicious\Memcached', CLICKALICIOUS_MEMCACHED_BASE_PATH . 'Clickalicious\Memcached'); diff --git a/src/Clickalicious/Memcached/Cache.php b/src/Clickalicious/Memcached/Cache.php deleted file mode 100644 index 1b3768e..0000000 --- a/src/Clickalicious/Memcached/Cache.php +++ /dev/null @@ -1,153 +0,0 @@ - - * @copyright 2014 - 2015 Benjamin Carl - * @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause - * @version Git: $Id$ - * @link https://github.com/clickalicious/Memcached.php - */ - -use Psr\Cache\CacheItemInterface; - -/** - * Memcached.php - * - * PSR compatbile Cache-Client() based on Client() - Plain vanilla PHP Memcached client with full - * support of Memcached protocol. - * - * @category Clickalicious - * @package Clickalicious_Memcached - * @subpackage Clickalicious_Memcached_Cache - * @author Benjamin Carl - * @copyright 2014 - 2015 Benjamin Carl - * @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause - * @version Git: $Id$ - * @link https://github.com/clickalicious/Memcached.php - */ -class Cache extends Client implements CacheItemInterface -{ - /** - * Returns the key for the current cache item. - * - * The key is loaded by the Implementing Library, but should be available to - * the higher level callers when needed. - * - * @return string - * The key string for this cache item. - */ - public function getKey() - { - - } - - /** - * Confirms if the cache item lookup resulted in a cache hit. - * - * Note: This method MUST NOT have a race condition between calling isHit() - * and calling get(). - * - * @return boolean - * True if the request resulted in a cache hit. False otherwise. - */ - public function isHit() - { - - } - - /** - * Confirms if the cache item exists in the cache. - * - * Note: This method MAY avoid retrieving the cached value for performance - * reasons, which could result in a race condition between exists() and get(). - * To avoid that potential race condition use isHit() instead. - * - * @return boolean - * True if item exists in the cache, false otherwise. - */ - public function exists() - { - - } - - /** - * Sets the expiration for this cache item. - * - * @param int|\DateTime $ttl - * - If an integer is passed, it is interpreted as the number of seconds - * after which the item MUST be considered expired. - * - If a DateTime object is passed, it is interpreted as the point in - * time after which the item MUST be considered expired. - * - If null is passed, a default value MAY be used. If none is set, - * the value should be stored permanently or for as long as the - * implementation allows. - * - * @return static - * The called object. - */ - public function setExpiration($ttl = null) - { - - } - - /** - * Returns the expiration time of a not-yet-expired cache item. - * - * If this cache item is a Cache Miss, this method MAY return the time at - * which the item expired or the current time if that is not available. - * - * @return \DateTime - * The timestamp at which this cache item will expire. - */ - public function getExpiration() - { - - } -} diff --git a/src/Clickalicious/Memcached/Compression/CompressionInterface.php b/src/Clickalicious/Memcached/Compression/CompressionInterface.php deleted file mode 100644 index 90e299c..0000000 --- a/src/Clickalicious/Memcached/Compression/CompressionInterface.php +++ /dev/null @@ -1,96 +0,0 @@ - - * @copyright 2014 - 2015 Benjamin Carl - * @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause - * @version Git: $Id$ - * @link https://github.com/clickalicious/Memcached.php - * @see http://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch - */ - -/** - * Memcached.php - * - * Compression interface. - * - * @category Clickalicious - * @package Clickalicious_Memcached - * @subpackage Clickalicious_Memcached_Compression - * @author Benjamin Carl - * @copyright 2014 - 2015 Benjamin Carl - * @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause - * @version Git: $Id$ - * @link https://github.com/clickalicious/Memcached.php - * @see http://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch - */ -interface CompressionInterface -{ - /** - * Compresses a buffer. - * - * @param string $buffer The buffer to compress - * - * @author Benjamin Carl - * @return string The compressed input - * @access protected - */ - public function compress($buffer); - - /** - * Decompresses a buffer. - * - * @param string $buffer The buffer to decompress - * - * @author Benjamin Carl - * @return string The decompressed input - * @access protected - */ - public function decompress($buffer); -} diff --git a/src/Clickalicious/Memcached/Compression/Zlib.php b/src/Clickalicious/Memcached/Compression/Zlib.php deleted file mode 100644 index 92bf858..0000000 --- a/src/Clickalicious/Memcached/Compression/Zlib.php +++ /dev/null @@ -1,106 +0,0 @@ - - * @copyright 2014 - 2015 Benjamin Carl - * @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause - * @version Git: $Id$ - * @link https://github.com/clickalicious/Memcached.php - * @see - - */ - -use Clickalicious\Memcached\Compression\CompressionInterface; - -/** - * Memcached.php - * - * ZLIB-compression. - * - * @category Clickalicious - * @package Clickalicious_Memcached - * @subpackage Clickalicious_Memcached_Compression - * @author Benjamin Carl - * @copyright 2014 - 2015 Benjamin Carl - * @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause - * @version Git: $Id$ - * @link https://github.com/clickalicious/Memcached.php - * @see - - */ -class Zlib implements CompressionInterface -{ - /** - * Compresses a buffer. - * - * @param string $buffer The buffer to compress - * - * @author Benjamin Carl - * @return string The compressed input - * @access protected - */ - public function compress($buffer) - { - return gzcompress($buffer); - } - - /** - * Decompresses a buffer. - * - * @param string $buffer The buffer to decompress - * - * @author Benjamin Carl - * @return string The decompressed input - * @access protected - */ - public function decompress($buffer) - { - return gzuncompress($buffer); - } -} diff --git a/src/Clickalicious/Memcached/Exception.php b/src/Clickalicious/Memcached/Exception.php deleted file mode 100644 index 2911b35..0000000 --- a/src/Clickalicious/Memcached/Exception.php +++ /dev/null @@ -1,74 +0,0 @@ - - * @copyright 2014 - 2015 Benjamin Carl - * @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause - * @version Git: $Id$ - * @link https://github.com/clickalicious/Memcached.php - */ - -/** - * Memcached.php - * - * Exception of Memcached.php package. - * - * @category Clickalicious - * @package Clickalicious_Memcached - * @subpackage Clickalicious_Memcached_Exception - * @author Benjamin Carl - * @copyright 2014 - 2015 Benjamin Carl - * @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause - * @version Git: $Id$ - * @link https://github.com/clickalicious/Memcached.php - */ -class Exception extends \Exception -{ - // Namespace -} diff --git a/src/Clickalicious/Memcached/Php/Cache.php b/src/Clickalicious/Memcached/Php/Cache.php new file mode 100644 index 0000000..543dc8f --- /dev/null +++ b/src/Clickalicious/Memcached/Php/Cache.php @@ -0,0 +1,126 @@ + + */ +class Cache extends Client implements CacheItemInterface +{ + /** + * Returns the key for the current cache item. + * + * The key is loaded by the Implementing Library, but should be available to + * the higher level callers when needed. + * + * @return string + * The key string for this cache item. + */ + public function getKey() + { + + } + + /** + * Confirms if the cache item lookup resulted in a cache hit. + * + * Note: This method MUST NOT have a race condition between calling isHit() + * and calling get(). + * + * @return boolean + * True if the request resulted in a cache hit. False otherwise. + */ + public function isHit() + { + + } + + /** + * Confirms if the cache item exists in the cache. + * + * Note: This method MAY avoid retrieving the cached value for performance + * reasons, which could result in a race condition between exists() and get(). + * To avoid that potential race condition use isHit() instead. + * + * @return boolean + * True if item exists in the cache, false otherwise. + */ + public function exists() + { + + } + + /** + * Sets the expiration for this cache item. + * + * @param int|\DateTime $ttl + * - If an integer is passed, it is interpreted as the number of seconds + * after which the item MUST be considered expired. + * - If a DateTime object is passed, it is interpreted as the point in + * time after which the item MUST be considered expired. + * - If null is passed, a default value MAY be used. If none is set, + * the value should be stored permanently or for as long as the + * implementation allows. + * + * @return static + * The called object. + */ + public function setExpiration($ttl = null) + { + + } + + /** + * Returns the expiration time of a not-yet-expired cache item. + * + * If this cache item is a Cache Miss, this method MAY return the time at + * which the item expired or the current time if that is not available. + * + * @return \DateTime + * The timestamp at which this cache item will expire. + */ + public function getExpiration() + { + + } + + public function expiresAfter($time) + { + // TODO: Implement expiresAfter() method. + } + + public function expiresAt($expiration) + { + // TODO: Implement expiresAt() method. + } +} diff --git a/src/Clickalicious/Memcached/Client.php b/src/Clickalicious/Memcached/Php/Client.php similarity index 95% rename from src/Clickalicious/Memcached/Client.php rename to src/Clickalicious/Memcached/Php/Client.php index 150b6c1..fffac69 100644 --- a/src/Clickalicious/Memcached/Client.php +++ b/src/Clickalicious/Memcached/Php/Client.php @@ -1,74 +1,37 @@ - * @copyright 2014 - 2015 Benjamin Carl - * @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause - * @version Git: $Id$ - * @link https://github.com/clickalicious/Memcached.php + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ -use Clickalicious\Memcached\Exception; +namespace Clickalicious\Memcached\Php; /** - * Memcached.php - * - * Plain vanilla PHP Memcached client with full support of Memcached protocol. + * Class Client * - * @category Clickalicious - * @package Clickalicious_Memcached - * @subpackage Clickalicious_Memcached_Client - * @author Benjamin Carl - * @copyright 2014 - 2015 Benjamin Carl - * @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause - * @version Git: $Id$ - * @link https://github.com/clickalicious/Memcached.php + * @package Clickalicious\Memcached\Php + * @author Benjamin Carl */ class Client { @@ -929,7 +892,8 @@ public function getTimeout() * @author Benjamin Carl * @return resource|null The resource (socket) on success, otherwise NULL * @access public - * @throws \Clickalicious\Memcached\Exception + * + * @throws \Clickalicious\Memcached\Php\Exception */ public function connect($host, $port, $timeout = null) { @@ -1871,7 +1835,7 @@ protected function parseReadResponse($buffer, array $lines) if($lines[$line + $frame] === self::RESPONSE_END && !isset($lines[$line + $frame+1])) { $frame_break = true; break; - } + } $value .= $lines[$line + $frame]; } } else { diff --git a/src/Clickalicious/Memcached/Php/Compression/CompressionInterface.php b/src/Clickalicious/Memcached/Php/Compression/CompressionInterface.php new file mode 100644 index 0000000..b4b0550 --- /dev/null +++ b/src/Clickalicious/Memcached/Php/Compression/CompressionInterface.php @@ -0,0 +1,59 @@ + + */ +interface CompressionInterface +{ + /** + * Compresses a buffer. + * + * @param string $buffer The buffer to compress + * + * @author Benjamin Carl + * @return string The compressed input + * @access protected + */ + public function compress($buffer); + + /** + * Decompresses a buffer. + * + * @param string $buffer The buffer to decompress + * + * @author Benjamin Carl + * @return string The decompressed input + * @access protected + */ + public function decompress($buffer); +} diff --git a/src/Clickalicious/Memcached/Compression/Lzw.php b/src/Clickalicious/Memcached/Php/Compression/Lzw.php similarity index 62% rename from src/Clickalicious/Memcached/Compression/Lzw.php rename to src/Clickalicious/Memcached/Php/Compression/Lzw.php index 88c0469..e61c5ea 100644 --- a/src/Clickalicious/Memcached/Compression/Lzw.php +++ b/src/Clickalicious/Memcached/Php/Compression/Lzw.php @@ -1,10 +1,36 @@ - * @copyright 2014 - 2015 Benjamin Carl - * @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause - * @version Git: $Id$ - * @link https://github.com/clickalicious/Memcached.php - * @see http://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch - */ - -use Clickalicious\Memcached\Compression\CompressionInterface; -use Clickalicious\Memcached\Exception; - -/** - * Memcached.php - * - * LZW-compression (LZW = Lempel–Ziv–Welch). - * - * @category Clickalicious - * @package Clickalicious_Memcached - * @subpackage Clickalicious_Memcached_Compression - * @author Benjamin Carl - * @copyright 2014 - 2015 Benjamin Carl - * @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause - * @version Git: $Id$ - * @link https://github.com/clickalicious/Memcached.php - * @see http://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch + * @package Clickalicious\Memcached\Php\Compression + * @author Benjamin Carl */ class Lzw implements CompressionInterface { diff --git a/src/Clickalicious/Memcached/Compression/Smaz.php b/src/Clickalicious/Memcached/Php/Compression/Smaz.php similarity index 68% rename from src/Clickalicious/Memcached/Compression/Smaz.php rename to src/Clickalicious/Memcached/Php/Compression/Smaz.php index 76e14dd..5f4b05a 100644 --- a/src/Clickalicious/Memcached/Compression/Smaz.php +++ b/src/Clickalicious/Memcached/Php/Compression/Smaz.php @@ -1,10 +1,34 @@ - * @copyright 2014 - 2015 Benjamin Carl - * @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause - * @version Git: $Id$ - * @link https://github.com/clickalicious/Memcached.php - * @see https://github.com/zhenhao/smaz.php - */ - -use Clickalicious\Memcached\Compression\CompressionInterface; - -/** - * Memcached.php - * - * SMAZ - compression for very small strings - * - * @category Clickalicious - * @package Clickalicious_Memcached - * @subpackage Clickalicious_Memcached_Compression - * @author Benjamin Carl - * @copyright 2014 - 2015 Benjamin Carl - * @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause - * @version Git: $Id$ - * @link https://github.com/clickalicious/Memcached.php - * @see https://github.com/zhenhao/smaz.php + * @package Clickalicious\Memcached\Php\Compression + * @author Benjamin Carl */ class Smaz implements CompressionInterface { diff --git a/src/Clickalicious/Memcached/Php/Compression/Zlib.php b/src/Clickalicious/Memcached/Php/Compression/Zlib.php new file mode 100644 index 0000000..b6e86ae --- /dev/null +++ b/src/Clickalicious/Memcached/Php/Compression/Zlib.php @@ -0,0 +1,69 @@ + + */ +class Zlib implements CompressionInterface +{ + /** + * Compresses a buffer. + * + * @param string $buffer The buffer to compress + * + * @author Benjamin Carl + * @return string The compressed input + * @access protected + */ + public function compress($buffer) + { + return gzcompress($buffer); + } + + /** + * Decompresses a buffer. + * + * @param string $buffer The buffer to decompress + * + * @author Benjamin Carl + * @return string The decompressed input + * @access protected + */ + public function decompress($buffer) + { + return gzuncompress($buffer); + } +} diff --git a/src/Clickalicious/Memcached/Php/Exception.php b/src/Clickalicious/Memcached/Php/Exception.php new file mode 100644 index 0000000..6969b01 --- /dev/null +++ b/src/Clickalicious/Memcached/Php/Exception.php @@ -0,0 +1,39 @@ + + */ +class Exception extends \Exception +{ + // Intentionally left empty. +} diff --git a/src/Clickalicious/Memcached/Util.php b/src/Clickalicious/Memcached/Php/Util.php similarity index 74% rename from src/Clickalicious/Memcached/Util.php rename to src/Clickalicious/Memcached/Php/Util.php index d533d1b..3da6af2 100644 --- a/src/Clickalicious/Memcached/Util.php +++ b/src/Clickalicious/Memcached/Php/Util.php @@ -1,60 +1,32 @@ 5.3. array_column() for example. - * - * - * PHP versions 5.3 - * - * LICENSE: - * Memcached.php - Plain vanilla PHP Memcached client with full support of Memcached protocol. - * - * Copyright (c) 2014 - 2015, Benjamin Carl - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * - Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * - Neither the name of Memcached.php nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Please feel free to contact us via e-mail: opensource@clickalicious.de - * - * @category Clickalicious - * @package Clickalicious_Memcached - * @subpackage Clickalicious_Memcached_Util - * @author Benjamin Carl - * @copyright 2014 - 2015 Benjamin Carl - * @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause - * @version Git: $Id$ - * @link https://github.com/clickalicious/Memcached.php + * (The MIT license) + * Copyright 2017 clickalicious, Benjamin Carl + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ +namespace Clickalicious\Memcached\Php; + /*---------------------------------------------------------------------------------------------------------------------- | General Tools & Helper +---------------------------------------------------------------------------------------------------------------------*/ diff --git a/tests/Clickalicious/Memcached/ClientTest.php b/tests/Clickalicious/Memcached/Php/ClientTest.php similarity index 84% rename from tests/Clickalicious/Memcached/ClientTest.php rename to tests/Clickalicious/Memcached/Php/ClientTest.php index a603a24..c5d4db8 100644 --- a/tests/Clickalicious/Memcached/ClientTest.php +++ b/tests/Clickalicious/Memcached/Php/ClientTest.php @@ -1,74 +1,39 @@ - * @copyright 2014 - 2015 Benjamin Carl - * @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause - * @version Git: $Id$ - * @link https://github.com/clickalicious/Memcached.php + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ -use Clickalicious\Memcached\Client; +namespace Clickalicious\Memcached\Php; /** - * Memcached.php - * - * Unit tests for client functionality. + * Class ClientTest * - * @category Clickalicious - * @package Clickalicious_Memcached - * @subpackage Clickalicious_Memcached_Tests - * @author Benjamin Carl - * @copyright 2014 - 2015 Benjamin Carl - * @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause - * @version Git: $Id$ - * @link https://github.com/clickalicious/Memcached.php + * @package Clickalicious\Memcached + * @author Benjamin Carl */ -class ClientTest extends PHPUnit_Framework_TestCase +class ClientTest extends \PHPUnit_Framework_TestCase { /** * The Host used for testing. diff --git a/tests/Clickalicious/Memcached/UtilTest.php b/tests/Clickalicious/Memcached/Php/UtilTest.php similarity index 61% rename from tests/Clickalicious/Memcached/UtilTest.php rename to tests/Clickalicious/Memcached/Php/UtilTest.php index 360d93e..61d2876 100644 --- a/tests/Clickalicious/Memcached/UtilTest.php +++ b/tests/Clickalicious/Memcached/Php/UtilTest.php @@ -1,77 +1,42 @@ - * @copyright 2014 - 2015 Benjamin Carl - * @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause - * @version Git: $Id$ - * @link https://github.com/clickalicious/Memcached.php + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ -require_once CLICKALICIOUS_MEMCACHED_BASE_PATH . 'Clickalicious/Memcached/Util.php'; +namespace Clickalicious\Memcached\Php; /** - * Memcached.php - * - * Unit tests for util functionality. + * Class UtilTest * - * @category Clickalicious - * @package Clickalicious_Memcached - * @subpackage Clickalicious_Memcached_Tests - * @author Benjamin Carl - * @copyright 2014 - 2015 Benjamin Carl - * @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause - * @version Git: $Id$ - * @link https://github.com/clickalicious/Memcached.php + * @package Clickalicious\Memcached\Php + * @author Benjamin Carl */ -class UtilTest extends PHPUnit_Framework_TestCase +class UtilTest extends \PHPUnit_Framework_TestCase { /** - * The PHP version running on. + * PHP version running on. * * @var string * @access protected @@ -154,7 +119,7 @@ public function testArrayColumnEmulation() [3] => Peter ) */ - $data = \Clickalicious\Memcached\array_column_emulation($this->data, 'first_name'); + $data = \Clickalicious\Memcached\Php\array_column_emulation($this->data, 'first_name'); $this->assertContains('John', $data); $this->assertContains('Sally', $data); @@ -185,7 +150,7 @@ public function testArrayColumnEmulationCustomIndex() [5623] => Doe ) */ - $data = \Clickalicious\Memcached\array_column_emulation($this->data, 'first_name', 'id'); + $data = \Clickalicious\Memcached\Php\array_column_emulation($this->data, 'first_name', 'id'); $this->assertContains('John', $data); $this->assertContains('Sally', $data); @@ -216,7 +181,7 @@ public function testArrayColumnEmulationWrongColumnString() [5623] => Doe ) */ - $data = \Clickalicious\Memcached\array_column_emulation($this->data, 'foo'); + $data = \Clickalicious\Memcached\Php\array_column_emulation($this->data, 'foo'); $this->assertArrayHasKey(0, $data); $this->assertArrayHasKey(1, $data); $this->assertArrayHasKey(2, $data); @@ -241,7 +206,7 @@ public function testArrayColumnEmulationCustomIndexInt() [5623] => Doe ) */ - $data = \Clickalicious\Memcached\array_column_emulation($this->data, 'first_name', 1); + $data = \Clickalicious\Memcached\Php\array_column_emulation($this->data, 'first_name', 1); $this->assertArrayHasKey(0, $data); $this->assertArrayHasKey(1, $data); $this->assertArrayHasKey(2, $data); @@ -266,7 +231,7 @@ public function testArrayColumnEmulationCustomIndexString() [5623] => Doe ) */ - $data = \Clickalicious\Memcached\array_column_emulation($this->data, 'first_name', 'foo'); + $data = \Clickalicious\Memcached\Php\array_column_emulation($this->data, 'first_name', 'foo'); $this->assertArrayHasKey(0, $data); $this->assertArrayHasKey(1, $data); $this->assertArrayHasKey(2, $data); @@ -283,7 +248,7 @@ public function testArrayColumnEmulationCustomIndexString() */ public function testArrayColumnEmulationErrorHandlingWrongSecondArgument() { - \Clickalicious\Memcached\array_column_emulation($this->data, new stdClass()); + \Clickalicious\Memcached\Php\array_column_emulation($this->data, new stdClass()); } /** @@ -296,7 +261,7 @@ public function testArrayColumnEmulationErrorHandlingWrongSecondArgument() */ public function testArrayColumnEmulationErrorHandlingWrongThirdArgument() { - \Clickalicious\Memcached\array_column_emulation($this->data, 'first_name', new stdClass()); + \Clickalicious\Memcached\Php\array_column_emulation($this->data, 'first_name', new stdClass()); } /** @@ -318,7 +283,7 @@ public function testArrayColumnProxy() [3] => Peter ) */ - $data = \Clickalicious\Memcached\array_column($this->data, 'first_name'); + $data = \Clickalicious\Memcached\Php\array_column($this->data, 'first_name'); $this->assertContains('John', $data); $this->assertContains('Sally', $data); @@ -348,30 +313,30 @@ public function testArrayColumnProxy() public function testBoolvalEmulation() { $data = true; - $this->assertTrue(\Clickalicious\Memcached\boolval_emulation($data)); + $this->assertTrue(\Clickalicious\Memcached\Php\boolval_emulation($data)); $data = 1; - $this->assertTrue(\Clickalicious\Memcached\boolval_emulation($data)); + $this->assertTrue(\Clickalicious\Memcached\Php\boolval_emulation($data)); $data = 'TRUE'; - $this->assertTrue(\Clickalicious\Memcached\boolval_emulation($data)); + $this->assertTrue(\Clickalicious\Memcached\Php\boolval_emulation($data)); $data = 'YES'; - $this->assertTrue(\Clickalicious\Memcached\boolval_emulation($data)); + $this->assertTrue(\Clickalicious\Memcached\Php\boolval_emulation($data)); $data = 'Y'; - $this->assertTrue(\Clickalicious\Memcached\boolval_emulation($data)); + $this->assertTrue(\Clickalicious\Memcached\Php\boolval_emulation($data)); $data = 'ON'; - $this->assertTrue(\Clickalicious\Memcached\boolval_emulation($data)); + $this->assertTrue(\Clickalicious\Memcached\Php\boolval_emulation($data)); $data = '1'; - $this->assertTrue(\Clickalicious\Memcached\boolval_emulation($data)); + $this->assertTrue(\Clickalicious\Memcached\Php\boolval_emulation($data)); $data = '-1'; - $this->assertTrue(\Clickalicious\Memcached\boolval_emulation($data)); + $this->assertTrue(\Clickalicious\Memcached\Php\boolval_emulation($data)); $data = false; - $this->assertFalse(\Clickalicious\Memcached\boolval_emulation($data)); + $this->assertFalse(\Clickalicious\Memcached\Php\boolval_emulation($data)); } } diff --git a/tests/phpunit.xml b/tests/phpunit.xml deleted file mode 100644 index 6cbf3d3..0000000 --- a/tests/phpunit.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - . - - - - - - ../src - - ../vendor - ../src/Clickalicious/Memcached/Autoloader.php - ../src/Clickalicious/Memcached/Bootstrap.php - ../src/Clickalicious/Memcached/Cache.php - ../src/Clickalicious/Memcached/Exception.php - ../src/Clickalicious/Memcached/Compression - - - - diff --git a/update-gh-pages.sh b/update-gh-pages.sh index 14dd9a1..07dd9bd 100644 --- a/update-gh-pages.sh +++ b/update-gh-pages.sh @@ -11,7 +11,7 @@ if [ $TRAVIS_PULL_REQUEST == 'false' ]; then git config --global user.name "Travis" #using token clone gh-pages branch - git clone --quiet --branch=gh-pages https://$GITHUBKEY@github.com/clickalicious/Memcached.php.git gh-pages > /dev/null 2>&1 + git clone --quiet --branch=gh-pages https://$GITHUBKEY@github.com/clickalicious/memcached-php.git gh-pages > /dev/null 2>&1 #go into diractory and copy data we're interested in to that directory cd gh-pages From 2ae453cbeae2e7483b00dac3a889152b66cb6866 Mon Sep 17 00:00:00 2001 From: Benjamin Carl Date: Sat, 15 Apr 2017 10:16:27 +0200 Subject: [PATCH 02/12] First working upgraded version. --- composer.json | 6 ++++-- .../Clickalicious/Memcached/Php/ClientTest.php | 12 ++++++------ tests/Clickalicious/Memcached/Php/UtilTest.php | 18 +++++++++--------- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/composer.json b/composer.json index 2ad5914..8111215 100644 --- a/composer.json +++ b/composer.json @@ -25,12 +25,14 @@ "autoload": { "psr-4": { "Clickalicious\\": "src/Clickalicious/" - } + }, + "files": ["src/Clickalicious/Memcached/Php/Util.php"] }, "autoload-dev": { "psr-4": { "Clickalicious\\": "src/Clickalicious/" - } + }, + "files": ["src/Clickalicious/Memcached/Php/Util.php"] }, "keywords": [ "Memcached", diff --git a/tests/Clickalicious/Memcached/Php/ClientTest.php b/tests/Clickalicious/Memcached/Php/ClientTest.php index c5d4db8..0a4bbcd 100644 --- a/tests/Clickalicious/Memcached/Php/ClientTest.php +++ b/tests/Clickalicious/Memcached/Php/ClientTest.php @@ -46,7 +46,7 @@ class ClientTest extends \PHPUnit_Framework_TestCase /** * Client instance * - * @var \Clickalicious\Memcached\Client + * @var \Clickalicious\Memcached\Php\Client * @access protected */ protected $client; @@ -264,7 +264,7 @@ public function testSendAValidCustomCommandString() * @author Benjamin Carl * @return void * @access protected - * @expectedException \Clickalicious\Memcached\Exception + * @expectedException \Clickalicious\Memcached\Php\Exception */ public function testSendAnInvalidCustomCommandString() { @@ -476,7 +476,7 @@ public function testDecrementAStoredValue() * @author Benjamin Carl * @return void * @access protected - * @expectedException \Clickalicious\Memcached\Exception + * @expectedException \Clickalicious\Memcached\Php\Exception */ public function testConnectToAMemcachedDaemon() { @@ -567,7 +567,7 @@ public function testRetrieveStats() * @author Benjamin Carl * @return void * @access protected - * @expectedException \Clickalicious\Memcached\Exception + * @expectedException \Clickalicious\Memcached\Php\Exception */ public function testTriggerAndHandleError() { @@ -580,7 +580,7 @@ public function testTriggerAndHandleError() * @author Benjamin Carl * @return void * @access protected - * @expectedException \Clickalicious\Memcached\Exception + * @expectedException \Clickalicious\Memcached\Php\Exception */ public function testTriggerAndHandleClientError() { @@ -593,7 +593,7 @@ public function testTriggerAndHandleClientError() * @author Benjamin Carl * @return void * @access protected - * @expectedException \Clickalicious\Memcached\Exception + * @expectedException \Clickalicious\Memcached\Php\Exception */ public function testTriggerAndHandleServerError() { diff --git a/tests/Clickalicious/Memcached/Php/UtilTest.php b/tests/Clickalicious/Memcached/Php/UtilTest.php index 61d2876..f336317 100644 --- a/tests/Clickalicious/Memcached/Php/UtilTest.php +++ b/tests/Clickalicious/Memcached/Php/UtilTest.php @@ -119,7 +119,7 @@ public function testArrayColumnEmulation() [3] => Peter ) */ - $data = \Clickalicious\Memcached\Php\array_column_emulation($this->data, 'first_name'); + $data = array_column_emulation($this->data, 'first_name'); $this->assertContains('John', $data); $this->assertContains('Sally', $data); @@ -150,7 +150,7 @@ public function testArrayColumnEmulationCustomIndex() [5623] => Doe ) */ - $data = \Clickalicious\Memcached\Php\array_column_emulation($this->data, 'first_name', 'id'); + $data = array_column_emulation($this->data, 'first_name', 'id'); $this->assertContains('John', $data); $this->assertContains('Sally', $data); @@ -181,7 +181,7 @@ public function testArrayColumnEmulationWrongColumnString() [5623] => Doe ) */ - $data = \Clickalicious\Memcached\Php\array_column_emulation($this->data, 'foo'); + $data = array_column_emulation($this->data, 'foo'); $this->assertArrayHasKey(0, $data); $this->assertArrayHasKey(1, $data); $this->assertArrayHasKey(2, $data); @@ -206,7 +206,7 @@ public function testArrayColumnEmulationCustomIndexInt() [5623] => Doe ) */ - $data = \Clickalicious\Memcached\Php\array_column_emulation($this->data, 'first_name', 1); + $data = array_column_emulation($this->data, 'first_name', 1); $this->assertArrayHasKey(0, $data); $this->assertArrayHasKey(1, $data); $this->assertArrayHasKey(2, $data); @@ -231,7 +231,7 @@ public function testArrayColumnEmulationCustomIndexString() [5623] => Doe ) */ - $data = \Clickalicious\Memcached\Php\array_column_emulation($this->data, 'first_name', 'foo'); + $data = array_column_emulation($this->data, 'first_name', 'foo'); $this->assertArrayHasKey(0, $data); $this->assertArrayHasKey(1, $data); $this->assertArrayHasKey(2, $data); @@ -244,11 +244,11 @@ public function testArrayColumnEmulationCustomIndexString() * @author Benjamin Carl * @return void * @access protected - * @expectedException PHPUnit_Framework_Error + * @expectedException \PHPUnit_Framework_Error */ public function testArrayColumnEmulationErrorHandlingWrongSecondArgument() { - \Clickalicious\Memcached\Php\array_column_emulation($this->data, new stdClass()); + array_column_emulation($this->data, new \stdClass()); } /** @@ -257,11 +257,11 @@ public function testArrayColumnEmulationErrorHandlingWrongSecondArgument() * @author Benjamin Carl * @return void * @access protected - * @expectedException PHPUnit_Framework_Error + * @expectedException \PHPUnit_Framework_Error */ public function testArrayColumnEmulationErrorHandlingWrongThirdArgument() { - \Clickalicious\Memcached\Php\array_column_emulation($this->data, 'first_name', new stdClass()); + array_column_emulation($this->data, 'first_name', new \stdClass()); } /** From a4a833da4795bac3816ad347d181b1bbe8aa113e Mon Sep 17 00:00:00 2001 From: Benjamin Carl Date: Sun, 16 Apr 2017 01:53:33 +0200 Subject: [PATCH 03/12] Documentation. --- Demo.php | 89 ++++++++++-------------------------------- README.md | 36 ++++++++--------- docs/color.txt | 1 + docs/logo-128x128.png | Bin 0 -> 4155 bytes docs/logo-16x16.png | Bin 0 -> 875 bytes docs/logo-24x24.png | Bin 0 -> 1059 bytes docs/logo-256x256.png | Bin 0 -> 7988 bytes docs/logo-32x32.png | Bin 0 -> 1294 bytes docs/logo-512x512.png | Bin 0 -> 16600 bytes docs/logo-64x64.png | Bin 0 -> 2019 bytes docs/logo-large.png | Bin 16696 -> 7988 bytes 11 files changed, 38 insertions(+), 88 deletions(-) create mode 100644 docs/color.txt create mode 100644 docs/logo-128x128.png create mode 100644 docs/logo-16x16.png create mode 100644 docs/logo-24x24.png create mode 100644 docs/logo-256x256.png create mode 100644 docs/logo-32x32.png create mode 100644 docs/logo-512x512.png create mode 100644 docs/logo-64x64.png diff --git a/Demo.php b/Demo.php index b0b6450..7d8a72e 100644 --- a/Demo.php +++ b/Demo.php @@ -1,80 +1,33 @@ - * @copyright 2014 - 2015 Benjamin Carl - * @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause - * @version Git: $Id$ - * @link https://github.com/clickalicious/memcached-php - */ - -/** - * THE FOLLOWING REQUIRE IS ONLY REQUIRED AND RECOMMENDED IN DEVELOPMENT/FOR DEVELOPMENT OF Rng - * IT DOES NOT ONLY INSTALL AN ADDITIONAL AUTOLOADER (IN ADDITION TO COMPOSER) IT ALSO ADJUST - * THE DEBUG SETTINGS, ERROR-REPORTING AND THINGS LIKE THAT! SO DO NOT BOOTSTRAP IN PRODUCTION! + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ -require_once 'src/Clickalicious/Memcached/Bootstrap.php'; -use Clickalicious\Memcached\Client; +require_once __DIR__.'vendor/autoload.php'; -/** - * memcached-php - * - * Demonstration of memcached-php Memcached Client. - * - * @category Clickalicious - * @package Clickalicious_Memcached - * @subpackage Clickalicious_Memcached_Demo - * @author Benjamin Carl - * @copyright 2014 - 2015 Benjamin Carl - * @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause - * @version Git: $Id$ - * @link https://github.com/clickalicious/memcached-php - */ +use Clickalicious\Memcached\Php\Client; // Create memcached-php instance ... $memcached = new Client( diff --git a/README.md b/README.md index f1f443e..9ade4ef 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ - + --- ![Logo of memcached-php](docs/logo-large.png) -Plain vanilla PHP `Memcached` client library with full support of Memcached ASCII protocol +Plain vanilla PHP `Memcached` client library with full support of Memcached ASCII protocol. -| [![Build Status](https://travis-ci.org/clickalicious/memcached-php.svg?branch=master)](https://travis-ci.org/clickalicious/memcached-php) | [![Scrutinizer](https://img.shields.io/scrutinizer/g/clickalicious/memcached-php.svg)](https://scrutinizer-ci.com/g/clickalicious/memcached-php/) | [![Scrutinizer Coverage](https://img.shields.io/scrutinizer/coverage/g/clickalicious/memcached-php.svg?maxAge=2592000)](http://clickalicious.github.io/memcached-php/) | [![clickalicious open-source](https://img.shields.io/badge/clickalicious-open--source-green.svg?style=flat)](https://www.clickalicious.de/) | +| [![Build Status](https://travis-ci.org/clickalicious/memcached-php.svg?branch=master)](https://travis-ci.org/clickalicious/memcached-php) | [![Codacy branch grade](https://img.shields.io/codacy/grade/76a1648856b64c728b3e00c4954342ad/master.svg)](https://www.codacy.com/app/clickalicious/memcached-php?utm_source=github.com&utm_medium=referral&utm_content=clickalicious/memcached-php&utm_campaign=Badge_Grade) | [![Codacy coverage](https://img.shields.io/codacy/coverage/76a1648856b64c728b3e00c4954342ad.svg)](https://www.codacy.com/app/clickalicious/memcached-php?utm_source=github.com&utm_medium=referral&utm_content=clickalicious/webserver-daemon&utm_campaign=Badge_Grade) | [![clickalicious open-source](https://img.shields.io/badge/clickalicious-open--source-green.svg?style=flat)](https://www.clickalicious.de/) | |--- |--- |--- |--- | -| [![GitHub release](https://img.shields.io/github/release/clickalicious/memcached-php.svg?style=flat)](https://github.com/clickalicious/memcached-php/releases) | [![Waffle.io](https://img.shields.io/waffle/label/clickalicious/memcached-php/in%20progress.svg)](https://waffle.io/clickalicious/memcached-php) | [![SensioLabsInsight](https://insight.sensiolabs.com/projects/57efa79c-5ece-4296-abab-5c3eb9053c2c/mini.png)](https://insight.sensiolabs.com/projects/57efa79c-5ece-4296-abab-5c3eb9053c2c) | [![Packagist](https://img.shields.io/packagist/l/clickalicious/memcached-php.svg?style=flat)](http://opensource.org/licenses/BSD-3-Clause) | +| [![GitHub release](https://img.shields.io/github/release/clickalicious/memcached-php.svg?style=flat)](https://github.com/clickalicious/memcached-php/releases) | [![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://opensource.org/licenses/MIT) | [![Issue Stats](https://img.shields.io/issuestats/i/github/clickalicious/memcached-php.svg)](https://github.com/clickalicious/memcached-php/issues) | [![Dependency Status](https://dependencyci.com/github/clickalicious/memcached-php/badge)](https://dependencyci.com/github/clickalicious/memcached-php) | ## Table of Contents @@ -23,7 +23,6 @@ Plain vanilla PHP `Memcached` client library with full support of Memcached ASCI - [Security-Issues](#security-issues) - [License »](LICENSE) - ## Features - ~ 100% of `Memcached` *ASCII*-protocol specification covered @@ -31,8 +30,11 @@ Plain vanilla PHP `Memcached` client library with full support of Memcached ASCI - Increment & Decrement support - Efficient connection sharing - Configurable connection close behavior - - Clean & well documented code - - Unit-Tested + - High-quality & stable codebase (following PSR standards e.g. `PSR-1,2,4`) + - Built on top of good PHP libraries + - PHP >= 7.2 ready + - Clean + well documented code + - Unit-tested with a good coverage **memcached-php** covers almost 100% of the `Memcached` protocol specification. The code is clean, full documented and developed following the PSR coding standards (PSR-0/4, PSR-1, PSR-2). The code is unit-tested (PHPUnit) and the coverage is high. The library supports \ and \ command on stored integers (strings) and the [connection handling is done like recommended](https://github.com/memcached/memcached/blob/master/doc/protocol.txt#L10 "Keep connections open and share them via a pool across instances.") in the `Memcached` protocol specification. Last but not least it supports seven of [PHP's eight variable types](http://php.net/manual/en/language.types.intro.php "PHP's variable types") - in detail four scalar types: @@ -55,12 +57,12 @@ So `resource` is the only type not supported. ## Example - - Create a `client` instance and connect it (*lazy*) to `Memcached` daemon on host *127.0.0.1* (on default port [11211]) + - Create a `Client` instance and connect it (*lazy*) to `Memcached` daemon on host *127.0.0.1* (on default port [11211]) - Set *key* **foo** with *value* **1.00** - Retrieve *value* for *key* **foo** ```php -$client = new \Clickalicious\Memcached\Client('127.0.0.1'); +$client = new \Clickalicious\Memcached\Php\Client('127.0.0.1'); // Set a value of type float $client->set('foo', 1.00); @@ -68,29 +70,23 @@ $client->set('foo', 1.00); // Returns 1.00 as PHP's type float! $client->get('foo'); ``` -You will find a demonstration `Demo.php` showing in detail how to use the **memcached-php** `client`. - +You will find a demonstration [`Demo.php` »](Demo.php) showing in detail how to use the **memcached-php** `client`. ## Requirements - - `PHP >= 5.4` (compatible up to version 5.6 as well as 7.x - but **not compatible** with HHVM) - + - `PHP >= 5.6` (compatible up to version `7.2` as well as `HHVM`) ## Philosophy This client is neither tested nor designed to be used in heavy load environments. It was designed and developed by me as a client library for my [phpMemAdmin](https://github.com/clickalicious/phpMemAdmin "phpMemAdmin on github") project. So I was able to remove dependencies of both `Memcache` + `Memcached` (PECL) extensions - both are designed in a way i don't like. I've tried to align 100% with the Memcached protocol specification. In some cases I didn't liked the naming convention and so I created some proxies. As an example - I decided to implement increment() as proxy to incr() and decrement() as proxy to decr(). I will add some more responsibilities in some more classes like a [PSR compatible](https://github.com/php-fig/fig-standards/blob/master/proposed/cache.md "PSR Cache proposal") Caching proxy and a Pool/Cluster Class for management operations soon. - ## Versioning For a consistent versioning i decided to make use of `Semantic Versioning 2.0.0` http://semver.org. Its easy to understand, very common and known from many other software projects. - ## Roadmap -- [x] Target stable release `1.0.0` -- [x] `>= 90%` test coverage -- [ ] Security check through 3rd-Party (Please get in contact with me) +- [ ] n.a. [![Throughput Graph](https://graphs.waffle.io/clickalicious/memcached-php/throughput.svg)](https://waffle.io/clickalicious/memcached-php/metrics) @@ -102,7 +98,7 @@ The recommended way to install this library is through [Composer](http://getcomp ```json { "require": { - "clickalicious/memcached-php": "~0.1" + "clickalicious/memcachedphp": "^2.0" } } ``` @@ -181,4 +177,4 @@ Thanks to our sponsors and supporters: ###### Copyright -Icons made by Freepik licensed by CC 3.0 BY +
Icons made by Vectors Market from www.flaticon.com is licensed by CC 3.0 BY
\ No newline at end of file diff --git a/docs/color.txt b/docs/color.txt new file mode 100644 index 0000000..d13cf62 --- /dev/null +++ b/docs/color.txt @@ -0,0 +1 @@ +#66B0A0 \ No newline at end of file diff --git a/docs/logo-128x128.png b/docs/logo-128x128.png new file mode 100644 index 0000000000000000000000000000000000000000..f39d8325716354393568ea46e28dc85d0b40f361 GIT binary patch literal 4155 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 HoD!M<2Nlc9 literal 0 HcmV?d00001 diff --git a/docs/logo-256x256.png b/docs/logo-256x256.png new file mode 100644 index 0000000000000000000000000000000000000000..e94417fcea78ea36d19dd670cf2e3990456880bd GIT binary patch literal 7988 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 Doxpwo literal 0 HcmV?d00001 diff --git a/docs/logo-32x32.png b/docs/logo-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..bf437af2c709e4c6d6f203f6c8315934a45956ec GIT binary patch literal 1294 zcmZ`%X;4#F7`-7x2#8__MQEuw6{W0U)lw-F3_?U08j=c(ELq-rgrx}~7+DQ!tzbLs z%9dtqJ4lWx|5v0B@LZN*YyC|b%g)Uwv}KBDdPM`!N+?z`VR=R4=S_f{n( z#yUB;I{*Njm~jjiTs?_tPll1vS$zX8wirEu4gg-`xXPsvKlyR21OQ5Q0w6yLz%oq9 zCjls;0q{5rfQV)QT*dAEZ|((v)SPsX9RmT`J!({^LU0wT|92pIHQLgGtYwJU)sb(k zNA=2Q7@YXX##K35Ez+&dGAYqDI)*FdCKIaHVAfL4Yr+abTmdn_VzDA2orf!+DAKK2 z%cxOYiRqM9B1fyW3Sr%ZH|)8tu=hg<7Ibw&ClCqcP@Dij@EV2K%H?uYr$lfy)~(=o zwOi{T1{I=vW}Xn$sZrRVUX^d`#Qre}4*_DVTL{3_h`~yMNF|1AAl`Z)Rz(v!6p{Vm&ecjxls)nWCncv8zsqf>iS zm8Pinvn%AG@z-uOJ?-5z@>1GYF?qtj)*U@=qY?#fn_BSAnw%?URxfV2^y!m+eaEcg zPT0)o#n9=Bf`;3#Z1oN{o!o&-jQ4KvoF6*4mPd{ya|V(QP~UjKx_ce*fR0GK_*e!w zP4S3>8>%=iwGckfVPcY^z1xn$q@9$R5M%d?tux7XyQ1E&4{|Or8FcoZ!FTFbGGCy( ze5qU47{EQSWNu74Eb|z37La2COTEsC?pSK;%t>=&wEaH3NCz7=k3p&NJrPyx zfUxgj=4N^>cl2sb1_RWPXaU@V6HmOHi^I5~3U3`( z&mGYdS{o{<{VmFPhv}o9W1Py|eHl(EtTbkX=lc$n0r~uy&cv@R8VPl!e0?e31Mt5}9@xK8lctS}4 literal 0 HcmV?d00001 diff --git a/docs/logo-512x512.png b/docs/logo-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..24bb56d7c72f5c87f8e60f08265d8db785362369 GIT binary patch literal 16600 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 Ua_j%}vwung9R* literal 0 HcmV?d00001 diff --git a/docs/logo-large.png b/docs/logo-large.png index dde9cb232a01eb7f521395937f42ca0a2867d16a..07876003f02f38bc8832be568766e7cbe01cdc86 100644 GIT binary patch literal 7988 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 From 27462e0ae91bb3039d5973660aa1b8956b498d99 Mon Sep 17 00:00:00 2001 From: Benjamin Carl Date: Mon, 24 Apr 2017 17:41:52 +0200 Subject: [PATCH 04/12] Restructuring. --- .gitattributes | 21 ++++-- .gitignore | 65 +------------------ .travis.yml | 9 ++- composer.json | 11 ++-- docs/coverage/.gitkeep | 0 phpunit.xml.dist | 6 +- .../Memcached/Php => }/Cache.php | 0 .../Memcached/Php => }/Client.php | 10 +-- .../Compression/CompressionInterface.php | 0 .../Memcached/Php => }/Compression/Lzw.php | 0 .../Memcached/Php => }/Compression/Smaz.php | 0 .../Memcached/Php => }/Compression/Zlib.php | 0 .../Memcached/Php => }/Exception.php | 0 .../Memcached/Php => }/Util.php | 0 .../Memcached/Php => }/ClientTest.php | 0 .../Memcached/Php => }/UtilTest.php | 0 16 files changed, 40 insertions(+), 82 deletions(-) delete mode 100644 docs/coverage/.gitkeep rename src/{Clickalicious/Memcached/Php => }/Cache.php (100%) rename src/{Clickalicious/Memcached/Php => }/Client.php (99%) rename src/{Clickalicious/Memcached/Php => }/Compression/CompressionInterface.php (100%) rename src/{Clickalicious/Memcached/Php => }/Compression/Lzw.php (100%) rename src/{Clickalicious/Memcached/Php => }/Compression/Smaz.php (100%) rename src/{Clickalicious/Memcached/Php => }/Compression/Zlib.php (100%) rename src/{Clickalicious/Memcached/Php => }/Exception.php (100%) rename src/{Clickalicious/Memcached/Php => }/Util.php (100%) rename tests/{Clickalicious/Memcached/Php => }/ClientTest.php (100%) rename tests/{Clickalicious/Memcached/Php => }/UtilTest.php (100%) diff --git a/.gitattributes b/.gitattributes index 3dc84fd..8d49fb8 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,3 @@ -* text=auto *.java eol=lf text ident diff=java *.c eol=lf text ident diff=cpp *.h eol=lf text ident diff=cpp @@ -6,9 +5,23 @@ *.xml eol=lf text ident diff=html *.vm text ident *.php eol=lf text ident diff=php +*.phps eol=lf text ident diff=php *.properties text ident -*.js text ident diff=html -*.css text ident diff=html +*.js eol=lf text ident diff=java +*.json eol=lf text ident diff=java +*.css eol=lf text ident diff=html *.sh eol=lf text ident *.bat eol=crlf text ident -*.vcproj eol=crlf text ident \ No newline at end of file +*.cmd eol=crlf text ident +*.vcproj eol=crlf text ident +*.md eol=lf text ident diff=html +*.gif binary +*.png binary +*.jpg binary +*.jar binary +*.class binary +*.gz binary +*.tar binary +*.dll binary +*.exe binary +*.zip binary \ No newline at end of file diff --git a/.gitignore b/.gitignore index fdecffc..fa9a3eb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,69 +1,8 @@ -### PhpStorm ### -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm - -*.iml - -## Directory-based project format: -.idea/ - -## File-based project format: -*.ipr -*.iws - -## Plugin-specific files: - -# IntelliJ -out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties - -### Windows ### -# Windows image file caches -Thumbs.db -ehthumbs.db - -# Folder config file -Desktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msm -*.msp - -# Windows shortcuts -*.lnk - -# Project specific +# clickalicious __*/ -docs/coverage/* +build/* bin/* -# Common -*.bak -*.tmp -*.log -*.old -.settings -.buildpath -.project -.externalToolBuilders - # Composer vendor/ composer.lock - -# Node & npm -npm_modules/ diff --git a/.travis.yml b/.travis.yml index 249c38d..a21a10d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,9 @@ language: php php: + - 5.3 + - 5.4 + - 5.5 - 5.6 - 7.0 - 7.1 @@ -11,6 +14,9 @@ sudo: false matrix: allow_failures: + - php: 5.3 + - php: 5.4 + - php: 5.5 - php: nightly - php: hhvm fast_finish: true @@ -27,14 +33,13 @@ env: - PREFER_LOWEST="" before_script: - - composer update --prefer-dist $PREFER_LOWEST + - composer update --ignore-platform-reqs --prefer-dist $PREFER_LOWEST - composer --optimize-autoloader --no-interaction script: - bin/phpunit --configuration . --coverage-clover=build/logs/clover.xml --coverage-html=build/html/coverage after_script: - # We upload only for reference platform! This is our base for further code analyses and so on. - if [ $(phpenv version-name) == "5.6" ] && [ "$PREFER_LOWEST" == "--prefer-lowest" ]; then bin/codacycoverage clover build/logs/clover.xml > /dev/null 2>&1; fi after_success: diff --git a/composer.json b/composer.json index 8111215..8ec1549 100644 --- a/composer.json +++ b/composer.json @@ -24,15 +24,14 @@ }, "autoload": { "psr-4": { - "Clickalicious\\": "src/Clickalicious/" + "Clickalicious\\Memcached\\Php\\": "src/" }, - "files": ["src/Clickalicious/Memcached/Php/Util.php"] + "files": ["src/Util.php"] }, "autoload-dev": { "psr-4": { - "Clickalicious\\": "src/Clickalicious/" - }, - "files": ["src/Clickalicious/Memcached/Php/Util.php"] + "Clickalicious\\Memcached\\Php\\": "tests/" + } }, "keywords": [ "Memcached", @@ -53,6 +52,6 @@ }, "config" : { "bin-dir" : "bin", - "preferred-install": "source" + "preferred-install": "dist" } } diff --git a/docs/coverage/.gitkeep b/docs/coverage/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 04d644a..8f7080b 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,10 +1,10 @@ - + ./tests @@ -14,9 +14,11 @@ ./src ./bin + ./build ./docs ./vendor ./tests + ./demo diff --git a/src/Clickalicious/Memcached/Php/Cache.php b/src/Cache.php similarity index 100% rename from src/Clickalicious/Memcached/Php/Cache.php rename to src/Cache.php diff --git a/src/Clickalicious/Memcached/Php/Client.php b/src/Client.php similarity index 99% rename from src/Clickalicious/Memcached/Php/Client.php rename to src/Client.php index fffac69..2427e45 100644 --- a/src/Clickalicious/Memcached/Php/Client.php +++ b/src/Client.php @@ -958,7 +958,7 @@ public function connect($host, $port, $timeout = null) * @author Benjamin Carl * @return mixed|array The result from Memcached daemon * @access public - * @throws \Clickalicious\Memcached\Exception + * @throws \Clickalicious\Memcached\Php\Exception */ public function send($command, $data = '') { @@ -1791,7 +1791,7 @@ protected function reset() * @author Benjamin Carl * @return array|bool Response parsed as collection, otherwise FALSE (ERROR) * @access protected - * @throws \Clickalicious\Memcached\Exception + * @throws \Clickalicious\Memcached\Php\Exception */ protected function parseReadResponse($buffer, array $lines) { @@ -2005,7 +2005,7 @@ protected function parseDeleteResponse(array $lines) * @author Benjamin Carl * @return array|bool The stats as collection indexed by hostname?, otherwise FALSE (ERROR) * @access protected - * @throws \Clickalicious\Memcached\Exception + * @throws \Clickalicious\Memcached\Php\Exception */ protected function parseStatsResponse(array $lines) { @@ -2166,7 +2166,7 @@ protected function checkResponse($buffer) * @author Benjamin Carl * @return bool|mixed FALSE on error, otherwise parsed response * @access protected - * @throws \Clickalicious\Memcached\Exception + * @throws \Clickalicious\Memcached\Php\Exception */ protected function parseResponse($command, $buffer) { @@ -2235,7 +2235,7 @@ protected function parseResponse($command, $buffer) * @author Benjamin Carl * @return array ... * @access protected - * @throws \Clickalicious\Memcached\Exception + * @throws \Clickalicious\Memcached\Php\Exception */ protected function serializeValue($value) { diff --git a/src/Clickalicious/Memcached/Php/Compression/CompressionInterface.php b/src/Compression/CompressionInterface.php similarity index 100% rename from src/Clickalicious/Memcached/Php/Compression/CompressionInterface.php rename to src/Compression/CompressionInterface.php diff --git a/src/Clickalicious/Memcached/Php/Compression/Lzw.php b/src/Compression/Lzw.php similarity index 100% rename from src/Clickalicious/Memcached/Php/Compression/Lzw.php rename to src/Compression/Lzw.php diff --git a/src/Clickalicious/Memcached/Php/Compression/Smaz.php b/src/Compression/Smaz.php similarity index 100% rename from src/Clickalicious/Memcached/Php/Compression/Smaz.php rename to src/Compression/Smaz.php diff --git a/src/Clickalicious/Memcached/Php/Compression/Zlib.php b/src/Compression/Zlib.php similarity index 100% rename from src/Clickalicious/Memcached/Php/Compression/Zlib.php rename to src/Compression/Zlib.php diff --git a/src/Clickalicious/Memcached/Php/Exception.php b/src/Exception.php similarity index 100% rename from src/Clickalicious/Memcached/Php/Exception.php rename to src/Exception.php diff --git a/src/Clickalicious/Memcached/Php/Util.php b/src/Util.php similarity index 100% rename from src/Clickalicious/Memcached/Php/Util.php rename to src/Util.php diff --git a/tests/Clickalicious/Memcached/Php/ClientTest.php b/tests/ClientTest.php similarity index 100% rename from tests/Clickalicious/Memcached/Php/ClientTest.php rename to tests/ClientTest.php diff --git a/tests/Clickalicious/Memcached/Php/UtilTest.php b/tests/UtilTest.php similarity index 100% rename from tests/Clickalicious/Memcached/Php/UtilTest.php rename to tests/UtilTest.php From fd35011a654cba1ca7a204aad1618eb597374a39 Mon Sep 17 00:00:00 2001 From: Benjamin Carl Date: Tue, 25 Apr 2017 17:04:09 +0200 Subject: [PATCH 05/12] Documentation update. --- README.md | 92 +++++++++++------------------------------- docs/color.txt | 1 - docs/info.txt | 3 ++ docs/logo-128x128.png | Bin 4155 -> 4876 bytes docs/logo-16x16.png | Bin 875 -> 905 bytes docs/logo-24x24.png | Bin 1059 -> 1163 bytes docs/logo-256x256.png | Bin 7988 -> 9240 bytes docs/logo-32x32.png | Bin 1294 -> 1444 bytes docs/logo-512x512.png | Bin 16600 -> 19725 bytes docs/logo-64x64.png | Bin 2019 -> 2242 bytes docs/logo-large.png | Bin 7988 -> 9240 bytes 11 files changed, 26 insertions(+), 70 deletions(-) delete mode 100644 docs/color.txt create mode 100644 docs/info.txt diff --git a/README.md b/README.md index 9ade4ef..a3088f2 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ![Logo of memcached-php](docs/logo-large.png) -Plain vanilla PHP `Memcached` client library with full support of Memcached ASCII protocol. +`Memcached` client library in plain vanilla PHP. | [![Build Status](https://travis-ci.org/clickalicious/memcached-php.svg?branch=master)](https://travis-ci.org/clickalicious/memcached-php) | [![Codacy branch grade](https://img.shields.io/codacy/grade/76a1648856b64c728b3e00c4954342ad/master.svg)](https://www.codacy.com/app/clickalicious/memcached-php?utm_source=github.com&utm_medium=referral&utm_content=clickalicious/memcached-php&utm_campaign=Badge_Grade) | [![Codacy coverage](https://img.shields.io/codacy/coverage/76a1648856b64c728b3e00c4954342ad.svg)](https://www.codacy.com/app/clickalicious/memcached-php?utm_source=github.com&utm_medium=referral&utm_content=clickalicious/webserver-daemon&utm_campaign=Badge_Grade) | [![clickalicious open-source](https://img.shields.io/badge/clickalicious-open--source-green.svg?style=flat)](https://www.clickalicious.de/) | @@ -25,18 +25,20 @@ Plain vanilla PHP `Memcached` client library with full support of Memcached ASCI ## Features - - ~ 100% of `Memcached` *ASCII*-protocol specification covered + - 100% of `Memcached` *ASCII*-protocol specification covered - Support for storing native PHP variable types (arrays, objects ...) - Increment & Decrement support - Efficient connection sharing - Configurable connection close behavior - High-quality & stable codebase (following PSR standards e.g. `PSR-1,2,4`) - Built on top of good PHP libraries - - PHP >= 7.2 ready - Clean + well documented code - Unit-tested with a good coverage -**memcached-php** covers almost 100% of the `Memcached` protocol specification. The code is clean, full documented and developed following the PSR coding standards (PSR-0/4, PSR-1, PSR-2). The code is unit-tested (PHPUnit) and the coverage is high. The library supports \ and \ command on stored integers (strings) and the [connection handling is done like recommended](https://github.com/memcached/memcached/blob/master/doc/protocol.txt#L10 "Keep connections open and share them via a pool across instances.") in the `Memcached` protocol specification. Last but not least it supports seven of [PHP's eight variable types](http://php.net/manual/en/language.types.intro.php "PHP's variable types") - in detail four scalar types: + +## Example + +**memcached-php** covers almost 100% of the `Memcached` protocol specification. The code is clean, full documented and developed following the PSR coding standards (PSR-1, PSR-2, PSR-4). The code is unit-tested and the coverage is > 80%. The library supports \ and \ command on stored integers (strings) and the [connection handling is done like recommended](https://github.com/memcached/memcached/blob/master/doc/protocol.txt#L10 "Keep connections open and share them via a pool across instances.") in the `Memcached` protocol specification. Last but not least it supports seven of [PHP's eight variable types](http://php.net/manual/en/language.types.intro.php "PHP's variable types") - in detail four scalar types: boolean integer @@ -54,9 +56,6 @@ and finally one special type: So `resource` is the only type not supported. - -## Example - - Create a `Client` instance and connect it (*lazy*) to `Memcached` daemon on host *127.0.0.1* (on default port [11211]) - Set *key* **foo** with *value* **1.00** - Retrieve *value* for *key* **foo** @@ -72,46 +71,12 @@ $client->get('foo'); ``` You will find a demonstration [`Demo.php` »](Demo.php) showing in detail how to use the **memcached-php** `client`. -## Requirements - - - `PHP >= 5.6` (compatible up to version `7.2` as well as `HHVM`) - -## Philosophy - -This client is neither tested nor designed to be used in heavy load environments. It was designed and developed by me as a client library for my [phpMemAdmin](https://github.com/clickalicious/phpMemAdmin "phpMemAdmin on github") project. So I was able to remove dependencies of both `Memcache` + `Memcached` (PECL) extensions - both are designed in a way i don't like. I've tried to align 100% with the Memcached protocol specification. In some cases I didn't liked the naming convention and so I created some proxies. As an example - I decided to implement increment() as proxy to incr() and decrement() as proxy to decr(). I will add some more responsibilities in some more classes like a [PSR compatible](https://github.com/php-fig/fig-standards/blob/master/proposed/cache.md "PSR Cache proposal") Caching proxy and a Pool/Cluster Class for management operations soon. - -## Versioning - -For a consistent versioning i decided to make use of `Semantic Versioning 2.0.0` http://semver.org. Its easy to understand, very common and known from many other software projects. - -## Roadmap - -- [ ] n.a. - -[![Throughput Graph](https://graphs.waffle.io/clickalicious/memcached-php/throughput.svg)](https://waffle.io/clickalicious/memcached-php/metrics) - - -## Installation - -The recommended way to install this library is through [Composer](http://getcomposer.org/). Require the `clickalicious/memcached-php` package into your `composer.json` file: - -```json -{ - "require": { - "clickalicious/memcachedphp": "^2.0" - } -} -``` - -**memcached-php** is also available as [download from github packed as zip-file](https://github.com/clickalicious/memcached-php/archive/master.zip "zip package containing library for download") or via `git clone https://github.com/clickalicious/memcached-php.git .` - - -## Data +### Data `Strings`, `Integers` and `Float-Values` are never modified by this library in any way. Those types will be stored by `Memcached`'s internal system - while all other types will be serialized by this client and can optionally be stored compressed (*LZW*/*Smaz*) - in one of the next releases of this library - targeting 0.4.0. I'm working on an PoC implementation of `Smaz - a short string compression library` (https://github.com/zhenhao/smaz.php) and on a german translation of the translation table used by `Smaz`. -## Metadata +### Metadata `Memcached` provides a 32 Bit (Version > 1.2.1) unsigned Integer field for meta data. From the `Memcached` protocol specification: > Note that in memcached 1.2.1 and higher, flags may be 32-bits, instead @@ -121,43 +86,32 @@ compatibility with older versions. **memcached-php** uses this field for its meta data. The meta data is required to mark data for serialization and stuff like this. This meta data is stored via the clients` flags field. The lower first **8 Bits** (*lowest Byte*) are reserved by **memcached-php**. The other 8 Bits (half of the 16 Bits) can be used by your app. -## Documentation - -The best and currently only existing documentation is the inline documentation of this project. So please have a look at the source to understand how **memcached-php** works internally. - - -## Tests +## Requirements -**memcached-php** is unit-tested and the code coverage is high. For an in-detail view have a look at this always up to date [Code Coverage report](http://clickalicious.github.io/memcached-php/dashboard.html "Code Coverage"). + - `PHP >= 5.6` (compatible up to version `7.2` as well as `HHVM`) -Running the Tests -You will find a PHPUnit configuration including testsuites in directory `tests/`. To run those configuration execute the following command on `cli`: - -```sh -phpunit -c tests/phpunit.xml --testdox -``` +## Philosophy -### Tests -The unit-tests are fired against an existing and real `Memcached` daemon. Please be aware that you need a running `Memcached` deamon on the host you run the unit-tests listening on the default port (11211). In Result the unit-tests are not that isolated cause they are bound to a running `Memcached` daemon and network as well. +This client is neither tested nor designed to be used in heavy load environments. It was designed and developed by me as a client library for my [phpMemAdmin](https://github.com/clickalicious/phpMemAdmin "phpMemAdmin on github") project. So I was able to remove dependencies of both `Memcache` + `Memcached` (PECL) extensions - both are designed in a way I don't like. I've tried to align 100% with the Memcached protocol specification. In some cases I didn't liked the naming convention and so I created some proxies. As an example - I decided to implement increment() as proxy to incr() and decrement() as proxy to decr(). I will add some more responsibilities in some more classes like a [PSR compatible](https://github.com/php-fig/fig-standards/blob/master/proposed/cache.md "PSR Cache proposal") Caching proxy and a Pool/Cluster Class for management operations soon. ## Versioning -For a consistent versioning i decided to make use of `Semantic Versioning 2.0.0` http://semver.org. Its easy to understand, very common and known from many other software projects. + +For a consistent versioning I decided to make use of `Semantic Versioning 2.0.0` http://semver.org. Its easy to understand, very common and known from many other software projects. ## Roadmap - - [ ] Hardening code - more stability! - - [ ] `\Clickalicious\Memcached\Proxy` - This should become a proxy implementation which is able to act as `Memcache` or `Memcached` (both PECL) extension (emulate) for testing (primary mocking/stubbing). - - [ ] `\Clickalicious\Memcached\Server` - This should become a virtual (emulated) mode which emulates a complete `Memcached` backend. - - [ ] Add compression support to be able to manipulate PECL Memcached stored data (FastLZ, zlib, LZW) - - [ ] Replace explodes and array operations for data anlysis with regular expressions. - - [ ] Increase coverage and cover (currently unused compression classes) more parts. +- [ ] `\Clickalicious\Memcached\Proxy` + This should become a proxy implementation which is able to act as `Memcache` or `Memcached` (both PECL) extension (emulate) for testing (primary mocking/stubbing). +- [ ] `\Clickalicious\Memcached\Server` + This should become a virtual (emulated) mode which emulates a complete `Memcached` backend. +- [ ] Add compression support to be able to manipulate PECL Memcached stored data (FastLZ, zlib, LZW) +- [ ] Replace explodes and array operations for data anlysis with regular expressions. +- [ ] Increase coverage and cover (currently unused compression classes) more parts. -If you are interested in any of these features too - please let me know. Maybe we can adjust the priority and speed things up ... +[![Throughput Graph](https://graphs.waffle.io/clickalicious/memcached-php/throughput.svg)](https://waffle.io/clickalicious/memcached-php/metrics) ## Participate & share @@ -177,4 +131,4 @@ Thanks to our sponsors and supporters: ###### Copyright -
Icons made by Vectors Market from www.flaticon.com is licensed by CC 3.0 BY
\ No newline at end of file +
Icons made by Freepik from www.flaticon.com is licensed by CC 3.0 BY
\ No newline at end of file diff --git a/docs/color.txt b/docs/color.txt deleted file mode 100644 index d13cf62..0000000 --- a/docs/color.txt +++ /dev/null @@ -1 +0,0 @@ -#66B0A0 \ No newline at end of file diff --git a/docs/info.txt b/docs/info.txt new file mode 100644 index 0000000..bcf9485 --- /dev/null +++ b/docs/info.txt @@ -0,0 +1,3 @@ +url: http://www.flaticon.com/free-icon/data-storage_359133 +color: #66B0A0 +author:
Icons made by Freepik from www.flaticon.com is licensed by CC 3.0 BY
\ No newline at end of file diff --git a/docs/logo-128x128.png b/docs/logo-128x128.png index f39d8325716354393568ea46e28dc85d0b40f361..1b6917293cd4cc306b008263222b39adf70f7e8a 100644 GIT binary patch delta 4694 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 diff --git a/docs/logo-24x24.png b/docs/logo-24x24.png index ddfc470c4f4501593aa071e237f733b68beade17..cc984e7350d4c3cc47bcc83ff06638a39d3108e7 100644 GIT binary patch delta 949 zcmZ3?(akv_p}sl5C&ZP3fuW(HA#L;gv<(Z>HY`A(jSJE?E=*WIKV{?W)b$I~R?SS^ z2vh@7kh&hqNMAQMWz&2hH)Yejw9ShkvW2TwK^W;97o=`jkO4AhLHeeJ89+lI=5L+} zG&y}^J&Kj7V0BOx8y2Q-m7LFAal#obcnk_P6z1%a)GEmciW_NpgM@7AmrM`=^N%V{QnO$1)?r> z9ndL2bLRuSlDQV@rVOwwP(kWCpr?TffQo@(un1^q%K8NvK!UXp{!%DBYr76SvLgQtsQh{pNW!1VA#4gxWaEm{Hr0V-vJ z+}mz&FHjN_5frHwuYS+_e0{cuV2#~e{mtgZ`FZ92FXTo3s%34lYP%}kqo%ZZ>5u5` zw+`I0WPhNuFvvj9wfykR-C<$pscTj?qKpY#_3_Go;;&p; zBbAZ4HG?Vlt%LjW)!uLWg?F)bym)ywFn)g)&kW{f=fld42^uz5W^#gRQ#71Io_MBZ zMLm%`CK!Bg!`X9Fvmeix{=6yFNQm2WuY{bJXIrPgSMi5#m%4<`;9H*h(o0W<-&tMt z{?@Kti+R<}YUB5ax-i}T8U6Kb!t}5mwzv4*`MAEzb{9-JsCe<>$Nu>p#wCZA`!7zn z(7&L6!&_-O``_@UcUJ!UcOIQ9QSslQ{LSObo14TLB}=ES&yUj(c=P_|p9fdkdvyM` z`ro+cU3K&-o6zJ5%rc@zrXdCvRt82^rWV>j(!k()?5dQ>2eHX4V-UJA`4h7t0N2aT A_5c6? delta 844 zcmeC?T+A^cq24RNC&ZNj2>$<1->@KU!$L3uG8Uw7T#&YQapu~25RvqC3kz4R0*V7s z>c;tLo0p}o2eCmafh3T<5vX@T`uh2~+a{%hrE<3|fhb5_w+JM%abfC)g{hn7C$F88 zzF|ICsvf9+enUe;(#E-|>*uF}4a(fIG<4Ofl(kUJ5JOWpECMosYBD#@1?dDDw|;&a z(1{y?R)WNVf*D{EtPJRSpn8yC$|k4>(l*Xd-LxQW=1j0HK)nlr?ygS-8UgYv$n9Ah z=79_anhq9B0qFFaH=d!n5-@kn=UyvZ+%z4k?XLVu3HpM={YEvt- zd9yr{i@NIHFH=2J5p`baaJJZt&jx1ac8?^b$RpOrIFWpxZ_@B+u1Cf zwM^5f;_D&l|DnDQY}aPqau@zF`y>B^hbPlz_hqSu?_2%m4SNn}$bGM`*42}LFw2M< mnuQn`S{ayF8JcSw7+Dz@gtj@Zo2 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 literal 7988 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 Doxpwo diff --git a/docs/logo-32x32.png b/docs/logo-32x32.png index bf437af2c709e4c6d6f203f6c8315934a45956ec..225eb6a79235e0c329dec6415b55085f2a8afc42 100644 GIT binary patch delta 1247 zcmZWneN5tBN&wJndKF{y>JkRgmbGziE zYNkMc@&r8$hrdRsO49?kD0E}Rc0jZ?GA{Gx^qEa9y z0Pmo6mbwM=2mlm@Ot85Hm`6^xJX3K2>w;Oq+F=@ij7h>Ag&2ddTA+so1t?o3!3<$T zl0qoVGGzeeED#+K0Mv!W#xe-Z?oi;-G~sRgfRxoC0j={C3b2EfscvQVgEEY1zG11G z!GR4-m;^ux=D>93sX#555cZmN~>zn%xst|V_4aIeOxhnv$dkpdi7ZGw>L_t1uccu zzQ1#hR9!f9(c?#0OHUl@y&LE3DB1bX(t#a!P7f^aTPPoTm_nPFc_|PI-!r%-pB0HL zJO$((1Y$lhrV@w4>!N6(^q}F6nhl1{+qN1ncKv>bN@3P3ylQe?fS;^~)<~+0X_`r| zlzS#;T{1MCNtsP=n`5kKr-O2jjpa%hi{juZ@#(4J^YgXxqaOu4NuAl)((J3LadMxP zr5F0idCM{3_2fHau}W5Bg@@2zH0&iHB^+)aeF3mYOP7`>>>=bL8| zmT`@6&zi?82x%uxN3IOGEHCY`F2DBE?yq9CZzU(g7Ny;*CnMZOG@hd!>z^Qk)(D@z zV#@3Bc&H(%`!P=9uCy}-bVkjEd`g)aF4LD;u&go?@l{@F(IWJ&fuWDRRTVN)Q9pY1 zxXn`BbB|qu4mJdSEGbGptdqZyams ziEI^~ZG(n>rjRyo(zQ~tK`?9e1A&w${6IeHY|w?2(7f%5DrQB!(ZL}}nxNF+C)cBF z4-=jJ${E9Me@y*4&UDT5-S1?yzkhY5DREM#fyd|j7L?Jtmhw}?;0>MOY_E0U4T~#2 zNn5;zR2_*-H$Hp=i63=BMlOvkq&zJGf2b=zERoO3;IpZj`Jzh8-F80ybV?AF;!C9j cx>J0p6v~;v{s$tZrQ`pRH6Afr^vKftUy2TSZvX%Q delta 1104 zcmZ3&-N!W{p?*eyPlzi614G8IWrY30wUKg&fPXCZNq%1 znvEbv#>NF%t5&71TbQtaei~SN`o{SU`}e19SdPZJeLJem+EJ=&Dr^o6^^T9G4EWICINVpyHIZOMp%Rss!4Xwh^QN zs2HpT#BFG3NLxQYb>qVHb#s%~&ViVdx@ke`dZ_C`mabn|p9b{eh6TCXmZZa-2@D3H zA_#Xq&_N4RH$n6ORnAY}xCp2o5+o2Sfn?U!`DvShXdXm7dE;WBnslI>K!$+KPu(~_ z0b=pynJF7*16`H^_DpI$(A7Za14)pl=NGP81#t(+Pa75h=OdErCw^W&@Wq-(jbqyD3>Nbxzh3>hzEItpapvzSymRb37nM#pw4M3d z6U(zx|7{VxD-(6xH7D~gTSXJof_#1XH{Z1rUVNi=MAxDv;>)EF0C3^ilFeM;d#L`$*EGRU9?<``DYBXb)zgM@P`9(=R6MszX-C0%q|HCaJ-|5D%mdDp~eqdmB)vs2a zo`2#0goy_NK0Y>Z(@L`CVp^Ew!rHIhY+uQh8ge83zzoOi#0NXG;#?T|84dIjeV%<) z6{s{#G?}Ym;3>j9C-Lz|Ki}juv;MOmYYQ_>{znPqH?ZuSP~*L&q3CDo)Q-opx{qw@ zcZ+;bjJo9hVDc=jhDxJEPlj3FBjXsweq=s5&k-awafaLi{rJYvMLZkam=9EKadiw*QcS`=>PaNu%AeO?w$~)cLOca|~5baeB6jiIZ_o z80(Y8``2u)4{gkLj^m98it@Bl=;dNP)$qA{iQl>Zi;6f;cWo`>uA4M@vLLIBo1s~V zfuWUwiIuUjwtEKDq>Z;~ JCKt0B0syZ87rX!f diff --git a/docs/logo-512x512.png b/docs/logo-512x512.png index 24bb56d7c72f5c87f8e60f08265d8db785362369..2f89debee9da79c1b60873deebee3ab6ec7efa2b 100644 GIT binary patch literal 19725 zcmd3Oi9b~B8~2$RV{8qQ-4I%kA|j?l_OgW*OGVkT3saJ1rUh;2Ny#$yqM~e3iJ9st zB4ewuXZj^Wlc-U)Vcu(=_x&T@KA%2noO7RZ?sMJOb$yq6t{iZ-TO+0>YbvOJ%_@slKHT;A>i&jm^@Iy4p-ZKWS)-U*rvee3pLlB4W`>gjK zj2oIBJ)3{bW`+Oj$SIW2)OroKG9BN&Qqtm`=n9OE+|`Q#x*`iUmFoxIUg%v%Sov~5 zcAdgo%gC!*S1-&xzMx?vC9*Yhe7-e*V@GY`%;&EB^J87DUw5yytUWj8RLf3eJEElj zzyI@KNP>g(DqA{>V+<)ad67Zsvtx!7Q@Y>9$9Ac_`CI04)WXn}A#3aSn_}eMPHn;J zzF6P4r;lzyt{EmKXBQwpaX*!!M@>IujLK4JV@ur48ok0gNTSYo{f6ql`u$$?-}Un| z@4{Gx^c$U|H$^jiL!x;ru_(=c&Crs~@ptOuJ1`!&0do1+WMKfciyp?3n~=XixiFhO z$>UePxsn!pOUy!2)U--03>`L<(6qfei}YCTE^b7K7u@})f0Flx`O4b*{xvZRhs*hg zk>mTH_edVy;>rH59z%X~E=2p80g?ZVq%VEb`2gzFmCQaj`eAw~C7iG}&24j-!EkzkYHo%d%5l>Spc(L_yrN3inbb4j1sQWiF+aY)CFdXa^LfZVpu- z3L>Uew$H!NFmX3YsySyQQRNtOVMZFGs{y}3qXgZ(QN8>&nX0{WCA+jMk+!53rF2gs zzx>7(>wm9CSoUd+*PG0qVI8C2IkmQM_oriY7m8mAH@OzCsj*^%^;No*&qVe}yp;EN zN6e^yeUC0pe;;qHZ)WM-RT37nvR+^Ot}HeA12H>IR+fwxGYuF2fen+?^`+W*O*yYe zk6mN%hl!4QqNa&r3*R%ANpb^>3Hnw4D*Srzs5miQ-$a2vr^T&azAb!B`MEh*W0iQ ztMPrteFu<~rCm;nMY{7ZTbGQkUAM*h=I4r9lHwl~EaT8wkTQR-4dLONC9V0fj zhh9$kR>@6sq+{u=f40TM-CjpVWuWXZKK1n+w7$5MKj_O%V zt$g#btQM`$5@iXoSd%;w^F$e~0+}86TGewe;D%z9 zgvWgjq}urBj*9>=2`q`H0#zM&T)fb-as@EIG3;m!3gB;u$2p{kKTe`Y8L@qyp02h>&2E zLKJHD%Ovk9b8p$%xz0z!%5rQZHXqb+PT)R*8fQZbHTlc+KhlJ~*mgV%DL(Wh(K{ zGb?`;CVEvWI=-AFm6ORv$*1Q3t;cIjGwXJ~&FmAWxKoU(>eA}z#aYLM&i~DNb*BrZ z@V<*YZ^NExgv0u7X(H<7$0%c^d9=2;#C0Z5JXN<$|2oVvFk74Xe)0iYD%#6rJk}&i zh4|dXzvTG7=Q&{!1nW)K8AASU@%NES`Es}<%#Q%7P1)OmHAkhQdxbrx4P;CV#$z30 zigMcS#+SgnpG?Arg~K5@U5R{!==dhM)#_1ghuubK9lwy34fKf#erSEm*lS(ZHrCIH zVH|ldtJFC)Y1iz^VAlUkWjy>ZMFyd-xuyFZM|ntlsCMtl`sE!&gG_3Zbs~u19g|V23ZFB z2gDASNq9bS^6YJpE9wjxJ122VoJG5{uNcI9zqnp6cZd@!;TAut4@_3gi*T6MtDZv=XgJbS9dR56(pI1jWB=3S>$Z|3*(~QGk0n_v47JTYM})G+NF8~ z;bs3eVP!xFZL8XXbawI`eGNIfvUyH}xG(crT**}xD!-gmF?1VA&?%6(7oW=yqVHDTqL@Z<66c_rfRm*cKvqdDSnon0D z4&z0mwg-gA(Vkyk+_}Gc>nxQ$7?B*bB=i%9pkErrFdIxyAWSz%eh z&cdG+q0keo!ww#UwfT{5;Qlwgn$zVd%@+%Cdi6Jg4J(7RNkr zir2*G)h*Ig{w0z~jq7>Tf>Jjp*;EZl$F>YN>^o#o8#qR3M4#_nUOqH}ds+W?@S6_u zCKvb8l$iX2BsJ9%B5u(ZyqUF*qtZ92)84f5wUk>|`KE17)U-L%P3TPV*^CFP%CohT zyi(>~TS%oP=PB}`Pcm2C15l@om!GS~-67oxj@tO``BWE#?OCJYb;(z2v!2RtUZV=B z^8zXD=B51O`*I%DY?%EpF}zWmy{~d_a_6YR_qD7~J&Uby&iqF&q(wC01JN#ZR76FH zSGKk8en9-C{DL(q$jgk+Lsu81S&Kc3G;dr^#l+1{F8L3cL|LP~%WBUs(OaIdmO9o& zycFv~5~Qa+QP^hg4takq5lpuOGBT>kNg zs&1rK)b-H=VW1nHGI~(+t}V}68wx5nkM@lCx1i_RZ*XBX)>T@?dO^>{BGG@+^Cml6 zO>!l>?H=kt`XW%6)7$r4_rc7}l=uDh|8D6ViA_h}X=E7K^kbC&{^pKDvytdu~jlSo{*Moj30088Oo;kp#(s9%9G3MCh1E9H~*M z=pnIz!ry5{M}Ejr%|8<_IX=l1qwl4&D`OmAnP!!`8O3gLmx~_4nxLWQ=-AMSW=@y8 zR%iAF(u1_Tts6(FS?085NaS;#bRq&TT9$Q-X}T#`F2J)tl*G+;zQQO(ULp&(Nq6>n zUD)FtqekUh#jZNhA89Qo)DP~bXIELZ%{@FR1gi(js)JL%ShCEX&X6}v!&NaRS+!@H zom6p$r&~*d@rioTc@_OO)t%!rBonp8JM}I(lp=4# zmh4N})4LTiWC{1ly>(s2)X#EDi^6NEG-|E?>@}w6reOE>SBHBqhAgBms&f#TDA8)j z0K!m*3ro1Eu6V_VXS!C@595wU5?;H-CPC9<(SEuI+NzvOPg3%Sh>C}7Pk14@YYq(6 z#!kfgawd7rkOgTrY?sooJETI=ozqK2B(jn<+eP@eD6fOqA+#0q!;fG|?1~pE+B&nE zEb>Ev8tHdFTM+w%Z-LQGaLT~8@%o3PG&oBSV~kooMGd&{7jL>1B7xXZ`ClRX9KIr| zkg8_Q9#f+h`Vp-Ct0#G%vM`|`g#onn@#(y>Ngg4~yiumI&XI1A^^MVMrE7o@?RnPc(QS&ncWQ-G?5gij-N9&^^&Y92j2d0!Nqy3<(q~trbpyQv zy&MXm|5)?m1l3RCNiF_B=_GHvg*U@#A+9Z0nj_V#xJeMx_<%?6mCVFmni2Wtob?(q zJz;7pRWa8svA+I=_X*0vRhBC@?(g1*2o+77YU|M`oJsK;-=a3m%F;Xu+$ z(urvD{VMHT2O~j~WnWnbTKoYs> z9Gr5C7EJY1&_TXRQTcmj2^^JD52d(Ll1PryHT3LSj=b4*t5R1P6_7u9%->AFIT)jXyvt5c`?s=OnUv$gt^pK~`hHG>1 z{Nl&={GTQgPFE2l@dA=kWVEcFR5oYd5X+=$f!WBn9kQ?a-< z;=lS0wfOZi{nO|J!>&bLHTdi#E}nvu%SDlWRc9mh`)EQp(l z<~WlcVyiy$DO{~@28;OOV6eU)oe+{=oon#=095a|qPlaKy>Fn!!Z8~d>V)~tEEGq| zssgFME{22=qP<=dFSYF$>>-k4kH;LGy$tu}(F!vJztt5>_?LLezWj2&oKhbEmC)9)r0-#R&N}#Td_EK3WBjI!Q z^ejFi^F7%L{_4I?Fn381{)xKd_4)7`@q?+gn4-A`P8CK%@bFN+$7-|s8mzsxj@PS4 zWs>kySBJ#N#?%mLS#tT%_VGE$#jwhvb4Bx!5A_UsvI5rYQ@D-6I%ekTy29?RBR?bTobPhqimdwZN0#a|9*RTrHw(tPCKJBMo9vQO1BBampa5Ex+V*>!_v8uI< z?P<3E4Un)$SfxCC+dVn;a(N65$E;;&OsLl5_0z;GuATa4cwrh{sSrJ+P4pSA&YQcL z*=J0DFnO(N9mlOJ@3|=x<5KYrjQCuC>2c z1ro~`)-FXw3ILaEw3o*CoEb}kWy(AUBR)W4nRR5JOZiFbqP^OQ*XteHrasZf$dVpG z(l25kJ^jzp&Os`hpIzHn5(km+ypjb?H+6 z9dXmdHP5gM`Y#r**~kn}6V_PWVR?aj`x{&h?hIs^G|9zLXU{X7G zWxm+&yiM6!9pp^IsmHr4oyTw|V^{H<1HFo!zj}tzYN!S~Zn$-sfe!Nbu<#z-54C8b ztO>MAyEq37OaaWg9j5#ct@NYXS^IC$LH>B$tjv;qdvQ(`xB{Ey}Xm`Q0Wuv(1}U1)5e$shtosQhf!O;MhSohr zebl@j?bLd?1Q}WZi$bQsu>*TSpvZQ&GIo26Tdejo4%X59K>gz`f`o2A&x` zS(LqFdyw5jJX)*m;_3yuV>|Ijj%Gr#Ye7<-k1!!;%L-eky@H-m%H>z%HJh!l7S?41 z;lF58X%o*5iGYVY4(e$1;tjRRuc%^2RHKQto^Le_v~>^-jp;gtZ}bOwmd=|m*ty@$Qt^f@0+eq@2c-~mb4s!ZEaJI@^{%RtVc`VZ@BfW7Tp*b*6HT;vBo-5JgQs5a|*X~ew&q(4%vYy-Da``En zBW`Ch`o>NxGD68T<0i3f0R-C@kKh2!o<$604cV~rON zcV^!N&|~N8_pF0p6nd#XUM~}+RsE2(>87@c=)g`QKN5m>8~ru2xGeq=M*ebs3zA?e zprbP#pCqZF;;sVtr!Ud7$)1qSHcdXEH}ou?wPy37nC>2r-4V5Wsd3xay7jZ?SiH=> zm?}W3z4DjU;bq&f&Bb?T7$_A5mAf^CQW{apcI+Y|Z6-XLO65>9)gVGM{lg$k-8L<2-0gw}ktpl7U;?WY zqv_InTnTj-5Q9$%x5@pEgsRBM3F^N1_T4O9Uz{&>l6OLLglCTBDU9;&o&Sq={`8Xk zw@-w=4tP0qi$XL}A#TezwbqWRn0=w0=yYs4H0e8gT|yXf{B5&I9C~l|X9-~2hKd)J zw6>O^yVX0n18Nv^ylL~g0cA7Q%wJ8H4L&5t#;&gRB%N%V$^nYJ-z)V_OM_^@ar&9k z+lL-xtD+n)PVx$IW~vJkQMXB(9`u&dwdr|UGRrMy>3RHz^qKBXpYPSev&)%%k18*= z{R=fm$;isj2iu|)J%q$MeQce0NA8~D`dy~Ia*6p)fkzNpu`b~ee`<8Tc zJod$+!%86H1DFop=_QhaVxAbeIFK-n%^IvfkIq94yA{ktSeo9o8v3f*KD+8k_v~3r zcKU-cZms)1VSc(!%ONHR%F4wS^C&aN!R@EtPsC5?kNG@&?$6t!8l5XEpi!h0sbUrz z1<+vA*%iDAl!P7CMqKe%N=(IX!GRcgOptrTQnKPe22BP62FEBGNgJP1#YPB2RS0{m zOja)C5{9#(Ezu3w{E{S`*o3b(uCDmAx!A2RazDKhrnxOJmb+8jG+g-T$(0h^VnXa{ z3=mUrh8(wperNL7mD{1gpTA(_@r|Y~n|JUJGs<4d-2Pu7{cM%r)zzd1)foBN@>fS< zSAR~eI+3`^y;UD*Uwj}fH<5~Jp!M)eQXIHyaI(8=0^MZCcB#f|xRj&2U!(Q&0hzTo zWjNjSkzm@KH@n`Cp%m-hL-CKDisalp_G(TQeP1!MURf&#hPXOW~@Bk+fTJxU49RP z7XD$M*2wKz^vnusZT=a^k^;X?N$mCo zc=R+FZ?tqI=7#ooy->BuHCvIupbv9@pt3I>Op_B{vIdyhr4?5~lY?2i)12jwLLR3o_ z)i3u0CTUH=u{SMWp$n5=*ikLsyu4{zm$gRcVnh7Xfb{;{&L8J)J{C8fmPmL{Y~5ih zGoVGJU5r?jk)hJ|5NEWxds|#`Ec!!_JIu(9F(>EIaKnpv_D76LPqcQQ?@EY7J}Xd@ z6ZN_#XHq74RXvN6ZkyNzA=DtH1Sqj`9I2JcF>xg8%1(`AGSObrCO1A0+5Syp`O$@Y z7JcBZH^Q>CWsjl5>~gFJ9o*Wc7Mbcxx5bbjz66UUJ44R00?U)>N%Y?qu=eXv&tl}C z1%D@ZAYl*uom)T$M2G2lTrki+;GL&@07Yh}g6{mPdwO@ zpOl7eKl*Lru*{q-FmdWjH^eM{LA6z`w6dQG`RlMCTAfC^qrD_QJ2c5_=~+Ckm=aMn z$#bE|qi9bVy{JNQ(`E+I+T0a&*QFFOptgut1`tkOO|tbII-3r89GG9J=h(S!NXTG9eWvXvFWjy(eYPEQo*T#=&AWr{o2VHsBOgL$M6mS%Wolf zQu2W3>qZgvPsM`{8tw_Wvo92Q@-08C&h_`o)lsOq-~4)^=V(MfK_1sJ3{cm)f8MMA z_2tP4 zr;D0Soy)pD?oc1p$(Nz;`cIS97?r2;H{TN)34()LDrGRc9I(D8p~kH-U4bD>3;hs3ovWl?EsVsQNX=!kgz3|7W1fEiWk=cwvdC%m@_W#@QZxH(>BB(8^+Ec3 zQ)OijHJv8(*>0Ef+M^j~)G7~wjQ(I)_8Bbhco2Ce#OwVA$7WE}IBl0cmA~~gQu^to zqyN#9%@)`q!^5Z~ndqy;ju$lO!cm_gBdJ7`>(1we6RaVY`8wSx`-&gAGE6eH-B6R2 z^O$5_uD{XDx{>q~@b;%+%t9(D?X(GW4bMnv4bWe_{6v9Fn0h&XsCI+n zCg^yv0osSAKg&~TABdMeLKLe7xUs$pD9W_~eOq&2RZVTj3@AU_I9?yw7VOHgFxWa2 zb#&@W&!P-G1hY!J{JZrimUhN8(}Lt$ep|RQ#Ty2xS_# zq^RcW+L)&As&{xv>u=OSMt_B|cG1_Um(TCOy|Ztc4kq|Y#GuruksX`#+zQReH1AY} zH@GTHeU6yL*(zb4IV(4_@4S`sHBa0>SHw-X+IX&d)>%Q`0S@>tXoZq-@}wEb>Jx2K zvP|`%{aE_DA-W_5K=l|Px)TKZP5zQBMW(v#>8(*kB#~g+ed|>^u~nhI@$Ci@iTe*I z5#-OecaI)r_0w+(`kq|nvp%7C&F2Mf{|kZ0d70C;(I5a8s5W;W07HWdtz@<}d!`28 zYpjH-c!SQeGG!o-B~24|jMrZvX(=*8AIwik&XS;cy;Z`UM;F#JwqMPmpRcs4zbR&s z$P{i_Ga?;L+%-P8n(1EE*`${EFOjabx_8%|P5YNs*6>~t|JB-4RC-~vGqwou*DxZz z09?-XKh@2koZ2Vz9m#h}pT4ooBCwC+ z67Eh;9jAYDF&2HeWo&UHp>AeC%bWmSJ=6ADOO zK=q`8O|Kn~)t%@IgotVgX`xk<9&djM8Q>0FF}!o_fUQap8HG7WTBpbU}Y=rE_}Um{WUQ$;uHO1Vm_S%4%H*P09vTSfLLQ~CcuXm)Fo zKO11;Tw|tjAH+>Zg?Q(>|A3`Oms4~&H*dv@Zrk}sgLOMA+-CTcDyggc&S&ni_Oa(m zY)w-OaCYoKcPCG{F>55(U3f&vqd;Y3d!=&9GW&khQ$ElJBZXcfllKS;Ew5X3$5ATrgknKs+hUv(zs$kOs<2e zHSbwGpC^k_19k5DPGCZ}0qQ^Qu(O|dYXzjd!7PJnscNlbNkX44;v4bn6Q`xxdk1W) zE=tsZ;c7eel4*li{|<*pFCmYAem_3GlmFoPCPC5`neypb+@+nG`zdgH`&CuNi(+K^ zT~nj9k1VP0ySFU>2|8qfwJ1wm#i;}lEH@DOXIW^D%A0|`L73}Kf?p&-%Q65&@K+BG z@+QtWAVyE|GK|sm0ox)FM>5^XO5#&^CMu*Hy}oxFEi~nr%|j__vXn_Dkkbc|cSVAa z%RO*Xincd0P{&oGzu!A%zRMYgHk&||d!l;u6X{`f=|eq5)%0YR2@OjshIU9!dh_mA!o4Pj%MbDg%OGZ1tNh+ z7?My>Yu9XFwJn1&b`tJ#L+1$}Hs~z>!va;qR1f(ygC&Dv+_Tu_dX=67K;E|KeR8kx zQ>sgieswV^&FfDhNfbOUi-qg)#KM2;Mtxyg_H@@bqK(;NnRKEDJ26n z@4=QvO(4k)Kmp&GZ3i@R|WEjE!$jlx8%aM*=M9p zQ(KY+w}N^qOU|LcfQJF=64-@ym2l6yO^75Yn<-`f>=q%{+s3Z?(5V7O{}a*T2GlNf zB0mFKFM13)u{+Sx0ABggNSK=oA|ov|~m266h#-kDz}FxaENtWp5h0 zqE1mRS7PQaO!AJh*z_(dXFVYR{I`Dp*ypeF7z7Rj(SDc~5YZHrP?I1H%JwXpX=UL4 z+Hx8LLRLYo@Lvmy4Gpk=6T~=KldK6EH>L^IRPtWSVEfbXhZ+W|z|lR<&@f#e`j ztyC?WNyY)l-vXkL@~GH{2K@FkF**d)3oW8Kl&mCf8q1Tujo#HAZK;?eMy_{`O~Oar zBBk$z&Z-#g3y~ZZyZY}WZf`khaEE=O#*eT`u*lFvLUFajO7)y6zQj^lL$sP6aQ zhJ!A3zo_8~!yBj@d4b{1e(OQR>%OVnU*3d(+O0+baK5I6^O@{wSXm8)`4bCKMh&P9 z7>>spM5#obrkS7Yb1M0kdrXASJZgu6OQ4uIV^mQg8>x5Ouo_TgOzY&MlNNF(HX zlwL7T|M%Z9MjUyJg}*q^uLJ zrVz4*Wx=9n_HCqpp}XX5yq;J*j4f)geO5^#M_gr$i60#}>H_4#rh6h1+pu8s5z(~@ z?(!lInSrbp_%yLut+OMK^GLni>OVTD7H>5A;u!L?Ch3*c?pC73R)~D$h;e^4c*?Xi zu^qI)^J0zu!7=UH&RWC0kYnE{NHF%?AFvP0EISKtYOP6=f zIVPZUMGg6(kG+NYw9H$j66O2Y5G{F67YTC)^dNG^<@|J9ixhR{dBF2y1V(s4)yI12 zHz7<^{9b6h+wZ(1u0E?1JZ}66OdZr{3dhPBw=-k^`INWZkz7?w<+|u0NJ)8@^Z(vg ziKs6k-BB$=uMcmyOCsaF)-7y^a&HUn%5-s})BdtQa*cTX8%!+7 zDZAHOvm+buI~0{r4o=S(?qPou{;pT&T`*X9MToD z2zSs4L<$@5J;I&%i<>c@i4JN9P?-&!Z_?!ACtQ8j>&!kc=7~+OOAubql8xloQT1}8 zN~@2$<8u z!(KsCn(&Ixba>+Qt4#tli$N;+q-Qu2k2=**TJ_6uV${l6*)z4`PSh?N_LKbIcHgDy zPJmUoD(hBuNGz`UoWP#CSNBr>;d_-#k3phC_9#9dlfNb(Fh>V5)86}X{;ESE;rSlC zBxWX!Q5ZoG3KOT-&?A_IWY-ZSOqxm?Cfe@=VYRIC<7Bqz&16(MhH;$%IMcRFen$sO zwKbx^v$FvCtVVT#3-P0z>ym!?dEgcfwFQfE){pl7_&qNZ&!0$^57_ecMhO?lWyw#S zK4qnM_U`20)D==8L=SZl)4r*7Pi5^pXf!Nja&HjzL`_9FEQoM2_XEn{+mF;kC0w;f zQ=a+u8J?es7T*F9=%EukiI;LR`@qtpYk0kHk)Hmvll23dEF7g2EHf8P=~w94;O+28 zm7c9)VFi?(+xt~{<9dg#fq4kNt*l%R;o@^I+mHi zn0cH>vp*6n%35f~Ed>8 zdyGJ(^}xKUxuNdfB~U(2uen%hhZG%q1Hx(&VB}Xsd)zekWjoP2^{8-|9Cf~DF%JZR zx$T{(yU&i1HD4?$GY=pP?9kk{a7ET!W?v6<$X=;AR|6;ckC>)J7Roo06##Z$&s1sY z8;E0XfqJ>#8O}~aaAg=*jMXu=i=Ib7^=PQU8z^=j=*u|8@8<8z`m(C2yQwFwts2Vh z4Cn|gqYF1ah+BL;#i9XPKXHgvnb~*N(m8zBa=c#q;-_%Z&gx^atAw6&gnUgmc|eP% z8^-4ffNOkcT3mslMFv*(%*B?MJxhU*(;yiy>`wKe5M^s5BCBPn;1J0auk+ds96^r0rw+>T8f!K#>#(K z9C>T_T~2bv$4*gf8LY0-Q;!2*|IUTRR&-W)zv<-W&G24(rX;=mP#%iU3O)xXWFTWG zd^#h~Iab1yD)b|4MKq_#+&yA--UBCnleoj~PUP58@1B;OMz?-lndI#xE}|Z7L7-XA z!1y#WdQM2XITtO14TTcHp8&tG_RQ)do}t>Sb7UyZ;8X@w9qpA>d+YPE)^J1PROpEH zhvLcv;v;vp>UI|p=1u@Bc8sIj$2!gmrJpV3*1hPkF!5yw)7_Rw=G6q>`@QUQ&0Kwxot2~qP7$A1+IG0%)v~sq5S$7FINUh=VhRcIf z9vZz#z)6D=ghO#W*;Ca%V6YmwB6%=eaB8B+!edhUHWNvce}3bJf8`x-n~E*k%9hU; z`7!iEN?XGqxU-gh*6k{Nh&Q0R=P_Yn)O*(oid6oY zlVR0Q@>XuGSg4m9lKVYrMW+`Gs{?kjZPM(MMNPM2RGcGqjm#wn0(DC7eUe8Y3Hn6fE!_*tC+Ao$GneSO_SI>Uau06?m7I|R`G{4 z3wvv2X)qU!m^GQx{X2^#@(M?fW=kZUQH_pY6V*a$JN+^x9P~dyz^tx|j#s`XQct?H zm^I1s-pm#E1`{rbp2$os!?oBmE8rI9GVA=uWL6iugj?8%SJzm9607m;CK-_bz9&~0 zJ|;arAK#&?rJ{Dvp5f>kY-GECUg-iVO#^JWL$_;5%Z61zjeVoPlUa#>K_-2_5|U0C zy5VBXM2topw9XTAzzC+zt2tD!&CduC)n*wmVN-DbTrGz!tU?L%s;Xe z%%6p1DfhJFO?P=Nq0mCc8%Bnj!TYyB#!L%M1u%O zCtkZgCF4H$>40@vUJ17!^8|gr{#%56r_f)*j7!9{3b|Nd`+oz5v%0qGBxP#yZIX!e z%`0FAwe@^np922DyVy!OD(!4#;lPqK8IZ`ip2cP`e-N!&wz(kZ0pUsKT%Y8bYiXi( zdIc$_Zd70^e<@J>Pj3;85(DW~4#gVoSv)Q2a3$?y#krOw?O7F$uHUn412O&YgWJsv z(fS^-t1el6n9wn?Mz!5S34_E-X>A8V5iHU*dy*)5D}W-I{^wSFErRUW+z~FWa*}$l zD-j4|A8-q4P`ksPFUtZESPf*>#3}RQAmG@W(Iwnv*M>DoZA2HfcnMj3=_#{9Oj0D_ zq{J3Xsp{H~eOO;$!u@>>hqhBnvsFeVa;~J!T@TcJaTfJdFI1Iup4Fm-AOdd_xXZC0 z>KJ{v0t>sH*X%Pw4hstb{WV}PUcY3|uZx7(2DNcAp(yNETr;Ua^(=TsW|-=*A<4n; z1?*pnxqecZWw%tAcnA>&Y7Ko{BtiOaIJMj^5u8|Q_VPd09EB3Su4PbbCOAG?jeG@4 zXyI=U-x!Xr+4#~VFJ3U#i(*r=S;?%bxX^mANsbho4`gd17ok-_HNcLa>&pr@il4w@ z1_$1Q1EIp%*0e^M56okcf)U#VE#E}e4$khaJ0(rP!<%NGP1xW32z^GvbgS_k4sHu% z$X!I5Oto3mWm3VNrEtSthpo#ZcWkDrg3nOs8pa#HvKPRvH9L9D%e%Fd1D&^ag~V2p z5NT=6vHPE$*bQn+7#&3Ol&W~XM3{xA?deCP`*L@!OfT)TE}PjgT%Z;J!?_4VNP0oRUliniO_(a28g9v&Da>ygxs$X$tALXOr+NhY=mUhK) z@M=>^EuGCv^27PszUMecZ=0U|u4ZXn)(lcQ04)UeVgx7*W4Pi-wOVx<>EXMX2MMCE zf?QddP?WV>Eq`Lez&f%PAik9J1A0G261fKU^^@m?!+Tl~u-ljH4bj%{1A=}nmA^MO zNwb~#y8J)^W&Tks`5Jg6(>%a@h2J0`Pj-*jf21dalL+4YYe+Q|Bl1NG-z&%%N2gbgGyU^A4Z4b%=nRaZx0(A`M zfgMm

(7kJr0h#2ruMjJ5Q`)x@XcIAOnt78A|XaTC>@OOqyV%%gM3DXiR+%AMy`#x z!kFiZL2|g6FY&{la5Bw_?4xU)ST6r(T!d~9fX{vPLjK4P<7cL`Llg0$f5Liq=iy&i;SG4`lq$%AQz1^G4jU!Ac}t@hu`%PDe$A77K&fh(=dQ!0&SEJ z#i^=J;}*dhNhD7tvO|?y zj+u?u=YrcA*2WEnns9y~H6X3&zd5XII2Zm1r3>n=)qG8=nbGsNVZDG~8~01Nvj*!T3KEuzmWGw^m^eK5jr4NyCiy z90QI!EkvBI1MWVL)QY=eu&SFz{Nck-AzKlo$pcs65stOU05^^8**X}qKn3+|i1=Rr z%o@|DCuCH_jbzEDoq@I*8V0pPw#Z@CXRnTnkAzbL(+*MkVZe`HnJ8gT3mYeEe@7sN zf^-onY9TH^k{YPcktqxW+ z1pRaXFDsOk7~df)rm66`{V0>2> zou67^4-c&g?jyfPQ=w-=#pwI#NlbMew_#24A;J5o2jdGo*Q_=c*ls` z8jjTMiU&66${+&xo3J6dUB;o$)*MZL>TGo3Eg4jpgCz~<>c@gum#dgMJ$3PoELqLH zU;nM|u1oo!VS@#lUx(KRw42s85Tb;Ni$_7jwubkPIH-m1pRo8u-LjIO>F&gqfp=c9 zB#l(eZ%61MbQddUaZg;$T52-#>(fMCpan>CCt%4iAtCZ5=RyON|+{sJykHH|99zzZ3Wh7x5?1<$42JD z3gDBOGGeMDqf!98$?imJa8VpNGf-Y_xIWtQQuLLiz%=3)bBJPlu7f zt@`g=79NsbpZ36;n2jIo`_UdzQH6X!8o=bB;r0M7IqD}%zD3Tfa!-H5rKqLmzl%3( z9zX2cKuS}eQ?Ri1kAnc>*fpO3G9AHrm#dUdQ%aWt&iQhK+`UcRQBu;1CFf$ihEsz3 zZ?Fm68u~PIU#m6}G}mLh_oXoBT z2;AQ5`&bDA<~xX1`3@f6D!hw-Vyz}TlM5y(fST~nap9wc=q2BbY54K2>6z_TJzk$X`$iG)@+u`}p!2DgDImbF{k$VMi2hsrt{O$xIt< z8`itbz6?v}r_=qrRgD-JBo62-@Iz>52sa2HuEB4={y-M?(DN4QJgAGP4Qap|IUhlR&2am5MS756At=OU05g9S=#Chc`F)~tk{-*8b7aSw&cGu}^ z9zlvHRja{U^qHbI*_L9`v&hHg`>uR{5hh%%Q@wRonkB;0Wkt8EtsbM7f-0k4KHZcx z<-u`j*Giv!JAK#2I%PrV&nOS|SRsL%ikBt7!%>tgeNJ4Ki+CKdAgVvSgw62X+FpG( z%^tYdDxsHMpobM0qlarCLloK!{+@N^92tC)duHANbiRdyhNiaGW%7xa~+>GQDxy*6yJ}S@pd$0)m!nvwKtUKFyeL@@MhMR0g}J@;MAR!Lxe*t2|IeeDo!}3l+=6{JC&F3Fg5ce>1$Oi=B-7V2p%?sM4{UU0U2G*{$OK_M2bq?|Gr%#)}1?N^r-0s;o=7a>w$y;9wR7A&|PTD za6(3AlB-=CCujDf$eVXQ7!+Q({U@jl;J6-^(ZJY_??8f5@(&BpzCrH9bAAn72gLHI zz}VH=o+QhDF$ACq3b&!DYu{M#8!aR&*)hnt)62T-a4I?ymI&T2X?i9Eg#hxFrjB72 za^IJib-~y|+hA_@|!e%f7n*m~(;+f*O@Gl=D1N+x-ctxNl04-hanh3M6crJ#rN z7aFdkkuWq5zBVCCFz2Ywnt7iZ7{W)X%f$eQkDf&%7!$b$=^n{YsqP;qZX=!s$GW$4` zTNTOg_~g^g%c)}jp%`ISfJHr{_YvSkWDIV8ZzuxWsv)OYTf>%vV0o5&)-A_jme{7K z4GFS99})Sm=Nv=dHhGgCyNHg0y+xv4($Bz{P$gi0)YQxQm~@O>12{x{f*Zc5Wrx5< z@=4wmIK2MUnE+k|DSe;ES`*Fj*aK}3UeM~md~E6LQ<{IuoAayX%_3&D?+WjN4(%>J zenZp-Qej9xv1iepGUfpc6;^9=1hwitMZ`2%WGhMvR&rn4|xmV8DAnyI3Y8- z_NJq2>}nv(m!skiril$KureE)70TQcyPUuK+sC0lKm)u^CU-ec$RGAW5U}5ta>sB? zRMI75$?umgf`i7&@fDg*$?72=@lKV#`4_kp)#&|M%kBSTp4${m6L2nzm&0>evt<(t z12b;StBtSHdlth8Co6zI(jFGl1Bi=BFXvf316yC($0vg$Y<*u6*p{MB#I73MT6tTg zfKt#Q^3St2W(64?@n#1kL;~43G9u^1zpHB;H}7! z*6i|nui`&gU)b6CmyXKXchTrUGop*--LB;x*L$8G9oGu3bQd6rZ~hF8;LV`+h*kzt z%dH-xjbVd@!EMQ2cYxc#0PXJ>uKlOqgM=a%*o(s2_-=w7pcGq)~F|*>n z>af551@0Lode7U+iTJ4Q`5;$Sy|lKz&N7KXy`BX4LV|Dbr6jj2F^9*O!Pn^AiaQJ@ zZ(x>z-Bsf9f8&oj;1^fHFh1rLR4~pNSz{u+a!gu!s^R}MbFDFLm0|dtLum!E-GPBP zuGYymHrlRSbJR^4b%IzCrXetaZf#lRQc{bhgbmnOQZp`Xm}D2WOl1MafD$qwnZpyK z8{$}E(Zb>e=+X^VggS*lKfKpUXSGLmS!d7^@rZ`0h-y zJi=>Ob75afThl?m;-9`vt=`v3j11Ma4wLhRp05Q5pZNl6iD~^t6cZ=A=?q}|Jn5wJ zUJ2`LUwbInLYjS!M9DTd6{pIBXiKH`<9WZTY37vajV5F8(w6o;T>e z$`gmbuuCYtuy84TW$pYo+8!#><8<~N`C~&73e-vG<-#dE-A#d#+>fl}x6Up38L(~3 zUK0Ad#Xi4J3kPj9Y~z%eC+fe08nQ38Hl|y<0l`EVWWk^4+Mdv`V5LOsDc4OTJ^O1Y z5tZlaT0i16a$5!vai}=K1>9SK!a0jHYoHkogN5M-tk7i1t%Vi9tqfCce}yt@#&4J2 zr{E3^qG_*oO5FL|-+Lv(N_s8NYX%a#v#Z3NE)%HMQ4w|bgtf2;Y9ZjE&8YEuY z|EMdUzDz~FKT*#O<>Td*rL>)B=hAPrJFp~*yW;pt=K9bTii$9<*@>p-g{8}xf!MD9BSAydAPb2OXMyNqlgYdjs| zmfaYr>kR)xVrzGN3kX>pj`3Cd9hM`aLGvE zeWs?D*lX+}FtQw8QAi+Rp|Q6Q=Pnnh(spP2XHzKSl%NX@PGfwY;X(mIT?MmVn-!f zfxW1gW$Tp+y^1-kQ{h5LCA$-2Bylm)_ydw8MiR@!Chn9-8Hwbnp`o~c1(ax&8nyAi z1B?#Bgn;KfGh}JidV@lzA`AwDNK;z$;bBFYN~F~lo?m!*3vMD(W%87sm;9>;qe?{gf_A@1wGuIoOp>pIWR`8hw=d1s2l zDse?|1VL8qa3s4T2pay0Mnpy6$Lz7*Mff2UYQ4uAL8>z)c>9Incl85~u6q#V#6|?6 zok0*m6WTa}Q1uYxTOfjvt|5qgOn!rl89WdP+T}oopYY#;ZDTV05Q}m2iG$Jl1plLK zG;SV45X-t9WNY_h13!k2fBfpU{OV`k_3G#yM~RzvDZPGhW{Zom&#v~@vE8R11?=2O zAeLo~7E*TJxng_Nrq?A89-O#%&e2bp0-mqxqafkT@A5&|z&0*Ie5#3^ z>%&9~?Ct9fIt4U+nhi=hp!2+y|FihRya z*lPFL{7+1+)a4UQzpy!e9$$o|tCpO>vW!-7B;P1|%kRuc$?7@8JgFhF^qaP!!oZ_{ zhFPE?VI99R%uXSfn7AXs92?4E^dM#s)$TJ^R(?pOy;U)fN0VG2;_sw%4LL_f?E9vT$S==x$;<4-x{BOB#W*Mr90S< z?%(I56_Z9?i?1$5tFIF`d5ii^-a1_wMR!$^(Uy-@bf~@? zojiLvAY)K8SuAlKDLD_uR8W?s$w`<*N+_e7UXw1kvm}_+OzW-%zHb^nd&LbEb~~$Q zk-48qV*8Z*x`l4WD&y(NFG&~EXEVjEE}Y5tL^n%BMAGdJjqT?Q%G668QbT2nIHST+ zVh{4`Q-)~O?BgZuD9oQrEIyl*o__JNxYhHs`A5;0?M>%)Bp*~K!?wZ^iBDZnVRy57 zh%y%*KdNI9Fa6f5@{3>D0<-Tnm!*k_n?z!jF1g762HH7}z&J}=my+ol zir?&yNi-xS*HESL*SE^j6u}DiM{2qMuzkp-Gs;y`G4b(955Yvo>$b8{CEE#%O7vLa zvw(ZyQ5o0EW^5_De85CrJ(5;$&*XmZV}9f z4OF>oou}mLBb`nasp*c^dB`Cm?5`LjMc$oTFHx3aCZ~Gf6&XW}Glsx$Lcc=Ad57XB zQrD!I7tO4JS8A_|Tb(_d{}P>sDX3==yF)`UribRN@TQuGtjDL}WX-DcFa+v};=EP- z3LfkhS%P-9CNM^65-GCfHR+Q5KbT9!Gxfe$e7KSaonRr|=SCLAP}`^D3Bb zfSiDGcWj%u#4PCK=>GUc;(X5P@%;NhxR7W=(Q^GT7HZjxKvRGpmWWAIIo!Z)2^lox5rfEBgKU>)1(iSe+TF&BX zgiOd?pKOswePw7Ijk{vWviSGiSfR1-&*S^9`=5A}%1=T+zEJeSB7)bjE42)fbZMLL zVp7;9hrYHj)iiXj^NdKzY1G0bQIiVv<6l2@W)rH{jJbVZJKr7F;_UO%t3)HzO;yWf zOPl|-Zy$-v%9S05hFWX$Y9}wUEcdH8f7`_$m)y}dp~OT%pPwn}d6_MS%l-XXJ+4f) z%t6l6@qeCm>aSL&mTWsU7$ zHUH5WG1RS+m=@09_7jVLo5kSm6y{SW&BCN)X{fFYexA9Qvh~(!ejjPK+Zi}GZeNo%_NqbuE=G55 zA~3F|9NtX%=kSG1G+l==r-wM=Iti1Cokto!DuTc7niQr81;wurxO&|RvNQ{gdZlHp zABzru3^U$(&*nY-{AQQiNi_C0UHD+tEzD(M-22H?@Ufj(0%Mtmsfd$&z03?PnhEvA zW>22U&qXCkCU%l87=EgeNw7KPRL}R!6K84w4zRACI;w<=F;Si08K2Zdm7ef#3-`+) z3cNsg6Jv7}b|oo(j%wO<@n&97+7$QeNYDzXO-JYTm1DVwt^v zpyYgNzt!3NZ9+GUtv;=4(j_I|q)J!I#@DXGD$)N3;C z2yWT}X6WmMhPg~V#C4N35f#n3CBFQ02zSe=_zlx5<2*j>ETgM=m=&S&5QxcKF-}g` zLWD|5c)VC)Tw`bXGrn)&npG?M2BV|2IZOiBhJ9jH%!#%M>}dIw+4CU?$@x}Al2%m- zHP_+^*4vcCFQiJBrDgTpq_q_K3AM64n3o&YcdFe$jd!RThE6A+4jKqZZHeDPO4h2{ z(E3E0=*bp(u*xJdQl&)95ThqC>RDEgj;>{OV|9_64r992IO1=a*a78vj2;Uxz$Yo% zsUiUYE7N+5bPQ}XJTQ&z_egEIXdk~~7U;!QCNkw|?Y5M5K?_-$&r&UwX7NFn#FyYo zyC!!neh_?UPILT$%kYv?W5c}tknooc2Sfe{UJl8~h-3}!42FU~z5=h?wfUBqbzt~AkY;b_~0 zF@2rhVNJx~Q5u!z(ty(L{Ree{ZQ@GbC5!#^12R#>M19zHasO2@hkB#${|aUN2qNNar*0ZQD0Iv zwW~QSPBCaE8j~Ic31?oJG0{wY=3chb-Dm6)W6ybvubRi*8R}$;sL8U5_YlcJY9r<_ zd8`r^{)b$wujFH#_rIiF1!fW-B|o}btoW}S1El;R=~T`yPW6VAQO1d;Blr0GIu_4{ zr4M6F$zz**vDILotH0D9V9Un1zh=gHuPR`v&g5?^>{lzK%mq0KJrI5{cJBg7mcclsh3p& zZS(&D)HDH@`F6i{m8JnI)3swabRi=MD$bVRK1Gc)sB5`}XFt_EjE-NC=1P0IBRfZb zBxj-}d_pI>*9V@B6@KJ6dM5voNYbStbUpXYR5B{&lx*ot8eybXqc!*IAfkkc^9{Xs z7r^E#zqG1Cw6nNL1(x>#%*wjRijGvV2@bKL+(%oYJe9iZo1~1-21RzXlozzrW%clw zax&Qb$Ot+kXd_z5VT>QF65J0nATUyCm40L6&o+{#9ljNs?VsV(mK8c0yBHdt#|k9( zf>RA%&cClQQcIjx$XNBRHoabe`+Hs zxg5;5?@HstQ0ebq5_hWD)g|TB&J&uChOABZ=<=XSLgmz(_!0Q8#@;x9X`L^NL^b*fAzd(>w}AXsP4ya*%aSJdMlCI(Se)ky;ku_u@FcD*JY@9+zboPIE7cd z^Io{H2@|*hY}fFNL`!d*Et<)5$^UcgJOurPy-%!N57agwNc({V`{TN*-M^FZW%qvV zJ-0_ANc-E5go8|}acHsyNk8<0Ko`VurLpmtlE114xebG#UTmBaC zhPf6Bn8Y3u+42_o=KF9F&cZlP2uw(Pn$EIBH_1Ql0FM=+7JF~dLchlubvN@T8wG^R zI+EqBpfToQ=->KBC~9CI@-1G>O4y2g2m+L0m2gZWblh(-GV95eetHPLsYjBTBY1Q1 zc9U|I$#gn3*X70~#dql1r%0HH75N1~f^8Y?DC+Q*u*L?K;Lp9`B7zPTH<0)VfucB=G}@Tn}?peTeHk+vkIo#w!#)O}Pn1`S&P+u~1G*{Fu9^uHrrV5v#)K9L+U*{e({ z!c>afB6Sh!4V1Uuh#`w~CcRDiyU54GDn$-;*M$WZ{6ISkUOZhAsE}mlEw;&t+3Ktw zZ>}_5u=YJ0kj{fJ;CUcen8)E^)%vRI)O5=K7zC-6b|w!TjOGY)rOo?WNmiSD{gF4} zrDAN8hG~0X%!tod(_4g%{{<&$u{QP|Z^NLcJgqw!Hr(x8ECslZf32CzU@1fF^nR{% zP4@sAa@bRC6I%i|bGP{49opAJBo+gka!xU-Mj~RrN}y**4NfnDz}U(RO-W5zD4&U+ z_-~x|8IB?Ooc74<_P|Iu630ZmHAn#;3_^X4FwVC=@Vq#V-?qG|B zL8+afL<(dAZ1mBZHHsAIB-<2vw9|& zQ?w1`Gm>ql-yVYFZ$p&#Jlz&^|NBAOOBq^L4}+GBVUgNtZzakBTaaJa0Hwh(gpcok zL1JF)ZHB1)6=y<6D`VaN`0gsGh*E)F-5Cv*|L^VJ!+WuWEesA=rd3-`t(v7DeBT%L z;nsKyKt$;}?y)LXkrFTw70wj1vAkyOwLUne=~u^jGfW@B;>vN6b!7*^B*!_c3o8ac zvwywYLoUhuR*MTfD0uXqLL2=yH-!qQVg>DnV>2+hm*W$t%2ijnJM!<30m=0Ojw!B_ zBc=CR(D9vECt6&2TzuF>?P?~RliiiS z1asdIBP^TvQ*tQq@8bu8)AXhSb2lEQ^~1JnTCR%ay0y;_KC6u8CqR|)j>3g&fVFt@ z_EfXq`-|r$flU2tJm2^v{g?{sWt>M8A#hZrPY|)s)<(w_P?lc^+|ya!5XmYHK(qm@ zFl~nRyQ0RumkbA#Tzd2pm4>T;z1=%edv!B1dj0hnhU*(zR#j6xGv}8EV`nvVARU;d z52P2pnl6Y9r4prsuphBp_v<1{zJdnV25Fn%%#YUsR|ns&r;Ch=SjwXhLobh0A+|h( z{ph-3)P~tu5f?vR!8|@X8WmS6T$nhPjQ?TQ$x!NZ`-mqHiIsoSsWhD?31icd;O4W{ zK46BoJC0=XO)CP4Ro*Rl5bo->vD{F&|U`IoddEx1(_Dum8DYTN^ zSac1LjjsEtS_pCzVVukFz>U`2K+U77==RXj5=7eEgF`pp|b9*IzNhGnFN zI$OfqCit|cTW&>M%11Xr(y0an8_~$Q4~3k5OTAAJ6fKVit9G3FKj;B6t$4s9K z(MUbg7Os$CJxP6@7%RISKW`Kh9N$cF@Xbhf-`N%}#zAqFPwCB~ysw z&DqnnFl0D?A$`{2i4up2?I8G^32NtX`(J*Hg4+KZ6LbZ zC1A9?)5dd*Qq)idsw3MK`X#N}Cg{vetU~kWTOv42?gduzzSNfVHY=k@?f0rcWlH{1 zk_=u2t?FbhtmLW$&u5%iZ7e>&KYnE^SC2@Hgrgw#_%+F)z#Ll+B+@JO=J`T#W;E>w zcD~5{{kxQR|C1FFySEV-TC_Sq)gS0G7ts8^Dcz5AyTG3GuwhQJTGhe$l`o1sJ8}oF zgTzFdQ1h+oN5#J%Mrlm7M)XkmuVo-?05SUtsB6deW%2PP(^2t$^!(12mavvkgd()h z^h)0IPb$opT?9st&puc-Nrjb0x4^bgQAcwp}VIHeD{We}Q z5mKU9%Vy`65QI|rA(g+LBOU0mYK2IAFGp_?o4-N5j4~mP^ybA;?fv0csiC)3C)tUN zD=_WG<}R_Es&MOw#2Pue<>{cM-=#iljf>O*!rTShJ)z~adoPCBSUi&q>@1P!Ke)!Y zo%8}p1oo}DD=E8+g?zV-OITXMR(Zy+kOcd<`{_839Jq;dh`R+7vv*-*Q>eK-IN?~s zxuX|qHh)?e4i(J)ggiZ2q+jN7H(->f`2J+WfIve7WEgAz-a_-6Mj&^b*8_PDIe7NX zGHToH z+*8P#<@8En&$^_qr)CYS+~QXv1k=+u2Cqizp=k@S*U6$8|94Td`j{%Cw~9Qq+mAn0 z3J%bJ03|SkNJ~Ad+hO?I=yoc^#^$9K+_ALZH9vAfX>hFf>?LRti3ZY82RU+PiNf|- z3{D;A9US7FiwZ>^-8M<(lLH+DEAV3tJy~qtxbupEfAi3f=<#DBg^6=~slqYYG}$#p zGn>4@t>H}xj6Nh;*z*xE4odc8x<6)f8n2_~rQa&>qZZV#E* z@`5gnA>oL!AE)fd!68L6X)b(o^q+~5oR`#TlaqRjZWwKIr>*l4A6~cu2?n7{nP{X> zAn`4^(m_PzFlHB^=dL9iO1u5o)U6Mj-p1nt+rlZFeL!A?8i+4lx%5ERhH}BRJZwn$ zn0eK)g=_7kn~tlVB4&FnqA)CB)+LtZA(h=CS|hb$tqs)=>N_gamy2hx9pBf4cpw%# ztKD4vA!~1(NLzwPE^Hwjv8&6MYj$9rD>a99pGH37)g{4B3D&crSRxkD^kRnuRJ+4i z=;OObJo6BX!^@KP|JeoRZIoIW^~`N_{f>G1n5vqcyVlmFwol~GDiaknk8D?%-7Frl z+kEP@AFf~e@iF@Xz2^xTKzMFm!B*Z+EuP^6V$lGz(UelYK(Qle^fwf~E)f_c+-m(b zz>P3jOd;+T^BWVlwj12k{M1ES(D*2-9HA2AY(qJTl1H?lf`mst3GKzlE>;0&S^4YI zNRj|xLc#2`!D8!1Iof#{H~yuYmx1F}jU}Px@p^1hX8P^}s+>Fm<7M+gShX6OUybWZ zeRBPEc-lI_+L_X>mgmfkr*FJ94Fx-ieAIWK*T^B?4igv$-e~vULA_Zu(i{W^F=&$1 zD^{R2+{#SU65w={3Hn$IRTMwb#z=9OO9-cp^RD;KjB|`^DfchVNU)R9y|OE1JO2rp@_|NoTtZB9Apj zW_^KJC3P)eX=;#yKU*s0GYtipNV@6<^^L0G(%wBJfM1kB6_Ojl(!6+NGH;4xuUE` z>W`Aorti3QT%i=N2SiEq0)nlpfD8i=Pw%bbReX*{48?Kk{imy?hrC zT5Hv1k?f<_vBm}?BS6^pQEG6Vck<>I%MPQCp~F6&Pj>V97Z9N2P(Cx-mSXCM&1Tt+ zRbnTrSzTwLnF5qId^0}#y8G|#?)2lAcG?q&KMZlkPt1_r-@ZtJS*Cxzrn!Y6-)1M( zI5u1avyBDaT~oj)tHmz<182gUF3!}4q)(7rmfXWhnVf1TorxcDdq6r@d(-*+Fm$;oG12a^+A3#f>0 z0QzoE^_4z*0nvN0LehO;k4CaZ`_X(w(8*%yN}* zO1Mg)#D)7vdU|i4AFbVnLT$*o|N0w%ZT{N8BU~Ak6PhIcH8{7~89u1TW+Ss9T=hr@ zwCH0@l0%x@TH7O%2UW(008h2$-o7Swx2H5}k4N72do!7+ua78Qw;N$6q`UVAG4Bu< zMir^GA0h82x!*_YeG(x1)~9lCm}*m~S#tN=z3Z%CH4Fs2z@fk-&d02tuWW1IodyV| z5Dg^8^(QF?Hk3hMY&hX9B5CFS$6F3xcG`pn-T_S2tzUNob+_Fgxq*_#Pq4hz&*obI z^=|h~U+^XSOjbYsw(lYAUsu~2)T=Z3%|R+98#a1l$2-wqG(P$C{pWnXLD}cywlM?? zNx{T38!NP29T2@~=kmX1vXY;Z9G`24sTA7Q+(+8N%oXV^{{r2o3w1PMol*+i8gCFV1&)|-kw65JoM82$nR>S?G{^%byC}>laE4%mYj||Cz ztFQx{(^+o*o|Rr7G@0Mo1d?GhML6*KL;v&XlDp$ql3+45Fd1L@$LmLC0fQOU--G1F z%;el+M761$w}8kn1n%9M^OSi>(47$n_{e|Q)`z~U2u|I|T8;%*#$Ui)C}fQDo-@xe ze+!x&i3!1%H`>l?1P+0H%eP0-b%7O{$^h%^}6s{%-u^F=|IQ0F_%(y305SLX#%mKBGtX0 z0^~oTAqFFZ(d=a)G#%LZx5s$~%v`3OU=BK**{q&FY-`PLaq#W#gXpSTa7M%H$9Zuw zgX6bC=?Bw8ut~D9izQHd5HqQ8c)ATatte0X-9A$eNdtMT0INKWg$WwU8E+n`Wl*!k z%LW^huRTC`J)}MIUxgl3fx}o$FF+zB@}mU3VJ=IWoO(-2ulx0h9|_6p#HU{ma`PmZ zM$BF8vIYRW;0chf1^E;e+A!GtVX7?!>zAf?RP?tAFEk@>u4XMpnx32+yR&fPBTwbvre^lk4t(Ei=ds zhtJ-b&T*+u+0L?TR&pRCbd;T-5POlsigCfjhM0B3$J^HIpW-4|A8)8ySCn< znH-uEdXrD6g1*y{G^?S~=O^owehm<%XMj-xmY zWtMuUfTBG}J6<-nQIgobx@!UI+rSJy)J@Aj`Utlbad3?|=k5^dFt*8QIb}kEd50}_ zvMRJKTvb4;8my-CHDj`E#Xha}Xtys5KFg^0L7KC3%`Ecj#X1R~x zMejV@p`C&v zch@3;Q33R({c*476HFqY_5b8YH|O@OHTZW^2)D%in0c$tULx@-WNSk64`DKkQE#Wj z)0yAc1pA3=*%rpGsK>}LHYrl-PxzOkSBS2bT?FH`@F zjq}p6D^KqsSH|`R1kxX4?>pze(d=G`tTmoOZCdHq&gY6>{7Ii3==I7&1VKg@`@A>g!oF@TL7cVF>Z-wJMV!jA9 zUjM(t$kLsdugb=hDk+hNz+-2hP@Oe+dY<{yVCl?OgN3?=TZheY^T&InN~E(x&a(>8 z-zA%Z=?ATH>u`EnAY(w(>R4TyU}2}Of7sM<54l*MBx3F>X8Zy+ndA1N8HTGeb)^Uj z0#N2-cAUw#z$LyTIjAkicmmKzJ4Y!_$8kgT#82kkH?|`mT1Voa1YY}WS5ifWPd9Lg zXY!9?l6Ib&TPXOXRy>o}@h#_N$LBt7;;r8iOefgm-dH?-0`jZffl7g=fnN9RB{3as zCspSw$fYM8=jn8IzLNO^0$r^~KxKmI5ZLyJDPY!t`XuL3&vdJN3YpQh-g` z{qtP)W+-ypqgYhuymTA$K@b3L1A-5M(ttdoR(Pwi#?!ZLLW?OSpf}bxJeF>6crKhn%aouaWV*IE&t{WI zhNFtzm@Z~padSe5hke_G6~~IJjF~3-t8O2fC`$WM#@F$-vl8@KU*Fsy89*G}V z-%|Dky5~#9Wc;rAp7Yd(;$Oc3pREa{gw!G-vmpANH=yst^dVE=Nv!(1zErmkMJ2`{ zZ$B^=xdaBkMqus>f>Ob%l(h01cbB%!NHRM2AvD%SHQJAzRLpDCL`HpS1wNKvEJ#~M zYSRQbYag&Nt0EAyK>8g`5OJQ%r<8a%S)@^Kp-jz4$^MlsPx30Hq>6m79z5fb+M54; zB^qUMm|iR{5c4S?Y`G7y`!Q5N_q}^bT~dPT&e?nl`o=HTamg$jij%|GJD_KN1KG3I z`@&~V&W6ntE59q^QK`NLIh!X_lCwS!O=6hZ0vT1R$BP*@svK zkUwZ05PV^oc?={79DBoguh+3mUD{z=%1z(2>}TR8KXAO8M^~-n&|vDPKCEsS zgXK=n4XTPL`mM2oKMt_FEH|qsg!z`q2zwWQV5nRi5}FJWzxX%=pS1iHQF5R)lqHV_ z#;^RFbgH_m1Cgd%t}*UelvT)TO|i`F%OwLBE|$M%X{D>|{!oepcrwtE7DMI?1$g4L zQ^uaGfv@O{>vPQt9w?0;c5(0Sse*nrc9Gf^zK^RHv~I-EuWiDc<5LOc@>6z}j|-Ph9<=)Eej-9?ph zp!t*prVpo3CrcYXmtPxIyb*T1t0Zr!ho`a}^0mEdK}+Q4*(F!xjo()bgWkLsFVO;k zTGq5oR)qbGi#bijP29YcqUN2JbzQ;)G`^(kL2vjK?Bf@gq3}IY*nAB5Maa@cl+CZ? zz4)|=<_D9LG0>jRh;(+F(EbP@@F>WkMRSl}Q zShoalbTN@1Zp7`&ICU)=0;qEAjklz%+4oU2cA{a=e~U}LDo{AEpi4|a2 zVDq`9@;08*1^^@`Bk}dd2`{E&ZfSqwNJdsq1A9NPgr*kF2kEvVU;RP-NEiS~9tfRw z$zb?NGST-+jQjpe?OM2rUB0wv(dv5cQ(WgWt*|{D-BIn-cGz4lACy4adq%SN8G-T0 zY3ofrrAM%+)L|1<5y<)pQ^Fm#&JR=;JD}AJN7c0nH4}~ z-xuw5IL>_chHVn8@#qn*(}sc4@=d#Y7ucj%k~2!Ua^y;y?00hYd22syoHE92Z7k__ zQ>DkfJxgY_P(*caeOELSp!R3G%D7RLpq|`W;4b{uq52;0O;*ou0^{A;{Cx*hcG~xr z=Clq>?im zwvAGc5{vwh6KSRg;*dei1xT7U)2BKuZlJREFBw#Y$FJ<*s@o2Ug3KJ-`etyfqTq3x zXsvm~()hc*=O$bOXRM=t4wl`XIwMQxD@`8>eE2&@{yGZ^y}{#?bvX5kBX|L$ufSDz zyB2X)fzH<^9q_;BAY6EAvKpSosJ5=GTqSLK6`}imoOc_LV*rx98AxE1llNHG?QTh$ zSqC~eRZ%H)CtCPF`u_mGVOU*Xd$96b9&tOeSB)%f0 zIZkYWFE!jw9*YKc;F6)(e9L%Z)xZd2%~xSWoz$WRSQ3X14BK?%{1dkdjN>|Ui3oYR z@tqUeT79oDNV8iwwIT0TGq>XLRH=9ZHuS(f(3mQ2t|lo zQ_WScpvp8jG@ZX?b2Dp4@i3YoM-Pp;qpPyUyG2p($Gz9ThFKgJk58dHCJIV zULgIHc0JjLp1Q~?2vG?()bvLX$d{e3(Aq(u2$hX@nC1w&?P3UjoVOWdX@8O=%LmVx zuh$F^w97=@L@aAq#27u(-H&3YUH|aa{}VJgA{9QSbj+E+CzClEWQe zZ>{j)9l8aqZ*ID(e~5IZcYrG6w$f6E>vpQ!jJT1-vz3i07S`WEc+`+;zXJiVjJ*|%#ZBr!KE)T zY8O}q;VR@q+Iv>j{6W67ANOF_Oq_d@ksl~U^u zP3O57r+nt$eu-Og?opt@rpuv0YL|z!2<6tW*1z?l2uG>K`rZucWBx8T5JU3N0S|gS z%Uw<8kVXJywK#prIy&ka2oj>1hpdX_!W`>>bW3MdN|lQ#qd$WCIvXY2EK!c94?Cx} z%!cX5n6);pcuKA~&nVE!?8XX^qY(8d)ka^;*w;%`V97CFn5A=Y5rZw9yG3mmashpy zB98qrn6rap!CCqBN~^s`$5^9kw$MN0+>kTAh9J4;<>WxP?hrR%gD4%&fC*R6qr1%rF>syydNinQBA#XJpo#bCbEL zb9sPBq|T~kaCwZ}#usn4fP`o_p5CI=Z!|LNpTSQ#T5_ibifTr2S~1Av7D~?byFhi6K zR%Sh}$6XUK*PK%B-!w8y4Lp#sM5#x~J!EYJMot$shT4P*{pQ*H%nh~nPAvY-0&{`D zFsJR={Dy`B9RVb>NBWRWZXaLH-358M3u?T0oOeM+!h?kqsPicgz{~EM)6(u z{2c62HxL>Xb5Gu)rVBL@PgUm^@oId<#TtBe$nhhwByG3WM{#m&9pp8X(z3{}K6=x?-`ar-^xJ^Z-Ar zhYlEf%3-Xa7PrY2yNKOY0&06I?YTgg|11=2Bq7lh%Wn@6b~jp#3Gq78sS zc3w-7cRy@D2g^*hp(uH$;mh=)K!6jd-&aJw2h#0A!c7G1Toe>?@9h+}DuJs=bBGuE zKIpeEzs2Q4AH7BzU-Pj43`*y_b<@|3O3~XF^)C_kLZ`^7MzJT)XGc5 z;m`Jvn_%Xv0OB_GA;XYey9z@m>r6W!ZgxaLB@Zep-fk%A>y)-|3C>dhWq~h&97>Z0 z9k2SYLni^Rqzr0~)PnvKzYE@U`+H>t;`6zn!@$ku!Vsl(!VP0B+K+&`4%#7X73@0* z@%r`$^^sXc;EzB?3oR~F5rrE^1Z)kt910Fs1h)z=_yNf9g#ex|3kLMsavIlmNJ$n6 z*Z%ukxQf-esLe2hpo4wYS%jiq(q@SYj>JDxA7)LMCvP zS}?~up zBKlG3W4q~au9?2A6H<`LidLidJz^0JpuEuy1a!$bkH8dX{-hnvxqn%HbJWmYsCTae z35v&ds9FgK6E%@PASM@`-%=%8b&>iYw;}Vh%&T>RJ9BVSBw!Smbk?v?A`TL!3A#+4 zC>xclqc-Cg8gWew?nRl)%>6FF?Nt$671>=91r}ahPfFH~`O&KesTs0G3XHyCir|2`A>`|RjdDsnwn z+{%c4c2Wo!(uE8E^Sdx?Q}lyc7I0rsLzgW^JNt)v3p&ri!bbH=%L*?;-1~G>!~`7d zB4-mDHS*pa(bOa*OT}D);wvBN;l4U*1W+$~)A8RXO{blOe0@|hn0ADZLLmDWE{ z13s5^CjS$;dOOnwuF;Z1{v&Wyx)vPDQ`;_vixh5zvyHxql7jo7`0MAfDSM3Oy$V8* zLmNkG*Jq^|!8O(N-DBG_p7AMA|33^B$Lr%f49Knv`~)w&@n9}0^0>AJh>=|E05Y$7 zW0f@CG!wOif(p`W(#3d+aN$>Q&n_P~R9RLPQC0Ebm&Ry0G*F%lRF$7|M--vjwk;5@ zM&9=s!)L!dp8fexa{ww7YCn%~>z~41XSjh}1h-8789g8tsKeb$bk)*D=JGO^Xr@Io zUn*>OB=ao&&$^`X>kn$ct^N)1cHZ&9`{NIi4#XaWABeu**6rGQhT2=Vx$A8u=^2p> zjJ5RiNqTy?&Hh6Fj~61NgTq6P|KDFojvsvtFDM8`aElI!PuL%O5J^Z#&%Q*$e%?uOcXN=DRF;Gw zNX8L|^#r>O@=Az;wXpvDTd-{;V_Y#1#7mc24HN@sbpp=Q6@p^-Kv3c(2wDcN#3=}h zMnljWJOr8NLeTc`f+i132-=Y8?&56+03>05U-G2MF$vi6_dne!c~kTZ1$HB5Sy5o zDCGYy3H*%>82*i{FG!Fb1Og~$?dbvzWwUDj43~?e4LI_fT@`=2B7$~V? zPP-PKt1`DSHb6CR3GEeXdDI6A95pra#Ok!mTq7&(ounN!wK2xX!0f2O0 z^;aV;`Vs{$2GT$v=G(;pcruR#b6H3qs^~<{_dzpZKF}{D=Q6-;gi~iT!1gEA)6EN# z-R~K8^;QcfGup>;hsdF_&wt9a^t=|{euZSv zpzAccdp==LHOhvW<-bcp&EjH6&F`n*4-)>gEm=51d9ZRWd!Y2y(l#S1>wu(VQJ;O( z1p2SEqXmzH<@D9bp^1U~LX9!$ch-LQWqwl5Z;Som?m@u?%;ENqV(u@$X&`3CSX$NRboH4p``lm?{n&HihGkMS)L!5P3yxwe!yar#RKo z)lU4?MkNW+%~CJ^+6TUPSw}3!+o$c_&(i@i9y_c$J)f4m^VJPKmC2DWok&&;9oVmX zNy5&SPfV$4z&SO@XVrRQ>oTtKiKBvMgZDI=MLo@E_`}N5)LQuavqg*NiY%fg4+@^x zP5o>2HClya72p2QLouhgvcqS+Hjl4#(pf5y{C-S(_mOKljq?p3YE1XIP{;ea_hla+ zetEgq;!JPJTtUU8G<4Rivr{JwV+6B%j04g{+!ht2DeFqXd<^7QK{L}sODI%dJY>6e z%+N(|M=#aX!&+Q0*`2zh-QM1gkYJUVJ7jwJ>bT>942UKsmvw2>7Wb3y6PBj4h$g!P z<(Prntl{q`S;8`9xQf-d_}2dLdCPcLX{k~9RyDW+-|H0AubXSV?cWe=*w~M%K_vuN zmS{^&MPt`uaj?o5Dp92ql^-b)TkUvJ=4F&p11-cRZU`suHzdeSbJ=*(=B9uaW^$r@ zHMH?lfRc?$*l1<|S0P9$#_X-)z`(PE-EqZ6*{KU=G-EuWIXNdqRD5_sIz-`$Q{RA3I|{rZ3%u z@83V>BBJ1#a+MBgW0$iiivd)!#Ej43r^Tq3)Ae~9J7jXtSt_?aKA86| z1(n)(-}A*iqoir+yTgwzNS>ZQ9`YDtnAx^|ukf~I()Kj6j`WMxtKBZPA0yr;h%c&n zbkN0IR}8x2=eE2G9b%zmRM~Y7K7pnNYEfFe4k;_Tv3fQp`UFNVo&F}RXvC;5w!P4^ zC#hV<h+CK6Vcd`6wcjc%%b6Xt}WT)&X;_>?Zx`q#vpC3jW((!2b zm{z<^idu8tw(6V7^R6RMXzNWvK&eh+ljPBo4sNjZi#RT7N^0Sa{u*1&c6PBr^hTp{ zw(I=ov}?;9T{!>9dYAaYh#7kUE;4LWeew0A8!yY?z6$KAl|z58KNy}np1mxvzFhDh zM!-R4-ycMlUe8JlmvwY$=c931ZT;u+yKH?=gMSN6s+|v&7)XW936U_^AVajlfxT$s zy@n>Ap<%=z(%JtXAgQfN0Rc)#2Cpy@H6}0;hGJr3 g_640f8AS*Thxdg=lJjRRl>rEHv~$C9Z1ETV0j@kPx&QzG literal 2019 zcmZ`&X;f3!8a*ToVTcbZ$e>b+qO3ABAjnW;CIL;5NmPUi%^YNs05a&hAgx&YD###V zNNxf+#F7ar8bK*oL4?*<6|4oPilDT`QW0^0ckacy*8B0+%01KF-`@M%C--nvB#mrj zV*~&oM}&vQz&Q|mNJKa`b`<{(CtYq3BM1QXQR6o$dT?*Y43A*|P_PaFq!fTx@Cca% zAcq3L(_{dAYXPv#Y|uvg0YFHJqQ`|n081@pt5A+g!cj{&Dk%qBqiiM0#THUdn}pML z0(%R$5UEB}nh@-|}_)iKJ7r81i1asRYT+KTMuJSzq7NTsNnX8iW6tXP2oUM>T zIkrN^R^rUK7E|5;C6;J$OE`mC%2hNX2(om}R5u})F8za%K^3l2vP2FKx5?#jgwA7Au{kB5PvpQ{`398lOezr6*kg%0s> zpcXWbsl!^FldDEq>iVS&SsK}r$+WZ0T&0w&!cF3#@i9)UO=fEY93crL=d?Cq_%=L7 zxR8smEk+C5hl)#ggm1B|V0ZXij2XWy%s-l}lq8*hrwh$P(IsQ>6-)ygA)yjYJ9~_) zma$dhRLCsCgs`-+z9eiYgkj!?fF}u6@CG4L@EsNl)@f_AKqZE^iQnMT6}+orYj8pp z3jbYm(OxBRx+;u`j0LX3Q{A_p+|)`69L>L&xSnrebolunES-;kaM(IK8S?V+g`k541~cmJ%3}S0m+x5fW056y^rKrhmUW#wcD8hV-rR>3suR}#ac}AS z*ROZJ@@YK&r@W`OZKyRRHvAh?LIZ%984(&3x1)EwW_}kv+Ny6ic|*%F4c)4$ofnW=)FIMprxAoDUw>tk({V ztbcId;;QqF`&}{5j>NZ=5o7*(E)X}?xF%M4er7*U?R;xpVp!R)k&hVZOkJmQYBJZY zTrbKV?)#LaO$^$hUuT*mC9h=u?L1$AF!bt;J4}Pk0t8NJ8I=m{AH}t*rdqG!&F4bh z?v%f) z_P8m=LeAzX>D~V3T@q{RNJSB&iw;(yy(^bJES6SOC4{L*i((oX`z-3R7Cv(D;+eVj z_X-zD{;FR#EBuir$J67rUAw;0_F1$Ld0Wx;c%(8~I8kN$%gLX-(>;st&{y^Bbka%( zy2(Qiw@?eesSBU*Db0zA*bBdD3jG<8FOz=9${8^cx{gqtbdR zACzjDJ!3pS#nUF*-2ID9vbCUm7AYdy`iCB*MU^I0n}FL|$^*Ae`${51ZpI?stj7Z& zaB+hxbYu@Y^e0gXmVw$e!sRm@1%SE({o<*frPn-iyEYYbvLfPQ->LZo? z2Wri_Pl<>megFHtwBeb@BZeS(CM&_kR`L=Vbo)_%*5vYx4v^5SXWds2C$pD#5(`{> zijXo&cb#ClOJ%ztABkx?^CT-D33^Eb%<7QcRyq4U9rbTFydGHfDI3Wcjaf*KGa~&) zIKZnWJda<7?wYs1LvQ_Y-~8gZXiI+X zDO&R*@s)^qa$0e-U;p-uO87ZC2*Y*=St&xcFLN&&PQabw=H){1aBPKfHnV9ke0#XbMpUxL0$GC2`sS26k;=0i=gLn{&1R-#vRyOw Ua_j%}vwung9R* diff --git a/docs/logo-large.png b/docs/logo-large.png index 07876003f02f38bc8832be568766e7cbe01cdc86..30d939901daa458185ce136e03bd4ee233946224 100644 GIT binary patch literal 9240 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 literal 7988 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 Date: Tue, 9 May 2017 00:12:56 +0200 Subject: [PATCH 06/12] Basic preparations for new release // Small refactoring. --- .travis.yml | 6 - README.md | 2 +- composer.json | 6 +- Demo.php => demo/Client.php | 16 +- src/Cache.php | 11 +- src/Client.php | 400 +++++++++++++++++++----------------- src/Util.php | 24 +-- tests/ClientTest.php | 14 +- tests/UtilTest.php | 139 ++++++------- 9 files changed, 312 insertions(+), 306 deletions(-) rename Demo.php => demo/Client.php (87%) diff --git a/.travis.yml b/.travis.yml index a21a10d..921772f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,6 @@ language: php php: - - 5.3 - - 5.4 - - 5.5 - 5.6 - 7.0 - 7.1 @@ -14,9 +11,6 @@ sudo: false matrix: allow_failures: - - php: 5.3 - - php: 5.4 - - php: 5.5 - php: nightly - php: hhvm fast_finish: true diff --git a/README.md b/README.md index a3088f2..7d3c7ed 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ $client->set('foo', 1.00); // Returns 1.00 as PHP's type float! $client->get('foo'); ``` -You will find a demonstration [`Demo.php` »](Demo.php) showing in detail how to use the **memcached-php** `client`. +You will find a demonstration [`Demo.php` »](demo/Client.php) showing in detail how to use the **memcached-php** `client`. ### Data diff --git a/composer.json b/composer.json index 8ec1549..f404e8f 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "clickalicious/memcached-php", - "description": "Plain vanilla PHP Memcached client library with full support of Memcached ASCII protocol.", + "description": "Memcached client library in plain vanilla PHP.", "type": "library", "authors": [ { @@ -10,12 +10,12 @@ "role": "Lead" } ], - "minimum-stability": "stable", + "minimum-stability": "dev", "prefer-stable": true, "require": { "php": ">=5.6.0", "roave/security-advisories": "dev-master as 1.0.x-dev", - "gpupo/cache": "^1.3" + "gpupo/cache": "dev-master as 1.3.x-dev" }, "require-dev": { "phpunit/phpunit": "^5.7", diff --git a/Demo.php b/demo/Client.php similarity index 87% rename from Demo.php rename to demo/Client.php index 7d8a72e..02a7979 100644 --- a/Demo.php +++ b/demo/Client.php @@ -25,14 +25,12 @@ * SOFTWARE. */ -require_once __DIR__.'vendor/autoload.php'; +require_once dirname(__DIR__) . '/vendor/autoload.php'; -use Clickalicious\Memcached\Php\Client; +use \Clickalicious\Memcached\Php\Client; // Create memcached-php instance ... -$memcached = new Client( - '127.0.0.1' -); +$memcached = new Client('127.0.0.1'); // Some setup for randomized key(s) for demonstration ... srand(microtime(true)); @@ -51,14 +49,8 @@ $result = $memcached->get($dummy); $memcached->delete($dummy); - } catch (Exception $e) { $result = $e->getMessage(); - } -echo '

';
-echo '

Simple Demonstration

'; -echo 'Result should be "5":
'; -echo $result; -echo '
'; +echo sprintf('Result = "%s" (should be "5")', $result) . PHP_EOL; diff --git a/src/Cache.php b/src/Cache.php index 543dc8f..36d75bf 100644 --- a/src/Cache.php +++ b/src/Cache.php @@ -48,7 +48,6 @@ class Cache extends Client implements CacheItemInterface */ public function getKey() { - } /** @@ -62,7 +61,6 @@ public function getKey() */ public function isHit() { - } /** @@ -77,18 +75,17 @@ public function isHit() */ public function exists() { - } /** * Sets the expiration for this cache item. * * @param int|\DateTime $ttl - * - If an integer is passed, it is interpreted as the number of seconds + * - If an integer is passed, it is interpreted as the number of seconds * after which the item MUST be considered expired. - * - If a DateTime object is passed, it is interpreted as the point in + * - If a DateTime object is passed, it is interpreted as the point in * time after which the item MUST be considered expired. - * - If null is passed, a default value MAY be used. If none is set, + * - If null is passed, a default value MAY be used. If none is set, * the value should be stored permanently or for as long as the * implementation allows. * @@ -97,7 +94,6 @@ public function exists() */ public function setExpiration($ttl = null) { - } /** @@ -111,7 +107,6 @@ public function setExpiration($ttl = null) */ public function getExpiration() { - } public function expiresAfter($time) diff --git a/src/Client.php b/src/Client.php index 2427e45..58dab05 100644 --- a/src/Client.php +++ b/src/Client.php @@ -591,7 +591,7 @@ class Client * @access public * @const */ - const COMMAND_SEPARATOR = ' '; + const COMMAND_SEPARATOR = ' '; /** * The terminator used to terminate a commandline send to Memcached instance. @@ -642,70 +642,123 @@ class Client * Flags for PHP types * * (Memcached PHP extension compatible) + * * @access public * @const */ - const FLAG_DECIMAL_STRING = 0; // PHP Type "string" Mask - Decimal: 0 - Bit(s): 0 - const FLAG_DECIMAL_INTEGER = 1; // PHP Type "integer" Mask - Decimal: 1 - Bit(s): 1 - const FLAG_DECIMAL_FLOAT = 2; // PHP Type "float" Mask - Decimal: 2 - Bit(s): 2 - const FLAG_DECIMAL_BOOLEAN = 3; // PHP Type "boolean" Mask - Decimal: 3 - Bit(s): 1 & 2 + const FLAG_DECIMAL_STRING = 0; // PHP Type "string" Mask - Decimal: 0 - Bit(s): 0 + + const FLAG_DECIMAL_INTEGER = 1; // PHP Type "integer" Mask - Decimal: 1 - Bit(s): 1 + + const FLAG_DECIMAL_FLOAT = 2; // PHP Type "float" Mask - Decimal: 2 - Bit(s): 2 + + const FLAG_DECIMAL_BOOLEAN = 3; // PHP Type "boolean" Mask - Decimal: 3 - Bit(s): 1 & 2 + const FLAG_DECIMAL_SERIALIZED = 4; // PHP Type "object" || "array" Mask - Decimal: 4 - Bit(s): 4 /** * Memcached Constant Values */ - const MEMCACHED_SUCCESS = 0; - const MEMCACHED_FAILURE = 1; - const MEMCACHED_HOST_LOOKUP_FAILURE = 2; - const MEMCACHED_CONNECTION_FAILURE = 3; - const MEMCACHED_CONNECTION_BIND_FAILURE = 4; - const MEMCACHED_WRITE_FAILURE = 5; - const MEMCACHED_READ_FAILURE = 6; - const MEMCACHED_UNKNOWN_READ_FAILURE = 7; - const MEMCACHED_PROTOCOL_ERROR = 8; - const MEMCACHED_CLIENT_ERROR = 9; - const MEMCACHED_SERVER_ERROR = 10; + const MEMCACHED_SUCCESS = 0; + + const MEMCACHED_FAILURE = 1; + + const MEMCACHED_HOST_LOOKUP_FAILURE = 2; + + const MEMCACHED_CONNECTION_FAILURE = 3; + + const MEMCACHED_CONNECTION_BIND_FAILURE = 4; + + const MEMCACHED_WRITE_FAILURE = 5; + + const MEMCACHED_READ_FAILURE = 6; + + const MEMCACHED_UNKNOWN_READ_FAILURE = 7; + + const MEMCACHED_PROTOCOL_ERROR = 8; + + const MEMCACHED_CLIENT_ERROR = 9; + + const MEMCACHED_SERVER_ERROR = 10; + const MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE = 11; - const MEMCACHED_DATA_EXISTS = 12; - const MEMCACHED_DATA_DOES_NOT_EXIST = 13; - const MEMCACHED_NOTSTORED = 14; - const MEMCACHED_STORED = 15; - const MEMCACHED_NOTFOUND = 16; - const MEMCACHED_MEMORY_ALLOCATION_FAILURE = 17; - const MEMCACHED_PARTIAL_READ = 18; - const MEMCACHED_SOME_ERRORS = 19; - const MEMCACHED_NO_SERVERS = 20; - const MEMCACHED_END = 21; - const MEMCACHED_DELETED = 22; - const MEMCACHED_VALUE = 23; - const MEMCACHED_STAT = 24; - const MEMCACHED_ITEM = 25; - const MEMCACHED_ERRNO = 26; - const MEMCACHED_FAIL_UNIX_SOCKET = 27; - const MEMCACHED_NOT_SUPPORTED = 28; - const MEMCACHED_NO_KEY_PROVIDED = 29; /* Deprecated. Use const MEMCACHED_BAD_KEY_PROVIDED! */ - const MEMCACHED_FETCH_NOTFINISHED = 30; - const MEMCACHED_TIMEOUT = 31; - const MEMCACHED_BUFFERED = 32; - const MEMCACHED_BAD_KEY_PROVIDED = 33; - const MEMCACHED_INVALID_HOST_PROTOCOL = 34; - const MEMCACHED_SERVER_MARKED_DEAD = 35; - const MEMCACHED_UNKNOWN_STAT_KEY = 36; - const MEMCACHED_E2BIG = 37; - const MEMCACHED_INVALID_ARGUMENTS = 38; - const MEMCACHED_KEY_TOO_BIG = 39; - const MEMCACHED_AUTH_PROBLEM = 40; - const MEMCACHED_AUTH_FAILURE = 41; - const MEMCACHED_AUTH_CONTINUE = 42; - const MEMCACHED_PARSE_ERROR = 43; - const MEMCACHED_PARSE_USER_ERROR = 44; - const MEMCACHED_DEPRECATED = 45; - const MEMCACHED_IN_PROGRESS = 46; - const MEMCACHED_SERVER_TEMPORARILY_DISABLED = 47; + + const MEMCACHED_DATA_EXISTS = 12; + + const MEMCACHED_DATA_DOES_NOT_EXIST = 13; + + const MEMCACHED_NOTSTORED = 14; + + const MEMCACHED_STORED = 15; + + const MEMCACHED_NOTFOUND = 16; + + const MEMCACHED_MEMORY_ALLOCATION_FAILURE = 17; + + const MEMCACHED_PARTIAL_READ = 18; + + const MEMCACHED_SOME_ERRORS = 19; + + const MEMCACHED_NO_SERVERS = 20; + + const MEMCACHED_END = 21; + + const MEMCACHED_DELETED = 22; + + const MEMCACHED_VALUE = 23; + + const MEMCACHED_STAT = 24; + + const MEMCACHED_ITEM = 25; + + const MEMCACHED_ERRNO = 26; + + const MEMCACHED_FAIL_UNIX_SOCKET = 27; + + const MEMCACHED_NOT_SUPPORTED = 28; + + const MEMCACHED_NO_KEY_PROVIDED = 29; /* Deprecated. Use const MEMCACHED_BAD_KEY_PROVIDED! */ + const MEMCACHED_FETCH_NOTFINISHED = 30; + + const MEMCACHED_TIMEOUT = 31; + + const MEMCACHED_BUFFERED = 32; + + const MEMCACHED_BAD_KEY_PROVIDED = 33; + + const MEMCACHED_INVALID_HOST_PROTOCOL = 34; + + const MEMCACHED_SERVER_MARKED_DEAD = 35; + + const MEMCACHED_UNKNOWN_STAT_KEY = 36; + + const MEMCACHED_E2BIG = 37; + + const MEMCACHED_INVALID_ARGUMENTS = 38; + + const MEMCACHED_KEY_TOO_BIG = 39; + + const MEMCACHED_AUTH_PROBLEM = 40; + + const MEMCACHED_AUTH_FAILURE = 41; + + const MEMCACHED_AUTH_CONTINUE = 42; + + const MEMCACHED_PARSE_ERROR = 43; + + const MEMCACHED_PARSE_USER_ERROR = 44; + + const MEMCACHED_DEPRECATED = 45; + + const MEMCACHED_IN_PROGRESS = 46; + + const MEMCACHED_SERVER_TEMPORARILY_DISABLED = 47; + const MEMCACHED_SERVER_MEMORY_ALLOCATION_FAILURE = 48; - const MEMCACHED_MAXIMUM_RETURN = 49; - /* Always add new error code before */ + const MEMCACHED_MAXIMUM_RETURN = 49; + + /* Always add new error code before */ /** * Constructor. @@ -714,22 +767,23 @@ class Client * @param int $port The port to connect to. * @param int|null $timeout The timeout for connecting in seconds * @param string $persistentId By default the Memcached instances are destroyed at the end of the request. - * To create an instance that persists between requests, use persistent_id to specify a - * unique ID for the instance. All instances created with the same persistent_id will - * share the same connection. - * @param bool $compression TRUE to enable compression (default), FALSE to disable + * To create an instance that persists between requests, use persistent_id to specify + * a unique ID for the instance. All instances created with the same persistent_id + * will share the same connection. + * @param bool $compression TRUE to enable compression (default), FALSE to disable * * @author Benjamin Carl * @return Client * @access public */ public function __construct( - $host = null, - $port = self::DEFAULT_PORT, - $timeout = self::DEFAULT_TIMEOUT, + $host = null, + $port = self::DEFAULT_PORT, + $timeout = self::DEFAULT_TIMEOUT, $persistentId = null, - $compression = true - ) { + $compression = true + ) + { // Extract host and port from host string if ($host !== null) { $this @@ -785,6 +839,7 @@ public function setHost($host) public function host($host) { $this->setHost($host); + return $this; } @@ -826,6 +881,7 @@ public function setPort($port) public function port($port) { $this->setPort($port); + return $this; } @@ -867,6 +923,7 @@ public function setTimeout($timeout) public function timeout($timeout) { $this->setTimeout($timeout); + return $this; } @@ -941,7 +998,6 @@ public function connect($host, $port, $timeout = null) // Store for further access/use ... self::$connections[$this->getPersistentId()][$uuid] = $connection; - } else { $connection = self::$connections[$this->getPersistentId()][$uuid]; } @@ -1033,9 +1089,9 @@ public function touch($key, $expiration) */ // Build packet to send ... - $data = self::COMMAND_TOUCH . self::COMMAND_SEPARATOR . - $key . self::COMMAND_SEPARATOR . - $expiration . self::COMMAND_TERMINATOR; + $data = self::COMMAND_TOUCH . self::COMMAND_SEPARATOR . + $key . self::COMMAND_SEPARATOR . + $expiration . self::COMMAND_TERMINATOR; return $this->send(self::COMMAND_TOUCH, $data); } @@ -1043,8 +1099,8 @@ public function touch($key, $expiration) /** * Proxy to: incr() * - * @param string $key The key to increment - * @param int $offset How much to increment + * @param string $key The key to increment + * @param int $offset How much to increment * * @author Benjamin Carl * @return string The result of operation @@ -1059,8 +1115,8 @@ public function increment($key, $offset = 1) * Increments an existing key by offset. * Does currently not support the creation of not existing keys. * - * @param string $key The key to increment - * @param int $offset How much to increment + * @param string $key The key to increment + * @param int $offset How much to increment * * @author Benjamin Carl * @return string The result of operation @@ -1073,9 +1129,9 @@ public function incr($key, $offset = 1) */ // Build packet to send ... - $data = self::COMMAND_INCR . self::COMMAND_SEPARATOR . - $key . self::COMMAND_SEPARATOR . - $offset . self::COMMAND_TERMINATOR; + $data = self::COMMAND_INCR . self::COMMAND_SEPARATOR . + $key . self::COMMAND_SEPARATOR . + $offset . self::COMMAND_TERMINATOR; return $this->send(self::COMMAND_INCR, $data); } @@ -1083,8 +1139,8 @@ public function incr($key, $offset = 1) /** * Proxy to: decr(). * - * @param string $key The key to decrement - * @param int $offset How much to decrement + * @param string $key The key to decrement + * @param int $offset How much to decrement * * @author Benjamin Carl * @return string The result of operation @@ -1099,8 +1155,8 @@ public function decrement($key, $offset = 1) * Decrements an existing key by offset. * Does currently not support the creation of not existing keys. * - * @param string $key The key to decrement - * @param int $offset How much to decrement + * @param string $key The key to decrement + * @param int $offset How much to decrement * * @author Benjamin Carl * @return string The result of operation @@ -1113,9 +1169,9 @@ public function decr($key, $offset = 1) */ // Build packet to send ... - $data = self::COMMAND_DECR . self::COMMAND_SEPARATOR . - $key . self::COMMAND_SEPARATOR . - $offset . self::COMMAND_TERMINATOR; + $data = self::COMMAND_DECR . self::COMMAND_SEPARATOR . + $key . self::COMMAND_SEPARATOR . + $offset . self::COMMAND_TERMINATOR; return $this->send(self::COMMAND_DECR, $data); } @@ -1152,12 +1208,12 @@ public function set($key, $value, $expiration = 0, $flags = 0, $bytes = null) $bytes = ($bytes !== null) ? $bytes : strlen($value); // Build packet to send ... - $data = self::COMMAND_SET . self::COMMAND_SEPARATOR . - $key . self::COMMAND_SEPARATOR . - $flags . self::COMMAND_SEPARATOR . - $expiration . self::COMMAND_SEPARATOR . - $bytes . self::COMMAND_TERMINATOR . - $value . self::COMMAND_TERMINATOR; + $data = self::COMMAND_SET . self::COMMAND_SEPARATOR . + $key . self::COMMAND_SEPARATOR . + $flags . self::COMMAND_SEPARATOR . + $expiration . self::COMMAND_SEPARATOR . + $bytes . self::COMMAND_TERMINATOR . + $value . self::COMMAND_TERMINATOR; return $this->send(self::COMMAND_SET, $data); } @@ -1195,12 +1251,12 @@ public function add($key, $value, $expiration = 0, $flags = 0, $bytes = null) $bytes = ($bytes !== null) ? $bytes : strlen($value); // Build packet to send ... - $data = self::COMMAND_ADD . self::COMMAND_SEPARATOR . - $key . self::COMMAND_SEPARATOR . - $flags . self::COMMAND_SEPARATOR . - $expiration . self::COMMAND_SEPARATOR . - $bytes . self::COMMAND_TERMINATOR . - $value . self::COMMAND_TERMINATOR; + $data = self::COMMAND_ADD . self::COMMAND_SEPARATOR . + $key . self::COMMAND_SEPARATOR . + $flags . self::COMMAND_SEPARATOR . + $expiration . self::COMMAND_SEPARATOR . + $bytes . self::COMMAND_TERMINATOR . + $value . self::COMMAND_TERMINATOR; return $this->send(self::COMMAND_ADD, $data); } @@ -1238,12 +1294,12 @@ public function replace($key, $value, $expiration = 0, $flags = 0, $bytes = null $bytes = ($bytes !== null) ? $bytes : strlen($value); // Build packet to send ... - $data = self::COMMAND_REPLACE . self::COMMAND_SEPARATOR . - $key . self::COMMAND_SEPARATOR . - $flags . self::COMMAND_SEPARATOR . - $expiration . self::COMMAND_SEPARATOR . - $bytes . self::COMMAND_TERMINATOR . - $value . self::COMMAND_TERMINATOR; + $data = self::COMMAND_REPLACE . self::COMMAND_SEPARATOR . + $key . self::COMMAND_SEPARATOR . + $flags . self::COMMAND_SEPARATOR . + $expiration . self::COMMAND_SEPARATOR . + $bytes . self::COMMAND_TERMINATOR . + $value . self::COMMAND_TERMINATOR; return $this->send(self::COMMAND_REPLACE, $data); } @@ -1273,12 +1329,12 @@ public function append($key, $value, $expiration = 0, $flags = self::FLAG_DEFAUL $bytes = ($bytes !== null) ? $bytes : strlen($value); // Build packet to send ... - $data = self::COMMAND_APPEND . self::COMMAND_SEPARATOR . - $key . self::COMMAND_SEPARATOR . - $flags . self::COMMAND_SEPARATOR . - $expiration . self::COMMAND_SEPARATOR . - $bytes . self::COMMAND_TERMINATOR . - $value . self::COMMAND_TERMINATOR; + $data = self::COMMAND_APPEND . self::COMMAND_SEPARATOR . + $key . self::COMMAND_SEPARATOR . + $flags . self::COMMAND_SEPARATOR . + $expiration . self::COMMAND_SEPARATOR . + $bytes . self::COMMAND_TERMINATOR . + $value . self::COMMAND_TERMINATOR; return $this->send(self::COMMAND_APPEND, $data); } @@ -1308,12 +1364,12 @@ public function prepend($key, $value, $expiration = 0, $flags = self::FLAG_DEFAU $bytes = ($bytes !== null) ? $bytes : strlen($value); // Build packet to send ... - $data = self::COMMAND_PREPEND . self::COMMAND_SEPARATOR . - $key . self::COMMAND_SEPARATOR . - $flags . self::COMMAND_SEPARATOR . - $expiration . self::COMMAND_SEPARATOR . - $bytes . self::COMMAND_TERMINATOR . - $value . self::COMMAND_TERMINATOR; + $data = self::COMMAND_PREPEND . self::COMMAND_SEPARATOR . + $key . self::COMMAND_SEPARATOR . + $flags . self::COMMAND_SEPARATOR . + $expiration . self::COMMAND_SEPARATOR . + $bytes . self::COMMAND_TERMINATOR . + $value . self::COMMAND_TERMINATOR; return $this->send(self::COMMAND_PREPEND, $data); } @@ -1354,13 +1410,13 @@ public function cas($token, $key, $value, $expiration = 0, $flags = self::FLAG_D $bytes = ($bytes !== null) ? $bytes : strlen($value); // Build packet to send ... - $data = self::COMMAND_CAS . self::COMMAND_SEPARATOR . - $key . self::COMMAND_SEPARATOR . - $flags . self::COMMAND_SEPARATOR . - $expiration . self::COMMAND_SEPARATOR . - $bytes . self::COMMAND_SEPARATOR . - $token . self::COMMAND_TERMINATOR . - $value . self::COMMAND_TERMINATOR; + $data = self::COMMAND_CAS . self::COMMAND_SEPARATOR . + $key . self::COMMAND_SEPARATOR . + $flags . self::COMMAND_SEPARATOR . + $expiration . self::COMMAND_SEPARATOR . + $bytes . self::COMMAND_SEPARATOR . + $token . self::COMMAND_TERMINATOR . + $value . self::COMMAND_TERMINATOR; return $this->send(self::COMMAND_CAS, $data); } @@ -1382,8 +1438,8 @@ public function get($key, $metadata = false) */ // Build packet to send ... - $data = self::COMMAND_GET . self::COMMAND_SEPARATOR . - $key . self::COMMAND_TERMINATOR; + $data = self::COMMAND_GET . self::COMMAND_SEPARATOR . + $key . self::COMMAND_TERMINATOR; $result = $this->send(self::COMMAND_GET, $data); @@ -1416,8 +1472,8 @@ public function gets(array $keys, $metadata = false) $keys = implode(self::COMMAND_SEPARATOR, $keys); // Build packet to send ... - $data = self::COMMAND_GETS . self::COMMAND_SEPARATOR . - $keys . self::COMMAND_TERMINATOR; + $data = self::COMMAND_GETS . self::COMMAND_SEPARATOR . + $keys . self::COMMAND_TERMINATOR; $result = $this->send(self::COMMAND_GETS, $data); @@ -1445,8 +1501,8 @@ public function delete($key) */ // Build packet to send ... - $data = self::COMMAND_DELETE . self::COMMAND_SEPARATOR . - $key . self::COMMAND_TERMINATOR; + $data = self::COMMAND_DELETE . self::COMMAND_SEPARATOR . + $key . self::COMMAND_TERMINATOR; return $this->send(self::COMMAND_DELETE, $data); } @@ -1483,9 +1539,9 @@ public function stats($type = '', $argument1 = '', $argument2 = '') // Build packet to send ... $data = self::COMMAND_STATS . $type . - $argument1 . - $argument2 . - self::COMMAND_TERMINATOR; + $argument1 . + $argument2 . + self::COMMAND_TERMINATOR; // Generic stats requires us to fetch as long as data arrives ... if ($type === '') { @@ -1503,7 +1559,6 @@ public function stats($type = '', $argument1 = '', $argument2 = '') $result[$key] = $value; } } - } elseif ($type === self::COMMAND_SEPARATOR . self::STATS_TYPE_SLABS) { // Initial fetch ... @@ -1526,15 +1581,12 @@ public function stats($type = '', $argument1 = '', $argument2 = '') } $result[$key] = array_merge($result[$key], $value); - } else { // Meta! $result[$key] = $value; - } } } - } else { // Issue stat command ... $result = $this->send(self::COMMAND_STATS, $data); @@ -1632,6 +1684,7 @@ protected function setCompression($compression) protected function compression($compression) { $this->setCompression($compression); + return $this; } @@ -1674,6 +1727,7 @@ protected function setPersistentId($persistentId) protected function persistentId($persistentId) { $this->setPersistentId($persistentId); + return $this; } @@ -1701,6 +1755,7 @@ protected function getPersistentId() protected function setLastResponse($response) { $this->lastResponse = $response; + return ($response === 0); } @@ -1716,6 +1771,7 @@ protected function setLastResponse($response) protected function lastResponse($response) { $this->setLastResponse($response); + return $this; } @@ -1759,11 +1815,11 @@ protected function isSerializable($value) $type = gettype($value); return ( - $type !== "string" && // Mask - Decimal: 0 - Bit(s): 0 + $type !== "string" && // Mask - Decimal: 0 - Bit(s): 0 $type !== "integer" && // Mask - Decimal: 1 - Bit(s): 1 - $type !== "double" && // Mask - Decimal: 2 - Bit(s): 2 + $type !== "double" && // Mask - Decimal: 2 - Bit(s): 2 $type !== "boolean" // Mask - Decimal: 3 - Bit(s): 1 & 2 - // Mask - Decimal: 4 - Bit(s): 4 + // Mask - Decimal: 4 - Bit(s): 4 ); } @@ -1779,7 +1835,6 @@ protected function reset() return $this ->lastResponse(0); - } /** @@ -1821,18 +1876,18 @@ protected function parseReadResponse($buffer, array $lines) // @codeCoverageIgnoreEnd // Value must be at least starting on next line - and can continue to spawn on n following lines ... - $key = $metaData[1]; - $value = ''; - $flags = (int)$metaData[2]; + $key = $metaData[1]; + $value = ''; + $flags = (int)$metaData[2]; $length = $metaData[3]; - $cas = (isset($metaData[4])) ? (float)$metaData[4] : null; - $frame = 0; + $cas = (isset($metaData[4])) ? (float)$metaData[4] : null; + $frame = 0; if ($length > 0) { // Fetch whole & complete value! while (strlen($value) < $length) { - ++$frame; - if($lines[$line + $frame] === self::RESPONSE_END && !isset($lines[$line + $frame+1])) { + ++ $frame; + if ($lines[$line + $frame] === self::RESPONSE_END && !isset($lines[$line + $frame + 1])) { $frame_break = true; break; } @@ -1846,8 +1901,8 @@ protected function parseReadResponse($buffer, array $lines) $result[$key] = array( // 1st bit set = we use un-/serialize to keep the values intact ... //'value' => $value, - 'key' => $key, - 'meta' => array( + 'key' => $key, + 'meta' => array( 'key' => $key, 'flags' => $flags, 'length' => $length, @@ -1858,22 +1913,19 @@ protected function parseReadResponse($buffer, array $lines) if ($this->isFlagSet($flags, self::FLAG_DECIMAL_SERIALIZED) === true) { $length = strlen($value); - $value = unserialize($value); - + $value = unserialize($value); } elseif ($this->isFlagSet($flags, self::FLAG_DECIMAL_BOOLEAN) === true) { - $value = boolval($value); + $value = boolval($value); $length = strlen($value); - } elseif ($this->isFlagSet($flags, self::FLAG_DECIMAL_FLOAT) === true) { - $value = floatval($value); + $value = floatval($value); $length = strlen($value); - } elseif ($this->isFlagSet($flags, self::FLAG_DECIMAL_INTEGER) === true) { - $value = intval($value); + $value = intval($value); $length = strlen($value); } - $result[$key]['value'] = $value; + $result[$key]['value'] = $value; $result[$key]['meta']['length'] = $length; // Increment by one and check @@ -1887,10 +1939,8 @@ protected function parseReadResponse($buffer, array $lines) // Memcached compatible success $this->lastResponse(self::MEMCACHED_SUCCESS); - } else { $result = $this->setLastResponse(self::MEMCACHED_NOTFOUND); - } return $result; @@ -1921,7 +1971,6 @@ protected function parseWriteResponse($buffer, array $lines) // Successful? if ($result === true) { $this->lastResponse(self::MEMCACHED_SUCCESS); - } else { // Default error case $this->lastResponse(self::MEMCACHED_FAILURE); @@ -1985,7 +2034,6 @@ protected function parseDeleteResponse(array $lines) if ($metaData[0] !== self::RESPONSE_DELETED) { if ($metaData[0] === self::RESPONSE_NOT_FOUND) { $result = $this->setLastResponse(self::MEMCACHED_NOTFOUND); - } else { // Generic error $result = $this->setLastResponse(self::MEMCACHED_FAILURE); @@ -2031,7 +2079,7 @@ protected function parseStatsResponse(array $lines) } // @codeCoverageIgnoreEnd - $nodes = explode(':', $metaData[1]); + $nodes = explode(':', $metaData[1]); $countNodes = count($nodes); if ($countNodes === 2) { @@ -2040,7 +2088,6 @@ protected function parseStatsResponse(array $lines) $result[$nodes[0]] = array(); } $result[$nodes[0]][$nodes[1]] = $metaData[2]; - } elseif ($countNodes === 3) { // ??? if (isset($result[$nodes[0]]) === false) { @@ -2051,15 +2098,14 @@ protected function parseStatsResponse(array $lines) } } $result[$nodes[0]][$nodes[1]][$nodes[2]] = $metaData[2]; - } else { - $identifier = array_shift($metaData); - $key = array_shift($metaData); - $value = implode(self::COMMAND_SEPARATOR, $metaData); + $identifier = array_shift($metaData); + $key = array_shift($metaData); + $value = implode(self::COMMAND_SEPARATOR, $metaData); $result[$key] = $value; }; - ++$line; + ++ $line; } $this->lastResponse(self::MEMCACHED_SUCCESS); @@ -2073,7 +2119,7 @@ protected function parseStatsResponse(array $lines) * SUCCESS = RESPONSE = "\r\n" * FAILED = RESPONSE = "???" * - * @param array $lines Response split into single lines + * @param array $lines Response split into single lines * * @author Benjamin Carl * @return string|bool The version as string if valid, otherwise FALSE @@ -2087,7 +2133,6 @@ protected function parseVersionResponse(array $lines) // If Version response valid if ($metaData[0] === strtoupper(self::COMMAND_VERSION)) { $result = $metaData[1]; - } else { $result = $this->setLastResponse(self::MEMCACHED_FAILURE); } @@ -2116,7 +2161,6 @@ protected function parseArithmeticResponse($buffer, array $lines) // Check buffer for failure response if ($buffer === self::RESPONSE_NOT_FOUND . self::COMMAND_TERMINATOR) { $result = $this->setLastResponse(self::MEMCACHED_NOTFOUND); - } else { // Insert the response (= new value) as result $result = (float)$metaData[0]; @@ -2140,15 +2184,12 @@ protected function checkResponse($buffer) if (preg_match('/' . self::ERROR . '(.*)\R/mu', $buffer, $error) > 0) { // ERROR\r\n $result = self::MEMCACHED_FAILURE; - } elseif (preg_match('/' . self::ERROR_CLIENT . '(.*)\R/mu', $buffer, $error) > 0) { // CLIENT_ERROR\r\n $result = self::RESPONSE_CLIENT_ERROR; - } elseif (preg_match('/' . self::ERROR_SERVER . '(.*)\R/mu', $buffer, $error) > 0) { // SERVER_ERROR\r\n $result = self::ERROR_SERVER; - } else { $result = self::MEMCACHED_SUCCESS; } @@ -2172,8 +2213,8 @@ protected function parseResponse($command, $buffer) { // At this point we retrieve a raw response containing at least a trailing terminator - rip it $response = substr($buffer, 0, strlen($buffer) - strlen(self::COMMAND_TERMINATOR)); - $lines = explode(self::COMMAND_TERMINATOR, $response); - $result = false; + $lines = explode(self::COMMAND_TERMINATOR, $response); + $result = false; if ( $command === self::COMMAND_GET || @@ -2181,36 +2222,31 @@ protected function parseResponse($command, $buffer) ) { // PARSER for $result = $this->parseReadResponse($buffer, $lines); - } elseif ( - $command === self::COMMAND_SET || - $command === self::COMMAND_ADD || + $command === self::COMMAND_SET || + $command === self::COMMAND_ADD || $command === self::COMMAND_REPLACE || - $command === self::COMMAND_APPEND || + $command === self::COMMAND_APPEND || $command === self::COMMAND_PREPEND || $command === self::COMMAND_CAS ) { // PARSER for $result = $this->parseWriteResponse($buffer, $lines); - } elseif ( $command === self::COMMAND_DELETE ) { // PARSER for $result = $this->parseDeleteResponse($lines); - } elseif ( $command === self::COMMAND_STATS ) { // PARSER for $result = $this->parseStatsResponse($lines); - } elseif ( $command === self::COMMAND_VERSION ) { // PARSER for $result = $this->parseVersionResponse($lines); - } elseif ( $command === self::COMMAND_INCR || $command === self::COMMAND_DECR @@ -2244,23 +2280,18 @@ protected function serializeValue($value) $value = serialize($value); $bytes = strlen($value); $flags = self::FLAG_DECIMAL_SERIALIZED; - } else { // Real numbers should keep real numbers - Bit 2 = int , 3 = double/float if (is_int($value) === true) { $flags = self::FLAG_DECIMAL_INTEGER; - } elseif (is_float($value) === true) { $flags = self::FLAG_DECIMAL_FLOAT; - } elseif (is_string($value) === true) { // Never serialize strings! Otherwise append() & prepend() won't work! $flags = self::FLAG_DECIMAL_STRING; - } elseif (is_bool($value) === true) { $value = strval($value); $flags = self::FLAG_DECIMAL_BOOLEAN; - } else { throw new Exception( sprintf('Unhandable value. Don\'t know how to process!') @@ -2301,6 +2332,7 @@ protected function isFlagSet($flags, $flag) protected function setFlag($flags, $flag) { $flags |= $flag; + return $flags; } } diff --git a/src/Util.php b/src/Util.php index 3da6af2..8d3a00c 100644 --- a/src/Util.php +++ b/src/Util.php @@ -52,9 +52,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * - * @author Ben Ramsey + * @author Ben Ramsey * @copyright 2013 Ben Ramsey - * @license http://opensource.org/licenses/MIT MIT + * @license http://opensource.org/licenses/MIT MIT * @codeCoverageIgnore */ if (false === function_exists('array_column')) { @@ -98,10 +98,10 @@ function array_column(array $input, $column_key, $index_key = null) function array_column_emulation(array $input, $column_key, $index_key = null) { // Check for ... - if (!is_int($column_key) && - !is_float($column_key) && + if (!is_int($column_key) && + !is_float($column_key) && !is_string($column_key) && - $column_key !== null && + $column_key !== null && !( is_object($column_key) && method_exists($column_key, '__toString') ) @@ -113,10 +113,10 @@ function array_column_emulation(array $input, $column_key, $index_key = null) } // Check for ... - if (null !== $index_key && - !is_int($index_key) && - !is_float($index_key) && - !is_string($index_key) && + if (null !== $index_key && + !is_int($index_key) && + !is_float($index_key) && + !is_string($index_key) && !( is_object($index_key) && method_exists($index_key, '__toString') ) @@ -139,12 +139,12 @@ function array_column_emulation(array $input, $column_key, $index_key = null) $result = array(); foreach ($input as $row) { - $key = null; + $key = null; $keySet = false; if (null !== $index_key && true === is_array($row) && true === array_key_exists($index_key, $row)) { $keySet = true; - $key = (string)$row[$index_key]; + $key = (string)$row[$index_key]; } if (true === is_array($row) && true === array_key_exists($column_key, $row)) { @@ -156,7 +156,7 @@ function array_column_emulation(array $input, $column_key, $index_key = null) if ($keySet === true) { $result[$key] = $value; } else { - $result[] = $value; + $result[] = $value; } } diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 0a4bbcd..a18ed37 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -67,7 +67,6 @@ class ClientTest extends \PHPUnit_Framework_TestCase */ protected $value; - /** * Prepare some stuff. * @@ -77,7 +76,7 @@ class ClientTest extends \PHPUnit_Framework_TestCase */ protected function setUp() { - $this->key = md5(microtime(true)); + $this->key = md5(microtime(true)); $this->value = sha1($this->key); $this->client = new Client( @@ -275,7 +274,6 @@ public function testSendAnInvalidCustomCommandString() $this->client->send(Client::COMMAND_VERSION, $testCommand); } - /** * Test: Retrieve version. * @@ -431,9 +429,9 @@ public function testStoringPhpTypeBoolean() /** * Test: a stored value. * - * @author Benjamin Carl + * @author Benjamin Carl * @return void - * @access protected + * @access protected * @depends testStoringPhpTypeInteger */ public function testIncrementAStoredValue() @@ -452,9 +450,9 @@ public function testIncrementAStoredValue() /** * Test: a stored value. * - * @author Benjamin Carl + * @author Benjamin Carl * @return void - * @access protected + * @access protected * @depends testStoringPhpTypeInteger */ public function testDecrementAStoredValue() @@ -542,7 +540,7 @@ public function testRetrieveStats() $cachedump = array(); - for ($i = 1; $i <= $slabs; ++$i) { + for ($i = 1; $i <= $slabs; ++ $i) { $cachedumpTemp = $this->client->stats( Client::STATS_TYPE_CACHEDUMP, $i, diff --git a/tests/UtilTest.php b/tests/UtilTest.php index f336317..a2cf2c9 100644 --- a/tests/UtilTest.php +++ b/tests/UtilTest.php @@ -59,7 +59,6 @@ class UtilTest extends \PHPUnit_Framework_TestCase */ protected $data; - /** * Prepare some stuff. * @@ -79,24 +78,24 @@ protected function setUp() // Test data $this->data = array( array( - 'id' => 2135, + 'id' => 2135, 'first_name' => 'John', - 'last_name' => 'Doe', + 'last_name' => 'Doe', ), array( - 'id' => 3245, + 'id' => 3245, 'first_name' => 'Sally', - 'last_name' => 'Smith', + 'last_name' => 'Smith', ), array( - 'id' => 5342, + 'id' => 5342, 'first_name' => 'Jane', - 'last_name' => 'Jones', + 'last_name' => 'Jones', ), array( - 'id' => 5623, + 'id' => 5623, 'first_name' => 'Peter', - 'last_name' => 'Doe', + 'last_name' => 'Doe', ) ); } @@ -111,25 +110,25 @@ protected function setUp() public function testArrayColumnEmulation() { /** - Array - ( - [0] => John - [1] => Sally - [2] => Jane - [3] => Peter - ) - */ + * Array + * ( + * [0] => John + * [1] => Sally + * [2] => Jane + * [3] => Peter + * ) + */ $data = array_column_emulation($this->data, 'first_name'); - $this->assertContains('John', $data); + $this->assertContains('John', $data); $this->assertContains('Sally', $data); - $this->assertContains('Jane', $data); + $this->assertContains('Jane', $data); $this->assertContains('Peter', $data); - $this->assertArrayHasKey(0, $data); - $this->assertArrayHasKey(1, $data); - $this->assertArrayHasKey(2, $data); - $this->assertArrayHasKey(3, $data); + $this->assertArrayHasKey(0, $data); + $this->assertArrayHasKey(1, $data); + $this->assertArrayHasKey(2, $data); + $this->assertArrayHasKey(3, $data); } /** @@ -142,19 +141,19 @@ public function testArrayColumnEmulation() public function testArrayColumnEmulationCustomIndex() { /** - Array - ( - [2135] => Doe - [3245] => Smith - [5342] => Jones - [5623] => Doe - ) + * Array + * ( + * [2135] => Doe + * [3245] => Smith + * [5342] => Jones + * [5623] => Doe + * ) */ $data = array_column_emulation($this->data, 'first_name', 'id'); - $this->assertContains('John', $data); + $this->assertContains('John', $data); $this->assertContains('Sally', $data); - $this->assertContains('Jane', $data); + $this->assertContains('Jane', $data); $this->assertContains('Peter', $data); $this->assertArrayHasKey(2135, $data); @@ -173,14 +172,14 @@ public function testArrayColumnEmulationCustomIndex() public function testArrayColumnEmulationWrongColumnString() { /** - Array - ( - [2135] => Doe - [3245] => Smith - [5342] => Jones - [5623] => Doe - ) - */ + * Array + * ( + * [2135] => Doe + * [3245] => Smith + * [5342] => Jones + * [5623] => Doe + * ) + */ $data = array_column_emulation($this->data, 'foo'); $this->assertArrayHasKey(0, $data); $this->assertArrayHasKey(1, $data); @@ -198,13 +197,13 @@ public function testArrayColumnEmulationWrongColumnString() public function testArrayColumnEmulationCustomIndexInt() { /** - Array - ( - [2135] => Doe - [3245] => Smith - [5342] => Jones - [5623] => Doe - ) + * Array + * ( + * [2135] => Doe + * [3245] => Smith + * [5342] => Jones + * [5623] => Doe + * ) */ $data = array_column_emulation($this->data, 'first_name', 1); $this->assertArrayHasKey(0, $data); @@ -223,14 +222,14 @@ public function testArrayColumnEmulationCustomIndexInt() public function testArrayColumnEmulationCustomIndexString() { /** - Array - ( - [2135] => Doe - [3245] => Smith - [5342] => Jones - [5623] => Doe - ) - */ + * Array + * ( + * [2135] => Doe + * [3245] => Smith + * [5342] => Jones + * [5623] => Doe + * ) + */ $data = array_column_emulation($this->data, 'first_name', 'foo'); $this->assertArrayHasKey(0, $data); $this->assertArrayHasKey(1, $data); @@ -275,34 +274,30 @@ public function testArrayColumnProxy() { if ($this->testProxy === true) { /** - Array - ( - [0] => John - [1] => Sally - [2] => Jane - [3] => Peter - ) + * Array + * ( + * [0] => John + * [1] => Sally + * [2] => Jane + * [3] => Peter + * ) */ $data = \Clickalicious\Memcached\Php\array_column($this->data, 'first_name'); - $this->assertContains('John', $data); + $this->assertContains('John', $data); $this->assertContains('Sally', $data); - $this->assertContains('Jane', $data); + $this->assertContains('Jane', $data); $this->assertContains('Peter', $data); - $this->assertArrayHasKey(0, $data); - $this->assertArrayHasKey(1, $data); - $this->assertArrayHasKey(2, $data); - $this->assertArrayHasKey(3, $data); - - + $this->assertArrayHasKey(0, $data); + $this->assertArrayHasKey(1, $data); + $this->assertArrayHasKey(2, $data); + $this->assertArrayHasKey(3, $data); } else { $this->assertTrue(true); } } - - /** * Test: Set a key value pair. * From 5b4a5151582b474bd64362b17b7aff620b1aa83d Mon Sep 17 00:00:00 2001 From: Benjamin Carl Date: Tue, 9 May 2017 00:28:30 +0200 Subject: [PATCH 07/12] Added STYLECI ruleset. --- .styleci.yml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .styleci.yml diff --git a/.styleci.yml b/.styleci.yml new file mode 100644 index 0000000..f014f74 --- /dev/null +++ b/.styleci.yml @@ -0,0 +1,9 @@ +preset: symfony + +risky: true + +finder: + exclude: + - "tests" + name: + - "*.php" From 05a899c44d3f2dd61a8dbc4d51d556b3cd779604 Mon Sep 17 00:00:00 2001 From: Benjamin Carl Date: Tue, 9 May 2017 00:35:40 +0200 Subject: [PATCH 08/12] STYLECI --- demo/Client.php | 8 +- src/Cache.php | 33 +- src/Client.php | 492 ++++++++++------------- src/Compression/CompressionInterface.php | 9 +- src/Compression/Lzw.php | 58 ++- src/Compression/Smaz.php | 99 +++-- src/Compression/Zlib.php | 9 +- src/Exception.php | 5 +- src/Util.php | 32 +- 9 files changed, 327 insertions(+), 418 deletions(-) diff --git a/demo/Client.php b/demo/Client.php index 02a7979..c8818cd 100644 --- a/demo/Client.php +++ b/demo/Client.php @@ -2,7 +2,7 @@ /** * (The MIT license) - * Copyright 2017 clickalicious, Benjamin Carl + * Copyright 2017 clickalicious, Benjamin Carl. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -24,8 +24,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - -require_once dirname(__DIR__) . '/vendor/autoload.php'; +require_once dirname(__DIR__).'/vendor/autoload.php'; use \Clickalicious\Memcached\Php\Client; @@ -38,7 +37,6 @@ // Try to do some stuff with memcached instance ... try { - $memcached->set($dummy, 1); $memcached->increment($dummy, 2); $memcached->increment($dummy, 2); @@ -53,4 +51,4 @@ $result = $e->getMessage(); } -echo sprintf('Result = "%s" (should be "5")', $result) . PHP_EOL; +echo sprintf('Result = "%s" (should be "5")', $result).PHP_EOL; diff --git a/src/Cache.php b/src/Cache.php index 36d75bf..4637c8c 100644 --- a/src/Cache.php +++ b/src/Cache.php @@ -2,7 +2,7 @@ /** * (The MIT license) - * Copyright 2017 clickalicious, Benjamin Carl + * Copyright 2017 clickalicious, Benjamin Carl. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -30,9 +30,8 @@ use Psr\Cache\CacheItemInterface; /** - * Class Cache + * Class Cache. * - * @package Clickalicious\Memcached\Php * @author Benjamin Carl */ class Cache extends Client implements CacheItemInterface @@ -44,7 +43,7 @@ class Cache extends Client implements CacheItemInterface * the higher level callers when needed. * * @return string - * The key string for this cache item. + * The key string for this cache item */ public function getKey() { @@ -56,8 +55,8 @@ public function getKey() * Note: This method MUST NOT have a race condition between calling isHit() * and calling get(). * - * @return boolean - * True if the request resulted in a cache hit. False otherwise. + * @return bool + * True if the request resulted in a cache hit. False otherwise. */ public function isHit() { @@ -70,8 +69,8 @@ public function isHit() * reasons, which could result in a race condition between exists() and get(). * To avoid that potential race condition use isHit() instead. * - * @return boolean - * True if item exists in the cache, false otherwise. + * @return bool + * True if item exists in the cache, false otherwise */ public function exists() { @@ -81,16 +80,16 @@ public function exists() * Sets the expiration for this cache item. * * @param int|\DateTime $ttl - * - If an integer is passed, it is interpreted as the number of seconds - * after which the item MUST be considered expired. - * - If a DateTime object is passed, it is interpreted as the point in - * time after which the item MUST be considered expired. - * - If null is passed, a default value MAY be used. If none is set, - * the value should be stored permanently or for as long as the - * implementation allows. + * - If an integer is passed, it is interpreted as the number of seconds + * after which the item MUST be considered expired. + * - If a DateTime object is passed, it is interpreted as the point in + * time after which the item MUST be considered expired. + * - If null is passed, a default value MAY be used. If none is set, + * the value should be stored permanently or for as long as the + * implementation allows. * * @return static - * The called object. + * The called object */ public function setExpiration($ttl = null) { @@ -103,7 +102,7 @@ public function setExpiration($ttl = null) * which the item expired or the current time if that is not available. * * @return \DateTime - * The timestamp at which this cache item will expire. + * The timestamp at which this cache item will expire */ public function getExpiration() { diff --git a/src/Client.php b/src/Client.php index 58dab05..a26a48f 100644 --- a/src/Client.php +++ b/src/Client.php @@ -2,7 +2,7 @@ /** * (The MIT license) - * Copyright 2017 clickalicious, Benjamin Carl + * Copyright 2017 clickalicious, Benjamin Carl. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -28,9 +28,8 @@ namespace Clickalicious\Memcached\Php; /** - * Class Client + * Class Client. * - * @package Clickalicious\Memcached\Php * @author Benjamin Carl */ class Client @@ -39,7 +38,6 @@ class Client * The persistent ID of the instance for sharing connections via static! * * @var string - * @access protected */ protected $persistentId; @@ -47,7 +45,6 @@ class Client * The Memcached daemons host. * * @var string - * @access protected */ protected $host; @@ -55,23 +52,20 @@ class Client * The Memcached daemons port. * * @var string - * @access protected */ protected $port; /** - * The timeout for connecting in seconds + * The timeout for connecting in seconds. * * @var int - * @access protected */ protected $timeout; /** - * Weather compression enabled + * Weather compression enabled. * * @var bool - * @access protected */ protected $compression; @@ -79,23 +73,20 @@ class Client * The default compressor used. * * @var string - * @access protected */ protected $compressor = self::DEFAULT_COMPRESSOR; /** - * All open connections + * All open connections. * * @var array - * @access protected */ protected static $connections = array(); /** - * Last result + * Last result. * * @var int - * @access protected */ protected $lastResponse = 0; @@ -103,7 +94,6 @@ class Client * Signals for transfer ended. Send as terminator by Memcached instance. * * @var array - * @access protected */ protected $sigsEnd = array( self::RESPONSE_END, @@ -122,7 +112,6 @@ class Client * A VALUE intro - used to detect VALUES response of get() & gets(). * * @var string - * @access public * @const */ const RESPONSE_VALUE = 'VALUE'; @@ -131,7 +120,6 @@ class Client * A STAT intro - used to detect STAT response of . * * @var string - * @access public * @const */ const RESPONSE_STAT = 'STAT'; @@ -140,106 +128,94 @@ class Client * A STAT VALUE intro - used to detect STAT VALUE response of . * * @var string - * @access public * @const */ const RESPONSE_ITEM = 'ITEM'; /** - * Response END + * Response END. * * @var string - * @access public * @const */ const RESPONSE_END = 'END'; /** - * Response DELETED + * Response DELETED. * * @var string - * @access public * @const */ const RESPONSE_DELETED = 'DELETED'; /** - * Response NOT_FOUND + * Response NOT_FOUND. * * @var string - * @access public * @const */ const RESPONSE_NOT_FOUND = 'NOT_FOUND'; /** - * Response OK + * Response OK. * * @var string - * @access public * @const */ const RESPONSE_OK = 'OK'; /** - * Response EXISTS + * Response EXISTS. * * @var string - * @access public * @const */ const RESPONSE_EXISTS = 'EXISTS'; /** - * Response ERROR + * Response ERROR. * * @var string - * @access public * @const */ const RESPONSE_ERROR = 'ERROR'; /** - * Response RESET + * Response RESET. * * @var string - * @access public * @const */ const RESPONSE_RESET = 'RESET'; /** - * Response STORED + * Response STORED. * * @var string - * @access public * @const */ const RESPONSE_STORED = 'STORED'; /** - * Response NOT_STORED + * Response NOT_STORED. * * @var string - * @access public * @const */ const RESPONSE_NOT_STORED = 'NOT_STORED'; /** - * Response VERSION + * Response VERSION. * * @var string - * @access public * @const */ const RESPONSE_VERSION = 'VERSION'; /** - * Response CLIENT_ERROR + * Response CLIENT_ERROR. * * @var string - * @access public * @const */ const RESPONSE_CLIENT_ERROR = 'CLIENT_ERROR'; @@ -248,7 +224,6 @@ class Client * A collection of allowed commands which can be send to Memcached instance. * * @var array - * @access protected */ protected $allowedCommands = array( self::COMMAND_SET, @@ -273,7 +248,6 @@ class Client * The command for setting a key value pair to a Memcached instance. * * @var string - * @access public * @const */ const COMMAND_SET = 'set'; @@ -282,7 +256,6 @@ class Client * Command for adding data to if not already exists. * * @var string - * @access public * @const */ const COMMAND_ADD = 'add'; @@ -291,7 +264,6 @@ class Client * Command for replacing a value with another one. * * @var string - * @access public * @const */ const COMMAND_REPLACE = 'replace'; @@ -300,7 +272,6 @@ class Client * Command for append data to existing data of an existing key. * * @var string - * @access public * @const */ const COMMAND_APPEND = 'append'; @@ -309,7 +280,6 @@ class Client * Command for prepend data to existing data of an existing key. * * @var string - * @access public * @const */ const COMMAND_PREPEND = 'prepend'; @@ -318,7 +288,6 @@ class Client * Command for cas. * * @var string - * @access public * @const */ const COMMAND_CAS = 'cas'; @@ -327,7 +296,6 @@ class Client * Command for incr. * * @var string - * @access public * @const */ const COMMAND_INCR = 'incr'; @@ -336,7 +304,6 @@ class Client * Command for decr. * * @var string - * @access public * @const */ const COMMAND_DECR = 'decr'; @@ -345,7 +312,6 @@ class Client * Command for retrieving a key and its value from a Memcached instance. * * @var string - * @access public * @const */ const COMMAND_GET = 'get'; @@ -354,7 +320,6 @@ class Client * Command for retrieving multiple keys and the values from a Memcached instance. * * @var string - * @access public * @const */ const COMMAND_GETS = 'gets'; @@ -363,7 +328,6 @@ class Client * The command for deleting a key value pair from a Memcached instance. * * @var string - * @access public * @const */ const COMMAND_DELETE = 'delete'; @@ -372,7 +336,6 @@ class Client * The command for touching a key value pair from a Memcached instance to change expiration time. * * @var string - * @access public * @const */ const COMMAND_TOUCH = 'touch'; @@ -381,7 +344,6 @@ class Client * The command for retrieving the version from a Memcached instance. * * @var string - * @access public * @const */ const COMMAND_VERSION = 'version'; @@ -390,7 +352,6 @@ class Client * The command for retrieving the stats from a Memcached instance. * * @var string - * @access public * @const */ const COMMAND_STATS = 'stats'; @@ -399,7 +360,6 @@ class Client * The command for flushing all keys from a Memcached instance. * * @var string - * @access public * @const */ const COMMAND_FLUSH_ALL = 'flush_all'; @@ -408,7 +368,6 @@ class Client * The command which will fail for unit testing. * * @var string - * @access public * @const */ const COMMAND_PHPUNIT = 'phpunit'; @@ -417,7 +376,6 @@ class Client * The command for retrieving the stats settings from a Memcached instance. * * @var string - * @access public * @const */ const COMMAND_STATS_SETTINGS = 'settings'; @@ -426,7 +384,6 @@ class Client * The command for retrieving the stats items from a Memcached instance. * * @var string - * @access public * @const */ const COMMAND_STATS_ITEMS = 'items'; @@ -435,7 +392,6 @@ class Client * The command for retrieving the stats slabs from a Memcached instance. * * @var string - * @access public * @const */ const COMMAND_STATS_SLABS = 'slabs'; @@ -444,7 +400,6 @@ class Client * The command for retrieving the stats reset from a Memcached instance. * * @var string - * @access public * @const */ const COMMAND_STATS_RESET = 'reset'; @@ -453,7 +408,6 @@ class Client * Command for retrieving stats sizes. * * @var string - * @access public * @const */ const COMMAND_STATS_SIZES = 'sizes'; @@ -462,7 +416,6 @@ class Client * Command for retrieving stats conns. * * @var string - * @access public * @const */ const COMMAND_STATS_CONNS = 'conns'; @@ -471,7 +424,6 @@ class Client * Command for retrieving stats cachedump. * * @var string - * @access public * @const */ const COMMAND_STATS_CACHEDUMP = 'cachedump'; @@ -480,7 +432,6 @@ class Client * The settings stats type. * * @var string - * @access public * @const */ const STATS_TYPE_SETTINGS = self::COMMAND_STATS_SETTINGS; @@ -489,7 +440,6 @@ class Client * The items stats type. * * @var string - * @access public * @const */ const STATS_TYPE_ITEMS = self::COMMAND_STATS_ITEMS; @@ -498,7 +448,6 @@ class Client * The slabs stats type. * * @var string - * @access public * @const */ const STATS_TYPE_SLABS = self::COMMAND_STATS_SLABS; @@ -507,7 +456,6 @@ class Client * The reset stats type. * * @var string - * @access public * @const */ const STATS_TYPE_RESET = self::COMMAND_STATS_RESET; @@ -516,7 +464,6 @@ class Client * The conns stats type. * * @var string - * @access public * @const */ const STATS_TYPE_CONNS = self::COMMAND_STATS_CONNS; @@ -525,7 +472,6 @@ class Client * The cachedump stats type. * * @var string - * @access public * @const */ const STATS_TYPE_CACHEDUMP = self::COMMAND_STATS_CACHEDUMP; @@ -534,7 +480,6 @@ class Client * The sizes stats type. * * @var string - * @access public * @const */ const STATS_TYPE_SIZES = self::COMMAND_STATS_SIZES; @@ -543,7 +488,6 @@ class Client * Number of bytes fetched in one cycle from socket. * * @var int - * @access public * @const */ const SOCKET_READ_FETCH_BYTES = 256; @@ -552,7 +496,6 @@ class Client * Maximum items to fetch if not overridden. * * @var int - * @access public * @const */ const CACHEDUMP_ITEMS_MAX = PHP_INT_MAX; @@ -561,7 +504,6 @@ class Client * The default compressor. * * @var string - * @access public * @const */ const DEFAULT_COMPRESSOR = 'SMAZ'; @@ -570,7 +512,6 @@ class Client * The default port of a host added. * * @var int - * @access public * @const */ const DEFAULT_PORT = 11211; @@ -579,7 +520,6 @@ class Client * The default timeout when connecting to instance. * * @var null - * @access public * @const */ const DEFAULT_TIMEOUT = null; @@ -588,7 +528,6 @@ class Client * The separator for building commandline for Memcached instance. * * @var string - * @access public * @const */ const COMMAND_SEPARATOR = ' '; @@ -597,7 +536,6 @@ class Client * The terminator used to terminate a commandline send to Memcached instance. * * @var string - * @access public * @const */ const COMMAND_TERMINATOR = "\r\n"; @@ -606,7 +544,6 @@ class Client * The default and generic Memcached error. * * @var string - * @access public * @const */ const ERROR = 'ERROR'; @@ -615,7 +552,6 @@ class Client * The Memcached error for client error. * * @var string - * @access public * @const */ const ERROR_CLIENT = 'CLIENT_ERROR'; @@ -624,7 +560,6 @@ class Client * The Memcached error for server error. * * @var string - * @access public * @const */ const ERROR_SERVER = 'SERVER_ERROR'; @@ -633,17 +568,15 @@ class Client * The default bitmask to detect serialization support. * * @var int - * @access public * @const */ const FLAG_DEFAULT = 4; /** - * Flags for PHP types + * Flags for PHP types. * * (Memcached PHP extension compatible) * - * @access public * @const */ const FLAG_DECIMAL_STRING = 0; // PHP Type "string" Mask - Decimal: 0 - Bit(s): 0 @@ -657,7 +590,7 @@ class Client const FLAG_DECIMAL_SERIALIZED = 4; // PHP Type "object" || "array" Mask - Decimal: 4 - Bit(s): 4 /** - * Memcached Constant Values + * Memcached Constant Values. */ const MEMCACHED_SUCCESS = 0; @@ -764,7 +697,7 @@ class Client * Constructor. * * @param string $host The host name this instance works on - * @param int $port The port to connect to. + * @param int $port the port to connect to * @param int|null $timeout The timeout for connecting in seconds * @param string $persistentId By default the Memcached instances are destroyed at the end of the request. * To create an instance that persists between requests, use persistent_id to specify @@ -773,8 +706,8 @@ class Client * @param bool $compression TRUE to enable compression (default), FALSE to disable * * @author Benjamin Carl + * * @return Client - * @access public */ public function __construct( $host = null, @@ -782,8 +715,7 @@ public function __construct( $timeout = self::DEFAULT_TIMEOUT, $persistentId = null, $compression = true - ) - { + ) { // Extract host and port from host string if ($host !== null) { $this @@ -816,11 +748,9 @@ public function __construct( /** * Setter for host. * - * @param string $host The host to set. + * @param string $host the host to set * * @author Benjamin Carl - * @return void - * @access public */ public function setHost($host) { @@ -830,11 +760,11 @@ public function setHost($host) /** * Setter for host. * - * @param string $host The host to set. + * @param string $host the host to set * * @author Benjamin Carl + * * @return $this Instance for chaining - * @access public */ public function host($host) { @@ -847,8 +777,8 @@ public function host($host) * Getter for host. * * @author Benjamin Carl - * @return string The host if set, otherwise NULL. - * @access public + * + * @return string the host if set, otherwise NULL */ public function getHost() { @@ -858,11 +788,9 @@ public function getHost() /** * Setter for port. * - * @param string $port The port to set. + * @param string $port the port to set * * @author Benjamin Carl - * @return void - * @access public */ public function setPort($port) { @@ -872,11 +800,11 @@ public function setPort($port) /** * Setter for port. * - * @param string $port The port to set. + * @param string $port the port to set * * @author Benjamin Carl + * * @return $this Instance for chaining - * @access public */ public function port($port) { @@ -889,8 +817,8 @@ public function port($port) * Getter for port. * * @author Benjamin Carl - * @return string The port if set, otherwise NULL. - * @access public + * + * @return string the port if set, otherwise NULL */ public function getPort() { @@ -903,8 +831,6 @@ public function getPort() * @param int $timeout Timeout for connecting in seconds!!! * * @author Benjamin Carl - * @return void - * @access public */ public function setTimeout($timeout) { @@ -917,8 +843,8 @@ public function setTimeout($timeout) * @param int $timeout Timeout for connecting in seconds!!! * * @author Benjamin Carl + * * @return $this Instance for chaining - * @access public */ public function timeout($timeout) { @@ -931,8 +857,8 @@ public function timeout($timeout) * Getter for timeout. * * @author Benjamin Carl - * @return integer The timeout in seconds or NULL - * @access public + * + * @return int The timeout in seconds or NULL */ public function getTimeout() { @@ -947,8 +873,8 @@ public function getTimeout() * @param int|null $timeout Timeout in seconds * * @author Benjamin Carl + * * @return resource|null The resource (socket) on success, otherwise NULL - * @access public * * @throws \Clickalicious\Memcached\Php\Exception */ @@ -1012,8 +938,9 @@ public function connect($host, $port, $timeout = null) * @param string $data The data to pass with command * * @author Benjamin Carl + * * @return mixed|array The result from Memcached daemon - * @access public + * * @throws \Clickalicious\Memcached\Php\Exception */ public function send($command, $data = '') @@ -1039,7 +966,6 @@ public function send($command, $data = '') // Fetch while receiving data ... while ((!feof($socket))) { - // Fetch Bytes from socket ... $buffer .= fgets($socket, self::SOCKET_READ_FETCH_BYTES); @@ -1049,7 +975,7 @@ public function send($command, $data = '') } foreach ($this->sigsEnd as $sigEnd) { - if (preg_match('/^' . $sigEnd . '/imu', $buffer)) { + if (preg_match('/^'.$sigEnd.'/imu', $buffer)) { break 2; } } @@ -1062,7 +988,7 @@ public function send($command, $data = '') 'Error "%s" while sending command "%s" to host "%s"', $this->getLastResponse(), $command, - $this->getHost() . ':' . $this->getPort() + $this->getHost().':'.$this->getPort() ) ); } @@ -1078,33 +1004,33 @@ public function send($command, $data = '') * @param int $expiration When to expire * * @author Benjamin Carl + * * @return string The result of operation - * @access public * @codeCoverageIgnore */ public function touch($key, $expiration) { /** - * touch [noreply]\r\n + * touch [noreply]\r\n. */ // Build packet to send ... - $data = self::COMMAND_TOUCH . self::COMMAND_SEPARATOR . - $key . self::COMMAND_SEPARATOR . - $expiration . self::COMMAND_TERMINATOR; + $data = self::COMMAND_TOUCH.self::COMMAND_SEPARATOR. + $key.self::COMMAND_SEPARATOR. + $expiration.self::COMMAND_TERMINATOR; return $this->send(self::COMMAND_TOUCH, $data); } /** - * Proxy to: incr() + * Proxy to: incr(). * * @param string $key The key to increment * @param int $offset How much to increment * * @author Benjamin Carl + * * @return string The result of operation - * @access public */ public function increment($key, $offset = 1) { @@ -1119,19 +1045,19 @@ public function increment($key, $offset = 1) * @param int $offset How much to increment * * @author Benjamin Carl + * * @return string The result of operation - * @access public */ public function incr($key, $offset = 1) { /** - * incr [noreply]\r\n + * incr [noreply]\r\n. */ // Build packet to send ... - $data = self::COMMAND_INCR . self::COMMAND_SEPARATOR . - $key . self::COMMAND_SEPARATOR . - $offset . self::COMMAND_TERMINATOR; + $data = self::COMMAND_INCR.self::COMMAND_SEPARATOR. + $key.self::COMMAND_SEPARATOR. + $offset.self::COMMAND_TERMINATOR; return $this->send(self::COMMAND_INCR, $data); } @@ -1143,8 +1069,8 @@ public function incr($key, $offset = 1) * @param int $offset How much to decrement * * @author Benjamin Carl + * * @return string The result of operation - * @access public */ public function decrement($key, $offset = 1) { @@ -1159,19 +1085,19 @@ public function decrement($key, $offset = 1) * @param int $offset How much to decrement * * @author Benjamin Carl + * * @return string The result of operation - * @access public */ public function decr($key, $offset = 1) { /** - * decr [noreply]\r\n + * decr [noreply]\r\n. */ // Build packet to send ... - $data = self::COMMAND_DECR . self::COMMAND_SEPARATOR . - $key . self::COMMAND_SEPARATOR . - $offset . self::COMMAND_TERMINATOR; + $data = self::COMMAND_DECR.self::COMMAND_SEPARATOR. + $key.self::COMMAND_SEPARATOR. + $offset.self::COMMAND_TERMINATOR; return $this->send(self::COMMAND_DECR, $data); } @@ -1187,14 +1113,14 @@ public function decr($key, $offset = 1) * @param int|null $bytes The length in bytes * * @author Benjamin Carl + * * @return mixed The response for command set - * @access public */ public function set($key, $value, $expiration = 0, $flags = 0, $bytes = null) { /** * set [noreply]\r\n - * \r\n + * \r\n. */ // Run through our serializer @@ -1208,12 +1134,12 @@ public function set($key, $value, $expiration = 0, $flags = 0, $bytes = null) $bytes = ($bytes !== null) ? $bytes : strlen($value); // Build packet to send ... - $data = self::COMMAND_SET . self::COMMAND_SEPARATOR . - $key . self::COMMAND_SEPARATOR . - $flags . self::COMMAND_SEPARATOR . - $expiration . self::COMMAND_SEPARATOR . - $bytes . self::COMMAND_TERMINATOR . - $value . self::COMMAND_TERMINATOR; + $data = self::COMMAND_SET.self::COMMAND_SEPARATOR. + $key.self::COMMAND_SEPARATOR. + $flags.self::COMMAND_SEPARATOR. + $expiration.self::COMMAND_SEPARATOR. + $bytes.self::COMMAND_TERMINATOR. + $value.self::COMMAND_TERMINATOR; return $this->send(self::COMMAND_SET, $data); } @@ -1230,14 +1156,14 @@ public function set($key, $value, $expiration = 0, $flags = 0, $bytes = null) * @param int|null $bytes The length in bytes * * @author Benjamin Carl + * * @return mixed The response for command add - * @access public */ public function add($key, $value, $expiration = 0, $flags = 0, $bytes = null) { /** * add [noreply]\r\n - * \r\n + * \r\n. */ // Run through our serializer @@ -1251,12 +1177,12 @@ public function add($key, $value, $expiration = 0, $flags = 0, $bytes = null) $bytes = ($bytes !== null) ? $bytes : strlen($value); // Build packet to send ... - $data = self::COMMAND_ADD . self::COMMAND_SEPARATOR . - $key . self::COMMAND_SEPARATOR . - $flags . self::COMMAND_SEPARATOR . - $expiration . self::COMMAND_SEPARATOR . - $bytes . self::COMMAND_TERMINATOR . - $value . self::COMMAND_TERMINATOR; + $data = self::COMMAND_ADD.self::COMMAND_SEPARATOR. + $key.self::COMMAND_SEPARATOR. + $flags.self::COMMAND_SEPARATOR. + $expiration.self::COMMAND_SEPARATOR. + $bytes.self::COMMAND_TERMINATOR. + $value.self::COMMAND_TERMINATOR; return $this->send(self::COMMAND_ADD, $data); } @@ -1273,14 +1199,14 @@ public function add($key, $value, $expiration = 0, $flags = 0, $bytes = null) * @param int|null $bytes The length in bytes * * @author Benjamin Carl + * * @return mixed The response for command replace - * @access public */ public function replace($key, $value, $expiration = 0, $flags = 0, $bytes = null) { /** * replace [noreply]\r\n - * \r\n + * \r\n. */ // Run through our serializer @@ -1294,12 +1220,12 @@ public function replace($key, $value, $expiration = 0, $flags = 0, $bytes = null $bytes = ($bytes !== null) ? $bytes : strlen($value); // Build packet to send ... - $data = self::COMMAND_REPLACE . self::COMMAND_SEPARATOR . - $key . self::COMMAND_SEPARATOR . - $flags . self::COMMAND_SEPARATOR . - $expiration . self::COMMAND_SEPARATOR . - $bytes . self::COMMAND_TERMINATOR . - $value . self::COMMAND_TERMINATOR; + $data = self::COMMAND_REPLACE.self::COMMAND_SEPARATOR. + $key.self::COMMAND_SEPARATOR. + $flags.self::COMMAND_SEPARATOR. + $expiration.self::COMMAND_SEPARATOR. + $bytes.self::COMMAND_TERMINATOR. + $value.self::COMMAND_TERMINATOR; return $this->send(self::COMMAND_REPLACE, $data); } @@ -1315,26 +1241,26 @@ public function replace($key, $value, $expiration = 0, $flags = 0, $bytes = null * @param int|null $bytes The length in bytes * * @author Benjamin Carl + * * @return mixed The response for command append - * @access public */ public function append($key, $value, $expiration = 0, $flags = self::FLAG_DEFAULT, $bytes = null) { /** * replace [noreply]\r\n - * \r\n + * \r\n. */ // Calculate bytes if not precalculated $bytes = ($bytes !== null) ? $bytes : strlen($value); // Build packet to send ... - $data = self::COMMAND_APPEND . self::COMMAND_SEPARATOR . - $key . self::COMMAND_SEPARATOR . - $flags . self::COMMAND_SEPARATOR . - $expiration . self::COMMAND_SEPARATOR . - $bytes . self::COMMAND_TERMINATOR . - $value . self::COMMAND_TERMINATOR; + $data = self::COMMAND_APPEND.self::COMMAND_SEPARATOR. + $key.self::COMMAND_SEPARATOR. + $flags.self::COMMAND_SEPARATOR. + $expiration.self::COMMAND_SEPARATOR. + $bytes.self::COMMAND_TERMINATOR. + $value.self::COMMAND_TERMINATOR; return $this->send(self::COMMAND_APPEND, $data); } @@ -1350,26 +1276,26 @@ public function append($key, $value, $expiration = 0, $flags = self::FLAG_DEFAUL * @param int|null $bytes The length in bytes * * @author Benjamin Carl + * * @return mixed The response for command prepend - * @access public */ public function prepend($key, $value, $expiration = 0, $flags = self::FLAG_DEFAULT, $bytes = null) { /** * replace [noreply]\r\n - * \r\n + * \r\n. */ // Calculate bytes if not precalculated $bytes = ($bytes !== null) ? $bytes : strlen($value); // Build packet to send ... - $data = self::COMMAND_PREPEND . self::COMMAND_SEPARATOR . - $key . self::COMMAND_SEPARATOR . - $flags . self::COMMAND_SEPARATOR . - $expiration . self::COMMAND_SEPARATOR . - $bytes . self::COMMAND_TERMINATOR . - $value . self::COMMAND_TERMINATOR; + $data = self::COMMAND_PREPEND.self::COMMAND_SEPARATOR. + $key.self::COMMAND_SEPARATOR. + $flags.self::COMMAND_SEPARATOR. + $expiration.self::COMMAND_SEPARATOR. + $bytes.self::COMMAND_TERMINATOR. + $value.self::COMMAND_TERMINATOR; return $this->send(self::COMMAND_PREPEND, $data); } @@ -1389,14 +1315,14 @@ public function prepend($key, $value, $expiration = 0, $flags = self::FLAG_DEFAU * @param int|null $bytes The length in bytes * * @author Benjamin Carl + * * @return mixed The response for command cas - * @access public */ public function cas($token, $key, $value, $expiration = 0, $flags = self::FLAG_DEFAULT, $bytes = null) { /** * cas [noreply]\r\n - * \r\n + * \r\n. */ // Run through our serializer @@ -1410,13 +1336,13 @@ public function cas($token, $key, $value, $expiration = 0, $flags = self::FLAG_D $bytes = ($bytes !== null) ? $bytes : strlen($value); // Build packet to send ... - $data = self::COMMAND_CAS . self::COMMAND_SEPARATOR . - $key . self::COMMAND_SEPARATOR . - $flags . self::COMMAND_SEPARATOR . - $expiration . self::COMMAND_SEPARATOR . - $bytes . self::COMMAND_SEPARATOR . - $token . self::COMMAND_TERMINATOR . - $value . self::COMMAND_TERMINATOR; + $data = self::COMMAND_CAS.self::COMMAND_SEPARATOR. + $key.self::COMMAND_SEPARATOR. + $flags.self::COMMAND_SEPARATOR. + $expiration.self::COMMAND_SEPARATOR. + $bytes.self::COMMAND_SEPARATOR. + $token.self::COMMAND_TERMINATOR. + $value.self::COMMAND_TERMINATOR; return $this->send(self::COMMAND_CAS, $data); } @@ -1424,22 +1350,22 @@ public function cas($token, $key, $value, $expiration = 0, $flags = self::FLAG_D /** * Returns the response for passed key. * - * @param string $key The key to return response for. + * @param string $key the key to return response for * @param bool $metadata TRUE to return metadata like lifetime ... as well, FALSE to return value only * * @author Benjamin Carl + * * @return mixed The response for command get - * @access public */ public function get($key, $metadata = false) { /** - * get *\r\n + * get *\r\n. */ // Build packet to send ... - $data = self::COMMAND_GET . self::COMMAND_SEPARATOR . - $key . self::COMMAND_TERMINATOR; + $data = self::COMMAND_GET.self::COMMAND_SEPARATOR. + $key.self::COMMAND_TERMINATOR; $result = $this->send(self::COMMAND_GET, $data); @@ -1455,25 +1381,25 @@ public function get($key, $metadata = false) /** * Returns the response for passed keys. * - * @param array $keys The keys to return response for. + * @param array $keys the keys to return response for * @param bool $metadata TRUE to return metadata like lifetime ... as well, FALSE to return value only * * @author Benjamin Carl + * * @return mixed The response for command get - * @access public */ public function gets(array $keys, $metadata = false) { /** - * gets *\r\n + * gets *\r\n. */ // Build key request string $keys = implode(self::COMMAND_SEPARATOR, $keys); // Build packet to send ... - $data = self::COMMAND_GETS . self::COMMAND_SEPARATOR . - $keys . self::COMMAND_TERMINATOR; + $data = self::COMMAND_GETS.self::COMMAND_SEPARATOR. + $keys.self::COMMAND_TERMINATOR; $result = $this->send(self::COMMAND_GETS, $data); @@ -1488,21 +1414,21 @@ public function gets(array $keys, $metadata = false) /** * Deletes an element/key (+ its data) from Memcached instance. * - * @param string $key The key to delete. + * @param string $key the key to delete * * @author Benjamin Carl + * * @return mixed The response for command delete - * @access public */ public function delete($key) { /** - * delete [noreply]\r\n + * delete [noreply]\r\n. */ // Build packet to send ... - $data = self::COMMAND_DELETE . self::COMMAND_SEPARATOR . - $key . self::COMMAND_TERMINATOR; + $data = self::COMMAND_DELETE.self::COMMAND_SEPARATOR. + $key.self::COMMAND_TERMINATOR; return $this->send(self::COMMAND_DELETE, $data); } @@ -1510,42 +1436,41 @@ public function delete($key) /** * Stats - sends the stats command with default or custom type to Memcached instance. * - * @param string $type The type to send to Memcached instance. - * @param string $argument1 Optional additional argument. - * @param string $argument2 Optional additional argument. + * @param string $type the type to send to Memcached instance + * @param string $argument1 optional additional argument + * @param string $argument2 optional additional argument * * @author Benjamin Carl + * * @return mixed The response for command stats - * @access public */ public function stats($type = '', $argument1 = '', $argument2 = '') { - /** + /* * stats [noreply]\r\n * [TYPES ] */ if ($type !== '') { - $type = self::COMMAND_SEPARATOR . $type; + $type = self::COMMAND_SEPARATOR.$type; } if ($argument1 !== '') { - $argument1 = self::COMMAND_SEPARATOR . $argument1; + $argument1 = self::COMMAND_SEPARATOR.$argument1; } if ($argument2 !== '') { - $argument2 = self::COMMAND_SEPARATOR . $argument2; + $argument2 = self::COMMAND_SEPARATOR.$argument2; } // Build packet to send ... - $data = self::COMMAND_STATS . $type . - $argument1 . - $argument2 . + $data = self::COMMAND_STATS.$type. + $argument1. + $argument2. self::COMMAND_TERMINATOR; // Generic stats requires us to fetch as long as data arrives ... if ($type === '') { - // Initial fetch ... $result = $this->send(self::COMMAND_STATS, $data); @@ -1559,20 +1484,17 @@ public function stats($type = '', $argument1 = '', $argument2 = '') $result[$key] = $value; } } - } elseif ($type === self::COMMAND_SEPARATOR . self::STATS_TYPE_SLABS) { - + } elseif ($type === self::COMMAND_SEPARATOR.self::STATS_TYPE_SLABS) { // Initial fetch ... $result = $this->send(self::COMMAND_STATS, $data); // Now read until whole structure contains finally "active_slabs" key! while (isset($result['active_slabs']) === false) { - // Issue stat command ... $memory = $this->send(self::COMMAND_STATS, $data); // Iterate Slabs from response foreach ($memory as $key => $value) { - // Now check for slabId or meta-data key. Slab = numeric, otherwise String. if (is_numeric($key) === true) { // Slab! @@ -1608,27 +1530,27 @@ public function stats($type = '', $argument1 = '', $argument2 = '') * Version - sends the version command to Memcached instance and returns the result. * * @author Benjamin Carl + * * @return mixed The response for command version - * @access public */ public function version() { /** - * version\r\n + * version\r\n. */ // Build packet to send ... - $data = self::COMMAND_VERSION . self::COMMAND_TERMINATOR; + $data = self::COMMAND_VERSION.self::COMMAND_TERMINATOR; return $this->send(self::COMMAND_VERSION, $data); } /** - * Proxy to: flush_all() + * Proxy to: flush_all(). * * @author Benjamin Carl + * * @return mixed The response for command version - * @access public */ public function flush() { @@ -1639,17 +1561,17 @@ public function flush() * Flush - sends the flush_all command to Memcached instance and returns the result. * * @author Benjamin Carl + * * @return mixed The response for command version - * @access public */ public function flush_all() { /** - * flush_all\r\n + * flush_all\r\n. */ // Build packet to send ... - $data = self::COMMAND_FLUSH_ALL . self::COMMAND_TERMINATOR; + $data = self::COMMAND_FLUSH_ALL.self::COMMAND_TERMINATOR; return $this->send(self::COMMAND_FLUSH_ALL, $data); } @@ -1664,8 +1586,6 @@ public function flush_all() * @param bool $compression TRUE or FALSE * * @author Benjamin Carl - * @return void - * @access public */ protected function setCompression($compression) { @@ -1678,8 +1598,8 @@ protected function setCompression($compression) * @param bool $compression TRUE or FALSE * * @author Benjamin Carl + * * @return $this Instance for chaining - * @access public */ protected function compression($compression) { @@ -1692,8 +1612,8 @@ protected function compression($compression) * Getter for compression. * * @author Benjamin Carl + * * @return bool TRUE if compression is enabled, otherwise FALSE - * @access public * @codeCoverageIgnore */ protected function getCompression() @@ -1707,8 +1627,6 @@ protected function getCompression() * @param string $persistentId The persistent Id to store * * @author Benjamin Carl - * @return void - * @access protected */ protected function setPersistentId($persistentId) { @@ -1721,8 +1639,8 @@ protected function setPersistentId($persistentId) * @param string $persistentId The persistent Id to store * * @author Benjamin Carl + * * @return $this Instance for chaining - * @access protected */ protected function persistentId($persistentId) { @@ -1735,8 +1653,8 @@ protected function persistentId($persistentId) * Getter for persistent Id. * * @author Benjamin Carl + * * @return string The persistent Id set at instantiation or generated by this instance - * @access protected */ protected function getPersistentId() { @@ -1749,14 +1667,14 @@ protected function getPersistentId() * @param int $response The response * * @author Benjamin Carl - * @return boolean TRUE if response valid (0) was set, otherwise FALSE - * @access protected + * + * @return bool TRUE if response valid (0) was set, otherwise FALSE */ protected function setLastResponse($response) { $this->lastResponse = $response; - return ($response === 0); + return $response === 0; } /** @@ -1765,8 +1683,8 @@ protected function setLastResponse($response) * @param int $response The response array (command => buffer) * * @author Benjamin Carl + * * @return $this Instance for chaining - * @access protected */ protected function lastResponse($response) { @@ -1779,8 +1697,8 @@ protected function lastResponse($response) * Getter for lastResponse. * * @author Benjamin Carl + * * @return int The last response - * @access protected */ protected function getLastResponse() { @@ -1791,8 +1709,8 @@ protected function getLastResponse() * Simple generic hashing of dynamic input. * * @author Benjamin Carl + * * @return string The calculated UUID - * @access protected */ protected function uuid() { @@ -1807,28 +1725,28 @@ protected function uuid() * @param mixed $value The value to check * * @author Benjamin Carl + * * @return bool TRUE if is serializable, otherwise FALSE - * @access public */ protected function isSerializable($value) { $type = gettype($value); - return ( - $type !== "string" && // Mask - Decimal: 0 - Bit(s): 0 - $type !== "integer" && // Mask - Decimal: 1 - Bit(s): 1 - $type !== "double" && // Mask - Decimal: 2 - Bit(s): 2 - $type !== "boolean" // Mask - Decimal: 3 - Bit(s): 1 & 2 + return + $type !== 'string' && // Mask - Decimal: 0 - Bit(s): 0 + $type !== 'integer' && // Mask - Decimal: 1 - Bit(s): 1 + $type !== 'double' && // Mask - Decimal: 2 - Bit(s): 2 + $type !== 'boolean' // Mask - Decimal: 3 - Bit(s): 1 & 2 // Mask - Decimal: 4 - Bit(s): 4 - ); + ; } /** * Resets state to clean fresh as new instantiated. * * @author Benjamin Carl + * * @return $this Instance for chaining - * @access protected */ protected function reset() { @@ -1844,8 +1762,9 @@ protected function reset() * @param array $lines The buffer separated into single lines in a collection * * @author Benjamin Carl + * * @return array|bool Response parsed as collection, otherwise FALSE (ERROR) - * @access protected + * * @throws \Clickalicious\Memcached\Php\Exception */ protected function parseReadResponse($buffer, array $lines) @@ -1862,7 +1781,7 @@ protected function parseReadResponse($buffer, array $lines) * Try to fetch metadata. Why try? Cause we can receive multiple lines for a value. If the current key * reference to a simple value like an integer (65000 for example) then we have one line data and * one line data. But if the value contains a "\r\n" itself it breaks this simple assumption so - * that we must + * that we must. */ $metaData = explode(self::COMMAND_SEPARATOR, $lines[$line]); @@ -1878,15 +1797,15 @@ protected function parseReadResponse($buffer, array $lines) // Value must be at least starting on next line - and can continue to spawn on n following lines ... $key = $metaData[1]; $value = ''; - $flags = (int)$metaData[2]; + $flags = (int) $metaData[2]; $length = $metaData[3]; - $cas = (isset($metaData[4])) ? (float)$metaData[4] : null; + $cas = (isset($metaData[4])) ? (float) $metaData[4] : null; $frame = 0; if ($length > 0) { // Fetch whole & complete value! while (strlen($value) < $length) { - ++ $frame; + ++$frame; if ($lines[$line + $frame] === self::RESPONSE_END && !isset($lines[$line + $frame + 1])) { $frame_break = true; break; @@ -1901,14 +1820,14 @@ protected function parseReadResponse($buffer, array $lines) $result[$key] = array( // 1st bit set = we use un-/serialize to keep the values intact ... //'value' => $value, - 'key' => $key, + 'key' => $key, 'meta' => array( - 'key' => $key, - 'flags' => $flags, + 'key' => $key, + 'flags' => $flags, 'length' => $length, - 'cas' => $cas, - 'frames' => $frame - ) + 'cas' => $cas, + 'frames' => $frame, + ), ); if ($this->isFlagSet($flags, self::FLAG_DECIMAL_SERIALIZED) === true) { @@ -1947,7 +1866,7 @@ protected function parseReadResponse($buffer, array $lines) } /** - * Parser for response of write operations like: + * Parser for response of write operations like: . * * - "STORED\r\n" to indicate success. * - "NOT_STORED\r\n" to indicate the data was not stored, but not because of an error. This normally means that the @@ -1960,13 +1879,13 @@ protected function parseReadResponse($buffer, array $lines) * @param array $lines Response split into single lines * * @author Benjamin Carl + * * @return bool TRUE if write operation was successful (STORED), otherwise (ALL OTHER CASE) FALSE - * @access protected */ protected function parseWriteResponse($buffer, array $lines) { // We assume that everything beside "STORED" is an error case ... - $result = ($buffer === self::RESPONSE_STORED . self::COMMAND_TERMINATOR); + $result = ($buffer === self::RESPONSE_STORED.self::COMMAND_TERMINATOR); // Successful? if ($result === true) { @@ -1996,25 +1915,25 @@ protected function parseWriteResponse($buffer, array $lines) } /** - * Parser for response of flush operation: + * Parser for response of flush operation: . * * SUCCESS = RESPONSE = "OK" * FAILED = RESPONSE = "?" * - * @param string $buffer We do only need the buffer. + * @param string $buffer we do only need the buffer * * @author Benjamin Carl + * * @return bool TRUE if flush operation was successful (STORED), otherwise (ALL OTHER CASE) FALSE - * @access protected */ protected function parseFlushResponse($buffer) { // We assume that everything beside "OK" is an error case ... - return ($buffer === self::RESPONSE_OK . self::COMMAND_TERMINATOR); + return $buffer === self::RESPONSE_OK.self::COMMAND_TERMINATOR; } /** - * Parser for response of delete operation: + * Parser for response of delete operation: . * * SUCCESS = RESPONSE = "DELETED" * FAILED = RESPONSE = "NOT_FOUND" @@ -2022,8 +1941,8 @@ protected function parseFlushResponse($buffer) * @param array $lines Response split into single lines * * @author Benjamin Carl + * * @return bool TRUE if delete operation was successful (STORED), otherwise (ALL OTHER CASE) FALSE - * @access protected */ protected function parseDeleteResponse(array $lines) { @@ -2046,13 +1965,14 @@ protected function parseDeleteResponse(array $lines) /** * Parser for response of stats* operation: * Try to fetch in this way: split descriptor/key from value - each stats entry is on one line - * STAT \r\n + * STAT \r\n. * * @param array $lines The buffer separated into single lines in a collection * * @author Benjamin Carl + * * @return array|bool The stats as collection indexed by hostname?, otherwise FALSE (ERROR) - * @access protected + * * @throws \Clickalicious\Memcached\Php\Exception */ protected function parseStatsResponse(array $lines) @@ -2103,9 +2023,9 @@ protected function parseStatsResponse(array $lines) $key = array_shift($metaData); $value = implode(self::COMMAND_SEPARATOR, $metaData); $result[$key] = $value; - }; + } - ++ $line; + ++$line; } $this->lastResponse(self::MEMCACHED_SUCCESS); @@ -2122,8 +2042,8 @@ protected function parseStatsResponse(array $lines) * @param array $lines Response split into single lines * * @author Benjamin Carl + * * @return string|bool The version as string if valid, otherwise FALSE - * @access protected */ protected function parseVersionResponse(array $lines) { @@ -2150,8 +2070,8 @@ protected function parseVersionResponse(array $lines) * @param array $lines Response split into single lines * * @author Benjamin Carl + * * @return string|bool The version as string if valid, otherwise FALSE - * @access protected */ protected function parseArithmeticResponse($buffer, array $lines) { @@ -2159,11 +2079,11 @@ protected function parseArithmeticResponse($buffer, array $lines) $metaData = explode(self::COMMAND_SEPARATOR, $lines[0]); // Check buffer for failure response - if ($buffer === self::RESPONSE_NOT_FOUND . self::COMMAND_TERMINATOR) { + if ($buffer === self::RESPONSE_NOT_FOUND.self::COMMAND_TERMINATOR) { $result = $this->setLastResponse(self::MEMCACHED_NOTFOUND); } else { // Insert the response (= new value) as result - $result = (float)$metaData[0]; + $result = (float) $metaData[0]; } return $result; @@ -2175,19 +2095,19 @@ protected function parseArithmeticResponse($buffer, array $lines) * @param string $buffer The buffer to check * * @author Benjamin Carl + * * @return bool TRUE on success, otherwise FALSE if response contains ERROR(s) - * @access protected */ protected function checkResponse($buffer) { // Check for HARD errors. Not an unsuccessful response from command -> here = real errors - if (preg_match('/' . self::ERROR . '(.*)\R/mu', $buffer, $error) > 0) { + if (preg_match('/'.self::ERROR.'(.*)\R/mu', $buffer, $error) > 0) { // ERROR\r\n $result = self::MEMCACHED_FAILURE; - } elseif (preg_match('/' . self::ERROR_CLIENT . '(.*)\R/mu', $buffer, $error) > 0) { + } elseif (preg_match('/'.self::ERROR_CLIENT.'(.*)\R/mu', $buffer, $error) > 0) { // CLIENT_ERROR\r\n $result = self::RESPONSE_CLIENT_ERROR; - } elseif (preg_match('/' . self::ERROR_SERVER . '(.*)\R/mu', $buffer, $error) > 0) { + } elseif (preg_match('/'.self::ERROR_SERVER.'(.*)\R/mu', $buffer, $error) > 0) { // SERVER_ERROR\r\n $result = self::ERROR_SERVER; } else { @@ -2199,14 +2119,15 @@ protected function checkResponse($buffer) } /** - * Parses a response from a Memcached daemon + * Parses a response from a Memcached daemon. * - * @param string $command The command which has triggered the buffer response from instance. - * @param string $buffer The buffer to parse. + * @param string $command the command which has triggered the buffer response from instance + * @param string $buffer the buffer to parse * * @author Benjamin Carl + * * @return bool|mixed FALSE on error, otherwise parsed response - * @access protected + * * @throws \Clickalicious\Memcached\Php\Exception */ protected function parseResponse($command, $buffer) @@ -2269,8 +2190,9 @@ protected function parseResponse($command, $buffer) * @param mixed $value The value to serialize * * @author Benjamin Carl + * * @return array ... - * @access protected + * * @throws \Clickalicious\Memcached\Php\Exception */ protected function serializeValue($value) @@ -2312,22 +2234,23 @@ protected function serializeValue($value) * Checks if a decimal flag ($flag) is set in passed ($flags). * * @author Benjamin Carl - * @param integer $flags - * @param integer $flag + * + * @param int $flags + * @param int $flag + * * @return bool TRUE if flag is set in flags, otherwise FALSE - * @access protected */ protected function isFlagSet($flags, $flag) { - return (($flags & $flag) === $flag); + return ($flags & $flag) === $flag; } /** * Sets a decimal flag ($lfag) in passed flags ($flags). * * @author Benjamin Carl + * * @return int The flags after setting new flag - * @access protected */ protected function setFlag($flags, $flag) { @@ -2336,4 +2259,3 @@ protected function setFlag($flags, $flag) return $flags; } } - diff --git a/src/Compression/CompressionInterface.php b/src/Compression/CompressionInterface.php index b4b0550..73fc7ca 100644 --- a/src/Compression/CompressionInterface.php +++ b/src/Compression/CompressionInterface.php @@ -2,7 +2,7 @@ /** * (The MIT license) - * Copyright 2017 clickalicious, Benjamin Carl + * Copyright 2017 clickalicious, Benjamin Carl. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -28,9 +28,8 @@ namespace Clickalicious\Memcached\Php\Compression; /** - * Interface CompressionInterface + * Interface CompressionInterface. * - * @package Clickalicious\Memcached\Php\Compression * @author Benjamin Carl */ interface CompressionInterface @@ -41,8 +40,8 @@ interface CompressionInterface * @param string $buffer The buffer to compress * * @author Benjamin Carl + * * @return string The compressed input - * @access protected */ public function compress($buffer); @@ -52,8 +51,8 @@ public function compress($buffer); * @param string $buffer The buffer to decompress * * @author Benjamin Carl + * * @return string The decompressed input - * @access protected */ public function decompress($buffer); } diff --git a/src/Compression/Lzw.php b/src/Compression/Lzw.php index e61c5ea..23a3daf 100644 --- a/src/Compression/Lzw.php +++ b/src/Compression/Lzw.php @@ -2,7 +2,7 @@ /** * (The MIT license) - * Copyright 2017 clickalicious, Benjamin Carl + * Copyright 2017 clickalicious, Benjamin Carl. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -30,7 +30,7 @@ use Clickalicious\Memcached\Php\Exception; /** - * Class Lzw + * Class Lzw. * * Lzw.php - LZW-compression (LZW = Lempel–Ziv–Welch). * http://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch @@ -39,7 +39,6 @@ * * Originally from: https://code.google.com/p/smt2/ * - * @package Clickalicious\Memcached\Php\Compression * @author Benjamin Carl */ class Lzw implements CompressionInterface @@ -48,8 +47,9 @@ class Lzw implements CompressionInterface * Constructor. * * @author Benjamin Carl + * * @return Lzw - * @access public + * * @throws Exception */ public function __construct() @@ -67,33 +67,32 @@ public function __construct() * @param string $buffer The buffer to compress * * @author Benjamin Carl + * * @return string The compressed input - * @access protected */ public function compress($buffer) { $dictionary = array(); - $data = str_split($buffer . ''); - $out = array(); - $phrase = $data[0]; - $code = 256; - $countData = count($data); + $data = str_split($buffer.''); + $out = array(); + $phrase = $data[0]; + $code = 256; + $countData = count($data); for ($i = 1; $i < $countData; ++$i) { $currentCharacter = $data[$i]; if (isset($dictionary[$phrase.$currentCharacter])) { $phrase .= $currentCharacter; - } else { - $out[] = strlen($phrase) > 1 ? $dictionary[$phrase] : $this->charCodeAt($phrase,0); + $out[] = strlen($phrase) > 1 ? $dictionary[$phrase] : $this->charCodeAt($phrase, 0); $dictionary[$phrase.$currentCharacter] = $code; - $code++; + ++$code; $phrase = $currentCharacter; } } - $out[] = strlen($phrase) > 1 ? $dictionary[$phrase] : $this->charCodeAt($phrase,0); + $out[] = strlen($phrase) > 1 ? $dictionary[$phrase] : $this->charCodeAt($phrase, 0); $countOut = count($out); for ($i = 0; $i < $countOut; ++$i) { @@ -109,35 +108,34 @@ public function compress($buffer) * @param string $buffer The buffer to decompress * * @author Benjamin Carl + * * @return string The decompressed input - * @access protected */ public function decompress($buffer) { - $dictionary = array(); - $data = $this->multibyteStringSplit($buffer); + $dictionary = array(); + $data = $this->multibyteStringSplit($buffer); $currentCharacter = $data[0]; - $oldPhrase = $currentCharacter; - $out = array($currentCharacter); - $code = 256; - $countData = count($data); + $oldPhrase = $currentCharacter; + $out = array($currentCharacter); + $code = 256; + $countData = count($data); for ($i = 1; $i < $countData; ++$i) { $currCode = $this->uniord($data[$i]); if ($currCode < 256) { $phrase = $data[$i]; - } else { $phrase = isset($dictionary[$currCode]) ? $dictionary[$currCode] : $oldPhrase.$currentCharacter; } - $out[] = $phrase; - $currentCharacter = $phrase[0]; - $dictionary[$code] = $oldPhrase . $currentCharacter; + $out[] = $phrase; + $currentCharacter = $phrase[0]; + $dictionary[$code] = $oldPhrase.$currentCharacter; - $code++; + ++$code; $oldPhrase = $phrase; } @@ -151,8 +149,8 @@ public function decompress($buffer) * @param string $string The input to split * * @author Benjamin Carl + * * @return array The splitted string - * @access protected */ protected function multibyteStringSplit($string) { @@ -166,8 +164,8 @@ protected function multibyteStringSplit($string) * @param int $position The position to return character from * * @author Benjamin Carl + * * @return int The position - * @access protected */ protected function charCodeAt($buffer, $position) { @@ -181,8 +179,8 @@ protected function charCodeAt($buffer, $position) * @param string $character The character * * @author Benjamin Carl + * * @return string The unicode character - * @access protected */ protected function unichr($character) { @@ -198,8 +196,8 @@ protected function unichr($character) * @param string $buffer The buffer to return character from * * @author Benjamin Carl + * * @return int The ASCII value of the character - * @access protected */ protected function uniord($buffer) { diff --git a/src/Compression/Smaz.php b/src/Compression/Smaz.php index 5f4b05a..a4d5b52 100644 --- a/src/Compression/Smaz.php +++ b/src/Compression/Smaz.php @@ -2,7 +2,7 @@ /** * (The MIT license) - * Copyright 2017 clickalicious, Benjamin Carl + * Copyright 2017 clickalicious, Benjamin Carl. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -28,7 +28,7 @@ namespace Clickalicious\Memcached\Php\Compression; /** - * Class Smaz + * Class Smaz. * * Smaz.php - SMAZ - compression for very small strings * @@ -40,7 +40,6 @@ * * Originally from: https://github.com/zhenhao/smaz.php * - * @package Clickalicious\Memcached\Php\Compression * @author Benjamin Carl */ class Smaz implements CompressionInterface @@ -49,7 +48,6 @@ class Smaz implements CompressionInterface * The encode book. * * @var array|null - * @access private * @static */ private static $encodeBook; @@ -58,31 +56,30 @@ class Smaz implements CompressionInterface * The decode book. * * @var array - * @access private * @static */ private static $decodeBook = array( - " ", "the", "e", "t", "a", "of", "o", "and", "i", "n", "s", "e ", "r", " th", - " t", "in", "he", "th", "h", "he ", "to", "\r\n", "l", "s ", "d", " a", "an", - "er", "c", " o", "d ", "on", " of", "re", "of ", "t ", ", ", "is", "u", "at", - " ", "n ", "or", "which", "f", "m", "as", "it", "that", "\n", "was", "en", - " ", " w", "es", " an", " i", "\r", "f ", "g", "p", "nd", " s", "nd ", "ed ", - "w", "ed", "http://", "for", "te", "ing", "y ", "The", " c", "ti", "r ", "his", - "st", " in", "ar", "nt", ",", " to", "y", "ng", " h", "with", "le", "al", "to ", - "b", "ou", "be", "were", " b", "se", "o ", "ent", "ha", "ng ", "their", "\"", - "hi", "from", " f", "in ", "de", "ion", "me", "v", ".", "ve", "all", "re ", - "ri", "ro", "is ", "co", "f t", "are", "ea", ". ", "her", " m", "er ", " p", - "es ", "by", "they", "di", "ra", "ic", "not", "s, ", "d t", "at ", "ce", "la", - "h ", "ne", "as ", "tio", "on ", "n t", "io", "we", " a ", "om", ", a", "s o", - "ur", "li", "ll", "ch", "had", "this", "e t", "g ", "e\r\n", " wh", "ere", - " co", "e o", "a ", "us", " d", "ss", "\n\r\n", "\r\n\r", "=\"", " be", " e", - "s a", "ma", "one", "t t", "or ", "but", "el", "so", "l ", "e s", "s,", "no", - "ter", " wa", "iv", "ho", "e a", " r", "hat", "s t", "ns", "ch ", "wh", "tr", - "ut", "/", "have", "ly ", "ta", " ha", " on", "tha", "-", " l", "ati", "en ", - "pe", " re", "there", "ass", "si", " fo", "wa", "ec", "our", "who", "its", "z", - "fo", "rs", ">", "ot", "un", "<", "im", "th ", "nc", "ate", "><", "ver", "ad", - " we", "ly", "ee", " n", "id", " cl", "ac", "il", "', 'ot', 'un', '<', 'im', 'th ', 'nc', 'ate', '><', 'ver', 'ad', + ' we', 'ly', 'ee', ' n', 'id', ' cl', 'ac', 'il', ' + * * @return string The compressed input - * @access protected */ public function compress($buffer) { - $inLen = strlen($buffer); - $inIdx = 0; + $inLen = strlen($buffer); + $inIdx = 0; $encodeBook = $this->getEncodeBook(); - $output = ''; - $verbatim = ''; + $output = ''; + $verbatim = ''; while ($inIdx < $inLen) { $encode = false; @@ -109,32 +106,32 @@ public function compress($buffer) $code = isset($encodeBook[substr($buffer, $inIdx, $j)]) ? $encodeBook[substr($buffer, $inIdx, $j)] : null; - if($code !== null) { - if(strlen($verbatim)) { + if ($code !== null) { + if (strlen($verbatim)) { $output .= $this->flushVerbatim($verbatim); $verbatim = ''; } $output .= chr($code); - $inIdx += $j; - $encode = true; + $inIdx += $j; + $encode = true; break; } } - if(!$encode) { + if (!$encode) { $verbatim .= $buffer[$inIdx]; - $inIdx++; + ++$inIdx; - if(strlen($verbatim) == 255) { + if (strlen($verbatim) == 255) { $output .= $this->flushVerbatim($verbatim); $verbatim = ''; } } } - if(strlen($verbatim)) { + if (strlen($verbatim)) { $output .= $this->flushVerbatim($verbatim); } @@ -147,30 +144,28 @@ public function compress($buffer) * @param string $buffer The buffer to decompress * * @author Benjamin Carl + * * @return string The decompressed input - * @access protected */ public function decompress($buffer) { $decodeBook = $this->getDecodeBook(); - $output = ''; - $i = 0; + $output = ''; + $i = 0; while ($i < strlen($buffer)) { $code = ord($buffer[$i]); if ($code == 254) { $output .= $buffer[$i + 1]; - $i += 2; - - } else if($code == 255) { - $len = ord($buffer[$i + 1]); + $i += 2; + } elseif ($code == 255) { + $len = ord($buffer[$i + 1]); $output .= substr($buffer, $i + 2, $len); - $i += 2 + $len; - + $i += 2 + $len; } else { $output .= $decodeBook[$code]; - $i++; + ++$i; } } @@ -181,6 +176,7 @@ public function decompress($buffer) * Flushes ... * * @param $verbatim + * * @return string */ protected function flushVerbatim($verbatim) @@ -194,7 +190,6 @@ protected function flushVerbatim($verbatim) if (strlen($verbatim) > 1) { $output .= chr(255); $output .= chr(strlen($verbatim)); - } else { $output .= chr(254); } @@ -208,8 +203,8 @@ protected function flushVerbatim($verbatim) * Returns the book used to encode. * * @author Benjamin Carl + * * @return array The encode book - * @access protected */ protected function getEncodeBook() { @@ -224,8 +219,8 @@ protected function getEncodeBook() * Returns the book used to decode. * * @author Benjamin Carl + * * @return array The decode book - * @access protected */ protected function getDecodeBook() { diff --git a/src/Compression/Zlib.php b/src/Compression/Zlib.php index b6e86ae..91dac53 100644 --- a/src/Compression/Zlib.php +++ b/src/Compression/Zlib.php @@ -2,7 +2,7 @@ /** * (The MIT license) - * Copyright 2017 clickalicious, Benjamin Carl + * Copyright 2017 clickalicious, Benjamin Carl. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -28,13 +28,12 @@ namespace Clickalicious\Memcached\Php\Compression; /** - * Class Zlib + * Class Zlib. * * Zlib.php - ZLIB-compression. * * ZLIB-compression interface to builtin PHP functionality. * - * @package Clickalicious\Memcached\Php\Compression * @author Benjamin Carl */ class Zlib implements CompressionInterface @@ -45,8 +44,8 @@ class Zlib implements CompressionInterface * @param string $buffer The buffer to compress * * @author Benjamin Carl + * * @return string The compressed input - * @access protected */ public function compress($buffer) { @@ -59,8 +58,8 @@ public function compress($buffer) * @param string $buffer The buffer to decompress * * @author Benjamin Carl + * * @return string The decompressed input - * @access protected */ public function decompress($buffer) { diff --git a/src/Exception.php b/src/Exception.php index 6969b01..79d7145 100644 --- a/src/Exception.php +++ b/src/Exception.php @@ -2,7 +2,7 @@ /** * (The MIT license) - * Copyright 2017 clickalicious, Benjamin Carl + * Copyright 2017 clickalicious, Benjamin Carl. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -28,9 +28,8 @@ namespace Clickalicious\Memcached\Php; /** - * Class Exception + * Class Exception. * - * @package Clickalicious\Memcached\Php * @author Benjamin Carl */ class Exception extends \Exception diff --git a/src/Util.php b/src/Util.php index 8d3a00c..ff04f9d 100644 --- a/src/Util.php +++ b/src/Util.php @@ -2,7 +2,7 @@ /** * (The MIT license) - * Copyright 2017 clickalicious, Benjamin Carl + * Copyright 2017 clickalicious, Benjamin Carl. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -31,7 +31,7 @@ | General Tools & Helper +---------------------------------------------------------------------------------------------------------------------*/ -/** +/* * Copyright (c) 2013 Ben Ramsey * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -58,24 +58,24 @@ * @codeCoverageIgnore */ if (false === function_exists('array_column')) { - /** * Returns the values from a single column of the input array, identified by the $columnKey. * * Optionally, you may provide an $indexKey to index the values in the returned array by the values from the * $indexKey column in the input array. * - * @param array $input A multi-dimensional array (record set) from which to pull a column of values. + * @param array $input a multi-dimensional array (record set) from which to pull a column of values * @param mixed $column_key The column of values to return. This value may be the integer key of the column you * wish to retrieve, or it may be the string key name for an associative array. * @param mixed $index_key (Optional.) The column to use as the index/keys for the returned array. This value * may be the integer key of the column, or it may be the string key name. + * * @return array * @codeCoverageIgnore */ function array_column(array $input, $column_key, $index_key = null) { - /** + /* * Proxy call to array_column_emulation(): makes it a bit cleaner and testing on all systems possible */ return array_column_emulation($input, $column_key, $index_key); @@ -88,11 +88,12 @@ function array_column(array $input, $column_key, $index_key = null) * Optionally, you may provide an $indexKey to index the values in the returned array by the values from the * $indexKey column in the input array. * - * @param array $input A multi-dimensional array (record set) from which to pull a column of values. + * @param array $input a multi-dimensional array (record set) from which to pull a column of values * @param mixed $column_key The column of values to return. This value may be the integer key of the column you * wish to retrieve, or it may be the string key name for an associative array. * @param mixed $index_key (Optional.) The column to use as the index/keys for the returned array. This value * may be the integer key of the column, or it may be the string key name. + * * @return array */ function array_column_emulation(array $input, $column_key, $index_key = null) @@ -130,9 +131,9 @@ function array_column_emulation(array $input, $column_key, $index_key = null) // Check for passed index key if (null !== $index_key) { if (is_float($index_key) || is_int($index_key)) { - $index_key = (int)$index_key; + $index_key = (int) $index_key; } else { - $index_key = (string)$index_key; + $index_key = (string) $index_key; } } @@ -144,7 +145,7 @@ function array_column_emulation(array $input, $column_key, $index_key = null) if (null !== $index_key && true === is_array($row) && true === array_key_exists($index_key, $row)) { $keySet = true; - $key = (string)$row[$index_key]; + $key = (string) $row[$index_key]; } if (true === is_array($row) && true === array_key_exists($column_key, $row)) { @@ -163,7 +164,7 @@ function array_column_emulation(array $input, $column_key, $index_key = null) return $result; } -/** +/* * Alternative implementation of boolval() for PHP releases < 5.5. * * @param mixed $boolean The potential boolean @@ -174,20 +175,19 @@ function array_column_emulation(array $input, $column_key, $index_key = null) * @codeCoverageIgnore */ if (false === function_exists('boolval')) { - /** * Alternative implementation of boolval() for PHP releases < 5.5. * * @param mixed $boolean The potential boolean * * @author Benjamin Carl - * @return bool TRUE || FALSE depending on input. - * @access public + * + * @return bool tRUE || FALSE depending on input * @codeCoverageIgnore */ function boolval($boolean) { - /** + /* * Proxy call to boolval_emulation() */ return boolval_emulation($boolean); @@ -200,8 +200,8 @@ function boolval($boolean) * @param mixed $boolean The potential boolean * * @author Benjamin Carl - * @return bool TRUE || FALSE depending on input. - * @access public + * + * @return bool tRUE || FALSE depending on input */ function boolval_emulation($boolean) { From 4df702632819842c64571c8683f78ff9fdb93ebe Mon Sep 17 00:00:00 2001 From: Benjamin Carl Date: Tue, 9 May 2017 08:05:40 +0200 Subject: [PATCH 09/12] Removed unfinished Cache implementation. --- src/Cache.php | 120 -------------------------------------------------- 1 file changed, 120 deletions(-) delete mode 100644 src/Cache.php diff --git a/src/Cache.php b/src/Cache.php deleted file mode 100644 index 4637c8c..0000000 --- a/src/Cache.php +++ /dev/null @@ -1,120 +0,0 @@ - - */ -class Cache extends Client implements CacheItemInterface -{ - /** - * Returns the key for the current cache item. - * - * The key is loaded by the Implementing Library, but should be available to - * the higher level callers when needed. - * - * @return string - * The key string for this cache item - */ - public function getKey() - { - } - - /** - * Confirms if the cache item lookup resulted in a cache hit. - * - * Note: This method MUST NOT have a race condition between calling isHit() - * and calling get(). - * - * @return bool - * True if the request resulted in a cache hit. False otherwise. - */ - public function isHit() - { - } - - /** - * Confirms if the cache item exists in the cache. - * - * Note: This method MAY avoid retrieving the cached value for performance - * reasons, which could result in a race condition between exists() and get(). - * To avoid that potential race condition use isHit() instead. - * - * @return bool - * True if item exists in the cache, false otherwise - */ - public function exists() - { - } - - /** - * Sets the expiration for this cache item. - * - * @param int|\DateTime $ttl - * - If an integer is passed, it is interpreted as the number of seconds - * after which the item MUST be considered expired. - * - If a DateTime object is passed, it is interpreted as the point in - * time after which the item MUST be considered expired. - * - If null is passed, a default value MAY be used. If none is set, - * the value should be stored permanently or for as long as the - * implementation allows. - * - * @return static - * The called object - */ - public function setExpiration($ttl = null) - { - } - - /** - * Returns the expiration time of a not-yet-expired cache item. - * - * If this cache item is a Cache Miss, this method MAY return the time at - * which the item expired or the current time if that is not available. - * - * @return \DateTime - * The timestamp at which this cache item will expire - */ - public function getExpiration() - { - } - - public function expiresAfter($time) - { - // TODO: Implement expiresAfter() method. - } - - public function expiresAt($expiration) - { - // TODO: Implement expiresAt() method. - } -} From f0357bf07fb117cb4a96ea48a05d85c9bce28117 Mon Sep 17 00:00:00 2001 From: Benjamin Carl Date: Tue, 9 May 2017 08:19:53 +0200 Subject: [PATCH 10/12] Changes recommended by Codacy. --- src/Client.php | 2 +- src/Compression/Smaz.php | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Client.php b/src/Client.php index a26a48f..206bacb 100644 --- a/src/Client.php +++ b/src/Client.php @@ -2019,7 +2019,7 @@ protected function parseStatsResponse(array $lines) } $result[$nodes[0]][$nodes[1]][$nodes[2]] = $metaData[2]; } else { - $identifier = array_shift($metaData); + array_shift($metaData); $key = array_shift($metaData); $value = implode(self::COMMAND_SEPARATOR, $metaData); $result[$key] = $value; diff --git a/src/Compression/Smaz.php b/src/Compression/Smaz.php index a4d5b52..64808f1 100644 --- a/src/Compression/Smaz.php +++ b/src/Compression/Smaz.php @@ -151,21 +151,21 @@ public function decompress($buffer) { $decodeBook = $this->getDecodeBook(); $output = ''; - $i = 0; - - while ($i < strlen($buffer)) { - $code = ord($buffer[$i]); - - if ($code == 254) { - $output .= $buffer[$i + 1]; - $i += 2; - } elseif ($code == 255) { - $len = ord($buffer[$i + 1]); - $output .= substr($buffer, $i + 2, $len); - $i += 2 + $len; + $count = 0; + + while ($count < strlen($buffer)) { + $code = ord($buffer[$count]); + + if (254 === $code) { + $output .= $buffer[$count + 1]; + $count += 2; + } elseif (255 === $code) { + $len = ord($buffer[$count + 1]); + $output .= substr($buffer, $count + 2, $len); + $count += 2 + $len; } else { $output .= $decodeBook[$code]; - ++$i; + ++$count; } } From f7371cc3573a9f226791d183940ef26493895513 Mon Sep 17 00:00:00 2001 From: Benjamin Carl Date: Thu, 11 May 2017 09:40:04 +0200 Subject: [PATCH 11/12] Added cat for output of xml on request of Codacy. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 921772f..01b8b7d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,6 +35,7 @@ script: after_script: - if [ $(phpenv version-name) == "5.6" ] && [ "$PREFER_LOWEST" == "--prefer-lowest" ]; then bin/codacycoverage clover build/logs/clover.xml > /dev/null 2>&1; fi + - if [ $(phpenv version-name) == "5.6" ] && [ "$PREFER_LOWEST" == "--prefer-lowest" ]; then cat build/logs/clover.xml fi after_success: # Push coverage to github pages branch From 246b5c8ef02a427fcdc5a5b9cdfebd69e572d8af Mon Sep 17 00:00:00 2001 From: Benjamin Carl Date: Fri, 12 May 2017 07:13:49 +0200 Subject: [PATCH 12/12] Fix for travis build. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 01b8b7d..83d83c3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,7 +35,7 @@ script: after_script: - if [ $(phpenv version-name) == "5.6" ] && [ "$PREFER_LOWEST" == "--prefer-lowest" ]; then bin/codacycoverage clover build/logs/clover.xml > /dev/null 2>&1; fi - - if [ $(phpenv version-name) == "5.6" ] && [ "$PREFER_LOWEST" == "--prefer-lowest" ]; then cat build/logs/clover.xml fi + - if [ $(phpenv version-name) == "5.6" ] && [ "$PREFER_LOWEST" == "--prefer-lowest" ]; then cat build/logs/clover.xml; fi after_success: # Push coverage to github pages branch