From 707a978dcb63df257170e884434e04830db4e00f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Mon, 31 Aug 2015 22:20:24 +0200 Subject: [PATCH 01/24] take changes from crossjoin/browscap --- .gitignore | 1 + composer.lock | 1684 --------------------------- src/Data/PropertyHolder.php | 1 + src/Helper/Converter.php | 67 +- src/Helper/Quoter.php | 34 + src/Parser/Helper/GetPattern.php | 2 +- src/Parser/Helper/Pattern.php | 33 +- src/Parser/Helper/SubKey.php | 78 ++ src/Parser/Ini.php | 78 +- src/Parser/IniParser.php | 4 +- tests/Parser/Helper/PatternTest.php | 18 - tests/Parser/Helper/SubKeyTest.php | 58 + 12 files changed, 265 insertions(+), 1793 deletions(-) delete mode 100644 composer.lock create mode 100644 src/Parser/Helper/SubKey.php create mode 100644 tests/Parser/Helper/SubKeyTest.php diff --git a/.gitignore b/.gitignore index cc65465a..5395aaff 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ .idea/ vendor/ phpunit.xml +composer.lock diff --git a/composer.lock b/composer.lock deleted file mode 100644 index 68afceb6..00000000 --- a/composer.lock +++ /dev/null @@ -1,1684 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", - "This file is @generated automatically" - ], - "hash": "aa2da694f8e5dfbdd0e24244ea68afe3", - "packages": [ - { - "name": "mimmi20/file-loader", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/mimmi20/FileLoader.git", - "reference": "a31d76ba841e71073637972f2a4f831c828e4ba9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mimmi20/FileLoader/zipball/a31d76ba841e71073637972f2a4f831c828e4ba9", - "reference": "a31d76ba841e71073637972f2a4f831c828e4ba9", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "require-dev": { - "mikey179/vfsstream": "1.3.*@dev", - "monolog/monolog": "1.*@dev", - "phpunit/phpunit": "*" - }, - "suggest": { - "ext-curl": "to use curl requests to get the file" - }, - "type": "library", - "autoload": { - "psr-4": { - "FileLoader\\": "src/", - "FileLoaderTest\\": "tests/FileLoaderTest/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Thomas Müller", - "homepage": "https://github.com/mimmi20", - "role": "Developer" - }, - { - "name": "Contributors", - "homepage": "https://github.com/mimmi20/FileLoader/graphs/contributors" - } - ], - "description": "a class to load remote or local files", - "homepage": "http://github.com/mimmi20/FileLoader", - "time": "2015-06-05 20:07:45" - }, - { - "name": "mimmi20/wurflcache", - "version": "1.3.1", - "source": { - "type": "git", - "url": "https://github.com/mimmi20/WurflCache.git", - "reference": "f163c666a3965a59b7ae8e47302ef64dc8d3f3ea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mimmi20/WurflCache/zipball/f163c666a3965a59b7ae8e47302ef64dc8d3f3ea", - "reference": "f163c666a3965a59b7ae8e47302ef64dc8d3f3ea", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "symfony/filesystem": "*" - }, - "require-dev": { - "desarrolla2/cache": "*", - "doctrine/cache": "1.4.*@dev", - "mikey179/vfsstream": "1.3.*@dev", - "phpunit/phpunit": "*", - "zendframework/zend-cache": "2.3.*", - "zetacomponents/cache": "dev-master" - }, - "suggest": { - "desarrolla2/cache": "to use other caches handled by desarrolla", - "doctrine/cache": "to use other caches handled by doctrine", - "zendframework/zend-cache": "to use other caches handled by zend", - "zetacomponents/cache": "to use other caches handled by zeta" - }, - "type": "library", - "autoload": { - "psr-4": { - "WurflCache\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Thomas Müller", - "homepage": "https://github.com/mimmi20", - "role": "Developer" - }, - { - "name": "Contributors", - "homepage": "https://github.com/mimmi20/WurflCache/graphs/contributors" - } - ], - "description": "the Cache Classes for the Wurfl PHP Library for PHP 5.3", - "homepage": "https://github.com/mimmi20/WurflCache", - "keywords": [ - "Wurfl", - "cache" - ], - "time": "2015-06-05 21:53:23" - }, - { - "name": "monolog/monolog", - "version": "1.14.0", - "source": { - "type": "git", - "url": "https://github.com/Seldaek/monolog.git", - "reference": "b287fbbe1ca27847064beff2bad7fb6920bf08cc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/b287fbbe1ca27847064beff2bad7fb6920bf08cc", - "reference": "b287fbbe1ca27847064beff2bad7fb6920bf08cc", - "shasum": "" - }, - "require": { - "php": ">=5.3.0", - "psr/log": "~1.0" - }, - "provide": { - "psr/log-implementation": "1.0.0" - }, - "require-dev": { - "aws/aws-sdk-php": "^2.4.9", - "doctrine/couchdb": "~1.0@dev", - "graylog2/gelf-php": "~1.0", - "php-console/php-console": "^3.1.3", - "phpunit/phpunit": "~4.5", - "phpunit/phpunit-mock-objects": "2.3.0", - "raven/raven": "~0.8", - "ruflin/elastica": ">=0.90 <3.0", - "swiftmailer/swiftmailer": "~5.3", - "videlalvaro/php-amqplib": "~2.4" - }, - "suggest": { - "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", - "doctrine/couchdb": "Allow sending log messages to a CouchDB server", - "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", - "ext-mongo": "Allow sending log messages to a MongoDB server", - "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", - "php-console/php-console": "Allow sending log messages to Google Chrome", - "raven/raven": "Allow sending log messages to a Sentry server", - "rollbar/rollbar": "Allow sending log messages to Rollbar", - "ruflin/elastica": "Allow sending log messages to an Elastic Search server", - "videlalvaro/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.14.x-dev" - } - }, - "autoload": { - "psr-4": { - "Monolog\\": "src/Monolog" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "Sends your logs to files, sockets, inboxes, databases and various web services", - "homepage": "http://github.com/Seldaek/monolog", - "keywords": [ - "log", - "logging", - "psr-3" - ], - "time": "2015-06-19 13:29:54" - }, - { - "name": "psr/log", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", - "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", - "shasum": "" - }, - "type": "library", - "autoload": { - "psr-0": { - "Psr\\Log\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "time": "2012-12-21 11:40:51" - }, - { - "name": "symfony/console", - "version": "v2.7.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/Console.git", - "reference": "564398bc1f33faf92fc2ec86859983d30eb81806" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Console/zipball/564398bc1f33faf92fc2ec86859983d30eb81806", - "reference": "564398bc1f33faf92fc2ec86859983d30eb81806", - "shasum": "" - }, - "require": { - "php": ">=5.3.9" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/event-dispatcher": "~2.1", - "symfony/phpunit-bridge": "~2.7", - "symfony/process": "~2.1" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/process": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Console Component", - "homepage": "https://symfony.com", - "time": "2015-06-10 15:30:22" - }, - { - "name": "symfony/filesystem", - "version": "v2.7.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/Filesystem.git", - "reference": "a0d43eb3e17d4f4c6990289805a488a0482a07f3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Filesystem/zipball/a0d43eb3e17d4f4c6990289805a488a0482a07f3", - "reference": "a0d43eb3e17d4f4c6990289805a488a0482a07f3", - "shasum": "" - }, - "require": { - "php": ">=5.3.9" - }, - "require-dev": { - "symfony/phpunit-bridge": "~2.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Filesystem Component", - "homepage": "https://symfony.com", - "time": "2015-06-08 09:37:21" - }, - { - "name": "symfony/finder", - "version": "v2.7.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/Finder.git", - "reference": "c13a40d638aeede1e8400f8c956c7f9246c05f75" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Finder/zipball/c13a40d638aeede1e8400f8c956c7f9246c05f75", - "reference": "c13a40d638aeede1e8400f8c956c7f9246c05f75", - "shasum": "" - }, - "require": { - "php": ">=5.3.9" - }, - "require-dev": { - "symfony/phpunit-bridge": "~2.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Finder\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Finder Component", - "homepage": "https://symfony.com", - "time": "2015-06-04 20:11:48" - } - ], - "packages-dev": [ - { - "name": "doctrine/instantiator", - "version": "1.0.5", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", - "shasum": "" - }, - "require": { - "php": ">=5.3,<8.0-DEV" - }, - "require-dev": { - "athletic/athletic": "~0.1.8", - "ext-pdo": "*", - "ext-phar": "*", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://github.com/doctrine/instantiator", - "keywords": [ - "constructor", - "instantiate" - ], - "time": "2015-06-14 21:17:01" - }, - { - "name": "fabpot/php-cs-fixer", - "version": "v1.9", - "source": { - "type": "git", - "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", - "reference": "ef528b9d3f1dd66197baabf8f77c8402c62bb9fc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/ef528b9d3f1dd66197baabf8f77c8402c62bb9fc", - "reference": "ef528b9d3f1dd66197baabf8f77c8402c62bb9fc", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=5.3.6", - "sebastian/diff": "~1.1", - "symfony/console": "~2.3", - "symfony/event-dispatcher": "~2.1", - "symfony/filesystem": "~2.1", - "symfony/finder": "~2.1", - "symfony/process": "~2.3", - "symfony/stopwatch": "~2.5" - }, - "require-dev": { - "satooshi/php-coveralls": "0.7.*@dev" - }, - "bin": [ - "php-cs-fixer" - ], - "type": "application", - "autoload": { - "psr-4": { - "Symfony\\CS\\": "Symfony/CS/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Dariusz Rumiński", - "email": "dariusz.ruminski@gmail.com" - }, - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - } - ], - "description": "A tool to automatically fix PHP code style", - "time": "2015-06-13 09:30:19" - }, - { - "name": "mikey179/vfsStream", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/mikey179/vfsStream.git", - "reference": "3a0e11a1df90d32b553bf12c012f836729a5abff" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mikey179/vfsStream/zipball/3a0e11a1df90d32b553bf12c012f836729a5abff", - "reference": "3a0e11a1df90d32b553bf12c012f836729a5abff", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.5.x-dev" - } - }, - "autoload": { - "psr-0": { - "org\\bovigo\\vfs\\": "src/main/php" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Frank Kleine", - "homepage": "http://frankkleine.de/", - "role": "Developer" - } - ], - "description": "Virtual file system to mock the real file system in unit tests.", - "homepage": "http://vfs.bovigo.org/", - "time": "2015-04-13 17:47:24" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8", - "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "suggest": { - "dflydev/markdown": "~1.0", - "erusev/parsedown": "~1.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-0": { - "phpDocumentor": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "mike.vanriel@naenius.com" - } - ], - "time": "2015-02-03 12:10:50" - }, - { - "name": "phpspec/prophecy", - "version": "v1.4.1", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373", - "reference": "3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.2", - "phpdocumentor/reflection-docblock": "~2.0", - "sebastian/comparator": "~1.1" - }, - "require-dev": { - "phpspec/phpspec": "~2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4.x-dev" - } - }, - "autoload": { - "psr-0": { - "Prophecy\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "time": "2015-04-27 22:15:08" - }, - { - "name": "phpunit/php-code-coverage", - "version": "2.1.6", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "631e365cf26bb2c078683e8d9bcf8bc631ac4d44" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/631e365cf26bb2c078683e8d9bcf8bc631ac4d44", - "reference": "631e365cf26bb2c078683e8d9bcf8bc631ac4d44", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "phpunit/php-file-iterator": "~1.3", - "phpunit/php-text-template": "~1.2", - "phpunit/php-token-stream": "~1.3", - "sebastian/environment": "~1.0", - "sebastian/version": "~1.0" - }, - "require-dev": { - "ext-xdebug": ">=2.1.4", - "phpunit/phpunit": "~4" - }, - "suggest": { - "ext-dom": "*", - "ext-xdebug": ">=2.2.1", - "ext-xmlwriter": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.1.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "time": "2015-06-19 07:11:55" - }, - { - "name": "phpunit/php-file-iterator", - "version": "1.4.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "a923bb15680d0089e2316f7a4af8f437046e96bb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a923bb15680d0089e2316f7a4af8f437046e96bb", - "reference": "a923bb15680d0089e2316f7a4af8f437046e96bb", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "time": "2015-04-02 05:19:05" - }, - { - "name": "phpunit/php-text-template", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "time": "2015-06-21 13:50:34" - }, - { - "name": "phpunit/php-timer", - "version": "1.0.6", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "83fe1bdc5d47658b727595c14da140da92b3d66d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/83fe1bdc5d47658b727595c14da140da92b3d66d", - "reference": "83fe1bdc5d47658b727595c14da140da92b3d66d", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "time": "2015-06-13 07:35:30" - }, - { - "name": "phpunit/php-token-stream", - "version": "1.4.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "7a9b0969488c3c54fd62b4d504b3ec758fd005d9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/7a9b0969488c3c54fd62b4d504b3ec758fd005d9", - "reference": "7a9b0969488c3c54fd62b4d504b3ec758fd005d9", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", - "keywords": [ - "tokenizer" - ], - "time": "2015-06-19 03:43:16" - }, - { - "name": "phpunit/phpunit", - "version": "4.7.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "f6701ef3faea759acd1910a7751d8d102a7fd5bc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f6701ef3faea759acd1910a7751d8d102a7fd5bc", - "reference": "f6701ef3faea759acd1910a7751d8d102a7fd5bc", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-spl": "*", - "php": ">=5.3.3", - "phpspec/prophecy": "~1.3,>=1.3.1", - "phpunit/php-code-coverage": "~2.1", - "phpunit/php-file-iterator": "~1.4", - "phpunit/php-text-template": "~1.2", - "phpunit/php-timer": ">=1.0.6", - "phpunit/phpunit-mock-objects": "~2.3", - "sebastian/comparator": "~1.1", - "sebastian/diff": "~1.2", - "sebastian/environment": "~1.2", - "sebastian/exporter": "~1.2", - "sebastian/global-state": "~1.0", - "sebastian/version": "~1.0", - "symfony/yaml": "~2.1|~3.0" - }, - "suggest": { - "phpunit/php-invoker": "~1.1" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.7.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "time": "2015-06-21 07:23:36" - }, - { - "name": "phpunit/phpunit-mock-objects", - "version": "2.3.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "92408bb1968a81b3217a6fdf6c1a198da83caa35" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/92408bb1968a81b3217a6fdf6c1a198da83caa35", - "reference": "92408bb1968a81b3217a6fdf6c1a198da83caa35", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "~1.0,>=1.0.2", - "php": ">=5.3.3", - "phpunit/php-text-template": "~1.2" - }, - "require-dev": { - "phpunit/phpunit": "~4.4" - }, - "suggest": { - "ext-soap": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.3.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" - ], - "time": "2015-06-11 15:55:48" - }, - { - "name": "sebastian/comparator", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "1dd8869519a225f7f2b9eb663e225298fade819e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1dd8869519a225f7f2b9eb663e225298fade819e", - "reference": "1dd8869519a225f7f2b9eb663e225298fade819e", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "sebastian/diff": "~1.2", - "sebastian/exporter": "~1.2" - }, - "require-dev": { - "phpunit/phpunit": "~4.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "http://www.github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "time": "2015-01-29 16:28:08" - }, - { - "name": "sebastian/diff", - "version": "1.3.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "863df9687835c62aa423a22412d26fa2ebde3fd3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/863df9687835c62aa423a22412d26fa2ebde3fd3", - "reference": "863df9687835c62aa423a22412d26fa2ebde3fd3", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Diff implementation", - "homepage": "http://www.github.com/sebastianbergmann/diff", - "keywords": [ - "diff" - ], - "time": "2015-02-22 15:13:53" - }, - { - "name": "sebastian/environment", - "version": "1.2.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "5a8c7d31914337b69923db26c4221b81ff5a196e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5a8c7d31914337b69923db26c4221b81ff5a196e", - "reference": "5a8c7d31914337b69923db26c4221b81ff5a196e", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "time": "2015-01-01 10:01:08" - }, - { - "name": "sebastian/exporter", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "84839970d05254c73cde183a721c7af13aede943" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/84839970d05254c73cde183a721c7af13aede943", - "reference": "84839970d05254c73cde183a721c7af13aede943", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "sebastian/recursion-context": "~1.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "time": "2015-01-27 07:23:06" - }, - { - "name": "sebastian/global-state", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/c7428acdb62ece0a45e6306f1ae85e1c05b09c01", - "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.2" - }, - "suggest": { - "ext-uopz": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "time": "2014-10-06 09:23:50" - }, - { - "name": "sebastian/recursion-context", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "3989662bbb30a29d20d9faa04a846af79b276252" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/3989662bbb30a29d20d9faa04a846af79b276252", - "reference": "3989662bbb30a29d20d9faa04a846af79b276252", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2015-01-24 09:48:32" - }, - { - "name": "sebastian/version", - "version": "1.0.6", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", - "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", - "shasum": "" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "time": "2015-06-21 13:59:46" - }, - { - "name": "squizlabs/php_codesniffer", - "version": "2.3.3", - "source": { - "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "c1a26c729508f73560c1a4f767f60b8ab6b4a666" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/c1a26c729508f73560c1a4f767f60b8ab6b4a666", - "reference": "c1a26c729508f73560c1a4f767f60b8ab6b4a666", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.1.2" - }, - "bin": [ - "scripts/phpcs", - "scripts/phpcbf" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "CodeSniffer.php", - "CodeSniffer/CLI.php", - "CodeSniffer/Exception.php", - "CodeSniffer/File.php", - "CodeSniffer/Fixer.php", - "CodeSniffer/Report.php", - "CodeSniffer/Reporting.php", - "CodeSniffer/Sniff.php", - "CodeSniffer/Tokens.php", - "CodeSniffer/Reports/", - "CodeSniffer/Tokenizers/", - "CodeSniffer/DocGenerators/", - "CodeSniffer/Standards/AbstractPatternSniff.php", - "CodeSniffer/Standards/AbstractScopeSniff.php", - "CodeSniffer/Standards/AbstractVariableSniff.php", - "CodeSniffer/Standards/IncorrectPatternException.php", - "CodeSniffer/Standards/Generic/Sniffs/", - "CodeSniffer/Standards/MySource/Sniffs/", - "CodeSniffer/Standards/PEAR/Sniffs/", - "CodeSniffer/Standards/PSR1/Sniffs/", - "CodeSniffer/Standards/PSR2/Sniffs/", - "CodeSniffer/Standards/Squiz/Sniffs/", - "CodeSniffer/Standards/Zend/Sniffs/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Greg Sherwood", - "role": "lead" - } - ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "http://www.squizlabs.com/php-codesniffer", - "keywords": [ - "phpcs", - "standards" - ], - "time": "2015-06-24 03:16:23" - }, - { - "name": "symfony/event-dispatcher", - "version": "v2.7.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/EventDispatcher.git", - "reference": "be3c5ff8d503c46768aeb78ce6333051aa6f26d9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/be3c5ff8d503c46768aeb78ce6333051aa6f26d9", - "reference": "be3c5ff8d503c46768aeb78ce6333051aa6f26d9", - "shasum": "" - }, - "require": { - "php": ">=5.3.9" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~2.0,>=2.0.5", - "symfony/dependency-injection": "~2.6", - "symfony/expression-language": "~2.6", - "symfony/phpunit-bridge": "~2.7", - "symfony/stopwatch": "~2.3" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony EventDispatcher Component", - "homepage": "https://symfony.com", - "time": "2015-06-08 09:37:21" - }, - { - "name": "symfony/process", - "version": "v2.7.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/Process.git", - "reference": "552d8efdc80980cbcca50b28d626ac8e36e3cdd1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Process/zipball/552d8efdc80980cbcca50b28d626ac8e36e3cdd1", - "reference": "552d8efdc80980cbcca50b28d626ac8e36e3cdd1", - "shasum": "" - }, - "require": { - "php": ">=5.3.9" - }, - "require-dev": { - "symfony/phpunit-bridge": "~2.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Process\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Process Component", - "homepage": "https://symfony.com", - "time": "2015-06-08 09:37:21" - }, - { - "name": "symfony/stopwatch", - "version": "v2.7.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/Stopwatch.git", - "reference": "c653f1985f6c2b7dbffd04d48b9c0a96aaef814b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Stopwatch/zipball/c653f1985f6c2b7dbffd04d48b9c0a96aaef814b", - "reference": "c653f1985f6c2b7dbffd04d48b9c0a96aaef814b", - "shasum": "" - }, - "require": { - "php": ">=5.3.9" - }, - "require-dev": { - "symfony/phpunit-bridge": "~2.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Stopwatch\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Stopwatch Component", - "homepage": "https://symfony.com", - "time": "2015-06-04 20:11:48" - }, - { - "name": "symfony/yaml", - "version": "v2.7.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/Yaml.git", - "reference": "9808e75c609a14f6db02f70fccf4ca4aab53c160" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Yaml/zipball/9808e75c609a14f6db02f70fccf4ca4aab53c160", - "reference": "9808e75c609a14f6db02f70fccf4ca4aab53c160", - "shasum": "" - }, - "require": { - "php": ">=5.3.9" - }, - "require-dev": { - "symfony/phpunit-bridge": "~2.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Yaml Component", - "homepage": "https://symfony.com", - "time": "2015-06-10 15:30:22" - } - ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": { - "mikey179/vfsstream": 20 - }, - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": ">=5.5.0" - }, - "platform-dev": [] -} diff --git a/src/Data/PropertyHolder.php b/src/Data/PropertyHolder.php index b4f55e55..67637440 100644 --- a/src/Data/PropertyHolder.php +++ b/src/Data/PropertyHolder.php @@ -72,6 +72,7 @@ public function getPropertyType($propertyName) 'RenderingEngine_Maker', 'Parent', 'PropertyName', + 'CDF', ); if (in_array($propertyName, $stringProperties)) { diff --git a/src/Helper/Converter.php b/src/Helper/Converter.php index c9f9f3e3..5289c58d 100644 --- a/src/Helper/Converter.php +++ b/src/Helper/Converter.php @@ -34,6 +34,7 @@ use BrowscapPHP\Data\PropertyHolder; use BrowscapPHP\Exception\FileNotFoundException; use BrowscapPHP\Parser\Helper\Pattern; +use BrowscapPHP\Parser\Helper\SubKey; use BrowscapPHP\Parser\Ini; use Psr\Log\LoggerInterface; @@ -217,9 +218,10 @@ public function convertString($iniString) */ public function getIniVersion($iniString) { - $key = $this->pregQuote(Ini::BROWSCAP_VERSION_KEY); + $quoterHelper = new Quoter(); + $key = $quoterHelper->pregQuote(Ini::BROWSCAP_VERSION_KEY); - if (preg_match("/\.*\[".$key."\][^\[]*Version=(\d+)\D.*/", $iniString, $matches)) { + if (preg_match('/\.*\[' . $key . '\][^\[]*Version=(\d+)\D.*/', $iniString, $matches)) { if (isset($matches[1])) { $this->iniVersion = (int) $matches[1]; } @@ -254,21 +256,6 @@ public function storeVersion() return $this; } - /** - * Quotes a pattern from the browscap.ini file, so that it can be used in regular expressions - * - * @param string $pattern - * @return string - */ - private function pregQuote($pattern) - { - $pattern = preg_quote($pattern, '/'); - - // The \\x replacement is a fix for "Der gro\xdfe BilderSauger 2.00u" user agent match - // @source https://github.com/browscap/browscap-php - return str_replace(array('\*', '\?', '\\x'), array('.*', '.', '\\\\x'), $pattern); - } - /** * Creates new ini part cache files * @param string $content @@ -288,7 +275,7 @@ private function createIniParts($content) foreach ($patternpositions as $position => $pattern) { $pattern = strtolower($pattern); $patternhash = md5($pattern); - $subkey = self::getIniPartCacheSubkey($patternhash); + $subkey = SubKey::getPatternCacheSubkey($patternhash); if (!isset($contents[$subkey])) { $contents[$subkey] = array(); @@ -313,20 +300,16 @@ private function createIniParts($content) unset($patternpositions); unset($iniParts); + $subkeys = array_flip(SubKey::getAllPatternCacheSubkeys()); foreach ($contents as $subkey => $content) { - $this->getCache()->setItem('browscap.iniparts.'.$subkey, $content, true); + $subkey = (string) $subkey; + $this->getCache()->setItem('browscap.iniparts.' . $subkey, $content, true); + unset($subkeys[$subkey]); } - } - /** - * Gets the subkey for the ini parts cache file, generated from the given string - * - * @param string $string - * @return string - */ - public static function getIniPartCacheSubkey($string) - { - return $string[0].$string[1]; + foreach (array_keys($subkeys) as $subkey) { + $this->getCache()->setItem('browscap.iniparts.' . $subkey, array(), true); + } } /** @@ -370,7 +353,22 @@ private function createPatterns($content) if (!isset($data[$tmpStart])) { $data[$tmpStart] = array(); } - $data[$tmpStart][] = $match; + + $quoterHelper = new Quoter(); + $match = $quoterHelper->pregQuote($match); + + // Check if the pattern contains digits - in this case we replace them with a digit regular expression, + // so that very similar patterns (e.g. only with different browser version numbers) can be compressed. + // This helps to speed up the first (and most expensive) part of the pattern search a lot. + if (strpbrk($match, '0123456789') !== false) { + $compressedPattern = preg_replace('/\d/', '[\d]', $match); + if (!in_array($compressedPattern, $data[$tmpStart])) { + $data[$tmpStart][] = $compressedPattern; + } + } else { + $data[$tmpStart][] = $match; + } + //$data[$tmpStart][] = $match; } unset($matches); @@ -388,7 +386,7 @@ private function createPatterns($content) array_slice($tmpPatterns, ($i * $this->joinPatterns), $this->joinPatterns) ); - $tmpSubkey = Pattern::getPatternCacheSubkey($tmpStart); + $tmpSubkey = SubKey::getPatternCacheSubkey($tmpStart); if (!isset($contents[$tmpSubkey])) { $contents[$tmpSubkey] = array(); @@ -403,14 +401,15 @@ private function createPatterns($content) // write cache files. important: also write empty cache files for // unused patterns, so that the regeneration is not unnecessarily // triggered by the getPatterns() method. - $subkeys = array_flip(Pattern::getAllPatternCacheSubkeys()); + $subkeys = array_flip(SubKey::getAllPatternCacheSubkeys()); foreach ($contents as $subkey => $content) { - $this->cache->setItem('browscap.patterns.'.$subkey, $content, true); + $subkey = (string) $subkey; + $this->cache->setItem('browscap.patterns.' . $subkey, $content, true); unset($subkeys[$subkey]); } foreach (array_keys($subkeys) as $subkey) { - $this->getCache()->setItem('browscap.patterns.'.$subkey, array(), true); + $this->getCache()->setItem('browscap.patterns.' . $subkey, array(), true); } return true; diff --git a/src/Helper/Quoter.php b/src/Helper/Quoter.php index cbd7848b..7d4504a9 100644 --- a/src/Helper/Quoter.php +++ b/src/Helper/Quoter.php @@ -58,4 +58,38 @@ public function pregQuote($user_agent, $delimiter = '/') // the \\x replacement is a fix for "Der gro\xdfe BilderSauger 2.00u" user agent match return str_replace(array('\*', '\?', '\\x'), array('.*', '.', '\\\\x'), $pattern); } + + /** + * Reverts the quoting of a pattern. + * + * @param string $pattern + * @return string + */ + public function pregUnQuote($pattern) + { + // Fast check, because most parent pattern like 'DefaultProperties' don't need a replacement + if (preg_match('/[^a-z\s]/i', $pattern)) { + // Undo the \\x replacement, that is a fix for "Der gro\xdfe BilderSauger 2.00u" user agent match + // @source https://github.com/browscap/browscap-php + $pattern = preg_replace( + ['/(?", "\\|", "\\:", "\\-", "\\.", "\\/" + ), + array( + "\\", "+", "*", "?", "[", "^", "]", "\$", "(", ")", "{", "}", "=", "!", "<", ">", "|", ":", + "-", ".", "/" + ), + $pattern + ); + } + return $pattern; + } } diff --git a/src/Parser/Helper/GetPattern.php b/src/Parser/Helper/GetPattern.php index a66223ca..2ec894de 100644 --- a/src/Parser/Helper/GetPattern.php +++ b/src/Parser/Helper/GetPattern.php @@ -127,7 +127,7 @@ public function getPatterns($userAgent) // get patterns, first for the given browser and if that is not found, // for the default browser (with a special key) foreach ($starts as $tmpStart) { - $tmpSubkey = Pattern::getPatternCacheSubkey($tmpStart); + $tmpSubkey = SubKey::getPatternCacheSubkey($tmpStart); $success = null; $file = $this->getCache()->getItem('browscap.patterns.'.$tmpSubkey, true, $success); diff --git a/src/Parser/Helper/Pattern.php b/src/Parser/Helper/Pattern.php index 0e89272d..d3006a24 100644 --- a/src/Parser/Helper/Pattern.php +++ b/src/Parser/Helper/Pattern.php @@ -44,17 +44,6 @@ */ class Pattern { - /** - * Gets the subkey for the pattern cache file, generated from the given string - * - * @param string $string - * @return string - */ - public static function getPatternCacheSubkey($string) - { - return $string[0].$string[1]; - } - /** * Gets a hash or an array of hashes from the first characters of a pattern/user agent, that can * be used for a fast comparison, by comparing only the hashes, without having to match the @@ -93,7 +82,8 @@ public static function getPatternStart($pattern, $variants = false) $patternStarts = array(); for ($i = strlen($string); $i >= 1; $i--) { - $patternStarts[] = md5(substr($string, 0, $i)); + $string = substr($string, 0, $i); + $patternStarts[] = md5($string); } // Add empty pattern start to include patterns that start with "*", @@ -117,23 +107,4 @@ public static function getPatternLength($pattern) { return strlen(str_replace('*', '', $pattern)); } - - /** - * Gets all subkeys for the pattern cache files - * - * @return array - */ - public static function getAllPatternCacheSubkeys() - { - $subkeys = array(); - $chars = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'); - - foreach ($chars as $char_one) { - foreach ($chars as $char_two) { - $subkeys[] = $char_one.$char_two; - } - } - - return $subkeys; - } } diff --git a/src/Parser/Helper/SubKey.php b/src/Parser/Helper/SubKey.php new file mode 100644 index 00000000..9ce049fe --- /dev/null +++ b/src/Parser/Helper/SubKey.php @@ -0,0 +1,78 @@ + + * @author Thomas Müller + * @copyright Copyright (c) 1998-2014 Browser Capabilities Project + * @version 3.0 + * @license http://www.opensource.org/licenses/MIT MIT License + * @link https://github.com/browscap/browscap-php/ + */ +class SubKey +{ + /** + * Gets the subkey for the pattern cache file, generated from the given string + * + * @param string $string + * @return string + */ + public static function getPatternCacheSubkey($string) + { + return $string[0].$string[1].$string[2]; + } + + /** + * Gets all subkeys for the pattern cache files + * + * @return array + */ + public static function getAllPatternCacheSubkeys() + { + $subkeys = array(); + $chars = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'); + + foreach ($chars as $char_one) { + foreach ($chars as $char_two) { + foreach ($chars as $char_three) { + $subkeys[] = $char_one . $char_two . $char_three; + } + } + } + + return $subkeys; + } +} diff --git a/src/Parser/Ini.php b/src/Parser/Ini.php index 931ad272..f015252d 100644 --- a/src/Parser/Ini.php +++ b/src/Parser/Ini.php @@ -36,6 +36,7 @@ use BrowscapPHP\Helper\Converter; use BrowscapPHP\Helper\Quoter; use BrowscapPHP\Parser\Helper\GetPatternInterface; +use BrowscapPHP\Parser\Helper\SubKey; use Psr\Log\LoggerInterface; /** @@ -178,13 +179,12 @@ public function getLogger() */ public function getBrowser($userAgent) { - $userAgent = strtolower($userAgent); - $formatter = null; - $quoterHelper = new Quoter(); + $userAgent = strtolower($userAgent); + $formatter = null; foreach ($this->getHelper()->getPatterns($userAgent) as $patterns) { $result = preg_match( - '/^(?:'.str_replace("\t", ')|(?:', $quoterHelper->pregQuote($patterns, '/')).')$/i', + '/^(?:'.str_replace("\t", ')|(?:', $patterns).')$/i', $userAgent ); @@ -196,12 +196,29 @@ public function getBrowser($userAgent) $pattern = strtok($patterns, "\t"); while ($pattern !== false) { - $quotedPattern = '/^'.$quoterHelper->pregQuote($pattern, '/').'$/i'; - - if (preg_match($quotedPattern, $userAgent)) { - $formatter = $this->getFormatter(); - $formatter->setData($this->getSettings($pattern)); - break 2; + $pattern = str_replace('[\d]', '(\d)', $pattern); + $quotedPattern = '/^' . $pattern . '$/i'; + + if (preg_match($quotedPattern, $userAgent, $matches)) { + // Insert the digits back into the pattern, so that we can search the settings for it + if (count($matches) > 1) { + array_shift($matches); + foreach ($matches as $one_match) { + $numPos = strpos($pattern, '(\d)'); + $pattern = substr_replace($pattern, $one_match, $numPos, 4); + } + } + + // Try to get settings - as digits have been replaced to speed up the pattern search (up to 90 faster), + // we won't always find the data in the first step - so check if settings have been found and if not, + // search for the next pattern. + $settings = $this->getSettings($pattern); + + if (count($settings) > 0) { + $formatter = $this->getFormatter(); + $formatter->setData($settings); + break 2; + } } $pattern = strtok("\t"); @@ -221,31 +238,44 @@ public function getBrowser($userAgent) */ private function getSettings($pattern, array $settings = array()) { + $quoterHelper = new Quoter(); + // The pattern has been pre-quoted on generation to speed up the pattern search, + // but for this check we need the unquoted version + $unquotedPattern = $quoterHelper->pregUnQuote($pattern); + + // Try to get settings for the pattern + $addedSettings = $this->getIniPart($unquotedPattern); + // set some additional data if (count($settings) === 0) { - $quoterHelper = new Quoter(); - - $settings['browser_name_regex'] = '/^'.$quoterHelper->pregQuote($pattern).'$/'; - $settings['browser_name_pattern'] = $pattern; + // The optimization with replaced digits get can now result in setting searches, for which we + // won't find a result - so only add the pattern information, is settings have been found. + // + // If not an empty array will be returned and the calling function can easily check if a pattern + // has been found. + if (count($addedSettings) > 0) { + $settings['browser_name_regex'] = '/^' . $pattern . '$/'; + $settings['browser_name_pattern'] = $unquotedPattern; + } + //$settings['browser_name_regex'] = '/^'.$quoterHelper->pregQuote($pattern).'$/'; + //$settings['browser_name_pattern'] = $pattern; } - $add_settings = $this->getIniPart($pattern); - // check if parent pattern set, only keep the first one - $parent_pattern = null; - if (isset($add_settings['Parent'])) { - $parent_pattern = $add_settings['Parent']; + $parentPattern = null; + if (isset($addedSettings['Parent'])) { + $parentPattern = $addedSettings['Parent']; if (isset($settings['Parent'])) { - unset($add_settings['Parent']); + unset($addedSettings['Parent']); } } // merge settings - $settings += $add_settings; + $settings += $addedSettings; - if ($parent_pattern !== null) { - return $this->getSettings($parent_pattern, $settings); + if ($parentPattern !== null) { + return $this->getSettings($quoterHelper->pregQuote($parentPattern), $settings); } return $settings; @@ -261,7 +291,7 @@ private function getIniPart($pattern) { $pattern = strtolower($pattern); $patternhash = md5($pattern); - $subkey = Converter::getIniPartCacheSubkey($patternhash); + $subkey = SubKey::getPatternCacheSubkey($patternhash); if (!$this->getCache()->hasItem('browscap.iniparts.'.$subkey, true)) { return array(); diff --git a/src/Parser/IniParser.php b/src/Parser/IniParser.php index 70e8ccd0..7c7d5700 100644 --- a/src/Parser/IniParser.php +++ b/src/Parser/IniParser.php @@ -30,6 +30,8 @@ namespace BrowscapPHP\Parser; +use BrowscapPHP\Exception\InvalidArgumentException; + /** * parses the ini data into an array of sections with their data * @@ -116,7 +118,7 @@ private function getLinesFromFile() $filename = $this->filename; if (!file_exists($filename)) { - throw new \BrowscapPHP\Exception\InvalidArgumentException("File not found: {$filename}"); + throw new InvalidArgumentException("File not found: {$filename}"); } return file($filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); diff --git a/tests/Parser/Helper/PatternTest.php b/tests/Parser/Helper/PatternTest.php index ed3b41a4..4508040b 100644 --- a/tests/Parser/Helper/PatternTest.php +++ b/tests/Parser/Helper/PatternTest.php @@ -38,14 +38,6 @@ */ class PatternTest extends \PHPUnit_Framework_TestCase { - /** - * - */ - public function testGetPatternCacheSubkey() - { - self::assertSame('ab', Pattern::getPatternCacheSubkey('abcd')); - } - /** * */ @@ -144,14 +136,4 @@ public function testGetPatternLength() { self::assertSame(4, Pattern::getPatternLength('abcd')); } - - /** - * - */ - public function testGetAllPatternCacheSubkeys() - { - $result = Pattern::getAllPatternCacheSubkeys(); - self::assertInternalType('array', $result); - self::assertSame(256, count($result)); - } } diff --git a/tests/Parser/Helper/SubKeyTest.php b/tests/Parser/Helper/SubKeyTest.php new file mode 100644 index 00000000..fff19c92 --- /dev/null +++ b/tests/Parser/Helper/SubKeyTest.php @@ -0,0 +1,58 @@ + + * @copyright Copyright (c) 2006-2012 Jonathan Stoppani + * @version 1.0 + * @license http://www.opensource.org/licenses/MIT MIT License + * @link https://github.com/GaretJax/BrowscapPHP/ + */ +class SubKeyTest extends \PHPUnit_Framework_TestCase +{ + /** + * + */ + public function testGetPatternCacheSubkey() + { + self::assertSame('abc', SubKey::getPatternCacheSubkey('abcd')); + } + + /** + * + */ + public function testGetAllPatternCacheSubkeys() + { + $result = SubKey::getAllPatternCacheSubkeys(); + self::assertInternalType('array', $result); + self::assertSame(4096, count($result)); + } +} From 0e506738568548ff7ccdb234e6b17cc84e1a72eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Tue, 1 Sep 2015 22:32:19 +0200 Subject: [PATCH 02/24] revert some changes --- src/Helper/Converter.php | 2 +- src/Parser/Helper/SubKey.php | 38 ++++++++++++++++++++--- src/Parser/Ini.php | 2 +- tests/Command/ConvertCommandTest.php | 17 +++++++++- tests/CompareBrowscapWithOriginalTest.php | 19 ++++++++++-- tests/Parser/Helper/SubKeyTest.php | 4 +-- 6 files changed, 71 insertions(+), 11 deletions(-) diff --git a/src/Helper/Converter.php b/src/Helper/Converter.php index 5289c58d..119460fd 100644 --- a/src/Helper/Converter.php +++ b/src/Helper/Converter.php @@ -300,7 +300,7 @@ private function createIniParts($content) unset($patternpositions); unset($iniParts); - $subkeys = array_flip(SubKey::getAllPatternCacheSubkeys()); + $subkeys = array_flip(SubKey::getAllIniPartCacheSubKeys()); foreach ($contents as $subkey => $content) { $subkey = (string) $subkey; $this->getCache()->setItem('browscap.iniparts.' . $subkey, $content, true); diff --git a/src/Parser/Helper/SubKey.php b/src/Parser/Helper/SubKey.php index 9ce049fe..e046fe9f 100644 --- a/src/Parser/Helper/SubKey.php +++ b/src/Parser/Helper/SubKey.php @@ -52,7 +52,7 @@ class SubKey */ public static function getPatternCacheSubkey($string) { - return $string[0].$string[1].$string[2]; + return $string[0].$string[1]; } /** @@ -67,12 +67,42 @@ public static function getAllPatternCacheSubkeys() foreach ($chars as $char_one) { foreach ($chars as $char_two) { - foreach ($chars as $char_three) { - $subkeys[] = $char_one . $char_two . $char_three; - } + $subkeys[] = $char_one . $char_two; } } return $subkeys; } + + /** + * Gets the sub key for the ini parts cache file, generated from the given string + * + * @param string $string + * @return string + */ + public static function getIniPartCacheSubKey($string) + { + return $string[0] . $string[1] . $string[2]; + } + + /** + * Gets all sub keys for the inipart cache files + * + * @return array + */ + public static function getAllIniPartCacheSubKeys() + { + $subKeys = array(); + $chars = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'); + + foreach ($chars as $charOne) { + foreach ($chars as $charTwo) { + foreach ($chars as $charThree) { + $subKeys[] = $charOne . $charTwo . $charThree; + } + } + } + + return $subKeys; + } } diff --git a/src/Parser/Ini.php b/src/Parser/Ini.php index f015252d..68a19f6d 100644 --- a/src/Parser/Ini.php +++ b/src/Parser/Ini.php @@ -291,7 +291,7 @@ private function getIniPart($pattern) { $pattern = strtolower($pattern); $patternhash = md5($pattern); - $subkey = SubKey::getPatternCacheSubkey($patternhash); + $subkey = SubKey::getIniPartCacheSubKey($patternhash); if (!$this->getCache()->hasItem('browscap.iniparts.'.$subkey, true)) { return array(); diff --git a/tests/Command/ConvertCommandTest.php b/tests/Command/ConvertCommandTest.php index 8a527afd..99c23263 100644 --- a/tests/Command/ConvertCommandTest.php +++ b/tests/Command/ConvertCommandTest.php @@ -104,7 +104,22 @@ public function testConfigure() */ public function testExecute() { - $input = $this->getMock('\Symfony\Component\Console\Input\ArgvInput', array(), array(), '', false); + $input = $this->getMock('\Symfony\Component\Console\Input\ArgvInput', array(), array(), '', false); + $objectIniPath = ini_get('browscap'); + + if (!is_file($objectIniPath)) { + $this->setExpectedException( + '\BrowscapPHP\Exception', + 'an error occured while converting the local file into the cache' + ); + } else { + $input + ->expects(self::exactly(2)) + ->method('getArgument') + ->with('file') + ->will(self::returnValue($objectIniPath)) + ; + } $output = $this->getMock('\Symfony\Component\Console\Output\ConsoleOutput', array(), array(), '', false); $class = new \ReflectionClass('\BrowscapPHP\Command\ConvertCommand'); diff --git a/tests/CompareBrowscapWithOriginalTest.php b/tests/CompareBrowscapWithOriginalTest.php index 6af5196b..c87bbc36 100644 --- a/tests/CompareBrowscapWithOriginalTest.php +++ b/tests/CompareBrowscapWithOriginalTest.php @@ -99,6 +99,8 @@ public function testCheckProperties() $libProperties = get_object_vars(get_browser('x')); $bcProperties = get_object_vars(self::$object->getBrowser('x')); + unset($libProperties['parent'], $bcProperties['parent']); + $libPropertyKeys = array_keys($libProperties); $bcPropertyKeys = array_keys($bcProperties); @@ -108,7 +110,7 @@ public function testCheckProperties() self::fail('the properties found by "get_browser()" differ from found by "Browser::getBrowser()"'); } - foreach (array_keys($bcProperties) as $bcProp) { + foreach ($bcPropertyKeys as $bcProp) { self::assertArrayHasKey( strtolower($bcProp), $libProperties, @@ -144,13 +146,26 @@ public function testCompare($userAgent) $bcProp = strtolower($bcProp); + self::assertClassHasAttribute( + $bcProp, + $libResult, + 'Actual library result does not have "' . $bcProp . '" property' + ); + + self::assertClassHasAttribute( + $bcProp, + $bcResult, + 'Actual browscap result does not have "' . $bcProp . '" property' + ); + $libValue = (string) $libResult->{$bcProp}; $bcValue = (string) $bcResult->{$bcProp}; self::assertSame( $libValue, $bcValue, - $bcProp . ': ' . $libValue . ' != ' . $bcValue + 'Expected actual "' . $bcProp . '" to be "' . $libValue . '" (was "' . $bcValue + . '"; for useragent: ' . $userAgent .')' ); } } diff --git a/tests/Parser/Helper/SubKeyTest.php b/tests/Parser/Helper/SubKeyTest.php index fff19c92..ee8fd65b 100644 --- a/tests/Parser/Helper/SubKeyTest.php +++ b/tests/Parser/Helper/SubKeyTest.php @@ -43,7 +43,7 @@ class SubKeyTest extends \PHPUnit_Framework_TestCase */ public function testGetPatternCacheSubkey() { - self::assertSame('abc', SubKey::getPatternCacheSubkey('abcd')); + self::assertSame('ab', SubKey::getPatternCacheSubkey('abcd')); } /** @@ -53,6 +53,6 @@ public function testGetAllPatternCacheSubkeys() { $result = SubKey::getAllPatternCacheSubkeys(); self::assertInternalType('array', $result); - self::assertSame(4096, count($result)); + self::assertSame(256, count($result)); } } From 04965561e0b9300f42cbd0d543d97b1afb1dd16c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 2 Sep 2015 23:09:09 +0200 Subject: [PATCH 03/24] updates --- .travis.yml | 3 --- tests/CompareBrowscapWithOriginalTest.php | 21 ++++++++++++++------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index 40b0fbf1..d856b368 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,12 +9,9 @@ php: os: - linux - - osx before_script: - wget http://browscap.org/stream?q=Full_PHP_BrowsCapINI -O /tmp/browscap.ini - - if [ "`phpenv version-name`" == "5.5" ] || [ "`phpenv version-name`" == "5.6" ]; then echo 'opcache.enable=1' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi - - if [ "`phpenv version-name`" == "5.5" ] || [ "`phpenv version-name`" == "5.6" ]; then echo 'opcache.enable_cli=1' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi - composer self-update - composer install -o --prefer-source - cp /tmp/browscap.ini resources/browscap.ini diff --git a/tests/CompareBrowscapWithOriginalTest.php b/tests/CompareBrowscapWithOriginalTest.php index c87bbc36..ab999e99 100644 --- a/tests/CompareBrowscapWithOriginalTest.php +++ b/tests/CompareBrowscapWithOriginalTest.php @@ -110,20 +110,27 @@ public function testCheckProperties() self::fail('the properties found by "get_browser()" differ from found by "Browser::getBrowser()"'); } - foreach ($bcPropertyKeys as $bcProp) { + foreach (array_keys($this->properties) as $bcProp) { + $bcProp = strtolower($bcProp); + + if (in_array($bcProp, array('browser_name_regex', 'browser_name_pattern', 'parent'))) { + unset($libProperties[$bcProp]); + continue; + } + self::assertArrayHasKey( - strtolower($bcProp), + $bcProp, $libProperties, 'Property `' . $bcProp . '` from Browscap doesn\'t match anything in get_browser.' ); - unset($libProperties[strtolower($bcProp)]); + unset($libProperties[$bcProp]); } self::assertSame( 0, count($libProperties), - 'There are ' . count($libProperties) . '(' . implode(', ', array_keys($libProperties)) + 'There are ' . count($libProperties) . '(' . implode(', ', $libPropertyKeys) . ') properties in get_browser that do not match those in Browscap.' ); } @@ -146,13 +153,13 @@ public function testCompare($userAgent) $bcProp = strtolower($bcProp); - self::assertClassHasAttribute( + self::assertObjectHasAttribute( $bcProp, $libResult, 'Actual library result does not have "' . $bcProp . '" property' ); - self::assertClassHasAttribute( + self::assertObjectHasAttribute( $bcProp, $bcResult, 'Actual browscap result does not have "' . $bcProp . '" property' @@ -165,7 +172,7 @@ public function testCompare($userAgent) $libValue, $bcValue, 'Expected actual "' . $bcProp . '" to be "' . $libValue . '" (was "' . $bcValue - . '"; for useragent: ' . $userAgent .')' + . '"; used pattern: ' . $bcResult->browser_name_pattern .')' ); } } From 07aff37c3cc8a30f4b55e2f54b7c495b0ee5014a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Fri, 4 Sep 2015 22:49:30 +0200 Subject: [PATCH 04/24] add php nightly to tests --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d856b368..15818839 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,8 @@ sudo: false php: - 5.5 - 5.6 - - 7 + - 7.0 + - nightly - hhvm os: From 291ba09579642f93cba2ceee859ce29f73589fca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Sat, 5 Sep 2015 18:52:02 +0200 Subject: [PATCH 05/24] change detection --- .gitignore | 1 + src/Browscap.php | 9 +++++- src/Helper/Converter.php | 49 ++++++++---------------------- src/Parser/Helper/GetPattern.php | 36 ++++++++-------------- src/Parser/Helper/SubKey.php | 30 ------------------ src/Parser/Ini.php | 34 +++++++++++---------- tests/Parser/Helper/SubKeyTest.php | 12 +------- 7 files changed, 52 insertions(+), 119 deletions(-) diff --git a/.gitignore b/.gitignore index 5395aaff..7a1ebda9 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ vendor/ phpunit.xml composer.lock +PHP_BrowscapINI diff --git a/src/Browscap.php b/src/Browscap.php index 0964b7db..d441433e 100644 --- a/src/Browscap.php +++ b/src/Browscap.php @@ -336,6 +336,8 @@ public function convertFile($iniFile) * reads and parses an ini string and writes the results into the cache * * @param string $iniString + * + * @throws \BrowscapPHP\Exception */ public function convertString($iniString) { @@ -344,8 +346,13 @@ public function convertString($iniString) $converter ->setLogger($this->getLogger()) ->setCache($this->getCache()) - ->convertString($iniString) ; + + try { + $converter->convertString($iniString); + } catch (Helper\Exception $e) { + throw new Exception('an error occured while converting the data string into the cache', 0, $e); + } } /** diff --git a/src/Helper/Converter.php b/src/Helper/Converter.php index 119460fd..14061866 100644 --- a/src/Helper/Converter.php +++ b/src/Helper/Converter.php @@ -275,10 +275,9 @@ private function createIniParts($content) foreach ($patternpositions as $position => $pattern) { $pattern = strtolower($pattern); $patternhash = md5($pattern); - $subkey = SubKey::getPatternCacheSubkey($patternhash); - if (!isset($contents[$subkey])) { - $contents[$subkey] = array(); + if (!isset($contents[$patternhash])) { + $contents[$patternhash] = array(); } $browserProperties = parse_ini_string($iniParts[($position + 1)]); @@ -291,7 +290,7 @@ private function createIniParts($content) } // the position has to be moved by one, because the header of the ini file // is also returned as a part - $contents[$subkey][] = $patternhash.json_encode( + $contents[$patternhash][] = json_encode( $browserProperties, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP ); @@ -300,15 +299,8 @@ private function createIniParts($content) unset($patternpositions); unset($iniParts); - $subkeys = array_flip(SubKey::getAllIniPartCacheSubKeys()); - foreach ($contents as $subkey => $content) { - $subkey = (string) $subkey; - $this->getCache()->setItem('browscap.iniparts.' . $subkey, $content, true); - unset($subkeys[$subkey]); - } - - foreach (array_keys($subkeys) as $subkey) { - $this->getCache()->setItem('browscap.iniparts.' . $subkey, array(), true); + foreach ($contents as $patternhash => $content) { + $this->getCache()->setItem('browscap.iniparts.' . $patternhash, $content, true); } } @@ -342,7 +334,7 @@ private function createPatterns($content) $match = strtolower($match); // get the first characters for a fast search - $tmpStart = Pattern::getPatternStart($match); + $tmpStart = Pattern::getPatternStart($match, false); $tmpLength = Pattern::getPatternLength($match); // special handling of default entry @@ -362,13 +354,13 @@ private function createPatterns($content) // This helps to speed up the first (and most expensive) part of the pattern search a lot. if (strpbrk($match, '0123456789') !== false) { $compressedPattern = preg_replace('/\d/', '[\d]', $match); + if (!in_array($compressedPattern, $data[$tmpStart])) { $data[$tmpStart][] = $compressedPattern; } } else { $data[$tmpStart][] = $match; } - //$data[$tmpStart][] = $match; } unset($matches); @@ -378,39 +370,22 @@ private function createPatterns($content) // array with pattern strings instead of an large array with single patterns) and also enables // us to search for multiple patterns in one preg_match call for a fast first search // (3-10 faster), followed by a detailed search for each single pattern. - $contents = array(); foreach ($data as $tmpStart => $tmpPatterns) { + $patterns = array(); + for ($i = 0, $j = ceil(count($tmpPatterns) / $this->joinPatterns); $i < $j; $i++) { $tmpJoinPatterns = implode( "\t", array_slice($tmpPatterns, ($i * $this->joinPatterns), $this->joinPatterns) ); - $tmpSubkey = SubKey::getPatternCacheSubkey($tmpStart); - - if (!isset($contents[$tmpSubkey])) { - $contents[$tmpSubkey] = array(); - } - - $contents[$tmpSubkey][] = $tmpStart.' '.$tmpJoinPatterns; + $patterns[] = $tmpJoinPatterns; } - } - - unset($data); - // write cache files. important: also write empty cache files for - // unused patterns, so that the regeneration is not unnecessarily - // triggered by the getPatterns() method. - $subkeys = array_flip(SubKey::getAllPatternCacheSubkeys()); - foreach ($contents as $subkey => $content) { - $subkey = (string) $subkey; - $this->cache->setItem('browscap.patterns.' . $subkey, $content, true); - unset($subkeys[$subkey]); + $this->cache->setItem('browscap.patterns.' . $tmpStart, $patterns, true); } - foreach (array_keys($subkeys) as $subkey) { - $this->getCache()->setItem('browscap.patterns.' . $subkey, array(), true); - } + unset($data); return true; } diff --git a/src/Parser/Helper/GetPattern.php b/src/Parser/Helper/GetPattern.php index 2ec894de..78205287 100644 --- a/src/Parser/Helper/GetPattern.php +++ b/src/Parser/Helper/GetPattern.php @@ -119,7 +119,6 @@ public function getLogger() public function getPatterns($userAgent) { $starts = Pattern::getPatternStart($userAgent, true); - $length = Pattern::getPatternLength($userAgent); // add special key to fall back to the default browser $starts[] = str_repeat('z', 32); @@ -127,38 +126,27 @@ public function getPatterns($userAgent) // get patterns, first for the given browser and if that is not found, // for the default browser (with a special key) foreach ($starts as $tmpStart) { - $tmpSubkey = SubKey::getPatternCacheSubkey($tmpStart); - $success = null; + if (!$this->getCache()->hasItem('browscap.patterns.'.$tmpStart, true)) { + $this->getLogger()->debug('cache key "browscap.patterns.'.$tmpStart.'" not found'); + continue; + } + + $success = null; - $file = $this->getCache()->getItem('browscap.patterns.'.$tmpSubkey, true, $success); + $patterns = $this->getCache()->getItem('browscap.patterns.'.$tmpStart, true, $success); if (!$success) { - $this->getLogger()->debug('cache key "browscap.patterns.'.$tmpSubkey.'" not found'); + $this->getLogger()->debug('cache key "browscap.patterns.'.$tmpStart.'" not found'); continue; } - if (!is_array($file) || !count($file)) { - $this->getLogger()->debug('cache key "browscap.patterns.'.$tmpSubkey.'" was empty'); + if (!is_array($patterns) || !count($patterns)) { + $this->getLogger()->debug('cache key "browscap.patterns.'.$tmpStart.'" was empty'); continue; } - $found = false; - - foreach ($file as $buffer) { - $tmpBuffer = substr($buffer, 0, 32); - if ($tmpBuffer === $tmpStart) { - // get length of the pattern - $len = (int) strstr(substr($buffer, 33, 4), ' ', true); - - // the user agent must be longer than the pattern without place holders - if ($len <= $length) { - list(, $patterns) = explode(' ', $buffer, 2); - yield trim($patterns); - } - $found = true; - } elseif ($found === true) { - break; - } + foreach ($patterns as $tmpJoinPatterns) { + yield trim($tmpJoinPatterns); } } diff --git a/src/Parser/Helper/SubKey.php b/src/Parser/Helper/SubKey.php index e046fe9f..2d24f111 100644 --- a/src/Parser/Helper/SubKey.php +++ b/src/Parser/Helper/SubKey.php @@ -44,36 +44,6 @@ */ class SubKey { - /** - * Gets the subkey for the pattern cache file, generated from the given string - * - * @param string $string - * @return string - */ - public static function getPatternCacheSubkey($string) - { - return $string[0].$string[1]; - } - - /** - * Gets all subkeys for the pattern cache files - * - * @return array - */ - public static function getAllPatternCacheSubkeys() - { - $subkeys = array(); - $chars = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'); - - foreach ($chars as $char_one) { - foreach ($chars as $char_two) { - $subkeys[] = $char_one . $char_two; - } - } - - return $subkeys; - } - /** * Gets the sub key for the ini parts cache file, generated from the given string * diff --git a/src/Parser/Ini.php b/src/Parser/Ini.php index 68a19f6d..e048e4fa 100644 --- a/src/Parser/Ini.php +++ b/src/Parser/Ini.php @@ -36,6 +36,7 @@ use BrowscapPHP\Helper\Converter; use BrowscapPHP\Helper\Quoter; use BrowscapPHP\Parser\Helper\GetPatternInterface; +use BrowscapPHP\Parser\Helper\Pattern; use BrowscapPHP\Parser\Helper\SubKey; use Psr\Log\LoggerInterface; @@ -181,10 +182,15 @@ public function getBrowser($userAgent) { $userAgent = strtolower($userAgent); $formatter = null; + $starts = Pattern::getPatternStart($userAgent, true); + + // add special key to fall back to the default browser + $starts[] = str_repeat('z', 32); foreach ($this->getHelper()->getPatterns($userAgent) as $patterns) { + $usedMatch = '/^(?:'.str_replace("\t", ')|(', $patterns).')$/i'; $result = preg_match( - '/^(?:'.str_replace("\t", ')|(?:', $patterns).')$/i', + $usedMatch, $userAgent ); @@ -239,6 +245,7 @@ public function getBrowser($userAgent) private function getSettings($pattern, array $settings = array()) { $quoterHelper = new Quoter(); + // The pattern has been pre-quoted on generation to speed up the pattern search, // but for this check we need the unquoted version $unquotedPattern = $quoterHelper->pregUnQuote($pattern); @@ -257,8 +264,6 @@ private function getSettings($pattern, array $settings = array()) $settings['browser_name_regex'] = '/^' . $pattern . '$/'; $settings['browser_name_pattern'] = $unquotedPattern; } - //$settings['browser_name_regex'] = '/^'.$quoterHelper->pregQuote($pattern).'$/'; - //$settings['browser_name_pattern'] = $pattern; } // check if parent pattern set, only keep the first one @@ -291,14 +296,13 @@ private function getIniPart($pattern) { $pattern = strtolower($pattern); $patternhash = md5($pattern); - $subkey = SubKey::getIniPartCacheSubKey($patternhash); - if (!$this->getCache()->hasItem('browscap.iniparts.'.$subkey, true)) { + if (!$this->getCache()->hasItem('browscap.iniparts.'.$patternhash, true)) { return array(); } $success = null; - $file = $this->getCache()->getItem('browscap.iniparts.'.$subkey, true, $success); + $file = $this->getCache()->getItem('browscap.iniparts.'.$patternhash, true, $success); if (!$success) { return array(); @@ -306,18 +310,16 @@ private function getIniPart($pattern) $return = array(); foreach ($file as $buffer) { - if (substr($buffer, 0, 32) === $patternhash) { - $return = json_decode(substr($buffer, 32), true); - - foreach (array_keys($return) as $property) { - $return[$property] = $this->formatPropertyValue( - $return[$property], - $property - ); - } + $return = json_decode($buffer, true); - break; + foreach (array_keys($return) as $property) { + $return[$property] = $this->formatPropertyValue( + $return[$property], + $property + ); } + + break; } return $return; diff --git a/tests/Parser/Helper/SubKeyTest.php b/tests/Parser/Helper/SubKeyTest.php index ee8fd65b..ec8ddf2b 100644 --- a/tests/Parser/Helper/SubKeyTest.php +++ b/tests/Parser/Helper/SubKeyTest.php @@ -43,16 +43,6 @@ class SubKeyTest extends \PHPUnit_Framework_TestCase */ public function testGetPatternCacheSubkey() { - self::assertSame('ab', SubKey::getPatternCacheSubkey('abcd')); - } - - /** - * - */ - public function testGetAllPatternCacheSubkeys() - { - $result = SubKey::getAllPatternCacheSubkeys(); - self::assertInternalType('array', $result); - self::assertSame(256, count($result)); + self::markTestSkipped('test removed'); } } From a246477562e3f8567e4d49e8babd01bb7de700ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Mon, 7 Sep 2015 19:41:16 +0200 Subject: [PATCH 06/24] remove include --- src/Helper/Converter.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Helper/Converter.php b/src/Helper/Converter.php index 14061866..5e58a4a5 100644 --- a/src/Helper/Converter.php +++ b/src/Helper/Converter.php @@ -34,7 +34,6 @@ use BrowscapPHP\Data\PropertyHolder; use BrowscapPHP\Exception\FileNotFoundException; use BrowscapPHP\Parser\Helper\Pattern; -use BrowscapPHP\Parser\Helper\SubKey; use BrowscapPHP\Parser\Ini; use Psr\Log\LoggerInterface; From 7bae99d969802f4adede227425af108a121546f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Tue, 8 Sep 2015 22:42:25 +0200 Subject: [PATCH 07/24] update --- src/Data/PropertyFormatter.php | 96 +++++++++ src/Helper/Converter.php | 198 ++--------------- src/IniParser/IniParser.php | 197 +++++++++++++++++ src/Parser/Helper/GetPattern.php | 2 +- src/Parser/Helper/Pattern.php | 14 +- src/Parser/Ini.php | 8 +- src/Parser/IniParser.php | 225 -------------------- src/Util/Logfile/ReaderCollection.php | 4 +- src/Util/Logfile/ReaderFactory.php | 2 +- tests/BrowscapTest.php | 3 + tests/Cache/BrowscapCacheTest.php | 3 + tests/Formatter/PhpGetBrowserTest.php | 2 +- tests/Helper/ConverterTest.php | 3 + tests/Helper/IniLoaderTest.php | 2 + tests/Parser/Helper/GetPatternTest.php | 4 + tests/Parser/Helper/PatternTest.php | 15 +- tests/Parser/IniParserTest.php | 67 ------ tests/Parser/IniTest.php | 4 + tests/Util/LogFile/ReaderCollectionTest.php | 3 + 19 files changed, 360 insertions(+), 492 deletions(-) create mode 100644 src/Data/PropertyFormatter.php create mode 100644 src/IniParser/IniParser.php delete mode 100644 src/Parser/IniParser.php delete mode 100644 tests/Parser/IniParserTest.php diff --git a/src/Data/PropertyFormatter.php b/src/Data/PropertyFormatter.php new file mode 100644 index 00000000..89d5fe34 --- /dev/null +++ b/src/Data/PropertyFormatter.php @@ -0,0 +1,96 @@ + + * @author Thomas Müller + * @copyright Copyright (c) 1998-2014 Browser Capabilities Project + * @version 3.0 + * @license http://www.opensource.org/licenses/MIT MIT License + * @link https://github.com/browscap/browscap-php/ + */ +class PropertyFormatter +{ + /** + * @var PropertyHolder + */ + private $propertyHolder = null; + + /** + * @param PropertyHolder $propertyHolder + */ + public function setPropertyHolder(PropertyHolder $propertyHolder) + { + $this->propertyHolder = $propertyHolder; + } + + /** + * formats the name of a property + * + * @param string $value + * @param string $property + * + * @return string + */ + public function formatPropertyValue($value, $property) + { + $valueOutput = $value; + + switch ($this->propertyHolder->getPropertyType($property)) { + case PropertyHolder::TYPE_BOOLEAN: + if (true === $value || $value === 'true' || $value === '1') { + $valueOutput = true; + } elseif (false === $value || $value === 'false' || $value === '') { + $valueOutput = false; + } else { + $valueOutput = ''; + } + break; + case PropertyHolder::TYPE_IN_ARRAY: + try { + $valueOutput = $this->propertyHolder->checkValueInArray($property, $value); + } catch (\InvalidArgumentException $ex) { + $valueOutput = ''; + } + break; + default: + // nothing t do here + break; + } + + return $valueOutput; + } +} diff --git a/src/Helper/Converter.php b/src/Helper/Converter.php index 5e58a4a5..d5a9fe56 100644 --- a/src/Helper/Converter.php +++ b/src/Helper/Converter.php @@ -31,9 +31,8 @@ namespace BrowscapPHP\Helper; use BrowscapPHP\Cache\BrowscapCache; -use BrowscapPHP\Data\PropertyHolder; use BrowscapPHP\Exception\FileNotFoundException; -use BrowscapPHP\Parser\Helper\Pattern; +use BrowscapPHP\IniParser\IniParser; use BrowscapPHP\Parser\Ini; use Psr\Log\LoggerInterface; @@ -79,15 +78,6 @@ class Converter */ private $filessystem = null; - /** - * Number of pattern to combine for a faster regular expression search. - * - * @important The number of patterns that can be processed in one step - * is limited by the internal regular expression limits. - * @var int - */ - private $joinPatterns = 100; - /** * version of the ini file * @@ -195,15 +185,25 @@ public function convertFile($iniFile) */ public function convertString($iniString) { + $iniParser = new IniParser(); + $this->getLogger()->info('start creating patterns from the ini data'); - $this->createPatterns($iniString); + foreach ($iniParser->createPatterns($iniString) as $patternsHashList) { + foreach ($patternsHashList as $patternhash => $patterns) { + $this->cache->setItem('browscap.patterns.' . $patternhash, $patterns, true); + } + } $this->getLogger()->info('finished creating patterns from the ini data'); $this->getLogger()->info('start creating data from the ini data'); - $this->createIniParts($iniString); + foreach ($iniParser->createIniParts($iniString) as $patternsContentList) { + foreach ($patternsContentList as $patternhash => $content) { + $this->getCache()->setItem('browscap.iniparts.' . $patternhash, $content, true); + } + } $this->getLogger()->info('finished creating data from the ini data'); } @@ -254,176 +254,4 @@ public function storeVersion() return $this; } - - /** - * Creates new ini part cache files - * @param string $content - */ - private function createIniParts($content) - { - // get all patterns from the ini file in the correct order, - // so that we can calculate with index number of the resulting array, - // which part to use when the ini file is splitted into its sections. - preg_match_all('/(?<=\[)(?:[^\r\n]+)(?=\])/m', $content, $patternpositions); - $patternpositions = $patternpositions[0]; - - // split the ini file into sections and save the data in one line with a hash of the beloging - // pattern (filtered in the previous step) - $iniParts = preg_split('/\[[^\r\n]+\]/', $content); - $contents = array(); - foreach ($patternpositions as $position => $pattern) { - $pattern = strtolower($pattern); - $patternhash = md5($pattern); - - if (!isset($contents[$patternhash])) { - $contents[$patternhash] = array(); - } - - $browserProperties = parse_ini_string($iniParts[($position + 1)]); - - foreach (array_keys($browserProperties) as $property) { - $browserProperties[$property] = $this->formatPropertyValue( - $browserProperties[$property], - $property - ); - } - // the position has to be moved by one, because the header of the ini file - // is also returned as a part - $contents[$patternhash][] = json_encode( - $browserProperties, - JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP - ); - } - - unset($patternpositions); - unset($iniParts); - - foreach ($contents as $patternhash => $content) { - $this->getCache()->setItem('browscap.iniparts.' . $patternhash, $content, true); - } - } - - /** - * Creates new pattern cache files - * - * @param string $content - * - * @return bool - */ - private function createPatterns($content) - { - // get all relevant patterns from the INI file - // - containing "*" or "?" - // - not containing "*" or "?", but not having a comment - preg_match_all( - '/(?<=\[)(?:[^\r\n]*[?*][^\r\n]*)(?=\])|(?<=\[)(?:[^\r\n*?]+)(?=\])(?![^\[]*Comment=)/m', - $content, - $matches - ); - - if (empty($matches[0]) || !is_array($matches[0])) { - return false; - } - - // build an array to structure the data. this requires some memory, but we need this step to be able to - // sort the data in the way we need it (see below). - $data = array(); - - foreach ($matches[0] as $match) { - $match = strtolower($match); - - // get the first characters for a fast search - $tmpStart = Pattern::getPatternStart($match, false); - $tmpLength = Pattern::getPatternLength($match); - - // special handling of default entry - if ($tmpLength === 0) { - $tmpStart = str_repeat('z', 32); - } - - if (!isset($data[$tmpStart])) { - $data[$tmpStart] = array(); - } - - $quoterHelper = new Quoter(); - $match = $quoterHelper->pregQuote($match); - - // Check if the pattern contains digits - in this case we replace them with a digit regular expression, - // so that very similar patterns (e.g. only with different browser version numbers) can be compressed. - // This helps to speed up the first (and most expensive) part of the pattern search a lot. - if (strpbrk($match, '0123456789') !== false) { - $compressedPattern = preg_replace('/\d/', '[\d]', $match); - - if (!in_array($compressedPattern, $data[$tmpStart])) { - $data[$tmpStart][] = $compressedPattern; - } - } else { - $data[$tmpStart][] = $match; - } - } - - unset($matches); - - // write optimized file (grouped by the first character of the has, generated from the pattern - // start) with multiple patterns joined by tabs. this is to speed up loading of the data (small - // array with pattern strings instead of an large array with single patterns) and also enables - // us to search for multiple patterns in one preg_match call for a fast first search - // (3-10 faster), followed by a detailed search for each single pattern. - foreach ($data as $tmpStart => $tmpPatterns) { - $patterns = array(); - - for ($i = 0, $j = ceil(count($tmpPatterns) / $this->joinPatterns); $i < $j; $i++) { - $tmpJoinPatterns = implode( - "\t", - array_slice($tmpPatterns, ($i * $this->joinPatterns), $this->joinPatterns) - ); - - $patterns[] = $tmpJoinPatterns; - } - - $this->cache->setItem('browscap.patterns.' . $tmpStart, $patterns, true); - } - - unset($data); - - return true; - } - - /** - * formats the name of a property - * - * @param string $value - * @param string $property - * - * @return string - */ - private function formatPropertyValue($value, $property) - { - $valueOutput = $value; - $propertyHolder = new PropertyHolder(); - - switch ($propertyHolder->getPropertyType($property)) { - case PropertyHolder::TYPE_BOOLEAN: - if (true === $value || $value === 'true' || $value === '1') { - $valueOutput = 'true'; - } elseif (false === $value || $value === 'false' || $value === '') { - $valueOutput = 'false'; - } else { - $valueOutput = ''; - } - break; - case PropertyHolder::TYPE_IN_ARRAY: - try { - $valueOutput = $propertyHolder->checkValueInArray($property, $value); - } catch (\InvalidArgumentException $ex) { - $valueOutput = ''; - } - break; - default: - // nothing t do here - break; - } - - return $valueOutput; - } } diff --git a/src/IniParser/IniParser.php b/src/IniParser/IniParser.php new file mode 100644 index 00000000..b13cc720 --- /dev/null +++ b/src/IniParser/IniParser.php @@ -0,0 +1,197 @@ + + * @author Thomas Müller + * @copyright Copyright (c) 1998-2014 Browser Capabilities Project + * @version 3.0 + * @license http://www.opensource.org/licenses/MIT MIT License + * @link https://github.com/browscap/browscap-php/ + */ +class IniParser +{ + /** + * Number of pattern to combine for a faster regular expression search. + * + * @important The number of patterns that can be processed in one step + * is limited by the internal regular expression limits. + * @var int + */ + private $joinPatterns = 100; + + /** + * Creates new ini part cache files + * + * @param string $content + * + * @return \Generator + */ + public function createIniParts($content) + { + // get all patterns from the ini file in the correct order, + // so that we can calculate with index number of the resulting array, + // which part to use when the ini file is splitted into its sections. + preg_match_all('/(?<=\[)(?:[^\r\n]+)(?=\])/m', $content, $patternpositions); + $patternpositions = $patternpositions[0]; + + // split the ini file into sections and save the data in one line with a hash of the beloging + // pattern (filtered in the previous step) + $iniParts = preg_split('/\[[^\r\n]+\]/', $content); + $contents = array(); + + $propertyHolder = new PropertyHolder(); + $propertyFormatter = new PropertyFormatter(); + $propertyFormatter->setPropertyHolder($propertyHolder); + + foreach ($patternpositions as $position => $pattern) { + $pattern = strtolower($pattern); + $patternhash = Pattern::getHashForParts($pattern); + + if (!isset($contents[$patternhash])) { + $contents[$patternhash] = array(); + } + + $browserProperties = parse_ini_string($iniParts[($position + 1)]); + + foreach (array_keys($browserProperties) as $property) { + $browserProperties[$property] = $propertyFormatter->formatPropertyValue( + $browserProperties[$property], + $property + ); + } + // the position has to be moved by one, because the header of the ini file + // is also returned as a part + $contents[$patternhash][] = json_encode( + $browserProperties, + JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP + ); + } + + unset($patternpositions); + unset($iniParts); + + foreach ($contents as $patternhash => $content) { + yield array($patternhash => $content); + } + + yield false; + } + + /** + * Creates new pattern cache files + * + * @param string $content + * + * @return \Generator + */ + public function createPatterns($content) + { + // get all relevant patterns from the INI file + // - containing "*" or "?" + // - not containing "*" or "?", but not having a comment + preg_match_all( + '/(?<=\[)(?:[^\r\n]*[?*][^\r\n]*)(?=\])|(?<=\[)(?:[^\r\n*?]+)(?=\])(?![^\[]*Comment=)/m', + $content, + $matches + ); + + if (empty($matches[0]) || !is_array($matches[0])) { + yield false; + return; + } + + // build an array to structure the data. this requires some memory, but we need this step to be able to + // sort the data in the way we need it (see below). + $data = array(); + + foreach ($matches[0] as $match) { + $pattern = strtolower($match); + $patternhash = Pattern::getHashForPattern($pattern, false); + + if (!isset($data[$patternhash])) { + $data[$patternhash] = array(); + } + + $quoterHelper = new Quoter(); + $pattern = $quoterHelper->pregQuote($pattern); + + // Check if the pattern contains digits - in this case we replace them with a digit regular expression, + // so that very similar patterns (e.g. only with different browser version numbers) can be compressed. + // This helps to speed up the first (and most expensive) part of the pattern search a lot. + if (strpbrk($pattern, '0123456789') !== false) { + $compressedPattern = preg_replace('/\d/', '[\d]', $pattern); + + if (!isset($data[$patternhash][$compressedPattern])) { + $data[$patternhash][$compressedPattern] = $compressedPattern; + } + } else { + $data[$patternhash][] = $pattern; + } + } + + unset($matches); + + // write optimized file (grouped by the first character of the has, generated from the pattern + // start) with multiple patterns joined by tabs. this is to speed up loading of the data (small + // array with pattern strings instead of an large array with single patterns) and also enables + // us to search for multiple patterns in one preg_match call for a fast first search + // (3-10 faster), followed by a detailed search for each single pattern. + foreach ($data as $patternhash => $tmpPatterns) { + $patterns = array(); + + for ($i = 0, $j = ceil(count($tmpPatterns) / $this->joinPatterns); $i < $j; $i++) { + $tmpJoinPatterns = implode( + "\t", + array_slice($tmpPatterns, ($i * $this->joinPatterns), $this->joinPatterns) + ); + + $patterns[] = $tmpJoinPatterns; + } + + yield array($patternhash => $patterns); + } + + unset($data); + + yield false; + } +} diff --git a/src/Parser/Helper/GetPattern.php b/src/Parser/Helper/GetPattern.php index 78205287..3c141050 100644 --- a/src/Parser/Helper/GetPattern.php +++ b/src/Parser/Helper/GetPattern.php @@ -118,7 +118,7 @@ public function getLogger() */ public function getPatterns($userAgent) { - $starts = Pattern::getPatternStart($userAgent, true); + $starts = Pattern::getHashForPattern($userAgent, true); // add special key to fall back to the default browser $starts[] = str_repeat('z', 32); diff --git a/src/Parser/Helper/Pattern.php b/src/Parser/Helper/Pattern.php index d3006a24..1677f84d 100644 --- a/src/Parser/Helper/Pattern.php +++ b/src/Parser/Helper/Pattern.php @@ -67,7 +67,7 @@ class Pattern * @param boolean $variants * @return string|array */ - public static function getPatternStart($pattern, $variants = false) + public static function getHashForPattern($pattern, $variants = false) { $regex = '/^([^\*\?\s\r\n\\\\]+).*$/'; $pattern = substr($pattern, 0, 32); @@ -96,6 +96,18 @@ public static function getPatternStart($pattern, $variants = false) return md5($string); } + /** + * returns a hash for one pattern + * + * @param $pattern + * + * @return string + */ + public static function getHashForParts($pattern) + { + return md5($pattern); + } + /** * Gets the minimum length of the patern (used in the getPatterns() method to * check against the user agent length) diff --git a/src/Parser/Ini.php b/src/Parser/Ini.php index e048e4fa..b81565e5 100644 --- a/src/Parser/Ini.php +++ b/src/Parser/Ini.php @@ -33,11 +33,9 @@ use BrowscapPHP\Cache\BrowscapCache; use BrowscapPHP\Data\PropertyHolder; use BrowscapPHP\Formatter\FormatterInterface; -use BrowscapPHP\Helper\Converter; use BrowscapPHP\Helper\Quoter; use BrowscapPHP\Parser\Helper\GetPatternInterface; use BrowscapPHP\Parser\Helper\Pattern; -use BrowscapPHP\Parser\Helper\SubKey; use Psr\Log\LoggerInterface; /** @@ -182,10 +180,6 @@ public function getBrowser($userAgent) { $userAgent = strtolower($userAgent); $formatter = null; - $starts = Pattern::getPatternStart($userAgent, true); - - // add special key to fall back to the default browser - $starts[] = str_repeat('z', 32); foreach ($this->getHelper()->getPatterns($userAgent) as $patterns) { $usedMatch = '/^(?:'.str_replace("\t", ')|(', $patterns).')$/i'; @@ -295,7 +289,7 @@ private function getSettings($pattern, array $settings = array()) private function getIniPart($pattern) { $pattern = strtolower($pattern); - $patternhash = md5($pattern); + $patternhash = Pattern::getHashForParts($pattern); if (!$this->getCache()->hasItem('browscap.iniparts.'.$patternhash, true)) { return array(); diff --git a/src/Parser/IniParser.php b/src/Parser/IniParser.php deleted file mode 100644 index 7c7d5700..00000000 --- a/src/Parser/IniParser.php +++ /dev/null @@ -1,225 +0,0 @@ - - * @copyright Copyright (c) 1998-2014 Browser Capabilities Project - * @version 3.0 - * @license http://www.opensource.org/licenses/MIT MIT License - * @link https://github.com/browscap/browscap-php/ - */ -class IniParser -{ - /** - * @var string - */ - private $filename = null; - - /** - * @var bool - */ - private $shouldSort = false; - - /** - * @var array - */ - private $data = array(); - - /** - * @var array - */ - private $fileLines = array(); - - /** - * @param string $filename - */ - public function __construct($filename = null) - { - $this->filename = $filename; - } - - /** - * @param bool $shouldSort - * - * @return IniParser - */ - public function setShouldSort($shouldSort) - { - $this->shouldSort = (bool) $shouldSort; - - return $this; - } - - /** - * @return bool - */ - public function shouldSort() - { - return $this->shouldSort; - } - - /** - * @return array - */ - public function getParsed() - { - return $this->data; - } - - /** - * @return string - */ - public function getFilename() - { - return $this->filename; - } - - /** - * @return array - * @throws \InvalidArgumentException - */ - private function getLinesFromFile() - { - $filename = $this->filename; - - if (!file_exists($filename)) { - throw new InvalidArgumentException("File not found: {$filename}"); - } - - return file($filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); - } - - /** - * @param string[] $fileLines - */ - public function setFileLines(array $fileLines) - { - $this->fileLines = $fileLines; - } - - /** - * @return array - */ - private function getFileLines() - { - if (!$this->fileLines) { - $fileLines = $this->getLinesFromFile(); - } else { - $fileLines = $this->fileLines; - } - - return $fileLines; - } - - /** - * @return array - * @throws \RuntimeException - */ - public function parse() - { - $fileLines = $this->getFileLines(); - - $data = array(); - - $currentSection = ''; - $currentDivision = ''; - - for ($line = 0, $count = count($fileLines); $line < $count; $line++) { - $currentLine = ($fileLines[$line]); - $currentLineLength = strlen($currentLine); - - if ($currentLineLength == 0) { - continue; - } - - if (substr($currentLine, 0, 40) == ';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;') { - $currentDivision = trim(substr($currentLine, 41)); - continue; - } - - // We only skip comments that *start* with semicolon - if ($currentLine[0] == ';') { - continue; - } - - if ($currentLine[0] == '[') { - $currentSection = substr($currentLine, 1, ($currentLineLength - 2)); - continue; - } - - $bits = explode("=", $currentLine); - - if (count($bits) > 2) { - throw new \RuntimeException("Too many equals in line: {$currentLine}, in Division: {$currentDivision}"); - } - - if (count($bits) < 2) { - $bits[1] = ''; - } - - $data[$currentSection][$bits[0]] = trim($bits[1], '"'); - $data[$currentSection]['Division'] = $currentDivision; - } - - if ($this->shouldSort()) { - $data = $this->sortArrayAndChildArrays($data); - } - - $this->data = $data; - - return $data; - } - - /** - * @param array $array - * - * @return array - */ - private function sortArrayAndChildArrays(array $array) - { - ksort($array); - - foreach ($array as $key => $childArray) { - if (is_array($childArray) && !empty($childArray)) { - $array[$key] = $this->sortArrayAndChildArrays($childArray); - } - } - - return $array; - } -} diff --git a/src/Util/Logfile/ReaderCollection.php b/src/Util/Logfile/ReaderCollection.php index fa4f2737..6bcd0d03 100644 --- a/src/Util/Logfile/ReaderCollection.php +++ b/src/Util/Logfile/ReaderCollection.php @@ -53,11 +53,11 @@ class ReaderCollection implements ReaderInterface /** * adds a new reader to this collection * - * @param \BrowscapPHP\Util\Logfile\AbstractReader $reader + * @param \BrowscapPHP\Util\Logfile\ReaderInterface $reader * * @return \BrowscapPHP\Util\Logfile\ReaderCollection */ - public function addReader(AbstractReader $reader) + public function addReader(ReaderInterface $reader) { $this->readers[] = $reader; diff --git a/src/Util/Logfile/ReaderFactory.php b/src/Util/Logfile/ReaderFactory.php index 3a84f4d1..7138a02b 100644 --- a/src/Util/Logfile/ReaderFactory.php +++ b/src/Util/Logfile/ReaderFactory.php @@ -61,7 +61,7 @@ public static function factory() } /** - * @return ReaderInterface[] + * @return \BrowscapPHP\Util\Logfile\ReaderInterface[] */ private static function getReaders() { diff --git a/tests/BrowscapTest.php b/tests/BrowscapTest.php index 00b28afe..fe4e7239 100644 --- a/tests/BrowscapTest.php +++ b/tests/BrowscapTest.php @@ -62,6 +62,7 @@ public function setUp() */ public function testSetGetFormatter() { + /** @var \BrowscapPHP\Formatter\PhpGetBrowser $formatter */ $formatter = $this->getMock('\BrowscapPHP\Formatter\PhpGetBrowser', array(), array(), '', false); self::assertSame($this->object, $this->object->setFormatter($formatter)); @@ -81,6 +82,7 @@ public function testGetCache() */ public function testSetGetCache() { + /** @var \BrowscapPHP\Cache\BrowscapCache $cache */ $cache = $this->getMock('\BrowscapPHP\Cache\BrowscapCache', array(), array(), '', false); self::assertSame($this->object, $this->object->setCache($cache)); @@ -92,6 +94,7 @@ public function testSetGetCache() */ public function testSetGetCacheWithAdapter() { + /** @var \WurflCache\Adapter\Memory $cache */ $cache = $this->getMock('\WurflCache\Adapter\Memory', array(), array(), '', false); self::assertSame($this->object, $this->object->setCache($cache)); diff --git a/tests/Cache/BrowscapCacheTest.php b/tests/Cache/BrowscapCacheTest.php index 5bff625e..78961ca4 100644 --- a/tests/Cache/BrowscapCacheTest.php +++ b/tests/Cache/BrowscapCacheTest.php @@ -50,6 +50,7 @@ class BrowscapCacheTest extends \PHPUnit_Framework_TestCase */ public function setUp() { + /** @var \WurflCache\Adapter\File $adapter */ $adapter = $this->getMock('\WurflCache\Adapter\File', array(), array(), '', false); $this->object = new BrowscapCache($adapter); @@ -60,6 +61,7 @@ public function setUp() */ public function testSetGetCacheAdapter() { + /** @var \WurflCache\Adapter\Memcache $adapter */ $adapter = $this->getMock('\WurflCache\Adapter\Memcache', array(), array(), '', false); self::assertSame($this->object, $this->object->setCacheAdapter($adapter)); @@ -79,6 +81,7 @@ public function testSetUpdateInterval() */ public function testGetVersionNotCached() { + /** @var \WurflCache\Adapter\Memcache $adapter */ $adapter = $this->getMock('\WurflCache\Adapter\Memcache', array('hasItem', 'getItem'), array(), '', false); $adapter ->expects(self::once()) diff --git a/tests/Formatter/PhpGetBrowserTest.php b/tests/Formatter/PhpGetBrowserTest.php index 313a48b9..bc4d8bde 100644 --- a/tests/Formatter/PhpGetBrowserTest.php +++ b/tests/Formatter/PhpGetBrowserTest.php @@ -39,7 +39,7 @@ class PhpGetBrowserTest extends \PHPUnit_Framework_TestCase { /** - * @var \Browscap\Fotmatter\PhpGetBrowser + * @var \BrowscapPHP\Formatter\PhpGetBrowser */ private $object = null; diff --git a/tests/Helper/ConverterTest.php b/tests/Helper/ConverterTest.php index 1510a7c6..ac16e3fa 100644 --- a/tests/Helper/ConverterTest.php +++ b/tests/Helper/ConverterTest.php @@ -61,6 +61,7 @@ public function setUp() */ public function testSetGetLogger() { + /** @var \Monolog\Logger $logger */ $logger = $this->getMock('\Monolog\Logger', array(), array(), '', false); self::assertSame($this->object, $this->object->setLogger($logger)); @@ -72,6 +73,7 @@ public function testSetGetLogger() */ public function testSetGetCache() { + /** @var \BrowscapPHP\Cache\BrowscapCache $cache */ $cache = $this->getMock('\BrowscapPHP\Cache\BrowscapCache', array(), array(), '', false); self::assertSame($this->object, $this->object->setCache($cache)); @@ -85,6 +87,7 @@ public function testSetGetFilesystem() { self::assertInstanceOf('\BrowscapPHP\Helper\Filesystem', $this->object->getFilesystem()); + /** @var \BrowscapPHP\Helper\Filesystem $file */ $file = $this->getMock('\BrowscapPHP\Helper\Filesystem', array(), array(), '', false); self::assertSame($this->object, $this->object->setFilesystem($file)); diff --git a/tests/Helper/IniLoaderTest.php b/tests/Helper/IniLoaderTest.php index 06b0fca1..83276a95 100644 --- a/tests/Helper/IniLoaderTest.php +++ b/tests/Helper/IniLoaderTest.php @@ -62,6 +62,7 @@ public function testGetLoader() */ public function testSetGetLoader() { + /** @var \FileLoader\Loader $loader */ $loader = $this->getMock('\FileLoader\Loader', array(), array(), '', false); self::assertSame($this->object, $this->object->setLoader($loader)); @@ -76,6 +77,7 @@ public function testSetGetLoader() */ public function testSetGetLogger() { + /** @var \Monolog\Logger $logger */ $logger = $this->getMock('\Monolog\Logger', array(), array(), '', false); self::assertSame($this->object, $this->object->setLogger($logger)); diff --git a/tests/Parser/Helper/GetPatternTest.php b/tests/Parser/Helper/GetPatternTest.php index 93172310..fbfde95f 100644 --- a/tests/Parser/Helper/GetPatternTest.php +++ b/tests/Parser/Helper/GetPatternTest.php @@ -58,6 +58,7 @@ public function setUp() */ public function testSetGetCache() { + /** @var \BrowscapPHP\Cache\BrowscapCache $cache */ $cache = $this->getMock('\BrowscapPHP\Cache\BrowscapCache', array(), array(), '', false); self::assertSame($this->object, $this->object->setCache($cache)); @@ -69,6 +70,7 @@ public function testSetGetCache() */ public function testSetGetLogger() { + /** @var \Monolog\Logger $logger */ $logger = $this->getMock('\Monolog\Logger', array(), array(), '', false); self::assertSame($this->object, $this->object->setLogger($logger)); @@ -97,6 +99,7 @@ public function testGetPatterns() ) ); + /** @var \BrowscapPHP\Cache\BrowscapCache $cache */ $cache = $this->getMock('\BrowscapPHP\Cache\BrowscapCache', array('getItem'), array(), '', false); $cache ->expects(self::never()) @@ -106,6 +109,7 @@ public function testGetPatterns() $this->object->setCache($cache); + /** @var \Monolog\Logger $logger */ $logger = $this->getMock('\Monolog\Logger', array(), array(), '', false); $this->object->setLogger($logger); diff --git a/tests/Parser/Helper/PatternTest.php b/tests/Parser/Helper/PatternTest.php index 4508040b..a91ff685 100644 --- a/tests/Parser/Helper/PatternTest.php +++ b/tests/Parser/Helper/PatternTest.php @@ -74,7 +74,7 @@ public function testGetPatternStartWithoutVariants() CssVersion=0 AolVersion=0 '; - self::assertSame('235cb78c730de50ce5ba6a0c1784b16b', Pattern::getPatternStart($pattern, false)); + self::assertSame('235cb78c730de50ce5ba6a0c1784b16b', Pattern::getHashForPattern($pattern, false)); } /** @@ -126,7 +126,7 @@ public function testGetPatternStartWithVariants() 9 => 'd41d8cd98f00b204e9800998ecf8427e', ); - self::assertSame($expected, Pattern::getPatternStart($pattern, true)); + self::assertSame($expected, Pattern::getHashForPattern($pattern, true)); } /** @@ -136,4 +136,15 @@ public function testGetPatternLength() { self::assertSame(4, Pattern::getPatternLength('abcd')); } + + /** + * + */ + public function testGetHashForParts() + { + self::assertSame( + '529f1ddb64ea27d5cc6fc8ce8048d9e7', + Pattern::getHashForParts('mozilla/5.0 (*linux i686*rv:0.9*) gecko*') + ); + } } diff --git a/tests/Parser/IniParserTest.php b/tests/Parser/IniParserTest.php deleted file mode 100644 index e0c0cb71..00000000 --- a/tests/Parser/IniParserTest.php +++ /dev/null @@ -1,67 +0,0 @@ - - * @copyright Copyright (c) 2006-2012 Jonathan Stoppani - * @version 1.0 - * @license http://www.opensource.org/licenses/MIT MIT License - * @link https://github.com/GaretJax/BrowscapPHP/ - */ -class IniParserTest extends \PHPUnit_Framework_TestCase -{ - const STORAGE_DIR = 'storage'; - - /** - * @var \org\bovigo\vfs\vfsStreamDirectory - */ - private $root = null; - - public function setUp() - { - $this->root = vfsStream::setup(self::STORAGE_DIR); - } - - /** - * - */ - public function testShouldSort() - { - $file = vfsStream::url(self::STORAGE_DIR . DIRECTORY_SEPARATOR . 'test.ini'); - - $object = new IniParser($file); - - self::assertSame($object, $object->setShouldSort(true)); - self::assertTrue($object->shouldSort()); - self::assertSame($file, $object->getFilename()); - } -} diff --git a/tests/Parser/IniTest.php b/tests/Parser/IniTest.php index 5add38ab..0ce056c0 100644 --- a/tests/Parser/IniTest.php +++ b/tests/Parser/IniTest.php @@ -58,6 +58,7 @@ public function setUp() */ public function testSetGetHelper() { + /** @var \BrowscapPHP\Parser\Helper\GetPattern $helper */ $helper = $this->getMock('\BrowscapPHP\Parser\Helper\GetPattern', array(), array(), '', false); self::assertSame($this->object, $this->object->setHelper($helper)); @@ -69,6 +70,7 @@ public function testSetGetHelper() */ public function testSetGetFormatter() { + /** @var \BrowscapPHP\Formatter\PhpGetBrowser $formatter */ $formatter = $this->getMock('\BrowscapPHP\Formatter\PhpGetBrowser', array(), array(), '', false); self::assertSame($this->object, $this->object->setFormatter($formatter)); @@ -80,6 +82,7 @@ public function testSetGetFormatter() */ public function testSetGetCache() { + /** @var \BrowscapPHP\Cache\BrowscapCache $cache */ $cache = $this->getMock('\BrowscapPHP\Cache\BrowscapCache', array(), array(), '', false); self::assertSame($this->object, $this->object->setCache($cache)); @@ -91,6 +94,7 @@ public function testSetGetCache() */ public function testSetGetLogger() { + /** @var \Monolog\Logger $logger */ $logger = $this->getMock('\Monolog\Logger', array(), array(), '', false); self::assertSame($this->object, $this->object->setLogger($logger)); diff --git a/tests/Util/LogFile/ReaderCollectionTest.php b/tests/Util/LogFile/ReaderCollectionTest.php index fa4b6e31..30fdd146 100644 --- a/tests/Util/LogFile/ReaderCollectionTest.php +++ b/tests/Util/LogFile/ReaderCollectionTest.php @@ -58,6 +58,7 @@ public function setUp() */ public function testaddReader() { + /** @var \BrowscapPHP\Util\Logfile\ApacheCommonLogFormatReader $reader */ $reader = $this->getMock('\BrowscapPHP\Util\Logfile\ApacheCommonLogFormatReader', array(), array(), '', false); self::assertSame($this->object, $this->object->addReader($reader)); @@ -68,6 +69,7 @@ public function testaddReader() */ public function testTestSuccessFull() { + /** @var \BrowscapPHP\Util\Logfile\ApacheCommonLogFormatReader $reader */ $reader = $this->getMock( '\BrowscapPHP\Util\Logfile\ApacheCommonLogFormatReader', array('test'), @@ -91,6 +93,7 @@ public function testTestSuccessFull() */ public function testTestNotSuccessFull() { + /** @var \BrowscapPHP\Util\Logfile\ApacheCommonLogFormatReader $reader */ $reader = $this->getMock( '\BrowscapPHP\Util\Logfile\ApacheCommonLogFormatReader', array('test'), From 737688ecb84fab49596d621c4e258988b64ad791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Tue, 8 Sep 2015 22:48:42 +0200 Subject: [PATCH 08/24] revert change --- src/Helper/Converter.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Helper/Converter.php b/src/Helper/Converter.php index d5a9fe56..cc03096e 100644 --- a/src/Helper/Converter.php +++ b/src/Helper/Converter.php @@ -33,6 +33,7 @@ use BrowscapPHP\Cache\BrowscapCache; use BrowscapPHP\Exception\FileNotFoundException; use BrowscapPHP\IniParser\IniParser; +use BrowscapPHP\Parser\Helper\SubKey; use BrowscapPHP\Parser\Ini; use Psr\Log\LoggerInterface; From c2fb053dd6e49469becd88a722d37fb819f479e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Tue, 8 Sep 2015 22:54:38 +0200 Subject: [PATCH 09/24] revert change --- src/Browscap.php | 9 +- src/Helper/Converter.php | 197 +++++++++++++++++++++++++++++ src/Parser/Helper/GetPattern.php | 36 ++++-- src/Parser/Helper/SubKey.php | 30 +++++ src/Parser/Ini.php | 30 +++-- tests/Parser/Helper/SubKeyTest.php | 12 +- 6 files changed, 280 insertions(+), 34 deletions(-) diff --git a/src/Browscap.php b/src/Browscap.php index d441433e..0964b7db 100644 --- a/src/Browscap.php +++ b/src/Browscap.php @@ -336,8 +336,6 @@ public function convertFile($iniFile) * reads and parses an ini string and writes the results into the cache * * @param string $iniString - * - * @throws \BrowscapPHP\Exception */ public function convertString($iniString) { @@ -346,13 +344,8 @@ public function convertString($iniString) $converter ->setLogger($this->getLogger()) ->setCache($this->getCache()) + ->convertString($iniString) ; - - try { - $converter->convertString($iniString); - } catch (Helper\Exception $e) { - throw new Exception('an error occured while converting the data string into the cache', 0, $e); - } } /** diff --git a/src/Helper/Converter.php b/src/Helper/Converter.php index cc03096e..ef232dcb 100644 --- a/src/Helper/Converter.php +++ b/src/Helper/Converter.php @@ -255,4 +255,201 @@ public function storeVersion() return $this; } + + /** + * Creates new ini part cache files + * @param string $content + */ + private function createIniParts($content) + { + // get all patterns from the ini file in the correct order, + // so that we can calculate with index number of the resulting array, + // which part to use when the ini file is splitted into its sections. + preg_match_all('/(?<=\[)(?:[^\r\n]+)(?=\])/m', $content, $patternpositions); + $patternpositions = $patternpositions[0]; + + // split the ini file into sections and save the data in one line with a hash of the beloging + // pattern (filtered in the previous step) + $iniParts = preg_split('/\[[^\r\n]+\]/', $content); + $contents = array(); + foreach ($patternpositions as $position => $pattern) { + $pattern = strtolower($pattern); + $patternhash = md5($pattern); + $subkey = SubKey::getPatternCacheSubkey($patternhash); + + if (!isset($contents[$subkey])) { + $contents[$subkey] = array(); + } + + $browserProperties = parse_ini_string($iniParts[($position + 1)]); + + foreach (array_keys($browserProperties) as $property) { + $browserProperties[$property] = $this->formatPropertyValue( + $browserProperties[$property], + $property + ); + } + // the position has to be moved by one, because the header of the ini file + // is also returned as a part + $contents[$subkey][] = $patternhash.json_encode( + $browserProperties, + JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP + ); + } + + unset($patternpositions); + unset($iniParts); + + $subkeys = array_flip(SubKey::getAllIniPartCacheSubKeys()); + foreach ($contents as $subkey => $content) { + $subkey = (string) $subkey; + $this->getCache()->setItem('browscap.iniparts.' . $subkey, $content, true); + unset($subkeys[$subkey]); + } + + foreach (array_keys($subkeys) as $subkey) { + $this->getCache()->setItem('browscap.iniparts.' . $subkey, array(), true); + } + } + + /** + * Creates new pattern cache files + * + * @param string $content + * + * @return bool + */ + private function createPatterns($content) + { + // get all relevant patterns from the INI file + // - containing "*" or "?" + // - not containing "*" or "?", but not having a comment + preg_match_all( + '/(?<=\[)(?:[^\r\n]*[?*][^\r\n]*)(?=\])|(?<=\[)(?:[^\r\n*?]+)(?=\])(?![^\[]*Comment=)/m', + $content, + $matches + ); + + if (empty($matches[0]) || !is_array($matches[0])) { + return false; + } + + // build an array to structure the data. this requires some memory, but we need this step to be able to + // sort the data in the way we need it (see below). + $data = array(); + + foreach ($matches[0] as $match) { + $match = strtolower($match); + + // get the first characters for a fast search + $tmpStart = Pattern::getPatternStart($match); + $tmpLength = Pattern::getPatternLength($match); + + // special handling of default entry + if ($tmpLength === 0) { + $tmpStart = str_repeat('z', 32); + } + + if (!isset($data[$tmpStart])) { + $data[$tmpStart] = array(); + } + + $quoterHelper = new Quoter(); + $match = $quoterHelper->pregQuote($match); + + // Check if the pattern contains digits - in this case we replace them with a digit regular expression, + // so that very similar patterns (e.g. only with different browser version numbers) can be compressed. + // This helps to speed up the first (and most expensive) part of the pattern search a lot. + if (strpbrk($match, '0123456789') !== false) { + $compressedPattern = preg_replace('/\d/', '[\d]', $match); + if (!in_array($compressedPattern, $data[$tmpStart])) { + $data[$tmpStart][] = $compressedPattern; + } + } else { + $data[$tmpStart][] = $match; + } + //$data[$tmpStart][] = $match; + } + + unset($matches); + + // write optimized file (grouped by the first character of the has, generated from the pattern + // start) with multiple patterns joined by tabs. this is to speed up loading of the data (small + // array with pattern strings instead of an large array with single patterns) and also enables + // us to search for multiple patterns in one preg_match call for a fast first search + // (3-10 faster), followed by a detailed search for each single pattern. + $contents = array(); + foreach ($data as $tmpStart => $tmpPatterns) { + for ($i = 0, $j = ceil(count($tmpPatterns) / $this->joinPatterns); $i < $j; $i++) { + $tmpJoinPatterns = implode( + "\t", + array_slice($tmpPatterns, ($i * $this->joinPatterns), $this->joinPatterns) + ); + + $tmpSubkey = SubKey::getPatternCacheSubkey($tmpStart); + + if (!isset($contents[$tmpSubkey])) { + $contents[$tmpSubkey] = array(); + } + + $contents[$tmpSubkey][] = $tmpStart.' '.$tmpJoinPatterns; + } + } + + unset($data); + + // write cache files. important: also write empty cache files for + // unused patterns, so that the regeneration is not unnecessarily + // triggered by the getPatterns() method. + $subkeys = array_flip(SubKey::getAllPatternCacheSubkeys()); + foreach ($contents as $subkey => $content) { + $subkey = (string) $subkey; + $this->cache->setItem('browscap.patterns.' . $subkey, $content, true); + unset($subkeys[$subkey]); + } + + foreach (array_keys($subkeys) as $subkey) { + $this->getCache()->setItem('browscap.patterns.' . $subkey, array(), true); + } + + return true; + } + + /** + * formats the name of a property + * + * @param string $value + * @param string $property + * + * @return string + */ + private function formatPropertyValue($value, $property) + { + $valueOutput = $value; + $propertyHolder = new PropertyHolder(); + + switch ($propertyHolder->getPropertyType($property)) { + case PropertyHolder::TYPE_BOOLEAN: + if (true === $value || $value === 'true' || $value === '1') { + $valueOutput = 'true'; + } elseif (false === $value || $value === 'false' || $value === '') { + $valueOutput = 'false'; + } else { + $valueOutput = ''; + } + break; + case PropertyHolder::TYPE_IN_ARRAY: + try { + $valueOutput = $propertyHolder->checkValueInArray($property, $value); + } catch (\InvalidArgumentException $ex) { + $valueOutput = ''; + } + break; + default: + // nothing t do here + break; + } + + return $valueOutput; + } } diff --git a/src/Parser/Helper/GetPattern.php b/src/Parser/Helper/GetPattern.php index 3c141050..accfff68 100644 --- a/src/Parser/Helper/GetPattern.php +++ b/src/Parser/Helper/GetPattern.php @@ -119,6 +119,7 @@ public function getLogger() public function getPatterns($userAgent) { $starts = Pattern::getHashForPattern($userAgent, true); + $length = Pattern::getPatternLength($userAgent); // add special key to fall back to the default browser $starts[] = str_repeat('z', 32); @@ -126,27 +127,38 @@ public function getPatterns($userAgent) // get patterns, first for the given browser and if that is not found, // for the default browser (with a special key) foreach ($starts as $tmpStart) { - if (!$this->getCache()->hasItem('browscap.patterns.'.$tmpStart, true)) { - $this->getLogger()->debug('cache key "browscap.patterns.'.$tmpStart.'" not found'); - continue; - } - - $success = null; + $tmpSubkey = SubKey::getPatternCacheSubkey($tmpStart); + $success = null; - $patterns = $this->getCache()->getItem('browscap.patterns.'.$tmpStart, true, $success); + $file = $this->getCache()->getItem('browscap.patterns.'.$tmpSubkey, true, $success); if (!$success) { - $this->getLogger()->debug('cache key "browscap.patterns.'.$tmpStart.'" not found'); + $this->getLogger()->debug('cache key "browscap.patterns.'.$tmpSubkey.'" not found'); continue; } - if (!is_array($patterns) || !count($patterns)) { - $this->getLogger()->debug('cache key "browscap.patterns.'.$tmpStart.'" was empty'); + if (!is_array($file) || !count($file)) { + $this->getLogger()->debug('cache key "browscap.patterns.'.$tmpSubkey.'" was empty'); continue; } - foreach ($patterns as $tmpJoinPatterns) { - yield trim($tmpJoinPatterns); + $found = false; + + foreach ($file as $buffer) { + $tmpBuffer = substr($buffer, 0, 32); + if ($tmpBuffer === $tmpStart) { + // get length of the pattern + $len = (int) strstr(substr($buffer, 33, 4), ' ', true); + + // the user agent must be longer than the pattern without place holders + if ($len <= $length) { + list(, $patterns) = explode(' ', $buffer, 2); + yield trim($patterns); + } + $found = true; + } elseif ($found === true) { + break; + } } } diff --git a/src/Parser/Helper/SubKey.php b/src/Parser/Helper/SubKey.php index 2d24f111..e046fe9f 100644 --- a/src/Parser/Helper/SubKey.php +++ b/src/Parser/Helper/SubKey.php @@ -44,6 +44,36 @@ */ class SubKey { + /** + * Gets the subkey for the pattern cache file, generated from the given string + * + * @param string $string + * @return string + */ + public static function getPatternCacheSubkey($string) + { + return $string[0].$string[1]; + } + + /** + * Gets all subkeys for the pattern cache files + * + * @return array + */ + public static function getAllPatternCacheSubkeys() + { + $subkeys = array(); + $chars = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'); + + foreach ($chars as $char_one) { + foreach ($chars as $char_two) { + $subkeys[] = $char_one . $char_two; + } + } + + return $subkeys; + } + /** * Gets the sub key for the ini parts cache file, generated from the given string * diff --git a/src/Parser/Ini.php b/src/Parser/Ini.php index b81565e5..1980a6e6 100644 --- a/src/Parser/Ini.php +++ b/src/Parser/Ini.php @@ -36,6 +36,7 @@ use BrowscapPHP\Helper\Quoter; use BrowscapPHP\Parser\Helper\GetPatternInterface; use BrowscapPHP\Parser\Helper\Pattern; +use BrowscapPHP\Parser\Helper\SubKey; use Psr\Log\LoggerInterface; /** @@ -182,9 +183,8 @@ public function getBrowser($userAgent) $formatter = null; foreach ($this->getHelper()->getPatterns($userAgent) as $patterns) { - $usedMatch = '/^(?:'.str_replace("\t", ')|(', $patterns).')$/i'; $result = preg_match( - $usedMatch, + '/^(?:'.str_replace("\t", ')|(?:', $patterns).')$/i', $userAgent ); @@ -239,7 +239,6 @@ public function getBrowser($userAgent) private function getSettings($pattern, array $settings = array()) { $quoterHelper = new Quoter(); - // The pattern has been pre-quoted on generation to speed up the pattern search, // but for this check we need the unquoted version $unquotedPattern = $quoterHelper->pregUnQuote($pattern); @@ -258,6 +257,8 @@ private function getSettings($pattern, array $settings = array()) $settings['browser_name_regex'] = '/^' . $pattern . '$/'; $settings['browser_name_pattern'] = $unquotedPattern; } + //$settings['browser_name_regex'] = '/^'.$quoterHelper->pregQuote($pattern).'$/'; + //$settings['browser_name_pattern'] = $pattern; } // check if parent pattern set, only keep the first one @@ -290,13 +291,14 @@ private function getIniPart($pattern) { $pattern = strtolower($pattern); $patternhash = Pattern::getHashForParts($pattern); + $subkey = SubKey::getIniPartCacheSubKey($patternhash); - if (!$this->getCache()->hasItem('browscap.iniparts.'.$patternhash, true)) { + if (!$this->getCache()->hasItem('browscap.iniparts.'.$subkey, true)) { return array(); } $success = null; - $file = $this->getCache()->getItem('browscap.iniparts.'.$patternhash, true, $success); + $file = $this->getCache()->getItem('browscap.iniparts.'.$subkey, true, $success); if (!$success) { return array(); @@ -304,16 +306,18 @@ private function getIniPart($pattern) $return = array(); foreach ($file as $buffer) { - $return = json_decode($buffer, true); + if (substr($buffer, 0, 32) === $patternhash) { + $return = json_decode(substr($buffer, 32), true); + + foreach (array_keys($return) as $property) { + $return[$property] = $this->formatPropertyValue( + $return[$property], + $property + ); + } - foreach (array_keys($return) as $property) { - $return[$property] = $this->formatPropertyValue( - $return[$property], - $property - ); + break; } - - break; } return $return; diff --git a/tests/Parser/Helper/SubKeyTest.php b/tests/Parser/Helper/SubKeyTest.php index ec8ddf2b..ee8fd65b 100644 --- a/tests/Parser/Helper/SubKeyTest.php +++ b/tests/Parser/Helper/SubKeyTest.php @@ -43,6 +43,16 @@ class SubKeyTest extends \PHPUnit_Framework_TestCase */ public function testGetPatternCacheSubkey() { - self::markTestSkipped('test removed'); + self::assertSame('ab', SubKey::getPatternCacheSubkey('abcd')); + } + + /** + * + */ + public function testGetAllPatternCacheSubkeys() + { + $result = SubKey::getAllPatternCacheSubkeys(); + self::assertInternalType('array', $result); + self::assertSame(256, count($result)); } } From dc24b3990e9632b82d341a82d142ab1bb29294f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Tue, 8 Sep 2015 23:30:51 +0200 Subject: [PATCH 10/24] update converter --- src/Helper/Converter.php | 104 +----------------------------------- src/IniParser/IniParser.php | 79 +++++++++++++++++++++------ src/Parser/Ini.php | 48 +++-------------- 3 files changed, 72 insertions(+), 159 deletions(-) diff --git a/src/Helper/Converter.php b/src/Helper/Converter.php index ef232dcb..c7423616 100644 --- a/src/Helper/Converter.php +++ b/src/Helper/Converter.php @@ -33,6 +33,7 @@ use BrowscapPHP\Cache\BrowscapCache; use BrowscapPHP\Exception\FileNotFoundException; use BrowscapPHP\IniParser\IniParser; +use BrowscapPHP\Parser\Helper\Pattern; use BrowscapPHP\Parser\Helper\SubKey; use BrowscapPHP\Parser\Ini; use Psr\Log\LoggerInterface; @@ -312,109 +313,6 @@ private function createIniParts($content) } } - /** - * Creates new pattern cache files - * - * @param string $content - * - * @return bool - */ - private function createPatterns($content) - { - // get all relevant patterns from the INI file - // - containing "*" or "?" - // - not containing "*" or "?", but not having a comment - preg_match_all( - '/(?<=\[)(?:[^\r\n]*[?*][^\r\n]*)(?=\])|(?<=\[)(?:[^\r\n*?]+)(?=\])(?![^\[]*Comment=)/m', - $content, - $matches - ); - - if (empty($matches[0]) || !is_array($matches[0])) { - return false; - } - - // build an array to structure the data. this requires some memory, but we need this step to be able to - // sort the data in the way we need it (see below). - $data = array(); - - foreach ($matches[0] as $match) { - $match = strtolower($match); - - // get the first characters for a fast search - $tmpStart = Pattern::getPatternStart($match); - $tmpLength = Pattern::getPatternLength($match); - - // special handling of default entry - if ($tmpLength === 0) { - $tmpStart = str_repeat('z', 32); - } - - if (!isset($data[$tmpStart])) { - $data[$tmpStart] = array(); - } - - $quoterHelper = new Quoter(); - $match = $quoterHelper->pregQuote($match); - - // Check if the pattern contains digits - in this case we replace them with a digit regular expression, - // so that very similar patterns (e.g. only with different browser version numbers) can be compressed. - // This helps to speed up the first (and most expensive) part of the pattern search a lot. - if (strpbrk($match, '0123456789') !== false) { - $compressedPattern = preg_replace('/\d/', '[\d]', $match); - if (!in_array($compressedPattern, $data[$tmpStart])) { - $data[$tmpStart][] = $compressedPattern; - } - } else { - $data[$tmpStart][] = $match; - } - //$data[$tmpStart][] = $match; - } - - unset($matches); - - // write optimized file (grouped by the first character of the has, generated from the pattern - // start) with multiple patterns joined by tabs. this is to speed up loading of the data (small - // array with pattern strings instead of an large array with single patterns) and also enables - // us to search for multiple patterns in one preg_match call for a fast first search - // (3-10 faster), followed by a detailed search for each single pattern. - $contents = array(); - foreach ($data as $tmpStart => $tmpPatterns) { - for ($i = 0, $j = ceil(count($tmpPatterns) / $this->joinPatterns); $i < $j; $i++) { - $tmpJoinPatterns = implode( - "\t", - array_slice($tmpPatterns, ($i * $this->joinPatterns), $this->joinPatterns) - ); - - $tmpSubkey = SubKey::getPatternCacheSubkey($tmpStart); - - if (!isset($contents[$tmpSubkey])) { - $contents[$tmpSubkey] = array(); - } - - $contents[$tmpSubkey][] = $tmpStart.' '.$tmpJoinPatterns; - } - } - - unset($data); - - // write cache files. important: also write empty cache files for - // unused patterns, so that the regeneration is not unnecessarily - // triggered by the getPatterns() method. - $subkeys = array_flip(SubKey::getAllPatternCacheSubkeys()); - foreach ($contents as $subkey => $content) { - $subkey = (string) $subkey; - $this->cache->setItem('browscap.patterns.' . $subkey, $content, true); - unset($subkeys[$subkey]); - } - - foreach (array_keys($subkeys) as $subkey) { - $this->getCache()->setItem('browscap.patterns.' . $subkey, array(), true); - } - - return true; - } - /** * formats the name of a property * diff --git a/src/IniParser/IniParser.php b/src/IniParser/IniParser.php index b13cc720..85d45c39 100644 --- a/src/IniParser/IniParser.php +++ b/src/IniParser/IniParser.php @@ -34,6 +34,7 @@ use BrowscapPHP\Data\PropertyHolder; use BrowscapPHP\Helper\Quoter; use BrowscapPHP\Parser\Helper\Pattern; +use BrowscapPHP\Parser\Helper\SubKey; /** * Ini parser class (compatible with PHP 5.3+) @@ -56,7 +57,7 @@ class IniParser * is limited by the internal regular expression limits. * @var int */ - private $joinPatterns = 100; + CONST COUNT_PATTERN = 100; /** * Creates new ini part cache files @@ -85,9 +86,10 @@ public function createIniParts($content) foreach ($patternpositions as $position => $pattern) { $pattern = strtolower($pattern); $patternhash = Pattern::getHashForParts($pattern); + $subkey = SubKey::getIniPartCacheSubKey($patternhash); - if (!isset($contents[$patternhash])) { - $contents[$patternhash] = array(); + if (!isset($contents[$subkey])) { + $contents[$subkey] = array(); } $browserProperties = parse_ini_string($iniParts[($position + 1)]); @@ -98,9 +100,10 @@ public function createIniParts($content) $property ); } + // the position has to be moved by one, because the header of the ini file // is also returned as a part - $contents[$patternhash][] = json_encode( + $contents[$subkey][] = $patternhash.json_encode( $browserProperties, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP ); @@ -109,8 +112,19 @@ public function createIniParts($content) unset($patternpositions); unset($iniParts); - foreach ($contents as $patternhash => $content) { - yield array($patternhash => $content); + $subkeys = array_flip(SubKey::getAllIniPartCacheSubKeys()); + foreach ($contents as $subkey => $content) { + $subkey = (string) $subkey; + + yield array($subkey => $content); + + unset($subkeys[$subkey]); + } + + foreach (array_keys($subkeys) as $subkey) { + $subkey = (string) $subkey; + + yield array($subkey => array()); } yield false; @@ -139,6 +153,8 @@ public function createPatterns($content) return; } + $quoterHelper = new Quoter(); + // build an array to structure the data. this requires some memory, but we need this step to be able to // sort the data in the way we need it (see below). $data = array(); @@ -146,13 +162,18 @@ public function createPatterns($content) foreach ($matches[0] as $match) { $pattern = strtolower($match); $patternhash = Pattern::getHashForPattern($pattern, false); + $tmpLength = Pattern::getPatternLength($match); + + // special handling of default entry + if ($tmpLength === 0) { + $patternhash = str_repeat('z', 32); + } if (!isset($data[$patternhash])) { $data[$patternhash] = array(); } - $quoterHelper = new Quoter(); - $pattern = $quoterHelper->pregQuote($pattern); + $pattern = $quoterHelper->pregQuote($pattern); // Check if the pattern contains digits - in this case we replace them with a digit regular expression, // so that very similar patterns (e.g. only with different browser version numbers) can be compressed. @@ -166,6 +187,16 @@ public function createPatterns($content) } else { $data[$patternhash][] = $pattern; } + + if (strpbrk($match, '0123456789') !== false) { + $compressedPattern = preg_replace('/\d/', '[\d]', $match); + + if (!isset($data[$patternhash][$compressedPattern])) { + $data[$patternhash][$compressedPattern] = $compressedPattern; + } + } else { + $data[$patternhash][$match] = $match; + } } unset($matches); @@ -175,23 +206,41 @@ public function createPatterns($content) // array with pattern strings instead of an large array with single patterns) and also enables // us to search for multiple patterns in one preg_match call for a fast first search // (3-10 faster), followed by a detailed search for each single pattern. + $contents = array(); foreach ($data as $patternhash => $tmpPatterns) { - $patterns = array(); - - for ($i = 0, $j = ceil(count($tmpPatterns) / $this->joinPatterns); $i < $j; $i++) { + for ($i = 0, $j = ceil(count($tmpPatterns) / self::COUNT_PATTERN); $i < $j; $i++) { $tmpJoinPatterns = implode( "\t", - array_slice($tmpPatterns, ($i * $this->joinPatterns), $this->joinPatterns) + array_slice($tmpPatterns, ($i * self::COUNT_PATTERN), self::COUNT_PATTERN) ); - $patterns[] = $tmpJoinPatterns; - } + $tmpSubkey = SubKey::getPatternCacheSubkey($patternhash); - yield array($patternhash => $patterns); + if (!isset($contents[$tmpSubkey])) { + $contents[$tmpSubkey] = array(); + } + + $contents[$tmpSubkey][] = $patternhash.' '.$tmpJoinPatterns; + } } unset($data); + $subkeys = array_flip(SubKey::getAllPatternCacheSubkeys()); + foreach ($contents as $subkey => $content) { + $subkey = (string) $subkey; + + yield array($subkey => $content); + + unset($subkeys[$subkey]); + } + + foreach (array_keys($subkeys) as $subkey) { + $subkey = (string) $subkey; + + yield array($subkey => array()); + } + yield false; } } diff --git a/src/Parser/Ini.php b/src/Parser/Ini.php index 1980a6e6..32b24e96 100644 --- a/src/Parser/Ini.php +++ b/src/Parser/Ini.php @@ -31,6 +31,7 @@ namespace BrowscapPHP\Parser; use BrowscapPHP\Cache\BrowscapCache; +use BrowscapPHP\Data\PropertyFormatter; use BrowscapPHP\Data\PropertyHolder; use BrowscapPHP\Formatter\FormatterInterface; use BrowscapPHP\Helper\Quoter; @@ -239,6 +240,7 @@ public function getBrowser($userAgent) private function getSettings($pattern, array $settings = array()) { $quoterHelper = new Quoter(); + // The pattern has been pre-quoted on generation to speed up the pattern search, // but for this check we need the unquoted version $unquotedPattern = $quoterHelper->pregUnQuote($pattern); @@ -257,8 +259,6 @@ private function getSettings($pattern, array $settings = array()) $settings['browser_name_regex'] = '/^' . $pattern . '$/'; $settings['browser_name_pattern'] = $unquotedPattern; } - //$settings['browser_name_regex'] = '/^'.$quoterHelper->pregQuote($pattern).'$/'; - //$settings['browser_name_pattern'] = $pattern; } // check if parent pattern set, only keep the first one @@ -304,13 +304,17 @@ private function getIniPart($pattern) return array(); } + $propertyHolder = new PropertyHolder(); + $propertyFormatter = new PropertyFormatter(); + $propertyFormatter->setPropertyHolder($propertyHolder); + $return = array(); foreach ($file as $buffer) { if (substr($buffer, 0, 32) === $patternhash) { $return = json_decode(substr($buffer, 32), true); foreach (array_keys($return) as $property) { - $return[$property] = $this->formatPropertyValue( + $return[$property] = $propertyFormatter->formatPropertyValue( $return[$property], $property ); @@ -322,42 +326,4 @@ private function getIniPart($pattern) return $return; } - - /** - * formats the name of a property - * - * @param string $value - * @param string $property - * - * @return string - */ - private function formatPropertyValue($value, $property) - { - $valueOutput = $value; - $propertyHolder = new PropertyHolder(); - - switch ($propertyHolder->getPropertyType($property)) { - case PropertyHolder::TYPE_BOOLEAN: - if (true === $value || $value === 'true' || $value === '1') { - $valueOutput = true; - } elseif (false === $value || $value === 'false' || $value === '') { - $valueOutput = false; - } else { - $valueOutput = ''; - } - break; - case PropertyHolder::TYPE_IN_ARRAY: - try { - $valueOutput = $propertyHolder->checkValueInArray($property, $value); - } catch (\InvalidArgumentException $ex) { - $valueOutput = ''; - } - break; - default: - // nothing t do here - break; - } - - return $valueOutput; - } } From 3326b47e2b42867a87aaaf008b94affdace17375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 9 Sep 2015 23:32:46 +0200 Subject: [PATCH 11/24] change detection --- src/Helper/Converter.php | 104 ++--------------------------------- src/IniParser/IniParser.php | 47 ++++++---------- src/Parser/Helper/SubKey.php | 6 +- src/Parser/Ini.php | 9 ++- 4 files changed, 29 insertions(+), 137 deletions(-) diff --git a/src/Helper/Converter.php b/src/Helper/Converter.php index c7423616..33c508de 100644 --- a/src/Helper/Converter.php +++ b/src/Helper/Converter.php @@ -33,8 +33,6 @@ use BrowscapPHP\Cache\BrowscapCache; use BrowscapPHP\Exception\FileNotFoundException; use BrowscapPHP\IniParser\IniParser; -use BrowscapPHP\Parser\Helper\Pattern; -use BrowscapPHP\Parser\Helper\SubKey; use BrowscapPHP\Parser\Ini; use Psr\Log\LoggerInterface; @@ -192,8 +190,8 @@ public function convertString($iniString) $this->getLogger()->info('start creating patterns from the ini data'); foreach ($iniParser->createPatterns($iniString) as $patternsHashList) { - foreach ($patternsHashList as $patternhash => $patterns) { - $this->cache->setItem('browscap.patterns.' . $patternhash, $patterns, true); + foreach ($patternsHashList as $subkey => $content) { + $this->cache->setItem('browscap.patterns.' . $subkey, $content, true); } } @@ -202,8 +200,8 @@ public function convertString($iniString) $this->getLogger()->info('start creating data from the ini data'); foreach ($iniParser->createIniParts($iniString) as $patternsContentList) { - foreach ($patternsContentList as $patternhash => $content) { - $this->getCache()->setItem('browscap.iniparts.' . $patternhash, $content, true); + foreach ($patternsContentList as $subkey => $content) { + $this->getCache()->setItem('browscap.iniparts.' . $subkey, $content, true); } } @@ -256,98 +254,4 @@ public function storeVersion() return $this; } - - /** - * Creates new ini part cache files - * @param string $content - */ - private function createIniParts($content) - { - // get all patterns from the ini file in the correct order, - // so that we can calculate with index number of the resulting array, - // which part to use when the ini file is splitted into its sections. - preg_match_all('/(?<=\[)(?:[^\r\n]+)(?=\])/m', $content, $patternpositions); - $patternpositions = $patternpositions[0]; - - // split the ini file into sections and save the data in one line with a hash of the beloging - // pattern (filtered in the previous step) - $iniParts = preg_split('/\[[^\r\n]+\]/', $content); - $contents = array(); - foreach ($patternpositions as $position => $pattern) { - $pattern = strtolower($pattern); - $patternhash = md5($pattern); - $subkey = SubKey::getPatternCacheSubkey($patternhash); - - if (!isset($contents[$subkey])) { - $contents[$subkey] = array(); - } - - $browserProperties = parse_ini_string($iniParts[($position + 1)]); - - foreach (array_keys($browserProperties) as $property) { - $browserProperties[$property] = $this->formatPropertyValue( - $browserProperties[$property], - $property - ); - } - // the position has to be moved by one, because the header of the ini file - // is also returned as a part - $contents[$subkey][] = $patternhash.json_encode( - $browserProperties, - JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP - ); - } - - unset($patternpositions); - unset($iniParts); - - $subkeys = array_flip(SubKey::getAllIniPartCacheSubKeys()); - foreach ($contents as $subkey => $content) { - $subkey = (string) $subkey; - $this->getCache()->setItem('browscap.iniparts.' . $subkey, $content, true); - unset($subkeys[$subkey]); - } - - foreach (array_keys($subkeys) as $subkey) { - $this->getCache()->setItem('browscap.iniparts.' . $subkey, array(), true); - } - } - - /** - * formats the name of a property - * - * @param string $value - * @param string $property - * - * @return string - */ - private function formatPropertyValue($value, $property) - { - $valueOutput = $value; - $propertyHolder = new PropertyHolder(); - - switch ($propertyHolder->getPropertyType($property)) { - case PropertyHolder::TYPE_BOOLEAN: - if (true === $value || $value === 'true' || $value === '1') { - $valueOutput = 'true'; - } elseif (false === $value || $value === 'false' || $value === '') { - $valueOutput = 'false'; - } else { - $valueOutput = ''; - } - break; - case PropertyHolder::TYPE_IN_ARRAY: - try { - $valueOutput = $propertyHolder->checkValueInArray($property, $value); - } catch (\InvalidArgumentException $ex) { - $valueOutput = ''; - } - break; - default: - // nothing t do here - break; - } - - return $valueOutput; - } } diff --git a/src/IniParser/IniParser.php b/src/IniParser/IniParser.php index 85d45c39..e92214aa 100644 --- a/src/IniParser/IniParser.php +++ b/src/IniParser/IniParser.php @@ -57,7 +57,7 @@ class IniParser * is limited by the internal regular expression limits. * @var int */ - CONST COUNT_PATTERN = 100; + CONST COUNT_PATTERN = 50; /** * Creates new ini part cache files @@ -92,7 +92,7 @@ public function createIniParts($content) $contents[$subkey] = array(); } - $browserProperties = parse_ini_string($iniParts[($position + 1)]); + $browserProperties = parse_ini_string($iniParts[($position + 1)], INI_SCANNER_RAW); foreach (array_keys($browserProperties) as $property) { $browserProperties[$property] = $propertyFormatter->formatPropertyValue( @@ -126,8 +126,6 @@ public function createIniParts($content) yield array($subkey => array()); } - - yield false; } /** @@ -149,7 +147,7 @@ public function createPatterns($content) ); if (empty($matches[0]) || !is_array($matches[0])) { - yield false; + yield array(); return; } @@ -159,10 +157,9 @@ public function createPatterns($content) // sort the data in the way we need it (see below). $data = array(); - foreach ($matches[0] as $match) { - $pattern = strtolower($match); + foreach ($matches[0] as $pattern) { $patternhash = Pattern::getHashForPattern($pattern, false); - $tmpLength = Pattern::getPatternLength($match); + $tmpLength = Pattern::getPatternLength($pattern); // special handling of default entry if ($tmpLength === 0) { @@ -181,22 +178,12 @@ public function createPatterns($content) if (strpbrk($pattern, '0123456789') !== false) { $compressedPattern = preg_replace('/\d/', '[\d]', $pattern); - if (!isset($data[$patternhash][$compressedPattern])) { - $data[$patternhash][$compressedPattern] = $compressedPattern; + if (!in_array($compressedPattern, $data[$patternhash])) { + $data[$patternhash][] = $compressedPattern; } } else { $data[$patternhash][] = $pattern; } - - if (strpbrk($match, '0123456789') !== false) { - $compressedPattern = preg_replace('/\d/', '[\d]', $match); - - if (!isset($data[$patternhash][$compressedPattern])) { - $data[$patternhash][$compressedPattern] = $compressedPattern; - } - } else { - $data[$patternhash][$match] = $match; - } } unset($matches); @@ -208,19 +195,23 @@ public function createPatterns($content) // (3-10 faster), followed by a detailed search for each single pattern. $contents = array(); foreach ($data as $patternhash => $tmpPatterns) { + if (empty($tmpPatterns)) { + continue; + } + + $subkey = SubKey::getPatternCacheSubkey($patternhash); + + if (!isset($contents[$subkey])) { + $contents[$subkey] = array(); + } + for ($i = 0, $j = ceil(count($tmpPatterns) / self::COUNT_PATTERN); $i < $j; $i++) { $tmpJoinPatterns = implode( "\t", array_slice($tmpPatterns, ($i * self::COUNT_PATTERN), self::COUNT_PATTERN) ); - $tmpSubkey = SubKey::getPatternCacheSubkey($patternhash); - - if (!isset($contents[$tmpSubkey])) { - $contents[$tmpSubkey] = array(); - } - - $contents[$tmpSubkey][] = $patternhash.' '.$tmpJoinPatterns; + $contents[$subkey][] = $patternhash.' '.$tmpJoinPatterns; } } @@ -240,7 +231,5 @@ public function createPatterns($content) yield array($subkey => array()); } - - yield false; } } diff --git a/src/Parser/Helper/SubKey.php b/src/Parser/Helper/SubKey.php index e046fe9f..78ebbbbe 100644 --- a/src/Parser/Helper/SubKey.php +++ b/src/Parser/Helper/SubKey.php @@ -65,9 +65,9 @@ public static function getAllPatternCacheSubkeys() $subkeys = array(); $chars = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'); - foreach ($chars as $char_one) { - foreach ($chars as $char_two) { - $subkeys[] = $char_one . $char_two; + foreach ($chars as $charOne) { + foreach ($chars as $charTwo) { + $subkeys[] = $charOne . $charTwo; } } diff --git a/src/Parser/Ini.php b/src/Parser/Ini.php index 32b24e96..99a51e72 100644 --- a/src/Parser/Ini.php +++ b/src/Parser/Ini.php @@ -184,12 +184,11 @@ public function getBrowser($userAgent) $formatter = null; foreach ($this->getHelper()->getPatterns($userAgent) as $patterns) { - $result = preg_match( - '/^(?:'.str_replace("\t", ')|(?:', $patterns).')$/i', - $userAgent - ); + $patternToMatch = '/^(?:'.str_replace("\t", ')|(?:', $patterns).')$/i'; + print_r($userAgent . "\n"); + print_r($patternToMatch . "\n"); - if (!$result) { + if (!preg_match($patternToMatch, $userAgent)) { continue; } From c40e2e1e5e48f1769fe1e163bdb90392ac565aca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Thu, 10 Sep 2015 12:55:03 +0200 Subject: [PATCH 12/24] change detection --- src/Helper/Converter.php | 2 ++ src/IniParser/IniParser.php | 12 ++++++++---- src/Parser/Helper/SubKey.php | 2 +- src/Parser/Ini.php | 8 +++++--- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/Helper/Converter.php b/src/Helper/Converter.php index 33c508de..4e423b24 100644 --- a/src/Helper/Converter.php +++ b/src/Helper/Converter.php @@ -190,6 +190,7 @@ public function convertString($iniString) $this->getLogger()->info('start creating patterns from the ini data'); foreach ($iniParser->createPatterns($iniString) as $patternsHashList) { + var_dump(__CLASS__ . '::' . __FUNCTION__, $patternsHashList); foreach ($patternsHashList as $subkey => $content) { $this->cache->setItem('browscap.patterns.' . $subkey, $content, true); } @@ -200,6 +201,7 @@ public function convertString($iniString) $this->getLogger()->info('start creating data from the ini data'); foreach ($iniParser->createIniParts($iniString) as $patternsContentList) { + var_dump(__CLASS__ . '::' . __FUNCTION__, $patternsContentList); foreach ($patternsContentList as $subkey => $content) { $this->getCache()->setItem('browscap.iniparts.' . $subkey, $content, true); } diff --git a/src/IniParser/IniParser.php b/src/IniParser/IniParser.php index e92214aa..36404d84 100644 --- a/src/IniParser/IniParser.php +++ b/src/IniParser/IniParser.php @@ -84,9 +84,11 @@ public function createIniParts($content) $propertyFormatter->setPropertyHolder($propertyHolder); foreach ($patternpositions as $position => $pattern) { + var_dump(__CLASS__ . '::' . __FUNCTION__, $pattern); $pattern = strtolower($pattern); $patternhash = Pattern::getHashForParts($pattern); $subkey = SubKey::getIniPartCacheSubKey($patternhash); + var_dump(__CLASS__ . '::' . __FUNCTION__, $pattern, $patternhash, $subkey); if (!isset($contents[$subkey])) { $contents[$subkey] = array(); @@ -115,7 +117,7 @@ public function createIniParts($content) $subkeys = array_flip(SubKey::getAllIniPartCacheSubKeys()); foreach ($contents as $subkey => $content) { $subkey = (string) $subkey; - + var_dump(__CLASS__ . '::' . __FUNCTION__, $subkey, $content); yield array($subkey => $content); unset($subkeys[$subkey]); @@ -160,6 +162,7 @@ public function createPatterns($content) foreach ($matches[0] as $pattern) { $patternhash = Pattern::getHashForPattern($pattern, false); $tmpLength = Pattern::getPatternLength($pattern); + var_dump(__CLASS__ . '::' . __FUNCTION__, $pattern, $patternhash); // special handling of default entry if ($tmpLength === 0) { @@ -200,7 +203,7 @@ public function createPatterns($content) } $subkey = SubKey::getPatternCacheSubkey($patternhash); - + var_dump(__CLASS__ . '::' . __FUNCTION__, $patternhash, $subkey); if (!isset($contents[$subkey])) { $contents[$subkey] = array(); } @@ -212,15 +215,16 @@ public function createPatterns($content) ); $contents[$subkey][] = $patternhash.' '.$tmpJoinPatterns; + var_dump(__CLASS__ . '::' . __FUNCTION__, $subkey, $patternhash.' '.$tmpJoinPatterns); } } unset($data); - $subkeys = array_flip(SubKey::getAllPatternCacheSubkeys()); + $subkeys = SubKey::getAllPatternCacheSubkeys(); foreach ($contents as $subkey => $content) { $subkey = (string) $subkey; - + var_dump(__CLASS__ . '::' . __FUNCTION__, $subkey, $content); yield array($subkey => $content); unset($subkeys[$subkey]); diff --git a/src/Parser/Helper/SubKey.php b/src/Parser/Helper/SubKey.php index 78ebbbbe..ccea1477 100644 --- a/src/Parser/Helper/SubKey.php +++ b/src/Parser/Helper/SubKey.php @@ -67,7 +67,7 @@ public static function getAllPatternCacheSubkeys() foreach ($chars as $charOne) { foreach ($chars as $charTwo) { - $subkeys[] = $charOne . $charTwo; + $subkeys[$charOne . $charTwo] = ''; } } diff --git a/src/Parser/Ini.php b/src/Parser/Ini.php index 99a51e72..5bda8c0f 100644 --- a/src/Parser/Ini.php +++ b/src/Parser/Ini.php @@ -184,11 +184,12 @@ public function getBrowser($userAgent) $formatter = null; foreach ($this->getHelper()->getPatterns($userAgent) as $patterns) { + var_dump(__CLASS__ . '::' . __FUNCTION__, $userAgent, $patterns); $patternToMatch = '/^(?:'.str_replace("\t", ')|(?:', $patterns).')$/i'; print_r($userAgent . "\n"); print_r($patternToMatch . "\n"); - if (!preg_match($patternToMatch, $userAgent)) { + if (!preg_match($patternToMatch, $userAgent)) {var_dump(__CLASS__ . '::' . __FUNCTION__, 'pattern not found'); continue; } @@ -212,8 +213,9 @@ public function getBrowser($userAgent) // Try to get settings - as digits have been replaced to speed up the pattern search (up to 90 faster), // we won't always find the data in the first step - so check if settings have been found and if not, // search for the next pattern. + var_dump(__CLASS__ . '::' . __FUNCTION__, $pattern); $settings = $this->getSettings($pattern); - + var_dump(__CLASS__ . '::' . __FUNCTION__, $pattern, $settings); if (count($settings) > 0) { $formatter = $this->getFormatter(); $formatter->setData($settings); @@ -246,7 +248,7 @@ private function getSettings($pattern, array $settings = array()) // Try to get settings for the pattern $addedSettings = $this->getIniPart($unquotedPattern); - + var_dump(__CLASS__ . '::' . __FUNCTION__, $pattern, $unquotedPattern, $addedSettings); // set some additional data if (count($settings) === 0) { // The optimization with replaced digits get can now result in setting searches, for which we From 8451b22c93fc38bdead4360c24971f38c37a9ba5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Sun, 13 Sep 2015 23:28:53 +0200 Subject: [PATCH 13/24] update --- .gitignore | 1 + src/Cache/BrowscapCache.php | 8 +- src/Helper/Converter.php | 18 +- src/IniParser/IniParser.php | 214 +++++++++++++++++++++- src/Parser/Helper/GetPattern.php | 16 +- src/Parser/Ini.php | 24 ++- tests/CompareBrowscapWithOriginalTest.php | 10 + tests/Parser/Helper/PatternTest.php | 100 +++------- 8 files changed, 298 insertions(+), 93 deletions(-) diff --git a/.gitignore b/.gitignore index b2257af9..ed9f0aa5 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ phpunit.xml composer.lock PHP_BrowscapINI resources +ergebnis.txt diff --git a/src/Cache/BrowscapCache.php b/src/Cache/BrowscapCache.php index 804287e9..3399d57e 100644 --- a/src/Cache/BrowscapCache.php +++ b/src/Cache/BrowscapCache.php @@ -164,7 +164,11 @@ public function getItem($cacheId, $withVersion = true, & $success = null) $success = null; $data = $this->getCacheAdapter()->getItem($cacheId, $success); - $success = true; + if (!isset($data['content'])) { + $success = false; + + return null; + } return unserialize($data['content']); } @@ -182,7 +186,7 @@ public function setItem($cacheId, $content, $withVersion = true) { // Get the whole PHP code $data = array( - 'content' => serialize($content), + 'content' => serialize($content), ); if ($withVersion) { diff --git a/src/Helper/Converter.php b/src/Helper/Converter.php index 4e423b24..5e83a47d 100644 --- a/src/Helper/Converter.php +++ b/src/Helper/Converter.php @@ -187,12 +187,22 @@ public function convertString($iniString) { $iniParser = new IniParser(); + list($patterns, $browsers, $contents) = $iniParser->createCacheNewWay($iniString); + print_r($contents); + print_r($patterns); + print_r($browsers); + exit; + $this->getLogger()->info('start creating patterns from the ini data'); foreach ($iniParser->createPatterns($iniString) as $patternsHashList) { var_dump(__CLASS__ . '::' . __FUNCTION__, $patternsHashList); + print_r($patternsHashList); foreach ($patternsHashList as $subkey => $content) { - $this->cache->setItem('browscap.patterns.' . $subkey, $content, true); + if (!$this->cache->setItem('browscap.patterns.' . $subkey, $content, true)) { + var_dump(__CLASS__ . '::' . __FUNCTION__, 'could not write pattern data "' . $subkey . '" to the cache'); + $this->getLogger()->error('could not write pattern data "' . $subkey . '" to the cache'); + } } } @@ -202,8 +212,12 @@ public function convertString($iniString) foreach ($iniParser->createIniParts($iniString) as $patternsContentList) { var_dump(__CLASS__ . '::' . __FUNCTION__, $patternsContentList); + print_r($patternsContentList); foreach ($patternsContentList as $subkey => $content) { - $this->getCache()->setItem('browscap.iniparts.' . $subkey, $content, true); + if (!$this->getCache()->setItem('browscap.iniparts.' . $subkey, $content, true)) { + var_dump(__CLASS__ . '::' . __FUNCTION__, 'could not write property data "' . $subkey . '" to the cache'); + $this->getLogger()->error('could not write property data "' . $subkey . '" to the cache'); + } } } diff --git a/src/IniParser/IniParser.php b/src/IniParser/IniParser.php index 36404d84..51b9ffe3 100644 --- a/src/IniParser/IniParser.php +++ b/src/IniParser/IniParser.php @@ -50,6 +50,17 @@ */ class IniParser { + /** + * Options for regex patterns. + * + * REGEX_DELIMITER: Delimiter of all the regex patterns in the whole class. + * REGEX_MODIFIERS: Regex modifiers. + */ + const REGEX_DELIMITER = '@'; + const REGEX_MODIFIERS = 'i'; + const COMPRESSION_PATTERN_START = '@'; + const COMPRESSION_PATTERN_DELIMITER = '|'; + /** * Number of pattern to combine for a faster regular expression search. * @@ -84,7 +95,7 @@ public function createIniParts($content) $propertyFormatter->setPropertyHolder($propertyHolder); foreach ($patternpositions as $position => $pattern) { - var_dump(__CLASS__ . '::' . __FUNCTION__, $pattern); + //var_dump(__CLASS__ . '::' . __FUNCTION__, $pattern); $pattern = strtolower($pattern); $patternhash = Pattern::getHashForParts($pattern); $subkey = SubKey::getIniPartCacheSubKey($patternhash); @@ -117,7 +128,7 @@ public function createIniParts($content) $subkeys = array_flip(SubKey::getAllIniPartCacheSubKeys()); foreach ($contents as $subkey => $content) { $subkey = (string) $subkey; - var_dump(__CLASS__ . '::' . __FUNCTION__, $subkey, $content); + //var_dump(__CLASS__ . '::' . __FUNCTION__, $subkey, $content); yield array($subkey => $content); unset($subkeys[$subkey]); @@ -160,9 +171,13 @@ public function createPatterns($content) $data = array(); foreach ($matches[0] as $pattern) { + if ('GJK_Browscap_Version' === $pattern) { + continue; + } + + $pattern = strtolower($pattern); $patternhash = Pattern::getHashForPattern($pattern, false); $tmpLength = Pattern::getPatternLength($pattern); - var_dump(__CLASS__ . '::' . __FUNCTION__, $pattern, $patternhash); // special handling of default entry if ($tmpLength === 0) { @@ -236,4 +251,197 @@ public function createPatterns($content) yield array($subkey => array()); } } + + /** + * creates the cache content + * + * @param string $iniContent The content of the downloaded ini file + * + * @return array + * @throws \UnexpectedValueException + */ + public function createCacheNewWay($iniContent) + { + $matches = array(); + $patternPositions = array(); + + // get all patterns from the ini file in the correct order, + // so that we can calculate with index number of the resulting array, + // which part to use when the ini file is split into its sections. + preg_match_all('/(?<=\[)(?:[^\r\n]+)(?=\])/m', $iniContent, $patternPositions); + + if (!isset($patternPositions[0])) { + throw new \UnexpectedValueException('could not extract patterns from ini file'); + } + + $patternPositions = $patternPositions[0]; + + if (!count($patternPositions)) { + throw new \UnexpectedValueException('no patterns were found inside the ini file'); + } + + // split the ini file into sections and save the data in one line with a hash of the belonging + // pattern (filtered in the previous step) + $iniParts = preg_split('/\[[^\r\n]+\]/', $iniContent); + $tmpPatterns = array(); + $browsers = array(); + + $quoterHelper = new Quoter(); + + foreach ($patternPositions as $position => $userAgent) { + if ('GJK_Browscap_Version' === $userAgent) { + continue; + } + + $properties = parse_ini_string($iniParts[($position + 1)], true, INI_SCANNER_RAW); + + if (empty($properties['Comment']) + || false !== strpos($userAgent, '*') + || false !== strpos($userAgent, '?') + ) { + $pattern = $quoterHelper->pregQuote($userAgent, self::REGEX_DELIMITER); + $matchesCount = preg_match_all('@\d@', $pattern, $matches); + + if (!$matchesCount) { + $tmpPatterns[$pattern] = $userAgent; + } else { + $compressedPattern = preg_replace('@\d@', '(\d)', $pattern); + + if (!isset($tmpPatterns[$compressedPattern])) { + $tmpPatterns[$compressedPattern] = array('first' => $pattern); + } + + $tmpPatterns[$compressedPattern][$userAgent] = $matches[0]; + } + } + + $browsers[$userAgent] = $properties; + + unset($position, $userAgent); + } + + $patterns = $this->deduplicatePattern($tmpPatterns); + $data = array(); + + foreach (array_keys($patterns) as $pattern) { + $patternhash = Pattern::getHashForPattern($pattern, false); + $tmpLength = Pattern::getPatternLength($pattern); + + // special handling of default entry + if ($tmpLength === 0) { + $patternhash = str_repeat('z', 32); + } + + if (!isset($data[$patternhash])) { + $data[$patternhash] = array(); + } + + $data[$patternhash][] = $pattern; + } + + // write optimized file (grouped by the first character of the has, generated from the pattern + // start) with multiple patterns joined by tabs. this is to speed up loading of the data (small + // array with pattern strings instead of an large array with single patterns) and also enables + // us to search for multiple patterns in one preg_match call for a fast first search + // (3-10 faster), followed by a detailed search for each single pattern. + $contents = array(); + foreach ($data as $patternhash => $tmpPatterns) { + if (empty($tmpPatterns)) { + continue; + } + + $subkey = SubKey::getPatternCacheSubkey($patternhash); + if (!isset($contents[$subkey])) { + $contents[$subkey] = array(); + } + + for ($i = 0, $j = ceil(count($tmpPatterns) / self::COUNT_PATTERN); $i < $j; $i++) { + $tmpJoinPatterns = implode( + "\t", + array_slice($tmpPatterns, ($i * self::COUNT_PATTERN), self::COUNT_PATTERN) + ); + + $contents[$subkey][] = $patternhash.' '.$tmpJoinPatterns; + } + } + + unset($data); + return array($patterns, $browsers, $contents); + } + + /** + * @param array $tmpPatterns + * + * @return array + */ + private function deduplicatePattern(array $tmpPatterns) + { + $patternList = array(); + + foreach ($tmpPatterns as $pattern => $patternData) { + if (is_string($patternData)) { + $data = $patternData; + } elseif (2 == count($patternData)) { + end($patternData); + + $pattern = $patternData['first']; + $data = key($patternData); + } else { + unset($patternData['first']); + + $data = $this->deduplicateCompressionPattern($patternData, $pattern); + } + + $patternList[$pattern] = $data; + } + + return $patternList; + } + + /** + * That looks complicated... + * All numbers are taken out into $matches, so we check if any of those numbers are identical + * in all the $matches and if they are we restore them to the $pattern, removing from the $matches. + * This gives us patterns with "(\d)" only in places that differ for some matches. + * + * @param array $matches + * @param string $pattern + * + * @return array of $matches + */ + private function deduplicateCompressionPattern($matches, &$pattern) + { + $tmp_matches = $matches; + $first_match = array_shift($tmp_matches); + $differences = array(); + + foreach ($tmp_matches as $some_match) { + $differences += array_diff_assoc($first_match, $some_match); + } + + $identical = array_diff_key($first_match, $differences); + + $prepared_matches = array(); + + foreach ($matches as $i => $some_match) { + $key = self::COMPRESSION_PATTERN_START + . implode( + self::COMPRESSION_PATTERN_DELIMITER, + array_diff_assoc($some_match, $identical) + ); + + $prepared_matches[$key] = $i; + } + + $pattern_parts = explode('(\d)', $pattern); + + foreach ($identical as $position => $value) { + $pattern_parts[$position + 1] = $pattern_parts[$position] . $value . $pattern_parts[$position + 1]; + unset($pattern_parts[$position]); + } + + $pattern = implode('(\d)', $pattern_parts); + + return $prepared_matches; + } } diff --git a/src/Parser/Helper/GetPattern.php b/src/Parser/Helper/GetPattern.php index accfff68..5497bd63 100644 --- a/src/Parser/Helper/GetPattern.php +++ b/src/Parser/Helper/GetPattern.php @@ -54,7 +54,11 @@ class GetPattern implements GetPatternInterface */ private $cache = null; - /** @var \Psr\Log\LoggerInterface */ + /** + * a logger instance + * + * @var \Psr\Log\LoggerInterface + */ private $logger = null; /** @@ -123,21 +127,30 @@ public function getPatterns($userAgent) // add special key to fall back to the default browser $starts[] = str_repeat('z', 32); + var_dump(__CLASS__ . '::' . __FUNCTION__, $userAgent, $starts); // get patterns, first for the given browser and if that is not found, // for the default browser (with a special key) foreach ($starts as $tmpStart) { $tmpSubkey = SubKey::getPatternCacheSubkey($tmpStart); + var_dump(__CLASS__ . '::' . __FUNCTION__, $tmpSubkey); + if (!$this->getCache()->hasItem('browscap.patterns.'.$tmpSubkey, true)) { + var_dump(__CLASS__ . '::' . __FUNCTION__, 'pattern not found for subkey ' . $tmpSubkey); + $this->getLogger()->debug('cache key "browscap.patterns.'.$tmpSubkey.'" not found'); + continue; + } $success = null; $file = $this->getCache()->getItem('browscap.patterns.'.$tmpSubkey, true, $success); if (!$success) { + var_dump(__CLASS__ . '::' . __FUNCTION__, 'pattern not found for subkey ' . $tmpSubkey . ' (2)'); $this->getLogger()->debug('cache key "browscap.patterns.'.$tmpSubkey.'" not found'); continue; } if (!is_array($file) || !count($file)) { + var_dump(__CLASS__ . '::' . __FUNCTION__, 'pattern was empty for subkey ' . $tmpSubkey . ' type: ' . gettype($file)); $this->getLogger()->debug('cache key "browscap.patterns.'.$tmpSubkey.'" was empty'); continue; } @@ -146,6 +159,7 @@ public function getPatterns($userAgent) foreach ($file as $buffer) { $tmpBuffer = substr($buffer, 0, 32); + if ($tmpBuffer === $tmpStart) { // get length of the pattern $len = (int) strstr(substr($buffer, 33, 4), ' ', true); diff --git a/src/Parser/Ini.php b/src/Parser/Ini.php index 5bda8c0f..3e26ffad 100644 --- a/src/Parser/Ini.php +++ b/src/Parser/Ini.php @@ -184,10 +184,10 @@ public function getBrowser($userAgent) $formatter = null; foreach ($this->getHelper()->getPatterns($userAgent) as $patterns) { - var_dump(__CLASS__ . '::' . __FUNCTION__, $userAgent, $patterns); + //var_dump(__CLASS__ . '::' . __FUNCTION__, $userAgent, $patterns); $patternToMatch = '/^(?:'.str_replace("\t", ')|(?:', $patterns).')$/i'; - print_r($userAgent . "\n"); - print_r($patternToMatch . "\n"); + //print_r($userAgent . "\n"); + //print_r($patternToMatch . "\n");continue; if (!preg_match($patternToMatch, $userAgent)) {var_dump(__CLASS__ . '::' . __FUNCTION__, 'pattern not found'); continue; @@ -199,8 +199,11 @@ public function getBrowser($userAgent) while ($pattern !== false) { $pattern = str_replace('[\d]', '(\d)', $pattern); $quotedPattern = '/^' . $pattern . '$/i'; + $matches = array(); if (preg_match($quotedPattern, $userAgent, $matches)) { + var_dump(__CLASS__ . '::' . __FUNCTION__, 1, "$pattern matched", $quotedPattern); + // Insert the digits back into the pattern, so that we can search the settings for it if (count($matches) > 1) { array_shift($matches); @@ -213,9 +216,8 @@ public function getBrowser($userAgent) // Try to get settings - as digits have been replaced to speed up the pattern search (up to 90 faster), // we won't always find the data in the first step - so check if settings have been found and if not, // search for the next pattern. - var_dump(__CLASS__ . '::' . __FUNCTION__, $pattern); $settings = $this->getSettings($pattern); - var_dump(__CLASS__ . '::' . __FUNCTION__, $pattern, $settings); + var_dump(__CLASS__ . '::' . __FUNCTION__, 2, $userAgent, $pattern, $settings); if (count($settings) > 0) { $formatter = $this->getFormatter(); $formatter->setData($settings); @@ -293,8 +295,10 @@ private function getIniPart($pattern) $pattern = strtolower($pattern); $patternhash = Pattern::getHashForParts($pattern); $subkey = SubKey::getIniPartCacheSubKey($patternhash); - + var_dump(__CLASS__ . '::' . __FUNCTION__, $pattern, $patternhash, $subkey); if (!$this->getCache()->hasItem('browscap.iniparts.'.$subkey, true)) { + $this->getLogger()->debug('cache key "browscap.iniparts.'.$subkey.'" not found'); + var_dump(__CLASS__ . '::' . __FUNCTION__, 'settings not found for subkey ' . $subkey); return array(); } @@ -302,6 +306,14 @@ private function getIniPart($pattern) $file = $this->getCache()->getItem('browscap.iniparts.'.$subkey, true, $success); if (!$success) { + $this->getLogger()->debug('cache key "browscap.iniparts.'.$subkey.'" not found'); + var_dump(__CLASS__ . '::' . __FUNCTION__, 'cache key "browscap.iniparts.'.$subkey.'" not found (2)'); + return array(); + } + + if (!is_array($file) || !count($file)) { + $this->getLogger()->debug('cache key "browscap.iniparts.'.$subkey.'" was empty'); + var_dump(__CLASS__ . '::' . __FUNCTION__, 'cache key "browscap.iniparts.'.$subkey.'" was empty type:' . gettype($file)); return array(); } diff --git a/tests/CompareBrowscapWithOriginalTest.php b/tests/CompareBrowscapWithOriginalTest.php index ab999e99..57bb46ef 100644 --- a/tests/CompareBrowscapWithOriginalTest.php +++ b/tests/CompareBrowscapWithOriginalTest.php @@ -87,6 +87,7 @@ public static function setUpBeforeClass() $cacheAdapter = new Memory(); $cache = new BrowscapCache($cacheAdapter); + $cache->flush(); self::$object ->setCache($cache) @@ -94,6 +95,9 @@ public static function setUpBeforeClass() ; } + /** + * @group compare + */ public function testCheckProperties() { $libProperties = get_object_vars(get_browser('x')); @@ -138,6 +142,7 @@ public function testCheckProperties() /** * @dataProvider providerUserAgent * @depends testCheckProperties + * @group compare * * @param string $userAgent */ @@ -179,6 +184,10 @@ public function testCompare($userAgent) public function providerUserAgent() { + return array( + array('Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.68 Safari/537.36'), + ); + /* return array( array('BlackBerry7100i/4.1.0 Profile/MIDP-2.0 Configuration/CLDC-1.1 VendorID/103'), array('check_http/v1.4.15 (nagios-plugins 1.4.15)'), @@ -221,5 +230,6 @@ public function providerUserAgent() array('Der gro\\xdfe BilderSauger 2.00u'), array('\\x22Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)\\x22'), ); + /**/ } } diff --git a/tests/Parser/Helper/PatternTest.php b/tests/Parser/Helper/PatternTest.php index a91ff685..45b724b5 100644 --- a/tests/Parser/Helper/PatternTest.php +++ b/tests/Parser/Helper/PatternTest.php @@ -39,98 +39,40 @@ class PatternTest extends \PHPUnit_Framework_TestCase { /** - * + * @group pattern */ public function testGetPatternStartWithoutVariants() { - $pattern = '[Mozilla/?.0 (compatible; Ask Jeeves/Teoma*)] - -Parent=Ask -Browser=Teoma -Comment=Ask -Version=0.0 -MajorVer=0 -MinorVer=0 -Platform=unknown -Platform_Version=unknown -Alpha= -Beta= -Win16= -Win32= -Win64= -Frames=1 -IFrames=1 -Tables=1 -Cookies= -BackgroundSounds= -JavaScript= -VBScript= -JavaApplets= -ActiveXControls= -isMobileDevice= -isTablet= -isSyndicationReader= -Crawler=1 -CssVersion=0 -AolVersion=0 -'; - self::assertSame('235cb78c730de50ce5ba6a0c1784b16b', Pattern::getHashForPattern($pattern, false)); + $pattern = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.68 Safari/537.36'; + self::assertSame('80ec954e53cf0878f566a0390e1c6b7b', Pattern::getHashForPattern(strtolower($pattern), false)); } /** - * + * @group pattern */ public function testGetPatternStartWithVariants() { - $pattern = '[Mozilla/?.0 (compatible; Ask Jeeves/Teoma*)] - -Parent=Ask -Browser=Teoma -Comment=Ask -Version=0.0 -MajorVer=0 -MinorVer=0 -Platform=unknown -Platform_Version=unknown -Alpha= -Beta= -Win16= -Win32= -Win64= -Frames=1 -IFrames=1 -Tables=1 -Cookies= -BackgroundSounds= -JavaScript= -VBScript= -JavaApplets= -ActiveXControls= -isMobileDevice= -isTablet= -isSyndicationReader= -Crawler=1 -CssVersion=0 -AolVersion=0 -'; - $expected = array( - 0 => '235cb78c730de50ce5ba6a0c1784b16b', - 1 => 'da1cf5a1bb225ddb155d34f2b0e24a2f', - 2 => '8a8cb4370f287109b2b04f733c9e0be8', - 3 => '31a3f71a58d7463916e992fe2e5e0636', - 4 => 'b15bdb2762b5359a3bcf114bf259dc4d', - 5 => '5e2a51bcd4ab81cc553588b044da6d1b', - 6 => '7d31a9d8363d6bc4d7d171bd9c0f032c', - 7 => 'ca66a7b1eb8df6a16330c46e4dee233b', - 8 => '815417267f76f6f460a4a61f9db75fdb', - 9 => 'd41d8cd98f00b204e9800998ecf8427e', + $pattern = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.68 Safari/537.36'; + $expected = array ( + 0 => '80ec954e53cf0878f566a0390e1c6b7b', + 1 => 'e2b019273cd71e36d9f6da01bd16fe86', + 2 => 'aaa556aeec36ac3edfe2f5deea5f1d28', + 3 => '31d050fd7a4ea6c972063ef30d18991a', + 4 => 'dbeb1c32b66fd7717de583d999f89ec3', + 5 => '13e6ce11d0a70e2a5a3df41bf11d493e', + 6 => '3a4a9ff7cf86e273442bad1305f3d1fd', + 7 => 'b70924c16a59b9cc2de329464b64118e', + 8 => '89364cb625249b3d478bace02699e05d', + 9 => '27c9d5187cd283f8d160ec1ed2b5ac89', + 10 => '6f8f57715090da2632453988d9a1501b', + 11 => 'd41d8cd98f00b204e9800998ecf8427e', ); - self::assertSame($expected, Pattern::getHashForPattern($pattern, true)); + self::assertSame($expected, Pattern::getHashForPattern(strtolower($pattern), true)); } /** - * + * @group pattern */ public function testGetPatternLength() { @@ -138,7 +80,7 @@ public function testGetPatternLength() } /** - * + * @group pattern */ public function testGetHashForParts() { From a31359132fb04f73024f1828e4f6bc540c22e8fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Mon, 14 Sep 2015 20:38:21 +0200 Subject: [PATCH 14/24] changes --- src/Parser/Helper/Pattern.php | 7 ++++--- tests/Parser/Helper/PatternTest.php | 24 +++++++++++------------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/Parser/Helper/Pattern.php b/src/Parser/Helper/Pattern.php index 1677f84d..9176646f 100644 --- a/src/Parser/Helper/Pattern.php +++ b/src/Parser/Helper/Pattern.php @@ -69,14 +69,15 @@ class Pattern */ public static function getHashForPattern($pattern, $variants = false) { - $regex = '/^([^\*\?\s\r\n\\\\]+).*$/'; + $regex = '/^([^\.\*\?\s\r\n\\\\]+).*$/'; $pattern = substr($pattern, 0, 32); + $matches = array(); - if (!preg_match($regex, $pattern)) { + if (!preg_match($regex, $pattern, $matches) || !isset($matches[1])) { return ($variants ? array(md5('')) : md5('')); } - $string = preg_replace($regex, '\\1', $pattern); + $string = $matches[1]; if (true === $variants) { $patternStarts = array(); diff --git a/tests/Parser/Helper/PatternTest.php b/tests/Parser/Helper/PatternTest.php index 45b724b5..886148ba 100644 --- a/tests/Parser/Helper/PatternTest.php +++ b/tests/Parser/Helper/PatternTest.php @@ -44,7 +44,7 @@ class PatternTest extends \PHPUnit_Framework_TestCase public function testGetPatternStartWithoutVariants() { $pattern = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.68 Safari/537.36'; - self::assertSame('80ec954e53cf0878f566a0390e1c6b7b', Pattern::getHashForPattern(strtolower($pattern), false)); + self::assertSame('aaa556aeec36ac3edfe2f5deea5f1d28', Pattern::getHashForPattern(strtolower($pattern), false)); } /** @@ -54,18 +54,16 @@ public function testGetPatternStartWithVariants() { $pattern = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.68 Safari/537.36'; $expected = array ( - 0 => '80ec954e53cf0878f566a0390e1c6b7b', - 1 => 'e2b019273cd71e36d9f6da01bd16fe86', - 2 => 'aaa556aeec36ac3edfe2f5deea5f1d28', - 3 => '31d050fd7a4ea6c972063ef30d18991a', - 4 => 'dbeb1c32b66fd7717de583d999f89ec3', - 5 => '13e6ce11d0a70e2a5a3df41bf11d493e', - 6 => '3a4a9ff7cf86e273442bad1305f3d1fd', - 7 => 'b70924c16a59b9cc2de329464b64118e', - 8 => '89364cb625249b3d478bace02699e05d', - 9 => '27c9d5187cd283f8d160ec1ed2b5ac89', - 10 => '6f8f57715090da2632453988d9a1501b', - 11 => 'd41d8cd98f00b204e9800998ecf8427e', + 0 => 'aaa556aeec36ac3edfe2f5deea5f1d28', + 1 => '31d050fd7a4ea6c972063ef30d18991a', + 2 => 'dbeb1c32b66fd7717de583d999f89ec3', + 3 => '13e6ce11d0a70e2a5a3df41bf11d493e', + 4 => '3a4a9ff7cf86e273442bad1305f3d1fd', + 5 => 'b70924c16a59b9cc2de329464b64118e', + 6 => '89364cb625249b3d478bace02699e05d', + 7 => '27c9d5187cd283f8d160ec1ed2b5ac89', + 8 => '6f8f57715090da2632453988d9a1501b', + 9 => 'd41d8cd98f00b204e9800998ecf8427e', ); self::assertSame($expected, Pattern::getHashForPattern(strtolower($pattern), true)); From f8783c9d2f8df8c368d66f472dd779a2279631e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Tue, 15 Sep 2015 23:47:13 +0200 Subject: [PATCH 15/24] update creating cache --- src/Cache/BrowscapCache.php | 2 +- src/Helper/Converter.php | 12 -- src/IniParser/IniParser.php | 211 +++------------------- src/Parser/Helper/GetPattern.php | 28 +-- src/Parser/Ini.php | 25 ++- tests/Cache/BrowscapCacheTest.php | 70 +++---- tests/CompareBrowscapWithOriginalTest.php | 8 +- tests/Helper/ConverterTest.php | 2 +- 8 files changed, 76 insertions(+), 282 deletions(-) diff --git a/src/Cache/BrowscapCache.php b/src/Cache/BrowscapCache.php index 3399d57e..c914d770 100644 --- a/src/Cache/BrowscapCache.php +++ b/src/Cache/BrowscapCache.php @@ -114,7 +114,7 @@ public function getCacheAdapter() public function getVersion() { if ($this->version === null) { - $success = null; + $success = true; $version = $this->getItem('browscap.version', false, $success); diff --git a/src/Helper/Converter.php b/src/Helper/Converter.php index 5e83a47d..2c3f755e 100644 --- a/src/Helper/Converter.php +++ b/src/Helper/Converter.php @@ -187,20 +187,11 @@ public function convertString($iniString) { $iniParser = new IniParser(); - list($patterns, $browsers, $contents) = $iniParser->createCacheNewWay($iniString); - print_r($contents); - print_r($patterns); - print_r($browsers); - exit; - $this->getLogger()->info('start creating patterns from the ini data'); foreach ($iniParser->createPatterns($iniString) as $patternsHashList) { - var_dump(__CLASS__ . '::' . __FUNCTION__, $patternsHashList); - print_r($patternsHashList); foreach ($patternsHashList as $subkey => $content) { if (!$this->cache->setItem('browscap.patterns.' . $subkey, $content, true)) { - var_dump(__CLASS__ . '::' . __FUNCTION__, 'could not write pattern data "' . $subkey . '" to the cache'); $this->getLogger()->error('could not write pattern data "' . $subkey . '" to the cache'); } } @@ -211,11 +202,8 @@ public function convertString($iniString) $this->getLogger()->info('start creating data from the ini data'); foreach ($iniParser->createIniParts($iniString) as $patternsContentList) { - var_dump(__CLASS__ . '::' . __FUNCTION__, $patternsContentList); - print_r($patternsContentList); foreach ($patternsContentList as $subkey => $content) { if (!$this->getCache()->setItem('browscap.iniparts.' . $subkey, $content, true)) { - var_dump(__CLASS__ . '::' . __FUNCTION__, 'could not write property data "' . $subkey . '" to the cache'); $this->getLogger()->error('could not write property data "' . $subkey . '" to the cache'); } } diff --git a/src/IniParser/IniParser.php b/src/IniParser/IniParser.php index 51b9ffe3..d6622eae 100644 --- a/src/IniParser/IniParser.php +++ b/src/IniParser/IniParser.php @@ -95,11 +95,9 @@ public function createIniParts($content) $propertyFormatter->setPropertyHolder($propertyHolder); foreach ($patternpositions as $position => $pattern) { - //var_dump(__CLASS__ . '::' . __FUNCTION__, $pattern); $pattern = strtolower($pattern); $patternhash = Pattern::getHashForParts($pattern); $subkey = SubKey::getIniPartCacheSubKey($patternhash); - var_dump(__CLASS__ . '::' . __FUNCTION__, $pattern, $patternhash, $subkey); if (!isset($contents[$subkey])) { $contents[$subkey] = array(); @@ -116,7 +114,7 @@ public function createIniParts($content) // the position has to be moved by one, because the header of the ini file // is also returned as a part - $contents[$subkey][] = $patternhash.json_encode( + $contents[$subkey][] = $patternhash . "\t" . json_encode( $browserProperties, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP ); @@ -128,7 +126,7 @@ public function createIniParts($content) $subkeys = array_flip(SubKey::getAllIniPartCacheSubKeys()); foreach ($contents as $subkey => $content) { $subkey = (string) $subkey; - //var_dump(__CLASS__ . '::' . __FUNCTION__, $subkey, $content); + yield array($subkey => $content); unset($subkeys[$subkey]); @@ -165,12 +163,14 @@ public function createPatterns($content) } $quoterHelper = new Quoter(); + $matches = $matches[0]; + usort($matches, array($this, 'compareBcStrings')); // build an array to structure the data. this requires some memory, but we need this step to be able to // sort the data in the way we need it (see below). $data = array(); - foreach ($matches[0] as $pattern) { + foreach ($matches as $pattern) { if ('GJK_Browscap_Version' === $pattern) { continue; } @@ -218,7 +218,7 @@ public function createPatterns($content) } $subkey = SubKey::getPatternCacheSubkey($patternhash); - var_dump(__CLASS__ . '::' . __FUNCTION__, $patternhash, $subkey); + if (!isset($contents[$subkey])) { $contents[$subkey] = array(); } @@ -229,8 +229,7 @@ public function createPatterns($content) array_slice($tmpPatterns, ($i * self::COUNT_PATTERN), self::COUNT_PATTERN) ); - $contents[$subkey][] = $patternhash.' '.$tmpJoinPatterns; - var_dump(__CLASS__ . '::' . __FUNCTION__, $subkey, $patternhash.' '.$tmpJoinPatterns); + $contents[$subkey][] = $patternhash . "\t" . $tmpJoinPatterns; } } @@ -239,7 +238,7 @@ public function createPatterns($content) $subkeys = SubKey::getAllPatternCacheSubkeys(); foreach ($contents as $subkey => $content) { $subkey = (string) $subkey; - var_dump(__CLASS__ . '::' . __FUNCTION__, $subkey, $content); + yield array($subkey => $content); unset($subkeys[$subkey]); @@ -253,195 +252,35 @@ public function createPatterns($content) } /** - * creates the cache content + * @param string $a + * @param string $b * - * @param string $iniContent The content of the downloaded ini file - * - * @return array - * @throws \UnexpectedValueException + * @return int */ - public function createCacheNewWay($iniContent) + private function compareBcStrings($a, $b) { - $matches = array(); - $patternPositions = array(); - - // get all patterns from the ini file in the correct order, - // so that we can calculate with index number of the resulting array, - // which part to use when the ini file is split into its sections. - preg_match_all('/(?<=\[)(?:[^\r\n]+)(?=\])/m', $iniContent, $patternPositions); + $a_len = strlen($a); + $b_len = strlen($b); - if (!isset($patternPositions[0])) { - throw new \UnexpectedValueException('could not extract patterns from ini file'); + if ($a_len > $b_len) { + return -1; } - $patternPositions = $patternPositions[0]; - - if (!count($patternPositions)) { - throw new \UnexpectedValueException('no patterns were found inside the ini file'); + if ($a_len < $b_len) { + return 1; } - // split the ini file into sections and save the data in one line with a hash of the belonging - // pattern (filtered in the previous step) - $iniParts = preg_split('/\[[^\r\n]+\]/', $iniContent); - $tmpPatterns = array(); - $browsers = array(); - - $quoterHelper = new Quoter(); - - foreach ($patternPositions as $position => $userAgent) { - if ('GJK_Browscap_Version' === $userAgent) { - continue; - } - - $properties = parse_ini_string($iniParts[($position + 1)], true, INI_SCANNER_RAW); - - if (empty($properties['Comment']) - || false !== strpos($userAgent, '*') - || false !== strpos($userAgent, '?') - ) { - $pattern = $quoterHelper->pregQuote($userAgent, self::REGEX_DELIMITER); - $matchesCount = preg_match_all('@\d@', $pattern, $matches); - - if (!$matchesCount) { - $tmpPatterns[$pattern] = $userAgent; - } else { - $compressedPattern = preg_replace('@\d@', '(\d)', $pattern); - - if (!isset($tmpPatterns[$compressedPattern])) { - $tmpPatterns[$compressedPattern] = array('first' => $pattern); - } + $a_len = strlen(str_replace(array('*', '?'), '', $a)); + $b_len = strlen(str_replace(array('*', '?'), '', $b)); - $tmpPatterns[$compressedPattern][$userAgent] = $matches[0]; - } - } - - $browsers[$userAgent] = $properties; - - unset($position, $userAgent); + if ($a_len > $b_len) { + return -1; } - $patterns = $this->deduplicatePattern($tmpPatterns); - $data = array(); - - foreach (array_keys($patterns) as $pattern) { - $patternhash = Pattern::getHashForPattern($pattern, false); - $tmpLength = Pattern::getPatternLength($pattern); - - // special handling of default entry - if ($tmpLength === 0) { - $patternhash = str_repeat('z', 32); - } - - if (!isset($data[$patternhash])) { - $data[$patternhash] = array(); - } - - $data[$patternhash][] = $pattern; + if ($a_len < $b_len) { + return 1; } - // write optimized file (grouped by the first character of the has, generated from the pattern - // start) with multiple patterns joined by tabs. this is to speed up loading of the data (small - // array with pattern strings instead of an large array with single patterns) and also enables - // us to search for multiple patterns in one preg_match call for a fast first search - // (3-10 faster), followed by a detailed search for each single pattern. - $contents = array(); - foreach ($data as $patternhash => $tmpPatterns) { - if (empty($tmpPatterns)) { - continue; - } - - $subkey = SubKey::getPatternCacheSubkey($patternhash); - if (!isset($contents[$subkey])) { - $contents[$subkey] = array(); - } - - for ($i = 0, $j = ceil(count($tmpPatterns) / self::COUNT_PATTERN); $i < $j; $i++) { - $tmpJoinPatterns = implode( - "\t", - array_slice($tmpPatterns, ($i * self::COUNT_PATTERN), self::COUNT_PATTERN) - ); - - $contents[$subkey][] = $patternhash.' '.$tmpJoinPatterns; - } - } - - unset($data); - return array($patterns, $browsers, $contents); - } - - /** - * @param array $tmpPatterns - * - * @return array - */ - private function deduplicatePattern(array $tmpPatterns) - { - $patternList = array(); - - foreach ($tmpPatterns as $pattern => $patternData) { - if (is_string($patternData)) { - $data = $patternData; - } elseif (2 == count($patternData)) { - end($patternData); - - $pattern = $patternData['first']; - $data = key($patternData); - } else { - unset($patternData['first']); - - $data = $this->deduplicateCompressionPattern($patternData, $pattern); - } - - $patternList[$pattern] = $data; - } - - return $patternList; - } - - /** - * That looks complicated... - * All numbers are taken out into $matches, so we check if any of those numbers are identical - * in all the $matches and if they are we restore them to the $pattern, removing from the $matches. - * This gives us patterns with "(\d)" only in places that differ for some matches. - * - * @param array $matches - * @param string $pattern - * - * @return array of $matches - */ - private function deduplicateCompressionPattern($matches, &$pattern) - { - $tmp_matches = $matches; - $first_match = array_shift($tmp_matches); - $differences = array(); - - foreach ($tmp_matches as $some_match) { - $differences += array_diff_assoc($first_match, $some_match); - } - - $identical = array_diff_key($first_match, $differences); - - $prepared_matches = array(); - - foreach ($matches as $i => $some_match) { - $key = self::COMPRESSION_PATTERN_START - . implode( - self::COMPRESSION_PATTERN_DELIMITER, - array_diff_assoc($some_match, $identical) - ); - - $prepared_matches[$key] = $i; - } - - $pattern_parts = explode('(\d)', $pattern); - - foreach ($identical as $position => $value) { - $pattern_parts[$position + 1] = $pattern_parts[$position] . $value . $pattern_parts[$position + 1]; - unset($pattern_parts[$position]); - } - - $pattern = implode('(\d)', $pattern_parts); - - return $prepared_matches; + return 0; } } diff --git a/src/Parser/Helper/GetPattern.php b/src/Parser/Helper/GetPattern.php index 5497bd63..4afc90ff 100644 --- a/src/Parser/Helper/GetPattern.php +++ b/src/Parser/Helper/GetPattern.php @@ -123,20 +123,18 @@ public function getLogger() public function getPatterns($userAgent) { $starts = Pattern::getHashForPattern($userAgent, true); - $length = Pattern::getPatternLength($userAgent); // add special key to fall back to the default browser $starts[] = str_repeat('z', 32); - var_dump(__CLASS__ . '::' . __FUNCTION__, $userAgent, $starts); // get patterns, first for the given browser and if that is not found, // for the default browser (with a special key) foreach ($starts as $tmpStart) { $tmpSubkey = SubKey::getPatternCacheSubkey($tmpStart); - var_dump(__CLASS__ . '::' . __FUNCTION__, $tmpSubkey); + if (!$this->getCache()->hasItem('browscap.patterns.'.$tmpSubkey, true)) { - var_dump(__CLASS__ . '::' . __FUNCTION__, 'pattern not found for subkey ' . $tmpSubkey); $this->getLogger()->debug('cache key "browscap.patterns.'.$tmpSubkey.'" not found'); + continue; } $success = null; @@ -144,38 +142,26 @@ public function getPatterns($userAgent) $file = $this->getCache()->getItem('browscap.patterns.'.$tmpSubkey, true, $success); if (!$success) { - var_dump(__CLASS__ . '::' . __FUNCTION__, 'pattern not found for subkey ' . $tmpSubkey . ' (2)'); $this->getLogger()->debug('cache key "browscap.patterns.'.$tmpSubkey.'" not found'); + continue; } if (!is_array($file) || !count($file)) { - var_dump(__CLASS__ . '::' . __FUNCTION__, 'pattern was empty for subkey ' . $tmpSubkey . ' type: ' . gettype($file)); $this->getLogger()->debug('cache key "browscap.patterns.'.$tmpSubkey.'" was empty'); + continue; } - $found = false; - foreach ($file as $buffer) { - $tmpBuffer = substr($buffer, 0, 32); + list($tmpBuffer, $patterns) = explode("\t", $buffer, 2); if ($tmpBuffer === $tmpStart) { - // get length of the pattern - $len = (int) strstr(substr($buffer, 33, 4), ' ', true); - - // the user agent must be longer than the pattern without place holders - if ($len <= $length) { - list(, $patterns) = explode(' ', $buffer, 2); - yield trim($patterns); - } - $found = true; - } elseif ($found === true) { - break; + yield trim($patterns); } } } - yield false; + yield ''; } } diff --git a/src/Parser/Ini.php b/src/Parser/Ini.php index 3e26ffad..07a74062 100644 --- a/src/Parser/Ini.php +++ b/src/Parser/Ini.php @@ -184,12 +184,9 @@ public function getBrowser($userAgent) $formatter = null; foreach ($this->getHelper()->getPatterns($userAgent) as $patterns) { - //var_dump(__CLASS__ . '::' . __FUNCTION__, $userAgent, $patterns); $patternToMatch = '/^(?:'.str_replace("\t", ')|(?:', $patterns).')$/i'; - //print_r($userAgent . "\n"); - //print_r($patternToMatch . "\n");continue; - if (!preg_match($patternToMatch, $userAgent)) {var_dump(__CLASS__ . '::' . __FUNCTION__, 'pattern not found'); + if (!preg_match($patternToMatch, $userAgent)) { continue; } @@ -202,8 +199,6 @@ public function getBrowser($userAgent) $matches = array(); if (preg_match($quotedPattern, $userAgent, $matches)) { - var_dump(__CLASS__ . '::' . __FUNCTION__, 1, "$pattern matched", $quotedPattern); - // Insert the digits back into the pattern, so that we can search the settings for it if (count($matches) > 1) { array_shift($matches); @@ -217,7 +212,7 @@ public function getBrowser($userAgent) // we won't always find the data in the first step - so check if settings have been found and if not, // search for the next pattern. $settings = $this->getSettings($pattern); - var_dump(__CLASS__ . '::' . __FUNCTION__, 2, $userAgent, $pattern, $settings); + if (count($settings) > 0) { $formatter = $this->getFormatter(); $formatter->setData($settings); @@ -250,7 +245,7 @@ private function getSettings($pattern, array $settings = array()) // Try to get settings for the pattern $addedSettings = $this->getIniPart($unquotedPattern); - var_dump(__CLASS__ . '::' . __FUNCTION__, $pattern, $unquotedPattern, $addedSettings); + // set some additional data if (count($settings) === 0) { // The optimization with replaced digits get can now result in setting searches, for which we @@ -295,10 +290,10 @@ private function getIniPart($pattern) $pattern = strtolower($pattern); $patternhash = Pattern::getHashForParts($pattern); $subkey = SubKey::getIniPartCacheSubKey($patternhash); - var_dump(__CLASS__ . '::' . __FUNCTION__, $pattern, $patternhash, $subkey); + if (!$this->getCache()->hasItem('browscap.iniparts.'.$subkey, true)) { $this->getLogger()->debug('cache key "browscap.iniparts.'.$subkey.'" not found'); - var_dump(__CLASS__ . '::' . __FUNCTION__, 'settings not found for subkey ' . $subkey); + return array(); } @@ -307,13 +302,13 @@ private function getIniPart($pattern) if (!$success) { $this->getLogger()->debug('cache key "browscap.iniparts.'.$subkey.'" not found'); - var_dump(__CLASS__ . '::' . __FUNCTION__, 'cache key "browscap.iniparts.'.$subkey.'" not found (2)'); + return array(); } if (!is_array($file) || !count($file)) { $this->getLogger()->debug('cache key "browscap.iniparts.'.$subkey.'" was empty'); - var_dump(__CLASS__ . '::' . __FUNCTION__, 'cache key "browscap.iniparts.'.$subkey.'" was empty type:' . gettype($file)); + return array(); } @@ -323,8 +318,10 @@ private function getIniPart($pattern) $return = array(); foreach ($file as $buffer) { - if (substr($buffer, 0, 32) === $patternhash) { - $return = json_decode(substr($buffer, 32), true); + list($tmpBuffer, $patterns) = explode("\t", $buffer, 2); + + if ($tmpBuffer === $patternhash) { + $return = json_decode($patterns, true); foreach (array_keys($return) as $property) { $return[$property] = $propertyFormatter->formatPropertyValue( diff --git a/tests/Cache/BrowscapCacheTest.php b/tests/Cache/BrowscapCacheTest.php index 78961ca4..4217b524 100644 --- a/tests/Cache/BrowscapCacheTest.php +++ b/tests/Cache/BrowscapCacheTest.php @@ -103,10 +103,6 @@ public function testGetVersionNotCached() */ public function testGetVersionCached() { - $data = array( - 'content' => serialize(42) - ); - $adapter = $this->getMock('\WurflCache\Adapter\Memcache', array('hasItem', 'getItem'), array(), '', false); $adapter ->expects(self::once()) @@ -116,13 +112,34 @@ public function testGetVersionCached() $adapter ->expects(self::once()) ->method('getItem') - ->will(self::returnValue($data)) + ->will(self::returnCallback(array($this, 'return42'))) ; $this->object->setCacheAdapter($adapter); self::assertSame(42, $this->object->getVersion()); } + public function return42($cacheId, & $success = null) + { + $success = true; + + switch ($cacheId) { + case 'test.42': + $data = array( + 'content' => serialize('this is a test') + ); + break; + case 'browscap.version': + case 'test': + default: + $data = array( + 'content' => serialize(42) + ); + } + + return $data; + } + /** * */ @@ -151,10 +168,6 @@ public function testGetItemNotCached() */ public function testGetItemCached() { - $data = array( - 'content' => serialize(42) - ); - $adapter = $this->getMock('\WurflCache\Adapter\Memcache', array('hasItem', 'getItem'), array(), '', false); $adapter ->expects(self::once()) @@ -164,7 +177,7 @@ public function testGetItemCached() $adapter ->expects(self::once()) ->method('getItem') - ->will(self::returnValue($data)) + ->will(self::returnCallback(array($this, 'return42'))) ; $this->object->setCacheAdapter($adapter); @@ -178,23 +191,6 @@ public function testGetItemCached() */ public function testGetItemCachedWithVersion() { - $map = array( - array( - 'browscap.version', - null, - array( - 'content' => serialize(42) - ) - ), - array( - 'test.42', - null, - array( - 'content' => serialize('this is a test') - ) - ) - ); - $adapter = $this->getMock('\WurflCache\Adapter\Memcache', array('hasItem', 'getItem'), array(), '', false); $adapter ->expects(self::exactly(2)) @@ -204,7 +200,7 @@ public function testGetItemCachedWithVersion() $adapter ->expects(self::exactly(2)) ->method('getItem') - ->will(self::returnValueMap($map)) + ->will(self::returnCallback(array($this, 'return42'))) ; $this->object->setCacheAdapter($adapter); @@ -234,10 +230,6 @@ public function testHasItemNotCached() */ public function testHasItemCachedWithVersion() { - $data = array( - 'content' => serialize(42) - ); - $adapter = $this->getMock('\WurflCache\Adapter\Memcache', array('hasItem', 'getItem'), array(), '', false); $adapter ->expects(self::exactly(2)) @@ -247,7 +239,7 @@ public function testHasItemCachedWithVersion() $adapter ->expects(self::once()) ->method('getItem') - ->will(self::returnValue($data)) + ->will(self::returnCallback(array($this, 'return42'))) ; $this->object->setCacheAdapter($adapter); @@ -259,10 +251,6 @@ public function testHasItemCachedWithVersion() */ public function testSetItemWithVersion() { - $data = array( - 'content' => serialize(42) - ); - $adapter = $this->getMock('\WurflCache\Adapter\Memcache', array('hasItem', 'getItem', 'setItem'), array(), '', false); $adapter ->expects(self::once()) @@ -272,7 +260,7 @@ public function testSetItemWithVersion() $adapter ->expects(self::once()) ->method('getItem') - ->will(self::returnValue($data)) + ->will(self::returnCallback(array($this, 'return42'))) ; $adapter ->expects(self::once()) @@ -289,10 +277,6 @@ public function testSetItemWithVersion() */ public function testRemoveItemWithVersion() { - $data = array( - 'content' => serialize(42) - ); - $adapter = $this->getMock('\WurflCache\Adapter\Memcache', array('hasItem', 'getItem', 'removeItem'), array(), '', false); $adapter ->expects(self::once()) @@ -302,7 +286,7 @@ public function testRemoveItemWithVersion() $adapter ->expects(self::once()) ->method('getItem') - ->will(self::returnValue($data)) + ->will(self::returnCallback(array($this, 'return42'))) ; $adapter ->expects(self::once()) diff --git a/tests/CompareBrowscapWithOriginalTest.php b/tests/CompareBrowscapWithOriginalTest.php index 57bb46ef..5d9890ed 100644 --- a/tests/CompareBrowscapWithOriginalTest.php +++ b/tests/CompareBrowscapWithOriginalTest.php @@ -184,10 +184,10 @@ public function testCompare($userAgent) public function providerUserAgent() { - return array( - array('Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.68 Safari/537.36'), - ); - /* + //return array( + // array('Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.68 Safari/537.36'), + //); + return array( array('BlackBerry7100i/4.1.0 Profile/MIDP-2.0 Configuration/CLDC-1.1 VendorID/103'), array('check_http/v1.4.15 (nagios-plugins 1.4.15)'), diff --git a/tests/Helper/ConverterTest.php b/tests/Helper/ConverterTest.php index ac16e3fa..235a0cce 100644 --- a/tests/Helper/ConverterTest.php +++ b/tests/Helper/ConverterTest.php @@ -425,7 +425,7 @@ public function testConvertStringWithoutPatternFaound() ->method('exists') ->will(self::returnValue(false)); - $logger = $this->getMock('\Monolog\Logger', array('info'), array(), '', false); + $logger = $this->getMock('\Monolog\Logger', array('info'), array(), ''); $logger->expects(self::exactly(4)) ->method('info') ->will(self::returnValue(false)); From 6b882aa0c79b02bdbdd666cb6804a37ad85ce9a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 16 Sep 2015 20:02:14 +0200 Subject: [PATCH 16/24] extract DataInterface --- src/Browscap.php | 15 +- src/Helper/Converter.php | 4 +- src/Parser/Helper/GetData.php | 247 ++++++++++++++++++++++ src/Parser/Helper/GetDataInterface.php | 104 +++++++++ src/Parser/Helper/GetPatternInterface.php | 2 +- src/Parser/Ini.php | 160 +++----------- src/Parser/ParserInterface.php | 40 +++- tests/BrowscapTest.php | 4 +- tests/Helper/ConverterTest.php | 14 +- tests/Parser/IniTest.php | 4 +- 10 files changed, 450 insertions(+), 144 deletions(-) create mode 100644 src/Parser/Helper/GetData.php create mode 100644 src/Parser/Helper/GetDataInterface.php diff --git a/src/Browscap.php b/src/Browscap.php index 7b0050f5..741b9f04 100644 --- a/src/Browscap.php +++ b/src/Browscap.php @@ -37,6 +37,7 @@ use BrowscapPHP\Helper\Converter; use BrowscapPHP\Helper\Filesystem; use BrowscapPHP\Helper\IniLoader; +use BrowscapPHP\Helper\Quoter; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; use WurflCache\Adapter\AdapterInterface; @@ -192,15 +193,23 @@ public function getParser() $this->setParser(new Parser\Ini()); } - $helper = new Parser\Helper\GetPattern(); + $patternHelper = new Parser\Helper\GetPattern(); + $dataHelper = new Parser\Helper\GetData(); - $helper + $patternHelper ->setCache($this->getCache()) ->setLogger($this->getLogger()) ; + $dataHelper + ->setCache($this->getCache()) + ->setLogger($this->getLogger()) + ->setQuoter(new Quoter()) + ; + $this->parser - ->setHelper($helper) + ->setPatternHelper($patternHelper) + ->setDataHelper($dataHelper) ->setFormatter($this->getFormatter()) ->setCache($this->getCache()) ->setLogger($this->getLogger()) diff --git a/src/Helper/Converter.php b/src/Helper/Converter.php index 2c3f755e..d226bd1a 100644 --- a/src/Helper/Converter.php +++ b/src/Helper/Converter.php @@ -37,7 +37,7 @@ use Psr\Log\LoggerInterface; /** - * helper to convert the ini data, parses the data and stores them into the cache + * patternHelper to convert the ini data, parses the data and stores them into the cache * * @category Browscap-PHP * @package Helper @@ -72,7 +72,7 @@ class Converter private $cache = null; /** - * a filesystem helper instance + * a filesystem patternHelper instance * * @var \BrowscapPHP\Helper\Filesystem */ diff --git a/src/Parser/Helper/GetData.php b/src/Parser/Helper/GetData.php new file mode 100644 index 00000000..b2033145 --- /dev/null +++ b/src/Parser/Helper/GetData.php @@ -0,0 +1,247 @@ + + * @author Thomas Müller + * @copyright Copyright (c) 1998-2014 Browser Capabilities Project + * @version 3.0 + * @license http://www.opensource.org/licenses/MIT MIT License + * @link https://github.com/browscap/browscap-php/ + */ +class GetData implements GetDataInterface +{ + /** + * The cache instance + * + * @var \BrowscapPHP\Cache\BrowscapCache + */ + private $cache = null; + + /** + * a logger instance + * + * @var \Psr\Log\LoggerInterface + */ + private $logger = null; + + /** + * @var \BrowscapPHP\Helper\Quoter + */ + private $quoter = null; + + /** + * Gets a cache instance + * + * @return \BrowscapPHP\Cache\BrowscapCache + */ + public function getCache() + { + return $this->cache; + } + + /** + * Sets a cache instance + * + * @param \BrowscapPHP\Cache\BrowscapCache $cache + * + * @return \BrowscapPHP\Parser\Helper\GetData + */ + public function setCache(BrowscapCache $cache) + { + $this->cache = $cache; + + return $this; + } + + /** + * Sets a logger instance + * + * @param \Psr\Log\LoggerInterface $logger + * + * @return \BrowscapPHP\Parser\Helper\GetData + */ + public function setLogger(LoggerInterface $logger) + { + $this->logger = $logger; + + return $this; + } + + /** + * Returns a logger instance + * + * @return \Psr\Log\LoggerInterface $logger + */ + public function getLogger() + { + return $this->logger; + } + + /** + * @param \BrowscapPHP\Helper\Quoter $quoter + * + * @return \BrowscapPHP\Parser\Helper\GetData + */ + public function setQuoter(Quoter $quoter) + { + $this->quoter = $quoter; + + return $this; + } + + /** + * @return \BrowscapPHP\Helper\Quoter + */ + public function getQuoter() + { + return $this->quoter; + } + + /** + * Gets the settings for a given pattern (method calls itself to + * get the data from the parent patterns) + * + * @param string $pattern + * @param array $settings + * @return array + */ + public function getSettings($pattern, array $settings = array()) + { + // The pattern has been pre-quoted on generation to speed up the pattern search, + // but for this check we need the unquoted version + $unquotedPattern = $this->getQuoter()->pregUnQuote($pattern); + + // Try to get settings for the pattern + $addedSettings = $this->getIniPart($unquotedPattern); + + // set some additional data + if (count($settings) === 0) { + // The optimization with replaced digits get can now result in setting searches, for which we + // won't find a result - so only add the pattern information, is settings have been found. + // + // If not an empty array will be returned and the calling function can easily check if a pattern + // has been found. + if (count($addedSettings) > 0) { + $settings['browser_name_regex'] = '/^' . $pattern . '$/'; + $settings['browser_name_pattern'] = $unquotedPattern; + } + } + + // check if parent pattern set, only keep the first one + $parentPattern = null; + if (isset($addedSettings['Parent'])) { + $parentPattern = $addedSettings['Parent']; + + if (isset($settings['Parent'])) { + unset($addedSettings['Parent']); + } + } + + // merge settings + $settings += $addedSettings; + + if ($parentPattern !== null) { + return $this->getSettings($this->getQuoter()->pregQuote($parentPattern), $settings); + } + + return $settings; + } + + /** + * Gets the relevant part (array of settings) of the ini file for a given pattern. + * + * @param string $pattern + * @return array + */ + private function getIniPart($pattern) + { + $pattern = strtolower($pattern); + $patternhash = Pattern::getHashForParts($pattern); + $subkey = SubKey::getIniPartCacheSubKey($patternhash); + + if (!$this->getCache()->hasItem('browscap.iniparts.'.$subkey, true)) { + $this->getLogger()->debug('cache key "browscap.iniparts.'.$subkey.'" not found'); + + return array(); + } + + $success = null; + $file = $this->getCache()->getItem('browscap.iniparts.'.$subkey, true, $success); + + if (!$success) { + $this->getLogger()->debug('cache key "browscap.iniparts.'.$subkey.'" not found'); + + return array(); + } + + if (!is_array($file) || !count($file)) { + $this->getLogger()->debug('cache key "browscap.iniparts.'.$subkey.'" was empty'); + + return array(); + } + + $propertyHolder = new PropertyHolder(); + $propertyFormatter = new PropertyFormatter(); + $propertyFormatter->setPropertyHolder($propertyHolder); + + $return = array(); + foreach ($file as $buffer) { + list($tmpBuffer, $patterns) = explode("\t", $buffer, 2); + + if ($tmpBuffer === $patternhash) { + $return = json_decode($patterns, true); + + foreach (array_keys($return) as $property) { + $return[$property] = $propertyFormatter->formatPropertyValue( + $return[$property], + $property + ); + } + + break; + } + } + + return $return; + } +} diff --git a/src/Parser/Helper/GetDataInterface.php b/src/Parser/Helper/GetDataInterface.php new file mode 100644 index 00000000..0b384c78 --- /dev/null +++ b/src/Parser/Helper/GetDataInterface.php @@ -0,0 +1,104 @@ + + * @author Thomas Müller + * @copyright Copyright (c) 1998-2014 Browser Capabilities Project + * @version 3.0 + * @license http://www.opensource.org/licenses/MIT MIT License + * @link https://github.com/browscap/browscap-php/ + */ +interface GetDataInterface +{ + /** + * Gets a cache instance + * + * @return \BrowscapPHP\Cache\BrowscapCache + */ + public function getCache(); + + /** + * Sets a cache instance + * + * @param \BrowscapPHP\Cache\BrowscapCache $cache + * + * @return \BrowscapPHP\Parser\Helper\GetDataInterface + */ + public function setCache(BrowscapCache $cache); + + /** + * Sets a logger instance + * + * @param \Psr\Log\LoggerInterface $logger + * + * @return \BrowscapPHP\Parser\Helper\GetDataInterface + */ + public function setLogger(LoggerInterface $logger); + + /** + * Returns a logger instance + * + * @return \Psr\Log\LoggerInterface $logger + */ + public function getLogger(); + + /** + * @param \BrowscapPHP\Helper\Quoter $quoter + * + * @return \BrowscapPHP\Parser\Helper\GetDataInterface + */ + public function setQuoter(Quoter $quoter); + + /** + * @return \BrowscapPHP\Helper\Quoter + */ + public function getQuoter(); + + /** + * Gets the settings for a given pattern (method calls itself to + * get the data from the parent patterns) + * + * @param string $pattern + * @param array $settings + * @return array + */ + public function getSettings($pattern, array $settings = array()); +} diff --git a/src/Parser/Helper/GetPatternInterface.php b/src/Parser/Helper/GetPatternInterface.php index 510ced22..07a9668e 100644 --- a/src/Parser/Helper/GetPatternInterface.php +++ b/src/Parser/Helper/GetPatternInterface.php @@ -34,7 +34,7 @@ use Psr\Log\LoggerInterface; /** - * interface for the parser helper + * interface for the parser patternHelper * * @category Browscap-PHP * @package Parser\Helper diff --git a/src/Parser/Ini.php b/src/Parser/Ini.php index 07a74062..7bb4d120 100644 --- a/src/Parser/Ini.php +++ b/src/Parser/Ini.php @@ -31,13 +31,8 @@ namespace BrowscapPHP\Parser; use BrowscapPHP\Cache\BrowscapCache; -use BrowscapPHP\Data\PropertyFormatter; -use BrowscapPHP\Data\PropertyHolder; use BrowscapPHP\Formatter\FormatterInterface; -use BrowscapPHP\Helper\Quoter; use BrowscapPHP\Parser\Helper\GetPatternInterface; -use BrowscapPHP\Parser\Helper\Pattern; -use BrowscapPHP\Parser\Helper\SubKey; use Psr\Log\LoggerInterface; /** @@ -69,7 +64,12 @@ class Ini implements ParserInterface /** * @var Helper\GetPatternInterface */ - private $helper = null; + private $patternHelper = null; + + /** + * @var Helper\GetDataInterface + */ + private $dataHelper = null; /** * Formatter to use @@ -82,13 +82,13 @@ class Ini implements ParserInterface private $logger = null; /** - * @param \BrowscapPHP\Parser\Helper\GetPatternInterface $helper + * @param \BrowscapPHP\Parser\Helper\GetPatternInterface $patternHelper * * @return \BrowscapPHP\Parser\Ini */ - public function setHelper(GetPatternInterface $helper) + public function setPatternHelper(GetPatternInterface $patternHelper) { - $this->helper = $helper; + $this->patternHelper = $patternHelper; return $this; } @@ -96,9 +96,29 @@ public function setHelper(GetPatternInterface $helper) /** * @return \BrowscapPHP\Parser\Helper\GetPatternInterface */ - public function getHelper() + public function getPatternHelper() { - return $this->helper; + return $this->patternHelper; + } + + /** + * @param \BrowscapPHP\Parser\Helper\GetDataInterface $dataHelper + * + * @return \BrowscapPHP\Parser\Ini + */ + public function setDataHelper(Helper\GetDataInterface $dataHelper) + { + $this->dataHelper = $dataHelper; + + return $this; + } + + /** + * @return \BrowscapPHP\Parser\Helper\GetDataInterface + */ + public function getDataHelper() + { + return $this->dataHelper; } /** @@ -183,7 +203,7 @@ public function getBrowser($userAgent) $userAgent = strtolower($userAgent); $formatter = null; - foreach ($this->getHelper()->getPatterns($userAgent) as $patterns) { + foreach ($this->getPatternHelper()->getPatterns($userAgent) as $patterns) { $patternToMatch = '/^(?:'.str_replace("\t", ')|(?:', $patterns).')$/i'; if (!preg_match($patternToMatch, $userAgent)) { @@ -202,16 +222,16 @@ public function getBrowser($userAgent) // Insert the digits back into the pattern, so that we can search the settings for it if (count($matches) > 1) { array_shift($matches); - foreach ($matches as $one_match) { + foreach ($matches as $oneMatch) { $numPos = strpos($pattern, '(\d)'); - $pattern = substr_replace($pattern, $one_match, $numPos, 4); + $pattern = substr_replace($pattern, $oneMatch, $numPos, 4); } } // Try to get settings - as digits have been replaced to speed up the pattern search (up to 90 faster), // we won't always find the data in the first step - so check if settings have been found and if not, // search for the next pattern. - $settings = $this->getSettings($pattern); + $settings = $this->getDataHelper()->getSettings($pattern); if (count($settings) > 0) { $formatter = $this->getFormatter(); @@ -226,114 +246,4 @@ public function getBrowser($userAgent) return $formatter; } - - /** - * Gets the settings for a given pattern (method calls itself to - * get the data from the parent patterns) - * - * @param string $pattern - * @param array $settings - * @return array - */ - private function getSettings($pattern, array $settings = array()) - { - $quoterHelper = new Quoter(); - - // The pattern has been pre-quoted on generation to speed up the pattern search, - // but for this check we need the unquoted version - $unquotedPattern = $quoterHelper->pregUnQuote($pattern); - - // Try to get settings for the pattern - $addedSettings = $this->getIniPart($unquotedPattern); - - // set some additional data - if (count($settings) === 0) { - // The optimization with replaced digits get can now result in setting searches, for which we - // won't find a result - so only add the pattern information, is settings have been found. - // - // If not an empty array will be returned and the calling function can easily check if a pattern - // has been found. - if (count($addedSettings) > 0) { - $settings['browser_name_regex'] = '/^' . $pattern . '$/'; - $settings['browser_name_pattern'] = $unquotedPattern; - } - } - - // check if parent pattern set, only keep the first one - $parentPattern = null; - if (isset($addedSettings['Parent'])) { - $parentPattern = $addedSettings['Parent']; - - if (isset($settings['Parent'])) { - unset($addedSettings['Parent']); - } - } - - // merge settings - $settings += $addedSettings; - - if ($parentPattern !== null) { - return $this->getSettings($quoterHelper->pregQuote($parentPattern), $settings); - } - - return $settings; - } - - /** - * Gets the relevant part (array of settings) of the ini file for a given pattern. - * - * @param string $pattern - * @return array - */ - private function getIniPart($pattern) - { - $pattern = strtolower($pattern); - $patternhash = Pattern::getHashForParts($pattern); - $subkey = SubKey::getIniPartCacheSubKey($patternhash); - - if (!$this->getCache()->hasItem('browscap.iniparts.'.$subkey, true)) { - $this->getLogger()->debug('cache key "browscap.iniparts.'.$subkey.'" not found'); - - return array(); - } - - $success = null; - $file = $this->getCache()->getItem('browscap.iniparts.'.$subkey, true, $success); - - if (!$success) { - $this->getLogger()->debug('cache key "browscap.iniparts.'.$subkey.'" not found'); - - return array(); - } - - if (!is_array($file) || !count($file)) { - $this->getLogger()->debug('cache key "browscap.iniparts.'.$subkey.'" was empty'); - - return array(); - } - - $propertyHolder = new PropertyHolder(); - $propertyFormatter = new PropertyFormatter(); - $propertyFormatter->setPropertyHolder($propertyHolder); - - $return = array(); - foreach ($file as $buffer) { - list($tmpBuffer, $patterns) = explode("\t", $buffer, 2); - - if ($tmpBuffer === $patternhash) { - $return = json_decode($patterns, true); - - foreach (array_keys($return) as $property) { - $return[$property] = $propertyFormatter->formatPropertyValue( - $return[$property], - $property - ); - } - - break; - } - } - - return $return; - } } diff --git a/src/Parser/ParserInterface.php b/src/Parser/ParserInterface.php index 415ed65f..4b877051 100644 --- a/src/Parser/ParserInterface.php +++ b/src/Parser/ParserInterface.php @@ -32,7 +32,9 @@ use BrowscapPHP\Cache\BrowscapCache; use BrowscapPHP\Formatter\FormatterInterface; +use BrowscapPHP\Parser\Helper\GetDataInterface; use BrowscapPHP\Parser\Helper\GetPatternInterface; +use Psr\Log\LoggerInterface; /** * the interface for the ini parser class @@ -51,21 +53,33 @@ interface ParserInterface /** * @param \BrowscapPHP\Parser\Helper\GetPatternInterface $helper * - * @return \BrowscapPHP\Parser\Ini + * @return \BrowscapPHP\Parser\ParserInterface */ - public function setHelper(GetPatternInterface $helper); + public function setPatternHelper(GetPatternInterface $helper); /** * @return \BrowscapPHP\Parser\Helper\GetPatternInterface */ - public function getHelper(); + public function getPatternHelper(); + + /** + * @param \BrowscapPHP\Parser\Helper\GetDataInterface $dataHelper + * + * @return \BrowscapPHP\Parser\ParserInterface + */ + public function setDataHelper(GetDataInterface $dataHelper); + + /** + * @return \BrowscapPHP\Parser\Helper\GetDataInterface + */ + public function getDataHelper(); /** * Set theformatter instance to use for the getBrowser() result * * @param \BrowscapPHP\Formatter\FormatterInterface $formatter * - * @return \BrowscapPHP\Parser\Ini + * @return \BrowscapPHP\Parser\ParserInterface */ public function setFormatter(FormatterInterface $formatter); @@ -86,10 +100,26 @@ public function getCache(); * * @param \BrowscapPHP\Cache\BrowscapCache $cache * - * @return \BrowscapPHP\Parser\Ini + * @return \BrowscapPHP\Parser\ParserInterface */ public function setCache(BrowscapCache $cache); + /** + * Sets a logger instance + * + * @param \Psr\Log\LoggerInterface $logger + * + * @return \BrowscapPHP\Parser\ParserInterface + */ + public function setLogger(LoggerInterface $logger); + + /** + * Returns a logger instance + * + * @return \Psr\Log\LoggerInterface $logger + */ + public function getLogger(); + /** * Gets the browser data formatr for the given user agent * (or null if no data avaailble, no even the default browser) diff --git a/tests/BrowscapTest.php b/tests/BrowscapTest.php index 75e934e0..f023c94f 100644 --- a/tests/BrowscapTest.php +++ b/tests/BrowscapTest.php @@ -125,14 +125,14 @@ public function testSetGetParser() { $parser = $this->getMock( '\BrowscapPHP\Parser\Ini', - array('setHelper', 'setFormatter', 'setCache', 'setLogger'), + array('setPatternHelper', 'setFormatter', 'setCache', 'setLogger'), array(), '', false ); $parser ->expects(self::once()) - ->method('setHelper') + ->method('setPatternHelper') ->will(self::returnSelf()) ; $parser diff --git a/tests/Helper/ConverterTest.php b/tests/Helper/ConverterTest.php index 235a0cce..30ff9d79 100644 --- a/tests/Helper/ConverterTest.php +++ b/tests/Helper/ConverterTest.php @@ -292,10 +292,13 @@ public function testConvertString() ->method('exists') ->will(self::returnValue(false)); - $logger = $this->getMock('\Monolog\Logger', array('info'), array(), '', false); + $logger = $this->getMock('\Monolog\Logger', array('info', 'error'), array('test'), ''); $logger->expects(self::exactly(4)) ->method('info') ->will(self::returnValue(false)); + $logger->expects(self::never()) + ->method('error') + ->will(self::returnValue(false)); $this->object->setFilesystem($file); $this->object->setLogger($logger); @@ -303,7 +306,7 @@ public function testConvertString() $cache = $this->getMock('\BrowscapPHP\Cache\BrowscapCache', array('setItem'), array(), '', false); $cache->expects(self::any()) ->method('setItem') - ->will(self::returnValue(false)); + ->will(self::returnValue(true)); $this->object->setCache($cache); @@ -425,10 +428,13 @@ public function testConvertStringWithoutPatternFaound() ->method('exists') ->will(self::returnValue(false)); - $logger = $this->getMock('\Monolog\Logger', array('info'), array(), ''); + $logger = $this->getMock('\Monolog\Logger', array('info', 'error'), array('test')); $logger->expects(self::exactly(4)) ->method('info') ->will(self::returnValue(false)); + $logger->expects(self::never()) + ->method('error') + ->will(self::returnValue(false)); $this->object->setFilesystem($file); $this->object->setLogger($logger); @@ -436,7 +442,7 @@ public function testConvertStringWithoutPatternFaound() $cache = $this->getMock('\BrowscapPHP\Cache\BrowscapCache', array('setItem'), array(), '', false); $cache->expects(self::any()) ->method('setItem') - ->will(self::returnValue(false)); + ->will(self::returnValue(true)); $this->object->setCache($cache); diff --git a/tests/Parser/IniTest.php b/tests/Parser/IniTest.php index 0ce056c0..71eeeadf 100644 --- a/tests/Parser/IniTest.php +++ b/tests/Parser/IniTest.php @@ -61,8 +61,8 @@ public function testSetGetHelper() /** @var \BrowscapPHP\Parser\Helper\GetPattern $helper */ $helper = $this->getMock('\BrowscapPHP\Parser\Helper\GetPattern', array(), array(), '', false); - self::assertSame($this->object, $this->object->setHelper($helper)); - self::assertSame($helper, $this->object->getHelper()); + self::assertSame($this->object, $this->object->setPatternHelper($helper)); + self::assertSame($helper, $this->object->getPatternHelper()); } /** From 16f2aeda8f14d49b0eab52f13977157efe0e9159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 16 Sep 2015 20:18:23 +0200 Subject: [PATCH 17/24] fix coding style --- .gitignore | 1 + .travis.yml | 4 ++-- src/IniParser/IniParser.php | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index ed9f0aa5..ad73239e 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ composer.lock PHP_BrowscapINI resources ergebnis.txt +tests/data/ \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 7feae96e..69264717 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,6 +29,6 @@ script: - php vendor/bin/phpcs --standard=psr2 -pn src tests after_script: - - wget https://scrutinizer-ci.com/ocular.phar - - php ocular.phar code-coverage:upload --format=php-clover coverage.clover + - if [ "`phpenv version-name`" != "hhvm" ]; then wget https://scrutinizer-ci.com/ocular.phar; fi + - if [ "`phpenv version-name`" != "hhvm" ]; then php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi diff --git a/src/IniParser/IniParser.php b/src/IniParser/IniParser.php index d6622eae..7aa350d6 100644 --- a/src/IniParser/IniParser.php +++ b/src/IniParser/IniParser.php @@ -68,7 +68,7 @@ class IniParser * is limited by the internal regular expression limits. * @var int */ - CONST COUNT_PATTERN = 50; + const COUNT_PATTERN = 50; /** * Creates new ini part cache files From 575bba609503ba5e1c186f863959a5ec88a4e89f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 16 Sep 2015 20:34:08 +0200 Subject: [PATCH 18/24] skipping out a test on hhvm --- tests/Command/ConvertCommandTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/Command/ConvertCommandTest.php b/tests/Command/ConvertCommandTest.php index 99c23263..8ea5ec1f 100644 --- a/tests/Command/ConvertCommandTest.php +++ b/tests/Command/ConvertCommandTest.php @@ -104,6 +104,10 @@ public function testConfigure() */ public function testExecute() { + if (defined('HHVM_VERSION')) { + self::markTestSkipped('test will fil for hhvm'); + } + $input = $this->getMock('\Symfony\Component\Console\Input\ArgvInput', array(), array(), '', false); $objectIniPath = ini_get('browscap'); From 7f789e8b608f3b8ed7f1fe99697c2ab3ce01ddcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 16 Sep 2015 20:37:37 +0200 Subject: [PATCH 19/24] skipping out a test on hhvm --- tests/Command/ConvertCommandTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Command/ConvertCommandTest.php b/tests/Command/ConvertCommandTest.php index 8ea5ec1f..f57ae84b 100644 --- a/tests/Command/ConvertCommandTest.php +++ b/tests/Command/ConvertCommandTest.php @@ -105,7 +105,7 @@ public function testConfigure() public function testExecute() { if (defined('HHVM_VERSION')) { - self::markTestSkipped('test will fil for hhvm'); + self::markTestSkipped('test will fail for hhvm'); } $input = $this->getMock('\Symfony\Component\Console\Input\ArgvInput', array(), array(), '', false); From 5064c6ff493f753c0c37af01359880faff423ca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Thu, 17 Sep 2015 23:32:34 +0200 Subject: [PATCH 20/24] update dependency injection --- src/Browscap.php | 58 +-- src/Cache/BrowscapCache.php | 59 +--- src/Data/PropertyFormatter.php | 8 +- src/Helper/Converter.php | 73 ++-- src/IniParser/IniParser.php | 8 +- src/Parser/Helper/GetData.php | 90 +---- src/Parser/Helper/GetDataInterface.php | 47 +-- src/Parser/Helper/GetPattern.php | 54 +-- src/Parser/Helper/GetPatternInterface.php | 30 +- src/Parser/Helper/SubKey.php | 4 +- src/Parser/Ini.php | 140 +------- src/Parser/ParserInterface.php | 81 +---- tests/Cache/.gitkeep | 0 tests/Cache/BrowscapCacheTest.php | 330 ------------------ .../Converter/ConverterConvertStringTest.php | 280 +++++++++++++++ .../Helper/{ => Converter}/ConverterTest.php | 297 +--------------- tests/Parser/Helper/GetPatternTest.php | 46 +-- tests/Parser/IniTest.php | 41 +-- tests/bootstrap.php | 3 + 19 files changed, 423 insertions(+), 1226 deletions(-) create mode 100644 tests/Cache/.gitkeep delete mode 100644 tests/Cache/BrowscapCacheTest.php create mode 100644 tests/Helper/Converter/ConverterConvertStringTest.php rename tests/Helper/{ => Converter}/ConverterTest.php (50%) diff --git a/src/Browscap.php b/src/Browscap.php index 741b9f04..071e24a8 100644 --- a/src/Browscap.php +++ b/src/Browscap.php @@ -38,6 +38,7 @@ use BrowscapPHP\Helper\Filesystem; use BrowscapPHP\Helper\IniLoader; use BrowscapPHP\Helper\Quoter; +use BrowscapPHP\Parser\ParserInterface; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; use WurflCache\Adapter\AdapterInterface; @@ -81,7 +82,9 @@ class Browscap */ private $cache = null; - /** @var \Psr\Log\LoggerInterface */ + /** + * @var @var \Psr\Log\LoggerInterface + */ private $logger = null; /** @@ -175,7 +178,7 @@ public function setCache($cache) * * @return \BrowscapPHP\Browscap */ - public function setParser(Parser\ParserInterface $parser) + public function setParser(ParserInterface $parser) { $this->parser = $parser; @@ -185,35 +188,16 @@ public function setParser(Parser\ParserInterface $parser) /** * returns an instance of the used parser class * - * @return Parser\ParserInterface + * @return \BrowscapPHP\Parser\ParserInterface */ public function getParser() { if (null === $this->parser) { - $this->setParser(new Parser\Ini()); - } + $patternHelper = new Parser\Helper\GetPattern($this->getCache(), $this->getLogger()); + $dataHelper = new Parser\Helper\GetData($this->getCache(), $this->getLogger(), new Quoter()); - $patternHelper = new Parser\Helper\GetPattern(); - $dataHelper = new Parser\Helper\GetData(); - - $patternHelper - ->setCache($this->getCache()) - ->setLogger($this->getLogger()) - ; - - $dataHelper - ->setCache($this->getCache()) - ->setLogger($this->getLogger()) - ->setQuoter(new Quoter()) - ; - - $this->parser - ->setPatternHelper($patternHelper) - ->setDataHelper($dataHelper) - ->setFormatter($this->getFormatter()) - ->setCache($this->getCache()) - ->setLogger($this->getLogger()) - ; + $this->parser = new Parser\Ini($patternHelper, $dataHelper, $this->getFormatter()); + } return $this->parser; } @@ -328,11 +312,7 @@ public function convertFile($iniFile) throw new Exception('an error occured while setting the local file', 0, $e); } - $converter = new Converter(); - $converter - ->setLogger($this->getLogger()) - ->setCache($this->getCache()) - ; + $converter = new Converter($this->getLogger(), $this->getCache()); try { $converter->convertString($loader->load()); @@ -348,13 +328,8 @@ public function convertFile($iniFile) */ public function convertString($iniString) { - $converter = new Converter(); - - $converter - ->setLogger($this->getLogger()) - ->setCache($this->getCache()) - ->convertString($iniString) - ; + $converter = new Converter($this->getLogger(), $this->getCache()); + $converter->convertString($iniString); } /** @@ -407,12 +382,7 @@ public function update($remoteFile = IniLoader::PHP_INI) { $this->getLogger()->debug('started fetching remote file'); - $converter = new Converter(); - - $converter - ->setLogger($this->getLogger()) - ->setCache($this->getCache()) - ; + $converter = new Converter($this->getLogger(), $this->getCache()); if (class_exists('\Browscap\Browscap')) { $resourceFolder = 'vendor/browscap/browscap/resources/'; diff --git a/src/Cache/BrowscapCache.php b/src/Cache/BrowscapCache.php index c914d770..bbd252c0 100644 --- a/src/Cache/BrowscapCache.php +++ b/src/Cache/BrowscapCache.php @@ -71,39 +71,12 @@ class BrowscapCache * if needed updated the definitions * * @param \WurflCache\Adapter\AdapterInterface $adapter - * - * @throws \BrowscapPHP\Exception - */ - public function __construct(AdapterInterface $adapter) - { - $this - ->setCacheAdapter($adapter) - ->setUpdateInterval(self::CACHE_LIVETIME) - ; - } - - /** - * sets the cache adapter - * - * @param \WurflCache\Adapter\AdapterInterface $adapter - * - * @return \BrowscapPHP\Cache\BrowscapCache + * @param int $updateInterval */ - public function setCacheAdapter(AdapterInterface $adapter) + public function __construct(AdapterInterface $adapter, $updateInterval = self::CACHE_LIVETIME) { $this->cache = $adapter; - - return $this; - } - - /** - * returns the cache adapter - * - * @return \WurflCache\Adapter\AdapterInterface - */ - public function getCacheAdapter() - { - return $this->cache; + $this->cache->setExpiration((int) $updateInterval); } /** @@ -126,20 +99,6 @@ public function getVersion() return $this->version; } - /** - * set the update intervall - * - * @param integer $updateInterval - * - * @return \BrowscapPHP\Cache\BrowscapCache - */ - public function setUpdateInterval($updateInterval) - { - $this->getCacheAdapter()->setExpiration((int) $updateInterval); - - return $this; - } - /** * Get an item. * @@ -155,14 +114,14 @@ public function getItem($cacheId, $withVersion = true, & $success = null) $cacheId .= '.'.$this->getVersion(); } - if (!$this->getCacheAdapter()->hasItem($cacheId)) { + if (!$this->cache->hasItem($cacheId)) { $success = false; return null; } $success = null; - $data = $this->getCacheAdapter()->getItem($cacheId, $success); + $data = $this->cache->getItem($cacheId, $success); if (!isset($data['content'])) { $success = false; @@ -194,7 +153,7 @@ public function setItem($cacheId, $content, $withVersion = true) } // Save and return - return $this->getCacheAdapter()->setItem($cacheId, $data); + return $this->cache->setItem($cacheId, $data); } /** @@ -211,7 +170,7 @@ public function hasItem($cacheId, $withVersion = true) $cacheId .= '.'.$this->getVersion(); } - return $this->getCacheAdapter()->hasItem($cacheId); + return $this->cache->hasItem($cacheId); } /** @@ -228,7 +187,7 @@ public function removeItem($cacheId, $withVersion = true) $cacheId .= '.'.$this->getVersion(); } - return $this->getCacheAdapter()->removeItem($cacheId); + return $this->cache->removeItem($cacheId); } /** @@ -238,6 +197,6 @@ public function removeItem($cacheId, $withVersion = true) */ public function flush() { - return $this->getCacheAdapter()->flush(); + return $this->cache->flush(); } } diff --git a/src/Data/PropertyFormatter.php b/src/Data/PropertyFormatter.php index 89d5fe34..a31ef992 100644 --- a/src/Data/PropertyFormatter.php +++ b/src/Data/PropertyFormatter.php @@ -1,6 +1,6 @@ propertyHolder = $propertyHolder; } diff --git a/src/Helper/Converter.php b/src/Helper/Converter.php index d226bd1a..590e3da7 100644 --- a/src/Helper/Converter.php +++ b/src/Helper/Converter.php @@ -61,6 +61,11 @@ class Converter const COMPRESSION_PATTERN_START = '@'; const COMPRESSION_PATTERN_DELIMITER = '|'; + /** + * The key to search for in the INI file to find the browscap settings + */ + const BROWSCAP_VERSION_KEY = 'GJK_Browscap_Version'; + /** @var \Psr\Log\LoggerInterface */ private $logger = null; @@ -86,51 +91,15 @@ class Converter private $iniVersion = 0; /** - * Sets a logger instance - * - * @param \Psr\Log\LoggerInterface $logger - * - * @return \BrowscapPHP\Helper\Converter - */ - public function setLogger(LoggerInterface $logger) - { - $this->logger = $logger; - - return $this; - } - - /** - * Returns a logger instance - * - * @return \Psr\Log\LoggerInterface $logger - */ - public function getLogger() - { - return $this->logger; - } - - /** - * Sets a cache instance + * class constructor * + * @param \Psr\Log\LoggerInterface $logger * @param \BrowscapPHP\Cache\BrowscapCache $cache - * - * @return \BrowscapPHP\Helper\Converter */ - public function setCache(BrowscapCache $cache) + public function __construct(LoggerInterface $logger, BrowscapCache $cache) { - $this->cache = $cache; - - return $this; - } - - /** - * Returns a cache instance - * - * @return \BrowscapPHP\Cache\BrowscapCache $cache - */ - public function getCache() - { - return $this->cache; + $this->logger = $logger; + $this->cache = $cache; } /** @@ -171,11 +140,11 @@ public function convertFile($iniFile) throw FileNotFoundException::fileNotFound($iniFile); } - $this->getLogger()->info('start reading file'); + $this->logger->info('start reading file'); $iniString = file_get_contents($iniFile); - $this->getLogger()->info('finished reading file'); + $this->logger->info('finished reading file'); $this->convertString($iniString); } @@ -187,29 +156,29 @@ public function convertString($iniString) { $iniParser = new IniParser(); - $this->getLogger()->info('start creating patterns from the ini data'); + $this->logger->info('start creating patterns from the ini data'); foreach ($iniParser->createPatterns($iniString) as $patternsHashList) { foreach ($patternsHashList as $subkey => $content) { if (!$this->cache->setItem('browscap.patterns.' . $subkey, $content, true)) { - $this->getLogger()->error('could not write pattern data "' . $subkey . '" to the cache'); + $this->logger->error('could not write pattern data "' . $subkey . '" to the cache'); } } } - $this->getLogger()->info('finished creating patterns from the ini data'); + $this->logger->info('finished creating patterns from the ini data'); - $this->getLogger()->info('start creating data from the ini data'); + $this->logger->info('start creating data from the ini data'); foreach ($iniParser->createIniParts($iniString) as $patternsContentList) { foreach ($patternsContentList as $subkey => $content) { - if (!$this->getCache()->setItem('browscap.iniparts.' . $subkey, $content, true)) { - $this->getLogger()->error('could not write property data "' . $subkey . '" to the cache'); + if (!$this->cache->setItem('browscap.iniparts.' . $subkey, $content, true)) { + $this->logger->error('could not write property data "' . $subkey . '" to the cache'); } } } - $this->getLogger()->info('finished creating data from the ini data'); + $this->logger->info('finished creating data from the ini data'); } /** @@ -222,7 +191,7 @@ public function convertString($iniString) public function getIniVersion($iniString) { $quoterHelper = new Quoter(); - $key = $quoterHelper->pregQuote(Ini::BROWSCAP_VERSION_KEY); + $key = $quoterHelper->pregQuote(self::BROWSCAP_VERSION_KEY); if (preg_match('/\.*\[' . $key . '\][^\[]*Version=(\d+)\D.*/', $iniString, $matches)) { if (isset($matches[1])) { @@ -254,7 +223,7 @@ public function setVersion($version) */ public function storeVersion() { - $this->getCache()->setItem('browscap.version', $this->iniVersion, false); + $this->cache->setItem('browscap.version', $this->iniVersion, false); return $this; } diff --git a/src/IniParser/IniParser.php b/src/IniParser/IniParser.php index 7aa350d6..24c62abb 100644 --- a/src/IniParser/IniParser.php +++ b/src/IniParser/IniParser.php @@ -1,6 +1,6 @@ setPropertyHolder($propertyHolder); + $propertyFormatter = new PropertyFormatter(new PropertyHolder()); foreach ($patternpositions as $position => $pattern) { $pattern = strtolower($pattern); diff --git a/src/Parser/Helper/GetData.php b/src/Parser/Helper/GetData.php index b2033145..4025fe8a 100644 --- a/src/Parser/Helper/GetData.php +++ b/src/Parser/Helper/GetData.php @@ -1,6 +1,6 @@ cache; - } - - /** - * Sets a cache instance + * class contsructor * * @param \BrowscapPHP\Cache\BrowscapCache $cache - * - * @return \BrowscapPHP\Parser\Helper\GetData - */ - public function setCache(BrowscapCache $cache) - { - $this->cache = $cache; - - return $this; - } - - /** - * Sets a logger instance - * - * @param \Psr\Log\LoggerInterface $logger - * - * @return \BrowscapPHP\Parser\Helper\GetData + * @param \Psr\Log\LoggerInterface $logger + * @param \BrowscapPHP\Helper\Quoter $quoter */ - public function setLogger(LoggerInterface $logger) + public function __contruct(BrowscapCache $cache, LoggerInterface $logger, Quoter $quoter) { + $this->cache = $cache; $this->logger = $logger; - - return $this; - } - - /** - * Returns a logger instance - * - * @return \Psr\Log\LoggerInterface $logger - */ - public function getLogger() - { - return $this->logger; - } - - /** - * @param \BrowscapPHP\Helper\Quoter $quoter - * - * @return \BrowscapPHP\Parser\Helper\GetData - */ - public function setQuoter(Quoter $quoter) - { $this->quoter = $quoter; - - return $this; - } - - /** - * @return \BrowscapPHP\Helper\Quoter - */ - public function getQuoter() - { - return $this->quoter; } /** @@ -146,10 +92,10 @@ public function getQuoter() * @return array */ public function getSettings($pattern, array $settings = array()) - { + {print_r(new \Exception('test')); // The pattern has been pre-quoted on generation to speed up the pattern search, // but for this check we need the unquoted version - $unquotedPattern = $this->getQuoter()->pregUnQuote($pattern); + $unquotedPattern = $this->quoter->pregUnQuote($pattern); // Try to get settings for the pattern $addedSettings = $this->getIniPart($unquotedPattern); @@ -181,7 +127,7 @@ public function getSettings($pattern, array $settings = array()) $settings += $addedSettings; if ($parentPattern !== null) { - return $this->getSettings($this->getQuoter()->pregQuote($parentPattern), $settings); + return $this->getSettings($this->quoter->pregQuote($parentPattern), $settings); } return $settings; @@ -199,32 +145,30 @@ private function getIniPart($pattern) $patternhash = Pattern::getHashForParts($pattern); $subkey = SubKey::getIniPartCacheSubKey($patternhash); - if (!$this->getCache()->hasItem('browscap.iniparts.'.$subkey, true)) { - $this->getLogger()->debug('cache key "browscap.iniparts.'.$subkey.'" not found'); + if (!$this->cache->hasItem('browscap.iniparts.'.$subkey, true)) { + $this->logger->debug('cache key "browscap.iniparts.'.$subkey.'" not found'); return array(); } $success = null; - $file = $this->getCache()->getItem('browscap.iniparts.'.$subkey, true, $success); + $file = $this->cache->getItem('browscap.iniparts.'.$subkey, true, $success); if (!$success) { - $this->getLogger()->debug('cache key "browscap.iniparts.'.$subkey.'" not found'); + $this->logger->debug('cache key "browscap.iniparts.'.$subkey.'" not found'); return array(); } if (!is_array($file) || !count($file)) { - $this->getLogger()->debug('cache key "browscap.iniparts.'.$subkey.'" was empty'); + $this->logger->debug('cache key "browscap.iniparts.'.$subkey.'" was empty'); return array(); } - $propertyHolder = new PropertyHolder(); - $propertyFormatter = new PropertyFormatter(); - $propertyFormatter->setPropertyHolder($propertyHolder); + $propertyFormatter = new PropertyFormatter(new PropertyHolder()); + $return = array(); - $return = array(); foreach ($file as $buffer) { list($tmpBuffer, $patterns) = explode("\t", $buffer, 2); diff --git a/src/Parser/Helper/GetDataInterface.php b/src/Parser/Helper/GetDataInterface.php index 0b384c78..3a29d824 100644 --- a/src/Parser/Helper/GetDataInterface.php +++ b/src/Parser/Helper/GetDataInterface.php @@ -1,6 +1,6 @@ cache; - } - - /** - * Sets a cache instance + * class contructor * * @param \BrowscapPHP\Cache\BrowscapCache $cache - * - * @return \BrowscapPHP\Parser\Helper\GetPattern - */ - public function setCache(BrowscapCache $cache) - { - $this->cache = $cache; - - return $this; - } - - /** - * Sets a logger instance - * - * @param \Psr\Log\LoggerInterface $logger - * - * @return \BrowscapPHP\Parser\Helper\GetPattern + * @param \Psr\Log\LoggerInterface $logger */ - public function setLogger(LoggerInterface $logger) + public function __construct(BrowscapCache $cache, LoggerInterface $logger) { + $this->cache = $cache; $this->logger = $logger; - - return $this; - } - - /** - * Returns a logger instance - * - * @return \Psr\Log\LoggerInterface $logger - */ - public function getLogger() - { - return $this->logger; } /** @@ -132,23 +96,23 @@ public function getPatterns($userAgent) foreach ($starts as $tmpStart) { $tmpSubkey = SubKey::getPatternCacheSubkey($tmpStart); - if (!$this->getCache()->hasItem('browscap.patterns.'.$tmpSubkey, true)) { - $this->getLogger()->debug('cache key "browscap.patterns.'.$tmpSubkey.'" not found'); + if (!$this->cache->hasItem('browscap.patterns.'.$tmpSubkey, true)) { + $this->logger->debug('cache key "browscap.patterns.'.$tmpSubkey.'" not found'); continue; } $success = null; - $file = $this->getCache()->getItem('browscap.patterns.'.$tmpSubkey, true, $success); + $file = $this->cache->getItem('browscap.patterns.'.$tmpSubkey, true, $success); if (!$success) { - $this->getLogger()->debug('cache key "browscap.patterns.'.$tmpSubkey.'" not found'); + $this->logger->debug('cache key "browscap.patterns.'.$tmpSubkey.'" not found'); continue; } if (!is_array($file) || !count($file)) { - $this->getLogger()->debug('cache key "browscap.patterns.'.$tmpSubkey.'" was empty'); + $this->logger->debug('cache key "browscap.patterns.'.$tmpSubkey.'" was empty'); continue; } diff --git a/src/Parser/Helper/GetPatternInterface.php b/src/Parser/Helper/GetPatternInterface.php index 07a9668e..adf4e5a7 100644 --- a/src/Parser/Helper/GetPatternInterface.php +++ b/src/Parser/Helper/GetPatternInterface.php @@ -48,36 +48,12 @@ interface GetPatternInterface { /** - * Gets a cache instance - * - * @return \BrowscapPHP\Cache\BrowscapCache - */ - public function getCache(); - - /** - * Sets a cache instance + * class contructor * * @param \BrowscapPHP\Cache\BrowscapCache $cache - * - * @return GetPatternInterface - */ - public function setCache(BrowscapCache $cache); - - /** - * Sets a logger instance - * - * @param \Psr\Log\LoggerInterface $logger - * - * @return \BrowscapPHP\Parser\Helper\GetPatternInterface - */ - public function setLogger(LoggerInterface $logger); - - /** - * Returns a logger instance - * - * @return \Psr\Log\LoggerInterface $logger + * @param \Psr\Log\LoggerInterface $logger */ - public function getLogger(); + public function __construct(BrowscapCache $cache, LoggerInterface $logger); /** * Gets some possible patterns that have to be matched against the user agent. With the given diff --git a/src/Parser/Helper/SubKey.php b/src/Parser/Helper/SubKey.php index ccea1477..dfee7054 100644 --- a/src/Parser/Helper/SubKey.php +++ b/src/Parser/Helper/SubKey.php @@ -1,6 +1,6 @@ patternHelper = $patternHelper; - - return $this; - } - - /** - * @return \BrowscapPHP\Parser\Helper\GetPatternInterface - */ - public function getPatternHelper() - { - return $this->patternHelper; - } - - /** - * @param \BrowscapPHP\Parser\Helper\GetDataInterface $dataHelper - * - * @return \BrowscapPHP\Parser\Ini - */ - public function setDataHelper(Helper\GetDataInterface $dataHelper) - { - $this->dataHelper = $dataHelper; - - return $this; - } - - /** - * @return \BrowscapPHP\Parser\Helper\GetDataInterface - */ - public function getDataHelper() - { - return $this->dataHelper; - } - - /** - * Set the formatter instance to use for the getBrowser() result - * - * @param \BrowscapPHP\Formatter\FormatterInterface $formatter - * - * @return \BrowscapPHP\Parser\Ini - */ - public function setFormatter(FormatterInterface $formatter) - { - $this->formatter = $formatter; - - return $this; - } - - /** - * @return \BrowscapPHP\Formatter\FormatterInterface - */ - public function getFormatter() - { - return $this->formatter; - } - - /** - * Gets a cache instance - * - * @return \BrowscapPHP\Cache\BrowscapCache - */ - public function getCache() - { - return $this->cache; - } - - /** - * Sets a cache instance - * - * @param \BrowscapPHP\Cache\BrowscapCache $cache - * - * @return \BrowscapPHP\Parser\Ini - */ - public function setCache(BrowscapCache $cache) - { - $this->cache = $cache; - - return $this; - } - - /** - * Sets a logger instance - * - * @param \Psr\Log\LoggerInterface $logger - * - * @return \BrowscapPHP\Parser\Ini - */ - public function setLogger(LoggerInterface $logger) - { - $this->logger = $logger; - - return $this; - } - - /** - * Returns a logger instance - * - * @return \Psr\Log\LoggerInterface $logger - */ - public function getLogger() - { - return $this->logger; + $this->dataHelper = $dataHelper; + $this->formatter = $formatter; } /** @@ -203,7 +95,7 @@ public function getBrowser($userAgent) $userAgent = strtolower($userAgent); $formatter = null; - foreach ($this->getPatternHelper()->getPatterns($userAgent) as $patterns) { + foreach ($this->patternHelper->getPatterns($userAgent) as $patterns) { $patternToMatch = '/^(?:'.str_replace("\t", ')|(?:', $patterns).')$/i'; if (!preg_match($patternToMatch, $userAgent)) { @@ -231,10 +123,10 @@ public function getBrowser($userAgent) // Try to get settings - as digits have been replaced to speed up the pattern search (up to 90 faster), // we won't always find the data in the first step - so check if settings have been found and if not, // search for the next pattern. - $settings = $this->getDataHelper()->getSettings($pattern); + $settings = $this->dataHelper->getSettings($pattern); if (count($settings) > 0) { - $formatter = $this->getFormatter(); + $formatter = $this->formatter; $formatter->setData($settings); break 2; } diff --git a/src/Parser/ParserInterface.php b/src/Parser/ParserInterface.php index 4b877051..f4f548f0 100644 --- a/src/Parser/ParserInterface.php +++ b/src/Parser/ParserInterface.php @@ -30,11 +30,9 @@ namespace BrowscapPHP\Parser; -use BrowscapPHP\Cache\BrowscapCache; use BrowscapPHP\Formatter\FormatterInterface; use BrowscapPHP\Parser\Helper\GetDataInterface; use BrowscapPHP\Parser\Helper\GetPatternInterface; -use Psr\Log\LoggerInterface; /** * the interface for the ini parser class @@ -51,81 +49,24 @@ interface ParserInterface { /** - * @param \BrowscapPHP\Parser\Helper\GetPatternInterface $helper + * class constructor * - * @return \BrowscapPHP\Parser\ParserInterface + * @param \BrowscapPHP\Parser\Helper\GetPatternInterface $patternHelper + * @param \BrowscapPHP\Parser\Helper\GetDataInterface $dataHelper + * @param \BrowscapPHP\Formatter\FormatterInterface $formatter */ - public function setPatternHelper(GetPatternInterface $helper); - - /** - * @return \BrowscapPHP\Parser\Helper\GetPatternInterface - */ - public function getPatternHelper(); - - /** - * @param \BrowscapPHP\Parser\Helper\GetDataInterface $dataHelper - * - * @return \BrowscapPHP\Parser\ParserInterface - */ - public function setDataHelper(GetDataInterface $dataHelper); - - /** - * @return \BrowscapPHP\Parser\Helper\GetDataInterface - */ - public function getDataHelper(); - - /** - * Set theformatter instance to use for the getBrowser() result - * - * @param \BrowscapPHP\Formatter\FormatterInterface $formatter - * - * @return \BrowscapPHP\Parser\ParserInterface - */ - public function setFormatter(FormatterInterface $formatter); - - /** - * @return \BrowscapPHP\Formatter\FormatterInterface - */ - public function getFormatter(); - - /** - * Gets a cache instance - * - * @return \BrowscapPHP\Cache\BrowscapCache - */ - public function getCache(); - - /** - * Sets a cache instance - * - * @param \BrowscapPHP\Cache\BrowscapCache $cache - * - * @return \BrowscapPHP\Parser\ParserInterface - */ - public function setCache(BrowscapCache $cache); - - /** - * Sets a logger instance - * - * @param \Psr\Log\LoggerInterface $logger - * - * @return \BrowscapPHP\Parser\ParserInterface - */ - public function setLogger(LoggerInterface $logger); - - /** - * Returns a logger instance - * - * @return \Psr\Log\LoggerInterface $logger - */ - public function getLogger(); + public function __construct( + GetPatternInterface $patternHelper, + GetDataInterface $dataHelper, + FormatterInterface $formatter + ); /** * Gets the browser data formatr for the given user agent * (or null if no data avaailble, no even the default browser) * - * @param string $user_agent + * @param string $userAgent * @return FormatterInterface|null */ - public function getBrowser($user_agent); + public function getBrowser($userAgent); } diff --git a/tests/Cache/.gitkeep b/tests/Cache/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/tests/Cache/BrowscapCacheTest.php b/tests/Cache/BrowscapCacheTest.php deleted file mode 100644 index 4217b524..00000000 --- a/tests/Cache/BrowscapCacheTest.php +++ /dev/null @@ -1,330 +0,0 @@ - - * @copyright Copyright (c) 2006-2012 Jonathan Stoppani - * @version 1.0 - * @license http://www.opensource.org/licenses/MIT MIT License - * @link https://github.com/GaretJax/BrowscapPHP/ - */ -class BrowscapCacheTest extends \PHPUnit_Framework_TestCase -{ - /** - * @var \BrowscapPHP\Cache\BrowscapCache - */ - private $object = null; - - /** - * Sets up the fixture, for example, open a network connection. - * This method is called before a test is executed. - * - */ - public function setUp() - { - /** @var \WurflCache\Adapter\File $adapter */ - $adapter = $this->getMock('\WurflCache\Adapter\File', array(), array(), '', false); - - $this->object = new BrowscapCache($adapter); - } - - /** - * - */ - public function testSetGetCacheAdapter() - { - /** @var \WurflCache\Adapter\Memcache $adapter */ - $adapter = $this->getMock('\WurflCache\Adapter\Memcache', array(), array(), '', false); - - self::assertSame($this->object, $this->object->setCacheAdapter($adapter)); - self::assertSame($adapter, $this->object->getCacheAdapter()); - } - - /** - * - */ - public function testSetUpdateInterval() - { - self::assertSame($this->object, $this->object->setUpdateInterval(1)); - } - - /** - * - */ - public function testGetVersionNotCached() - { - /** @var \WurflCache\Adapter\Memcache $adapter */ - $adapter = $this->getMock('\WurflCache\Adapter\Memcache', array('hasItem', 'getItem'), array(), '', false); - $adapter - ->expects(self::once()) - ->method('hasItem') - ->will(self::returnValue(false)) - ; - $adapter - ->expects(self::never()) - ->method('getItem') - ->will(self::returnValue(false)) - ; - - $this->object->setCacheAdapter($adapter); - self::assertNull($this->object->getVersion()); - } - - /** - * - */ - public function testGetVersionCached() - { - $adapter = $this->getMock('\WurflCache\Adapter\Memcache', array('hasItem', 'getItem'), array(), '', false); - $adapter - ->expects(self::once()) - ->method('hasItem') - ->will(self::returnValue(true)) - ; - $adapter - ->expects(self::once()) - ->method('getItem') - ->will(self::returnCallback(array($this, 'return42'))) - ; - - $this->object->setCacheAdapter($adapter); - self::assertSame(42, $this->object->getVersion()); - } - - public function return42($cacheId, & $success = null) - { - $success = true; - - switch ($cacheId) { - case 'test.42': - $data = array( - 'content' => serialize('this is a test') - ); - break; - case 'browscap.version': - case 'test': - default: - $data = array( - 'content' => serialize(42) - ); - } - - return $data; - } - - /** - * - */ - public function testGetItemNotCached() - { - $adapter = $this->getMock('\WurflCache\Adapter\Memcache', array('hasItem', 'getItem'), array(), '', false); - $adapter - ->expects(self::once()) - ->method('hasItem') - ->will(self::returnValue(false)) - ; - $adapter - ->expects(self::never()) - ->method('getItem') - ->will(self::returnValue(false)) - ; - - $this->object->setCacheAdapter($adapter); - $success = null; - self::assertNull($this->object->getItem('test', false, $success)); - self::assertFalse($success); - } - - /** - * - */ - public function testGetItemCached() - { - $adapter = $this->getMock('\WurflCache\Adapter\Memcache', array('hasItem', 'getItem'), array(), '', false); - $adapter - ->expects(self::once()) - ->method('hasItem') - ->will(self::returnValue(true)) - ; - $adapter - ->expects(self::once()) - ->method('getItem') - ->will(self::returnCallback(array($this, 'return42'))) - ; - - $this->object->setCacheAdapter($adapter); - $success = null; - self::assertSame(42, $this->object->getItem('test', false, $success)); - self::assertTrue($success); - } - - /** - * - */ - public function testGetItemCachedWithVersion() - { - $adapter = $this->getMock('\WurflCache\Adapter\Memcache', array('hasItem', 'getItem'), array(), '', false); - $adapter - ->expects(self::exactly(2)) - ->method('hasItem') - ->will(self::returnValue(true)) - ; - $adapter - ->expects(self::exactly(2)) - ->method('getItem') - ->will(self::returnCallback(array($this, 'return42'))) - ; - - $this->object->setCacheAdapter($adapter); - $success = null; - self::assertSame('this is a test', $this->object->getItem('test', true, $success)); - self::assertTrue($success); - } - - /** - * - */ - public function testHasItemNotCached() - { - $adapter = $this->getMock('\WurflCache\Adapter\Memcache', array('hasItem'), array(), '', false); - $adapter - ->expects(self::once()) - ->method('hasItem') - ->will(self::returnValue(false)) - ; - - $this->object->setCacheAdapter($adapter); - self::assertFalse($this->object->hasItem('test', false)); - } - - /** - * - */ - public function testHasItemCachedWithVersion() - { - $adapter = $this->getMock('\WurflCache\Adapter\Memcache', array('hasItem', 'getItem'), array(), '', false); - $adapter - ->expects(self::exactly(2)) - ->method('hasItem') - ->will(self::returnValue(true)) - ; - $adapter - ->expects(self::once()) - ->method('getItem') - ->will(self::returnCallback(array($this, 'return42'))) - ; - - $this->object->setCacheAdapter($adapter); - self::assertTrue($this->object->hasItem('test', true)); - } - - /** - * - */ - public function testSetItemWithVersion() - { - $adapter = $this->getMock('\WurflCache\Adapter\Memcache', array('hasItem', 'getItem', 'setItem'), array(), '', false); - $adapter - ->expects(self::once()) - ->method('hasItem') - ->will(self::returnValue(true)) - ; - $adapter - ->expects(self::once()) - ->method('getItem') - ->will(self::returnCallback(array($this, 'return42'))) - ; - $adapter - ->expects(self::once()) - ->method('setItem') - ->will(self::returnValue(true)) - ; - - $this->object->setCacheAdapter($adapter); - self::assertTrue($this->object->setItem('test', true, true)); - } - - /** - * - */ - public function testRemoveItemWithVersion() - { - $adapter = $this->getMock('\WurflCache\Adapter\Memcache', array('hasItem', 'getItem', 'removeItem'), array(), '', false); - $adapter - ->expects(self::once()) - ->method('hasItem') - ->will(self::returnValue(true)) - ; - $adapter - ->expects(self::once()) - ->method('getItem') - ->will(self::returnCallback(array($this, 'return42'))) - ; - $adapter - ->expects(self::once()) - ->method('removeItem') - ->will(self::returnValue(true)) - ; - - $this->object->setCacheAdapter($adapter); - self::assertTrue($this->object->removeItem('test', true)); - } - - /** - * - */ - public function testFlush() - { - $data = array( - 'content' => serialize(42) - ); - - $adapter = $this->getMock('\WurflCache\Adapter\Memcache', array('hasItem', 'getItem', 'flush'), array(), '', false); - $adapter - ->expects(self::never()) - ->method('hasItem') - ->will(self::returnValue(true)) - ; - $adapter - ->expects(self::never()) - ->method('getItem') - ->will(self::returnValue($data)) - ; - $adapter - ->expects(self::once()) - ->method('flush') - ->will(self::returnValue(true)) - ; - - $this->object->setCacheAdapter($adapter); - self::assertTrue($this->object->flush()); - } -} diff --git a/tests/Helper/Converter/ConverterConvertStringTest.php b/tests/Helper/Converter/ConverterConvertStringTest.php new file mode 100644 index 00000000..eba21025 --- /dev/null +++ b/tests/Helper/Converter/ConverterConvertStringTest.php @@ -0,0 +1,280 @@ + + * @copyright Copyright (c) 2006-2012 Jonathan Stoppani + * @version 1.0 + * @license http://www.opensource.org/licenses/MIT MIT License + * @link https://github.com/GaretJax/BrowscapPHP/ + */ +class ConverterConvertStringTest extends \PHPUnit_Framework_TestCase +{ + const STORAGE_DIR = 'storage'; + + /** + * @var \BrowscapPHP\Helper\Converter + */ + private $object = null; + + /** + * @var \org\bovigo\vfs\vfsStreamDirectory + */ + private $root = null; + + public function setUp() + { + $logger = $this->getMock('\Monolog\Logger', array('info'), array(), '', false); + $logger->expects(self::exactly(4)) + ->method('info') + ->will(self::returnValue(false)); + $logger->expects(self::never()) + ->method('error') + ->will(self::returnValue(false)); + + $cache = $this->getMock('\BrowscapPHP\Cache\BrowscapCache', array('setItem'), array(), '', false); + $cache->expects(self::any()) + ->method('setItem') + ->will(self::returnValue(true)); + + $this->object = new Converter($logger, $cache); + } + + /** + * + */ + public function testConvertString() + { + $file = $this->getMock('\BrowscapPHP\Helper\Filesystem', array('exists'), array(), '', false); + $file->expects(self::never()) + ->method('exists') + ->will(self::returnValue(false)); + + $this->object->setFilesystem($file); + + $content = ';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Browscap Version + +[GJK_Browscap_Version] +Version=5031 +Released=Mon, 30 Jun 2014 17:55:58 +0200 +Format=ASP +Type= + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DefaultProperties + +[DefaultProperties] + +Comment=DefaultProperties +Browser=DefaultProperties +Version=0.0 +MajorVer=0 +MinorVer=0 +Platform=unknown +Platform_Version=unknown +Alpha=false +Beta=false +Win16=false +Win32=false +Win64=false +Frames=false +IFrames=false +Tables=false +Cookies=false +BackgroundSounds=false +JavaScript=false +VBScript=false +JavaApplets=false +ActiveXControls=false +isMobileDevice=false +isTablet=false +isSyndicationReader=false +Crawler=false +CssVersion=0 +AolVersion=0 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Ask + +[Ask] + +Parent=DefaultProperties +Comment=Ask +Browser=Ask +Frames=1 +IFrames=1 +Tables=1 +Crawler=1 +Version=0.0 +MajorVer=0 +MinorVer=0 +Platform=unknown +Platform_Version=unknown +Alpha= +Beta= +Win16= +Win32= +Win64= +Cookies= +BackgroundSounds= +JavaScript= +VBScript= +JavaApplets= +ActiveXControls= +isMobileDevice= +isTablet= +isSyndicationReader= +CssVersion=0 +AolVersion=0 + +[Mozilla/?.0 (compatible; Ask Jeeves/Teoma*)] + +Parent=Ask +Browser=Teoma +Comment=Ask +Version=0.0 +MajorVer=0 +MinorVer=0 +Platform=unknown +Platform_Version=unknown +Alpha= +Beta= +Win16= +Win32= +Win64= +Frames=1 +IFrames=1 +Tables=1 +Cookies= +BackgroundSounds= +JavaScript= +VBScript= +JavaApplets= +ActiveXControls= +isMobileDevice= +isTablet= +isSyndicationReader= +Crawler=1 +CssVersion=0 +AolVersion=0 +'; + + self::assertNull($this->object->convertString($content)); + } + + /** + * + */ + public function testConvertStringWithoutPatternFaound() + { + $file = $this->getMock('\BrowscapPHP\Helper\Filesystem', array('exists'), array(), '', false); + $file->expects(self::never()) + ->method('exists') + ->will(self::returnValue(false)); + + $this->object->setFilesystem($file); + + $content = ';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Browscap Version + +[GJK_Browscap_Version] +Version=5031 +Released=Mon, 30 Jun 2014 17:55:58 +0200 +Format=ASP +Type= + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DefaultProperties + +[DefaultProperties] + +Comment=DefaultProperties +Browser=DefaultProperties +Version=0.0 +MajorVer=0 +MinorVer=0 +Platform=unknown +Platform_Version=unknown +Alpha=false +Beta=false +Win16=false +Win32=false +Win64=false +Frames=false +IFrames=false +Tables=false +Cookies=false +BackgroundSounds=false +JavaScript=false +VBScript=false +JavaApplets=false +ActiveXControls=false +isMobileDevice=false +isTablet=false +isSyndicationReader=false +Crawler=false +CssVersion=0 +AolVersion=0 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Ask + +[Ask] + +Parent=DefaultProperties +Comment=Ask +Browser=Ask +Frames=1 +IFrames=1 +Tables=1 +Crawler=1 +Version=0.0 +MajorVer=0 +MinorVer=0 +Platform=unknown +Platform_Version=unknown +Alpha= +Beta= +Win16= +Win32= +Win64= +Cookies= +BackgroundSounds= +JavaScript= +VBScript= +JavaApplets= +ActiveXControls= +isMobileDevice= +isTablet= +isSyndicationReader= +CssVersion=0 +AolVersion=0 +'; + + self::assertNull($this->object->convertString($content)); + } +} diff --git a/tests/Helper/ConverterTest.php b/tests/Helper/Converter/ConverterTest.php similarity index 50% rename from tests/Helper/ConverterTest.php rename to tests/Helper/Converter/ConverterTest.php index 30ff9d79..926939e6 100644 --- a/tests/Helper/ConverterTest.php +++ b/tests/Helper/Converter/ConverterTest.php @@ -1,6 +1,6 @@ object = new Converter(); - } - - /** - * - */ - public function testSetGetLogger() - { - /** @var \Monolog\Logger $logger */ - $logger = $this->getMock('\Monolog\Logger', array(), array(), '', false); - - self::assertSame($this->object, $this->object->setLogger($logger)); - self::assertSame($logger, $this->object->getLogger()); - } + $logger = $this->getMock('\Monolog\Logger', array('info'), array(), '', false); + $logger->expects(self::never()) + ->method('info') + ->will(self::returnValue(false)); - /** - * - */ - public function testSetGetCache() - { - /** @var \BrowscapPHP\Cache\BrowscapCache $cache */ - $cache = $this->getMock('\BrowscapPHP\Cache\BrowscapCache', array(), array(), '', false); + $cache = $this->getMock('\BrowscapPHP\Cache\BrowscapCache', array('setItem'), array(), '', false); + $cache->expects(self::any()) + ->method('setItem') + ->will(self::returnValue(true)); - self::assertSame($this->object, $this->object->setCache($cache)); - self::assertSame($cache, $this->object->getCache()); + $this->object = new Converter($logger, $cache); } /** @@ -105,13 +91,7 @@ public function testConvertMissingFile() ->method('exists') ->will(self::returnValue(false)); - $logger = $this->getMock('\Monolog\Logger', array('info'), array(), '', false); - $logger->expects(self::never()) - ->method('info') - ->will(self::returnValue(false)); - $this->object->setFilesystem($file); - $this->object->setLogger($logger); $this->object->convertFile('testFile'); } @@ -238,13 +218,7 @@ public function testConvertFile() ->method('exists') ->will(self::returnValue(false)); - $logger = $this->getMock('\Monolog\Logger', array('info'), array(), '', false); - $logger->expects(self::never()) - ->method('info') - ->will(self::returnValue(false)); - $this->object->setFilesystem($file); - $this->object->setLogger($logger); $this->object->convertFile(vfsStream::url(self::STORAGE_DIR . DIRECTORY_SEPARATOR . 'test.ini')); } @@ -258,17 +232,7 @@ public function testGetIniVersion() ->method('exists') ->will(self::returnValue(false)); - $logger = $this->getMock('\Monolog\Logger', array('info'), array(), '', false); - $logger->expects(self::never()) - ->method('info') - ->will(self::returnValue(false)); - $this->object->setFilesystem($file); - $this->object->setLogger($logger); - - $cache = $this->getMock('\BrowscapPHP\Cache\BrowscapCache', array(), array(), '', false); - - $this->object->setCache($cache); $content = ';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Browscap Version @@ -281,245 +245,4 @@ public function testGetIniVersion() self::assertSame(5031, $this->object->getIniVersion($content)); self::assertSame($this->object, $this->object->storeVersion()); } - - /** - * - */ - public function testConvertString() - { - $file = $this->getMock('\BrowscapPHP\Helper\Filesystem', array('exists'), array(), '', false); - $file->expects(self::never()) - ->method('exists') - ->will(self::returnValue(false)); - - $logger = $this->getMock('\Monolog\Logger', array('info', 'error'), array('test'), ''); - $logger->expects(self::exactly(4)) - ->method('info') - ->will(self::returnValue(false)); - $logger->expects(self::never()) - ->method('error') - ->will(self::returnValue(false)); - - $this->object->setFilesystem($file); - $this->object->setLogger($logger); - - $cache = $this->getMock('\BrowscapPHP\Cache\BrowscapCache', array('setItem'), array(), '', false); - $cache->expects(self::any()) - ->method('setItem') - ->will(self::returnValue(true)); - - $this->object->setCache($cache); - - $content = ';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Browscap Version - -[GJK_Browscap_Version] -Version=5031 -Released=Mon, 30 Jun 2014 17:55:58 +0200 -Format=ASP -Type= - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DefaultProperties - -[DefaultProperties] - -Comment=DefaultProperties -Browser=DefaultProperties -Version=0.0 -MajorVer=0 -MinorVer=0 -Platform=unknown -Platform_Version=unknown -Alpha=false -Beta=false -Win16=false -Win32=false -Win64=false -Frames=false -IFrames=false -Tables=false -Cookies=false -BackgroundSounds=false -JavaScript=false -VBScript=false -JavaApplets=false -ActiveXControls=false -isMobileDevice=false -isTablet=false -isSyndicationReader=false -Crawler=false -CssVersion=0 -AolVersion=0 - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Ask - -[Ask] - -Parent=DefaultProperties -Comment=Ask -Browser=Ask -Frames=1 -IFrames=1 -Tables=1 -Crawler=1 -Version=0.0 -MajorVer=0 -MinorVer=0 -Platform=unknown -Platform_Version=unknown -Alpha= -Beta= -Win16= -Win32= -Win64= -Cookies= -BackgroundSounds= -JavaScript= -VBScript= -JavaApplets= -ActiveXControls= -isMobileDevice= -isTablet= -isSyndicationReader= -CssVersion=0 -AolVersion=0 - -[Mozilla/?.0 (compatible; Ask Jeeves/Teoma*)] - -Parent=Ask -Browser=Teoma -Comment=Ask -Version=0.0 -MajorVer=0 -MinorVer=0 -Platform=unknown -Platform_Version=unknown -Alpha= -Beta= -Win16= -Win32= -Win64= -Frames=1 -IFrames=1 -Tables=1 -Cookies= -BackgroundSounds= -JavaScript= -VBScript= -JavaApplets= -ActiveXControls= -isMobileDevice= -isTablet= -isSyndicationReader= -Crawler=1 -CssVersion=0 -AolVersion=0 -'; - - self::assertNull($this->object->convertString($content)); - } - - /** - * - */ - public function testConvertStringWithoutPatternFaound() - { - $file = $this->getMock('\BrowscapPHP\Helper\Filesystem', array('exists'), array(), '', false); - $file->expects(self::never()) - ->method('exists') - ->will(self::returnValue(false)); - - $logger = $this->getMock('\Monolog\Logger', array('info', 'error'), array('test')); - $logger->expects(self::exactly(4)) - ->method('info') - ->will(self::returnValue(false)); - $logger->expects(self::never()) - ->method('error') - ->will(self::returnValue(false)); - - $this->object->setFilesystem($file); - $this->object->setLogger($logger); - - $cache = $this->getMock('\BrowscapPHP\Cache\BrowscapCache', array('setItem'), array(), '', false); - $cache->expects(self::any()) - ->method('setItem') - ->will(self::returnValue(true)); - - $this->object->setCache($cache); - - $content = ';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Browscap Version - -[GJK_Browscap_Version] -Version=5031 -Released=Mon, 30 Jun 2014 17:55:58 +0200 -Format=ASP -Type= - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DefaultProperties - -[DefaultProperties] - -Comment=DefaultProperties -Browser=DefaultProperties -Version=0.0 -MajorVer=0 -MinorVer=0 -Platform=unknown -Platform_Version=unknown -Alpha=false -Beta=false -Win16=false -Win32=false -Win64=false -Frames=false -IFrames=false -Tables=false -Cookies=false -BackgroundSounds=false -JavaScript=false -VBScript=false -JavaApplets=false -ActiveXControls=false -isMobileDevice=false -isTablet=false -isSyndicationReader=false -Crawler=false -CssVersion=0 -AolVersion=0 - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Ask - -[Ask] - -Parent=DefaultProperties -Comment=Ask -Browser=Ask -Frames=1 -IFrames=1 -Tables=1 -Crawler=1 -Version=0.0 -MajorVer=0 -MinorVer=0 -Platform=unknown -Platform_Version=unknown -Alpha= -Beta= -Win16= -Win32= -Win64= -Cookies= -BackgroundSounds= -JavaScript= -VBScript= -JavaApplets= -ActiveXControls= -isMobileDevice= -isTablet= -isSyndicationReader= -CssVersion=0 -AolVersion=0 -'; - - self::assertNull($this->object->convertString($content)); - } } diff --git a/tests/Parser/Helper/GetPatternTest.php b/tests/Parser/Helper/GetPatternTest.php index fbfde95f..803e0840 100644 --- a/tests/Parser/Helper/GetPatternTest.php +++ b/tests/Parser/Helper/GetPatternTest.php @@ -49,38 +49,6 @@ class GetPatternTest extends \PHPUnit_Framework_TestCase * */ public function setUp() - { - $this->object = new GetPattern(); - } - - /** - * - */ - public function testSetGetCache() - { - /** @var \BrowscapPHP\Cache\BrowscapCache $cache */ - $cache = $this->getMock('\BrowscapPHP\Cache\BrowscapCache', array(), array(), '', false); - - self::assertSame($this->object, $this->object->setCache($cache)); - self::assertSame($cache, $this->object->getCache()); - } - - /** - * - */ - public function testSetGetLogger() - { - /** @var \Monolog\Logger $logger */ - $logger = $this->getMock('\Monolog\Logger', array(), array(), '', false); - - self::assertSame($this->object, $this->object->setLogger($logger)); - self::assertSame($logger, $this->object->getLogger()); - } - - /** - * - */ - public function testGetPatterns() { $map = array( array( @@ -99,20 +67,24 @@ public function testGetPatterns() ) ); - /** @var \BrowscapPHP\Cache\BrowscapCache $cache */ - $cache = $this->getMock('\BrowscapPHP\Cache\BrowscapCache', array('getItem'), array(), '', false); + $cache = $this->getMock('\BrowscapPHP\Cache\BrowscapCache', array(), array(), '', false); $cache ->expects(self::never()) ->method('getItem') ->will(self::returnValueMap($map)) ; - $this->object->setCache($cache); - /** @var \Monolog\Logger $logger */ $logger = $this->getMock('\Monolog\Logger', array(), array(), '', false); - $this->object->setLogger($logger); + $this->object = new GetPattern($cache, $logger); + } + + /** + * + */ + public function testGetPatterns() + { $result = $this->object->getPatterns('Mozilla/5.0 (compatible; Ask Jeeves/Teoma*)'); self::assertInstanceOf('Generator', $result); diff --git a/tests/Parser/IniTest.php b/tests/Parser/IniTest.php index 71eeeadf..7567ae4b 100644 --- a/tests/Parser/IniTest.php +++ b/tests/Parser/IniTest.php @@ -49,32 +49,17 @@ class IniTest extends \PHPUnit_Framework_TestCase * */ public function setUp() - { - $this->object = new Ini(); - } - - /** - * - */ - public function testSetGetHelper() { /** @var \BrowscapPHP\Parser\Helper\GetPattern $helper */ - $helper = $this->getMock('\BrowscapPHP\Parser\Helper\GetPattern', array(), array(), '', false); + $patternHelper = $this->getMock('\BrowscapPHP\Parser\Helper\GetPattern', array(), array(), '', false); - self::assertSame($this->object, $this->object->setPatternHelper($helper)); - self::assertSame($helper, $this->object->getPatternHelper()); - } + /** @var \BrowscapPHP\Parser\Helper\GetPattern $helper */ + $dataHelper = $this->getMock('\BrowscapPHP\Parser\Helper\GetData', array(), array(), '', false); - /** - * - */ - public function testSetGetFormatter() - { /** @var \BrowscapPHP\Formatter\PhpGetBrowser $formatter */ $formatter = $this->getMock('\BrowscapPHP\Formatter\PhpGetBrowser', array(), array(), '', false); - self::assertSame($this->object, $this->object->setFormatter($formatter)); - self::assertSame($formatter, $this->object->getFormatter()); + $this->object = new Ini($patternHelper, $dataHelper, $formatter); } /** @@ -82,22 +67,6 @@ public function testSetGetFormatter() */ public function testSetGetCache() { - /** @var \BrowscapPHP\Cache\BrowscapCache $cache */ - $cache = $this->getMock('\BrowscapPHP\Cache\BrowscapCache', array(), array(), '', false); - - self::assertSame($this->object, $this->object->setCache($cache)); - self::assertSame($cache, $this->object->getCache()); - } - - /** - * - */ - public function testSetGetLogger() - { - /** @var \Monolog\Logger $logger */ - $logger = $this->getMock('\Monolog\Logger', array(), array(), '', false); - - self::assertSame($this->object, $this->object->setLogger($logger)); - self::assertSame($logger, $this->object->getLogger()); + self::markTestSkipped('need to be deleted'); } } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index cc601409..a71018da 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -24,3 +24,6 @@ } ini_set('memory_limit', '-1'); + +$logger = new \Monolog\Logger('test'); +\Monolog\ErrorHandler::register($logger); From a24bfffb93ec9761bd0ac5dc43f820526f8dcbba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Sun, 20 Sep 2015 19:36:09 +0200 Subject: [PATCH 21/24] change parsing ini files, change tests --- .gitignore | 15 +++++---- .travis.yml | 7 ++-- src/Browscap.php | 8 +++-- src/IniParser/IniParser.php | 40 ++++++++++++++++------- src/Parser/Helper/GetData.php | 4 +-- src/Parser/Helper/GetDataInterface.php | 2 +- src/Parser/Helper/GetPattern.php | 13 ++++++-- tests/BrowscapTest.php | 28 +--------------- tests/CompareBrowscapWithOriginalTest.php | 10 +++--- tests/Parser/IniTest.php | 19 ++++++++--- tests/data/.gitkeep | 0 11 files changed, 82 insertions(+), 64 deletions(-) create mode 100644 tests/data/.gitkeep diff --git a/.gitignore b/.gitignore index ad73239e..f044ceb7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,13 @@ -*~ -.settings +vendor/ +.idea/ .buildpath +.settings .project -.idea/ -vendor/ -phpunit.xml +composer.phar composer.lock +resources/ PHP_BrowscapINI -resources ergebnis.txt -tests/data/ \ No newline at end of file +tests/data/* +!tests/data/testdata.ini +!tests/data/.gitkeep diff --git a/.travis.yml b/.travis.yml index 69264717..acfe2430 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,10 +25,11 @@ before_script: - travis_retry composer install -o --prefer-source script: - - php -d browscap=$TRAVIS_BUILD_DIR/resources/browscap.ini vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover --colors --verbose + - php -d browscap=$TRAVIS_BUILD_DIR/resources/browscap.ini vendor/bin/phpunit --colors --verbose --exclude-group compare --coverage-text --coverage-clover=coverage.clover + - php -d browscap=$TRAVIS_BUILD_DIR/resources/browscap.ini vendor/bin/phpunit --colors --verbose --group compare - php vendor/bin/phpcs --standard=psr2 -pn src tests after_script: - - if [ "`phpenv version-name`" != "hhvm" ]; then wget https://scrutinizer-ci.com/ocular.phar; fi - - if [ "`phpenv version-name`" != "hhvm" ]; then php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi + - wget https://scrutinizer-ci.com/ocular.phar + - php ocular.phar code-coverage:upload --format=php-clover coverage.clover diff --git a/src/Browscap.php b/src/Browscap.php index 071e24a8..4014b9da 100644 --- a/src/Browscap.php +++ b/src/Browscap.php @@ -193,8 +193,12 @@ public function setParser(ParserInterface $parser) public function getParser() { if (null === $this->parser) { - $patternHelper = new Parser\Helper\GetPattern($this->getCache(), $this->getLogger()); - $dataHelper = new Parser\Helper\GetData($this->getCache(), $this->getLogger(), new Quoter()); + $cache = $this->getCache(); + $logger = $this->getLogger(); + $quoter = new Quoter(); + + $patternHelper = new Parser\Helper\GetPattern($cache, $logger); + $dataHelper = new Parser\Helper\GetData($cache, $logger, $quoter); $this->parser = new Parser\Ini($patternHelper, $dataHelper, $this->getFormatter()); } diff --git a/src/IniParser/IniParser.php b/src/IniParser/IniParser.php index 24c62abb..a89421e1 100644 --- a/src/IniParser/IniParser.php +++ b/src/IniParser/IniParser.php @@ -186,6 +186,10 @@ public function createPatterns($content) $data[$patternhash] = array(); } + if (!isset($data[$patternhash][$tmpLength])) { + $data[$patternhash][$tmpLength] = array(); + } + $pattern = $quoterHelper->pregQuote($pattern); // Check if the pattern contains digits - in this case we replace them with a digit regular expression, @@ -194,24 +198,35 @@ public function createPatterns($content) if (strpbrk($pattern, '0123456789') !== false) { $compressedPattern = preg_replace('/\d/', '[\d]', $pattern); - if (!in_array($compressedPattern, $data[$patternhash])) { - $data[$patternhash][] = $compressedPattern; + if (!in_array($compressedPattern, $data[$patternhash][$tmpLength])) { + $data[$patternhash][$tmpLength][] = $compressedPattern; } } else { - $data[$patternhash][] = $pattern; + $data[$patternhash][$tmpLength][] = $pattern; } } unset($matches); + // sorting of the data is important to check the patterns later in the correct order, because + // we need to check the most specific (=longest) patterns first, and the least specific + // (".*" for "Default Browser") last. + // + // sort by pattern start to group them + ksort($data); + // and then by pattern length (longest first) + foreach (array_keys($data) as $key) { + krsort($data[$key]); + } + // write optimized file (grouped by the first character of the has, generated from the pattern // start) with multiple patterns joined by tabs. this is to speed up loading of the data (small // array with pattern strings instead of an large array with single patterns) and also enables // us to search for multiple patterns in one preg_match call for a fast first search // (3-10 faster), followed by a detailed search for each single pattern. $contents = array(); - foreach ($data as $patternhash => $tmpPatterns) { - if (empty($tmpPatterns)) { + foreach ($data as $patternhash => $tmpEntries) { + if (empty($tmpEntries)) { continue; } @@ -221,13 +236,16 @@ public function createPatterns($content) $contents[$subkey] = array(); } - for ($i = 0, $j = ceil(count($tmpPatterns) / self::COUNT_PATTERN); $i < $j; $i++) { - $tmpJoinPatterns = implode( - "\t", - array_slice($tmpPatterns, ($i * self::COUNT_PATTERN), self::COUNT_PATTERN) - ); + foreach ($tmpEntries as $tmpLength => $tmpPatterns) { + if (empty($tmpPatterns)) { + continue; + } - $contents[$subkey][] = $patternhash . "\t" . $tmpJoinPatterns; + $chunks = array_chunk($tmpPatterns, self::COUNT_PATTERN); + + foreach ($chunks as $chunk) { + $contents[$subkey][] = $patternhash . "\t" . $tmpLength . "\t" . implode("\t", $chunk); + } } } diff --git a/src/Parser/Helper/GetData.php b/src/Parser/Helper/GetData.php index 4025fe8a..8e2b4deb 100644 --- a/src/Parser/Helper/GetData.php +++ b/src/Parser/Helper/GetData.php @@ -76,7 +76,7 @@ class GetData implements GetDataInterface * @param \Psr\Log\LoggerInterface $logger * @param \BrowscapPHP\Helper\Quoter $quoter */ - public function __contruct(BrowscapCache $cache, LoggerInterface $logger, Quoter $quoter) + public function __construct(BrowscapCache $cache, LoggerInterface $logger, Quoter $quoter) { $this->cache = $cache; $this->logger = $logger; @@ -92,7 +92,7 @@ public function __contruct(BrowscapCache $cache, LoggerInterface $logger, Quoter * @return array */ public function getSettings($pattern, array $settings = array()) - {print_r(new \Exception('test')); + { // The pattern has been pre-quoted on generation to speed up the pattern search, // but for this check we need the unquoted version $unquotedPattern = $this->quoter->pregUnQuote($pattern); diff --git a/src/Parser/Helper/GetDataInterface.php b/src/Parser/Helper/GetDataInterface.php index 3a29d824..2c2aac7a 100644 --- a/src/Parser/Helper/GetDataInterface.php +++ b/src/Parser/Helper/GetDataInterface.php @@ -55,7 +55,7 @@ interface GetDataInterface * @param \Psr\Log\LoggerInterface $logger * @param \BrowscapPHP\Helper\Quoter $quoter */ - public function __contruct(BrowscapCache $cache, LoggerInterface $logger, Quoter $quoter); + public function __construct(BrowscapCache $cache, LoggerInterface $logger, Quoter $quoter); /** * Gets the settings for a given pattern (method calls itself to diff --git a/src/Parser/Helper/GetPattern.php b/src/Parser/Helper/GetPattern.php index 032af3f8..cfe75bb0 100644 --- a/src/Parser/Helper/GetPattern.php +++ b/src/Parser/Helper/GetPattern.php @@ -87,6 +87,7 @@ public function __construct(BrowscapCache $cache, LoggerInterface $logger) public function getPatterns($userAgent) { $starts = Pattern::getHashForPattern($userAgent, true); + $length = strlen($userAgent); // add special key to fall back to the default browser $starts[] = str_repeat('z', 32); @@ -117,11 +118,19 @@ public function getPatterns($userAgent) continue; } + $found = false; + foreach ($file as $buffer) { - list($tmpBuffer, $patterns) = explode("\t", $buffer, 2); + list($tmpBuffer, $len, $patterns) = explode("\t", $buffer, 3); if ($tmpBuffer === $tmpStart) { - yield trim($patterns); + if ($len <= $length) { + yield trim($patterns); + } + + $found = true; + } elseif ($found === true) { + break; } } } diff --git a/tests/BrowscapTest.php b/tests/BrowscapTest.php index f023c94f..1fcd9743 100644 --- a/tests/BrowscapTest.php +++ b/tests/BrowscapTest.php @@ -123,33 +123,7 @@ public function testGetParser() */ public function testSetGetParser() { - $parser = $this->getMock( - '\BrowscapPHP\Parser\Ini', - array('setPatternHelper', 'setFormatter', 'setCache', 'setLogger'), - array(), - '', - false - ); - $parser - ->expects(self::once()) - ->method('setPatternHelper') - ->will(self::returnSelf()) - ; - $parser - ->expects(self::once()) - ->method('setFormatter') - ->will(self::returnSelf()) - ; - $parser - ->expects(self::once()) - ->method('setCache') - ->will(self::returnSelf()) - ; - $parser - ->expects(self::once()) - ->method('setLogger') - ->will(self::returnSelf()) - ; + $parser = $this->getMock('\BrowscapPHP\Parser\Ini', array(), array(), '', false); self::assertSame($this->object, $this->object->setParser($parser)); self::assertSame($parser, $this->object->getParser()); diff --git a/tests/CompareBrowscapWithOriginalTest.php b/tests/CompareBrowscapWithOriginalTest.php index 5d9890ed..7e8fe70d 100644 --- a/tests/CompareBrowscapWithOriginalTest.php +++ b/tests/CompareBrowscapWithOriginalTest.php @@ -8,6 +8,8 @@ /** * Compares get_browser results for all matches in browscap.ini with results from Browscap class. * Also compares the execution times. + * + * @group compare */ class CompareBrowscapWithOriginalTest extends \PHPUnit_Framework_TestCase { @@ -182,12 +184,11 @@ public function testCompare($userAgent) } } + /** + * @return array[] + */ public function providerUserAgent() { - //return array( - // array('Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.68 Safari/537.36'), - //); - return array( array('BlackBerry7100i/4.1.0 Profile/MIDP-2.0 Configuration/CLDC-1.1 VendorID/103'), array('check_http/v1.4.15 (nagios-plugins 1.4.15)'), @@ -230,6 +231,5 @@ public function providerUserAgent() array('Der gro\\xdfe BilderSauger 2.00u'), array('\\x22Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)\\x22'), ); - /**/ } } diff --git a/tests/Parser/IniTest.php b/tests/Parser/IniTest.php index 7567ae4b..88aa2159 100644 --- a/tests/Parser/IniTest.php +++ b/tests/Parser/IniTest.php @@ -2,6 +2,9 @@ namespace BrowscapPHPTest\Parser; +use BrowscapPHP\Helper\Quoter; +use BrowscapPHP\Parser\Helper\GetData; +use BrowscapPHP\Parser\Helper\GetPattern; use BrowscapPHP\Parser\Ini; /** @@ -50,11 +53,19 @@ class IniTest extends \PHPUnit_Framework_TestCase */ public function setUp() { - /** @var \BrowscapPHP\Parser\Helper\GetPattern $helper */ - $patternHelper = $this->getMock('\BrowscapPHP\Parser\Helper\GetPattern', array(), array(), '', false); + $cache = $this->getMock('\BrowscapPHP\Cache\BrowscapCache', array(), array(), '', false); - /** @var \BrowscapPHP\Parser\Helper\GetPattern $helper */ - $dataHelper = $this->getMock('\BrowscapPHP\Parser\Helper\GetData', array(), array(), '', false); + /** @var \Monolog\Logger $logger */ + $logger = $this->getMock('\Monolog\Logger', array(), array(), '', false); + + /** @var \BrowscapPHP\Helper\Quoter $quoter */ + $quoter = $this->getMock('\BrowscapPHP\Helper\Quoter', array(), array(), '', false); + + /** @var \BrowscapPHP\Parser\Helper\GetPattern $patternHelper */ + $patternHelper = new GetPattern($cache, $logger); + + /** @var \BrowscapPHP\Parser\Helper\GetData $dataHelper */ + $dataHelper = new GetData($cache, $logger, $quoter); /** @var \BrowscapPHP\Formatter\PhpGetBrowser $formatter */ $formatter = $this->getMock('\BrowscapPHP\Formatter\PhpGetBrowser', array(), array(), '', false); diff --git a/tests/data/.gitkeep b/tests/data/.gitkeep new file mode 100644 index 00000000..e69de29b From ba1897b006196773ffcf07c253c7e637d6a0903b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Sun, 20 Sep 2015 21:13:24 +0200 Subject: [PATCH 22/24] fix coding style --- src/Parser/Ini.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Parser/Ini.php b/src/Parser/Ini.php index 52c39d6d..d8adf727 100644 --- a/src/Parser/Ini.php +++ b/src/Parser/Ini.php @@ -76,8 +76,7 @@ public function __construct( GetPatternInterface $patternHelper, GetDataInterface $dataHelper, FormatterInterface $formatter - ) - { + ) { $this->patternHelper = $patternHelper; $this->dataHelper = $dataHelper; $this->formatter = $formatter; From 19682256723cded4acdead21bacd58f89848014f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Fri, 25 Sep 2015 20:32:41 +0200 Subject: [PATCH 23/24] Skip code coverage generation on HHVM --- .travis.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index acfe2430..e996aa82 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,11 +25,14 @@ before_script: - travis_retry composer install -o --prefer-source script: - - php -d browscap=$TRAVIS_BUILD_DIR/resources/browscap.ini vendor/bin/phpunit --colors --verbose --exclude-group compare --coverage-text --coverage-clover=coverage.clover +# XDebug is not installed on HHVM, the build should run without coverage generation + - if [ "`phpenv version-name`" != "hhvm" ]; then php -d browscap=$TRAVIS_BUILD_DIR/resources/browscap.ini vendor/bin/phpunit --colors --verbose --exclude-group compare --coverage-text --coverage-clover=coverage.clover; fi + - if [ "`phpenv version-name`" == "hhvm" ]; then php -d browscap=$TRAVIS_BUILD_DIR/resources/browscap.ini vendor/bin/phpunit --colors --verbose --exclude-group compare; fi - php -d browscap=$TRAVIS_BUILD_DIR/resources/browscap.ini vendor/bin/phpunit --colors --verbose --group compare - php vendor/bin/phpcs --standard=psr2 -pn src tests after_script: - - wget https://scrutinizer-ci.com/ocular.phar - - php ocular.phar code-coverage:upload --format=php-clover coverage.clover +# XDebug is not installed on HHVM, so we dont have coverage data to send to scrutinizer + - if [ "`phpenv version-name`" != "hhvm" ]; then wget https://scrutinizer-ci.com/ocular.phar; fi + - if [ "`phpenv version-name`" != "hhvm" ]; then php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi From 87607320ee534731b726059e82628c80263bec8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Sat, 26 Sep 2015 22:02:39 +0200 Subject: [PATCH 24/24] change comment --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index e996aa82..a8389604 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,14 +25,14 @@ before_script: - travis_retry composer install -o --prefer-source script: -# XDebug is not installed on HHVM, the build should run without coverage generation +# xdebug is not installed on hhvm, so we are skipping the creation of code coverage data there - if [ "`phpenv version-name`" != "hhvm" ]; then php -d browscap=$TRAVIS_BUILD_DIR/resources/browscap.ini vendor/bin/phpunit --colors --verbose --exclude-group compare --coverage-text --coverage-clover=coverage.clover; fi - if [ "`phpenv version-name`" == "hhvm" ]; then php -d browscap=$TRAVIS_BUILD_DIR/resources/browscap.ini vendor/bin/phpunit --colors --verbose --exclude-group compare; fi - php -d browscap=$TRAVIS_BUILD_DIR/resources/browscap.ini vendor/bin/phpunit --colors --verbose --group compare - php vendor/bin/phpcs --standard=psr2 -pn src tests after_script: -# XDebug is not installed on HHVM, so we dont have coverage data to send to scrutinizer +# xdebug is not installed on hhvm, so we have no code coverage data to send - if [ "`phpenv version-name`" != "hhvm" ]; then wget https://scrutinizer-ci.com/ocular.phar; fi - if [ "`phpenv version-name`" != "hhvm" ]; then php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi