Update core 8.3.0

This commit is contained in:
Rob Davies 2017-04-13 15:53:35 +01:00
parent da7a7918f8
commit cd7a898e66
6144 changed files with 132297 additions and 87747 deletions

View file

@ -1,4 +0,0 @@
phpunit.xml
composer.lock
composer.phar
/vendor/

View file

@ -1,13 +0,0 @@
language: php
php:
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm
before_script: composer install --dev
script: phpunit

19
web/vendor/asm89/stack-cors/LICENSE vendored Normal file
View file

@ -0,0 +1,19 @@
Copyright (c) 2013-2017 Alexander <iam.asm89@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -2,12 +2,11 @@
Library and middleware enabling cross-origin resource sharing for your
http-{foundation,kernel} using application. It attempts to implement the
[W3C Candidate Recommendation] for cross-origin resource sharing.
[W3C Recommendation] for cross-origin resource sharing.
[W3C Candidate Recommendation]: http://www.w3.org/TR/cors/
[W3C Recommendation]: http://www.w3.org/TR/cors/
Master [![Build Status](https://secure.travis-ci.org/asm89/stack-cors.png?branch=master)](http://travis-ci.org/asm89/stack-cors)
Develop [![Build Status](https://secure.travis-ci.org/asm89/stack-cors.png?branch=develop)](http://travis-ci.org/asm89/stack-cors)
## Installation
@ -15,27 +14,11 @@ Require `asm89/stack-cors` using composer.
## Usage
Stack middleware:
This package can be used as a library or as [stack middleware].
```php
<?php
[stack middleware]: http://stackphp.com/
use Asm89\Stack\Cors;
$app = new Cors($app, array(
// you can use array('*') to allow any headers
'allowedHeaders' => array('x-allowed-header', 'x-other-allowed-header'),
// you can use array('*') to allow any methods
'allowedMethods' => array('DELETE', 'GET', 'POST', 'PUT'),
// you can use array('*') to allow requests from any origin
'allowedOrigins' => array('localhost'),
'exposedHeaders' => false,
'maxAge' => false,
'supportsCredentials' => false,
));
```
Or use the library:
### Example: using the library
```php
<?php
@ -57,3 +40,23 @@ $cors->isActualRequestAllowed(Request $request);
$cors->isCorsRequest(Request $request);
$cors->isPreflightRequest(Request $request);
```
## Example: using the stack middleware
```php
<?php
use Asm89\Stack\Cors;
$app = new Cors($app, array(
// you can use array('*') to allow any headers
'allowedHeaders' => array('x-allowed-header', 'x-other-allowed-header'),
// you can use array('*') to allow any methods
'allowedMethods' => array('DELETE', 'GET', 'POST', 'PUT'),
// you can use array('*') to allow requests from any origin
'allowedOrigins' => array('localhost'),
'exposedHeaders' => false,
'maxAge' => false,
'supportsCredentials' => false,
));
```

View file

@ -12,11 +12,32 @@
}
],
"require": {
"php": ">=5.3.2",
"symfony/http-foundation": "~2.1|~3.0",
"symfony/http-kernel": "~2.1|~3.0"
"php": ">=5.5.9",
"symfony/http-foundation": "~2.7|~3.0",
"symfony/http-kernel": "~2.7|~3.0"
},
"require-dev": {
"phpunit/phpunit": "^5.0 || ^4.8.10",
"squizlabs/php_codesniffer": "^2.3"
},
"autoload": {
"psr-0": { "Asm89\\Stack": "src/" }
"psr-4": {
"Asm89\\Stack\\": "src/Asm89/Stack/"
}
},
"autoload-dev": {
"psr-4": {
"Asm89\\Stack\\": "test/Asm89/Stack/"
}
},
"scripts": {
"test": "phpunit",
"check-style": "phpcs -p --standard=PSR2 --runtime-set ignore_errors_on_exit 1 --runtime-set ignore_warnings_on_exit 1 src",
"fix-style": "phpcbf -p --standard=PSR2 --runtime-set ignore_errors_on_exit 1 --runtime-set ignore_warnings_on_exit 1 src"
},
"extra": {
"branch-alias": {
"dev-master": "1.1-dev"
}
}
}

View file

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="test/bootstrap.php"
>
<testsuites>
<testsuite name="Stack Cors Test Suite">
<directory>./test/Asm89/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">./src/</directory>
</whitelist>
</filter>
</phpunit>

14
web/vendor/asm89/stack-cors/src/Asm89/Stack/Cors.php vendored Executable file → Normal file
View file

@ -1,5 +1,14 @@
<?php
/*
* This file is part of asm89/stack-cors.
*
* (c) Alexander <iam.asm89@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Asm89\Stack;
use Symfony\Component\HttpKernel\HttpKernelInterface;
@ -31,12 +40,11 @@ class Cors implements HttpKernelInterface
{
$this->app = $app;
$this->cors = new CorsService(array_merge($this->defaultOptions, $options));
}
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
{
if ( ! $this->cors->isCorsRequest($request)) {
if (!$this->cors->isCorsRequest($request)) {
return $this->app->handle($request, $type, $catch);
}
@ -44,7 +52,7 @@ class Cors implements HttpKernelInterface
return $this->cors->handlePreflightRequest($request);
}
if ( ! $this->cors->isActualRequestAllowed($request)) {
if (!$this->cors->isActualRequestAllowed($request)) {
return new Response('Not allowed.', 403);
}

48
web/vendor/asm89/stack-cors/src/Asm89/Stack/CorsService.php vendored Executable file → Normal file
View file

@ -1,5 +1,14 @@
<?php
/*
* This file is part of asm89/stack-cors.
*
* (c) Alexander <iam.asm89@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Asm89\Stack;
use Symfony\Component\HttpKernel\HttpKernelInterface;
@ -17,7 +26,6 @@ class CorsService
private function normalizeOptions(array $options = array())
{
$options += array(
'allowedOrigins' => array(),
'supportsCredentials' => false,
@ -29,18 +37,18 @@ class CorsService
// normalize array('*') to true
if (in_array('*', $options['allowedOrigins'])) {
$options['allowedOrigins'] = true;
$options['allowedOrigins'] = true;
}
if (in_array('*', $options['allowedHeaders'])) {
$options['allowedHeaders'] = true;
$options['allowedHeaders'] = true;
} else {
$options['allowedHeaders'] = array_map('strtolower', $options['allowedHeaders']);
$options['allowedHeaders'] = array_map('strtolower', $options['allowedHeaders']);
}
if (in_array('*', $options['allowedMethods'])) {
$options['allowedMethods'] = true;
$options['allowedMethods'] = true;
} else {
$options['allowedMethods'] = array_map('strtoupper', $options['allowedMethods']);
$options['allowedMethods'] = array_map('strtoupper', $options['allowedMethods']);
}
return $options;
@ -53,25 +61,25 @@ class CorsService
public function isCorsRequest(Request $request)
{
return $request->headers->has('Origin');
return $request->headers->has('Origin') && !$this->isSameHost($request);
}
public function isPreflightRequest(Request $request)
{
return $this->isCorsRequest($request)
&&$request->getMethod() === 'OPTIONS'
&& $request->getMethod() === 'OPTIONS'
&& $request->headers->has('Access-Control-Request-Method');
}
public function addActualRequestHeaders(Response $response, Request $request)
{
if ( ! $this->checkOrigin($request)) {
if (!$this->checkOrigin($request)) {
return $response;
}
$response->headers->set('Access-Control-Allow-Origin', $request->headers->get('Origin'));
if ( ! $response->headers->has('Vary')) {
if (!$response->headers->has('Vary')) {
$response->headers->set('Vary', 'Origin');
} else {
$response->headers->set('Vary', $response->headers->get('Vary') . ', Origin');
@ -126,11 +134,11 @@ class CorsService
private function checkPreflightRequestConditions(Request $request)
{
if ( ! $this->checkOrigin($request)) {
if (!$this->checkOrigin($request)) {
return $this->createBadRequestResponse(403, 'Origin not allowed');
}
if ( ! $this->checkMethod($request)) {
if (!$this->checkMethod($request)) {
return $this->createBadRequestResponse(405, 'Method not allowed');
}
@ -138,10 +146,10 @@ class CorsService
// if allowedHeaders has been set to true ('*' allow all flag) just skip this check
if ($this->options['allowedHeaders'] !== true && $request->headers->has('Access-Control-Request-Headers')) {
$headers = strtolower($request->headers->get('Access-Control-Request-Headers'));
$requestHeaders = explode(',', $headers);
$requestHeaders = array_filter(explode(',', $headers));
foreach ($requestHeaders as $header) {
if ( ! in_array(trim($header), $this->options['allowedHeaders'])) {
if (!in_array(trim($header), $this->options['allowedHeaders'])) {
return $this->createBadRequestResponse(403, 'Header not allowed');
}
}
@ -155,7 +163,13 @@ class CorsService
return new Response($reason, $code);
}
private function checkOrigin(Request $request) {
private function isSameHost(Request $request)
{
return $request->headers->get('Origin') === $request->getSchemeAndHttpHost();
}
private function checkOrigin(Request $request)
{
if ($this->options['allowedOrigins'] === true) {
// allow all '*' flag
return true;
@ -165,7 +179,8 @@ class CorsService
return in_array($origin, $this->options['allowedOrigins']);
}
private function checkMethod(Request $request) {
private function checkMethod(Request $request)
{
if ($this->options['allowedMethods'] === true) {
// allow all '*' flag
return true;
@ -174,5 +189,4 @@ class CorsService
$requestMethod = strtoupper($request->headers->get('Access-Control-Request-Method'));
return in_array($requestMethod, $this->options['allowedMethods']);
}
}

View file

@ -1,395 +0,0 @@
<?php
namespace Asm89\Stack;
use PHPUnit_Framework_TestCase;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class CorsTest extends PHPUnit_Framework_TestCase
{
/**
* @test
*/
public function it_does_not_modify_on_a_request_without_origin()
{
$app = $this->createStackedApp();
$unmodifiedResponse = new Response();
$response = $app->handle(new Request());
$this->assertEquals($unmodifiedResponse->headers, $response->headers);
}
/**
* @test
*/
public function it_returns_403_on_valid_actual_request_with_origin_not_allowed()
{
$app = $this->createStackedApp(array('allowedOrigins' => array('notlocalhost')));
$request = $this->createValidActualRequest();
$response = $app->handle($request);
$this->assertEquals(403, $response->getStatusCode());
}
/**
* @test
*/
public function it_returns_allow_origin_header_on_valid_actual_request()
{
$app = $this->createStackedApp();
$request = $this->createValidActualRequest();
$response = $app->handle($request);
$this->assertTrue($response->headers->has('Access-Control-Allow-Origin'));
$this->assertEquals('localhost', $response->headers->get('Access-Control-Allow-Origin'));
}
/**
* @test
*/
public function it_returns_allow_origin_header_on_allow_all_origin_request()
{
$app = $this->createStackedApp(array('allowedOrigins' => array('*')));
$request = new Request();
$request->headers->set('Origin', 'http://localhost');
$response = $app->handle($request);
$this->assertEquals(200, $response->getStatusCode());
$this->assertTrue($response->headers->has('Access-Control-Allow-Origin'));
$this->assertEquals('http://localhost', $response->headers->get('Access-Control-Allow-Origin'));
}
/**
* @test
*/
public function it_returns_allow_headers_header_on_allow_all_headers_request()
{
$app = $this->createStackedApp(array('allowedHeaders' => array('*')));
$request = $this->createValidPreflightRequest();
$request->headers->set('Access-Control-Request-Headers', 'Foo, BAR');
$response = $app->handle($request);
$this->assertEquals(200, $response->getStatusCode());
$this->assertEquals('FOO, BAR', $response->headers->get('Access-Control-Allow-Headers'));
}
/**
* @test
*/
public function it_does_not_return_allow_origin_header_on_valid_actual_request_with_origin_not_allowed()
{
$app = $this->createStackedApp(array('allowedOrigins' => array('notlocalhost')));
$request = $this->createValidActualRequest();
$response = $app->handle($request);
$this->assertFalse($response->headers->has('Access-Control-Allow-Origin'));
}
/**
* @test
*/
public function it_sets_allow_credentials_header_when_flag_is_set_on_valid_actual_request()
{
$app = $this->createStackedApp(array('supportsCredentials' => true));
$request = $this->createValidActualRequest();
$response = $app->handle($request);
$this->assertTrue($response->headers->has('Access-Control-Allow-Credentials'));
$this->assertEquals('true', $response->headers->get('Access-Control-Allow-Credentials'));
}
/**
* @test
*/
public function it_does_not_set_allow_credentials_header_when_flag_is_not_set_on_valid_actual_request()
{
$app = $this->createStackedApp();
$request = $this->createValidActualRequest();
$response = $app->handle($request);
$this->assertFalse($response->headers->has('Access-Control-Allow-Credentials'));
}
/**
* @test
*/
public function it_sets_exposed_headers_when_configured_on_actual_request()
{
$app = $this->createStackedApp(array('exposedHeaders' => array('x-exposed-header', 'x-another-exposed-header')));
$request = $this->createValidActualRequest();
$response = $app->handle($request);
$this->assertTrue($response->headers->has('Access-Control-Expose-Headers'));
$this->assertEquals('x-exposed-header, x-another-exposed-header', $response->headers->get('Access-Control-Expose-Headers'));
}
/**
* @test
* @see http://www.w3.org/TR/cors/index.html#resource-implementation
*/
public function it_adds_a_vary_header()
{
$app = $this->createStackedApp();
$request = $this->createValidActualRequest();
$response = $app->handle($request);
$this->assertTrue($response->headers->has('Vary'));
$this->assertEquals('Origin', $response->headers->get('Vary'));
}
/**
* @test
* @see http://www.w3.org/TR/cors/index.html#resource-implementation
*/
public function it_appends_an_existing_vary_header()
{
$app = $this->createStackedApp(array(), array('Vary' => 'Content-Type'));
$request = $this->createValidActualRequest();
$response = $app->handle($request);
$this->assertTrue($response->headers->has('Vary'));
$this->assertEquals('Content-Type, Origin', $response->headers->get('Vary'));
}
/**
* @test
*/
public function it_returns_access_control_headers_on_cors_request()
{
$app = $this->createStackedApp();
$request = new Request();
$request->headers->set('Origin', 'localhost');
$response = $app->handle($request);
$this->assertTrue($response->headers->has('Access-Control-Allow-Origin'));
$this->assertEquals('localhost', $response->headers->get('Access-Control-Allow-Origin'));
}
/**
* @test
*/
public function it_returns_access_control_headers_on_valid_preflight_request()
{
$app = $this->createStackedApp();
$request = $this->createValidPreflightRequest();
$response = $app->handle($request);
$this->assertTrue($response->headers->has('Access-Control-Allow-Origin'));
$this->assertEquals('localhost', $response->headers->get('Access-Control-Allow-Origin'));
}
/**
* @test
*/
public function it_returns_403_on_valid_preflight_request_with_origin_not_allowed()
{
$app = $this->createStackedApp(array('allowedOrigins' => array('notlocalhost')));
$request = $this->createValidPreflightRequest();
$response = $app->handle($request);
$this->assertEquals(403, $response->getStatusCode());
}
/**
* @test
*/
public function it_does_not_modify_request_with_origin_not_allowed()
{
$passedOptions = array(
'allowedOrigins' => array('notlocalhost'),
);
$service = new CorsService($passedOptions);
$request = $this->createValidActualRequest();
$response = new Response();
$service->addActualRequestHeaders($response, $request);
$this->assertEquals($response, new Response());
}
/**
* @test
*/
public function it_returns_405_on_valid_preflight_request_with_method_not_allowed()
{
$app = $this->createStackedApp(array('allowedMethods' => array('put')));
$request = $this->createValidPreflightRequest();
$response = $app->handle($request);
$this->assertEquals(405, $response->getStatusCode());
}
/**
* @test
*/
public function it_allow_methods_on_valid_preflight_request()
{
$app = $this->createStackedApp(array('allowedMethods' => array('get', 'put')));
$request = $this->createValidPreflightRequest();
$response = $app->handle($request);
$this->assertTrue($response->headers->has('Access-Control-Allow-Methods'));
// it will uppercase the methods
$this->assertEquals('GET, PUT', $response->headers->get('Access-Control-Allow-Methods'));
}
/**
* @test
*/
public function it_returns_valid_preflight_request_with_allow_methods_all()
{
$app = $this->createStackedApp(array('allowedMethods' => array('*')));
$request = $this->createValidPreflightRequest();
$response = $app->handle($request);
$this->assertTrue($response->headers->has('Access-Control-Allow-Methods'));
// it will return the Access-Control-Request-Method pass in the request
$this->assertEquals('GET', $response->headers->get('Access-Control-Allow-Methods'));
}
/**
* @test
*/
public function it_returns_403_on_valid_preflight_request_with_one_of_the_requested_headers_not_allowed()
{
$app = $this->createStackedApp();
$request = $this->createValidPreflightRequest();
$request->headers->set('Access-Control-Request-Headers', 'x-not-allowed-header');
$response = $app->handle($request);
$this->assertEquals(403, $response->getStatusCode());
}
/**
* @test
*/
public function it_returns_ok_on_valid_preflight_request_with_requested_headers_allowed()
{
$app = $this->createStackedApp();
$requestHeaders = 'X-Allowed-Header, x-other-allowed-header';
$request = $this->createValidPreflightRequest();
$request->headers->set('Access-Control-Request-Headers', $requestHeaders);
$response = $app->handle($request);
$this->assertEquals(200, $response->getStatusCode());
$this->assertTrue($response->headers->has('Access-Control-Allow-Headers'));
// the response will have the "allowedHeaders" value passed to Cors rather than the request one
$this->assertEquals('x-allowed-header, x-other-allowed-header', $response->headers->get('Access-Control-Allow-Headers'));
}
/**
* @test
*/
public function it_sets_allow_credentials_header_when_flag_is_set_on_valid_preflight_request()
{
$app = $this->createStackedApp(array('supportsCredentials' => true));
$request = $this->createValidPreflightRequest();
$response = $app->handle($request);
$this->assertTrue($response->headers->has('Access-Control-Allow-Credentials'));
$this->assertEquals('true', $response->headers->get('Access-Control-Allow-Credentials'));
}
/**
* @test
*/
public function it_does_not_set_allow_credentials_header_when_flag_is_not_set_on_valid_preflight_request()
{
$app = $this->createStackedApp();
$request = $this->createValidPreflightRequest();
$response = $app->handle($request);
$this->assertFalse($response->headers->has('Access-Control-Allow-Credentials'));
}
/**
* @test
*/
public function it_sets_max_age_when_set()
{
$app = $this->createStackedApp(array('maxAge' => 42));
$request = $this->createValidPreflightRequest();
$response = $app->handle($request);
$this->assertTrue($response->headers->has('Access-Control-Max-Age'));
$this->assertEquals(42, $response->headers->get('Access-Control-Max-Age'));
}
private function createValidActualRequest()
{
$request = new Request();
$request->headers->set('Origin', 'localhost');
return $request;
}
private function createValidPreflightRequest()
{
$request = new Request();
$request->headers->set('Origin', 'localhost');
$request->headers->set('Access-Control-Request-Method', 'get');
$request->setMethod('OPTIONS');
return $request;
}
private function createStackedApp(array $options = array(), array $responseHeaders = array())
{
$passedOptions = array_merge(array(
'allowedHeaders' => array('x-allowed-header', 'x-other-allowed-header'),
'allowedMethods' => array('delete', 'get', 'post', 'put'),
'allowedOrigins' => array('localhost'),
'exposedHeaders' => false,
'maxAge' => false,
'supportsCredentials' => false,
),
$options
);
return new Cors(new MockApp($responseHeaders), $passedOptions);
}
}
class MockApp implements HttpKernelInterface
{
private $responseHeaders;
public function __construct(array $responseHeaders)
{
$this->responseHeaders = $responseHeaders;
}
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
{
$response = new Response();
$response->headers->add($this->responseHeaders);
return $response;
}
}

View file

@ -1,10 +0,0 @@
<?php
if (file_exists($file = __DIR__.'/../vendor/autoload.php')) {
$loader = require_once $file;
$loader->add('Asm89\Stack', __DIR__);
$loader->add('Asm89\Stack', __DIR__ . '/../src');
} else {
throw new RuntimeException('Install dependencies to run test suite.');
}

View file

@ -449,13 +449,5 @@ return array(
'SebastianBergmann\\RecursionContext\\InvalidArgumentException' => $vendorDir . '/sebastian/recursion-context/src/InvalidArgumentException.php',
'SebastianBergmann\\Version' => $vendorDir . '/sebastian/version/src/Version.php',
'SessionHandlerInterface' => $vendorDir . '/symfony/polyfill-php54/Resources/stubs/SessionHandlerInterface.php',
'Symfony\\Component\\HttpFoundation\\FileBag' => $vendorDir . '/symfony/http-foundation/FileBag.php',
'Symfony\\Component\\HttpFoundation\\HeaderBag' => $vendorDir . '/symfony/http-foundation/HeaderBag.php',
'Symfony\\Component\\HttpFoundation\\ParameterBag' => $vendorDir . '/symfony/http-foundation/ParameterBag.php',
'Symfony\\Component\\HttpFoundation\\Request' => $vendorDir . '/symfony/http-foundation/Request.php',
'Symfony\\Component\\HttpFoundation\\ServerBag' => $vendorDir . '/symfony/http-foundation/ServerBag.php',
'Symfony\\Component\\HttpKernel\\HttpKernel' => $vendorDir . '/symfony/http-kernel/HttpKernel.php',
'Symfony\\Component\\HttpKernel\\HttpKernelInterface' => $vendorDir . '/symfony/http-kernel/HttpKernelInterface.php',
'Symfony\\Component\\HttpKernel\\TerminableInterface' => $vendorDir . '/symfony/http-kernel/TerminableInterface.php',
'Text_Template' => $vendorDir . '/phpunit/php-text-template/src/Template.php',
);

View file

@ -11,9 +11,9 @@ return array(
'edc6464955a37aa4d5fbf39d40fb6ee7' => $vendorDir . '/symfony/polyfill-php55/bootstrap.php',
'3e2471375464aac821502deb0ac64275' => $vendorDir . '/symfony/polyfill-php54/bootstrap.php',
'32dcc8afd4335739640db7d200c1971d' => $vendorDir . '/symfony/polyfill-apcu/bootstrap.php',
'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
'5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php',
'def43f6c87e4f8dfd0c9e1b1bab14fe8' => $vendorDir . '/symfony/polyfill-iconv/bootstrap.php',
'5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php',
);

View file

@ -10,16 +10,12 @@ return array(
'org\\bovigo\\vfs\\' => array($vendorDir . '/mikey179/vfsStream/src/main/php'),
'Twig_' => array($vendorDir . '/twig/twig/lib'),
'Stack' => array($vendorDir . '/stack/builder/src'),
'Psr\\Log\\' => array($vendorDir . '/psr/log'),
'Prophecy\\' => array($vendorDir . '/phpspec/prophecy/src'),
'Egulias\\' => array($vendorDir . '/egulias/email-validator/src'),
'EasyRdf_' => array($vendorDir . '/easyrdf/easyrdf/lib'),
'Doctrine\\Common\\Lexer\\' => array($vendorDir . '/doctrine/lexer/lib'),
'Doctrine\\Common\\Inflector\\' => array($vendorDir . '/doctrine/inflector/lib'),
'Doctrine\\Common\\Collections\\' => array($vendorDir . '/doctrine/collections/lib'),
'Doctrine\\Common\\Cache\\' => array($vendorDir . '/doctrine/cache/lib'),
'Doctrine\\Common\\Annotations\\' => array($vendorDir . '/doctrine/annotations/lib'),
'Doctrine\\Common\\' => array($vendorDir . '/doctrine/common/lib'),
'Composer\\Installers\\' => array($vendorDir . '/composer/installers/src'),
'Asm89\\Stack' => array($vendorDir . '/asm89/stack-cors/src'),
);

View file

@ -9,7 +9,6 @@ return array(
'Zumba\\Mink\\Driver\\' => array($vendorDir . '/jcalderonzumba/mink-phantomjs-driver/src'),
'Zumba\\GastonJS\\' => array($vendorDir . '/jcalderonzumba/gastonjs/src'),
'Zend\\Stdlib\\' => array($vendorDir . '/zendframework/zend-stdlib/src'),
'Zend\\Hydrator\\' => array($vendorDir . '/zendframework/zend-hydrator/src'),
'Zend\\Feed\\' => array($vendorDir . '/zendframework/zend-feed/src'),
'Zend\\Escaper\\' => array($vendorDir . '/zendframework/zend-escaper/src'),
'Zend\\Diactoros\\' => array($vendorDir . '/zendframework/zend-diactoros/src'),
@ -36,19 +35,23 @@ return array(
'Symfony\\Component\\BrowserKit\\' => array($vendorDir . '/symfony/browser-kit'),
'Symfony\\Cmf\\Component\\Routing\\' => array($vendorDir . '/symfony-cmf/routing'),
'Symfony\\Bridge\\PsrHttpMessage\\' => array($vendorDir . '/symfony/psr-http-message-bridge'),
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
'Masterminds\\' => array($vendorDir . '/masterminds/html5/src'),
'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'),
'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),
'Goutte\\' => array($vendorDir . '/fabpot/goutte/Goutte'),
'Drupal\\Driver\\' => array($baseDir . '/drivers/lib/Drupal/Driver', $baseDir . '/drivers/lib/Drupal/Driver'),
'Drupal\\Driver\\' => array($baseDir . '/drivers/lib/Drupal/Driver'),
'Drupal\\Core\\Composer\\' => array($baseDir . '/core/lib/Drupal/Core/Composer'),
'Drupal\\Core\\' => array($baseDir . '/core/lib/Drupal/Core', $baseDir . '/core/lib/Drupal/Core'),
'Drupal\\Component\\' => array($baseDir . '/core/lib/Drupal/Component', $baseDir . '/core/lib/Drupal/Component'),
'Drupal\\Core\\' => array($baseDir . '/core/lib/Drupal/Core'),
'Drupal\\Component\\' => array($baseDir . '/core/lib/Drupal/Component'),
'DrupalComposer\\DrupalScaffold\\' => array($vendorDir . '/drupal-composer/drupal-scaffold/src'),
'Doctrine\\Instantiator\\' => array($vendorDir . '/doctrine/instantiator/src/Doctrine/Instantiator'),
'Doctrine\\Common\\Cache\\' => array($vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache'),
'Doctrine\\Common\\' => array($vendorDir . '/doctrine/common/lib/Doctrine/Common'),
'Composer\\Semver\\' => array($vendorDir . '/composer/semver/src'),
'Behat\\Mink\\Driver\\' => array($vendorDir . '/behat/mink-browserkit-driver/src', $vendorDir . '/behat/mink-goutte-driver/src'),
'Behat\\Mink\\' => array($vendorDir . '/behat/mink/src'),
'Asm89\\Stack\\' => array($vendorDir . '/asm89/stack-cors/src/Asm89/Stack'),
);

View file

@ -12,11 +12,11 @@ class ComposerStaticInitDrupal8
'edc6464955a37aa4d5fbf39d40fb6ee7' => __DIR__ . '/..' . '/symfony/polyfill-php55/bootstrap.php',
'3e2471375464aac821502deb0ac64275' => __DIR__ . '/..' . '/symfony/polyfill-php54/bootstrap.php',
'32dcc8afd4335739640db7d200c1971d' => __DIR__ . '/..' . '/symfony/polyfill-apcu/bootstrap.php',
'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
'5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php',
'def43f6c87e4f8dfd0c9e1b1bab14fe8' => __DIR__ . '/..' . '/symfony/polyfill-iconv/bootstrap.php',
'5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php',
);
public static $prefixLengthsPsr4 = array (
@ -25,7 +25,6 @@ class ComposerStaticInitDrupal8
'Zumba\\Mink\\Driver\\' => 18,
'Zumba\\GastonJS\\' => 15,
'Zend\\Stdlib\\' => 12,
'Zend\\Hydrator\\' => 14,
'Zend\\Feed\\' => 10,
'Zend\\Escaper\\' => 13,
'Zend\\Diactoros\\' => 15,
@ -61,6 +60,7 @@ class ComposerStaticInitDrupal8
),
'P' =>
array (
'Psr\\Log\\' => 8,
'Psr\\Http\\Message\\' => 17,
),
'M' =>
@ -82,6 +82,8 @@ class ComposerStaticInitDrupal8
'Drupal\\Component\\' => 17,
'DrupalComposer\\DrupalScaffold\\' => 30,
'Doctrine\\Instantiator\\' => 22,
'Doctrine\\Common\\Cache\\' => 22,
'Doctrine\\Common\\' => 16,
),
'C' =>
array (
@ -92,6 +94,10 @@ class ComposerStaticInitDrupal8
'Behat\\Mink\\Driver\\' => 18,
'Behat\\Mink\\' => 11,
),
'A' =>
array (
'Asm89\\Stack\\' => 12,
),
);
public static $prefixDirsPsr4 = array (
@ -107,10 +113,6 @@ class ComposerStaticInitDrupal8
array (
0 => __DIR__ . '/..' . '/zendframework/zend-stdlib/src',
),
'Zend\\Hydrator\\' =>
array (
0 => __DIR__ . '/..' . '/zendframework/zend-hydrator/src',
),
'Zend\\Feed\\' =>
array (
0 => __DIR__ . '/..' . '/zendframework/zend-feed/src',
@ -215,6 +217,10 @@ class ComposerStaticInitDrupal8
array (
0 => __DIR__ . '/..' . '/symfony/psr-http-message-bridge',
),
'Psr\\Log\\' =>
array (
0 => __DIR__ . '/..' . '/psr/log/Psr/Log',
),
'Psr\\Http\\Message\\' =>
array (
0 => __DIR__ . '/..' . '/psr/http-message/src',
@ -242,7 +248,6 @@ class ComposerStaticInitDrupal8
'Drupal\\Driver\\' =>
array (
0 => __DIR__ . '/../..' . '/drivers/lib/Drupal/Driver',
1 => __DIR__ . '/../..' . '/drivers/lib/Drupal/Driver',
),
'Drupal\\Core\\Composer\\' =>
array (
@ -251,12 +256,10 @@ class ComposerStaticInitDrupal8
'Drupal\\Core\\' =>
array (
0 => __DIR__ . '/../..' . '/core/lib/Drupal/Core',
1 => __DIR__ . '/../..' . '/core/lib/Drupal/Core',
),
'Drupal\\Component\\' =>
array (
0 => __DIR__ . '/../..' . '/core/lib/Drupal/Component',
1 => __DIR__ . '/../..' . '/core/lib/Drupal/Component',
),
'DrupalComposer\\DrupalScaffold\\' =>
array (
@ -266,6 +269,14 @@ class ComposerStaticInitDrupal8
array (
0 => __DIR__ . '/..' . '/doctrine/instantiator/src/Doctrine/Instantiator',
),
'Doctrine\\Common\\Cache\\' =>
array (
0 => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache',
),
'Doctrine\\Common\\' =>
array (
0 => __DIR__ . '/..' . '/doctrine/common/lib/Doctrine/Common',
),
'Composer\\Semver\\' =>
array (
0 => __DIR__ . '/..' . '/composer/semver/src',
@ -279,6 +290,10 @@ class ComposerStaticInitDrupal8
array (
0 => __DIR__ . '/..' . '/behat/mink/src',
),
'Asm89\\Stack\\' =>
array (
0 => __DIR__ . '/..' . '/asm89/stack-cors/src/Asm89/Stack',
),
);
public static $prefixesPsr0 = array (
@ -312,10 +327,6 @@ class ComposerStaticInitDrupal8
),
'P' =>
array (
'Psr\\Log\\' =>
array (
0 => __DIR__ . '/..' . '/psr/log',
),
'Prophecy\\' =>
array (
0 => __DIR__ . '/..' . '/phpspec/prophecy/src',
@ -346,18 +357,10 @@ class ComposerStaticInitDrupal8
array (
0 => __DIR__ . '/..' . '/doctrine/collections/lib',
),
'Doctrine\\Common\\Cache\\' =>
array (
0 => __DIR__ . '/..' . '/doctrine/cache/lib',
),
'Doctrine\\Common\\Annotations\\' =>
array (
0 => __DIR__ . '/..' . '/doctrine/annotations/lib',
),
'Doctrine\\Common\\' =>
array (
0 => __DIR__ . '/..' . '/doctrine/common/lib',
),
),
'C' =>
array (
@ -366,13 +369,6 @@ class ComposerStaticInitDrupal8
0 => __DIR__ . '/..' . '/composer/installers/src',
),
),
'A' =>
array (
'Asm89\\Stack' =>
array (
0 => __DIR__ . '/..' . '/asm89/stack-cors/src',
),
),
);
public static $classMap = array (
@ -819,14 +815,6 @@ class ComposerStaticInitDrupal8
'SebastianBergmann\\RecursionContext\\InvalidArgumentException' => __DIR__ . '/..' . '/sebastian/recursion-context/src/InvalidArgumentException.php',
'SebastianBergmann\\Version' => __DIR__ . '/..' . '/sebastian/version/src/Version.php',
'SessionHandlerInterface' => __DIR__ . '/..' . '/symfony/polyfill-php54/Resources/stubs/SessionHandlerInterface.php',
'Symfony\\Component\\HttpFoundation\\FileBag' => __DIR__ . '/..' . '/symfony/http-foundation/FileBag.php',
'Symfony\\Component\\HttpFoundation\\HeaderBag' => __DIR__ . '/..' . '/symfony/http-foundation/HeaderBag.php',
'Symfony\\Component\\HttpFoundation\\ParameterBag' => __DIR__ . '/..' . '/symfony/http-foundation/ParameterBag.php',
'Symfony\\Component\\HttpFoundation\\Request' => __DIR__ . '/..' . '/symfony/http-foundation/Request.php',
'Symfony\\Component\\HttpFoundation\\ServerBag' => __DIR__ . '/..' . '/symfony/http-foundation/ServerBag.php',
'Symfony\\Component\\HttpKernel\\HttpKernel' => __DIR__ . '/..' . '/symfony/http-kernel/HttpKernel.php',
'Symfony\\Component\\HttpKernel\\HttpKernelInterface' => __DIR__ . '/..' . '/symfony/http-kernel/HttpKernelInterface.php',
'Symfony\\Component\\HttpKernel\\TerminableInterface' => __DIR__ . '/..' . '/symfony/http-kernel/TerminableInterface.php',
'Text_Template' => __DIR__ . '/..' . '/phpunit/php-text-template/src/Template.php',
);

File diff suppressed because it is too large Load diff

View file

@ -3,6 +3,36 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
### [1.4.2] 2016-08-30
* Fixed: collapsing of complex constraints lead to buggy constraints
### [1.4.1] 2016-06-02
* Changed: branch-like requirements no longer strip build metadata - [composer/semver#38](https://github.com/composer/semver/pull/38).
### [1.4.0] 2016-03-30
* Added: getters on MultiConstraint - [composer/semver#35](https://github.com/composer/semver/pull/35).
### [1.3.0] 2016-02-25
* Fixed: stability parsing - [composer/composer#1234](https://github.com/composer/composer/issues/4889).
* Changed: collapse contiguous constraints when possible.
### [1.2.0] 2015-11-10
* Changed: allow multiple numerical identifiers in 'pre-release' version part.
* Changed: add more 'v' prefix support.
### [1.1.0] 2015-11-03
* Changed: dropped redundant `test` namespace.
* Changed: minor adjustment in datetime parsing normalization.
* Changed: `ConstraintInterface` relaxed, setPrettyString is not required anymore.
* Changed: `AbstractConstraint` marked deprecated, will be removed in 2.0.
* Changed: `Constraint` is now extensible.
### [1.0.0] 2015-09-21
* Break: `VersionConstraint` renamed to `Constraint`.
@ -13,7 +43,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
* Changed: `VersionParser::parseConstraints` allows (but ignores) prefixing numeric versions with a 'v' now.
* Changed: Fixed namespace(s) of test files.
* Changed: `Comparator::compare` no longer throws `InvalidArgumentException`.
* Changed: `VersionConstraint` now throws `InvalidArgumentException`.
* Changed: `Constraint` now throws `InvalidArgumentException`.
### [0.1.0] 2015-07-23
@ -26,3 +56,11 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Namespace: `Composer\Test\Package\Version` -> `Composer\Test\Semver`
- Namespace: `Composer\Test\Package\LinkConstraint` -> `Composer\Test\Semver\Constraint`
* Changed: code style using php-cs-fixer.
[1.4.1]: https://github.com/composer/semver/compare/1.4.0...1.4.1
[1.4.0]: https://github.com/composer/semver/compare/1.3.0...1.4.0
[1.3.0]: https://github.com/composer/semver/compare/1.2.0...1.3.0
[1.2.0]: https://github.com/composer/semver/compare/1.1.0...1.2.0
[1.1.0]: https://github.com/composer/semver/compare/1.0.0...1.1.0
[1.0.0]: https://github.com/composer/semver/compare/0.1.0...1.0.0
[0.1.0]: https://github.com/composer/semver/compare/5e0b9a4da...0.1.0

View file

@ -56,10 +56,10 @@ Comparator::greaterThan('1.25.0', '1.24.0'); // 1.25.0 > 1.24.0
### Semver
The `Composer\Semver\Semver` class providers the following methods:
The `Composer\Semver\Semver` class provides the following methods:
* satisfies($version, $constraints)
* satisfiedBy($constraint, array $versions)
* satisfiedBy(array $versions, $constraint)
* sort($versions)
* rsort($versions)

View file

@ -22,7 +22,8 @@
},
{
"name": "Rob Bast",
"email": "rob.bast@gmail.com"
"email": "rob.bast@gmail.com",
"homepage": "http://robbast.nl"
}
],
"support": {
@ -30,11 +31,11 @@
"issues": "https://github.com/composer/semver/issues"
},
"require": {
"php": ">=5.3.2"
"php": "^5.3.2 || ^7.0"
},
"require-dev": {
"phpunit/phpunit": "~4.5",
"phpunit/phpunit-mock-objects": "~2.3"
"phpunit/phpunit": "^4.5 || ^5.0.5",
"phpunit/phpunit-mock-objects": "2.3.0 || ^3.0"
},
"autoload": {
"psr-4": {
@ -43,12 +44,12 @@
},
"autoload-dev": {
"psr-4": {
"Composer\\Semver\\Test\\": "tests"
"Composer\\Semver\\": "tests"
}
},
"extra": {
"branch-alias": {
"dev-master": "0.1-dev"
"dev-master": "1.x-dev"
}
},
"scripts": {

View file

@ -11,6 +11,8 @@
namespace Composer\Semver\Constraint;
trigger_error('The ' . __CLASS__ . ' abstract class is deprecated, there is no replacement for it, it will be removed in the next major version.', E_USER_DEPRECATED);
/**
* Base constraint class.
*/
@ -26,17 +28,13 @@ abstract class AbstractConstraint implements ConstraintInterface
*/
public function matches(ConstraintInterface $provider)
{
if ($provider instanceof MultiConstraint) {
// turn matching around to find a match
return $provider->matches($this);
}
if ($provider instanceof $this) {
// see note at bottom of this class declaration
return $this->matchSpecific($provider);
}
return true;
// turn matching around to find a match
return $provider->matches($this);
}
/**

View file

@ -14,7 +14,7 @@ namespace Composer\Semver\Constraint;
/**
* Defines a constraint.
*/
class Constraint extends AbstractConstraint
class Constraint implements ConstraintInterface
{
/* operator integer values */
const OP_EQ = 0;
@ -55,10 +55,48 @@ class Constraint extends AbstractConstraint
);
/** @var string */
private $operator;
protected $operator;
/** @var string */
private $version;
protected $version;
/** @var string */
protected $prettyString;
/**
* @param ConstraintInterface $provider
*
* @return bool
*/
public function matches(ConstraintInterface $provider)
{
if ($provider instanceof $this) {
return $this->matchSpecific($provider);
}
// turn matching around to find a match
return $provider->matches($this);
}
/**
* @param string $prettyString
*/
public function setPrettyString($prettyString)
{
$this->prettyString = $prettyString;
}
/**
* @return string
*/
public function getPrettyString()
{
if ($this->prettyString) {
return $this->prettyString;
}
return $this->__toString();
}
/**
* Get all supported comparison operators.

View file

@ -20,11 +20,6 @@ interface ConstraintInterface
*/
public function matches(ConstraintInterface $provider);
/**
* @param string $prettyString
*/
public function setPrettyString($prettyString);
/**
* @return string
*/

View file

@ -35,6 +35,30 @@ class MultiConstraint implements ConstraintInterface
$this->conjunctive = $conjunctive;
}
/**
* @return ConstraintInterface[]
*/
public function getConstraints()
{
return $this->constraints;
}
/**
* @return bool
*/
public function isConjunctive()
{
return $this->conjunctive;
}
/**
* @return bool
*/
public function isDisjunctive()
{
return !$this->conjunctive;
}
/**
* @param ConstraintInterface $provider
*

View file

@ -23,13 +23,23 @@ use Composer\Semver\Constraint\Constraint;
*/
class VersionParser
{
/** @var string */
private static $modifierRegex = '[._-]?(?:(stable|beta|b|RC|alpha|a|patch|pl|p)(?:[.-]?(\d+))?)?([.-]?dev)?';
/**
* Regex to match pre-release data (sort of).
*
* Due to backwards compatibility:
* - Instead of enforcing hyphen, an underscore, dot or nothing at all are also accepted.
* - Only stabilities as recognized by Composer are allowed to precede a numerical identifier.
* - Numerical-only pre-release identifiers are not supported, see tests.
*
* |--------------|
* [major].[minor].[patch] -[pre-release] +[build-metadata]
*
* @var string
*/
private static $modifierRegex = '[._-]?(?:(stable|beta|b|RC|alpha|a|patch|pl|p)((?:[.-]?\d+)*+)?)?([.-]?dev)?';
/** @var array */
private static $stabilities = array(
'stable', 'RC', 'beta', 'alpha', 'dev',
);
private static $stabilities = array('stable', 'RC', 'beta', 'alpha', 'dev');
/**
* Returns the stability of a version.
@ -46,7 +56,7 @@ class VersionParser
return 'dev';
}
preg_match('{' . self::$modifierRegex . '$}i', strtolower($version), $match);
preg_match('{' . self::$modifierRegex . '(?:\+.*)?$}i', strtolower($version), $match);
if (!empty($match[3])) {
return 'dev';
}
@ -96,12 +106,7 @@ class VersionParser
}
// strip off aliasing
if (preg_match('{^([^,\s]+) +as +([^,\s]+)$}', $version, $match)) {
$version = $match[1];
}
// strip off build metadata
if (preg_match('{^([^,\s+]+)\+[^\s]+$}', $version, $match)) {
if (preg_match('{^([^,\s]++) ++as ++([^,\s]++)$}', $version, $match)) {
$version = $match[1];
}
@ -110,12 +115,18 @@ class VersionParser
return '9999999-dev';
}
// if requirement is branch-like, use full name
if ('dev-' === strtolower(substr($version, 0, 4))) {
return 'dev-' . substr($version, 4);
}
// strip off build metadata
if (preg_match('{^([^,\s+]++)\+[^\s]++$}', $version, $match)) {
$version = $match[1];
}
// match classical versioning
if (preg_match('{^v?(\d{1,5})(\.\d+)?(\.\d+)?(\.\d+)?' . self::$modifierRegex . '$}i', $version, $matches)) {
if (preg_match('{^v?(\d{1,5})(\.\d++)?(\.\d++)?(\.\d++)?' . self::$modifierRegex . '$}i', $version, $matches)) {
$version = $matches[1]
. (!empty($matches[2]) ? $matches[2] : '.0')
. (!empty($matches[3]) ? $matches[3] : '.0')
@ -123,7 +134,7 @@ class VersionParser
$index = 5;
// match date(time) based versioning
} elseif (preg_match('{^v?(\d{4}(?:[.:-]?\d{2}){1,6}(?:[.:-]?\d{1,3})?)' . self::$modifierRegex . '$}i', $version, $matches)) {
$version = preg_replace('{\D}', '-', $matches[1]);
$version = preg_replace('{\D}', '.', $matches[1]);
$index = 2;
}
@ -133,7 +144,7 @@ class VersionParser
if ('stable' === $matches[$index]) {
return $version;
}
$version .= '-' . $this->expandStability($matches[$index]) . (!empty($matches[$index + 1]) ? $matches[$index + 1] : '');
$version .= '-' . $this->expandStability($matches[$index]) . (!empty($matches[$index + 1]) ? ltrim($matches[$index + 1], '.-') : '');
}
if (!empty($matches[$index + 2])) {
@ -170,7 +181,7 @@ class VersionParser
*/
public function parseNumericAliasPrefix($branch)
{
if (preg_match('{^(?P<version>(\d+\\.)*\d+)(?:\.x)?-dev$}i', $branch, $matches)) {
if (preg_match('{^(?P<version>(\d++\\.)*\d++)(?:\.x)?-dev$}i', $branch, $matches)) {
return $matches['version'] . '.';
}
@ -192,7 +203,7 @@ class VersionParser
return $this->normalize($name);
}
if (preg_match('{^v?(\d+)(\.(?:\d+|[xX*]))?(\.(?:\d+|[xX*]))?(\.(?:\d+|[xX*]))?$}i', $name, $matches)) {
if (preg_match('{^v?(\d++)(\.(?:\d++|[xX*]))?(\.(?:\d++|[xX*]))?(\.(?:\d++|[xX*]))?$}i', $name, $matches)) {
$version = '';
for ($i = 1; $i < 5; ++$i) {
$version .= isset($matches[$i]) ? str_replace(array('*', 'X'), 'x', $matches[$i]) : '.x';
@ -205,7 +216,7 @@ class VersionParser
}
/**
* Parses as constraint string into LinkConstraint objects.
* Parses a constraint string into MultiConstraint and/or Constraint objects.
*
* @param string $constraints
*
@ -249,6 +260,23 @@ class VersionParser
if (1 === count($orGroups)) {
$constraint = $orGroups[0];
} elseif (2 === count($orGroups)
// parse the two OR groups and if they are contiguous we collapse
// them into one constraint
&& $orGroups[0] instanceof MultiConstraint
&& $orGroups[1] instanceof MultiConstraint
&& 2 === count($orGroups[0]->getConstraints())
&& 2 === count($orGroups[1]->getConstraints())
&& ($a = (string) $orGroups[0])
&& substr($a, 0, 3) === '[>=' && (false !== ($posA = strpos($a, '<', 4)))
&& ($b = (string) $orGroups[1])
&& substr($b, 0, 3) === '[>=' && (false !== ($posB = strpos($b, '<', 4)))
&& substr($a, $posA + 2, -1) === substr($b, 4, $posB - 5)
) {
$constraint = new MultiConstraint(array(
new Constraint('>=', substr($a, 4, $posA - 5)),
new Constraint('<', substr($b, $posB + 2, -1)),
));
} else {
$constraint = new MultiConstraint($orGroups, false);
}
@ -274,11 +302,11 @@ class VersionParser
}
}
if (preg_match('{^[xX*](\.[xX*])*$}i', $constraint)) {
if (preg_match('{^v?[xX*](\.[xX*])*$}i', $constraint)) {
return array(new EmptyConstraint());
}
$versionRegex = 'v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?' . self::$modifierRegex . '(?:\+[^\s]+)?';
$versionRegex = 'v?(\d++)(?:\.(\d++))?(?:\.(\d++))?(?:\.(\d++))?' . self::$modifierRegex . '(?:\+[^\s]+)?';
// Tilde Range
//
@ -372,7 +400,7 @@ class VersionParser
//
// Any of X, x, or * may be used to "stand in" for one of the numeric values in the [major, minor, patch] tuple.
// A partial version range is treated as an X-Range, so the special character is in fact optional.
if (preg_match('{^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.[xX*])+$}', $constraint, $matches)) {
if (preg_match('{^v?(\d++)(?:\.(\d++))?(?:\.(\d++))?(?:\.[xX*])++$}', $constraint, $matches)) {
if (isset($matches[3]) && '' !== $matches[3]) {
$position = 3;
} elseif (isset($matches[2]) && '' !== $matches[2]) {

View file

@ -1,11 +1,17 @@
language: php
sudo: false
cache:
directories:
- vendor
- $HOME/.composer/cache
php:
- 5.3
- 5.4
- 5.5
- 5.6
- hhvm
- 5.5
- 5.6
- 7.0
- hhvm
services:
- riak
@ -14,13 +20,14 @@ services:
- redis-server
before_install:
- sh -c "if [ $TRAVIS_PHP_VERSION != 'hhvm' ]; then pecl install riak-beta; fi"
- sh -c "if [[ $TRAVIS_PHP_VERSION != 'hhvm' && `php-config --vernum` -ge 50500 ]] ; then pecl config-set preferred_state beta; printf "yes\n" | pecl install apcu ; else echo 'extension="apc.so"' >> ./tests/travis/php.ini ;fi"
- composer self-update
- sh -c "if [ $TRAVIS_PHP_VERSION != 'hhvm' ]; then phpenv config-add ./tests/travis/php.ini; fi"
- if [[ $TRAVIS_PHP_VERSION != 'hhvm' ]] ; then pecl channel-update pecl.php.net; fi;
- if [[ $TRAVIS_PHP_VERSION != 'hhvm' && $TRAVIS_PHP_VERSION != '7.0' ]]; then pecl install riak-beta; fi;
- if [[ $TRAVIS_PHP_VERSION =~ 5.[56] ]] ; then echo yes | pecl install apcu-4.0.10; fi;
- if [[ $TRAVIS_PHP_VERSION = 7.* ]] ; then pecl config-set preferred_state beta; echo yes | pecl install apcu; fi;
- if [[ $TRAVIS_PHP_VERSION != 'hhvm' ]]; then phpenv config-add ./tests/travis/php.ini; fi;
install:
- composer --prefer-source --dev install
- travis_retry composer install
script:
- ./vendor/bin/phpunit -c ./tests/travis/phpunit.travis.xml -v
@ -29,5 +36,7 @@ after_script:
- php vendor/bin/coveralls -v
matrix:
allow_failures:
- php: hhvm
fast_finish: true
allow_failures:
- php: hhvm
- php: 7.0

View file

@ -1,4 +1,4 @@
Copyright (c) 2006-2012 Doctrine Project
Copyright (c) 2006-2015 Doctrine Project
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in

View file

@ -13,10 +13,10 @@
{"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
],
"require": {
"php": ">=5.3.2"
"php": "~5.5|~7.0"
},
"require-dev": {
"phpunit/phpunit": ">=3.7",
"phpunit/phpunit": "~4.8|~5.0",
"satooshi/php-coveralls": "~0.6",
"predis/predis": "~1.0"
},
@ -24,11 +24,14 @@
"doctrine/common": ">2.2,<2.4"
},
"autoload": {
"psr-0": { "Doctrine\\Common\\Cache\\": "lib/" }
"psr-4": { "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" }
},
"autoload-dev": {
"psr-4": { "Doctrine\\Tests\\": "tests/Doctrine/Tests" }
},
"extra": {
"branch-alias": {
"dev-master": "1.5.x-dev"
"dev-master": "1.6.x-dev"
}
}
}

View file

@ -22,13 +22,14 @@ namespace Doctrine\Common\Cache;
/**
* APC cache provider.
*
* @link www.doctrine-project.org
* @since 2.0
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author David Abdemoulaie <dave@hobodave.com>
* @link www.doctrine-project.org
* @deprecated since version 1.6, use ApcuCache instead
* @since 2.0
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author David Abdemoulaie <dave@hobodave.com>
*/
class ApcCache extends CacheProvider
{
@ -53,7 +54,7 @@ class ApcCache extends CacheProvider
*/
protected function doSave($id, $data, $lifeTime = 0)
{
return (bool) apc_store($id, $data, (int) $lifeTime);
return apc_store($id, $data, $lifeTime);
}
/**
@ -61,7 +62,8 @@ class ApcCache extends CacheProvider
*/
protected function doDelete($id)
{
return apc_delete($id);
// apc_delete returns false if the id does not exist
return apc_delete($id) || ! apc_exists($id);
}
/**
@ -77,7 +79,17 @@ class ApcCache extends CacheProvider
*/
protected function doFetchMultiple(array $keys)
{
return apc_fetch($keys);
return apc_fetch($keys) ?: [];
}
/**
* {@inheritdoc}
*/
protected function doSaveMultiple(array $keysAndValues, $lifetime = 0)
{
$result = apc_store($keysAndValues, null, $lifetime);
return empty($result);
}
/**

View file

@ -0,0 +1,106 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Cache;
/**
* APCu cache provider.
*
* @link www.doctrine-project.org
* @since 1.6
* @author Kévin Dunglas <dunglas@gmail.com>
*/
class ApcuCache extends CacheProvider
{
/**
* {@inheritdoc}
*/
protected function doFetch($id)
{
return apcu_fetch($id);
}
/**
* {@inheritdoc}
*/
protected function doContains($id)
{
return apcu_exists($id);
}
/**
* {@inheritdoc}
*/
protected function doSave($id, $data, $lifeTime = 0)
{
return apcu_store($id, $data, $lifeTime);
}
/**
* {@inheritdoc}
*/
protected function doDelete($id)
{
// apcu_delete returns false if the id does not exist
return apcu_delete($id) || ! apcu_exists($id);
}
/**
* {@inheritdoc}
*/
protected function doFlush()
{
return apcu_clear_cache();
}
/**
* {@inheritdoc}
*/
protected function doFetchMultiple(array $keys)
{
return apcu_fetch($keys) ?: [];
}
/**
* {@inheritdoc}
*/
protected function doSaveMultiple(array $keysAndValues, $lifetime = 0)
{
$result = apcu_store($keysAndValues, null, $lifetime);
return empty($result);
}
/**
* {@inheritdoc}
*/
protected function doGetStats()
{
$info = apcu_cache_info(true);
$sma = apcu_sma_info();
return array(
Cache::STATS_HITS => $info['num_hits'],
Cache::STATS_MISSES => $info['num_misses'],
Cache::STATS_UPTIME => $info['start_time'],
Cache::STATS_MEMORY_USAGE => $info['mem_size'],
Cache::STATS_MEMORY_AVAILABLE => $sma['avail_mem'],
);
}
}

View file

@ -33,16 +33,47 @@ namespace Doctrine\Common\Cache;
class ArrayCache extends CacheProvider
{
/**
* @var array $data
* @var array[] $data each element being a tuple of [$data, $expiration], where the expiration is int|bool
*/
private $data = array();
private $data = [];
/**
* @var int
*/
private $hitsCount = 0;
/**
* @var int
*/
private $missesCount = 0;
/**
* @var int
*/
private $upTime;
/**
* {@inheritdoc}
*/
public function __construct()
{
$this->upTime = time();
}
/**
* {@inheritdoc}
*/
protected function doFetch($id)
{
return $this->doContains($id) ? $this->data[$id] : false;
if (! $this->doContains($id)) {
$this->missesCount += 1;
return false;
}
$this->hitsCount += 1;
return $this->data[$id][0];
}
/**
@ -50,8 +81,19 @@ class ArrayCache extends CacheProvider
*/
protected function doContains($id)
{
// isset() is required for performance optimizations, to avoid unnecessary function calls to array_key_exists.
return isset($this->data[$id]) || array_key_exists($id, $this->data);
if (! isset($this->data[$id])) {
return false;
}
$expiration = $this->data[$id][1];
if ($expiration && $expiration < time()) {
$this->doDelete($id);
return false;
}
return true;
}
/**
@ -59,7 +101,7 @@ class ArrayCache extends CacheProvider
*/
protected function doSave($id, $data, $lifeTime = 0)
{
$this->data[$id] = $data;
$this->data[$id] = [$data, $lifeTime ? time() + $lifeTime : false];
return true;
}
@ -79,7 +121,7 @@ class ArrayCache extends CacheProvider
*/
protected function doFlush()
{
$this->data = array();
$this->data = [];
return true;
}
@ -89,6 +131,12 @@ class ArrayCache extends CacheProvider
*/
protected function doGetStats()
{
return null;
return [
Cache::STATS_HITS => $this->hitsCount,
Cache::STATS_MISSES => $this->missesCount,
Cache::STATS_UPTIME => $this->upTime,
Cache::STATS_MEMORY_USAGE => null,
Cache::STATS_MEMORY_AVAILABLE => null,
];
}
}

View file

@ -59,19 +59,22 @@ interface Cache
*
* @param string $id The cache id of the entry to check for.
*
* @return boolean TRUE if a cache entry exists for the given cache id, FALSE otherwise.
* @return bool TRUE if a cache entry exists for the given cache id, FALSE otherwise.
*/
public function contains($id);
/**
* Puts data into the cache.
*
* If a cache entry with the given id already exists, its data will be replaced.
*
* @param string $id The cache id.
* @param mixed $data The cache entry/data.
* @param int $lifeTime The cache lifetime.
* If != 0, sets a specific lifetime for this cache entry (0 => infinite lifeTime).
* @param int $lifeTime The lifetime in number of seconds for this cache entry.
* If zero (the default), the entry never expires (although it may be deleted from the cache
* to make place for other entries).
*
* @return boolean TRUE if the entry was successfully stored in the cache, FALSE otherwise.
* @return bool TRUE if the entry was successfully stored in the cache, FALSE otherwise.
*/
public function save($id, $data, $lifeTime = 0);
@ -80,7 +83,8 @@ interface Cache
*
* @param string $id The cache id.
*
* @return boolean TRUE if the cache entry was successfully deleted, FALSE otherwise.
* @return bool TRUE if the cache entry was successfully deleted, FALSE otherwise.
* Deleting a non-existing entry is considered successful.
*/
public function delete($id);

View file

@ -29,7 +29,7 @@ namespace Doctrine\Common\Cache;
* @author Roman Borschel <roman@code-factory.org>
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, MultiGetCache
abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, MultiGetCache, MultiPutCache
{
const DOCTRINE_NAMESPACE_CACHEKEY = 'DoctrineNamespaceCacheKey[%s]';
@ -83,6 +83,10 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M
*/
public function fetchMultiple(array $keys)
{
if (empty($keys)) {
return array();
}
// note: the array_combine() is in place to keep an association between our $keys and the $namespacedKeys
$namespacedKeys = array_combine($keys, array_map(array($this, 'getNamespacedId'), $keys));
$items = $this->doFetchMultiple($namespacedKeys);
@ -91,7 +95,7 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M
// no internal array function supports this sort of mapping: needs to be iterative
// this filters and combines keys in one pass
foreach ($namespacedKeys as $requestedKey => $namespacedKey) {
if (isset($items[$namespacedKey])) {
if (isset($items[$namespacedKey]) || array_key_exists($namespacedKey, $items)) {
$foundItems[$requestedKey] = $items[$namespacedKey];
}
}
@ -99,6 +103,19 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M
return $foundItems;
}
/**
* {@inheritdoc}
*/
public function saveMultiple(array $keysAndValues, $lifetime = 0)
{
$namespacedKeysAndValues = array();
foreach ($keysAndValues as $key => $value) {
$namespacedKeysAndValues[$this->getNamespacedId($key)] = $value;
}
return $this->doSaveMultiple($namespacedKeysAndValues, $lifetime);
}
/**
* {@inheritdoc}
*/
@ -147,9 +164,13 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M
$namespaceCacheKey = $this->getNamespaceCacheKey();
$namespaceVersion = $this->getNamespaceVersion() + 1;
$this->namespaceVersion = $namespaceVersion;
if ($this->doSave($namespaceCacheKey, $namespaceVersion)) {
$this->namespaceVersion = $namespaceVersion;
return $this->doSave($namespaceCacheKey, $namespaceVersion);
return true;
}
return false;
}
/**
@ -188,15 +209,7 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M
}
$namespaceCacheKey = $this->getNamespaceCacheKey();
$namespaceVersion = $this->doFetch($namespaceCacheKey);
if (false === $namespaceVersion) {
$namespaceVersion = 1;
$this->doSave($namespaceCacheKey, $namespaceVersion);
}
$this->namespaceVersion = $namespaceVersion;
$this->namespaceVersion = $this->doFetch($namespaceCacheKey) ?: 1;
return $this->namespaceVersion;
}
@ -211,8 +224,8 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M
{
$returnValues = array();
foreach ($keys as $index => $key) {
if (false !== ($item = $this->doFetch($key))) {
foreach ($keys as $key) {
if (false !== ($item = $this->doFetch($key)) || $this->doContains($key)) {
$returnValues[$key] = $item;
}
}
@ -225,7 +238,7 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M
*
* @param string $id The id of the cache entry to fetch.
*
* @return mixed|boolean The cached data or FALSE, if no cache entry exists for the given id.
* @return mixed|false The cached data or FALSE, if no cache entry exists for the given id.
*/
abstract protected function doFetch($id);
@ -234,10 +247,32 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M
*
* @param string $id The cache id of the entry to check for.
*
* @return boolean TRUE if a cache entry exists for the given cache id, FALSE otherwise.
* @return bool TRUE if a cache entry exists for the given cache id, FALSE otherwise.
*/
abstract protected function doContains($id);
/**
* Default implementation of doSaveMultiple. Each driver that supports multi-put should override it.
*
* @param array $keysAndValues Array of keys and values to save in cache
* @param int $lifetime The lifetime. If != 0, sets a specific lifetime for these
* cache entries (0 => infinite lifeTime).
*
* @return bool TRUE if the operation was successful, FALSE if it wasn't.
*/
protected function doSaveMultiple(array $keysAndValues, $lifetime = 0)
{
$success = true;
foreach ($keysAndValues as $key => $value) {
if (!$this->doSave($key, $value, $lifetime)) {
$success = false;
}
}
return $success;
}
/**
* Puts data into the cache.
*
@ -246,7 +281,7 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M
* @param int $lifeTime The lifetime. If != 0, sets a specific lifetime for this
* cache entry (0 => infinite lifeTime).
*
* @return boolean TRUE if the entry was successfully stored in the cache, FALSE otherwise.
* @return bool TRUE if the entry was successfully stored in the cache, FALSE otherwise.
*/
abstract protected function doSave($id, $data, $lifeTime = 0);
@ -255,14 +290,14 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M
*
* @param string $id The cache id.
*
* @return boolean TRUE if the cache entry was successfully deleted, FALSE otherwise.
* @return bool TRUE if the cache entry was successfully deleted, FALSE otherwise.
*/
abstract protected function doDelete($id);
/**
* Flushes all cache entries.
*
* @return boolean TRUE if the cache entries were successfully flushed, FALSE otherwise.
* @return bool TRUE if the cache entries were successfully flushed, FALSE otherwise.
*/
abstract protected function doFlush();

View file

@ -34,7 +34,7 @@ interface ClearableCache
/**
* Deletes all cache entries in the current cache namespace.
*
* @return boolean TRUE if the cache entries were successfully deleted, FALSE otherwise.
* @return bool TRUE if the cache entries were successfully deleted, FALSE otherwise.
*/
public function deleteAll();
}

View file

@ -24,6 +24,7 @@ namespace Doctrine\Common\Cache;
*
* @since 2.3
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
* @author Tobias Schultze <http://tobion.de>
*/
abstract class FileCache extends CacheProvider
{
@ -42,22 +43,24 @@ abstract class FileCache extends CacheProvider
private $extension;
/**
* @var string[] regular expressions for replacing disallowed characters in file name
* @var int
*/
private $disallowedCharacterPatterns = array(
'/\-/', // replaced to disambiguate original `-` and `-` derived from replacements
'/[^a-zA-Z0-9\-_\[\]]/' // also excludes non-ascii chars (not supported, depending on FS)
);
/**
* @var string[] replacements for disallowed file characters
*/
private $replacementCharacters = array('__', '-');
private $umask;
/**
* @var int
*/
private $umask;
private $directoryStringLength;
/**
* @var int
*/
private $extensionStringLength;
/**
* @var bool
*/
private $isRunningOnWindows;
/**
* Constructor.
@ -95,6 +98,10 @@ abstract class FileCache extends CacheProvider
// YES, this needs to be *after* createPathIfNeeded()
$this->directory = realpath($directory);
$this->extension = (string) $extension;
$this->directoryStringLength = strlen($this->directory);
$this->extensionStringLength = strlen($this->extension);
$this->isRunningOnWindows = defined('PHP_WINDOWS_VERSION_BUILD');
}
/**
@ -110,7 +117,7 @@ abstract class FileCache extends CacheProvider
/**
* Gets the cache file extension.
*
* @return string|null
* @return string
*/
public function getExtension()
{
@ -124,11 +131,29 @@ abstract class FileCache extends CacheProvider
*/
protected function getFilename($id)
{
$hash = hash('sha256', $id);
// This ensures that the filename is unique and that there are no invalid chars in it.
if (
'' === $id
|| ((strlen($id) * 2 + $this->extensionStringLength) > 255)
|| ($this->isRunningOnWindows && ($this->directoryStringLength + 4 + strlen($id) * 2 + $this->extensionStringLength) > 258)
) {
// Most filesystems have a limit of 255 chars for each path component. On Windows the the whole path is limited
// to 260 chars (including terminating null char). Using long UNC ("\\?\" prefix) does not work with the PHP API.
// And there is a bug in PHP (https://bugs.php.net/bug.php?id=70943) with path lengths of 259.
// So if the id in hex representation would surpass the limit, we use the hash instead. The prefix prevents
// collisions between the hash and bin2hex.
$filename = '_' . $hash;
} else {
$filename = bin2hex($id);
}
return $this->directory
. DIRECTORY_SEPARATOR
. implode(str_split(hash('sha256', $id), 2), DIRECTORY_SEPARATOR)
. substr($hash, 0, 2)
. DIRECTORY_SEPARATOR
. preg_replace($this->disallowedCharacterPatterns, $this->replacementCharacters, $id)
. $filename
. $this->extension;
}
@ -137,7 +162,9 @@ abstract class FileCache extends CacheProvider
*/
protected function doDelete($id)
{
return @unlink($this->getFilename($id));
$filename = $this->getFilename($id);
return @unlink($filename) || ! file_exists($filename);
}
/**
@ -146,7 +173,16 @@ abstract class FileCache extends CacheProvider
protected function doFlush()
{
foreach ($this->getIterator() as $name => $file) {
@unlink($name);
if ($file->isDir()) {
// Remove the intermediate directories which have been created to balance the tree. It only takes effect
// if the directory is empty. If several caches share the same directory but with different file extensions,
// the other ones are not removed.
@rmdir($name);
} elseif ($this->isFilenameEndingWithExtension($name)) {
// If an extension is set, only remove files which end with the given extension.
// If no extension is set, we have no other choice than removing everything.
@unlink($name);
}
}
return true;
@ -158,8 +194,10 @@ abstract class FileCache extends CacheProvider
protected function doGetStats()
{
$usage = 0;
foreach ($this->getIterator() as $file) {
$usage += $file->getSize();
foreach ($this->getIterator() as $name => $file) {
if (! $file->isDir() && $this->isFilenameEndingWithExtension($name)) {
$usage += $file->getSize();
}
}
$free = disk_free_space($this->directory);
@ -229,9 +267,20 @@ abstract class FileCache extends CacheProvider
*/
private function getIterator()
{
return new \RegexIterator(
new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory)),
'/^.+' . preg_quote($this->extension, '/') . '$/i'
return new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS),
\RecursiveIteratorIterator::CHILD_FIRST
);
}
/**
* @param string $name The filename
*
* @return bool
*/
private function isFilenameEndingWithExtension($name)
{
return '' === $this->extension
|| strrpos($name, $this->extension) === (strlen($name) - $this->extensionStringLength);
}
}

View file

@ -53,7 +53,7 @@ class FilesystemCache extends FileCache
$resource = fopen($filename, "r");
if (false !== ($line = fgets($resource))) {
$lifetime = (integer) $line;
$lifetime = (int) $line;
}
if ($lifetime !== 0 && $lifetime < time()) {
@ -86,7 +86,7 @@ class FilesystemCache extends FileCache
$resource = fopen($filename, "r");
if (false !== ($line = fgets($resource))) {
$lifetime = (integer) $line;
$lifetime = (int) $line;
}
fclose($resource);

View file

@ -31,7 +31,7 @@ interface FlushableCache
/**
* Flushes all cache entries, globally.
*
* @return boolean TRUE if the cache entries were successfully flushed, FALSE otherwise.
* @return bool TRUE if the cache entries were successfully flushed, FALSE otherwise.
*/
public function flushAll();
}

View file

@ -97,7 +97,8 @@ class MemcacheCache extends CacheProvider
*/
protected function doDelete($id)
{
return $this->memcache->delete($id);
// Memcache::delete() returns false if entry does not exist
return $this->memcache->delete($id) || ! $this->doContains($id);
}
/**

View file

@ -74,7 +74,19 @@ class MemcachedCache extends CacheProvider
*/
protected function doFetchMultiple(array $keys)
{
return $this->memcached->getMulti($keys);
return $this->memcached->getMulti($keys) ?: [];
}
/**
* {@inheritdoc}
*/
protected function doSaveMultiple(array $keysAndValues, $lifetime = 0)
{
if ($lifetime > 30 * 24 * 3600) {
$lifetime = time() + $lifetime;
}
return $this->memcached->setMulti($keysAndValues, $lifetime);
}
/**
@ -82,7 +94,8 @@ class MemcachedCache extends CacheProvider
*/
protected function doContains($id)
{
return (false !== $this->memcached->get($id));
return false !== $this->memcached->get($id)
|| $this->memcached->getResultCode() !== Memcached::RES_NOTFOUND;
}
/**
@ -101,7 +114,8 @@ class MemcachedCache extends CacheProvider
*/
protected function doDelete($id)
{
return $this->memcached->delete($id);
return $this->memcached->delete($id)
|| $this->memcached->getResultCode() === Memcached::RES_NOTFOUND;
}
/**

View file

@ -21,6 +21,7 @@ namespace Doctrine\Common\Cache;
use MongoBinData;
use MongoCollection;
use MongoCursorException;
use MongoDate;
/**
@ -41,7 +42,7 @@ class MongoDBCache extends CacheProvider
* cache entry should expire.
*
* With MongoDB 2.2+, entries can be automatically deleted by MongoDB by
* indexing this field wit the "expireAfterSeconds" option equal to zero.
* indexing this field with the "expireAfterSeconds" option equal to zero.
* This will direct MongoDB to regularly query for and delete any entries
* whose date is older than the current time. Entries without a date value
* in this field will be ignored.
@ -119,14 +120,18 @@ class MongoDBCache extends CacheProvider
*/
protected function doSave($id, $data, $lifeTime = 0)
{
$result = $this->collection->update(
array('_id' => $id),
array('$set' => array(
self::EXPIRATION_FIELD => ($lifeTime > 0 ? new MongoDate(time() + $lifeTime) : null),
self::DATA_FIELD => new MongoBinData(serialize($data), MongoBinData::BYTE_ARRAY),
)),
array('upsert' => true, 'multiple' => false)
);
try {
$result = $this->collection->update(
array('_id' => $id),
array('$set' => array(
self::EXPIRATION_FIELD => ($lifeTime > 0 ? new MongoDate(time() + $lifeTime) : null),
self::DATA_FIELD => new MongoBinData(serialize($data), MongoBinData::BYTE_ARRAY),
)),
array('upsert' => true, 'multiple' => false)
);
} catch (MongoCursorException $e) {
return false;
}
return isset($result['ok']) ? $result['ok'] == 1 : true;
}
@ -138,7 +143,7 @@ class MongoDBCache extends CacheProvider
{
$result = $this->collection->remove(array('_id' => $id));
return isset($result['n']) ? $result['n'] == 1 : true;
return isset($result['ok']) ? $result['ok'] == 1 : true;
}
/**
@ -170,8 +175,8 @@ class MongoDBCache extends CacheProvider
return array(
Cache::STATS_HITS => null,
Cache::STATS_MISSES => null,
Cache::STATS_UPTIME => (isset($serverStatus['uptime']) ? (integer) $serverStatus['uptime'] : null),
Cache::STATS_MEMORY_USAGE => (isset($collStats['size']) ? (integer) $collStats['size'] : null),
Cache::STATS_UPTIME => (isset($serverStatus['uptime']) ? (int) $serverStatus['uptime'] : null),
Cache::STATS_MEMORY_USAGE => (isset($collStats['size']) ? (int) $collStats['size'] : null),
Cache::STATS_MEMORY_AVAILABLE => null,
);
}
@ -180,7 +185,8 @@ class MongoDBCache extends CacheProvider
* Check if the document is expired.
*
* @param array $document
* @return boolean
*
* @return bool
*/
private function isExpired(array $document)
{

View file

@ -0,0 +1,41 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Cache;
/**
* Interface for cache drivers that allows to put many items at once.
*
* @link www.doctrine-project.org
* @since 1.6
* @author Daniel Gorgan <danut007ro@gmail.com>
*/
interface MultiPutCache
{
/**
* Returns a boolean value indicating if the operation succeeded.
*
* @param array $keysAndValues Array of keys and values to save in cache
* @param int $lifetime The lifetime. If != 0, sets a specific lifetime for these
* cache entries (0 => infinite lifeTime).
*
* @return bool TRUE if the operation was successful, FALSE if it wasn't.
*/
function saveMultiple(array $keysAndValues, $lifetime = 0);
}

View file

@ -2,7 +2,7 @@
namespace Doctrine\Common\Cache;
use Predis\Client;
use Predis\ClientInterface;
/**
* Predis cache provider.
@ -12,16 +12,16 @@ use Predis\Client;
class PredisCache extends CacheProvider
{
/**
* @var Client
* @var ClientInterface
*/
private $client;
/**
* @param Client $client
* @param ClientInterface $client
*
* @return void
*/
public function __construct(Client $client)
public function __construct(ClientInterface $client)
{
$this->client = $client;
}
@ -46,14 +46,43 @@ class PredisCache extends CacheProvider
{
$fetchedItems = call_user_func_array(array($this->client, 'mget'), $keys);
return array_filter(array_combine($keys, array_map('unserialize', $fetchedItems)));
return array_map('unserialize', array_filter(array_combine($keys, $fetchedItems)));
}
/**
* {@inheritdoc}
*/
protected function doSaveMultiple(array $keysAndValues, $lifetime = 0)
{
if ($lifetime) {
$success = true;
// Keys have lifetime, use SETEX for each of them
foreach ($keysAndValues as $key => $value) {
$response = $this->client->setex($key, $lifetime, serialize($value));
if ((string) $response != 'OK') {
$success = false;
}
}
return $success;
}
// No lifetime, use MSET
$response = $this->client->mset(array_map(function ($value) {
return serialize($value);
}, $keysAndValues));
return (string) $response == 'OK';
}
/**
* {@inheritdoc}
*/
protected function doContains($id)
{
return $this->client->exists($id);
return (bool) $this->client->exists($id);
}
/**
@ -76,7 +105,7 @@ class PredisCache extends CacheProvider
*/
protected function doDelete($id)
{
return $this->client->del($id) > 0;
return $this->client->del($id) >= 0;
}
/**

View file

@ -71,7 +71,40 @@ class RedisCache extends CacheProvider
*/
protected function doFetchMultiple(array $keys)
{
return array_filter(array_combine($keys, $this->redis->mget($keys)));
$fetchedItems = array_combine($keys, $this->redis->mget($keys));
// Redis mget returns false for keys that do not exist. So we need to filter those out unless it's the real data.
$foundItems = array();
foreach ($fetchedItems as $key => $value) {
if (false !== $value || $this->redis->exists($key)) {
$foundItems[$key] = $value;
}
}
return $foundItems;
}
/**
* {@inheritdoc}
*/
protected function doSaveMultiple(array $keysAndValues, $lifetime = 0)
{
if ($lifetime) {
$success = true;
// Keys have lifetime, use SETEX for each of them
foreach ($keysAndValues as $key => $value) {
if (!$this->redis->setex($key, $lifetime, $value)) {
$success = false;
}
}
return $success;
}
// No lifetime, use MSET
return (bool) $this->redis->mset($keysAndValues);
}
/**
@ -99,7 +132,7 @@ class RedisCache extends CacheProvider
*/
protected function doDelete($id)
{
return $this->redis->delete($id) > 0;
return $this->redis->delete($id) >= 0;
}
/**
@ -137,6 +170,11 @@ class RedisCache extends CacheProvider
if (defined('HHVM_VERSION')) {
return Redis::SERIALIZER_PHP;
}
return defined('Redis::SERIALIZER_IGBINARY') ? Redis::SERIALIZER_IGBINARY : Redis::SERIALIZER_PHP;
if (defined('Redis::SERIALIZER_IGBINARY') && extension_loaded('igbinary')) {
return Redis::SERIALIZER_IGBINARY;
}
return Redis::SERIALIZER_PHP;
}
}

View file

@ -202,7 +202,7 @@ class RiakCache extends CacheProvider
*
* @param \Riak\Object $object
*
* @return boolean
* @return bool
*/
private function isExpired(Object $object)
{

View file

@ -98,7 +98,7 @@ class SQLite3Cache extends CacheProvider
*/
protected function doContains($id)
{
return (boolean) $this->findById($id, false);
return null !== $this->findById($id, false);
}
/**
@ -157,7 +157,7 @@ class SQLite3Cache extends CacheProvider
* Find a single row by ID.
*
* @param mixed $id
* @param boolean $includeData
* @param bool $includeData
*
* @return array|null
*/
@ -208,7 +208,8 @@ class SQLite3Cache extends CacheProvider
* Check if the item is expired.
*
* @param array $item
* @return boolean
*
* @return bool
*/
private function isExpired(array $item)
{

View file

@ -21,5 +21,5 @@ namespace Doctrine\Common\Cache;
class Version
{
const VERSION = '1.4.0-DEV';
const VERSION = '1.6.1-DEV';
}

View file

@ -53,7 +53,7 @@ class WinCacheCache extends CacheProvider
*/
protected function doSave($id, $data, $lifeTime = 0)
{
return (bool) wincache_ucache_set($id, $data, (int) $lifeTime);
return wincache_ucache_set($id, $data, $lifeTime);
}
/**
@ -72,6 +72,24 @@ class WinCacheCache extends CacheProvider
return wincache_ucache_clear();
}
/**
* {@inheritdoc}
*/
protected function doFetchMultiple(array $keys)
{
return wincache_ucache_get($keys);
}
/**
* {@inheritdoc}
*/
protected function doSaveMultiple(array $keysAndValues, $lifetime = 0)
{
$result = wincache_ucache_set($keysAndValues, null, $lifetime);
return empty($result);
}
/**
* {@inheritdoc}
*/

View file

@ -1,16 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="./tests/Doctrine/Tests/TestInit.php"
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="vendor/autoload.php"
>
<php>
<ini name="error_reporting" value="-1" />
</php>
<testsuites>
<testsuite name="Doctrine Cache Test Suite">
<directory>./tests/Doctrine/</directory>

View file

@ -1 +0,0 @@
vendor/

View file

@ -1,21 +0,0 @@
language: php
php:
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm
- hhvm-nightly
matrix:
fast_finish: true
allow_failures:
- php: 7.0
before_script:
- composer --prefer-source install
script:
- phpunit

View file

@ -0,0 +1,67 @@
# Contribute to Doctrine
Thank you for contributing to Doctrine!
Before we can merge your Pull-Request here are some guidelines that you need to follow.
These guidelines exist not to annoy you, but to keep the code base clean,
unified and future proof.
## We only accept PRs to "master"
Our branching strategy is "everything to master first", even
bugfixes and we then merge them into the stable branches. You should only
open pull requests against the master branch. Otherwise we cannot accept the PR.
There is one exception to the rule, when we merged a bug into some stable branches
we do occasionally accept pull requests that merge the same bug fix into earlier
branches.
## Coding Standard
We use [doctrine coding standard](https://github.com/doctrine/coding-standard) which is PSR-1 and PSR-2:
* https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md
* https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md
with some exceptions/differences:
* Keep the nesting of control structures per method as small as possible
* Align equals (=) signs
* Add spaces between assignment, control and return statements
* Prefer early exit over nesting conditions
* Add spaces around a negation if condition ``if ( ! $cond)``
* Add legal information at the beginning of each source file
* Add ``@author`` [phpDoc](https://www.phpdoc.org/docs/latest/references/phpdoc/tags/author.html) comment at DockBlock of class/interface/trait that you create.
## Unit-Tests
Please try to add a test for your pull-request.
* If you want to contribute new functionality add unit- or functional tests
depending on the scope of the feature.
You can run the unit-tests by calling ``vendor/bin/phpunit`` from the root of the project.
It will run all the project tests.
In order to do that, you will need a fresh copy of doctrine/collections, and you
will have to run a composer installation in the project:
```sh
git clone git@github.com:doctrine/collections.git
cd collections
curl -sS https://getcomposer.org/installer | php --
./composer.phar install
```
## Travis
We automatically run your pull request through [Travis CI](https://www.travis-ci.org)
against supported PHP versions. If you break the tests, we cannot merge your code,
so please make sure that your code is working before opening up a Pull-Request.
## Getting merged
Please allow us time to review your pull requests. We will give our best to review
everything as fast as possible, but cannot always live up to our own expectations.
Thank you very much again for your contribution!

View file

@ -1,6 +1,8 @@
# Doctrine Collections
[![Build Status](https://travis-ci.org/doctrine/collections.svg?branch=master)](https://travis-ci.org/doctrine/collections)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/doctrine/collections/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/doctrine/collections/?branch=master)
[![Code Coverage](https://scrutinizer-ci.com/g/doctrine/collections/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/doctrine/collections/?branch=master)
Collections Abstraction library

View file

@ -13,17 +13,23 @@
{"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
],
"require": {
"php": ">=5.3.2"
"php": "^5.6 || ^7.0"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
"phpunit/phpunit": "^5.7",
"doctrine/coding-standard": "~0.1@dev"
},
"autoload": {
"psr-0": { "Doctrine\\Common\\Collections\\": "lib/" }
},
"autoload-dev": {
"psr-4": {
"Doctrine\\Tests\\": "tests/Doctrine/Tests"
}
},
"extra": {
"branch-alias": {
"dev-master": "1.2.x-dev"
"dev-master": "1.3.x-dev"
}
}
}

View file

@ -26,6 +26,11 @@ use Doctrine\Common\Collections\Expr\ClosureExpressionVisitor;
/**
* An ArrayCollection is a Collection implementation that wraps a regular PHP array.
*
* Warning: Using (un-)serialize() on a collection is not a supported use-case
* and may break when we change the internals in the future. If you need to
* serialize a collection use {@link toArray()} and reconstruct the collection
* manually.
*
* @since 2.0
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
@ -50,6 +55,21 @@ class ArrayCollection implements Collection, Selectable
$this->elements = $elements;
}
/**
* Creates a new instance from the specified elements.
*
* This method is provided for derived classes to specify how a new
* instance should be created when constructor semantics have changed.
*
* @param array $elements Elements.
*
* @return static
*/
protected function createFrom(array $elements)
{
return new static($elements);
}
/**
* {@inheritDoc}
*/
@ -254,9 +274,9 @@ class ArrayCollection implements Collection, Selectable
/**
* {@inheritDoc}
*/
public function add($value)
public function add($element)
{
$this->elements[] = $value;
$this->elements[] = $element;
return true;
}
@ -284,7 +304,7 @@ class ArrayCollection implements Collection, Selectable
*/
public function map(Closure $func)
{
return new static(array_map($func, $this->elements));
return $this->createFrom(array_map($func, $this->elements));
}
/**
@ -292,7 +312,7 @@ class ArrayCollection implements Collection, Selectable
*/
public function filter(Closure $p)
{
return new static(array_filter($this->elements, $p));
return $this->createFrom(array_filter($this->elements, $p));
}
/**
@ -324,7 +344,7 @@ class ArrayCollection implements Collection, Selectable
}
}
return array(new static($matches), new static($noMatches));
return array($this->createFrom($matches), $this->createFrom($noMatches));
}
/**
@ -368,8 +388,9 @@ class ArrayCollection implements Collection, Selectable
}
if ($orderings = $criteria->getOrderings()) {
$next = null;
foreach (array_reverse($orderings) as $field => $ordering) {
$next = ClosureExpressionVisitor::sortByField($field, $ordering == Criteria::DESC ? -1 : 1);
$next = ClosureExpressionVisitor::sortByField($field, $ordering == Criteria::DESC ? -1 : 1, $next);
}
uasort($filtered, $next);
@ -382,6 +403,6 @@ class ArrayCollection implements Collection, Selectable
$filtered = array_slice($filtered, (int)$offset, $length);
}
return new static($filtered);
return $this->createFrom($filtered);
}
}

View file

@ -69,6 +69,24 @@ class ClosureExpressionVisitor extends ExpressionVisitor
return $object[$field];
}
if (isset($object->$field)) {
return $object->$field;
}
// camelcase field name to support different variable naming conventions
$ccField = preg_replace_callback('/_(.?)/', function($matches) { return strtoupper($matches[1]); }, $field);
foreach ($accessors as $accessor) {
$accessor .= $ccField;
if ( ! method_exists($object, $accessor)) {
continue;
}
return $object->$accessor();
}
return $object->$field;
}
@ -155,6 +173,26 @@ class ClosureExpressionVisitor extends ExpressionVisitor
return false !== strpos(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value);
};
case Comparison::MEMBER_OF:
return function ($object) use ($field, $value) {
$fieldValues = ClosureExpressionVisitor::getObjectFieldValue($object, $field);
if (!is_array($fieldValues)) {
$fieldValues = iterator_to_array($fieldValues);
}
return in_array($value, $fieldValues);
};
case Comparison::STARTS_WITH:
return function ($object) use ($field, $value) {
return 0 === strpos(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value);
};
case Comparison::ENDS_WITH:
return function ($object) use ($field, $value) {
return $value === substr(ClosureExpressionVisitor::getObjectFieldValue($object, $field), -strlen($value));
};
default:
throw new \RuntimeException("Unknown comparison operator: " . $comparison->getOperator());
}

View file

@ -27,17 +27,19 @@ namespace Doctrine\Common\Collections\Expr;
*/
class Comparison implements Expression
{
const EQ = '=';
const NEQ = '<>';
const LT = '<';
const LTE = '<=';
const GT = '>';
const GTE = '>=';
const IS = '='; // no difference with EQ
const IN = 'IN';
const NIN = 'NIN';
const CONTAINS = 'CONTAINS';
const EQ = '=';
const NEQ = '<>';
const LT = '<';
const LTE = '<=';
const GT = '>';
const GTE = '>=';
const IS = '='; // no difference with EQ
const IN = 'IN';
const NIN = 'NIN';
const CONTAINS = 'CONTAINS';
const MEMBER_OF = 'MEMBER_OF';
const STARTS_WITH = 'STARTS_WITH';
const ENDS_WITH = 'ENDS_WITH';
/**
* @var string
*/

View file

@ -27,7 +27,7 @@ use Doctrine\Common\Collections\Expr\Value;
* Builder for Expressions in the {@link Selectable} interface.
*
* Important Notice for interoperable code: You have to use scalar
* values only for comparisons, otherwise the behavior of the comparision
* values only for comparisons, otherwise the behavior of the comparison
* may be different between implementations (Array vs ORM vs ODM).
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
@ -163,4 +163,38 @@ class ExpressionBuilder
{
return new Comparison($field, Comparison::CONTAINS, new Value($value));
}
/**
* @param string $field
* @param mixed $value
*
* @return Comparison
*/
public function memberOf ($field, $value)
{
return new Comparison($field, Comparison::MEMBER_OF, new Value($value));
}
/**
* @param string $field
* @param mixed $value
*
* @return Comparison
*/
public function startsWith($field, $value)
{
return new Comparison($field, Comparison::STARTS_WITH, new Value($value));
}
/**
* @param string $field
* @param mixed $value
*
* @return Comparison
*/
public function endsWith($field, $value)
{
return new Comparison($field, Comparison::ENDS_WITH, new Value($value));
}
}

View file

@ -1,31 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="./tests/Doctrine/Tests/TestInit.php"
>
<testsuites>
<testsuite name="Doctrine Collections Test Suite">
<directory>./tests/Doctrine/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./lib/Doctrine/</directory>
</whitelist>
</filter>
<groups>
<exclude>
<group>performance</group>
</exclude>
</groups>
</phpunit>

View file

@ -1,10 +0,0 @@
build/
logs/
reports/
dist/
tests/Doctrine/Tests/Common/Proxy/generated/
vendor/
.idea
composer.lock
doctrine-common-*.tar
doctrine-common-*.tar.gz

View file

@ -1,3 +0,0 @@
[submodule "lib/vendor/doctrine-build-common"]
path = lib/vendor/doctrine-build-common
url = git://github.com/doctrine/doctrine-build-common.git

View file

@ -1,26 +0,0 @@
language: php
sudo: false
cache:
directory:
- $HOME/.composer/cache
php:
- 5.3.3
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm
before_script:
- composer --prefer-source install
script:
- ./vendor/bin/phpunit
matrix:
allow_failures:
- php: 7.0

View file

@ -1,4 +1,4 @@
Copyright (c) 2006-2012 Doctrine Project
Copyright (c) 2006-2015 Doctrine Project
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in

View file

@ -1,3 +0,0 @@
# Version class and file
project.version_class = Doctrine\\Common\\Version
project.version_file = lib/Doctrine/Common/Version.php

View file

@ -1,101 +0,0 @@
<?xml version="1.0"?>
<project name="DoctrineCommon" default="build" basedir=".">
<property file="build.properties" />
<target name="php">
<exec executable="which" outputproperty="php_executable">
<arg value="php" />
</exec>
</target>
<target name="prepare">
<mkdir dir="build" />
</target>
<target name="build" depends="check-git-checkout-clean,prepare,php,composer">
<exec executable="${php_executable}">
<arg value="build/composer.phar" />
<arg value="archive" />
<arg value="--dir=build" />
</exec>
</target>
<target name="composer" depends="php,composer-check,composer-download">
<exec executable="${php_executable}">
<arg value="build/composer.phar" />
<arg value="install" />
</exec>
</target>
<target name="composer-check" depends="prepare">
<available file="build/composer.phar" property="composer.present"/>
</target>
<target name="composer-download" unless="composer.present">
<exec executable="wget">
<arg value="-Obuild/composer.phar" />
<arg value="http://getcomposer.org/composer.phar" />
</exec>
</target>
<target name="make-release" depends="check-git-checkout-clean,prepare,php">
<replace file="${project.version_file}" token="-DEV" value="" failOnNoReplacements="true" />
<exec executable="${php_executable}" outputproperty="doctrine.current_version" failonerror="true">
<arg value="-r" />
<arg value="require_once '${project.version_file}';echo ${project.version_class}::VERSION;" />
</exec>
<exec executable="${php_executable}" outputproperty="doctrine.next_version" failonerror="true">
<arg value="-r" />
<arg value="$parts = explode('.', str_ireplace(array('-DEV', '-ALPHA', '-BETA'), '', '${doctrine.current_version}'));
if (count($parts) != 3) {
throw new \InvalidArgumentException('Version is assumed in format x.y.z, ${doctrine.current_version} given');
}
$parts[2]++;
echo implode('.', $parts);
" />
</exec>
<git-commit file="${project.version_file}" message="Release ${doctrine.current_version}" />
<git-tag version="${doctrine.current_version}" />
<replace file="${project.version_file}" token="${doctrine.current_version}" value="${doctrine.next_version}-DEV" />
<git-commit file="${project.version_file}" message="Bump version to ${doctrine.next_version}" />
</target>
<target name="check-git-checkout-clean">
<exec executable="git" failonerror="true">
<arg value="diff-index" />
<arg value="--quiet" />
<arg value="HEAD" />
</exec>
</target>
<macrodef name="git-commit">
<attribute name="file" default="NOT SET"/>
<attribute name="message" default="NOT SET"/>
<sequential>
<exec executable="git">
<arg value="add" />
<arg value="@{file}" />
</exec>
<exec executable="git">
<arg value="commit" />
<arg value="-m" />
<arg value="@{message}" />
</exec>
</sequential>
</macrodef>
<macrodef name="git-tag">
<attribute name="version" default="NOT SET" />
<sequential>
<exec executable="git">
<arg value="tag" />
<arg value="-m" />
<arg value="v@{version}" />
<arg value="v@{version}" />
</exec>
</sequential>
</macrodef>
</project>

View file

@ -13,26 +13,24 @@
{"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
],
"require": {
"php": ">=5.3.2",
"php": "~5.6|~7.0",
"doctrine/inflector": "1.*",
"doctrine/cache": "1.*",
"doctrine/collections": "1.*",
"doctrine/lexer": "1.*",
"doctrine/annotations": "1.*"
},
"minimum-stability": "dev",
"require-dev": {
"phpunit/phpunit": "~3.7"
"phpunit/phpunit": "^5.4.6"
},
"autoload": {
"psr-0": { "Doctrine\\Common\\": "lib/" }
"psr-4": {
"Doctrine\\Common\\": "lib/Doctrine/Common"
}
},
"extra": {
"branch-alias": {
"dev-master": "2.6.x-dev"
"dev-master": "2.7.x-dev"
}
},
"archive": {
"exclude": ["!vendor", "tests", "*phpunit.xml", ".travis.yml", "build.xml", "build.properties", "composer.phar"]
}
}

View file

@ -0,0 +1,11 @@
{
"source": {
"directories": [
"lib\/Doctrine"
]
},
"timeout": 10,
"logs": {
"text": "reports/humbuglog.txt"
}
}

View file

@ -153,7 +153,7 @@ class ClassLoader
*/
public function register()
{
spl_autoload_register(array($this, 'loadClass'));
spl_autoload_register([$this, 'loadClass']);
}
/**
@ -163,7 +163,7 @@ class ClassLoader
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
spl_autoload_unregister([$this, 'loadClass']);
}
/**
@ -275,6 +275,6 @@ class ClassLoader
{
return class_exists($type, $autoload)
|| interface_exists($type, $autoload)
|| (function_exists('trait_exists') && trait_exists($type, $autoload));
|| trait_exists($type, $autoload);
}
}

View file

@ -38,7 +38,7 @@ class EventManager
*
* @var array
*/
private $_listeners = array();
private $_listeners = [];
/**
* Dispatches an event to all registered listeners.

View file

@ -141,7 +141,7 @@ abstract class AbstractManagerRegistry implements ManagerRegistry
*/
public function getConnections()
{
$connections = array();
$connections = [];
foreach ($this->connections as $name => $id) {
$connections[$name] = $this->getService($id);
}
@ -195,8 +195,13 @@ abstract class AbstractManagerRegistry implements ManagerRegistry
}
$proxyClass = new \ReflectionClass($class);
if ($proxyClass->implementsInterface($this->proxyInterfaceName)) {
$class = $proxyClass->getParentClass()->getName();
if (! $parentClass = $proxyClass->getParentClass()) {
return null;
}
$class = $parentClass->getName();
}
foreach ($this->managers as $id) {
@ -221,7 +226,7 @@ abstract class AbstractManagerRegistry implements ManagerRegistry
*/
public function getManagers()
{
$dms = array();
$dms = [];
foreach ($this->managers as $name => $id) {
$dms[$name] = $this->getService($id);
}
@ -253,5 +258,7 @@ abstract class AbstractManagerRegistry implements ManagerRegistry
// force the creation of a new document manager
// if the current one is closed
$this->resetService($this->managers[$name]);
return $this->getManager($name);
}
}

View file

@ -53,7 +53,7 @@ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
/**
* @var ClassMetadata[]
*/
private $loadedMetadata = array();
private $loadedMetadata = [];
/**
* @var bool
@ -110,7 +110,7 @@ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
}
$driver = $this->getDriver();
$metadata = array();
$metadata = [];
foreach ($driver->getAllClassNames() as $className) {
$metadata[] = $this->getMetadataFor($className);
}
@ -208,7 +208,7 @@ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
try {
if ($this->cacheDriver) {
if (($cached = $this->cacheDriver->fetch($realClassName . $this->cacheSalt)) !== false) {
if (($cached = $this->cacheDriver->fetch($realClassName . $this->cacheSalt)) instanceof ClassMetadata) {
$this->loadedMetadata[$realClassName] = $cached;
$this->wakeupReflection($cached, $this->getReflectionService());
@ -277,7 +277,7 @@ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
protected function getParentClasses($name)
{
// Collect parent classes, ignoring transient (not-mapped) classes.
$parentClasses = array();
$parentClasses = [];
foreach (array_reverse($this->getReflectionService()->getParentClasses($name)) as $parentClass) {
if ( ! $this->getDriver()->isTransient($parentClass)) {
$parentClasses[] = $parentClass;
@ -306,7 +306,7 @@ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
$this->initialize();
}
$loaded = array();
$loaded = [];
$parentClasses = $this->getParentClasses($name);
$parentClasses[] = $name;
@ -314,7 +314,7 @@ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
// Move down the hierarchy of parent classes, starting from the topmost class
$parent = null;
$rootEntityFound = false;
$visited = array();
$visited = [];
$reflService = $this->getReflectionService();
foreach ($parentClasses as $className) {
if (isset($this->loadedMetadata[$className])) {

View file

@ -45,14 +45,14 @@ abstract class AnnotationDriver implements MappingDriver
*
* @var array
*/
protected $paths = array();
protected $paths = [];
/**
* The paths excluded from path where to look for mapping files.
*
* @var array
*/
protected $excludePaths = array();
protected $excludePaths = [];
/**
* The file extension of mapping documents.
@ -73,7 +73,7 @@ abstract class AnnotationDriver implements MappingDriver
*
* @var array
*/
protected $entityAnnotationClasses = array();
protected $entityAnnotationClasses = [];
/**
* Initializes a new AnnotationDriver that uses the given AnnotationReader for reading
@ -200,8 +200,8 @@ abstract class AnnotationDriver implements MappingDriver
throw MappingException::pathRequired();
}
$classes = array();
$includedFiles = array();
$classes = [];
$includedFiles = [];
foreach ($this->paths as $path) {
if ( ! is_dir($path)) {

View file

@ -37,7 +37,7 @@ class DefaultFileLocator implements FileLocator
*
* @var array
*/
protected $paths = array();
protected $paths = [];
/**
* The file extension of mapping documents.
@ -125,7 +125,7 @@ class DefaultFileLocator implements FileLocator
*/
public function getAllClassNames($globalBasename)
{
$classes = array();
$classes = [];
if ($this->paths) {
foreach ($this->paths as $path) {

View file

@ -117,6 +117,8 @@ abstract class FileDriver implements MappingDriver
throw MappingException::invalidMappingFile($className, str_replace('\\', '.', $className) . $this->locator->getFileExtension());
}
$this->classCache[$className] = $result[$className];
return $result[$className];
}
@ -145,11 +147,14 @@ abstract class FileDriver implements MappingDriver
$this->initialize();
}
$classNames = (array)$this->locator->getAllClassNames($this->globalBasename);
if ($this->classCache) {
$classNames = array_merge(array_keys($this->classCache), $classNames);
if (! $this->classCache) {
return (array) $this->locator->getAllClassNames($this->globalBasename);
}
return $classNames;
return array_merge(
array_keys($this->classCache),
(array) $this->locator->getAllClassNames($this->globalBasename)
);
}
/**
@ -175,7 +180,7 @@ abstract class FileDriver implements MappingDriver
*/
protected function initialize()
{
$this->classCache = array();
$this->classCache = [];
if (null !== $this->globalBasename) {
foreach ($this->locator->getPaths() as $path) {
$file = $path.'/'.$this->globalBasename.$this->locator->getFileExtension();

View file

@ -39,12 +39,12 @@ class MappingDriverChain implements MappingDriver
*
* @var MappingDriver|null
*/
private $defaultDriver = null;
private $defaultDriver;
/**
* @var array
*/
private $drivers = array();
private $drivers = [];
/**
* Gets the default driver.
@ -117,8 +117,8 @@ class MappingDriverChain implements MappingDriver
*/
public function getAllClassNames()
{
$classNames = array();
$driverClasses = array();
$classNames = [];
$driverClasses = [];
/* @var $driver MappingDriver */
foreach ($this->drivers AS $namespace => $driver) {

View file

@ -44,8 +44,7 @@ class PHPDriver extends FileDriver
*/
public function __construct($locator, $fileExtension = null)
{
$fileExtension = ".php";
parent::__construct($locator, $fileExtension);
parent::__construct($locator, '.php');
}
/**
@ -54,6 +53,7 @@ class PHPDriver extends FileDriver
public function loadMetadataForClass($className, ClassMetadata $metadata)
{
$this->metadata = $metadata;
$this->loadMappingFile($this->locator->findMappingFile($className));
}
@ -65,6 +65,6 @@ class PHPDriver extends FileDriver
$metadata = $this->metadata;
include $file;
return array($metadata->getName() => $metadata);
return [$metadata->getName() => $metadata];
}
}

View file

@ -40,7 +40,7 @@ class StaticPHPDriver implements MappingDriver
*
* @var array
*/
private $paths = array();
private $paths = [];
/**
* Map of all class names.
@ -93,8 +93,8 @@ class StaticPHPDriver implements MappingDriver
throw MappingException::pathRequired();
}
$classes = array();
$includedFiles = array();
$classes = [];
$includedFiles = [];
foreach ($this->paths as $path) {
if (!is_dir($path)) {

View file

@ -37,14 +37,14 @@ class SymfonyFileLocator implements FileLocator
*
* @var array
*/
protected $paths = array();
protected $paths = [];
/**
* A map of mapping directory path to namespace prefix used to expand class shortnames.
*
* @var array
*/
protected $prefixes = array();
protected $prefixes = [];
/**
* File extension that is searched for.
@ -153,7 +153,9 @@ class SymfonyFileLocator implements FileLocator
}
$filename = $path.'/'.strtr(substr($className, strlen($prefix)+1), '\\', $this->nsSeparator).$this->fileExtension;
return is_file($filename);
if (is_file($filename)) {
return true;
}
}
return false;
@ -164,7 +166,7 @@ class SymfonyFileLocator implements FileLocator
*/
public function getAllClassNames($globalBasename = null)
{
$classes = array();
$classes = [];
if ($this->paths) {
foreach ((array) $this->paths as $path) {
@ -194,7 +196,7 @@ class SymfonyFileLocator implements FileLocator
'\\'
);
$classes[] = $this->prefixes[$path] . $nsSuffix . '\\' .str_replace($this->nsSeparator, '\\', $fileName);
$classes[] = $this->prefixes[$path] . str_replace(DIRECTORY_SEPARATOR, '\\', $nsSuffix) . '\\' .str_replace($this->nsSeparator, '\\', $fileName);
} else {
$classes[] = str_replace($this->nsSeparator, '\\', $fileName);
}
@ -230,8 +232,6 @@ class SymfonyFileLocator implements FileLocator
if (is_file($filename)) {
return $filename;
}
throw MappingException::mappingFileNotFound($className, $filename);
}
throw MappingException::mappingFileNotFound($className, substr($className, strrpos($className, '\\') + 1).$this->fileExtension);

View file

@ -31,7 +31,7 @@ class StaticReflectionService implements ReflectionService
*/
public function getParentClasses($class)
{
return array();
return [];
}
/**

View file

@ -34,7 +34,7 @@ interface ObjectRepository
*
* @param mixed $id The identifier.
*
* @return object The object.
* @return object|null The object.
*/
public function find($id);
@ -68,7 +68,7 @@ interface ObjectRepository
*
* @param array $criteria The criteria.
*
* @return object The object.
* @return object|null The object.
*/
public function findOneBy(array $criteria);

View file

@ -198,7 +198,7 @@ abstract class PersistentObject implements ObjectManagerAware
throw new \InvalidArgumentException("Expected persistent object of type '".$targetClass."'");
}
if (!($this->$field instanceof Collection)) {
$this->$field = new ArrayCollection($this->$field ?: array());
$this->$field = new ArrayCollection($this->$field ?: []);
}
$this->$field->add($args[0]);
$this->completeOwningSide($field, $targetClass, $args[0]);

View file

@ -87,7 +87,7 @@ abstract class AbstractProxyFactory
/**
* @var \Doctrine\Common\Proxy\ProxyDefinition[]
*/
private $definitions = array();
private $definitions = [];
/**
* @param \Doctrine\Common\Proxy\ProxyGenerator $proxyGenerator

View file

@ -49,9 +49,13 @@ class Autoloader
throw InvalidArgumentException::notProxyClass($className, $proxyNamespace);
}
$className = str_replace('\\', '', substr($className, strlen($proxyNamespace) + 1));
// remove proxy namespace from class name
$classNameRelativeToProxyNamespace = substr($className, strlen($proxyNamespace));
return $proxyDir . DIRECTORY_SEPARATOR . $className . '.php';
// remove namespace separators from remaining class name
$fileName = str_replace('\\', '', $classNameRelativeToProxyNamespace);
return $proxyDir . DIRECTORY_SEPARATOR . $fileName . '.php';
}
/**

View file

@ -41,15 +41,19 @@ class UnexpectedValueException extends BaseUnexpectedValueException implements P
}
/**
* @param string $className
* @param string $methodName
* @param string $parameterName
* @param \Exception $previous
* @param string $className
* @param string $methodName
* @param string $parameterName
* @param \Exception|null $previous
*
* @return self
*/
public static function invalidParameterTypeHint($className, $methodName, $parameterName, \Exception $previous)
{
public static function invalidParameterTypeHint(
$className,
$methodName,
$parameterName,
\Exception $previous = null
) {
return new self(
sprintf(
'The type hint of parameter "%s" in method "%s" in class "%s" is invalid.',
@ -61,4 +65,24 @@ class UnexpectedValueException extends BaseUnexpectedValueException implements P
$previous
);
}
/**
* @param $className
* @param $methodName
* @param \Exception|null $previous
*
* @return self
*/
public static function invalidReturnTypeHint($className, $methodName, \Exception $previous = null)
{
return new self(
sprintf(
'The return type of method "%s" in class "%s" is invalid.',
$methodName,
$className
),
0,
$previous
);
}
}

View file

@ -37,7 +37,7 @@ class ProxyGenerator
* Used to match very simple id methods that don't need
* to be decorated since the identifier is known.
*/
const PATTERN_MATCH_ID_METHOD = '((public\s+)?(function\s+%s\s*\(\)\s*)\s*{\s*return\s*\$this->%s;\s*})i';
const PATTERN_MATCH_ID_METHOD = '((public\s+)?(function\s+%s\s*\(\)\s*)\s*(?::\s*\??\s*\\\\?[a-z_\x7f-\xff][\w\x7f-\xff]*(?:\\\\[a-z_\x7f-\xff][\w\x7f-\xff]*)*\s*)?{\s*return\s*\$this->%s;\s*})i';
/**
* The namespace that contains all proxy classes.
@ -58,10 +58,10 @@ class ProxyGenerator
*
* @var string[]|callable[]
*/
protected $placeholders = array(
'baseProxyInterface' => 'Doctrine\Common\Proxy\Proxy',
protected $placeholders = [
'baseProxyInterface' => Proxy::class,
'additionalProperties' => '',
);
];
/**
* Template used as a blueprint to generate proxies.
@ -106,7 +106,7 @@ class <proxyShortClassName> extends \<className> implements \<baseProxyInterface
*
* @see \Doctrine\Common\Persistence\Proxy::__getLazyProperties
*/
public static $lazyPropertiesDefaults = array(<lazyPropertiesDefaults>);
public static $lazyPropertiesDefaults = [<lazyPropertiesDefaults>];
<additionalProperties>
@ -129,7 +129,7 @@ class <proxyShortClassName> extends \<className> implements \<baseProxyInterface
*/
public function __load()
{
$this->__initializer__ && $this->__initializer__->__invoke($this, \'__load\', array());
$this->__initializer__ && $this->__initializer__->__invoke($this, \'__load\', []);
}
/**
@ -263,12 +263,12 @@ class <proxyShortClassName> extends \<className> implements \<baseProxyInterface
preg_match_all('(<([a-zA-Z]+)>)', $this->proxyClassTemplate, $placeholderMatches);
$placeholderMatches = array_combine($placeholderMatches[0], $placeholderMatches[1]);
$placeholders = array();
$placeholders = [];
foreach ($placeholderMatches as $placeholder => $name) {
$placeholders[$placeholder] = isset($this->placeholders[$name])
? $this->placeholders[$name]
: array($this, 'generate' . $name);
: [$this, 'generate' . $name];
}
foreach ($placeholders as & $placeholder) {
@ -302,7 +302,7 @@ class <proxyShortClassName> extends \<className> implements \<baseProxyInterface
$tmpFileName = $fileName . '.' . uniqid('', true);
file_put_contents($tmpFileName, $proxyCode);
chmod($tmpFileName, 0664);
@chmod($tmpFileName, 0664);
rename($tmpFileName, $fileName);
}
@ -358,7 +358,7 @@ class <proxyShortClassName> extends \<className> implements \<baseProxyInterface
private function generateLazyPropertiesDefaults(ClassMetadata $class)
{
$lazyPublicProperties = $this->getLazyLoadedPublicProperties($class);
$values = array();
$values = [];
foreach ($lazyPublicProperties as $key => $value) {
$values[] = var_export($key, true) . ' => ' . var_export($value, true);
@ -385,7 +385,7 @@ class <proxyShortClassName> extends \<className> implements \<baseProxyInterface
{
EOT;
$toUnset = array();
$toUnset = [];
foreach ($this->getLazyLoadedPublicProperties($class) as $lazyPublicProperty => $unused) {
$toUnset[] = '$this->' . $lazyPublicProperty;
@ -443,7 +443,7 @@ EOT;
if ( ! empty($lazyPublicProperties)) {
$magicGet .= <<<'EOT'
if (array_key_exists($name, $this->__getLazyProperties())) {
$this->__initializer__ && $this->__initializer__->__invoke($this, '__get', array($name));
$this->__initializer__ && $this->__initializer__->__invoke($this, '__get', [$name]);
return $this->$name;
}
@ -454,7 +454,7 @@ EOT;
if ($hasParentGet) {
$magicGet .= <<<'EOT'
$this->__initializer__ && $this->__initializer__->__invoke($this, '__get', array($name));
$this->__initializer__ && $this->__initializer__->__invoke($this, '__get', [$name]);
return parent::__get($name);
@ -502,7 +502,7 @@ EOT;
if ( ! empty($lazyPublicProperties)) {
$magicSet .= <<<'EOT'
if (array_key_exists($name, $this->__getLazyProperties())) {
$this->__initializer__ && $this->__initializer__->__invoke($this, '__set', array($name, $value));
$this->__initializer__ && $this->__initializer__->__invoke($this, '__set', [$name, $value]);
$this->$name = $value;
@ -515,7 +515,7 @@ EOT;
if ($hasParentSet) {
$magicSet .= <<<'EOT'
$this->__initializer__ && $this->__initializer__->__invoke($this, '__set', array($name, $value));
$this->__initializer__ && $this->__initializer__->__invoke($this, '__set', [$name, $value]);
return parent::__set($name, $value);
EOT;
@ -559,7 +559,7 @@ EOT;
if ( ! empty($lazyPublicProperties)) {
$magicIsset .= <<<'EOT'
if (array_key_exists($name, $this->__getLazyProperties())) {
$this->__initializer__ && $this->__initializer__->__invoke($this, '__isset', array($name));
$this->__initializer__ && $this->__initializer__->__invoke($this, '__isset', [$name]);
return isset($this->$name);
}
@ -570,7 +570,7 @@ EOT;
if ($hasParentIsset) {
$magicIsset .= <<<'EOT'
$this->__initializer__ && $this->__initializer__->__invoke($this, '__isset', array($name));
$this->__initializer__ && $this->__initializer__->__invoke($this, '__isset', [$name]);
return parent::__isset($name);
@ -605,7 +605,7 @@ EOT;
if ($hasParentSleep) {
return $sleepImpl . <<<'EOT'
$properties = array_merge(array('__isInitialized__'), parent::__sleep());
$properties = array_merge(['__isInitialized__'], parent::__sleep());
if ($this->__isInitialized__) {
$properties = array_diff($properties, array_keys($this->__getLazyProperties()));
@ -616,7 +616,7 @@ EOT;
EOT;
}
$allProperties = array('__isInitialized__');
$allProperties = ['__isInitialized__'];
/* @var $prop \ReflectionProperty */
foreach ($class->getReflectionClass()->getProperties() as $prop) {
@ -645,10 +645,10 @@ EOT;
return $sleepImpl . <<<EOT
if (\$this->__isInitialized__) {
return array($allProperties);
return [$allProperties];
}
return array($protectedProperties);
return [$protectedProperties];
}
EOT;
}
@ -662,7 +662,7 @@ EOT;
*/
private function generateWakeupImpl(ClassMetadata $class)
{
$unsetPublicProperties = array();
$unsetPublicProperties = [];
$hasWakeup = $class->getReflectionClass()->hasMethod('__wakeup');
foreach (array_keys($this->getLazyLoadedPublicProperties($class)) as $lazyPublicProperty) {
@ -727,7 +727,7 @@ EOT;
*/
public function __clone()
{
\$this->__cloner__ && \$this->__cloner__->__invoke(\$this, '__clone', array());
\$this->__cloner__ && \$this->__cloner__->__invoke(\$this, '__clone', []);
$callParentClone }
EOT;
}
@ -742,16 +742,16 @@ EOT;
private function generateMethods(ClassMetadata $class)
{
$methods = '';
$methodNames = array();
$methodNames = [];
$reflectionMethods = $class->getReflectionClass()->getMethods(\ReflectionMethod::IS_PUBLIC);
$skippedMethods = array(
$skippedMethods = [
'__sleep' => true,
'__clone' => true,
'__wakeup' => true,
'__get' => true,
'__set' => true,
'__isset' => true,
);
];
foreach ($reflectionMethods as $method) {
$name = $method->getName();
@ -778,15 +778,18 @@ EOT;
}
$methods .= $name . '(' . $this->buildParametersString($class, $method, $method->getParameters()) . ')';
$methods .= $this->getMethodReturnType($method);
$methods .= "\n" . ' {' . "\n";
if ($this->isShortIdentifierGetter($method, $class)) {
$identifier = lcfirst(substr($name, 3));
$fieldType = $class->getTypeOfField($identifier);
$cast = in_array($fieldType, array('integer', 'smallint')) ? '(int) ' : '';
$cast = in_array($fieldType, ['integer', 'smallint']) ? '(int) ' : '';
$methods .= ' if ($this->__isInitialized__ === false) {' . "\n";
$methods .= ' return ' . $cast . ' parent::' . $method->getName() . "();\n";
$methods .= ' ';
$methods .= $this->shouldProxiedMethodReturn($method) ? 'return ' : '';
$methods .= $cast . ' parent::' . $method->getName() . "();\n";
$methods .= ' }' . "\n\n";
}
@ -795,8 +798,10 @@ EOT;
$methods .= "\n \$this->__initializer__ "
. "&& \$this->__initializer__->__invoke(\$this, " . var_export($name, true)
. ", array(" . $invokeParamsString . "));"
. "\n\n return parent::" . $name . '(' . $callParamsString . ');'
. ", [" . $invokeParamsString . "]);"
. "\n\n "
. ($this->shouldProxiedMethodReturn($method) ? 'return ' : '')
. "parent::" . $name . '(' . $callParamsString . ');'
. "\n" . ' }' . "\n";
}
@ -872,7 +877,7 @@ EOT;
private function getLazyLoadedPublicProperties(ClassMetadata $class)
{
$defaultProperties = $class->getReflectionClass()->getDefaultProperties();
$properties = array();
$properties = [];
foreach ($class->getReflectionClass()->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
$name = $property->getName();
@ -894,7 +899,7 @@ EOT;
*/
private function buildParametersString(ClassMetadata $class, \ReflectionMethod $method, array $parameters)
{
$parameterDefinitions = array();
$parameterDefinitions = [];
/* @var $param \ReflectionParameter */
foreach ($parameters as $param) {
@ -908,10 +913,8 @@ EOT;
$parameterDefinition .= '&';
}
if (method_exists($param, 'isVariadic')) {
if ($param->isVariadic()) {
$parameterDefinition .= '...';
}
if (method_exists($param, 'isVariadic') && $param->isVariadic()) {
$parameterDefinition .= '...';
}
$parameters[] = '$' . $param->getName();
@ -936,13 +939,20 @@ EOT;
*/
private function getParameterType(ClassMetadata $class, \ReflectionMethod $method, \ReflectionParameter $parameter)
{
if (method_exists($parameter, 'hasType')) {
if ( ! $parameter->hasType()) {
return '';
}
// We need to pick the type hint class too
return $this->formatType($parameter->getType(), $parameter->getDeclaringFunction(), $parameter);
}
// For PHP 5.x, we need to pick the type hint in the old way (to be removed for PHP 7.0+)
if ($parameter->isArray()) {
return 'array';
}
if (method_exists($parameter, 'isCallable') && $parameter->isCallable()) {
if ($parameter->isCallable()) {
return 'callable';
}
@ -990,10 +1000,8 @@ EOT;
function (\ReflectionParameter $parameter) {
$name = '';
if (method_exists($parameter, 'isVariadic')) {
if ($parameter->isVariadic()) {
$name .= '...';
}
if (method_exists($parameter, 'isVariadic') && $parameter->isVariadic()) {
$name .= '...';
}
$name .= '$' . $parameter->getName();
@ -1003,4 +1011,83 @@ EOT;
$parameters
);
}
/**
* @Param \ReflectionMethod $method
*
* @return string
*/
private function getMethodReturnType(\ReflectionMethod $method)
{
if ( ! method_exists($method, 'hasReturnType') || ! $method->hasReturnType()) {
return '';
}
return ': ' . $this->formatType($method->getReturnType(), $method);
}
/**
* @param \ReflectionMethod $method
*
* @return bool
*/
private function shouldProxiedMethodReturn(\ReflectionMethod $method)
{
if ( ! method_exists($method, 'hasReturnType') || ! $method->hasReturnType()) {
return true;
}
return 'void' !== strtolower($this->formatType($method->getReturnType(), $method));
}
/**
* @param \ReflectionType $type
* @param \ReflectionMethod $method
* @param \ReflectionParameter|null $parameter
*
* @return string
*/
private function formatType(
\ReflectionType $type,
\ReflectionMethod $method,
\ReflectionParameter $parameter = null
) {
$name = method_exists($type, 'getName') ? $type->getName() : (string) $type;
$nameLower = strtolower($name);
if ('self' === $nameLower) {
$name = $method->getDeclaringClass()->getName();
}
if ('parent' === $nameLower) {
$name = $method->getDeclaringClass()->getParentClass()->getName();
}
if ( ! $type->isBuiltin() && ! class_exists($name) && ! interface_exists($name)) {
if (null !== $parameter) {
throw UnexpectedValueException::invalidParameterTypeHint(
$method->getDeclaringClass()->getName(),
$method->getName(),
$parameter->getName()
);
}
throw UnexpectedValueException::invalidReturnTypeHint(
$method->getDeclaringClass()->getName(),
$method->getName()
);
}
if ( ! $type->isBuiltin()) {
$name = '\\' . $name;
}
if ($type->allowsNull()
&& (null === $parameter || ! $parameter->isDefaultValueAvailable() || null !== $parameter->getDefaultValue())
) {
$name = '?' . $name;
}
return $name;
}
}

View file

@ -402,7 +402,7 @@ class StaticReflectionClass extends ReflectionClass
/**
* {@inheritDoc}
*/
public function newInstanceArgs(array $args = array())
public function newInstanceArgs(array $args = [])
{
throw new ReflectionException('Method not implemented');
}

View file

@ -69,18 +69,18 @@ class StaticReflectionParser implements ReflectionProviderInterface
*
* @var array
*/
protected $useStatements = array();
protected $useStatements = [];
/**
* The docComment of the class.
*
* @var string
*/
protected $docComment = array(
protected $docComment = [
'class' => '',
'property' => array(),
'method' => array()
);
'property' => [],
'method' => []
];
/**
* The name of the class this class extends, if any.

View file

@ -91,7 +91,7 @@ class StaticReflectionProperty extends ReflectionProperty
/**
* {@inheritDoc}
*/
public static function export ($class, $name, $return = false)
public static function export($class, $name, $return = false)
{
throw new ReflectionException('Method not implemented');
}
@ -155,7 +155,7 @@ class StaticReflectionProperty extends ReflectionProperty
/**
* {@inheritDoc}
*/
public function setAccessible ($accessible)
public function setAccessible($accessible)
{
throw new ReflectionException('Method not implemented');
}
@ -163,7 +163,7 @@ class StaticReflectionProperty extends ReflectionProperty
/**
* {@inheritDoc}
*/
public function setValue ($object, $value = null)
public function setValue($object, $value = null)
{
throw new ReflectionException('Method not implemented');
}

View file

@ -19,6 +19,7 @@
namespace Doctrine\Common\Util;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Persistence\Proxy;
/**
@ -64,7 +65,7 @@ final class Debug
ini_set('xdebug.var_display_max_depth', $maxDepth);
}
$var = self::export($var, $maxDepth++);
$var = self::export($var, $maxDepth);
ob_start();
var_dump($var);
@ -76,11 +77,11 @@ final class Debug
$dumpText = ($stripTags ? strip_tags(html_entity_decode($dump)) : $dump);
ini_set('html_errors', $html);
if ($echo) {
echo $dumpText;
}
return $dumpText;
}
@ -95,51 +96,90 @@ final class Debug
$return = null;
$isObj = is_object($var);
if ($isObj && in_array('Doctrine\Common\Collections\Collection', class_implements($var))) {
if ($var instanceof Collection) {
$var = $var->toArray();
}
if ($maxDepth) {
if (is_array($var)) {
$return = array();
foreach ($var as $k => $v) {
$return[$k] = self::export($v, $maxDepth - 1);
}
} else if ($isObj) {
$return = new \stdclass();
if ($var instanceof \DateTime) {
$return->__CLASS__ = "DateTime";
$return->date = $var->format('c');
$return->timezone = $var->getTimeZone()->getName();
} else {
$reflClass = ClassUtils::newReflectionObject($var);
$return->__CLASS__ = ClassUtils::getClass($var);
if ($var instanceof Proxy) {
$return->__IS_PROXY__ = true;
$return->__PROXY_INITIALIZED__ = $var->__isInitialized();
}
if ($var instanceof \ArrayObject || $var instanceof \ArrayIterator) {
$return->__STORAGE__ = self::export($var->getArrayCopy(), $maxDepth - 1);
}
foreach ($reflClass->getProperties() as $reflProperty) {
$name = $reflProperty->getName();
$reflProperty->setAccessible(true);
$return->$name = self::export($reflProperty->getValue($var), $maxDepth - 1);
}
}
} else {
$return = $var;
}
} else {
$return = is_object($var) ? get_class($var)
if (! $maxDepth) {
return is_object($var) ? get_class($var)
: (is_array($var) ? 'Array(' . count($var) . ')' : $var);
}
if (is_array($var)) {
$return = [];
foreach ($var as $k => $v) {
$return[$k] = self::export($v, $maxDepth - 1);
}
return $return;
}
if (! $isObj) {
return $var;
}
$return = new \stdclass();
if ($var instanceof \DateTimeInterface) {
$return->__CLASS__ = get_class($var);
$return->date = $var->format('c');
$return->timezone = $var->getTimezone()->getName();
return $return;
}
$return->__CLASS__ = ClassUtils::getClass($var);
if ($var instanceof Proxy) {
$return->__IS_PROXY__ = true;
$return->__PROXY_INITIALIZED__ = $var->__isInitialized();
}
if ($var instanceof \ArrayObject || $var instanceof \ArrayIterator) {
$return->__STORAGE__ = self::export($var->getArrayCopy(), $maxDepth - 1);
}
return self::fillReturnWithClassAttributes($var, $return, $maxDepth);
}
/**
* Fill the $return variable with class attributes
*
* @param object $var
* @param stdClass $return
* @param int $maxDepth
*
* @return mixed
*/
private static function fillReturnWithClassAttributes($var, \stdClass $return, $maxDepth)
{
$reflClass = ClassUtils::newReflectionObject($var);
$parsedAttributes = array();
do {
$currentClassName = $reflClass->getName();
foreach ($reflClass->getProperties() as $reflProperty) {
$attributeKey = $reflProperty->isPrivate() ? $currentClassName . '#' : '';
$attributeKey .= $reflProperty->getName();
if (isset($parsedAttributes[$attributeKey])) {
continue;
}
$parsedAttributes[$attributeKey] = true;
$name =
$reflProperty->getName()
. ($return->__CLASS__ !== $currentClassName || $reflProperty->isPrivate() ? ':' . $currentClassName : '')
. ($reflProperty->isPrivate() ? ':private' : '')
. ($reflProperty->isProtected() ? ':protected' : '')
;
$reflProperty->setAccessible(true);
$return->$name = self::export($reflProperty->getValue($var), $maxDepth - 1);
}
} while ($reflClass = $reflClass->getParentClass());
return $return;
}

View file

@ -34,7 +34,7 @@ class Version
/**
* Current Doctrine Version.
*/
const VERSION = '2.6.0-DEV';
const VERSION = '2.7.0-DEV';
/**
* Compares a Doctrine version with the current one.

View file

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="./tests/Doctrine/Tests/TestInit.php"
>
<testsuites>
<testsuite name="Doctrine Common Test Suite">
<directory>./tests/Doctrine/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./lib/Doctrine/</directory>
</whitelist>
</filter>
</phpunit>

View file

@ -1,11 +1,21 @@
language: php
sudo: false
cache:
directory:
- $HOME/.composer/cache
php:
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm
install:
- composer --prefer-source install
- composer install -n
script:
- phpunit

View file

@ -1,4 +1,4 @@
Copyright (c) 2006-2013 Doctrine Project
Copyright (c) 2006-2015 Doctrine Project
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in

View file

@ -23,7 +23,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
"dev-master": "1.1.x-dev"
}
}
}

View file

@ -56,11 +56,12 @@ class Inflector
'/(p)erson$/i' => '\1eople',
'/(m)an$/i' => '\1en',
'/(c)hild$/i' => '\1hildren',
'/(buffal|tomat)o$/i' => '\1\2oes',
'/(f)oot$/i' => '\1eet',
'/(buffal|her|potat|tomat|volcan)o$/i' => '\1\2oes',
'/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i',
'/us$/i' => 'uses',
'/(alias)$/i' => '\1es',
'/(ax|cris|test)is$/i' => '\1es',
'/(analys|ax|cris|test|thes)is$/i' => '\1es',
'/s$/' => 's',
'/^$/' => '',
'/$/' => 's',
@ -70,27 +71,42 @@ class Inflector
),
'irregular' => array(
'atlas' => 'atlases',
'axe' => 'axes',
'beef' => 'beefs',
'brother' => 'brothers',
'cafe' => 'cafes',
'chateau' => 'chateaux',
'child' => 'children',
'cookie' => 'cookies',
'corpus' => 'corpuses',
'cow' => 'cows',
'criteria' => 'criterion',
'criterion' => 'criteria',
'curriculum' => 'curricula',
'demo' => 'demos',
'domino' => 'dominoes',
'echo' => 'echoes',
'foot' => 'feet',
'fungus' => 'fungi',
'ganglion' => 'ganglions',
'genie' => 'genies',
'genus' => 'genera',
'graffito' => 'graffiti',
'hippopotamus' => 'hippopotami',
'hoof' => 'hoofs',
'human' => 'humans',
'iris' => 'irises',
'leaf' => 'leaves',
'loaf' => 'loaves',
'man' => 'men',
'medium' => 'media',
'memorandum' => 'memoranda',
'money' => 'monies',
'mongoose' => 'mongooses',
'motto' => 'mottoes',
'move' => 'moves',
'mythos' => 'mythoi',
'niche' => 'niches',
'nucleus' => 'nuclei',
'numen' => 'numina',
'occiput' => 'occiputs',
'octopus' => 'octopuses',
@ -98,11 +114,19 @@ class Inflector
'ox' => 'oxen',
'penis' => 'penises',
'person' => 'people',
'plateau' => 'plateaux',
'runner-up' => 'runners-up',
'sex' => 'sexes',
'soliloquy' => 'soliloquies',
'son-in-law' => 'sons-in-law',
'syllabus' => 'syllabi',
'testis' => 'testes',
'thief' => 'thieves',
'tooth' => 'teeth',
'tornado' => 'tornadoes',
'trilby' => 'trilbys',
'turf' => 'turfs',
'volcano' => 'volcanoes',
)
);
@ -120,9 +144,10 @@ class Inflector
'/(vert|ind)ices$/i' => '\1ex',
'/^(ox)en/i' => '\1',
'/(alias)(es)*$/i' => '\1',
'/(buffal|her|potat|tomat|volcan)oes$/i' => '\1o',
'/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us',
'/([ftw]ax)es/i' => '\1',
'/(cris|ax|test)es$/i' => '\1is',
'/(analys|ax|cris|test|thes)es$/i' => '\1is',
'/(shoe|slave)s$/i' => '\1',
'/(o)es$/i' => '\1',
'/ouses$/' => 'ouse',
@ -143,6 +168,7 @@ class Inflector
'/(p)eople$/i' => '\1\2erson',
'/(m)en$/i' => '\1an',
'/(c)hildren$/i' => '\1\2hild',
'/(f)eet$/i' => '\1oot',
'/(n)ews$/i' => '\1\2ews',
'/eaus$/' => 'eau',
'/^(.*us)$/' => '\\1',
@ -159,10 +185,15 @@ class Inflector
'.*ss',
),
'irregular' => array(
'criterion' => 'criteria',
'curves' => 'curve',
'foes' => 'foe',
'waves' => 'wave',
'criteria' => 'criterion',
'curves' => 'curve',
'emphases' => 'emphasis',
'foes' => 'foe',
'hoaxes' => 'hoax',
'media' => 'medium',
'neuroses' => 'neurosis',
'waves' => 'wave',
'oases' => 'oasis',
)
);
@ -236,6 +267,42 @@ class Inflector
return lcfirst(self::classify($word));
}
/**
* Uppercases words with configurable delimeters between words.
*
* Takes a string and capitalizes all of the words, like PHP's built-in
* ucwords function. This extends that behavior, however, by allowing the
* word delimeters to be configured, rather than only separating on
* whitespace.
*
* Here is an example:
* <code>
* <?php
* $string = 'top-o-the-morning to all_of_you!';
* echo \Doctrine\Common\Inflector\Inflector::ucwords($string);
* // Top-O-The-Morning To All_of_you!
*
* echo \Doctrine\Common\Inflector\Inflector::ucwords($string, '-_ ');
* // Top-O-The-Morning To All_Of_You!
* ?>
* </code>
*
* @param string $string The string to operate on.
* @param string $delimiters A list of word separators.
*
* @return string The string with all delimeter-separated words capitalized.
*/
public static function ucwords($string, $delimiters = " \n\t\r\0\x0B-")
{
return preg_replace_callback(
'/[^' . preg_quote($delimiters, '/') . ']+/',
function($matches) {
return ucfirst($matches[0]);
},
$string
);
}
/**
* Clears Inflectors inflected value caches, and resets the inflection
* rules to the initial values.

View file

@ -12,15 +12,17 @@ php:
env:
global:
- deps=no
- deps=high
matrix:
fast_finish: true
include:
- php: 5.3
env: deps=low
- php: 5.6
env: deps=high
- php: 5.4
env: deps=no
- php: 5.5
env: deps=no
install:
- if [ "$deps" = "no" ]; then composer install; fi
@ -31,6 +33,3 @@ script:
- mkdir -p build/logs
- phpunit --coverage-clover build/logs/clover.xml
after_script:
- php vendor/bin/coveralls

View file

@ -9,7 +9,7 @@ With the help of
Run the command below to install via Composer
```shell
composer require egulias/email-validator
composer require egulias/email-validator "~1.2"
```
##Usage##

View file

@ -15,11 +15,10 @@
},
"require": {
"php": ">= 5.3.3",
"doctrine/lexer": "~1.0,>=1.0.1"
"doctrine/lexer": "^1.0.1"
},
"require-dev" : {
"satooshi/php-coveralls": "dev-master",
"phpunit/phpunit": "~4.4"
"phpunit/phpunit": "^4.8.24"
},
"autoload": {
"psr-0": {

File diff suppressed because it is too large Load diff

View file

@ -67,8 +67,6 @@ class EmailLexer extends AbstractLexer
"\n" => self::S_LF,
"\r\n" => self::CRLF,
'IPv6' => self::S_IPV6TAG,
'<' => self::S_LOWERTHAN,
'>' => self::S_GREATERTHAN,
'{' => self::S_OPENQBRACKET,
'}' => self::S_CLOSEQBRACKET,
'' => self::S_EMPTY,

View file

@ -7,49 +7,52 @@ namespace Egulias\EmailValidator;
*
* @author Eduardo Gulias Davis <me@egulias.com>
*/
class EmailValidator
class EmailValidator implements EmailValidatorInterface
{
/**
* Critical validation errors used to indicate that
* an email address is invalid:
*/
const ERR_CONSECUTIVEATS = 128;
const ERR_EXPECTING_DTEXT = 129;
const ERR_NOLOCALPART = 130;
const ERR_NODOMAIN = 131;
const ERR_CONSECUTIVEDOTS = 132;
const ERR_ATEXT_AFTER_CFWS = 133;
const ERR_ATEXT_AFTER_QS = 134;
const ERR_ATEXT_AFTER_DOMLIT = 135;
const ERR_EXPECTING_QPAIR = 136;
const ERR_EXPECTING_ATEXT = 137;
const ERR_EXPECTING_QTEXT = 138;
const ERR_EXPECTING_CTEXT = 139;
const ERR_BACKSLASHEND = 140;
const ERR_DOT_START = 141;
const ERR_DOT_END = 142;
const ERR_DOMAINHYPHENSTART = 143;
const ERR_DOMAINHYPHENEND = 144;
const ERR_UNCLOSEDQUOTEDSTR = 145;
const ERR_UNCLOSEDCOMMENT = 146;
const ERR_UNCLOSEDDOMLIT = 147;
const ERR_FWS_CRLF_X2 = 148;
const ERR_FWS_CRLF_END = 149;
const ERR_CR_NO_LF = 150;
const ERR_DEPREC_REACHED = 151;
const ERR_UNOPENEDCOMMENT = 152;
const ERR_ATEXT_AFTER_QS = 134; // not in use
const ERR_ATEXT_AFTER_DOMLIT = 135; // not in use
const ERR_EXPECTING_QTEXT = 138; // not in use
const ERR_BACKSLASHEND = 140; // not in use
const ERR_DOMAINHYPHENSTART = 143; // not in use
const ERR_UNCLOSEDDOMLIT = 147; // not in use
/**
* Informational validation warnings regarding unusual or
* deprecated features found in an email address:
*/
// Address is valid for SMTP (RFC-5321), but has unusual elements.
const RFC5321_TLD = 9;
const RFC5321_TLDNUMERIC = 10;
const RFC5321_QUOTEDSTRING = 11;
const RFC5321_ADDRESSLITERAL = 12;
const RFC5321_IPV6DEPRECATED = 13;
const CFWS_COMMENT = 17;
const CFWS_FWS = 18;
const DEPREC_LOCALPART = 33;
const DEPREC_FWS = 34;
const DEPREC_QTEXT = 35;
const DEPREC_QP = 36;
const DEPREC_COMMENT = 37;
const DEPREC_CTEXT = 38;
const DEPREC_CFWS_NEAR_AT = 49;
const RFC5321_TLDNUMERIC = 10; // not in use
// Address is only valid according to the broad
// definition of RFC-5322. It is otherwise invalid.
const RFC5322_LOCAL_TOOLONG = 64;
const RFC5322_LABEL_TOOLONG = 63;
const RFC5322_DOMAIN = 65;
const RFC5322_TOOLONG = 66;
const RFC5322_DOMAIN_TOOLONG = 255;
const RFC5322_DOMAINLITERAL = 70;
@ -60,12 +63,48 @@ class EmailValidator
const RFC5322_IPV6_MAXGRPS = 75;
const RFC5322_IPV6_COLONSTRT = 76;
const RFC5322_IPV6_COLONEND = 77;
const RFC5322_DOMAIN = 65; // not in use
// Address contains deprecated elements, but may
// still be valid in restricted contexts.
const DEPREC_QP = 36;
const DEPREC_COMMENT = 37;
const DEPREC_CFWS_NEAR_AT = 49;
const DEPREC_LOCALPART = 33; // not in use
const DEPREC_FWS = 34; // not in use
const DEPREC_QTEXT = 35; // not in use
const DEPREC_CTEXT = 38; // not in use
// Address is valid within the message,
// but cannot be used unmodified in the envelope.
const CFWS_COMMENT = 17;
const CFWS_FWS = 18;
// Hostname DNS checks were unsuccessful.
const DNSWARN_NO_MX_RECORD = 5;
const DNSWARN_NO_RECORD = 6;
/**
* @var EmailParser
*/
protected $parser;
/**
* Contains any informational warnings regarding unusual/deprecated
* features that were encountered during validation.
*
* @var array
*/
protected $warnings = array();
/**
* If a critical validation problem is encountered, this will be
* set to the value of one of this class's ERR_* constants.
*
* @var int
*/
protected $error;
/**
* @var int
*/
protected $threshold = 255;
public function __construct()
@ -73,6 +112,9 @@ class EmailValidator
$this->parser = new EmailParser(new EmailLexer());
}
/**
* {@inheritdoc}
*/
public function isValid($email, $checkDNS = false, $strict = false)
{
try {
@ -84,22 +126,18 @@ class EmailValidator
return false;
}
$dns = true;
if ($checkDNS) {
$dns = $this->checkDNS();
}
$dnsProblemExists = ($checkDNS ? !$this->checkDNS() : false);
if ($this->hasWarnings() && ((int) max($this->warnings) > $this->threshold)) {
$this->error = self::ERR_DEPREC_REACHED;
return false;
}
return !$strict || (!$this->hasWarnings() && $dns);
return ($strict ? (!$this->hasWarnings() && !$dnsProblemExists) : true);
}
/**
* @return boolean
* {@inheritdoc}
*/
public function hasWarnings()
{
@ -107,7 +145,7 @@ class EmailValidator
}
/**
* @return array
* {@inheritdoc}
*/
public function getWarnings()
{
@ -115,7 +153,7 @@ class EmailValidator
}
/**
* @return string
* {@inheritdoc}
*/
public function getError()
{
@ -123,9 +161,7 @@ class EmailValidator
}
/**
* @param int $threshold
*
* @return EmailValidator
* {@inheritdoc}
*/
public function setThreshold($threshold)
{
@ -135,26 +171,30 @@ class EmailValidator
}
/**
* @return int
* {@inheritdoc}
*/
public function getThreshold()
{
return $this->threshold;
}
/**
* @return bool Whether or not an MX record exists for the
* email address's host name.
*/
protected function checkDNS()
{
$checked = true;
$host = $this->parser->getParsedDomainPart();
$host = rtrim($host, '.') . '.';
$result = checkdnsrr(trim($this->parser->getParsedDomainPart()), 'MX');
$mxRecordExists = checkdnsrr($host, 'MX');
if (!$result) {
if (!$mxRecordExists) {
$this->warnings[] = self::DNSWARN_NO_RECORD;
$checked = false;
$this->addTLDWarnings();
}
return $checked;
return $mxRecordExists;
}
protected function addTLDWarnings()

View file

@ -0,0 +1,62 @@
<?php
namespace Egulias\EmailValidator;
/**
* EmailValidatorInterface
*
* @author Chris McCafferty <cilefen@gmail.com>
*/
interface EmailValidatorInterface
{
/**
* Validates an email address against the following standards:
*
* RFC-5321: Simple Mail Transfer Protocol
* RFC-5322: Internet Message Format
* RFC-6530: Overview and Framework for Internationalized Email
* RFC-6531: SMTP Extension for Internationalized Email
* RFC-6532: Internationalized Email Headers
* RFC-1123 section 2.1: Requirements for Internet Hosts -- Application and Support
* RFC-4291 section 2.2: IP Version 6 Addressing Architecture
*
* @param string $email The email address to validate.
* @param bool $checkDNS Whether or not the email address's hostname should
* be confirmed with a DNS lookup. This only comes
* into play if strict mode is also enabled.
* @param bool $strict If this is true, and any informational warnings
* were raised during validation, the email address
* will be considered invalid. Additionally, if
* $checkDNS is true and the DNS lookup failed,
* the email address will be considered invalid.
* @return bool
*/
public function isValid($email, $checkDNS = false, $strict = false);
/**
* @return bool
*/
public function hasWarnings();
/**
* @return array
*/
public function getWarnings();
/**
* @return string
*/
public function getError();
/**
* @param int $threshold The acceptable number of deprecation warnings.
*
* @return EmailValidator
*/
public function setThreshold($threshold);
/**
* @return int
*/
public function getThreshold();
}

Some files were not shown because too many files have changed in this diff Show more