update and video tag
This commit is contained in:
parent
5d7ce1bf03
commit
b4667f1975
|
|
@ -234,8 +234,8 @@ class UpdateManager
|
|||
|
||||
$params = [
|
||||
'core' => $this->getHash(),
|
||||
'plugins' => serialize($versions),
|
||||
'themes' => serialize($themes),
|
||||
'plugins' => base64_encode(json_encode($versions)),
|
||||
'themes' => base64_encode(json_encode($themes)),
|
||||
'build' => $build,
|
||||
'force' => $force
|
||||
];
|
||||
|
|
@ -590,8 +590,9 @@ class UpdateManager
|
|||
{
|
||||
$fileCode = $name . $hash;
|
||||
$filePath = $this->getFilePath($fileCode);
|
||||
$innerPath = str_replace('.', '/', strtolower($name));
|
||||
|
||||
if (!Zip::extract($filePath, plugins_path())) {
|
||||
if (!Zip::extract($filePath, plugins_path($innerPath))) {
|
||||
throw new ApplicationException(Lang::get('system::lang.zip.extract_failed', ['file' => $filePath]));
|
||||
}
|
||||
|
||||
|
|
@ -632,8 +633,9 @@ class UpdateManager
|
|||
{
|
||||
$fileCode = $name . $hash;
|
||||
$filePath = $this->getFilePath($fileCode);
|
||||
$innerPath = str_replace('.', '-', strtolower($name));
|
||||
|
||||
if (!Zip::extract($filePath, themes_path())) {
|
||||
if (!Zip::extract($filePath, themes_path($innerPath))) {
|
||||
throw new ApplicationException(Lang::get('system::lang.zip.extract_failed', ['file' => $filePath]));
|
||||
}
|
||||
|
||||
|
|
@ -903,13 +905,16 @@ class UpdateManager
|
|||
$http->toFile($filePath);
|
||||
});
|
||||
|
||||
if ($result->code != 200) {
|
||||
throw new ApplicationException(File::get($filePath));
|
||||
if (in_array($result->code, [301, 302])) {
|
||||
if ($redirectUrl = array_get($result->info, 'redirect_url')) {
|
||||
$result = Http::get($redirectUrl, function ($http) use ($postData, $filePath) {
|
||||
$http->toFile($filePath);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (md5_file($filePath) != $expectedHash) {
|
||||
@unlink($filePath);
|
||||
throw new ApplicationException(Lang::get('system::lang.server.file_corrupt'));
|
||||
if ($result->code != 200) {
|
||||
throw new ApplicationException(File::get($filePath));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -942,7 +947,7 @@ class UpdateManager
|
|||
*/
|
||||
protected function createServerUrl($uri)
|
||||
{
|
||||
$gateway = Config::get('cms.updateServer', 'http://gateway.octobercms.com/api');
|
||||
$gateway = Config::get('cms.updateServer', 'https://gateway.octobercms.com/api');
|
||||
if (substr($gateway, -1) != '/') {
|
||||
$gateway .= '/';
|
||||
}
|
||||
|
|
@ -958,10 +963,10 @@ class UpdateManager
|
|||
*/
|
||||
protected function applyHttpAttributes($http, $postData)
|
||||
{
|
||||
$postData['protocol_version'] = '1.1';
|
||||
$postData['protocol_version'] = '1.2';
|
||||
$postData['client'] = 'october';
|
||||
|
||||
$postData['server'] = base64_encode(serialize([
|
||||
$postData['server'] = base64_encode(json_encode([
|
||||
'php' => PHP_VERSION,
|
||||
'url' => Url::to('/'),
|
||||
'since' => PluginVersion::orderBy('created_at')->value('created_at')
|
||||
|
|
|
|||
|
|
@ -126,6 +126,8 @@ class VersionManager
|
|||
*/
|
||||
protected function applyPluginUpdate($code, $version, $details)
|
||||
{
|
||||
$version = $this->normalizeVersion($version);
|
||||
|
||||
list($comments, $scripts) = $this->extractScriptsAndComments($details);
|
||||
|
||||
/*
|
||||
|
|
@ -291,13 +293,18 @@ class VersionManager
|
|||
$versionInfo = [];
|
||||
}
|
||||
|
||||
if ($versionInfo) {
|
||||
uksort($versionInfo, function ($a, $b) {
|
||||
return version_compare($a, $b);
|
||||
});
|
||||
// Sort result
|
||||
uksort($versionInfo, function ($a, $b) {
|
||||
return version_compare($a, $b);
|
||||
});
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ($versionInfo as $version => $info) {
|
||||
$result[$this->normalizeVersion($version)] = $info;
|
||||
}
|
||||
|
||||
return $this->fileVersions[$code] = $versionInfo;
|
||||
return $this->fileVersions[$code] = $result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -549,6 +556,11 @@ class VersionManager
|
|||
return $this;
|
||||
}
|
||||
|
||||
protected function normalizeVersion($version)
|
||||
{
|
||||
return ltrim((string) $version, 'v');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $details
|
||||
*
|
||||
|
|
|
|||
|
|
@ -18,10 +18,18 @@ final class SecurityPolicy implements SecurityPolicyInterface
|
|||
* @var array List of forbidden methods.
|
||||
*/
|
||||
protected $blockedMethods = [
|
||||
// \October\Rain\Extension\ExtendableTrait
|
||||
'addDynamicMethod',
|
||||
'addDynamicProperty',
|
||||
|
||||
// \October\Rain\Support\Traits\Emitter
|
||||
'bindEvent',
|
||||
'bindEventOnce',
|
||||
|
||||
// Eloquent & Halcyon data modification
|
||||
'insert',
|
||||
'update',
|
||||
'delete',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -251,4 +251,5 @@
|
|||
'AhmadFatoni\\ApiGenerator\\Controllers\\ApiGeneratorController' => 'plugins/ahmadfatoni/apigenerator/controllers/ApiGeneratorController.php',
|
||||
'Zen\\Robots\\Controllers\\Generate' => 'plugins/zen/robots/controllers/Generate.php',
|
||||
'Tps\\Reklama\\Widgets\\Stats' => 'plugins/tps/reklama/widgets/Stats.php',
|
||||
'indikator\\devtools\\Plugin' => 'plugins/indikator/devtools/Plugin.php',
|
||||
);
|
||||
|
|
@ -72,5 +72,29 @@ slug = "{{ :slug }}"
|
|||
update: { view: '@#view' },
|
||||
|
||||
})
|
||||
var re = /\[video poster=\"(.+?)\".+?mp4=\"(.+?)\"/g;
|
||||
// re = /(\S+)=["']?((?:.(?!["']?\s+(?:\S+)=|\s*\/?[>"']))+.)["']?/g
|
||||
var ptags = document.querySelectorAll('p');
|
||||
|
||||
ptags.forEach(s =>{
|
||||
|
||||
var m;
|
||||
m = re.exec(s.innerText);
|
||||
if (m) {
|
||||
var video =document.createElement('video');
|
||||
video.setAttribute('src',m[2]);
|
||||
video.setAttribute('controls',"")
|
||||
video.setAttribute('class',"aligncenter")
|
||||
video.setAttribute('type',"video/mp4")
|
||||
video.setAttribute('poster',m[1])
|
||||
s.parentNode.replaceChild(video,s);
|
||||
|
||||
console.log(m);
|
||||
console.log(m[1]);
|
||||
}
|
||||
else{
|
||||
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endput %}
|
||||
{% endput %}
|
||||
|
|
|
|||
|
|
@ -6,14 +6,7 @@ $vendorDir = dirname(dirname(__FILE__));
|
|||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'ArithmeticError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php',
|
||||
'AssertionError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/AssertionError.php',
|
||||
'DivisionByZeroError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php',
|
||||
'Error' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/Error.php',
|
||||
'JSMin' => $vendorDir . '/linkorb/jsmin-php/src/jsmin-1.1.1.php',
|
||||
'JSMinException' => $vendorDir . '/linkorb/jsmin-php/src/jsmin-1.1.1.php',
|
||||
'Normalizer' => $vendorDir . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php',
|
||||
'ParseError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/ParseError.php',
|
||||
'SessionUpdateTimestampHandlerInterface' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php',
|
||||
'TypeError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/TypeError.php',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -8,9 +8,7 @@ $baseDir = dirname($vendorDir);
|
|||
return array(
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
|
||||
'023d27dca8066ef29e6739335ea73bad' => $vendorDir . '/symfony/polyfill-php70/bootstrap.php',
|
||||
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||
'bd9634f2d41831496de0d3dfe4c94881' => $vendorDir . '/symfony/polyfill-php56/bootstrap.php',
|
||||
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
|
||||
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
|
||||
|
|
|
|||
|
|
@ -12,6 +12,5 @@ return array(
|
|||
'Parsedown' => array($vendorDir . '/erusev/parsedown'),
|
||||
'Less' => array($vendorDir . '/october/rain/src/Parse/Assetic/Less/lib'),
|
||||
'Doctrine\\DBAL\\' => array($vendorDir . '/doctrine/dbal/lib'),
|
||||
'Doctrine\\Common\\Collections\\' => array($vendorDir . '/doctrine/collections/lib'),
|
||||
'Assetic' => array($vendorDir . '/kriswallsmith/assetic/src'),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7,19 +7,17 @@ $baseDir = dirname($vendorDir);
|
|||
|
||||
return array(
|
||||
'XdgBaseDir\\' => array($vendorDir . '/dnoegel/php-xdg-base-dir/src'),
|
||||
'Wikimedia\\Composer\\' => array($vendorDir . '/wikimedia/composer-merge-plugin/src'),
|
||||
'Twig\\' => array($vendorDir . '/twig/twig/src'),
|
||||
'TijsVerkoyen\\CssToInlineStyles\\' => array($vendorDir . '/tijsverkoyen/css-to-inline-styles/src'),
|
||||
'System\\' => array($baseDir . '/modules/system'),
|
||||
'Symfony\\Polyfill\\Util\\' => array($vendorDir . '/symfony/polyfill-util'),
|
||||
'Symfony\\Polyfill\\Php72\\' => array($vendorDir . '/symfony/polyfill-php72'),
|
||||
'Symfony\\Polyfill\\Php70\\' => array($vendorDir . '/symfony/polyfill-php70'),
|
||||
'Symfony\\Polyfill\\Php56\\' => array($vendorDir . '/symfony/polyfill-php56'),
|
||||
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
|
||||
'Symfony\\Polyfill\\Intl\\Normalizer\\' => array($vendorDir . '/symfony/polyfill-intl-normalizer'),
|
||||
'Symfony\\Polyfill\\Intl\\Idn\\' => array($vendorDir . '/symfony/polyfill-intl-idn'),
|
||||
'Symfony\\Polyfill\\Iconv\\' => array($vendorDir . '/symfony/polyfill-iconv'),
|
||||
'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'),
|
||||
'Symfony\\Contracts\\Translation\\' => array($vendorDir . '/symfony/translation-contracts'),
|
||||
'Symfony\\Contracts\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher-contracts'),
|
||||
'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'),
|
||||
'Symfony\\Component\\VarDumper\\' => array($vendorDir . '/symfony/var-dumper'),
|
||||
'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'),
|
||||
|
|
@ -39,8 +37,8 @@ return array(
|
|||
'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
|
||||
'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'),
|
||||
'October\\Rain\\' => array($vendorDir . '/october/rain/src'),
|
||||
'October\\Demo\\' => array($baseDir . '/plugins/october/demo'),
|
||||
'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
|
||||
'League\\MimeTypeDetection\\' => array($vendorDir . '/league/mime-type-detection/src'),
|
||||
'League\\Flysystem\\' => array($vendorDir . '/league/flysystem/src'),
|
||||
'League\\Csv\\' => array($vendorDir . '/league/csv/src'),
|
||||
'Leafo\\ScssPhp\\' => array($vendorDir . '/leafo/scssphp/src'),
|
||||
|
|
@ -51,8 +49,10 @@ return array(
|
|||
'Illuminate\\' => array($vendorDir . '/laravel/framework/src/Illuminate'),
|
||||
'Egulias\\EmailValidator\\' => array($vendorDir . '/egulias/email-validator/src'),
|
||||
'Dotenv\\' => array($vendorDir . '/vlucas/phpdotenv/src'),
|
||||
'Doctrine\\Inflector\\' => array($vendorDir . '/doctrine/inflector/lib/Doctrine/Inflector'),
|
||||
'Doctrine\\Common\\Lexer\\' => array($vendorDir . '/doctrine/lexer/lib/Doctrine/Common/Lexer'),
|
||||
'Doctrine\\Common\\Inflector\\' => array($vendorDir . '/doctrine/inflector/lib/Doctrine/Common/Inflector'),
|
||||
'Doctrine\\Common\\Collections\\' => array($vendorDir . '/doctrine/collections/lib/Doctrine/Common/Collections'),
|
||||
'Doctrine\\Common\\Cache\\' => array($vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache'),
|
||||
'Doctrine\\Common\\Annotations\\' => array($vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations'),
|
||||
'Doctrine\\Common\\' => array($vendorDir . '/doctrine/common/lib/Doctrine/Common'),
|
||||
|
|
|
|||
|
|
@ -9,9 +9,7 @@ class ComposerStaticInitce290a037d2cbd6fc6b8d537449d0ac2
|
|||
public static $files = array (
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
|
||||
'023d27dca8066ef29e6739335ea73bad' => __DIR__ . '/..' . '/symfony/polyfill-php70/bootstrap.php',
|
||||
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||
'bd9634f2d41831496de0d3dfe4c94881' => __DIR__ . '/..' . '/symfony/polyfill-php56/bootstrap.php',
|
||||
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||
'25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
|
||||
'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
|
||||
|
|
@ -32,10 +30,6 @@ class ComposerStaticInitce290a037d2cbd6fc6b8d537449d0ac2
|
|||
array (
|
||||
'XdgBaseDir\\' => 11,
|
||||
),
|
||||
'W' =>
|
||||
array (
|
||||
'Wikimedia\\Composer\\' => 19,
|
||||
),
|
||||
'T' =>
|
||||
array (
|
||||
'Twig\\' => 5,
|
||||
|
|
@ -44,15 +38,14 @@ class ComposerStaticInitce290a037d2cbd6fc6b8d537449d0ac2
|
|||
'S' =>
|
||||
array (
|
||||
'System\\' => 7,
|
||||
'Symfony\\Polyfill\\Util\\' => 22,
|
||||
'Symfony\\Polyfill\\Php72\\' => 23,
|
||||
'Symfony\\Polyfill\\Php70\\' => 23,
|
||||
'Symfony\\Polyfill\\Php56\\' => 23,
|
||||
'Symfony\\Polyfill\\Mbstring\\' => 26,
|
||||
'Symfony\\Polyfill\\Intl\\Normalizer\\' => 33,
|
||||
'Symfony\\Polyfill\\Intl\\Idn\\' => 26,
|
||||
'Symfony\\Polyfill\\Iconv\\' => 23,
|
||||
'Symfony\\Polyfill\\Ctype\\' => 23,
|
||||
'Symfony\\Contracts\\Translation\\' => 30,
|
||||
'Symfony\\Contracts\\EventDispatcher\\' => 34,
|
||||
'Symfony\\Component\\Yaml\\' => 23,
|
||||
'Symfony\\Component\\VarDumper\\' => 28,
|
||||
'Symfony\\Component\\Translation\\' => 30,
|
||||
|
|
@ -81,7 +74,6 @@ class ComposerStaticInitce290a037d2cbd6fc6b8d537449d0ac2
|
|||
'O' =>
|
||||
array (
|
||||
'October\\Rain\\' => 13,
|
||||
'October\\Demo\\' => 13,
|
||||
),
|
||||
'M' =>
|
||||
array (
|
||||
|
|
@ -89,6 +81,7 @@ class ComposerStaticInitce290a037d2cbd6fc6b8d537449d0ac2
|
|||
),
|
||||
'L' =>
|
||||
array (
|
||||
'League\\MimeTypeDetection\\' => 25,
|
||||
'League\\Flysystem\\' => 17,
|
||||
'League\\Csv\\' => 11,
|
||||
'Leafo\\ScssPhp\\' => 14,
|
||||
|
|
@ -111,8 +104,10 @@ class ComposerStaticInitce290a037d2cbd6fc6b8d537449d0ac2
|
|||
'D' =>
|
||||
array (
|
||||
'Dotenv\\' => 7,
|
||||
'Doctrine\\Inflector\\' => 19,
|
||||
'Doctrine\\Common\\Lexer\\' => 22,
|
||||
'Doctrine\\Common\\Inflector\\' => 26,
|
||||
'Doctrine\\Common\\Collections\\' => 28,
|
||||
'Doctrine\\Common\\Cache\\' => 22,
|
||||
'Doctrine\\Common\\Annotations\\' => 28,
|
||||
'Doctrine\\Common\\' => 16,
|
||||
|
|
@ -134,10 +129,6 @@ class ComposerStaticInitce290a037d2cbd6fc6b8d537449d0ac2
|
|||
array (
|
||||
0 => __DIR__ . '/..' . '/dnoegel/php-xdg-base-dir/src',
|
||||
),
|
||||
'Wikimedia\\Composer\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/wikimedia/composer-merge-plugin/src',
|
||||
),
|
||||
'Twig\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/twig/twig/src',
|
||||
|
|
@ -150,22 +141,10 @@ class ComposerStaticInitce290a037d2cbd6fc6b8d537449d0ac2
|
|||
array (
|
||||
0 => __DIR__ . '/../..' . '/modules/system',
|
||||
),
|
||||
'Symfony\\Polyfill\\Util\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-util',
|
||||
),
|
||||
'Symfony\\Polyfill\\Php72\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-php72',
|
||||
),
|
||||
'Symfony\\Polyfill\\Php70\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-php70',
|
||||
),
|
||||
'Symfony\\Polyfill\\Php56\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-php56',
|
||||
),
|
||||
'Symfony\\Polyfill\\Mbstring\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
|
||||
|
|
@ -186,6 +165,14 @@ class ComposerStaticInitce290a037d2cbd6fc6b8d537449d0ac2
|
|||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-ctype',
|
||||
),
|
||||
'Symfony\\Contracts\\Translation\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/translation-contracts',
|
||||
),
|
||||
'Symfony\\Contracts\\EventDispatcher\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/event-dispatcher-contracts',
|
||||
),
|
||||
'Symfony\\Component\\Yaml\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/yaml',
|
||||
|
|
@ -262,14 +249,14 @@ class ComposerStaticInitce290a037d2cbd6fc6b8d537449d0ac2
|
|||
array (
|
||||
0 => __DIR__ . '/..' . '/october/rain/src',
|
||||
),
|
||||
'October\\Demo\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/../..' . '/plugins/october/demo',
|
||||
),
|
||||
'Monolog\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/monolog/monolog/src/Monolog',
|
||||
),
|
||||
'League\\MimeTypeDetection\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/league/mime-type-detection/src',
|
||||
),
|
||||
'League\\Flysystem\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/league/flysystem/src',
|
||||
|
|
@ -310,6 +297,10 @@ class ComposerStaticInitce290a037d2cbd6fc6b8d537449d0ac2
|
|||
array (
|
||||
0 => __DIR__ . '/..' . '/vlucas/phpdotenv/src',
|
||||
),
|
||||
'Doctrine\\Inflector\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/inflector/lib/Doctrine/Inflector',
|
||||
),
|
||||
'Doctrine\\Common\\Lexer\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/lexer/lib/Doctrine/Common/Lexer',
|
||||
|
|
@ -318,6 +309,10 @@ class ComposerStaticInitce290a037d2cbd6fc6b8d537449d0ac2
|
|||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/inflector/lib/Doctrine/Common/Inflector',
|
||||
),
|
||||
'Doctrine\\Common\\Collections\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/collections/lib/Doctrine/Common/Collections',
|
||||
),
|
||||
'Doctrine\\Common\\Cache\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache',
|
||||
|
|
@ -391,10 +386,6 @@ class ComposerStaticInitce290a037d2cbd6fc6b8d537449d0ac2
|
|||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/dbal/lib',
|
||||
),
|
||||
'Doctrine\\Common\\Collections\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/collections/lib',
|
||||
),
|
||||
),
|
||||
'A' =>
|
||||
array (
|
||||
|
|
@ -406,16 +397,9 @@ class ComposerStaticInitce290a037d2cbd6fc6b8d537449d0ac2
|
|||
);
|
||||
|
||||
public static $classMap = array (
|
||||
'ArithmeticError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php',
|
||||
'AssertionError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/AssertionError.php',
|
||||
'DivisionByZeroError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php',
|
||||
'Error' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/Error.php',
|
||||
'JSMin' => __DIR__ . '/..' . '/linkorb/jsmin-php/src/jsmin-1.1.1.php',
|
||||
'JSMinException' => __DIR__ . '/..' . '/linkorb/jsmin-php/src/jsmin-1.1.1.php',
|
||||
'Normalizer' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php',
|
||||
'ParseError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/ParseError.php',
|
||||
'SessionUpdateTimestampHandlerInterface' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php',
|
||||
'TypeError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/TypeError.php',
|
||||
);
|
||||
|
||||
public static function getInitializer(ClassLoader $loader)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -57,12 +57,14 @@
|
|||
"PPI",
|
||||
"Puppet",
|
||||
"Porto",
|
||||
"ProcessWire",
|
||||
"RadPHP",
|
||||
"ReIndex",
|
||||
"Roundcube",
|
||||
"shopware",
|
||||
"SilverStripe",
|
||||
"SMF",
|
||||
"Starbug",
|
||||
"SyDES",
|
||||
"Sylius",
|
||||
"symfony",
|
||||
|
|
@ -86,10 +88,13 @@
|
|||
"autoload": {
|
||||
"psr-4": { "Composer\\Installers\\": "src/Composer/Installers" }
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": { "Composer\\Installers\\Test\\": "tests/Composer/Installers/Test" }
|
||||
},
|
||||
"extra": {
|
||||
"class": "Composer\\Installers\\Plugin",
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0-dev"
|
||||
"dev-main": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"replace": {
|
||||
|
|
@ -100,13 +105,15 @@
|
|||
"composer-plugin-api": "^1.0 || ^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"composer/composer": "1.6.* || 2.0.*@dev",
|
||||
"composer/semver": "1.0.* || 2.0.*@dev",
|
||||
"phpunit/phpunit": "^4.8.36",
|
||||
"sebastian/comparator": "^1.2.4",
|
||||
"symfony/process": "^2.3"
|
||||
"composer/composer": "1.6.* || ^2.0",
|
||||
"composer/semver": "^1 || ^3",
|
||||
"symfony/phpunit-bridge": "^4.2 || ^5",
|
||||
"phpstan/phpstan": "^0.12.55",
|
||||
"symfony/process": "^2.3",
|
||||
"phpstan/phpstan-phpunit": "^0.12.16"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "phpunit"
|
||||
"test": "SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT=1 vendor/bin/simple-phpunit",
|
||||
"phpstan": "vendor/bin/phpstan analyse"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,8 +74,8 @@ abstract class BaseInstaller
|
|||
/**
|
||||
* For an installer to override to modify the vars per installer.
|
||||
*
|
||||
* @param array $vars
|
||||
* @return array
|
||||
* @param array<string, string> $vars This will normally receive array{name: string, vendor: string, type: string}
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function inflectPackageVars($vars)
|
||||
{
|
||||
|
|
@ -85,7 +85,7 @@ abstract class BaseInstaller
|
|||
/**
|
||||
* Gets the installer's locations
|
||||
*
|
||||
* @return array
|
||||
* @return array<string, string> map of package types => install path
|
||||
*/
|
||||
public function getLocations()
|
||||
{
|
||||
|
|
@ -95,8 +95,8 @@ abstract class BaseInstaller
|
|||
/**
|
||||
* Replace vars in a path
|
||||
*
|
||||
* @param string $path
|
||||
* @param array $vars
|
||||
* @param string $path
|
||||
* @param array<string, string> $vars
|
||||
* @return string
|
||||
*/
|
||||
protected function templatePath($path, array $vars = array())
|
||||
|
|
@ -121,7 +121,7 @@ abstract class BaseInstaller
|
|||
* @param string $name
|
||||
* @param string $type
|
||||
* @param string $vendor = NULL
|
||||
* @return string
|
||||
* @return string|false
|
||||
*/
|
||||
protected function mapCustomInstallPaths(array $paths, $name, $type, $vendor = NULL)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
namespace Composer\Installers;
|
||||
|
||||
use Composer\DependencyResolver\Pool;
|
||||
use Composer\Semver\Constraint\Constraint;
|
||||
|
||||
class CakePHPInstaller extends BaseInstaller
|
||||
{
|
||||
|
|
@ -49,14 +50,6 @@ class CakePHPInstaller extends BaseInstaller
|
|||
*/
|
||||
protected function matchesCakeVersion($matcher, $version)
|
||||
{
|
||||
if (class_exists('Composer\Semver\Constraint\MultiConstraint')) {
|
||||
$multiClass = 'Composer\Semver\Constraint\MultiConstraint';
|
||||
$constraintClass = 'Composer\Semver\Constraint\Constraint';
|
||||
} else {
|
||||
$multiClass = 'Composer\Package\LinkConstraint\MultiConstraint';
|
||||
$constraintClass = 'Composer\Package\LinkConstraint\VersionConstraint';
|
||||
}
|
||||
|
||||
$repositoryManager = $this->composer->getRepositoryManager();
|
||||
if (! $repositoryManager) {
|
||||
return false;
|
||||
|
|
@ -67,6 +60,6 @@ class CakePHPInstaller extends BaseInstaller
|
|||
return false;
|
||||
}
|
||||
|
||||
return $repos->findPackage('cakephp/cakephp', new $constraintClass($matcher, $version)) !== null;
|
||||
return $repos->findPackage('cakephp/cakephp', new Constraint($matcher, $version)) !== null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,9 +12,7 @@ class CockpitInstaller extends BaseInstaller
|
|||
*
|
||||
* Strip `module-` prefix from package name.
|
||||
*
|
||||
* @param array @vars
|
||||
*
|
||||
* @return array
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function inflectPackageVars($vars)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use Composer\IO\IOInterface;
|
|||
use Composer\Package\PackageInterface;
|
||||
use Composer\Repository\InstalledRepositoryInterface;
|
||||
use Composer\Util\Filesystem;
|
||||
use React\Promise\PromiseInterface;
|
||||
|
||||
class Installer extends LibraryInstaller
|
||||
{
|
||||
|
|
@ -87,6 +88,7 @@ class Installer extends LibraryInstaller
|
|||
'radphp' => 'RadPHPInstaller',
|
||||
'phifty' => 'PhiftyInstaller',
|
||||
'porto' => 'PortoInstaller',
|
||||
'processwire' => 'ProcessWireInstaller',
|
||||
'redaxo' => 'RedaxoInstaller',
|
||||
'redaxo5' => 'Redaxo5Installer',
|
||||
'reindex' => 'ReIndexInstaller',
|
||||
|
|
@ -95,6 +97,7 @@ class Installer extends LibraryInstaller
|
|||
'sitedirect' => 'SiteDirectInstaller',
|
||||
'silverstripe' => 'SilverStripeInstaller',
|
||||
'smf' => 'SMFInstaller',
|
||||
'starbug' => 'StarbugInstaller',
|
||||
'sydes' => 'SyDESInstaller',
|
||||
'sylius' => 'SyliusInstaller',
|
||||
'symfony1' => 'Symfony1Installer',
|
||||
|
|
@ -160,9 +163,23 @@ class Installer extends LibraryInstaller
|
|||
|
||||
public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package)
|
||||
{
|
||||
parent::uninstall($repo, $package);
|
||||
$installPath = $this->getPackageBasePath($package);
|
||||
$this->io->write(sprintf('Deleting %s - %s', $installPath, !file_exists($installPath) ? '<comment>deleted</comment>' : '<error>not deleted</error>'));
|
||||
$io = $this->io;
|
||||
$outputStatus = function () use ($io, $installPath) {
|
||||
$io->write(sprintf('Deleting %s - %s', $installPath, !file_exists($installPath) ? '<comment>deleted</comment>' : '<error>not deleted</error>'));
|
||||
};
|
||||
|
||||
$promise = parent::uninstall($repo, $package);
|
||||
|
||||
// Composer v2 might return a promise here
|
||||
if ($promise instanceof PromiseInterface) {
|
||||
return $promise->then($outputStatus);
|
||||
}
|
||||
|
||||
// If not, execute the code right away as parent::uninstall executed synchronously (composer v1, or v2 without async)
|
||||
$outputStatus();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -184,23 +201,20 @@ class Installer extends LibraryInstaller
|
|||
/**
|
||||
* Finds a supported framework type if it exists and returns it
|
||||
*
|
||||
* @param string $type
|
||||
* @return string
|
||||
* @param string $type
|
||||
* @return string|false
|
||||
*/
|
||||
protected function findFrameworkType($type)
|
||||
{
|
||||
$frameworkType = false;
|
||||
|
||||
krsort($this->supportedTypes);
|
||||
|
||||
foreach ($this->supportedTypes as $key => $val) {
|
||||
if ($key === substr($type, 0, strlen($key))) {
|
||||
$frameworkType = substr($type, 0, strlen($key));
|
||||
break;
|
||||
return substr($type, 0, strlen($key));
|
||||
}
|
||||
}
|
||||
|
||||
return $frameworkType;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ class MoodleInstaller extends BaseInstaller
|
|||
'cachestore' => 'cache/stores/{$name}/',
|
||||
'cachelock' => 'cache/locks/{$name}/',
|
||||
'calendartype' => 'calendar/type/{$name}/',
|
||||
'fileconverter' => 'files/converter/{$name}/',
|
||||
'format' => 'course/format/{$name}/',
|
||||
'coursereport' => 'course/report/{$name}/',
|
||||
'customcertelement' => 'mod/customcert/element/{$name}/',
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class OxidInstaller extends BaseInstaller
|
|||
*
|
||||
* @param PackageInterface $package
|
||||
* @param string $frameworkType
|
||||
* @return void
|
||||
* @return string
|
||||
*/
|
||||
public function getInstallPath(PackageInterface $package, $frameworkType = '')
|
||||
{
|
||||
|
|
|
|||
|
|
@ -13,9 +13,7 @@ class SyDESInstaller extends BaseInstaller
|
|||
*
|
||||
* Strip `sydes-` prefix and a trailing '-theme' or '-module' from package name if present.
|
||||
*
|
||||
* @param array @vars
|
||||
*
|
||||
* @return array
|
||||
* {@inerhitDoc}
|
||||
*/
|
||||
public function inflectPackageVars($vars)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,7 +6,25 @@ namespace Composer\Installers;
|
|||
*/
|
||||
class TaoInstaller extends BaseInstaller
|
||||
{
|
||||
const EXTRA_TAO_EXTENSION_NAME = 'tao-extension-name';
|
||||
|
||||
protected $locations = array(
|
||||
'extension' => '{$name}'
|
||||
);
|
||||
|
||||
public function inflectPackageVars($vars)
|
||||
{
|
||||
$extra = $this->package->getExtra();
|
||||
|
||||
if (array_key_exists(self::EXTRA_TAO_EXTENSION_NAME, $extra)) {
|
||||
$vars['name'] = $extra[self::EXTRA_TAO_EXTENSION_NAME];
|
||||
return $vars;
|
||||
}
|
||||
|
||||
$vars['name'] = str_replace('extension-', '', $vars['name']);
|
||||
$vars['name'] = str_replace('-', ' ', $vars['name']);
|
||||
$vars['name'] = lcfirst(str_replace(' ', '', ucwords($vars['name'])));
|
||||
|
||||
return $vars;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,52 @@
|
|||
## Changelog
|
||||
|
||||
### 1.6.1
|
||||
|
||||
This release fixes an issue in which annotations such as `@foo-bar`
|
||||
and `@foo-` were incorrectly recognised as valid, and both erroneously
|
||||
parsed as `@foo`.
|
||||
|
||||
Any annotation with `@name-*` format will now silently be ignored,
|
||||
allowing vendor-specific annotations to be prefixed with the tool
|
||||
name.
|
||||
|
||||
Total issues resolved: **3**
|
||||
|
||||
- [165: Update the composer branch alias](https://github.com/doctrine/annotations/pull/165) thanks to @mikeSimonson
|
||||
- [209: Change Annotation::value typehint to mixed](https://github.com/doctrine/annotations/pull/209) thanks to @malarzm
|
||||
- [257: Skip parsing annotations containing dashes, such as `@Foo-bar`, or `@Foo-`](https://github.com/doctrine/annotations/pull/257) thanks to @Ocramius
|
||||
|
||||
### 1.6.0
|
||||
|
||||
This release brings a new endpoint that make sure that you can't shoot yourself in the foot by calling ```registerLoader``` multiple times and a few tests improvements.
|
||||
|
||||
Total issues resolved: **7**
|
||||
|
||||
- [145: Memory leak in AnnotationRegistry::registerLoader() when called multiple times](https://github.com/doctrine/annotations/issues/145) thanks to @TriAnMan
|
||||
- [146: Import error on @experimental Annotation](https://github.com/doctrine/annotations/issues/146) thanks to @aturki
|
||||
- [147: Ignoring @experimental annotation used by Symfony 3.3 CacheAdapter](https://github.com/doctrine/annotations/pull/147) thanks to @aturki
|
||||
- [151: Remove duplicate code in `DCOM58Test`](https://github.com/doctrine/annotations/pull/151) thanks to @tuanphpvn
|
||||
- [161: Prevent loading class_exists multiple times](https://github.com/doctrine/annotations/pull/161) thanks to @jrjohnson
|
||||
- [162: Add registerUniqueLoader to AnnotationRegistry](https://github.com/doctrine/annotations/pull/162) thanks to @jrjohnson
|
||||
- [163: Use assertDirectoryExists and assertDirectoryNotExists](https://github.com/doctrine/annotations/pull/163) thanks to @carusogabriel
|
||||
|
||||
Thanks to everyone involved in this release.
|
||||
|
||||
### 1.5.0
|
||||
|
||||
This release increments the minimum supported PHP version to 7.1.0.
|
||||
|
||||
Also, HHVM official support has been dropped.
|
||||
|
||||
Some noticeable performance improvements to annotation autoloading
|
||||
have been applied, making failed annotation autoloading less heavy
|
||||
on the filesystem access.
|
||||
|
||||
- [133: Add @throws annotation in AnnotationReader#__construct()](https://github.com/doctrine/annotations/issues/133) thanks to @SenseException
|
||||
- [134: Require PHP 7.1, drop HHVM support](https://github.com/doctrine/annotations/issues/134) thanks to @lcobucci
|
||||
- [135: Prevent the same loader from being registered twice](https://github.com/doctrine/annotations/issues/135) thanks to @jrjohnson
|
||||
- [137: #135 optimise multiple class load attempts in AnnotationRegistry](https://github.com/doctrine/annotations/issues/137) thanks to @Ocramius
|
||||
|
||||
|
||||
### 1.4.0
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,21 @@
|
|||
# Doctrine Annotations
|
||||
|
||||
[](https://travis-ci.org/doctrine/annotations)
|
||||
[](https://github.com/doctrine/persistence/actions)
|
||||
[](https://www.versioneye.com/package/php--doctrine--annotations)
|
||||
[](https://www.versioneye.com/php/doctrine:annotations/references)
|
||||
[](https://packagist.org/packages/doctrine/annotations)
|
||||
[](https://packagist.org/packages/doctrine/annotations)
|
||||
[](https://packagist.org/packages/doctrine/annotations)
|
||||
|
||||
Docblock Annotations Parser library (extracted from [Doctrine Common](https://github.com/doctrine/common)).
|
||||
|
||||
## Documentation
|
||||
|
||||
See the [doctrine-project website](http://docs.doctrine-project.org/projects/doctrine-common/en/latest/reference/annotations.html).
|
||||
See the [doctrine-project website](https://www.doctrine-project.org/projects/doctrine-annotations/en/latest/index.html).
|
||||
|
||||
## Contributing
|
||||
|
||||
When making a pull request, make sure your changes follow the
|
||||
[Coding Standard Guidelines](https://www.doctrine-project.org/projects/doctrine-coding-standard/en/current/reference/index.html#introduction).
|
||||
|
||||
## Changelog
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"type": "library",
|
||||
"description": "Docblock Annotations Parser",
|
||||
"keywords": ["annotations", "docblock", "parser"],
|
||||
"homepage": "http://www.doctrine-project.org",
|
||||
"homepage": "https://www.doctrine-project.org/projects/annotations.html",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
|
||||
|
|
@ -13,22 +13,30 @@
|
|||
{"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
|
||||
],
|
||||
"require": {
|
||||
"php": "^5.6 || ^7.0",
|
||||
"php": "^7.1 || ^8.0",
|
||||
"ext-tokenizer": "*",
|
||||
"doctrine/lexer": "1.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/cache": "1.*",
|
||||
"phpunit/phpunit": "^5.7"
|
||||
"doctrine/coding-standard": "^6.0 || ^8.1",
|
||||
"phpstan/phpstan": "^0.12.20",
|
||||
"phpunit/phpunit": "^7.5 || ^9.1.5"
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" }
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": { "Doctrine\\Tests\\Common\\Annotations\\": "tests/Doctrine/Tests/Common/Annotations" }
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.4.x-dev"
|
||||
}
|
||||
"psr-4": {
|
||||
"Doctrine\\Performance\\Common\\Annotations\\": "tests/Doctrine/Performance/Common/Annotations",
|
||||
"Doctrine\\Tests\\Common\\Annotations\\": "tests/Doctrine/Tests/Common/Annotations"
|
||||
},
|
||||
"files": [
|
||||
"tests/Doctrine/Tests/Common/Annotations/Fixtures/functions.php",
|
||||
"tests/Doctrine/Tests/Common/Annotations/Fixtures/SingleClassLOC1000.php"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,47 +1,27 @@
|
|||
<?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\Annotations;
|
||||
|
||||
use BadMethodCallException;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Annotations class.
|
||||
*
|
||||
* @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>
|
||||
*/
|
||||
class Annotation
|
||||
{
|
||||
/**
|
||||
* Value property. Common among all derived classes.
|
||||
*
|
||||
* @var string
|
||||
* @var mixed
|
||||
*/
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $data Key-value for properties to be defined in this class.
|
||||
* @param array<string, mixed> $data Key-value for properties to be defined in this class.
|
||||
*/
|
||||
public final function __construct(array $data)
|
||||
final public function __construct(array $data)
|
||||
{
|
||||
foreach ($data as $key => $value) {
|
||||
$this->$key = $value;
|
||||
|
|
@ -53,12 +33,12 @@ class Annotation
|
|||
*
|
||||
* @param string $name Unknown property name.
|
||||
*
|
||||
* @throws \BadMethodCallException
|
||||
* @throws BadMethodCallException
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
throw new \BadMethodCallException(
|
||||
sprintf("Unknown property '%s' on annotation '%s'.", $name, get_class($this))
|
||||
throw new BadMethodCallException(
|
||||
sprintf("Unknown property '%s' on annotation '%s'.", $name, static::class)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -68,12 +48,12 @@ class Annotation
|
|||
* @param string $name Unknown property name.
|
||||
* @param mixed $value Property value.
|
||||
*
|
||||
* @throws \BadMethodCallException
|
||||
* @throws BadMethodCallException
|
||||
*/
|
||||
public function __set($name, $value)
|
||||
{
|
||||
throw new \BadMethodCallException(
|
||||
sprintf("Unknown property '%s' on annotation '%s'.", $name, get_class($this))
|
||||
throw new BadMethodCallException(
|
||||
sprintf("Unknown property '%s' on annotation '%s'.", $name, static::class)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,47 +1,21 @@
|
|||
<?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\Annotations\Annotation;
|
||||
|
||||
/**
|
||||
* Annotation that can be used to signal to the parser
|
||||
* to check the attribute type during the parsing process.
|
||||
*
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
final class Attribute
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
/** @var string */
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
/** @var string */
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
/** @var bool */
|
||||
public $required = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,37 +1,15 @@
|
|||
<?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\Annotations\Annotation;
|
||||
|
||||
/**
|
||||
* Annotation that can be used to signal to the parser
|
||||
* to check the types of all declared attributes during the parsing process.
|
||||
*
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
final class Attributes
|
||||
{
|
||||
/**
|
||||
* @var array<Doctrine\Common\Annotations\Annotation\Attribute>
|
||||
*/
|
||||
/** @var array<Attribute> */
|
||||
public $value;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +1,20 @@
|
|||
<?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\Annotations\Annotation;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
use function get_class;
|
||||
use function gettype;
|
||||
use function in_array;
|
||||
use function is_object;
|
||||
use function is_scalar;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Annotation that can be used to signal to the parser
|
||||
* to check the available values during the parsing process.
|
||||
*
|
||||
* @since 2.4
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
*
|
||||
* @Annotation
|
||||
* @Attributes({
|
||||
* @Attribute("value", required = true, type = "array"),
|
||||
|
|
@ -35,34 +23,30 @@ namespace Doctrine\Common\Annotations\Annotation;
|
|||
*/
|
||||
final class Enum
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
/** @phpstan-var list<scalar> */
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* Literal target declaration.
|
||||
*
|
||||
* @var array
|
||||
* @var mixed[]
|
||||
*/
|
||||
public $literal;
|
||||
|
||||
/**
|
||||
* Annotation constructor.
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
* @param array $values
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @phpstan-param array{literal?: mixed[], value: list<scalar>} $values
|
||||
*/
|
||||
public function __construct(array $values)
|
||||
{
|
||||
if ( ! isset($values['literal'])) {
|
||||
$values['literal'] = array();
|
||||
if (! isset($values['literal'])) {
|
||||
$values['literal'] = [];
|
||||
}
|
||||
|
||||
foreach ($values['value'] as $var) {
|
||||
if( ! is_scalar($var)) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
if (! is_scalar($var)) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'@Enum supports only scalar values "%s" given.',
|
||||
is_object($var) ? get_class($var) : gettype($var)
|
||||
));
|
||||
|
|
@ -70,15 +54,16 @@ final class Enum
|
|||
}
|
||||
|
||||
foreach ($values['literal'] as $key => $var) {
|
||||
if( ! in_array($key, $values['value'])) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
if (! in_array($key, $values['value'])) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Undefined enumerator value "%s" for literal "%s".',
|
||||
$key , $var
|
||||
$key,
|
||||
$var
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
$this->value = $values['value'];
|
||||
$this->literal = $values['literal'];
|
||||
$this->value = $values['value'];
|
||||
$this->literal = $values['literal'];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,52 +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\Annotations\Annotation;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
use function json_encode;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Annotation that can be used to signal to the parser to ignore specific
|
||||
* annotations during the parsing process.
|
||||
*
|
||||
* @Annotation
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
final class IgnoreAnnotation
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
/** @phpstan-var list<string> */
|
||||
public $names;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @throws RuntimeException
|
||||
*
|
||||
* @param array $values
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
* @phpstan-param array{value: string|list<string>} $values
|
||||
*/
|
||||
public function __construct(array $values)
|
||||
{
|
||||
if (is_string($values['value'])) {
|
||||
$values['value'] = array($values['value']);
|
||||
$values['value'] = [$values['value']];
|
||||
}
|
||||
if (!is_array($values['value'])) {
|
||||
throw new \RuntimeException(sprintf('@IgnoreAnnotation expects either a string name, or an array of strings, but got %s.', json_encode($values['value'])));
|
||||
|
||||
if (! is_array($values['value'])) {
|
||||
throw new RuntimeException(sprintf(
|
||||
'@IgnoreAnnotation expects either a string name, or an array of strings, but got %s.',
|
||||
json_encode($values['value'])
|
||||
));
|
||||
}
|
||||
|
||||
$this->names = $values['value'];
|
||||
|
|
|
|||
|
|
@ -1,31 +1,11 @@
|
|||
<?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\Annotations\Annotation;
|
||||
|
||||
/**
|
||||
* Annotation that can be used to signal to the parser
|
||||
* to check if that attribute is required during the parsing process.
|
||||
*
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
final class Required
|
||||
|
|
|
|||
|
|
@ -1,89 +1,79 @@
|
|||
<?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\Annotations\Annotation;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
use function array_keys;
|
||||
use function get_class;
|
||||
use function gettype;
|
||||
use function implode;
|
||||
use function is_array;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Annotation that can be used to signal to the parser
|
||||
* to check the annotation target during the parsing process.
|
||||
*
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
final class Target
|
||||
{
|
||||
const TARGET_CLASS = 1;
|
||||
const TARGET_METHOD = 2;
|
||||
const TARGET_PROPERTY = 4;
|
||||
const TARGET_ANNOTATION = 8;
|
||||
const TARGET_ALL = 15;
|
||||
public const TARGET_CLASS = 1;
|
||||
public const TARGET_METHOD = 2;
|
||||
public const TARGET_PROPERTY = 4;
|
||||
public const TARGET_ANNOTATION = 8;
|
||||
public const TARGET_FUNCTION = 16;
|
||||
public const TARGET_ALL = 31;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $map = array(
|
||||
/** @var array<string, int> */
|
||||
private static $map = [
|
||||
'ALL' => self::TARGET_ALL,
|
||||
'CLASS' => self::TARGET_CLASS,
|
||||
'METHOD' => self::TARGET_METHOD,
|
||||
'PROPERTY' => self::TARGET_PROPERTY,
|
||||
'FUNCTION' => self::TARGET_FUNCTION,
|
||||
'ANNOTATION' => self::TARGET_ANNOTATION,
|
||||
);
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
/** @phpstan-var list<string> */
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* Targets as bitmask.
|
||||
*
|
||||
* @var integer
|
||||
* @var int
|
||||
*/
|
||||
public $targets;
|
||||
|
||||
/**
|
||||
* Literal target declaration.
|
||||
*
|
||||
* @var integer
|
||||
* @var string
|
||||
*/
|
||||
public $literal;
|
||||
|
||||
/**
|
||||
* Annotation constructor.
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
* @param array $values
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @phpstan-param array{value?: string|list<string>} $values
|
||||
*/
|
||||
public function __construct(array $values)
|
||||
{
|
||||
if (!isset($values['value'])){
|
||||
if (! isset($values['value'])) {
|
||||
$values['value'] = null;
|
||||
}
|
||||
if (is_string($values['value'])){
|
||||
$values['value'] = array($values['value']);
|
||||
|
||||
if (is_string($values['value'])) {
|
||||
$values['value'] = [$values['value']];
|
||||
}
|
||||
if (!is_array($values['value'])){
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf('@Target expects either a string value, or an array of strings, "%s" given.',
|
||||
|
||||
if (! is_array($values['value'])) {
|
||||
throw new InvalidArgumentException(
|
||||
sprintf(
|
||||
'@Target expects either a string value, or an array of strings, "%s" given.',
|
||||
is_object($values['value']) ? get_class($values['value']) : gettype($values['value'])
|
||||
)
|
||||
);
|
||||
|
|
@ -91,17 +81,21 @@ final class Target
|
|||
|
||||
$bitmask = 0;
|
||||
foreach ($values['value'] as $literal) {
|
||||
if(!isset(self::$map[$literal])){
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf('Invalid Target "%s". Available targets: [%s]',
|
||||
$literal, implode(', ', array_keys(self::$map)))
|
||||
if (! isset(self::$map[$literal])) {
|
||||
throw new InvalidArgumentException(
|
||||
sprintf(
|
||||
'Invalid Target "%s". Available targets: [%s]',
|
||||
$literal,
|
||||
implode(', ', array_keys(self::$map))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$bitmask |= self::$map[$literal];
|
||||
}
|
||||
|
||||
$this->targets = $bitmask;
|
||||
$this->value = $values['value'];
|
||||
$this->literal = implode(', ', $this->value);
|
||||
$this->targets = $bitmask;
|
||||
$this->value = $values['value'];
|
||||
$this->literal = implode(', ', $this->value);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +1,19 @@
|
|||
<?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\Annotations;
|
||||
|
||||
use Exception;
|
||||
|
||||
use function get_class;
|
||||
use function gettype;
|
||||
use function implode;
|
||||
use function is_object;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Description of AnnotationException
|
||||
*
|
||||
* @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>
|
||||
*/
|
||||
class AnnotationException extends \Exception
|
||||
class AnnotationException extends Exception
|
||||
{
|
||||
/**
|
||||
* Creates a new AnnotationException describing a Syntax error.
|
||||
|
|
@ -58,8 +43,6 @@ class AnnotationException extends \Exception
|
|||
* Creates a new AnnotationException describing an error which occurred during
|
||||
* the creation of the annotation.
|
||||
*
|
||||
* @since 2.2
|
||||
*
|
||||
* @param string $message
|
||||
*
|
||||
* @return AnnotationException
|
||||
|
|
@ -72,8 +55,6 @@ class AnnotationException extends \Exception
|
|||
/**
|
||||
* Creates a new AnnotationException describing a type error.
|
||||
*
|
||||
* @since 1.1
|
||||
*
|
||||
* @param string $message
|
||||
*
|
||||
* @return AnnotationException
|
||||
|
|
@ -86,8 +67,6 @@ class AnnotationException extends \Exception
|
|||
/**
|
||||
* Creates a new AnnotationException describing a constant semantical error.
|
||||
*
|
||||
* @since 2.3
|
||||
*
|
||||
* @param string $identifier
|
||||
* @param string $context
|
||||
*
|
||||
|
|
@ -105,8 +84,6 @@ class AnnotationException extends \Exception
|
|||
/**
|
||||
* Creates a new AnnotationException describing an type error of an attribute.
|
||||
*
|
||||
* @since 2.2
|
||||
*
|
||||
* @param string $attributeName
|
||||
* @param string $annotationName
|
||||
* @param string $context
|
||||
|
|
@ -130,8 +107,6 @@ class AnnotationException extends \Exception
|
|||
/**
|
||||
* Creates a new AnnotationException describing an required error of an attribute.
|
||||
*
|
||||
* @since 2.2
|
||||
*
|
||||
* @param string $attributeName
|
||||
* @param string $annotationName
|
||||
* @param string $context
|
||||
|
|
@ -153,21 +128,20 @@ class AnnotationException extends \Exception
|
|||
/**
|
||||
* Creates a new AnnotationException describing a invalid enummerator.
|
||||
*
|
||||
* @since 2.4
|
||||
*
|
||||
* @param string $attributeName
|
||||
* @param string $annotationName
|
||||
* @param string $context
|
||||
* @param array $available
|
||||
* @param mixed $given
|
||||
*
|
||||
* @return AnnotationException
|
||||
*
|
||||
* @phpstan-param list<string> $available
|
||||
*/
|
||||
public static function enumeratorError($attributeName, $annotationName, $context, $available, $given)
|
||||
{
|
||||
return new self(sprintf(
|
||||
'[Enum Error] Attribute "%s" of @%s declared on %s accept only [%s], but got %s.',
|
||||
$attributeName,
|
||||
'[Enum Error] Attribute "%s" of @%s declared on %s accepts only [%s], but got %s.',
|
||||
$attributeName,
|
||||
$annotationName,
|
||||
$context,
|
||||
implode(', ', $available),
|
||||
|
|
@ -181,7 +155,7 @@ class AnnotationException extends \Exception
|
|||
public static function optimizerPlusSaveComments()
|
||||
{
|
||||
return new self(
|
||||
"You have to enable opcache.save_comments=1 or zend_optimizerplus.save_comments=1."
|
||||
'You have to enable opcache.save_comments=1 or zend_optimizerplus.save_comments=1.'
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -191,7 +165,7 @@ class AnnotationException extends \Exception
|
|||
public static function optimizerPlusLoadComments()
|
||||
{
|
||||
return new self(
|
||||
"You have to enable opcache.load_comments=1 or zend_optimizerplus.load_comments=1."
|
||||
'You have to enable opcache.load_comments=1 or zend_optimizerplus.load_comments=1.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,122 +1,57 @@
|
|||
<?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\Annotations;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\IgnoreAnnotation;
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
use ReflectionClass;
|
||||
use ReflectionFunction;
|
||||
use ReflectionMethod;
|
||||
use ReflectionProperty;
|
||||
|
||||
use function array_merge;
|
||||
use function class_exists;
|
||||
use function extension_loaded;
|
||||
use function ini_get;
|
||||
|
||||
/**
|
||||
* A reader for docblock annotations.
|
||||
*
|
||||
* @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 Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
class AnnotationReader implements Reader
|
||||
{
|
||||
/**
|
||||
* Global map for imports.
|
||||
*
|
||||
* @var array
|
||||
* @var array<string, class-string>
|
||||
*/
|
||||
private static $globalImports = array(
|
||||
'ignoreannotation' => 'Doctrine\Common\Annotations\Annotation\IgnoreAnnotation',
|
||||
);
|
||||
private static $globalImports = [
|
||||
'ignoreannotation' => Annotation\IgnoreAnnotation::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* A list with annotations that are not causing exceptions when not resolved to an annotation class.
|
||||
*
|
||||
* The names are case sensitive.
|
||||
*
|
||||
* @var array
|
||||
* @var array<string, true>
|
||||
*/
|
||||
private static $globalIgnoredNames = array(
|
||||
// Annotation tags
|
||||
'Annotation' => true, 'Attribute' => true, 'Attributes' => true,
|
||||
/* Can we enable this? 'Enum' => true, */
|
||||
'Required' => true,
|
||||
'Target' => true,
|
||||
// Widely used tags (but not existent in phpdoc)
|
||||
'fix' => true , 'fixme' => true,
|
||||
'override' => true,
|
||||
// PHPDocumentor 1 tags
|
||||
'abstract'=> true, 'access'=> true,
|
||||
'code' => true,
|
||||
'deprec'=> true,
|
||||
'endcode' => true, 'exception'=> true,
|
||||
'final'=> true,
|
||||
'ingroup' => true, 'inheritdoc'=> true, 'inheritDoc'=> true,
|
||||
'magic' => true,
|
||||
'name'=> true,
|
||||
'toc' => true, 'tutorial'=> true,
|
||||
'private' => true,
|
||||
'static'=> true, 'staticvar'=> true, 'staticVar'=> true,
|
||||
'throw' => true,
|
||||
// PHPDocumentor 2 tags.
|
||||
'api' => true, 'author'=> true,
|
||||
'category'=> true, 'copyright'=> true,
|
||||
'deprecated'=> true,
|
||||
'example'=> true,
|
||||
'filesource'=> true,
|
||||
'global'=> true,
|
||||
'ignore'=> true, /* Can we enable this? 'index' => true, */ 'internal'=> true,
|
||||
'license'=> true, 'link'=> true,
|
||||
'method' => true,
|
||||
'package'=> true, 'param'=> true, 'property' => true, 'property-read' => true, 'property-write' => true,
|
||||
'return'=> true,
|
||||
'see'=> true, 'since'=> true, 'source' => true, 'subpackage'=> true,
|
||||
'throws'=> true, 'todo'=> true, 'TODO'=> true,
|
||||
'usedby'=> true, 'uses' => true,
|
||||
'var'=> true, 'version'=> true,
|
||||
// PHPUnit tags
|
||||
'codeCoverageIgnore' => true, 'codeCoverageIgnoreStart' => true, 'codeCoverageIgnoreEnd' => true,
|
||||
// PHPCheckStyle
|
||||
'SuppressWarnings' => true,
|
||||
// PHPStorm
|
||||
'noinspection' => true,
|
||||
// PEAR
|
||||
'package_version' => true,
|
||||
// PlantUML
|
||||
'startuml' => true, 'enduml' => true,
|
||||
);
|
||||
private static $globalIgnoredNames = ImplicitlyIgnoredAnnotationNames::LIST;
|
||||
|
||||
/**
|
||||
* A list with annotations that are not causing exceptions when not resolved to an annotation class.
|
||||
*
|
||||
* The names are case sensitive.
|
||||
*
|
||||
* @var array
|
||||
* @var array<string, true>
|
||||
*/
|
||||
private static $globalIgnoredNamespaces = array();
|
||||
private static $globalIgnoredNamespaces = [];
|
||||
|
||||
/**
|
||||
* Add a new annotation to the globally ignored annotation names with regard to exception handling.
|
||||
*
|
||||
* @param string $name
|
||||
*/
|
||||
static public function addGlobalIgnoredName($name)
|
||||
public static function addGlobalIgnoredName($name)
|
||||
{
|
||||
self::$globalIgnoredNames[$name] = true;
|
||||
}
|
||||
|
|
@ -126,7 +61,7 @@ class AnnotationReader implements Reader
|
|||
*
|
||||
* @param string $namespace
|
||||
*/
|
||||
static public function addGlobalIgnoredNamespace($namespace)
|
||||
public static function addGlobalIgnoredNamespace($namespace)
|
||||
{
|
||||
self::$globalIgnoredNamespaces[$namespace] = true;
|
||||
}
|
||||
|
|
@ -134,75 +69,68 @@ class AnnotationReader implements Reader
|
|||
/**
|
||||
* Annotations parser.
|
||||
*
|
||||
* @var \Doctrine\Common\Annotations\DocParser
|
||||
* @var DocParser
|
||||
*/
|
||||
private $parser;
|
||||
|
||||
/**
|
||||
* Annotations parser used to collect parsing metadata.
|
||||
*
|
||||
* @var \Doctrine\Common\Annotations\DocParser
|
||||
* @var DocParser
|
||||
*/
|
||||
private $preParser;
|
||||
|
||||
/**
|
||||
* PHP parser used to collect imports.
|
||||
*
|
||||
* @var \Doctrine\Common\Annotations\PhpParser
|
||||
* @var PhpParser
|
||||
*/
|
||||
private $phpParser;
|
||||
|
||||
/**
|
||||
* In-memory cache mechanism to store imported annotations per class.
|
||||
*
|
||||
* @var array
|
||||
* @psalm-var array<'class'|'function', array<string, array<string, class-string>>>
|
||||
*/
|
||||
private $imports = array();
|
||||
private $imports = [];
|
||||
|
||||
/**
|
||||
* In-memory cache mechanism to store ignored annotations per class.
|
||||
*
|
||||
* @var array
|
||||
* @psalm-var array<'class'|'function', array<string, array<string, true>>>
|
||||
*/
|
||||
private $ignoredAnnotationNames = array();
|
||||
private $ignoredAnnotationNames = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* Initializes a new AnnotationReader.
|
||||
*
|
||||
* @param DocParser $parser
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
public function __construct(DocParser $parser = null)
|
||||
public function __construct(?DocParser $parser = null)
|
||||
{
|
||||
if (extension_loaded('Zend Optimizer+') && (ini_get('zend_optimizerplus.save_comments') === "0" || ini_get('opcache.save_comments') === "0")) {
|
||||
if (
|
||||
extension_loaded('Zend Optimizer+') && (ini_get('zend_optimizerplus.save_comments') === '0' ||
|
||||
ini_get('opcache.save_comments') === '0')
|
||||
) {
|
||||
throw AnnotationException::optimizerPlusSaveComments();
|
||||
}
|
||||
|
||||
if (extension_loaded('Zend OPcache') && ini_get('opcache.save_comments') == 0) {
|
||||
if (extension_loaded('Zend OPcache') && ini_get('opcache.save_comments') === 0) {
|
||||
throw AnnotationException::optimizerPlusSaveComments();
|
||||
}
|
||||
|
||||
if (PHP_VERSION_ID < 70000) {
|
||||
if (extension_loaded('Zend Optimizer+') && (ini_get('zend_optimizerplus.load_comments') === "0" || ini_get('opcache.load_comments') === "0")) {
|
||||
throw AnnotationException::optimizerPlusLoadComments();
|
||||
}
|
||||
|
||||
if (extension_loaded('Zend OPcache') && ini_get('opcache.load_comments') == 0) {
|
||||
throw AnnotationException::optimizerPlusLoadComments();
|
||||
}
|
||||
}
|
||||
|
||||
AnnotationRegistry::registerFile(__DIR__ . '/Annotation/IgnoreAnnotation.php');
|
||||
// Make sure that the IgnoreAnnotation annotation is loaded
|
||||
class_exists(IgnoreAnnotation::class);
|
||||
|
||||
$this->parser = $parser ?: new DocParser();
|
||||
|
||||
$this->preParser = new DocParser;
|
||||
$this->preParser = new DocParser();
|
||||
|
||||
$this->preParser->setImports(self::$globalImports);
|
||||
$this->preParser->setIgnoreNotImportedAnnotations(true);
|
||||
$this->preParser->setIgnoredAnnotationNames(self::$globalIgnoredNames);
|
||||
|
||||
$this->phpParser = new PhpParser;
|
||||
$this->phpParser = new PhpParser();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -211,7 +139,7 @@ class AnnotationReader implements Reader
|
|||
public function getClassAnnotations(ReflectionClass $class)
|
||||
{
|
||||
$this->parser->setTarget(Target::TARGET_CLASS);
|
||||
$this->parser->setImports($this->getClassImports($class));
|
||||
$this->parser->setImports($this->getImports($class));
|
||||
$this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
|
||||
$this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
|
||||
|
||||
|
|
@ -240,7 +168,7 @@ class AnnotationReader implements Reader
|
|||
public function getPropertyAnnotations(ReflectionProperty $property)
|
||||
{
|
||||
$class = $property->getDeclaringClass();
|
||||
$context = 'property ' . $class->getName() . "::\$" . $property->getName();
|
||||
$context = 'property ' . $class->getName() . '::$' . $property->getName();
|
||||
|
||||
$this->parser->setTarget(Target::TARGET_PROPERTY);
|
||||
$this->parser->setImports($this->getPropertyImports($property));
|
||||
|
|
@ -299,66 +227,103 @@ class AnnotationReader implements Reader
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the ignored annotations for the given class.
|
||||
* Gets the annotations applied to a function.
|
||||
*
|
||||
* @param \ReflectionClass $class
|
||||
*
|
||||
* @return array
|
||||
* @phpstan-return list<object> An array of Annotations.
|
||||
*/
|
||||
private function getIgnoredAnnotationNames(ReflectionClass $class)
|
||||
public function getFunctionAnnotations(ReflectionFunction $function): array
|
||||
{
|
||||
$name = $class->getName();
|
||||
if (isset($this->ignoredAnnotationNames[$name])) {
|
||||
return $this->ignoredAnnotationNames[$name];
|
||||
}
|
||||
$context = 'function ' . $function->getName();
|
||||
|
||||
$this->collectParsingMetadata($class);
|
||||
$this->parser->setTarget(Target::TARGET_FUNCTION);
|
||||
$this->parser->setImports($this->getImports($function));
|
||||
$this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($function));
|
||||
$this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
|
||||
|
||||
return $this->ignoredAnnotationNames[$name];
|
||||
return $this->parser->parse($function->getDocComment(), $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves imports.
|
||||
* Gets a function annotation.
|
||||
*
|
||||
* @param \ReflectionClass $class
|
||||
*
|
||||
* @return array
|
||||
* @return object|null The Annotation or NULL, if the requested annotation does not exist.
|
||||
*/
|
||||
private function getClassImports(ReflectionClass $class)
|
||||
public function getFunctionAnnotation(ReflectionFunction $function, string $annotationName)
|
||||
{
|
||||
$name = $class->getName();
|
||||
if (isset($this->imports[$name])) {
|
||||
return $this->imports[$name];
|
||||
$annotations = $this->getFunctionAnnotations($function);
|
||||
|
||||
foreach ($annotations as $annotation) {
|
||||
if ($annotation instanceof $annotationName) {
|
||||
return $annotation;
|
||||
}
|
||||
}
|
||||
|
||||
$this->collectParsingMetadata($class);
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->imports[$name];
|
||||
/**
|
||||
* Returns the ignored annotations for the given class or function.
|
||||
*
|
||||
* @param ReflectionClass|ReflectionFunction $reflection
|
||||
*
|
||||
* @return array<string, true>
|
||||
*/
|
||||
private function getIgnoredAnnotationNames($reflection): array
|
||||
{
|
||||
$type = $reflection instanceof ReflectionClass ? 'class' : 'function';
|
||||
$name = $reflection->getName();
|
||||
|
||||
if (isset($this->ignoredAnnotationNames[$type][$name])) {
|
||||
return $this->ignoredAnnotationNames[$type][$name];
|
||||
}
|
||||
|
||||
$this->collectParsingMetadata($reflection);
|
||||
|
||||
return $this->ignoredAnnotationNames[$type][$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves imports for a class or a function.
|
||||
*
|
||||
* @param ReflectionClass|ReflectionFunction $reflection
|
||||
*
|
||||
* @return array<string, class-string>
|
||||
*/
|
||||
private function getImports($reflection): array
|
||||
{
|
||||
$type = $reflection instanceof ReflectionClass ? 'class' : 'function';
|
||||
$name = $reflection->getName();
|
||||
|
||||
if (isset($this->imports[$type][$name])) {
|
||||
return $this->imports[$type][$name];
|
||||
}
|
||||
|
||||
$this->collectParsingMetadata($reflection);
|
||||
|
||||
return $this->imports[$type][$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves imports for methods.
|
||||
*
|
||||
* @param \ReflectionMethod $method
|
||||
*
|
||||
* @return array
|
||||
* @return array<string, class-string>
|
||||
*/
|
||||
private function getMethodImports(ReflectionMethod $method)
|
||||
{
|
||||
$class = $method->getDeclaringClass();
|
||||
$classImports = $this->getClassImports($class);
|
||||
if (!method_exists($class, 'getTraits')) {
|
||||
return $classImports;
|
||||
}
|
||||
$class = $method->getDeclaringClass();
|
||||
$classImports = $this->getImports($class);
|
||||
|
||||
$traitImports = array();
|
||||
$traitImports = [];
|
||||
|
||||
foreach ($class->getTraits() as $trait) {
|
||||
if ($trait->hasMethod($method->getName())
|
||||
&& $trait->getFileName() === $method->getFileName()
|
||||
if (
|
||||
! $trait->hasMethod($method->getName())
|
||||
|| $trait->getFileName() !== $method->getFileName()
|
||||
) {
|
||||
$traitImports = array_merge($traitImports, $this->phpParser->parseClass($trait));
|
||||
continue;
|
||||
}
|
||||
|
||||
$traitImports = array_merge($traitImports, $this->phpParser->parseUseStatements($trait));
|
||||
}
|
||||
|
||||
return array_merge($classImports, $traitImports);
|
||||
|
|
@ -367,55 +332,58 @@ class AnnotationReader implements Reader
|
|||
/**
|
||||
* Retrieves imports for properties.
|
||||
*
|
||||
* @param \ReflectionProperty $property
|
||||
*
|
||||
* @return array
|
||||
* @return array<string, class-string>
|
||||
*/
|
||||
private function getPropertyImports(ReflectionProperty $property)
|
||||
{
|
||||
$class = $property->getDeclaringClass();
|
||||
$classImports = $this->getClassImports($class);
|
||||
if (!method_exists($class, 'getTraits')) {
|
||||
return $classImports;
|
||||
}
|
||||
$class = $property->getDeclaringClass();
|
||||
$classImports = $this->getImports($class);
|
||||
|
||||
$traitImports = array();
|
||||
$traitImports = [];
|
||||
|
||||
foreach ($class->getTraits() as $trait) {
|
||||
if ($trait->hasProperty($property->getName())) {
|
||||
$traitImports = array_merge($traitImports, $this->phpParser->parseClass($trait));
|
||||
if (! $trait->hasProperty($property->getName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$traitImports = array_merge($traitImports, $this->phpParser->parseUseStatements($trait));
|
||||
}
|
||||
|
||||
return array_merge($classImports, $traitImports);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects parsing metadata for a given class.
|
||||
* Collects parsing metadata for a given class or function.
|
||||
*
|
||||
* @param \ReflectionClass $class
|
||||
* @param ReflectionClass|ReflectionFunction $reflection
|
||||
*/
|
||||
private function collectParsingMetadata(ReflectionClass $class)
|
||||
private function collectParsingMetadata($reflection): void
|
||||
{
|
||||
$type = $reflection instanceof ReflectionClass ? 'class' : 'function';
|
||||
$name = $reflection->getName();
|
||||
|
||||
$ignoredAnnotationNames = self::$globalIgnoredNames;
|
||||
$annotations = $this->preParser->parse($class->getDocComment(), 'class ' . $class->name);
|
||||
$annotations = $this->preParser->parse($reflection->getDocComment(), $type . ' ' . $name);
|
||||
|
||||
foreach ($annotations as $annotation) {
|
||||
if ($annotation instanceof IgnoreAnnotation) {
|
||||
foreach ($annotation->names AS $annot) {
|
||||
$ignoredAnnotationNames[$annot] = true;
|
||||
}
|
||||
if (! ($annotation instanceof IgnoreAnnotation)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($annotation->names as $annot) {
|
||||
$ignoredAnnotationNames[$annot] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$name = $class->getName();
|
||||
|
||||
$this->imports[$name] = array_merge(
|
||||
$this->imports[$type][$name] = array_merge(
|
||||
self::$globalImports,
|
||||
$this->phpParser->parseClass($class),
|
||||
array('__NAMESPACE__' => $class->getNamespaceName())
|
||||
$this->phpParser->parseUseStatements($reflection),
|
||||
[
|
||||
'__NAMESPACE__' => $reflection->getNamespaceName(),
|
||||
'self' => $name,
|
||||
]
|
||||
);
|
||||
|
||||
$this->ignoredAnnotationNames[$name] = $ignoredAnnotationNames;
|
||||
$this->ignoredAnnotationNames[$type][$name] = $ignoredAnnotationNames;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,18 @@
|
|||
<?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\Annotations;
|
||||
|
||||
/**
|
||||
* AnnotationRegistry.
|
||||
*/
|
||||
use function array_key_exists;
|
||||
use function array_merge;
|
||||
use function class_exists;
|
||||
use function in_array;
|
||||
use function is_file;
|
||||
use function str_replace;
|
||||
use function stream_resolve_include_path;
|
||||
use function strpos;
|
||||
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
|
||||
final class AnnotationRegistry
|
||||
{
|
||||
/**
|
||||
|
|
@ -32,35 +23,49 @@ final class AnnotationRegistry
|
|||
*
|
||||
* This autoloading mechanism does not utilize the PHP autoloading but implements autoloading on its own.
|
||||
*
|
||||
* @var array
|
||||
* @var string[][]|string[]|null[]
|
||||
*/
|
||||
static private $autoloadNamespaces = array();
|
||||
private static $autoloadNamespaces = [];
|
||||
|
||||
/**
|
||||
* A map of autoloader callables.
|
||||
*
|
||||
* @var array
|
||||
* @var callable[]
|
||||
*/
|
||||
static private $loaders = array();
|
||||
private static $loaders = [];
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* An array of classes which cannot be found
|
||||
*
|
||||
* @var null[] indexed by class name
|
||||
*/
|
||||
static public function reset()
|
||||
private static $failedToAutoload = [];
|
||||
|
||||
/**
|
||||
* Whenever registerFile() was used. Disables use of standard autoloader.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private static $registerFileUsed = false;
|
||||
|
||||
public static function reset(): void
|
||||
{
|
||||
self::$autoloadNamespaces = array();
|
||||
self::$loaders = array();
|
||||
self::$autoloadNamespaces = [];
|
||||
self::$loaders = [];
|
||||
self::$failedToAutoload = [];
|
||||
self::$registerFileUsed = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers file.
|
||||
*
|
||||
* @param string $file
|
||||
*
|
||||
* @return void
|
||||
* @deprecated This method is deprecated and will be removed in
|
||||
* doctrine/annotations 2.0. Annotations will be autoloaded in 2.0.
|
||||
*/
|
||||
static public function registerFile($file)
|
||||
public static function registerFile(string $file): void
|
||||
{
|
||||
self::$registerFileUsed = true;
|
||||
|
||||
require_once $file;
|
||||
}
|
||||
|
||||
|
|
@ -69,12 +74,12 @@ final class AnnotationRegistry
|
|||
*
|
||||
* Loading of this namespaces will be done with a PSR-0 namespace loading algorithm.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @param string|array|null $dirs
|
||||
* @deprecated This method is deprecated and will be removed in
|
||||
* doctrine/annotations 2.0. Annotations will be autoloaded in 2.0.
|
||||
*
|
||||
* @return void
|
||||
* @phpstan-param string|list<string>|null $dirs
|
||||
*/
|
||||
static public function registerAutoloadNamespace($namespace, $dirs = null)
|
||||
public static function registerAutoloadNamespace(string $namespace, $dirs = null): void
|
||||
{
|
||||
self::$autoloadNamespaces[$namespace] = $dirs;
|
||||
}
|
||||
|
|
@ -84,11 +89,12 @@ final class AnnotationRegistry
|
|||
*
|
||||
* Loading of this namespaces will be done with a PSR-0 namespace loading algorithm.
|
||||
*
|
||||
* @param array $namespaces
|
||||
* @deprecated This method is deprecated and will be removed in
|
||||
* doctrine/annotations 2.0. Annotations will be autoloaded in 2.0.
|
||||
*
|
||||
* @return void
|
||||
* @param string[][]|string[]|null[] $namespaces indexed by namespace name
|
||||
*/
|
||||
static public function registerAutoloadNamespaces(array $namespaces)
|
||||
public static function registerAutoloadNamespaces(array $namespaces): void
|
||||
{
|
||||
self::$autoloadNamespaces = array_merge(self::$autoloadNamespaces, $namespaces);
|
||||
}
|
||||
|
|
@ -99,53 +105,86 @@ final class AnnotationRegistry
|
|||
* NOTE: These class loaders HAVE to be silent when a class was not found!
|
||||
* IMPORTANT: Loaders have to return true if they loaded a class that could contain the searched annotation class.
|
||||
*
|
||||
* @param callable $callable
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @deprecated This method is deprecated and will be removed in
|
||||
* doctrine/annotations 2.0. Annotations will be autoloaded in 2.0.
|
||||
*/
|
||||
static public function registerLoader($callable)
|
||||
public static function registerLoader(callable $callable): void
|
||||
{
|
||||
if (!is_callable($callable)) {
|
||||
throw new \InvalidArgumentException("A callable is expected in AnnotationRegistry::registerLoader().");
|
||||
// Reset our static cache now that we have a new loader to work with
|
||||
self::$failedToAutoload = [];
|
||||
self::$loaders[] = $callable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an autoloading callable for annotations, if it is not already registered
|
||||
*
|
||||
* @deprecated This method is deprecated and will be removed in
|
||||
* doctrine/annotations 2.0. Annotations will be autoloaded in 2.0.
|
||||
*/
|
||||
public static function registerUniqueLoader(callable $callable): void
|
||||
{
|
||||
if (in_array($callable, self::$loaders, true)) {
|
||||
return;
|
||||
}
|
||||
self::$loaders[] = $callable;
|
||||
|
||||
self::registerLoader($callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Autoloads an annotation class silently.
|
||||
*
|
||||
* @param string $class
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
static public function loadAnnotationClass($class)
|
||||
public static function loadAnnotationClass(string $class): bool
|
||||
{
|
||||
foreach (self::$autoloadNamespaces AS $namespace => $dirs) {
|
||||
if (strpos($class, $namespace) === 0) {
|
||||
$file = str_replace("\\", DIRECTORY_SEPARATOR, $class) . ".php";
|
||||
if ($dirs === null) {
|
||||
if ($path = stream_resolve_include_path($file)) {
|
||||
require $path;
|
||||
if (class_exists($class, false)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (array_key_exists($class, self::$failedToAutoload)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (self::$autoloadNamespaces as $namespace => $dirs) {
|
||||
if (strpos($class, $namespace) !== 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$file = str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
|
||||
|
||||
if ($dirs === null) {
|
||||
$path = stream_resolve_include_path($file);
|
||||
if ($path) {
|
||||
require $path;
|
||||
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
foreach ((array) $dirs as $dir) {
|
||||
if (is_file($dir . DIRECTORY_SEPARATOR . $file)) {
|
||||
require $dir . DIRECTORY_SEPARATOR . $file;
|
||||
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
foreach((array)$dirs AS $dir) {
|
||||
if (is_file($dir . DIRECTORY_SEPARATOR . $file)) {
|
||||
require $dir . DIRECTORY_SEPARATOR . $file;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (self::$loaders AS $loader) {
|
||||
if (call_user_func($loader, $class) === true) {
|
||||
foreach (self::$loaders as $loader) {
|
||||
if ($loader($class) === true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
self::$loaders === [] &&
|
||||
self::$autoloadNamespaces === [] &&
|
||||
self::$registerFileUsed === false &&
|
||||
class_exists($class)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
self::$failedToAutoload[$class] = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,67 +1,47 @@
|
|||
<?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\Annotations;
|
||||
|
||||
use Doctrine\Common\Cache\Cache;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
use ReflectionProperty;
|
||||
|
||||
use function array_map;
|
||||
use function array_merge;
|
||||
use function assert;
|
||||
use function filemtime;
|
||||
use function max;
|
||||
use function time;
|
||||
|
||||
/**
|
||||
* A cache aware annotation reader.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
final class CachedReader implements Reader
|
||||
{
|
||||
/**
|
||||
* @var Reader
|
||||
*/
|
||||
/** @var Reader */
|
||||
private $delegate;
|
||||
|
||||
/**
|
||||
* @var Cache
|
||||
*/
|
||||
/** @var Cache */
|
||||
private $cache;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
/** @var bool */
|
||||
private $debug;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $loadedAnnotations = array();
|
||||
/** @var array<string, array<object>> */
|
||||
private $loadedAnnotations = [];
|
||||
|
||||
/** @var int[] */
|
||||
private $loadedFilemtimes = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Reader $reader
|
||||
* @param Cache $cache
|
||||
* @param bool $debug
|
||||
* @param bool $debug
|
||||
*/
|
||||
public function __construct(Reader $reader, Cache $cache, $debug = false)
|
||||
{
|
||||
$this->delegate = $reader;
|
||||
$this->cache = $cache;
|
||||
$this->debug = (boolean) $debug;
|
||||
$this->cache = $cache;
|
||||
$this->debug = (bool) $debug;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -75,7 +55,8 @@ final class CachedReader implements Reader
|
|||
return $this->loadedAnnotations[$cacheKey];
|
||||
}
|
||||
|
||||
if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) {
|
||||
$annots = $this->fetchFromCache($cacheKey, $class);
|
||||
if ($annots === false) {
|
||||
$annots = $this->delegate->getClassAnnotations($class);
|
||||
$this->saveToCache($cacheKey, $annots);
|
||||
}
|
||||
|
|
@ -100,16 +81,17 @@ final class CachedReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getPropertyAnnotations(\ReflectionProperty $property)
|
||||
public function getPropertyAnnotations(ReflectionProperty $property)
|
||||
{
|
||||
$class = $property->getDeclaringClass();
|
||||
$cacheKey = $class->getName().'$'.$property->getName();
|
||||
$class = $property->getDeclaringClass();
|
||||
$cacheKey = $class->getName() . '$' . $property->getName();
|
||||
|
||||
if (isset($this->loadedAnnotations[$cacheKey])) {
|
||||
return $this->loadedAnnotations[$cacheKey];
|
||||
}
|
||||
|
||||
if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) {
|
||||
$annots = $this->fetchFromCache($cacheKey, $class);
|
||||
if ($annots === false) {
|
||||
$annots = $this->delegate->getPropertyAnnotations($property);
|
||||
$this->saveToCache($cacheKey, $annots);
|
||||
}
|
||||
|
|
@ -120,7 +102,7 @@ final class CachedReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName)
|
||||
public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
|
||||
{
|
||||
foreach ($this->getPropertyAnnotations($property) as $annot) {
|
||||
if ($annot instanceof $annotationName) {
|
||||
|
|
@ -134,16 +116,17 @@ final class CachedReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getMethodAnnotations(\ReflectionMethod $method)
|
||||
public function getMethodAnnotations(ReflectionMethod $method)
|
||||
{
|
||||
$class = $method->getDeclaringClass();
|
||||
$cacheKey = $class->getName().'#'.$method->getName();
|
||||
$class = $method->getDeclaringClass();
|
||||
$cacheKey = $class->getName() . '#' . $method->getName();
|
||||
|
||||
if (isset($this->loadedAnnotations[$cacheKey])) {
|
||||
return $this->loadedAnnotations[$cacheKey];
|
||||
}
|
||||
|
||||
if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) {
|
||||
$annots = $this->fetchFromCache($cacheKey, $class);
|
||||
if ($annots === false) {
|
||||
$annots = $this->delegate->getMethodAnnotations($method);
|
||||
$this->saveToCache($cacheKey, $annots);
|
||||
}
|
||||
|
|
@ -154,7 +137,7 @@ final class CachedReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getMethodAnnotation(\ReflectionMethod $method, $annotationName)
|
||||
public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
|
||||
{
|
||||
foreach ($this->getMethodAnnotations($method) as $annot) {
|
||||
if ($annot instanceof $annotationName) {
|
||||
|
|
@ -172,21 +155,22 @@ final class CachedReader implements Reader
|
|||
*/
|
||||
public function clearLoadedAnnotations()
|
||||
{
|
||||
$this->loadedAnnotations = array();
|
||||
$this->loadedAnnotations = [];
|
||||
$this->loadedFilemtimes = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a value from the cache.
|
||||
*
|
||||
* @param string $cacheKey The cache key.
|
||||
* @param ReflectionClass $class The related class.
|
||||
* @param string $cacheKey The cache key.
|
||||
*
|
||||
* @return mixed The cached value or false when the value is not in cache.
|
||||
*/
|
||||
private function fetchFromCache($cacheKey, ReflectionClass $class)
|
||||
{
|
||||
if (($data = $this->cache->fetch($cacheKey)) !== false) {
|
||||
if (!$this->debug || $this->isCacheFresh($cacheKey, $class)) {
|
||||
$data = $this->cache->fetch($cacheKey);
|
||||
if ($data !== false) {
|
||||
if (! $this->debug || $this->isCacheFresh($cacheKey, $class)) {
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
|
@ -205,58 +189,76 @@ final class CachedReader implements Reader
|
|||
private function saveToCache($cacheKey, $value)
|
||||
{
|
||||
$this->cache->save($cacheKey, $value);
|
||||
if ($this->debug) {
|
||||
$this->cache->save('[C]'.$cacheKey, time());
|
||||
if (! $this->debug) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->cache->save('[C]' . $cacheKey, time());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the cache is fresh.
|
||||
*
|
||||
* @param string $cacheKey
|
||||
* @param ReflectionClass $class
|
||||
* @param string $cacheKey
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
*/
|
||||
private function isCacheFresh($cacheKey, ReflectionClass $class)
|
||||
{
|
||||
if (null === $lastModification = $this->getLastModification($class)) {
|
||||
$lastModification = $this->getLastModification($class);
|
||||
if ($lastModification === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->cache->fetch('[C]'.$cacheKey) >= $lastModification;
|
||||
return $this->cache->fetch('[C]' . $cacheKey) >= $lastModification;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time the class was last modified, testing traits and parents
|
||||
*
|
||||
* @param ReflectionClass $class
|
||||
* @return int
|
||||
*/
|
||||
private function getLastModification(ReflectionClass $class)
|
||||
private function getLastModification(ReflectionClass $class): int
|
||||
{
|
||||
$filename = $class->getFileName();
|
||||
$parent = $class->getParentClass();
|
||||
|
||||
return max(array_merge(
|
||||
if (isset($this->loadedFilemtimes[$filename])) {
|
||||
return $this->loadedFilemtimes[$filename];
|
||||
}
|
||||
|
||||
$parent = $class->getParentClass();
|
||||
|
||||
$lastModification = max(array_merge(
|
||||
[$filename ? filemtime($filename) : 0],
|
||||
array_map([$this, 'getTraitLastModificationTime'], $class->getTraits()),
|
||||
array_map([$this, 'getLastModification'], $class->getInterfaces()),
|
||||
array_map(function (ReflectionClass $reflectionTrait): int {
|
||||
return $this->getTraitLastModificationTime($reflectionTrait);
|
||||
}, $class->getTraits()),
|
||||
array_map(function (ReflectionClass $class): int {
|
||||
return $this->getLastModification($class);
|
||||
}, $class->getInterfaces()),
|
||||
$parent ? [$this->getLastModification($parent)] : []
|
||||
));
|
||||
|
||||
assert($lastModification !== false);
|
||||
|
||||
return $this->loadedFilemtimes[$filename] = $lastModification;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ReflectionClass $reflectionTrait
|
||||
* @return int
|
||||
*/
|
||||
private function getTraitLastModificationTime(ReflectionClass $reflectionTrait)
|
||||
private function getTraitLastModificationTime(ReflectionClass $reflectionTrait): int
|
||||
{
|
||||
$fileName = $reflectionTrait->getFileName();
|
||||
|
||||
return max(array_merge(
|
||||
if (isset($this->loadedFilemtimes[$fileName])) {
|
||||
return $this->loadedFilemtimes[$fileName];
|
||||
}
|
||||
|
||||
$lastModificationTime = max(array_merge(
|
||||
[$fileName ? filemtime($fileName) : 0],
|
||||
array_map([$this, 'getTraitLastModificationTime'], $reflectionTrait->getTraits())
|
||||
array_map(function (ReflectionClass $reflectionTrait): int {
|
||||
return $this->getTraitLastModificationTime($reflectionTrait);
|
||||
}, $reflectionTrait->getTraits())
|
||||
));
|
||||
|
||||
assert($lastModificationTime !== false);
|
||||
|
||||
return $this->loadedFilemtimes[$fileName] = $lastModificationTime;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,61 +1,46 @@
|
|||
<?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\Annotations;
|
||||
|
||||
use Doctrine\Common\Lexer\AbstractLexer;
|
||||
|
||||
use function ctype_alpha;
|
||||
use function is_numeric;
|
||||
use function str_replace;
|
||||
use function stripos;
|
||||
use function strlen;
|
||||
use function strpos;
|
||||
use function strtolower;
|
||||
use function substr;
|
||||
|
||||
/**
|
||||
* Simple lexer for docblock annotations.
|
||||
*
|
||||
* @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 Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
final class DocLexer extends AbstractLexer
|
||||
{
|
||||
const T_NONE = 1;
|
||||
const T_INTEGER = 2;
|
||||
const T_STRING = 3;
|
||||
const T_FLOAT = 4;
|
||||
public const T_NONE = 1;
|
||||
public const T_INTEGER = 2;
|
||||
public const T_STRING = 3;
|
||||
public const T_FLOAT = 4;
|
||||
|
||||
// All tokens that are also identifiers should be >= 100
|
||||
const T_IDENTIFIER = 100;
|
||||
const T_AT = 101;
|
||||
const T_CLOSE_CURLY_BRACES = 102;
|
||||
const T_CLOSE_PARENTHESIS = 103;
|
||||
const T_COMMA = 104;
|
||||
const T_EQUALS = 105;
|
||||
const T_FALSE = 106;
|
||||
const T_NAMESPACE_SEPARATOR = 107;
|
||||
const T_OPEN_CURLY_BRACES = 108;
|
||||
const T_OPEN_PARENTHESIS = 109;
|
||||
const T_TRUE = 110;
|
||||
const T_NULL = 111;
|
||||
const T_COLON = 112;
|
||||
public const T_IDENTIFIER = 100;
|
||||
public const T_AT = 101;
|
||||
public const T_CLOSE_CURLY_BRACES = 102;
|
||||
public const T_CLOSE_PARENTHESIS = 103;
|
||||
public const T_COMMA = 104;
|
||||
public const T_EQUALS = 105;
|
||||
public const T_FALSE = 106;
|
||||
public const T_NAMESPACE_SEPARATOR = 107;
|
||||
public const T_OPEN_CURLY_BRACES = 108;
|
||||
public const T_OPEN_PARENTHESIS = 109;
|
||||
public const T_TRUE = 110;
|
||||
public const T_NULL = 111;
|
||||
public const T_COLON = 112;
|
||||
public const T_MINUS = 113;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $noCase = array(
|
||||
/** @var array<string, int> */
|
||||
protected $noCase = [
|
||||
'@' => self::T_AT,
|
||||
',' => self::T_COMMA,
|
||||
'(' => self::T_OPEN_PARENTHESIS,
|
||||
|
|
@ -64,28 +49,38 @@ final class DocLexer extends AbstractLexer
|
|||
'}' => self::T_CLOSE_CURLY_BRACES,
|
||||
'=' => self::T_EQUALS,
|
||||
':' => self::T_COLON,
|
||||
'\\' => self::T_NAMESPACE_SEPARATOR
|
||||
);
|
||||
'-' => self::T_MINUS,
|
||||
'\\' => self::T_NAMESPACE_SEPARATOR,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $withCase = array(
|
||||
/** @var array<string, int> */
|
||||
protected $withCase = [
|
||||
'true' => self::T_TRUE,
|
||||
'false' => self::T_FALSE,
|
||||
'null' => self::T_NULL
|
||||
);
|
||||
'null' => self::T_NULL,
|
||||
];
|
||||
|
||||
/**
|
||||
* Whether the next token starts immediately, or if there were
|
||||
* non-captured symbols before that
|
||||
*/
|
||||
public function nextTokenIsAdjacent(): bool
|
||||
{
|
||||
return $this->token === null
|
||||
|| ($this->lookahead !== null
|
||||
&& ($this->lookahead['position'] - $this->token['position']) === strlen($this->token['value']));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getCatchablePatterns()
|
||||
{
|
||||
return array(
|
||||
return [
|
||||
'[a-z_\\\][a-z0-9_\:\\\]*[a-z_][a-z0-9_]*',
|
||||
'(?:[+-]?[0-9]+(?:[\.][0-9]+)*)(?:[eE][+-]?[0-9]+)?',
|
||||
'"(?:""|[^"])*+"',
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -93,7 +88,7 @@ final class DocLexer extends AbstractLexer
|
|||
*/
|
||||
protected function getNonCatchablePatterns()
|
||||
{
|
||||
return array('\s+', '\*+', '(.)');
|
||||
return ['\s+', '\*+', '(.)'];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -125,7 +120,7 @@ final class DocLexer extends AbstractLexer
|
|||
|
||||
// Checking numeric value
|
||||
if (is_numeric($value)) {
|
||||
return (strpos($value, '.') !== false || stripos($value, 'e') !== false)
|
||||
return strpos($value, '.') !== false || stripos($value, 'e') !== false
|
||||
? self::T_FLOAT : self::T_INTEGER;
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,89 +1,84 @@
|
|||
<?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\Annotations;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
use ReflectionProperty;
|
||||
use RuntimeException;
|
||||
|
||||
use function chmod;
|
||||
use function file_put_contents;
|
||||
use function filemtime;
|
||||
use function gettype;
|
||||
use function is_dir;
|
||||
use function is_file;
|
||||
use function is_int;
|
||||
use function is_writable;
|
||||
use function mkdir;
|
||||
use function rename;
|
||||
use function rtrim;
|
||||
use function serialize;
|
||||
use function sha1;
|
||||
use function sprintf;
|
||||
use function strtr;
|
||||
use function tempnam;
|
||||
use function uniqid;
|
||||
use function unlink;
|
||||
use function var_export;
|
||||
|
||||
/**
|
||||
* File cache reader for annotations.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*
|
||||
* @deprecated the FileCacheReader is deprecated and will be removed
|
||||
* in version 2.0.0 of doctrine/annotations. Please use the
|
||||
* {@see \Doctrine\Common\Annotations\CachedReader} instead.
|
||||
*/
|
||||
class FileCacheReader implements Reader
|
||||
{
|
||||
/**
|
||||
* @var Reader
|
||||
*/
|
||||
/** @var Reader */
|
||||
private $reader;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
/** @var string */
|
||||
private $dir;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
/** @var bool */
|
||||
private $debug;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $loadedAnnotations = array();
|
||||
/** @phpstan-var array<string, list<object>> */
|
||||
private $loadedAnnotations = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $classNameHashes = array();
|
||||
/** @var array<string, string> */
|
||||
private $classNameHashes = [];
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
/** @var int */
|
||||
private $umask;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param string $cacheDir
|
||||
* @param bool $debug
|
||||
* @param int $umask
|
||||
*
|
||||
* @param Reader $reader
|
||||
* @param string $cacheDir
|
||||
* @param boolean $debug
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function __construct(Reader $reader, $cacheDir, $debug = false, $umask = 0002)
|
||||
{
|
||||
if ( ! is_int($umask)) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
if (! is_int($umask)) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'The parameter umask must be an integer, was: %s',
|
||||
gettype($umask)
|
||||
));
|
||||
}
|
||||
|
||||
$this->reader = $reader;
|
||||
$this->umask = $umask;
|
||||
$this->umask = $umask;
|
||||
|
||||
if (!is_dir($cacheDir) && !@mkdir($cacheDir, 0777 & (~$this->umask), true)) {
|
||||
throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist and could not be created.', $cacheDir));
|
||||
if (! is_dir($cacheDir) && ! @mkdir($cacheDir, 0777 & (~$this->umask), true)) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'The directory "%s" does not exist and could not be created.',
|
||||
$cacheDir
|
||||
));
|
||||
}
|
||||
|
||||
$this->dir = rtrim($cacheDir, '\\/');
|
||||
|
|
@ -93,31 +88,37 @@ class FileCacheReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getClassAnnotations(\ReflectionClass $class)
|
||||
public function getClassAnnotations(ReflectionClass $class)
|
||||
{
|
||||
if ( ! isset($this->classNameHashes[$class->name])) {
|
||||
if (! isset($this->classNameHashes[$class->name])) {
|
||||
$this->classNameHashes[$class->name] = sha1($class->name);
|
||||
}
|
||||
|
||||
$key = $this->classNameHashes[$class->name];
|
||||
|
||||
if (isset($this->loadedAnnotations[$key])) {
|
||||
return $this->loadedAnnotations[$key];
|
||||
}
|
||||
|
||||
$path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php';
|
||||
if (!is_file($path)) {
|
||||
$path = $this->dir . '/' . strtr($key, '\\', '-') . '.cache.php';
|
||||
if (! is_file($path)) {
|
||||
$annot = $this->reader->getClassAnnotations($class);
|
||||
$this->saveCacheFile($path, $annot);
|
||||
|
||||
return $this->loadedAnnotations[$key] = $annot;
|
||||
}
|
||||
|
||||
if ($this->debug
|
||||
&& (false !== $filename = $class->getFileName())
|
||||
&& filemtime($path) < filemtime($filename)) {
|
||||
$filename = $class->getFilename();
|
||||
if (
|
||||
$this->debug
|
||||
&& $filename !== false
|
||||
&& filemtime($path) < filemtime($filename)
|
||||
) {
|
||||
@unlink($path);
|
||||
|
||||
$annot = $this->reader->getClassAnnotations($class);
|
||||
$this->saveCacheFile($path, $annot);
|
||||
|
||||
return $this->loadedAnnotations[$key] = $annot;
|
||||
}
|
||||
|
||||
|
|
@ -127,32 +128,38 @@ class FileCacheReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getPropertyAnnotations(\ReflectionProperty $property)
|
||||
public function getPropertyAnnotations(ReflectionProperty $property)
|
||||
{
|
||||
$class = $property->getDeclaringClass();
|
||||
if ( ! isset($this->classNameHashes[$class->name])) {
|
||||
if (! isset($this->classNameHashes[$class->name])) {
|
||||
$this->classNameHashes[$class->name] = sha1($class->name);
|
||||
}
|
||||
$key = $this->classNameHashes[$class->name].'$'.$property->getName();
|
||||
|
||||
$key = $this->classNameHashes[$class->name] . '$' . $property->getName();
|
||||
|
||||
if (isset($this->loadedAnnotations[$key])) {
|
||||
return $this->loadedAnnotations[$key];
|
||||
}
|
||||
|
||||
$path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php';
|
||||
if (!is_file($path)) {
|
||||
$path = $this->dir . '/' . strtr($key, '\\', '-') . '.cache.php';
|
||||
if (! is_file($path)) {
|
||||
$annot = $this->reader->getPropertyAnnotations($property);
|
||||
$this->saveCacheFile($path, $annot);
|
||||
|
||||
return $this->loadedAnnotations[$key] = $annot;
|
||||
}
|
||||
|
||||
if ($this->debug
|
||||
&& (false !== $filename = $class->getFilename())
|
||||
&& filemtime($path) < filemtime($filename)) {
|
||||
$filename = $class->getFilename();
|
||||
if (
|
||||
$this->debug
|
||||
&& $filename !== false
|
||||
&& filemtime($path) < filemtime($filename)
|
||||
) {
|
||||
@unlink($path);
|
||||
|
||||
$annot = $this->reader->getPropertyAnnotations($property);
|
||||
$this->saveCacheFile($path, $annot);
|
||||
|
||||
return $this->loadedAnnotations[$key] = $annot;
|
||||
}
|
||||
|
||||
|
|
@ -162,32 +169,38 @@ class FileCacheReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getMethodAnnotations(\ReflectionMethod $method)
|
||||
public function getMethodAnnotations(ReflectionMethod $method)
|
||||
{
|
||||
$class = $method->getDeclaringClass();
|
||||
if ( ! isset($this->classNameHashes[$class->name])) {
|
||||
if (! isset($this->classNameHashes[$class->name])) {
|
||||
$this->classNameHashes[$class->name] = sha1($class->name);
|
||||
}
|
||||
$key = $this->classNameHashes[$class->name].'#'.$method->getName();
|
||||
|
||||
$key = $this->classNameHashes[$class->name] . '#' . $method->getName();
|
||||
|
||||
if (isset($this->loadedAnnotations[$key])) {
|
||||
return $this->loadedAnnotations[$key];
|
||||
}
|
||||
|
||||
$path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php';
|
||||
if (!is_file($path)) {
|
||||
$path = $this->dir . '/' . strtr($key, '\\', '-') . '.cache.php';
|
||||
if (! is_file($path)) {
|
||||
$annot = $this->reader->getMethodAnnotations($method);
|
||||
$this->saveCacheFile($path, $annot);
|
||||
|
||||
return $this->loadedAnnotations[$key] = $annot;
|
||||
}
|
||||
|
||||
if ($this->debug
|
||||
&& (false !== $filename = $class->getFilename())
|
||||
&& filemtime($path) < filemtime($filename)) {
|
||||
$filename = $class->getFilename();
|
||||
if (
|
||||
$this->debug
|
||||
&& $filename !== false
|
||||
&& filemtime($path) < filemtime($filename)
|
||||
) {
|
||||
@unlink($path);
|
||||
|
||||
$annot = $this->reader->getMethodAnnotations($method);
|
||||
$this->saveCacheFile($path, $annot);
|
||||
|
||||
return $this->loadedAnnotations[$key] = $annot;
|
||||
}
|
||||
|
||||
|
|
@ -204,36 +217,48 @@ class FileCacheReader implements Reader
|
|||
*/
|
||||
private function saveCacheFile($path, $data)
|
||||
{
|
||||
if (!is_writable($this->dir)) {
|
||||
throw new \InvalidArgumentException(sprintf('The directory "%s" is not writable. Both, the webserver and the console user need access. You can manage access rights for multiple users with "chmod +a". If your system does not support this, check out the acl package.', $this->dir));
|
||||
if (! is_writable($this->dir)) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
<<<'EXCEPTION'
|
||||
The directory "%s" is not writable. Both the webserver and the console user need access.
|
||||
You can manage access rights for multiple users with "chmod +a".
|
||||
If your system does not support this, check out the acl package.,
|
||||
EXCEPTION
|
||||
,
|
||||
$this->dir
|
||||
));
|
||||
}
|
||||
|
||||
$tempfile = tempnam($this->dir, uniqid('', true));
|
||||
|
||||
if (false === $tempfile) {
|
||||
throw new \RuntimeException(sprintf('Unable to create tempfile in directory: %s', $this->dir));
|
||||
if ($tempfile === false) {
|
||||
throw new RuntimeException(sprintf('Unable to create tempfile in directory: %s', $this->dir));
|
||||
}
|
||||
|
||||
@chmod($tempfile, 0666 & (~$this->umask));
|
||||
|
||||
$written = file_put_contents($tempfile, '<?php return unserialize('.var_export(serialize($data), true).');');
|
||||
$written = file_put_contents(
|
||||
$tempfile,
|
||||
'<?php return unserialize(' . var_export(serialize($data), true) . ');'
|
||||
);
|
||||
|
||||
if (false === $written) {
|
||||
throw new \RuntimeException(sprintf('Unable to write cached file to: %s', $tempfile));
|
||||
if ($written === false) {
|
||||
throw new RuntimeException(sprintf('Unable to write cached file to: %s', $tempfile));
|
||||
}
|
||||
|
||||
@chmod($tempfile, 0666 & (~$this->umask));
|
||||
|
||||
if (false === rename($tempfile, $path)) {
|
||||
if (rename($tempfile, $path) === false) {
|
||||
@unlink($tempfile);
|
||||
throw new \RuntimeException(sprintf('Unable to rename %s to %s', $tempfile, $path));
|
||||
|
||||
throw new RuntimeException(sprintf('Unable to rename %s to %s', $tempfile, $path));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getClassAnnotation(\ReflectionClass $class, $annotationName)
|
||||
public function getClassAnnotation(ReflectionClass $class, $annotationName)
|
||||
{
|
||||
$annotations = $this->getClassAnnotations($class);
|
||||
|
||||
|
|
@ -249,7 +274,7 @@ class FileCacheReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getMethodAnnotation(\ReflectionMethod $method, $annotationName)
|
||||
public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
|
||||
{
|
||||
$annotations = $this->getMethodAnnotations($method);
|
||||
|
||||
|
|
@ -265,7 +290,7 @@ class FileCacheReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName)
|
||||
public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
|
||||
{
|
||||
$annotations = $this->getPropertyAnnotations($property);
|
||||
|
||||
|
|
@ -285,6 +310,6 @@ class FileCacheReader implements Reader
|
|||
*/
|
||||
public function clearLoadedAnnotations()
|
||||
{
|
||||
$this->loadedAnnotations = array();
|
||||
$this->loadedAnnotations = [];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,41 +1,22 @@
|
|||
<?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\Annotations;
|
||||
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
use ReflectionProperty;
|
||||
|
||||
use function call_user_func_array;
|
||||
use function get_class;
|
||||
|
||||
/**
|
||||
* Allows the reader to be used in-place of Doctrine's reader.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
class IndexedReader implements Reader
|
||||
{
|
||||
/**
|
||||
* @var Reader
|
||||
*/
|
||||
/** @var Reader */
|
||||
private $delegate;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Reader $reader
|
||||
*/
|
||||
public function __construct(Reader $reader)
|
||||
{
|
||||
$this->delegate = $reader;
|
||||
|
|
@ -44,9 +25,9 @@ class IndexedReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getClassAnnotations(\ReflectionClass $class)
|
||||
public function getClassAnnotations(ReflectionClass $class)
|
||||
{
|
||||
$annotations = array();
|
||||
$annotations = [];
|
||||
foreach ($this->delegate->getClassAnnotations($class) as $annot) {
|
||||
$annotations[get_class($annot)] = $annot;
|
||||
}
|
||||
|
|
@ -57,7 +38,7 @@ class IndexedReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getClassAnnotation(\ReflectionClass $class, $annotation)
|
||||
public function getClassAnnotation(ReflectionClass $class, $annotation)
|
||||
{
|
||||
return $this->delegate->getClassAnnotation($class, $annotation);
|
||||
}
|
||||
|
|
@ -65,9 +46,9 @@ class IndexedReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getMethodAnnotations(\ReflectionMethod $method)
|
||||
public function getMethodAnnotations(ReflectionMethod $method)
|
||||
{
|
||||
$annotations = array();
|
||||
$annotations = [];
|
||||
foreach ($this->delegate->getMethodAnnotations($method) as $annot) {
|
||||
$annotations[get_class($annot)] = $annot;
|
||||
}
|
||||
|
|
@ -78,7 +59,7 @@ class IndexedReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getMethodAnnotation(\ReflectionMethod $method, $annotation)
|
||||
public function getMethodAnnotation(ReflectionMethod $method, $annotation)
|
||||
{
|
||||
return $this->delegate->getMethodAnnotation($method, $annotation);
|
||||
}
|
||||
|
|
@ -86,9 +67,9 @@ class IndexedReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getPropertyAnnotations(\ReflectionProperty $property)
|
||||
public function getPropertyAnnotations(ReflectionProperty $property)
|
||||
{
|
||||
$annotations = array();
|
||||
$annotations = [];
|
||||
foreach ($this->delegate->getPropertyAnnotations($property) as $annot) {
|
||||
$annotations[get_class($annot)] = $annot;
|
||||
}
|
||||
|
|
@ -99,7 +80,7 @@ class IndexedReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getPropertyAnnotation(\ReflectionProperty $property, $annotation)
|
||||
public function getPropertyAnnotation(ReflectionProperty $property, $annotation)
|
||||
{
|
||||
return $this->delegate->getPropertyAnnotation($property, $annotation);
|
||||
}
|
||||
|
|
@ -107,13 +88,13 @@ class IndexedReader implements Reader
|
|||
/**
|
||||
* Proxies all methods to the delegate.
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $args
|
||||
* @param string $method
|
||||
* @param mixed[] $args
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($method, $args)
|
||||
{
|
||||
return call_user_func_array(array($this->delegate, $method), $args);
|
||||
return call_user_func_array([$this->delegate, $method], $args);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,85 +1,86 @@
|
|||
<?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\Annotations;
|
||||
|
||||
use ReflectionClass;
|
||||
use ReflectionFunction;
|
||||
use SplFileObject;
|
||||
|
||||
use function is_file;
|
||||
use function method_exists;
|
||||
use function preg_quote;
|
||||
use function preg_replace;
|
||||
|
||||
/**
|
||||
* Parses a file for namespaces/use/class declarations.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Christian Kaps <christian.kaps@mohiva.com>
|
||||
*/
|
||||
final class PhpParser
|
||||
{
|
||||
/**
|
||||
* Parses a class.
|
||||
*
|
||||
* @param \ReflectionClass $class A <code>ReflectionClass</code> object.
|
||||
* @deprecated use parseUseStatements instead
|
||||
*
|
||||
* @return array A list with use statements in the form (Alias => FQN).
|
||||
* @param ReflectionClass $class A <code>ReflectionClass</code> object.
|
||||
*
|
||||
* @return array<string, class-string> A list with use statements in the form (Alias => FQN).
|
||||
*/
|
||||
public function parseClass(\ReflectionClass $class)
|
||||
public function parseClass(ReflectionClass $class)
|
||||
{
|
||||
if (method_exists($class, 'getUseStatements')) {
|
||||
return $class->getUseStatements();
|
||||
return $this->parseUseStatements($class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a class or function for use statements.
|
||||
*
|
||||
* @param ReflectionClass|ReflectionFunction $reflection
|
||||
*
|
||||
* @psalm-return array<string, string> a list with use statements in the form (Alias => FQN).
|
||||
*/
|
||||
public function parseUseStatements($reflection): array
|
||||
{
|
||||
if (method_exists($reflection, 'getUseStatements')) {
|
||||
return $reflection->getUseStatements();
|
||||
}
|
||||
|
||||
if (false === $filename = $class->getFileName()) {
|
||||
return array();
|
||||
$filename = $reflection->getFileName();
|
||||
|
||||
if ($filename === false) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$content = $this->getFileContent($filename, $class->getStartLine());
|
||||
$content = $this->getFileContent($filename, $reflection->getStartLine());
|
||||
|
||||
if (null === $content) {
|
||||
return array();
|
||||
if ($content === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$namespace = preg_quote($class->getNamespaceName());
|
||||
$content = preg_replace('/^.*?(\bnamespace\s+' . $namespace . '\s*[;{].*)$/s', '\\1', $content);
|
||||
$namespace = preg_quote($reflection->getNamespaceName());
|
||||
$content = preg_replace('/^.*?(\bnamespace\s+' . $namespace . '\s*[;{].*)$/s', '\\1', $content);
|
||||
$tokenizer = new TokenParser('<?php ' . $content);
|
||||
|
||||
$statements = $tokenizer->parseUseStatements($class->getNamespaceName());
|
||||
|
||||
return $statements;
|
||||
return $tokenizer->parseUseStatements($reflection->getNamespaceName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the content of the file right up to the given line number.
|
||||
*
|
||||
* @param string $filename The name of the file to load.
|
||||
* @param integer $lineNumber The number of lines to read from file.
|
||||
* @param string $filename The name of the file to load.
|
||||
* @param int $lineNumber The number of lines to read from file.
|
||||
*
|
||||
* @return string|null The content of the file or null if the file does not exist.
|
||||
*/
|
||||
private function getFileContent($filename, $lineNumber)
|
||||
{
|
||||
if ( ! is_file($filename)) {
|
||||
if (! is_file($filename)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$content = '';
|
||||
$lineCnt = 0;
|
||||
$file = new SplFileObject($filename);
|
||||
while (!$file->eof()) {
|
||||
if ($lineCnt++ == $lineNumber) {
|
||||
$file = new SplFileObject($filename);
|
||||
while (! $file->eof()) {
|
||||
if ($lineCnt++ === $lineNumber) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,89 +1,80 @@
|
|||
<?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\Annotations;
|
||||
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
use ReflectionProperty;
|
||||
|
||||
/**
|
||||
* Interface for annotation readers.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
interface Reader
|
||||
{
|
||||
/**
|
||||
* Gets the annotations applied to a class.
|
||||
*
|
||||
* @param \ReflectionClass $class The ReflectionClass of the class from which
|
||||
* the class annotations should be read.
|
||||
* @param ReflectionClass $class The ReflectionClass of the class from which
|
||||
* the class annotations should be read.
|
||||
*
|
||||
* @return array An array of Annotations.
|
||||
* @return array<object> An array of Annotations.
|
||||
*/
|
||||
function getClassAnnotations(\ReflectionClass $class);
|
||||
public function getClassAnnotations(ReflectionClass $class);
|
||||
|
||||
/**
|
||||
* Gets a class annotation.
|
||||
*
|
||||
* @param \ReflectionClass $class The ReflectionClass of the class from which
|
||||
* the class annotations should be read.
|
||||
* @param string $annotationName The name of the annotation.
|
||||
* @param ReflectionClass $class The ReflectionClass of the class from which
|
||||
* the class annotations should be read.
|
||||
* @param class-string<T> $annotationName The name of the annotation.
|
||||
*
|
||||
* @return object|null The Annotation or NULL, if the requested annotation does not exist.
|
||||
* @return T|null The Annotation or NULL, if the requested annotation does not exist.
|
||||
*
|
||||
* @template T
|
||||
*/
|
||||
function getClassAnnotation(\ReflectionClass $class, $annotationName);
|
||||
public function getClassAnnotation(ReflectionClass $class, $annotationName);
|
||||
|
||||
/**
|
||||
* Gets the annotations applied to a method.
|
||||
*
|
||||
* @param \ReflectionMethod $method The ReflectionMethod of the method from which
|
||||
* the annotations should be read.
|
||||
* @param ReflectionMethod $method The ReflectionMethod of the method from which
|
||||
* the annotations should be read.
|
||||
*
|
||||
* @return array An array of Annotations.
|
||||
* @return array<object> An array of Annotations.
|
||||
*/
|
||||
function getMethodAnnotations(\ReflectionMethod $method);
|
||||
public function getMethodAnnotations(ReflectionMethod $method);
|
||||
|
||||
/**
|
||||
* Gets a method annotation.
|
||||
*
|
||||
* @param \ReflectionMethod $method The ReflectionMethod to read the annotations from.
|
||||
* @param string $annotationName The name of the annotation.
|
||||
* @param ReflectionMethod $method The ReflectionMethod to read the annotations from.
|
||||
* @param class-string<T> $annotationName The name of the annotation.
|
||||
*
|
||||
* @return object|null The Annotation or NULL, if the requested annotation does not exist.
|
||||
* @return T|null The Annotation or NULL, if the requested annotation does not exist.
|
||||
*
|
||||
* @template T
|
||||
*/
|
||||
function getMethodAnnotation(\ReflectionMethod $method, $annotationName);
|
||||
public function getMethodAnnotation(ReflectionMethod $method, $annotationName);
|
||||
|
||||
/**
|
||||
* Gets the annotations applied to a property.
|
||||
*
|
||||
* @param \ReflectionProperty $property The ReflectionProperty of the property
|
||||
* from which the annotations should be read.
|
||||
* @param ReflectionProperty $property The ReflectionProperty of the property
|
||||
* from which the annotations should be read.
|
||||
*
|
||||
* @return array An array of Annotations.
|
||||
* @return array<object> An array of Annotations.
|
||||
*/
|
||||
function getPropertyAnnotations(\ReflectionProperty $property);
|
||||
public function getPropertyAnnotations(ReflectionProperty $property);
|
||||
|
||||
/**
|
||||
* Gets a property annotation.
|
||||
*
|
||||
* @param \ReflectionProperty $property The ReflectionProperty to read the annotations from.
|
||||
* @param string $annotationName The name of the annotation.
|
||||
* @param ReflectionProperty $property The ReflectionProperty to read the annotations from.
|
||||
* @param class-string<T> $annotationName The name of the annotation.
|
||||
*
|
||||
* @return object|null The Annotation or NULL, if the requested annotation does not exist.
|
||||
* @return T|null The Annotation or NULL, if the requested annotation does not exist.
|
||||
*
|
||||
* @template T
|
||||
*/
|
||||
function getPropertyAnnotation(\ReflectionProperty $property, $annotationName);
|
||||
public function getPropertyAnnotation(ReflectionProperty $property, $annotationName);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,44 +1,25 @@
|
|||
<?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\Annotations;
|
||||
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
use ReflectionProperty;
|
||||
|
||||
/**
|
||||
* Simple Annotation Reader.
|
||||
*
|
||||
* This annotation reader is intended to be used in projects where you have
|
||||
* full-control over all annotations that are available.
|
||||
*
|
||||
* @since 2.2
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
* @deprecated Deprecated in favour of using AnnotationReader
|
||||
*/
|
||||
class SimpleAnnotationReader implements Reader
|
||||
{
|
||||
/**
|
||||
* @var DocParser
|
||||
*/
|
||||
/** @var DocParser */
|
||||
private $parser;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* Initializes a new SimpleAnnotationReader.
|
||||
*/
|
||||
public function __construct()
|
||||
|
|
@ -62,31 +43,37 @@ class SimpleAnnotationReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getClassAnnotations(\ReflectionClass $class)
|
||||
public function getClassAnnotations(ReflectionClass $class)
|
||||
{
|
||||
return $this->parser->parse($class->getDocComment(), 'class '.$class->getName());
|
||||
return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getMethodAnnotations(\ReflectionMethod $method)
|
||||
public function getMethodAnnotations(ReflectionMethod $method)
|
||||
{
|
||||
return $this->parser->parse($method->getDocComment(), 'method '.$method->getDeclaringClass()->name.'::'.$method->getName().'()');
|
||||
return $this->parser->parse(
|
||||
$method->getDocComment(),
|
||||
'method ' . $method->getDeclaringClass()->name . '::' . $method->getName() . '()'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getPropertyAnnotations(\ReflectionProperty $property)
|
||||
public function getPropertyAnnotations(ReflectionProperty $property)
|
||||
{
|
||||
return $this->parser->parse($property->getDocComment(), 'property '.$property->getDeclaringClass()->name.'::$'.$property->getName());
|
||||
return $this->parser->parse(
|
||||
$property->getDocComment(),
|
||||
'property ' . $property->getDeclaringClass()->name . '::$' . $property->getName()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getClassAnnotation(\ReflectionClass $class, $annotationName)
|
||||
public function getClassAnnotation(ReflectionClass $class, $annotationName)
|
||||
{
|
||||
foreach ($this->getClassAnnotations($class) as $annot) {
|
||||
if ($annot instanceof $annotationName) {
|
||||
|
|
@ -100,7 +87,7 @@ class SimpleAnnotationReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getMethodAnnotation(\ReflectionMethod $method, $annotationName)
|
||||
public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
|
||||
{
|
||||
foreach ($this->getMethodAnnotations($method) as $annot) {
|
||||
if ($annot instanceof $annotationName) {
|
||||
|
|
@ -114,7 +101,7 @@ class SimpleAnnotationReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName)
|
||||
public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
|
||||
{
|
||||
foreach ($this->getPropertyAnnotations($property) as $annot) {
|
||||
if ($annot instanceof $annotationName) {
|
||||
|
|
|
|||
|
|
@ -1,36 +1,34 @@
|
|||
<?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\Annotations;
|
||||
|
||||
use function array_merge;
|
||||
use function count;
|
||||
use function explode;
|
||||
use function strtolower;
|
||||
use function token_get_all;
|
||||
|
||||
use const PHP_VERSION_ID;
|
||||
use const T_AS;
|
||||
use const T_COMMENT;
|
||||
use const T_DOC_COMMENT;
|
||||
use const T_NAME_FULLY_QUALIFIED;
|
||||
use const T_NAME_QUALIFIED;
|
||||
use const T_NAMESPACE;
|
||||
use const T_NS_SEPARATOR;
|
||||
use const T_STRING;
|
||||
use const T_USE;
|
||||
use const T_WHITESPACE;
|
||||
|
||||
/**
|
||||
* Parses a file for namespaces/use/class declarations.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Christian Kaps <christian.kaps@mohiva.com>
|
||||
*/
|
||||
class TokenParser
|
||||
{
|
||||
/**
|
||||
* The token list.
|
||||
*
|
||||
* @var array
|
||||
* @phpstan-var list<mixed[]>
|
||||
*/
|
||||
private $tokens;
|
||||
|
||||
|
|
@ -70,19 +68,20 @@ class TokenParser
|
|||
/**
|
||||
* Gets the next non whitespace and non comment token.
|
||||
*
|
||||
* @param boolean $docCommentIsComment If TRUE then a doc comment is considered a comment and skipped.
|
||||
* If FALSE then only whitespace and normal comments are skipped.
|
||||
* @param bool $docCommentIsComment If TRUE then a doc comment is considered a comment and skipped.
|
||||
* If FALSE then only whitespace and normal comments are skipped.
|
||||
*
|
||||
* @return array|null The token if exists, null otherwise.
|
||||
* @return mixed[]|string|null The token if exists, null otherwise.
|
||||
*/
|
||||
public function next($docCommentIsComment = TRUE)
|
||||
public function next($docCommentIsComment = true)
|
||||
{
|
||||
for ($i = $this->pointer; $i < $this->numTokens; $i++) {
|
||||
$this->pointer++;
|
||||
if ($this->tokens[$i][0] === T_WHITESPACE ||
|
||||
if (
|
||||
$this->tokens[$i][0] === T_WHITESPACE ||
|
||||
$this->tokens[$i][0] === T_COMMENT ||
|
||||
($docCommentIsComment && $this->tokens[$i][0] === T_DOC_COMMENT)) {
|
||||
|
||||
($docCommentIsComment && $this->tokens[$i][0] === T_DOC_COMMENT)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -95,38 +94,47 @@ class TokenParser
|
|||
/**
|
||||
* Parses a single use statement.
|
||||
*
|
||||
* @return array A list with all found class names for a use statement.
|
||||
* @return array<string, string> A list with all found class names for a use statement.
|
||||
*/
|
||||
public function parseUseStatement()
|
||||
{
|
||||
|
||||
$groupRoot = '';
|
||||
$class = '';
|
||||
$alias = '';
|
||||
$statements = array();
|
||||
$groupRoot = '';
|
||||
$class = '';
|
||||
$alias = '';
|
||||
$statements = [];
|
||||
$explicitAlias = false;
|
||||
while (($token = $this->next())) {
|
||||
$isNameToken = $token[0] === T_STRING || $token[0] === T_NS_SEPARATOR;
|
||||
if (!$explicitAlias && $isNameToken) {
|
||||
if (! $explicitAlias && $token[0] === T_STRING) {
|
||||
$class .= $token[1];
|
||||
$alias = $token[1];
|
||||
} elseif ($explicitAlias && $token[0] === T_STRING) {
|
||||
$alias = $token[1];
|
||||
} else if ($explicitAlias && $isNameToken) {
|
||||
$alias .= $token[1];
|
||||
} else if ($token[0] === T_AS) {
|
||||
} elseif (
|
||||
PHP_VERSION_ID >= 80000 &&
|
||||
($token[0] === T_NAME_QUALIFIED || $token[0] === T_NAME_FULLY_QUALIFIED)
|
||||
) {
|
||||
$class .= $token[1];
|
||||
|
||||
$classSplit = explode('\\', $token[1]);
|
||||
$alias = $classSplit[count($classSplit) - 1];
|
||||
} elseif ($token[0] === T_NS_SEPARATOR) {
|
||||
$class .= '\\';
|
||||
$alias = '';
|
||||
} elseif ($token[0] === T_AS) {
|
||||
$explicitAlias = true;
|
||||
$alias = '';
|
||||
} else if ($token === ',') {
|
||||
$alias = '';
|
||||
} elseif ($token === ',') {
|
||||
$statements[strtolower($alias)] = $groupRoot . $class;
|
||||
$class = '';
|
||||
$alias = '';
|
||||
$explicitAlias = false;
|
||||
} else if ($token === ';') {
|
||||
$class = '';
|
||||
$alias = '';
|
||||
$explicitAlias = false;
|
||||
} elseif ($token === ';') {
|
||||
$statements[strtolower($alias)] = $groupRoot . $class;
|
||||
break;
|
||||
} else if ($token === '{' ) {
|
||||
} elseif ($token === '{') {
|
||||
$groupRoot = $class;
|
||||
$class = '';
|
||||
} else if ($token === '}' ) {
|
||||
$class = '';
|
||||
} elseif ($token === '}') {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
|
|
@ -141,24 +149,25 @@ class TokenParser
|
|||
*
|
||||
* @param string $namespaceName The namespace name of the reflected class.
|
||||
*
|
||||
* @return array A list with all found use statements.
|
||||
* @return array<string, string> A list with all found use statements.
|
||||
*/
|
||||
public function parseUseStatements($namespaceName)
|
||||
{
|
||||
$statements = array();
|
||||
$statements = [];
|
||||
while (($token = $this->next())) {
|
||||
if ($token[0] === T_USE) {
|
||||
$statements = array_merge($statements, $this->parseUseStatement());
|
||||
continue;
|
||||
}
|
||||
if ($token[0] !== T_NAMESPACE || $this->parseNamespace() != $namespaceName) {
|
||||
|
||||
if ($token[0] !== T_NAMESPACE || $this->parseNamespace() !== $namespaceName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get fresh array for new namespace. This is to prevent the parser to collect the use statements
|
||||
// for a previous namespace with the same name. This is the case if a namespace is defined twice
|
||||
// or if a namespace with the same name is commented out.
|
||||
$statements = array();
|
||||
$statements = [];
|
||||
}
|
||||
|
||||
return $statements;
|
||||
|
|
@ -172,7 +181,12 @@ class TokenParser
|
|||
public function parseNamespace()
|
||||
{
|
||||
$name = '';
|
||||
while (($token = $this->next()) && ($token[0] === T_STRING || $token[0] === T_NS_SEPARATOR)) {
|
||||
while (
|
||||
($token = $this->next()) && ($token[0] === T_STRING || $token[0] === T_NS_SEPARATOR || (
|
||||
PHP_VERSION_ID >= 80000 &&
|
||||
($token[0] === T_NAME_QUALIFIED || $token[0] === T_NAME_FULLY_QUALIFIED)
|
||||
))
|
||||
) {
|
||||
$name .= $token[1];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,9 @@
|
|||
# Doctrine Cache
|
||||
|
||||
Master: [](http://travis-ci.org/doctrine/cache) [](https://coveralls.io/r/doctrine/cache?branch=master)
|
||||
[](http://travis-ci.org/doctrine/cache)
|
||||
[](https://codecov.io/gh/doctrine/dbal/branch/master)
|
||||
|
||||
[](https://packagist.org/packages/doctrine/cache) [](https://packagist.org/packages/doctrine/cache)
|
||||
[](https://packagist.org/packages/doctrine/cache)
|
||||
[](https://packagist.org/packages/doctrine/cache)
|
||||
|
||||
Cache component extracted from the Doctrine Common project.
|
||||
|
||||
## Changelog
|
||||
|
||||
### v1.2
|
||||
|
||||
* Added support for MongoDB as Cache Provider
|
||||
* Fix namespace version reset
|
||||
Cache component extracted from the Doctrine Common project. [Documentation](https://www.doctrine-project.org/projects/doctrine-cache/en/current/index.html)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,19 @@
|
|||
{
|
||||
"name": "doctrine/cache",
|
||||
"type": "library",
|
||||
"description": "Caching library offering an object-oriented API for many cache backends",
|
||||
"keywords": ["cache", "caching"],
|
||||
"homepage": "http://www.doctrine-project.org",
|
||||
"description": "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.",
|
||||
"keywords": [
|
||||
"php",
|
||||
"cache",
|
||||
"caching",
|
||||
"abstraction",
|
||||
"redis",
|
||||
"memcached",
|
||||
"couchdb",
|
||||
"xcache",
|
||||
"apcu"
|
||||
],
|
||||
"homepage": "https://www.doctrine-project.org/projects/cache.html",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
|
||||
|
|
@ -13,12 +23,17 @@
|
|||
{"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
|
||||
],
|
||||
"require": {
|
||||
"php": "~5.5|~7.0"
|
||||
"php": "~7.1 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.8|~5.0",
|
||||
"satooshi/php-coveralls": "~0.6",
|
||||
"predis/predis": "~1.0"
|
||||
"alcaeus/mongo-php-adapter": "^1.1",
|
||||
"mongodb/mongodb": "^1.1",
|
||||
"phpunit/phpunit": "^7.0",
|
||||
"predis/predis": "~1.0",
|
||||
"doctrine/coding-standard": "^6.0"
|
||||
},
|
||||
"suggest": {
|
||||
"alcaeus/mongo-php-adapter": "Required to use legacy MongoDB driver"
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/common": ">2.2,<2.4"
|
||||
|
|
@ -31,7 +46,7 @@
|
|||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.6.x-dev"
|
||||
"dev-master": "1.9.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,35 +1,22 @@
|
|||
<?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;
|
||||
|
||||
use const PHP_VERSION_ID;
|
||||
use function apc_cache_info;
|
||||
use function apc_clear_cache;
|
||||
use function apc_delete;
|
||||
use function apc_exists;
|
||||
use function apc_fetch;
|
||||
use function apc_sma_info;
|
||||
use function apc_store;
|
||||
|
||||
/**
|
||||
* APC cache provider.
|
||||
*
|
||||
* @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>
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
*/
|
||||
class ApcCache extends CacheProvider
|
||||
{
|
||||
|
|
@ -102,17 +89,17 @@ class ApcCache extends CacheProvider
|
|||
|
||||
// @TODO - Temporary fix @see https://github.com/krakjoe/apcu/pull/42
|
||||
if (PHP_VERSION_ID >= 50500) {
|
||||
$info['num_hits'] = isset($info['num_hits']) ? $info['num_hits'] : $info['nhits'];
|
||||
$info['num_misses'] = isset($info['num_misses']) ? $info['num_misses'] : $info['nmisses'];
|
||||
$info['start_time'] = isset($info['start_time']) ? $info['start_time'] : $info['stime'];
|
||||
$info['num_hits'] = $info['num_hits'] ?? $info['nhits'];
|
||||
$info['num_misses'] = $info['num_misses'] ?? $info['nmisses'];
|
||||
$info['start_time'] = $info['start_time'] ?? $info['stime'];
|
||||
}
|
||||
|
||||
return array(
|
||||
return [
|
||||
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'],
|
||||
);
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,30 +1,20 @@
|
|||
<?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;
|
||||
|
||||
use function apcu_cache_info;
|
||||
use function apcu_clear_cache;
|
||||
use function apcu_delete;
|
||||
use function apcu_exists;
|
||||
use function apcu_fetch;
|
||||
use function apcu_sma_info;
|
||||
use function apcu_store;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
* APCu cache provider.
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
* @since 1.6
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class ApcuCache extends CacheProvider
|
||||
{
|
||||
|
|
@ -61,6 +51,16 @@ class ApcuCache extends CacheProvider
|
|||
return apcu_delete($id) || ! apcu_exists($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDeleteMultiple(array $keys)
|
||||
{
|
||||
$result = apcu_delete($keys);
|
||||
|
||||
return $result !== false && count($result) !== count($keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
@ -95,12 +95,12 @@ class ApcuCache extends CacheProvider
|
|||
$info = apcu_cache_info(true);
|
||||
$sma = apcu_sma_info();
|
||||
|
||||
return array(
|
||||
return [
|
||||
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'],
|
||||
);
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,55 +1,26 @@
|
|||
<?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;
|
||||
|
||||
use function time;
|
||||
|
||||
/**
|
||||
* Array cache driver.
|
||||
*
|
||||
* @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>
|
||||
*/
|
||||
class ArrayCache extends CacheProvider
|
||||
{
|
||||
/**
|
||||
* @var array[] $data each element being a tuple of [$data, $expiration], where the expiration is int|bool
|
||||
*/
|
||||
/** @var array[] $data each element being a tuple of [$data, $expiration], where the expiration is int|bool */
|
||||
private $data = [];
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
/** @var int */
|
||||
private $hitsCount = 0;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
/** @var int */
|
||||
private $missesCount = 0;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
/** @var int */
|
||||
private $upTime;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,21 +1,4 @@
|
|||
<?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;
|
||||
|
||||
|
|
@ -23,27 +6,20 @@ namespace Doctrine\Common\Cache;
|
|||
* Interface for cache drivers.
|
||||
*
|
||||
* @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 Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
interface Cache
|
||||
{
|
||||
const STATS_HITS = 'hits';
|
||||
const STATS_MISSES = 'misses';
|
||||
const STATS_UPTIME = 'uptime';
|
||||
const STATS_MEMORY_USAGE = 'memory_usage';
|
||||
const STATS_MEMORY_AVAILABLE = 'memory_available';
|
||||
public const STATS_HITS = 'hits';
|
||||
public const STATS_MISSES = 'misses';
|
||||
public const STATS_UPTIME = 'uptime';
|
||||
public const STATS_MEMORY_USAGE = 'memory_usage';
|
||||
public const STATS_MEMORY_AVAILABLE = 'memory_available';
|
||||
/**
|
||||
* Only for backward compatibility (may be removed in next major release)
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
const STATS_MEMORY_AVAILIABLE = 'memory_available';
|
||||
public const STATS_MEMORY_AVAILIABLE = 'memory_available';
|
||||
|
||||
/**
|
||||
* Fetches an entry from the cache.
|
||||
|
|
@ -108,8 +84,6 @@ interface Cache
|
|||
* - <b>memory_available</b>
|
||||
* Memory allowed to use for storage.
|
||||
*
|
||||
* @since 2.2
|
||||
*
|
||||
* @return array|null An associative array with server's statistics if available, NULL otherwise.
|
||||
*/
|
||||
public function getStats();
|
||||
|
|
|
|||
|
|
@ -1,37 +1,18 @@
|
|||
<?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;
|
||||
|
||||
use function array_combine;
|
||||
use function array_key_exists;
|
||||
use function array_map;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Base class for cache provider implementations.
|
||||
*
|
||||
* @since 2.2
|
||||
* @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 Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
*/
|
||||
abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, MultiGetCache, MultiPutCache
|
||||
abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, MultiOperationCache
|
||||
{
|
||||
const DOCTRINE_NAMESPACE_CACHEKEY = 'DoctrineNamespaceCacheKey[%s]';
|
||||
public const DOCTRINE_NAMESPACE_CACHEKEY = 'DoctrineNamespaceCacheKey[%s]';
|
||||
|
||||
/**
|
||||
* The namespace to prefix all cache ids with.
|
||||
|
|
@ -43,7 +24,7 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M
|
|||
/**
|
||||
* The namespace version.
|
||||
*
|
||||
* @var integer|null
|
||||
* @var int|null
|
||||
*/
|
||||
private $namespaceVersion;
|
||||
|
||||
|
|
@ -84,20 +65,22 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M
|
|||
public function fetchMultiple(array $keys)
|
||||
{
|
||||
if (empty($keys)) {
|
||||
return array();
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
// 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));
|
||||
$namespacedKeys = array_combine($keys, array_map([$this, 'getNamespacedId'], $keys));
|
||||
$items = $this->doFetchMultiple($namespacedKeys);
|
||||
$foundItems = array();
|
||||
$foundItems = [];
|
||||
|
||||
// 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]) || array_key_exists($namespacedKey, $items)) {
|
||||
$foundItems[$requestedKey] = $items[$namespacedKey];
|
||||
if (! isset($items[$namespacedKey]) && ! array_key_exists($namespacedKey, $items)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$foundItems[$requestedKey] = $items[$namespacedKey];
|
||||
}
|
||||
|
||||
return $foundItems;
|
||||
|
|
@ -108,7 +91,7 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M
|
|||
*/
|
||||
public function saveMultiple(array $keysAndValues, $lifetime = 0)
|
||||
{
|
||||
$namespacedKeysAndValues = array();
|
||||
$namespacedKeysAndValues = [];
|
||||
foreach ($keysAndValues as $key => $value) {
|
||||
$namespacedKeysAndValues[$this->getNamespacedId($key)] = $value;
|
||||
}
|
||||
|
|
@ -132,6 +115,14 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M
|
|||
return $this->doSave($this->getNamespacedId($id), $data, $lifeTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteMultiple(array $keys)
|
||||
{
|
||||
return $this->doDeleteMultiple(array_map([$this, 'getNamespacedId'], $keys));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
@ -180,36 +171,32 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M
|
|||
*
|
||||
* @return string The namespaced id.
|
||||
*/
|
||||
private function getNamespacedId($id)
|
||||
private function getNamespacedId(string $id) : string
|
||||
{
|
||||
$namespaceVersion = $this->getNamespaceVersion();
|
||||
$namespaceVersion = $this->getNamespaceVersion();
|
||||
|
||||
return sprintf('%s[%s][%s]', $this->namespace, $id, $namespaceVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the namespace cache key.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getNamespaceCacheKey()
|
||||
private function getNamespaceCacheKey() : string
|
||||
{
|
||||
return sprintf(self::DOCTRINE_NAMESPACE_CACHEKEY, $this->namespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the namespace version.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
private function getNamespaceVersion()
|
||||
private function getNamespaceVersion() : int
|
||||
{
|
||||
if (null !== $this->namespaceVersion) {
|
||||
if ($this->namespaceVersion !== null) {
|
||||
return $this->namespaceVersion;
|
||||
}
|
||||
|
||||
$namespaceCacheKey = $this->getNamespaceCacheKey();
|
||||
$this->namespaceVersion = $this->doFetch($namespaceCacheKey) ?: 1;
|
||||
$namespaceCacheKey = $this->getNamespaceCacheKey();
|
||||
$this->namespaceVersion = (int) $this->doFetch($namespaceCacheKey) ?: 1;
|
||||
|
||||
return $this->namespaceVersion;
|
||||
}
|
||||
|
|
@ -218,16 +205,20 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M
|
|||
* Default implementation of doFetchMultiple. Each driver that supports multi-get should owerwrite it.
|
||||
*
|
||||
* @param array $keys Array of keys to retrieve from cache
|
||||
*
|
||||
* @return array Array of values retrieved for the given keys.
|
||||
*/
|
||||
protected function doFetchMultiple(array $keys)
|
||||
{
|
||||
$returnValues = array();
|
||||
$returnValues = [];
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if (false !== ($item = $this->doFetch($key)) || $this->doContains($key)) {
|
||||
$returnValues[$key] = $item;
|
||||
$item = $this->doFetch($key);
|
||||
if ($item === false && ! $this->doContains($key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$returnValues[$key] = $item;
|
||||
}
|
||||
|
||||
return $returnValues;
|
||||
|
|
@ -254,9 +245,9 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M
|
|||
/**
|
||||
* 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).
|
||||
* @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.
|
||||
*/
|
||||
|
|
@ -265,9 +256,11 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M
|
|||
$success = true;
|
||||
|
||||
foreach ($keysAndValues as $key => $value) {
|
||||
if (!$this->doSave($key, $value, $lifetime)) {
|
||||
$success = false;
|
||||
if ($this->doSave($key, $value, $lifetime)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$success = false;
|
||||
}
|
||||
|
||||
return $success;
|
||||
|
|
@ -285,6 +278,28 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M
|
|||
*/
|
||||
abstract protected function doSave($id, $data, $lifeTime = 0);
|
||||
|
||||
/**
|
||||
* Default implementation of doDeleteMultiple. Each driver that supports multi-delete should override it.
|
||||
*
|
||||
* @param array $keys Array of keys to delete from cache
|
||||
*
|
||||
* @return bool TRUE if the operation was successful, FALSE if it wasn't
|
||||
*/
|
||||
protected function doDeleteMultiple(array $keys)
|
||||
{
|
||||
$success = true;
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if ($this->doDelete($key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$success = false;
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a cache entry.
|
||||
*
|
||||
|
|
@ -304,8 +319,6 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M
|
|||
/**
|
||||
* Retrieves cached information from the data store.
|
||||
*
|
||||
* @since 2.2
|
||||
*
|
||||
* @return array|null An associative array with server's statistics if available, NULL otherwise.
|
||||
*/
|
||||
abstract protected function doGetStats();
|
||||
|
|
|
|||
|
|
@ -1,44 +1,36 @@
|
|||
<?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;
|
||||
|
||||
use Traversable;
|
||||
use function array_values;
|
||||
use function count;
|
||||
use function iterator_to_array;
|
||||
|
||||
/**
|
||||
* Cache provider that allows to easily chain multiple cache providers
|
||||
*
|
||||
* @author Michaël Gallego <mic.gallego@gmail.com>
|
||||
*/
|
||||
class ChainCache extends CacheProvider
|
||||
{
|
||||
/**
|
||||
* @var CacheProvider[]
|
||||
*/
|
||||
private $cacheProviders = array();
|
||||
/** @var CacheProvider[] */
|
||||
private $cacheProviders = [];
|
||||
|
||||
/** @var int */
|
||||
private $defaultLifeTimeForDownstreamCacheProviders = 0;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param CacheProvider[] $cacheProviders
|
||||
*/
|
||||
public function __construct($cacheProviders = array())
|
||||
public function __construct($cacheProviders = [])
|
||||
{
|
||||
$this->cacheProviders = $cacheProviders;
|
||||
$this->cacheProviders = $cacheProviders instanceof Traversable
|
||||
? iterator_to_array($cacheProviders, false)
|
||||
: array_values($cacheProviders);
|
||||
}
|
||||
|
||||
public function setDefaultLifeTimeForDownstreamCacheProviders(int $defaultLifeTimeForDownstreamCacheProviders) : void
|
||||
{
|
||||
$this->defaultLifeTimeForDownstreamCacheProviders = $defaultLifeTimeForDownstreamCacheProviders;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -63,8 +55,8 @@ class ChainCache extends CacheProvider
|
|||
$value = $cacheProvider->doFetch($id);
|
||||
|
||||
// We populate all the previous cache layers (that are assumed to be faster)
|
||||
for ($subKey = $key - 1 ; $subKey >= 0 ; $subKey--) {
|
||||
$this->cacheProviders[$subKey]->doSave($id, $value);
|
||||
for ($subKey = $key - 1; $subKey >= 0; $subKey--) {
|
||||
$this->cacheProviders[$subKey]->doSave($id, $value, $this->defaultLifeTimeForDownstreamCacheProviders);
|
||||
}
|
||||
|
||||
return $value;
|
||||
|
|
@ -74,6 +66,34 @@ class ChainCache extends CacheProvider
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFetchMultiple(array $keys)
|
||||
{
|
||||
/** @var CacheProvider[] $traversedProviders */
|
||||
$traversedProviders = [];
|
||||
$keysCount = count($keys);
|
||||
$fetchedValues = [];
|
||||
|
||||
foreach ($this->cacheProviders as $key => $cacheProvider) {
|
||||
$fetchedValues = $cacheProvider->doFetchMultiple($keys);
|
||||
|
||||
// We populate all the previous cache layers (that are assumed to be faster)
|
||||
if (count($fetchedValues) === $keysCount) {
|
||||
foreach ($traversedProviders as $previousCacheProvider) {
|
||||
$previousCacheProvider->doSaveMultiple($fetchedValues, $this->defaultLifeTimeForDownstreamCacheProviders);
|
||||
}
|
||||
|
||||
return $fetchedValues;
|
||||
}
|
||||
|
||||
$traversedProviders[] = $cacheProvider;
|
||||
}
|
||||
|
||||
return $fetchedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
|
@ -102,6 +122,20 @@ class ChainCache extends CacheProvider
|
|||
return $stored;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSaveMultiple(array $keysAndValues, $lifetime = 0)
|
||||
{
|
||||
$stored = true;
|
||||
|
||||
foreach ($this->cacheProviders as $cacheProvider) {
|
||||
$stored = $cacheProvider->doSaveMultiple($keysAndValues, $lifetime) && $stored;
|
||||
}
|
||||
|
||||
return $stored;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
|
@ -116,6 +150,20 @@ class ChainCache extends CacheProvider
|
|||
return $deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDeleteMultiple(array $keys)
|
||||
{
|
||||
$deleted = true;
|
||||
|
||||
foreach ($this->cacheProviders as $cacheProvider) {
|
||||
$deleted = $cacheProvider->doDeleteMultiple($keys) && $deleted;
|
||||
}
|
||||
|
||||
return $deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
|
@ -136,7 +184,7 @@ class ChainCache extends CacheProvider
|
|||
protected function doGetStats()
|
||||
{
|
||||
// We return all the stats from all adapters
|
||||
$stats = array();
|
||||
$stats = [];
|
||||
|
||||
foreach ($this->cacheProviders as $cacheProvider) {
|
||||
$stats[] = $cacheProvider->doGetStats();
|
||||
|
|
|
|||
|
|
@ -1,21 +1,4 @@
|
|||
<?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;
|
||||
|
||||
|
|
@ -26,8 +9,6 @@ namespace Doctrine\Common\Cache;
|
|||
* global "flushing", see {@see FlushableCache}.
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
* @since 1.4
|
||||
* @author Adirelle <adirelle@gmail.com>
|
||||
*/
|
||||
interface ClearableCache
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,45 +1,27 @@
|
|||
<?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;
|
||||
|
||||
use \Couchbase;
|
||||
use Couchbase;
|
||||
use function explode;
|
||||
use function time;
|
||||
|
||||
/**
|
||||
* Couchbase cache provider.
|
||||
*
|
||||
* @deprecated Couchbase SDK 1.x is now deprecated. Use \Doctrine\Common\Cache\CouchbaseBucketCache instead.
|
||||
* https://developer.couchbase.com/documentation/server/current/sdk/php/compatibility-versions-features.html
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.4
|
||||
* @author Michael Nitschinger <michael@nitschinger.at>
|
||||
*/
|
||||
class CouchbaseCache extends CacheProvider
|
||||
{
|
||||
/**
|
||||
* @var Couchbase|null
|
||||
*/
|
||||
/** @var Couchbase|null */
|
||||
private $couchbase;
|
||||
|
||||
/**
|
||||
* Sets the Couchbase instance to use.
|
||||
*
|
||||
* @param Couchbase $couchbase
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setCouchbase(Couchbase $couchbase)
|
||||
|
|
@ -70,7 +52,7 @@ class CouchbaseCache extends CacheProvider
|
|||
*/
|
||||
protected function doContains($id)
|
||||
{
|
||||
return (null !== $this->couchbase->get($id));
|
||||
return $this->couchbase->get($id) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -81,6 +63,7 @@ class CouchbaseCache extends CacheProvider
|
|||
if ($lifeTime > 30 * 24 * 3600) {
|
||||
$lifeTime = time() + $lifeTime;
|
||||
}
|
||||
|
||||
return $this->couchbase->set($id, $data, (int) $lifeTime);
|
||||
}
|
||||
|
||||
|
|
@ -107,15 +90,16 @@ class CouchbaseCache extends CacheProvider
|
|||
{
|
||||
$stats = $this->couchbase->getStats();
|
||||
$servers = $this->couchbase->getServers();
|
||||
$server = explode(":", $servers[0]);
|
||||
$key = $server[0] . ":" . "11210";
|
||||
$server = explode(':', $servers[0]);
|
||||
$key = $server[0] . ':11210';
|
||||
$stats = $stats[$key];
|
||||
return array(
|
||||
|
||||
return [
|
||||
Cache::STATS_HITS => $stats['get_hits'],
|
||||
Cache::STATS_MISSES => $stats['get_misses'],
|
||||
Cache::STATS_UPTIME => $stats['uptime'],
|
||||
Cache::STATS_MEMORY_USAGE => $stats['bytes'],
|
||||
Cache::STATS_MEMORY_AVAILABLE => $stats['limit_maxbytes'],
|
||||
);
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,30 +1,39 @@
|
|||
<?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;
|
||||
|
||||
use FilesystemIterator;
|
||||
use InvalidArgumentException;
|
||||
use Iterator;
|
||||
use RecursiveDirectoryIterator;
|
||||
use RecursiveIteratorIterator;
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
use const PATHINFO_DIRNAME;
|
||||
use function bin2hex;
|
||||
use function chmod;
|
||||
use function defined;
|
||||
use function disk_free_space;
|
||||
use function file_exists;
|
||||
use function file_put_contents;
|
||||
use function gettype;
|
||||
use function hash;
|
||||
use function is_dir;
|
||||
use function is_int;
|
||||
use function is_writable;
|
||||
use function mkdir;
|
||||
use function pathinfo;
|
||||
use function realpath;
|
||||
use function rename;
|
||||
use function rmdir;
|
||||
use function sprintf;
|
||||
use function strlen;
|
||||
use function strrpos;
|
||||
use function substr;
|
||||
use function tempnam;
|
||||
use function unlink;
|
||||
|
||||
/**
|
||||
* Base file cache driver.
|
||||
*
|
||||
* @since 2.3
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
* @author Tobias Schultze <http://tobion.de>
|
||||
*/
|
||||
abstract class FileCache extends CacheProvider
|
||||
{
|
||||
|
|
@ -42,54 +51,44 @@ abstract class FileCache extends CacheProvider
|
|||
*/
|
||||
private $extension;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
/** @var int */
|
||||
private $umask;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
/** @var int */
|
||||
private $directoryStringLength;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
/** @var int */
|
||||
private $extensionStringLength;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
/** @var bool */
|
||||
private $isRunningOnWindows;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $directory The cache directory.
|
||||
* @param string $extension The cache file extension.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function __construct($directory, $extension = '', $umask = 0002)
|
||||
{
|
||||
// YES, this needs to be *before* createPathIfNeeded()
|
||||
if ( ! is_int($umask)) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
if (! is_int($umask)) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'The umask parameter is required to be integer, was: %s',
|
||||
gettype($umask)
|
||||
));
|
||||
}
|
||||
$this->umask = $umask;
|
||||
|
||||
if ( ! $this->createPathIfNeeded($directory)) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
if (! $this->createPathIfNeeded($directory)) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'The directory "%s" does not exist and could not be created.',
|
||||
$directory
|
||||
));
|
||||
}
|
||||
|
||||
if ( ! is_writable($directory)) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
if (! is_writable($directory)) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'The directory "%s" is not writable.',
|
||||
$directory
|
||||
));
|
||||
|
|
@ -134,8 +133,7 @@ abstract class FileCache extends CacheProvider
|
|||
$hash = hash('sha256', $id);
|
||||
|
||||
// This ensures that the filename is unique and that there are no invalid chars in it.
|
||||
if (
|
||||
'' === $id
|
||||
if ($id === ''
|
||||
|| ((strlen($id) * 2 + $this->extensionStringLength) > 255)
|
||||
|| ($this->isRunningOnWindows && ($this->directoryStringLength + 4 + strlen($id) * 2 + $this->extensionStringLength) > 258)
|
||||
) {
|
||||
|
|
@ -195,32 +193,33 @@ abstract class FileCache extends CacheProvider
|
|||
{
|
||||
$usage = 0;
|
||||
foreach ($this->getIterator() as $name => $file) {
|
||||
if (! $file->isDir() && $this->isFilenameEndingWithExtension($name)) {
|
||||
$usage += $file->getSize();
|
||||
if ($file->isDir() || ! $this->isFilenameEndingWithExtension($name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$usage += $file->getSize();
|
||||
}
|
||||
|
||||
$free = disk_free_space($this->directory);
|
||||
|
||||
return array(
|
||||
return [
|
||||
Cache::STATS_HITS => null,
|
||||
Cache::STATS_MISSES => null,
|
||||
Cache::STATS_UPTIME => null,
|
||||
Cache::STATS_MEMORY_USAGE => $usage,
|
||||
Cache::STATS_MEMORY_AVAILABLE => $free,
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create path if needed.
|
||||
*
|
||||
* @param string $path
|
||||
* @return bool TRUE on success or if path already exists, FALSE if path cannot be created.
|
||||
*/
|
||||
private function createPathIfNeeded($path)
|
||||
private function createPathIfNeeded(string $path) : bool
|
||||
{
|
||||
if ( ! is_dir($path)) {
|
||||
if (false === @mkdir($path, 0777 & (~$this->umask), true) && !is_dir($path)) {
|
||||
if (! is_dir($path)) {
|
||||
if (@mkdir($path, 0777 & (~$this->umask), true) === false && ! is_dir($path)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -236,15 +235,15 @@ abstract class FileCache extends CacheProvider
|
|||
*
|
||||
* @return bool TRUE on success, FALSE if path cannot be created, if path is not writable or an any other error.
|
||||
*/
|
||||
protected function writeFile($filename, $content)
|
||||
protected function writeFile(string $filename, string $content) : bool
|
||||
{
|
||||
$filepath = pathinfo($filename, PATHINFO_DIRNAME);
|
||||
|
||||
if ( ! $this->createPathIfNeeded($filepath)) {
|
||||
if (! $this->createPathIfNeeded($filepath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! is_writable($filepath)) {
|
||||
if (! is_writable($filepath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -252,6 +251,7 @@ abstract class FileCache extends CacheProvider
|
|||
@chmod($tmpFile, 0666 & (~$this->umask));
|
||||
|
||||
if (file_put_contents($tmpFile, $content) !== false) {
|
||||
@chmod($tmpFile, 0666 & (~$this->umask));
|
||||
if (@rename($tmpFile, $filename)) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -262,25 +262,20 @@ abstract class FileCache extends CacheProvider
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Iterator
|
||||
*/
|
||||
private function getIterator()
|
||||
private function getIterator() : Iterator
|
||||
{
|
||||
return new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS),
|
||||
\RecursiveIteratorIterator::CHILD_FIRST
|
||||
return new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($this->directory, FilesystemIterator::SKIP_DOTS),
|
||||
RecursiveIteratorIterator::CHILD_FIRST
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name The filename
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isFilenameEndingWithExtension($name)
|
||||
private function isFilenameEndingWithExtension(string $name) : bool
|
||||
{
|
||||
return '' === $this->extension
|
||||
return $this->extension === ''
|
||||
|| strrpos($name, $this->extension) === (strlen($name) - $this->extensionStringLength);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,33 +1,22 @@
|
|||
<?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;
|
||||
|
||||
use const PHP_EOL;
|
||||
use function fclose;
|
||||
use function fgets;
|
||||
use function fopen;
|
||||
use function is_file;
|
||||
use function serialize;
|
||||
use function time;
|
||||
use function unserialize;
|
||||
|
||||
/**
|
||||
* Filesystem cache driver.
|
||||
*
|
||||
* @since 2.3
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
*/
|
||||
class FilesystemCache extends FileCache
|
||||
{
|
||||
const EXTENSION = '.doctrinecache.data';
|
||||
public const EXTENSION = '.doctrinecache.data';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
|
@ -46,13 +35,14 @@ class FilesystemCache extends FileCache
|
|||
$lifetime = -1;
|
||||
$filename = $this->getFilename($id);
|
||||
|
||||
if ( ! is_file($filename)) {
|
||||
if (! is_file($filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$resource = fopen($filename, "r");
|
||||
$resource = fopen($filename, 'r');
|
||||
$line = fgets($resource);
|
||||
|
||||
if (false !== ($line = fgets($resource))) {
|
||||
if ($line !== false) {
|
||||
$lifetime = (int) $line;
|
||||
}
|
||||
|
||||
|
|
@ -62,7 +52,7 @@ class FilesystemCache extends FileCache
|
|||
return false;
|
||||
}
|
||||
|
||||
while (false !== ($line = fgets($resource))) {
|
||||
while (($line = fgets($resource)) !== false) {
|
||||
$data .= $line;
|
||||
}
|
||||
|
||||
|
|
@ -79,13 +69,14 @@ class FilesystemCache extends FileCache
|
|||
$lifetime = -1;
|
||||
$filename = $this->getFilename($id);
|
||||
|
||||
if ( ! is_file($filename)) {
|
||||
if (! is_file($filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$resource = fopen($filename, "r");
|
||||
$resource = fopen($filename, 'r');
|
||||
$line = fgets($resource);
|
||||
|
||||
if (false !== ($line = fgets($resource))) {
|
||||
if ($line !== false) {
|
||||
$lifetime = (int) $line;
|
||||
}
|
||||
|
||||
|
|
@ -103,8 +94,8 @@ class FilesystemCache extends FileCache
|
|||
$lifeTime = time() + $lifeTime;
|
||||
}
|
||||
|
||||
$data = serialize($data);
|
||||
$filename = $this->getFilename($id);
|
||||
$data = serialize($data);
|
||||
$filename = $this->getFilename($id);
|
||||
|
||||
return $this->writeFile($filename, $lifeTime . PHP_EOL . $data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,4 @@
|
|||
<?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;
|
||||
|
||||
|
|
@ -23,8 +6,6 @@ namespace Doctrine\Common\Cache;
|
|||
* Interface for cache that can be flushed.
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
* @since 1.4
|
||||
* @author Adirelle <adirelle@gmail.com>
|
||||
*/
|
||||
interface FlushableCache
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,49 +1,25 @@
|
|||
<?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;
|
||||
|
||||
use \Memcache;
|
||||
use Memcache;
|
||||
use function time;
|
||||
|
||||
/**
|
||||
* Memcache cache provider.
|
||||
*
|
||||
* @deprecated
|
||||
*
|
||||
* @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>
|
||||
*/
|
||||
class MemcacheCache extends CacheProvider
|
||||
{
|
||||
/**
|
||||
* @var Memcache|null
|
||||
*/
|
||||
/** @var Memcache|null */
|
||||
private $memcache;
|
||||
|
||||
/**
|
||||
* Sets the memcache instance to use.
|
||||
*
|
||||
* @param Memcache $memcache
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setMemcache(Memcache $memcache)
|
||||
|
|
@ -76,9 +52,9 @@ class MemcacheCache extends CacheProvider
|
|||
{
|
||||
$flags = null;
|
||||
$this->memcache->get($id, $flags);
|
||||
|
||||
|
||||
//if memcache has changed the value of "flags", it means the value exists
|
||||
return ($flags !== null);
|
||||
return $flags !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -89,6 +65,7 @@ class MemcacheCache extends CacheProvider
|
|||
if ($lifeTime > 30 * 24 * 3600) {
|
||||
$lifeTime = time() + $lifeTime;
|
||||
}
|
||||
|
||||
return $this->memcache->set($id, $data, 0, (int) $lifeTime);
|
||||
}
|
||||
|
||||
|
|
@ -115,12 +92,13 @@ class MemcacheCache extends CacheProvider
|
|||
protected function doGetStats()
|
||||
{
|
||||
$stats = $this->memcache->getStats();
|
||||
return array(
|
||||
|
||||
return [
|
||||
Cache::STATS_HITS => $stats['get_hits'],
|
||||
Cache::STATS_MISSES => $stats['get_misses'],
|
||||
Cache::STATS_UPTIME => $stats['uptime'],
|
||||
Cache::STATS_MEMORY_USAGE => $stats['bytes'],
|
||||
Cache::STATS_MEMORY_AVAILABLE => $stats['limit_maxbytes'],
|
||||
);
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,49 +1,29 @@
|
|||
<?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;
|
||||
|
||||
use \Memcached;
|
||||
use Memcached;
|
||||
use function array_keys;
|
||||
use function preg_match;
|
||||
use function strlen;
|
||||
use function strpos;
|
||||
use function time;
|
||||
|
||||
/**
|
||||
* Memcached cache provider.
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.2
|
||||
* @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 MemcachedCache extends CacheProvider
|
||||
{
|
||||
/**
|
||||
* @var Memcached|null
|
||||
*/
|
||||
public const CACHE_ID_MAX_LENGTH = 250;
|
||||
|
||||
/** @var Memcached|null */
|
||||
private $memcached;
|
||||
|
||||
/**
|
||||
* Sets the memcache instance to use.
|
||||
*
|
||||
* @param Memcached $memcached
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setMemcached(Memcached $memcached)
|
||||
|
|
@ -82,6 +62,10 @@ class MemcachedCache extends CacheProvider
|
|||
*/
|
||||
protected function doSaveMultiple(array $keysAndValues, $lifetime = 0)
|
||||
{
|
||||
foreach (array_keys($keysAndValues) as $id) {
|
||||
$this->validateCacheId($id);
|
||||
}
|
||||
|
||||
if ($lifetime > 30 * 24 * 3600) {
|
||||
$lifetime = time() + $lifetime;
|
||||
}
|
||||
|
|
@ -104,12 +88,24 @@ class MemcachedCache extends CacheProvider
|
|||
*/
|
||||
protected function doSave($id, $data, $lifeTime = 0)
|
||||
{
|
||||
$this->validateCacheId($id);
|
||||
|
||||
if ($lifeTime > 30 * 24 * 3600) {
|
||||
$lifeTime = time() + $lifeTime;
|
||||
}
|
||||
|
||||
return $this->memcached->set($id, $data, (int) $lifeTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDeleteMultiple(array $keys)
|
||||
{
|
||||
return $this->memcached->deleteMulti($keys)
|
||||
|| $this->memcached->getResultCode() === Memcached::RES_NOTFOUND;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
@ -136,12 +132,39 @@ class MemcachedCache extends CacheProvider
|
|||
$servers = $this->memcached->getServerList();
|
||||
$key = $servers[0]['host'] . ':' . $servers[0]['port'];
|
||||
$stats = $stats[$key];
|
||||
return array(
|
||||
|
||||
return [
|
||||
Cache::STATS_HITS => $stats['get_hits'],
|
||||
Cache::STATS_MISSES => $stats['get_misses'],
|
||||
Cache::STATS_UPTIME => $stats['uptime'],
|
||||
Cache::STATS_MEMORY_USAGE => $stats['bytes'],
|
||||
Cache::STATS_MEMORY_AVAILABLE => $stats['limit_maxbytes'],
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the cache id
|
||||
*
|
||||
* @see https://github.com/memcached/memcached/blob/1.5.12/doc/protocol.txt#L41-L49
|
||||
*
|
||||
* @param string $id
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws InvalidCacheId
|
||||
*/
|
||||
private function validateCacheId($id)
|
||||
{
|
||||
if (strlen($id) > self::CACHE_ID_MAX_LENGTH) {
|
||||
throw InvalidCacheId::exceedsMaxLength($id, self::CACHE_ID_MAX_LENGTH);
|
||||
}
|
||||
|
||||
if (strpos($id, ' ') !== false) {
|
||||
throw InvalidCacheId::containsUnauthorizedCharacter($id, ' ');
|
||||
}
|
||||
|
||||
if (preg_match('/[\t\r\n]/', $id) === 1) {
|
||||
throw InvalidCacheId::containsControlCharacter($id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,41 +1,22 @@
|
|||
<?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;
|
||||
|
||||
use MongoBinData;
|
||||
use InvalidArgumentException;
|
||||
use MongoCollection;
|
||||
use MongoCursorException;
|
||||
use MongoDate;
|
||||
use MongoDB\Collection;
|
||||
use const E_USER_DEPRECATED;
|
||||
use function trigger_error;
|
||||
|
||||
/**
|
||||
* MongoDB cache provider.
|
||||
*
|
||||
* @since 1.1
|
||||
* @author Jeremy Mikola <jmikola@gmail.com>
|
||||
*/
|
||||
class MongoDBCache extends CacheProvider
|
||||
{
|
||||
/**
|
||||
* The data field will store the serialized PHP value.
|
||||
*/
|
||||
const DATA_FIELD = 'd';
|
||||
public const DATA_FIELD = 'd';
|
||||
|
||||
/**
|
||||
* The expiration field will store a MongoDate value indicating when the
|
||||
|
|
@ -52,29 +33,33 @@ class MongoDBCache extends CacheProvider
|
|||
*
|
||||
* @see http://docs.mongodb.org/manual/tutorial/expire-data/
|
||||
*/
|
||||
const EXPIRATION_FIELD = 'e';
|
||||
public const EXPIRATION_FIELD = 'e';
|
||||
|
||||
/** @var CacheProvider */
|
||||
private $provider;
|
||||
|
||||
/**
|
||||
* @var MongoCollection
|
||||
*/
|
||||
private $collection;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* This provider will default to the write concern and read preference
|
||||
* options set on the MongoCollection instance (or inherited from MongoDB or
|
||||
* options set on the collection instance (or inherited from MongoDB or
|
||||
* MongoClient). Using an unacknowledged write concern (< 1) may make the
|
||||
* return values of delete() and save() unreliable. Reading from secondaries
|
||||
* may make contain() and fetch() unreliable.
|
||||
*
|
||||
* @see http://www.php.net/manual/en/mongo.readpreferences.php
|
||||
* @see http://www.php.net/manual/en/mongo.writeconcerns.php
|
||||
* @param MongoCollection $collection
|
||||
*
|
||||
* @param MongoCollection|Collection $collection
|
||||
*/
|
||||
public function __construct(MongoCollection $collection)
|
||||
public function __construct($collection)
|
||||
{
|
||||
$this->collection = $collection;
|
||||
if ($collection instanceof MongoCollection) {
|
||||
@trigger_error('Using a MongoCollection instance for creating a cache adapter is deprecated and will be removed in 2.0', E_USER_DEPRECATED);
|
||||
$this->provider = new LegacyMongoDBCache($collection);
|
||||
} elseif ($collection instanceof Collection) {
|
||||
$this->provider = new ExtMongoDBCache($collection);
|
||||
} else {
|
||||
throw new InvalidArgumentException('Invalid collection given - expected a MongoCollection or MongoDB\Collection instance');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -82,18 +67,7 @@ class MongoDBCache extends CacheProvider
|
|||
*/
|
||||
protected function doFetch($id)
|
||||
{
|
||||
$document = $this->collection->findOne(array('_id' => $id), array(self::DATA_FIELD, self::EXPIRATION_FIELD));
|
||||
|
||||
if ($document === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->isExpired($document)) {
|
||||
$this->doDelete($id);
|
||||
return false;
|
||||
}
|
||||
|
||||
return unserialize($document[self::DATA_FIELD]->bin);
|
||||
return $this->provider->doFetch($id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -101,18 +75,7 @@ class MongoDBCache extends CacheProvider
|
|||
*/
|
||||
protected function doContains($id)
|
||||
{
|
||||
$document = $this->collection->findOne(array('_id' => $id), array(self::EXPIRATION_FIELD));
|
||||
|
||||
if ($document === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->isExpired($document)) {
|
||||
$this->doDelete($id);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return $this->provider->doContains($id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -120,20 +83,7 @@ class MongoDBCache extends CacheProvider
|
|||
*/
|
||||
protected function doSave($id, $data, $lifeTime = 0)
|
||||
{
|
||||
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;
|
||||
return $this->provider->doSave($id, $data, $lifeTime);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -141,9 +91,7 @@ class MongoDBCache extends CacheProvider
|
|||
*/
|
||||
protected function doDelete($id)
|
||||
{
|
||||
$result = $this->collection->remove(array('_id' => $id));
|
||||
|
||||
return isset($result['ok']) ? $result['ok'] == 1 : true;
|
||||
return $this->provider->doDelete($id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -151,10 +99,7 @@ class MongoDBCache extends CacheProvider
|
|||
*/
|
||||
protected function doFlush()
|
||||
{
|
||||
// Use remove() in lieu of drop() to maintain any collection indexes
|
||||
$result = $this->collection->remove();
|
||||
|
||||
return isset($result['ok']) ? $result['ok'] == 1 : true;
|
||||
return $this->provider->doFlush();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -162,36 +107,6 @@ class MongoDBCache extends CacheProvider
|
|||
*/
|
||||
protected function doGetStats()
|
||||
{
|
||||
$serverStatus = $this->collection->db->command(array(
|
||||
'serverStatus' => 1,
|
||||
'locks' => 0,
|
||||
'metrics' => 0,
|
||||
'recordStats' => 0,
|
||||
'repl' => 0,
|
||||
));
|
||||
|
||||
$collStats = $this->collection->db->command(array('collStats' => 1));
|
||||
|
||||
return array(
|
||||
Cache::STATS_HITS => null,
|
||||
Cache::STATS_MISSES => 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,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the document is expired.
|
||||
*
|
||||
* @param array $document
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isExpired(array $document)
|
||||
{
|
||||
return isset($document[self::EXPIRATION_FIELD]) &&
|
||||
$document[self::EXPIRATION_FIELD] instanceof MongoDate &&
|
||||
$document[self::EXPIRATION_FIELD]->sec < time();
|
||||
return $this->provider->doGetStats();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,30 +1,13 @@
|
|||
<?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 get many items at once.
|
||||
*
|
||||
* @deprecated
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
* @since 1.4
|
||||
* @author Asmir Mustafic <goetas@gmail.com>
|
||||
*/
|
||||
interface MultiGetCache
|
||||
{
|
||||
|
|
@ -32,8 +15,9 @@ interface MultiGetCache
|
|||
* Returns an associative array of values for keys is found in cache.
|
||||
*
|
||||
* @param string[] $keys Array of keys to retrieve from cache
|
||||
*
|
||||
* @return mixed[] Array of retrieved values, indexed by the specified keys.
|
||||
* Values that couldn't be retrieved are not contained in this array.
|
||||
*/
|
||||
function fetchMultiple(array $keys);
|
||||
public function fetchMultiple(array $keys);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,41 +1,24 @@
|
|||
<?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.
|
||||
*
|
||||
* @deprecated
|
||||
*
|
||||
* @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).
|
||||
* @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);
|
||||
public function saveMultiple(array $keysAndValues, $lifetime = 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,33 +1,29 @@
|
|||
<?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;
|
||||
|
||||
use function is_object;
|
||||
use function method_exists;
|
||||
use function restore_error_handler;
|
||||
use function serialize;
|
||||
use function set_error_handler;
|
||||
use function sprintf;
|
||||
use function time;
|
||||
use function var_export;
|
||||
|
||||
/**
|
||||
* Php file cache driver.
|
||||
*
|
||||
* @since 2.3
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
*/
|
||||
class PhpFileCache extends FileCache
|
||||
{
|
||||
const EXTENSION = '.doctrinecache.php';
|
||||
public const EXTENSION = '.doctrinecache.php';
|
||||
|
||||
/**
|
||||
* @var callable
|
||||
*
|
||||
* This is cached in a local static variable to avoid instantiating a closure each time we need an empty handler
|
||||
*/
|
||||
private static $emptyErrorHandler;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
|
@ -35,6 +31,9 @@ class PhpFileCache extends FileCache
|
|||
public function __construct($directory, $extension = self::EXTENSION, $umask = 0002)
|
||||
{
|
||||
parent::__construct($directory, $extension, $umask);
|
||||
|
||||
self::$emptyErrorHandler = static function () {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -44,7 +43,7 @@ class PhpFileCache extends FileCache
|
|||
{
|
||||
$value = $this->includeFileForId($id);
|
||||
|
||||
if (! $value) {
|
||||
if ($value === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -62,7 +61,7 @@ class PhpFileCache extends FileCache
|
|||
{
|
||||
$value = $this->includeFileForId($id);
|
||||
|
||||
if (! $value) {
|
||||
if ($value === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -78,41 +77,40 @@ class PhpFileCache extends FileCache
|
|||
$lifeTime = time() + $lifeTime;
|
||||
}
|
||||
|
||||
if (is_object($data) && ! method_exists($data, '__set_state')) {
|
||||
throw new \InvalidArgumentException(
|
||||
"Invalid argument given, PhpFileCache only allows objects that implement __set_state() " .
|
||||
"and fully support var_export(). You can use the FilesystemCache to save arbitrary object " .
|
||||
"graphs using serialize()/deserialize()."
|
||||
);
|
||||
}
|
||||
$filename = $this->getFilename($id);
|
||||
|
||||
$filename = $this->getFilename($id);
|
||||
|
||||
$value = array(
|
||||
$value = [
|
||||
'lifetime' => $lifeTime,
|
||||
'data' => $data
|
||||
);
|
||||
'data' => $data,
|
||||
];
|
||||
|
||||
$value = var_export($value, true);
|
||||
$code = sprintf('<?php return %s;', $value);
|
||||
if (is_object($data) && method_exists($data, '__set_state')) {
|
||||
$value = var_export($value, true);
|
||||
$code = sprintf('<?php return %s;', $value);
|
||||
} else {
|
||||
$value = var_export(serialize($value), true);
|
||||
$code = sprintf('<?php return unserialize(%s);', $value);
|
||||
}
|
||||
|
||||
return $this->writeFile($filename, $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
*
|
||||
* @return array|false
|
||||
* @return array|null
|
||||
*/
|
||||
private function includeFileForId($id)
|
||||
private function includeFileForId(string $id) : ?array
|
||||
{
|
||||
$fileName = $this->getFilename($id);
|
||||
|
||||
// note: error suppression is still faster than `file_exists`, `is_file` and `is_readable`
|
||||
$value = @include $fileName;
|
||||
set_error_handler(self::$emptyErrorHandler);
|
||||
|
||||
$value = include $fileName;
|
||||
|
||||
restore_error_handler();
|
||||
|
||||
if (! isset($value['lifetime'])) {
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
return $value;
|
||||
|
|
|
|||
|
|
@ -3,24 +3,21 @@
|
|||
namespace Doctrine\Common\Cache;
|
||||
|
||||
use Predis\ClientInterface;
|
||||
use function array_combine;
|
||||
use function array_filter;
|
||||
use function array_map;
|
||||
use function call_user_func_array;
|
||||
use function serialize;
|
||||
use function unserialize;
|
||||
|
||||
/**
|
||||
* Predis cache provider.
|
||||
*
|
||||
* @author othillo <othillo@othillo.nl>
|
||||
*/
|
||||
class PredisCache extends CacheProvider
|
||||
{
|
||||
/**
|
||||
* @var ClientInterface
|
||||
*/
|
||||
/** @var ClientInterface */
|
||||
private $client;
|
||||
|
||||
/**
|
||||
* @param ClientInterface $client
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(ClientInterface $client)
|
||||
{
|
||||
$this->client = $client;
|
||||
|
|
@ -32,7 +29,7 @@ class PredisCache extends CacheProvider
|
|||
protected function doFetch($id)
|
||||
{
|
||||
$result = $this->client->get($id);
|
||||
if (null === $result) {
|
||||
if ($result === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -44,7 +41,7 @@ class PredisCache extends CacheProvider
|
|||
*/
|
||||
protected function doFetchMultiple(array $keys)
|
||||
{
|
||||
$fetchedItems = call_user_func_array(array($this->client, 'mget'), $keys);
|
||||
$fetchedItems = call_user_func_array([$this->client, 'mget'], $keys);
|
||||
|
||||
return array_map('unserialize', array_filter(array_combine($keys, $fetchedItems)));
|
||||
}
|
||||
|
|
@ -59,18 +56,20 @@ class PredisCache extends CacheProvider
|
|||
|
||||
// Keys have lifetime, use SETEX for each of them
|
||||
foreach ($keysAndValues as $key => $value) {
|
||||
$response = $this->client->setex($key, $lifetime, serialize($value));
|
||||
$response = (string) $this->client->setex($key, $lifetime, serialize($value));
|
||||
|
||||
if ((string) $response != 'OK') {
|
||||
$success = false;
|
||||
if ($response == 'OK') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$success = false;
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
// No lifetime, use MSET
|
||||
$response = $this->client->mset(array_map(function ($value) {
|
||||
$response = $this->client->mset(array_map(static function ($value) {
|
||||
return serialize($value);
|
||||
}, $keysAndValues));
|
||||
|
||||
|
|
@ -108,6 +107,14 @@ class PredisCache extends CacheProvider
|
|||
return $this->client->del($id) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDeleteMultiple(array $keys)
|
||||
{
|
||||
return $this->client->del($keys) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
@ -125,12 +132,12 @@ class PredisCache extends CacheProvider
|
|||
{
|
||||
$info = $this->client->info();
|
||||
|
||||
return array(
|
||||
return [
|
||||
Cache::STATS_HITS => $info['Stats']['keyspace_hits'],
|
||||
Cache::STATS_MISSES => $info['Stats']['keyspace_misses'],
|
||||
Cache::STATS_UPTIME => $info['Server']['uptime_in_seconds'],
|
||||
Cache::STATS_MEMORY_USAGE => $info['Memory']['used_memory'],
|
||||
Cache::STATS_MEMORY_AVAILABLE => false
|
||||
);
|
||||
Cache::STATS_MEMORY_AVAILABLE => false,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,45 +1,31 @@
|
|||
<?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;
|
||||
|
||||
use Redis;
|
||||
use function array_combine;
|
||||
use function array_diff_key;
|
||||
use function array_fill_keys;
|
||||
use function array_filter;
|
||||
use function array_keys;
|
||||
use function count;
|
||||
use function defined;
|
||||
use function extension_loaded;
|
||||
use function is_bool;
|
||||
|
||||
/**
|
||||
* Redis cache provider.
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.2
|
||||
* @author Osman Ungur <osmanungur@gmail.com>
|
||||
*/
|
||||
class RedisCache extends CacheProvider
|
||||
{
|
||||
/**
|
||||
* @var Redis|null
|
||||
*/
|
||||
/** @var Redis|null */
|
||||
private $redis;
|
||||
|
||||
/**
|
||||
* Sets the redis instance to use.
|
||||
*
|
||||
* @param Redis $redis
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setRedis(Redis $redis)
|
||||
|
|
@ -74,15 +60,21 @@ class RedisCache extends CacheProvider
|
|||
$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();
|
||||
$keysToFilter = array_keys(array_filter($fetchedItems, static function ($item) : bool {
|
||||
return $item === false;
|
||||
}));
|
||||
|
||||
foreach ($fetchedItems as $key => $value) {
|
||||
if (false !== $value || $this->redis->exists($key)) {
|
||||
$foundItems[$key] = $value;
|
||||
if ($keysToFilter) {
|
||||
$multi = $this->redis->multi(Redis::PIPELINE);
|
||||
foreach ($keysToFilter as $key) {
|
||||
$multi->exists($key);
|
||||
}
|
||||
$existItems = array_filter($multi->exec());
|
||||
$missedItemKeys = array_diff_key($keysToFilter, $existItems);
|
||||
$fetchedItems = array_diff_key($fetchedItems, array_fill_keys($missedItemKeys, true));
|
||||
}
|
||||
|
||||
return $foundItems;
|
||||
return $fetchedItems;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -91,16 +83,14 @@ class RedisCache extends CacheProvider
|
|||
protected function doSaveMultiple(array $keysAndValues, $lifetime = 0)
|
||||
{
|
||||
if ($lifetime) {
|
||||
$success = true;
|
||||
|
||||
// Keys have lifetime, use SETEX for each of them
|
||||
$multi = $this->redis->multi(Redis::PIPELINE);
|
||||
foreach ($keysAndValues as $key => $value) {
|
||||
if (!$this->redis->setex($key, $lifetime, $value)) {
|
||||
$success = false;
|
||||
}
|
||||
$multi->setex($key, $lifetime, $value);
|
||||
}
|
||||
$succeeded = array_filter($multi->exec());
|
||||
|
||||
return $success;
|
||||
return count($succeeded) == count($keysAndValues);
|
||||
}
|
||||
|
||||
// No lifetime, use MSET
|
||||
|
|
@ -112,7 +102,13 @@ class RedisCache extends CacheProvider
|
|||
*/
|
||||
protected function doContains($id)
|
||||
{
|
||||
return $this->redis->exists($id);
|
||||
$exists = $this->redis->exists($id);
|
||||
|
||||
if (is_bool($exists)) {
|
||||
return $exists;
|
||||
}
|
||||
|
||||
return $exists > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -132,7 +128,15 @@ class RedisCache extends CacheProvider
|
|||
*/
|
||||
protected function doDelete($id)
|
||||
{
|
||||
return $this->redis->delete($id) >= 0;
|
||||
return $this->redis->del($id) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDeleteMultiple(array $keys)
|
||||
{
|
||||
return $this->redis->del($keys) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -149,13 +153,14 @@ class RedisCache extends CacheProvider
|
|||
protected function doGetStats()
|
||||
{
|
||||
$info = $this->redis->info();
|
||||
return array(
|
||||
|
||||
return [
|
||||
Cache::STATS_HITS => $info['keyspace_hits'],
|
||||
Cache::STATS_MISSES => $info['keyspace_misses'],
|
||||
Cache::STATS_UPTIME => $info['uptime_in_seconds'],
|
||||
Cache::STATS_MEMORY_USAGE => $info['used_memory'],
|
||||
Cache::STATS_MEMORY_AVAILABLE => false
|
||||
);
|
||||
Cache::STATS_MEMORY_AVAILABLE => false,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -163,14 +168,10 @@ class RedisCache extends CacheProvider
|
|||
* igbinary support, that is used. Otherwise the default PHP serializer is
|
||||
* used.
|
||||
*
|
||||
* @return integer One of the Redis::SERIALIZER_* constants
|
||||
* @return int One of the Redis::SERIALIZER_* constants
|
||||
*/
|
||||
protected function getSerializerValue()
|
||||
{
|
||||
if (defined('HHVM_VERSION')) {
|
||||
return Redis::SERIALIZER_PHP;
|
||||
}
|
||||
|
||||
if (defined('Redis::SERIALIZER_IGBINARY') && extension_loaded('igbinary')) {
|
||||
return Redis::SERIALIZER_IGBINARY;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,68 +1,50 @@
|
|||
<?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;
|
||||
|
||||
use SQLite3;
|
||||
use SQLite3Result;
|
||||
use const SQLITE3_ASSOC;
|
||||
use const SQLITE3_BLOB;
|
||||
use const SQLITE3_TEXT;
|
||||
use function array_search;
|
||||
use function implode;
|
||||
use function serialize;
|
||||
use function sprintf;
|
||||
use function time;
|
||||
use function unserialize;
|
||||
|
||||
/**
|
||||
* SQLite3 cache provider.
|
||||
*
|
||||
* @since 1.4
|
||||
* @author Jake Bell <jake@theunraveler.com>
|
||||
*/
|
||||
class SQLite3Cache extends CacheProvider
|
||||
{
|
||||
/**
|
||||
* The ID field will store the cache key.
|
||||
*/
|
||||
const ID_FIELD = 'k';
|
||||
public const ID_FIELD = 'k';
|
||||
|
||||
/**
|
||||
* The data field will store the serialized PHP value.
|
||||
*/
|
||||
const DATA_FIELD = 'd';
|
||||
public const DATA_FIELD = 'd';
|
||||
|
||||
/**
|
||||
* The expiration field will store a date value indicating when the
|
||||
* cache entry should expire.
|
||||
*/
|
||||
const EXPIRATION_FIELD = 'e';
|
||||
public const EXPIRATION_FIELD = 'e';
|
||||
|
||||
/**
|
||||
* @var SQLite3
|
||||
*/
|
||||
/** @var SQLite3 */
|
||||
private $sqlite;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
/** @var string */
|
||||
private $table;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* Calling the constructor will ensure that the database file and table
|
||||
* Calling the constructor will ensure that the database file and table
|
||||
* exist and will create both if they don't.
|
||||
*
|
||||
* @param SQLite3 $sqlite
|
||||
* @param string $table
|
||||
*/
|
||||
public function __construct(SQLite3 $sqlite, $table)
|
||||
|
|
@ -70,15 +52,20 @@ class SQLite3Cache extends CacheProvider
|
|||
$this->sqlite = $sqlite;
|
||||
$this->table = (string) $table;
|
||||
|
||||
list($id, $data, $exp) = $this->getFields();
|
||||
$this->ensureTableExists();
|
||||
}
|
||||
|
||||
return $this->sqlite->exec(sprintf(
|
||||
'CREATE TABLE IF NOT EXISTS %s(%s TEXT PRIMARY KEY NOT NULL, %s BLOB, %s INTEGER)',
|
||||
$table,
|
||||
$id,
|
||||
$data,
|
||||
$exp
|
||||
));
|
||||
private function ensureTableExists() : void
|
||||
{
|
||||
$this->sqlite->exec(
|
||||
sprintf(
|
||||
'CREATE TABLE IF NOT EXISTS %s(%s TEXT PRIMARY KEY NOT NULL, %s BLOB, %s INTEGER)',
|
||||
$this->table,
|
||||
static::ID_FIELD,
|
||||
static::DATA_FIELD,
|
||||
static::EXPIRATION_FIELD
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -86,11 +73,13 @@ class SQLite3Cache extends CacheProvider
|
|||
*/
|
||||
protected function doFetch($id)
|
||||
{
|
||||
if ($item = $this->findById($id)) {
|
||||
return unserialize($item[self::DATA_FIELD]);
|
||||
$item = $this->findById($id);
|
||||
|
||||
if (! $item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
return unserialize($item[self::DATA_FIELD]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -98,7 +87,7 @@ class SQLite3Cache extends CacheProvider
|
|||
*/
|
||||
protected function doContains($id)
|
||||
{
|
||||
return null !== $this->findById($id, false);
|
||||
return $this->findById($id, false) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -124,7 +113,7 @@ class SQLite3Cache extends CacheProvider
|
|||
*/
|
||||
protected function doDelete($id)
|
||||
{
|
||||
list($idField) = $this->getFields();
|
||||
[$idField] = $this->getFields();
|
||||
|
||||
$statement = $this->sqlite->prepare(sprintf(
|
||||
'DELETE FROM %s WHERE %s = :id',
|
||||
|
|
@ -157,15 +146,14 @@ class SQLite3Cache extends CacheProvider
|
|||
* Find a single row by ID.
|
||||
*
|
||||
* @param mixed $id
|
||||
* @param bool $includeData
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
private function findById($id, $includeData = true)
|
||||
private function findById($id, bool $includeData = true) : ?array
|
||||
{
|
||||
list($idField) = $fields = $this->getFields();
|
||||
[$idField] = $fields = $this->getFields();
|
||||
|
||||
if (!$includeData) {
|
||||
if (! $includeData) {
|
||||
$key = array_search(static::DATA_FIELD, $fields);
|
||||
unset($fields[$key]);
|
||||
}
|
||||
|
|
@ -199,19 +187,17 @@ class SQLite3Cache extends CacheProvider
|
|||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getFields()
|
||||
private function getFields() : array
|
||||
{
|
||||
return array(static::ID_FIELD, static::DATA_FIELD, static::EXPIRATION_FIELD);
|
||||
return [static::ID_FIELD, static::DATA_FIELD, static::EXPIRATION_FIELD];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the item is expired.
|
||||
*
|
||||
* @param array $item
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isExpired(array $item)
|
||||
private function isExpired(array $item) : bool
|
||||
{
|
||||
return isset($item[static::EXPIRATION_FIELD]) &&
|
||||
$item[self::EXPIRATION_FIELD] !== null &&
|
||||
|
|
|
|||
|
|
@ -1,25 +1,8 @@
|
|||
<?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;
|
||||
|
||||
class Version
|
||||
{
|
||||
const VERSION = '1.6.1-DEV';
|
||||
public const VERSION = '1.9.0-DEV';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,4 @@
|
|||
<?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;
|
||||
|
||||
|
|
@ -23,8 +6,6 @@ namespace Doctrine\Common\Cache;
|
|||
* Void cache driver. The cache could be of use in tests where you don`t need to cache anything.
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
* @since 1.5
|
||||
* @author Kotlyar Maksim <kotlyar.maksim@gmail.com>
|
||||
*/
|
||||
class VoidCache extends CacheProvider
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,34 +1,21 @@
|
|||
<?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;
|
||||
|
||||
use function count;
|
||||
use function is_array;
|
||||
use function wincache_ucache_clear;
|
||||
use function wincache_ucache_delete;
|
||||
use function wincache_ucache_exists;
|
||||
use function wincache_ucache_get;
|
||||
use function wincache_ucache_info;
|
||||
use function wincache_ucache_meminfo;
|
||||
use function wincache_ucache_set;
|
||||
|
||||
/**
|
||||
* WinCache cache provider.
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.2
|
||||
* @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 WinCacheCache extends CacheProvider
|
||||
{
|
||||
|
|
@ -90,6 +77,16 @@ class WinCacheCache extends CacheProvider
|
|||
return empty($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDeleteMultiple(array $keys)
|
||||
{
|
||||
$result = wincache_ucache_delete($keys);
|
||||
|
||||
return is_array($result) && count($result) !== count($keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
@ -98,12 +95,12 @@ class WinCacheCache extends CacheProvider
|
|||
$info = wincache_ucache_info();
|
||||
$meminfo = wincache_ucache_meminfo();
|
||||
|
||||
return array(
|
||||
return [
|
||||
Cache::STATS_HITS => $info['total_hit_count'],
|
||||
Cache::STATS_MISSES => $info['total_miss_count'],
|
||||
Cache::STATS_UPTIME => $info['total_cache_uptime'],
|
||||
Cache::STATS_MEMORY_USAGE => $meminfo['memory_total'],
|
||||
Cache::STATS_MEMORY_AVAILABLE => $meminfo['memory_free'],
|
||||
);
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +1,25 @@
|
|||
<?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;
|
||||
|
||||
use BadMethodCallException;
|
||||
use const XC_TYPE_VAR;
|
||||
use function ini_get;
|
||||
use function serialize;
|
||||
use function unserialize;
|
||||
use function xcache_clear_cache;
|
||||
use function xcache_get;
|
||||
use function xcache_info;
|
||||
use function xcache_isset;
|
||||
use function xcache_set;
|
||||
use function xcache_unset;
|
||||
|
||||
/**
|
||||
* Xcache cache driver.
|
||||
*
|
||||
* @deprecated
|
||||
*
|
||||
* @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>
|
||||
*/
|
||||
class XcacheCache extends CacheProvider
|
||||
{
|
||||
|
|
@ -81,12 +72,12 @@ class XcacheCache extends CacheProvider
|
|||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws \BadMethodCallException When xcache.admin.enable_auth is On.
|
||||
* @throws BadMethodCallException When xcache.admin.enable_auth is On.
|
||||
*/
|
||||
protected function checkAuthorization()
|
||||
{
|
||||
if (ini_get('xcache.admin.enable_auth')) {
|
||||
throw new \BadMethodCallException(
|
||||
throw new BadMethodCallException(
|
||||
'To use all features of \Doctrine\Common\Cache\XcacheCache, '
|
||||
. 'you must set "xcache.admin.enable_auth" to "Off" in your php.ini.'
|
||||
);
|
||||
|
|
@ -101,12 +92,13 @@ class XcacheCache extends CacheProvider
|
|||
$this->checkAuthorization();
|
||||
|
||||
$info = xcache_info(XC_TYPE_VAR, 0);
|
||||
return array(
|
||||
|
||||
return [
|
||||
Cache::STATS_HITS => $info['hits'],
|
||||
Cache::STATS_MISSES => $info['misses'],
|
||||
Cache::STATS_UPTIME => null,
|
||||
Cache::STATS_MEMORY_USAGE => $info['size'],
|
||||
Cache::STATS_MEMORY_AVAILABLE => $info['avail'],
|
||||
);
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,31 +1,16 @@
|
|||
<?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;
|
||||
|
||||
use function zend_shm_cache_clear;
|
||||
use function zend_shm_cache_delete;
|
||||
use function zend_shm_cache_fetch;
|
||||
use function zend_shm_cache_store;
|
||||
|
||||
/**
|
||||
* Zend Data Cache cache driver.
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @author Ralph Schindler <ralph.schindler@zend.com>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
*/
|
||||
class ZendDataCache extends CacheProvider
|
||||
{
|
||||
|
|
@ -42,7 +27,7 @@ class ZendDataCache extends CacheProvider
|
|||
*/
|
||||
protected function doContains($id)
|
||||
{
|
||||
return (false !== zend_shm_cache_fetch($id));
|
||||
return zend_shm_cache_fetch($id) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -70,6 +55,7 @@ class ZendDataCache extends CacheProvider
|
|||
if (empty($namespace)) {
|
||||
return zend_shm_cache_clear();
|
||||
}
|
||||
|
||||
return zend_shm_cache_clear($namespace);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,20 +18,7 @@ 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.
|
||||
We use the [Doctrine Coding Standard](https://github.com/doctrine/coding-standard).
|
||||
|
||||
## Unit-Tests
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,78 @@
|
|||
# Doctrine Collections
|
||||
|
||||
[](https://travis-ci.org/doctrine/collections)
|
||||
[](https://scrutinizer-ci.com/g/doctrine/collections/?branch=master)
|
||||
[](https://scrutinizer-ci.com/g/doctrine/collections/?branch=master)
|
||||
[](https://codecov.io/gh/doctrine/collections/branch/master)
|
||||
|
||||
Collections Abstraction library
|
||||
|
||||
## Changelog
|
||||
|
||||
### v1.6.1
|
||||
|
||||
This release, combined with the release of [`doctrine/collections` `v1.6.1`](https://github.com/doctrine/collections/releases/tag/v1.6.1),
|
||||
fixes an issue where parsing annotations was not possible
|
||||
for classes within `doctrine/collections`.
|
||||
|
||||
Specifically, `v1.6.0` introduced Psalm-specific annotations
|
||||
such as (for example) `@template` and `@template-implements`,
|
||||
which were both incorrectly recognized as `@template`.
|
||||
|
||||
`@template` has therefore been removed, and instead we use
|
||||
the prefixed `@psalm-template`, which is no longer parsed
|
||||
by `doctrine/collections` `v1.6.1`
|
||||
|
||||
Total issues resolved: **1**
|
||||
|
||||
- [186: Use `@psalm-template` annotation to avoid clashes](https://github.com/doctrine/collections/pull/186) thanks to @muglug
|
||||
|
||||
### v1.6.0
|
||||
|
||||
This release bumps the minimum required PHP version to 7.1.3.
|
||||
|
||||
Following improvements were introduced:
|
||||
|
||||
* `ArrayCollection#filter()` now allows filtering by key, value or both.
|
||||
* When using the `ClosureExpressionVisitor` over objects with a defined
|
||||
accessor and property, the accessor is prioritised.
|
||||
* Updated testing tools and coding standards, autoloading, which also
|
||||
led to marginal performance improvements
|
||||
* Introduced generic type docblock declarations from [psalm](https://github.com/vimeo/psalm),
|
||||
which should allow users to declare `/** @var Collection<KeyType, ValueType> */`
|
||||
in their code, and leverage the type propagation deriving from that.
|
||||
|
||||
Total issues resolved: **16**
|
||||
|
||||
- [127: Use PSR-4](https://github.com/doctrine/collections/pull/127) thanks to @Nyholm
|
||||
- [129: Remove space in method declaration](https://github.com/doctrine/collections/pull/129) thanks to @bounoable
|
||||
- [130: Update build to add PHPCS and PHPStan](https://github.com/doctrine/collections/pull/130) thanks to @lcobucci
|
||||
- [131: ClosureExpressionVisitor > Don't duplicate the accessor when the field already starts with it](https://github.com/doctrine/collections/pull/131) thanks to @ruudk
|
||||
- [139: Apply Doctrine CS 2.1](https://github.com/doctrine/collections/pull/139) thanks to @Majkl578
|
||||
- [142: CS 4.0, version composer.lock, merge stages](https://github.com/doctrine/collections/pull/142) thanks to @Majkl578
|
||||
- [144: Update to PHPUnit 7](https://github.com/doctrine/collections/pull/144) thanks to @carusogabriel
|
||||
- [146: Update changelog for v1.4.0 and v1.5.0](https://github.com/doctrine/collections/pull/146) thanks to @GromNaN
|
||||
- [154: Update index.rst](https://github.com/doctrine/collections/pull/154) thanks to @chraiet
|
||||
- [158: Extract Selectable method into own documentation section](https://github.com/doctrine/collections/pull/158) thanks to @SenseException
|
||||
- [160: Update homepage](https://github.com/doctrine/collections/pull/160) thanks to @Majkl578
|
||||
- [165: Allow `ArrayCollection#filter()` to filter by key, value or both](https://github.com/doctrine/collections/issues/165) thanks to @0x13a
|
||||
- [167: Allow `ArrayCollection#filter()` to filter by key and also value](https://github.com/doctrine/collections/pull/167) thanks to @0x13a
|
||||
- [175: CI: Test against PHP 7.4snapshot instead of nightly (8.0)](https://github.com/doctrine/collections/pull/175) thanks to @Majkl578
|
||||
- [177: Generify collections using Psalm](https://github.com/doctrine/collections/pull/177) thanks to @nschoellhorn
|
||||
- [178: Updated doctrine/coding-standard to 6.0](https://github.com/doctrine/collections/pull/178) thanks to @patrickjahns
|
||||
|
||||
### v1.5.0
|
||||
|
||||
* [Require PHP 7.1+](https://github.com/doctrine/collections/pull/105)
|
||||
* [Drop HHVM support](https://github.com/doctrine/collections/pull/118)
|
||||
|
||||
### v1.4.0
|
||||
|
||||
* [Require PHP 5.6+](https://github.com/doctrine/collections/pull/105)
|
||||
* [Add `ArrayCollection::createFrom()`](https://github.com/doctrine/collections/pull/91)
|
||||
* [Support non-camel-case naming](https://github.com/doctrine/collections/pull/57)
|
||||
* [Comparison `START_WITH`, `END_WITH`](https://github.com/doctrine/collections/pull/78)
|
||||
* [Comparison `MEMBER_OF`](https://github.com/doctrine/collections/pull/66)
|
||||
* [Add Contributing guide](https://github.com/doctrine/collections/pull/103)
|
||||
|
||||
### v1.3.0
|
||||
|
||||
* [Explicit casting of first and max results in criteria API](https://github.com/doctrine/collections/pull/26)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,14 @@
|
|||
{
|
||||
"name": "doctrine/collections",
|
||||
"type": "library",
|
||||
"description": "Collections Abstraction library",
|
||||
"keywords": ["collections", "array", "iterator"],
|
||||
"homepage": "http://www.doctrine-project.org",
|
||||
"description": "PHP Doctrine Collections library that adds additional functionality on top of PHP arrays.",
|
||||
"keywords": [
|
||||
"php",
|
||||
"collections",
|
||||
"array",
|
||||
"iterators"
|
||||
],
|
||||
"homepage": "https://www.doctrine-project.org/projects/collections.html",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
|
||||
|
|
@ -13,23 +18,20 @@
|
|||
{"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
|
||||
],
|
||||
"require": {
|
||||
"php": "^5.6 || ^7.0"
|
||||
"php": "^7.1.3 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^5.7",
|
||||
"doctrine/coding-standard": "~0.1@dev"
|
||||
"phpunit/phpunit": "^7.0",
|
||||
"doctrine/coding-standard": "^6.0",
|
||||
"phpstan/phpstan-shim": "^0.9.2",
|
||||
"vimeo/psalm": "^3.8.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": { "Doctrine\\Common\\Collections\\": "lib/" }
|
||||
"psr-4": { "Doctrine\\Common\\Collections\\": "lib/Doctrine/Common/Collections" }
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Doctrine\\Tests\\": "tests/Doctrine/Tests"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.3.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,4 @@
|
|||
<?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\Collections;
|
||||
|
||||
|
|
@ -24,21 +7,22 @@ use Closure;
|
|||
/**
|
||||
* Lazy collection that is backed by a concrete collection
|
||||
*
|
||||
* @author Michaël Gallego <mic.gallego@gmail.com>
|
||||
* @since 1.2
|
||||
* @phpstan-template TKey
|
||||
* @psalm-template TKey of array-key
|
||||
* @psalm-template T
|
||||
* @template-implements Collection<TKey,T>
|
||||
*/
|
||||
abstract class AbstractLazyCollection implements Collection
|
||||
{
|
||||
/**
|
||||
* The backed collection to use
|
||||
*
|
||||
* @psalm-var Collection<TKey,T>
|
||||
* @var Collection
|
||||
*/
|
||||
protected $collection;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
/** @var bool */
|
||||
protected $initialized = false;
|
||||
|
||||
/**
|
||||
|
|
@ -47,6 +31,7 @@ abstract class AbstractLazyCollection implements Collection
|
|||
public function count()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->count();
|
||||
}
|
||||
|
||||
|
|
@ -56,6 +41,7 @@ abstract class AbstractLazyCollection implements Collection
|
|||
public function add($element)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->add($element);
|
||||
}
|
||||
|
||||
|
|
@ -74,6 +60,7 @@ abstract class AbstractLazyCollection implements Collection
|
|||
public function contains($element)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->contains($element);
|
||||
}
|
||||
|
||||
|
|
@ -83,6 +70,7 @@ abstract class AbstractLazyCollection implements Collection
|
|||
public function isEmpty()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->isEmpty();
|
||||
}
|
||||
|
||||
|
|
@ -92,6 +80,7 @@ abstract class AbstractLazyCollection implements Collection
|
|||
public function remove($key)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->remove($key);
|
||||
}
|
||||
|
||||
|
|
@ -101,6 +90,7 @@ abstract class AbstractLazyCollection implements Collection
|
|||
public function removeElement($element)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->removeElement($element);
|
||||
}
|
||||
|
||||
|
|
@ -110,6 +100,7 @@ abstract class AbstractLazyCollection implements Collection
|
|||
public function containsKey($key)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->containsKey($key);
|
||||
}
|
||||
|
||||
|
|
@ -119,6 +110,7 @@ abstract class AbstractLazyCollection implements Collection
|
|||
public function get($key)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->get($key);
|
||||
}
|
||||
|
||||
|
|
@ -128,6 +120,7 @@ abstract class AbstractLazyCollection implements Collection
|
|||
public function getKeys()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->getKeys();
|
||||
}
|
||||
|
||||
|
|
@ -137,6 +130,7 @@ abstract class AbstractLazyCollection implements Collection
|
|||
public function getValues()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->getValues();
|
||||
}
|
||||
|
||||
|
|
@ -155,6 +149,7 @@ abstract class AbstractLazyCollection implements Collection
|
|||
public function toArray()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->toArray();
|
||||
}
|
||||
|
||||
|
|
@ -164,6 +159,7 @@ abstract class AbstractLazyCollection implements Collection
|
|||
public function first()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->first();
|
||||
}
|
||||
|
||||
|
|
@ -173,6 +169,7 @@ abstract class AbstractLazyCollection implements Collection
|
|||
public function last()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->last();
|
||||
}
|
||||
|
||||
|
|
@ -182,6 +179,7 @@ abstract class AbstractLazyCollection implements Collection
|
|||
public function key()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->key();
|
||||
}
|
||||
|
||||
|
|
@ -191,6 +189,7 @@ abstract class AbstractLazyCollection implements Collection
|
|||
public function current()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->current();
|
||||
}
|
||||
|
||||
|
|
@ -200,6 +199,7 @@ abstract class AbstractLazyCollection implements Collection
|
|||
public function next()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->next();
|
||||
}
|
||||
|
||||
|
|
@ -209,6 +209,7 @@ abstract class AbstractLazyCollection implements Collection
|
|||
public function exists(Closure $p)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->exists($p);
|
||||
}
|
||||
|
||||
|
|
@ -218,6 +219,7 @@ abstract class AbstractLazyCollection implements Collection
|
|||
public function filter(Closure $p)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->filter($p);
|
||||
}
|
||||
|
||||
|
|
@ -227,6 +229,7 @@ abstract class AbstractLazyCollection implements Collection
|
|||
public function forAll(Closure $p)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->forAll($p);
|
||||
}
|
||||
|
||||
|
|
@ -236,6 +239,7 @@ abstract class AbstractLazyCollection implements Collection
|
|||
public function map(Closure $func)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->map($func);
|
||||
}
|
||||
|
||||
|
|
@ -245,6 +249,7 @@ abstract class AbstractLazyCollection implements Collection
|
|||
public function partition(Closure $p)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->partition($p);
|
||||
}
|
||||
|
||||
|
|
@ -254,6 +259,7 @@ abstract class AbstractLazyCollection implements Collection
|
|||
public function indexOf($element)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->indexOf($element);
|
||||
}
|
||||
|
||||
|
|
@ -263,6 +269,7 @@ abstract class AbstractLazyCollection implements Collection
|
|||
public function slice($offset, $length = null)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->slice($offset, $length);
|
||||
}
|
||||
|
||||
|
|
@ -272,29 +279,44 @@ abstract class AbstractLazyCollection implements Collection
|
|||
public function getIterator()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->getIterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-param TKey $offset
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->offsetExists($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @param int|string $offset
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @psalm-param TKey $offset
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->offsetGet($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @psalm-param TKey $offset
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
|
|
@ -304,6 +326,8 @@ abstract class AbstractLazyCollection implements Collection
|
|||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-param TKey $offset
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
|
|
@ -328,10 +352,12 @@ abstract class AbstractLazyCollection implements Collection
|
|||
*/
|
||||
protected function initialize()
|
||||
{
|
||||
if ( ! $this->initialized) {
|
||||
$this->doInitialize();
|
||||
$this->initialized = true;
|
||||
if ($this->initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->doInitialize();
|
||||
$this->initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,27 +1,28 @@
|
|||
<?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\Collections;
|
||||
|
||||
use ArrayIterator;
|
||||
use Closure;
|
||||
use Doctrine\Common\Collections\Expr\ClosureExpressionVisitor;
|
||||
use const ARRAY_FILTER_USE_BOTH;
|
||||
use function array_filter;
|
||||
use function array_key_exists;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function array_reverse;
|
||||
use function array_search;
|
||||
use function array_slice;
|
||||
use function array_values;
|
||||
use function count;
|
||||
use function current;
|
||||
use function end;
|
||||
use function in_array;
|
||||
use function key;
|
||||
use function next;
|
||||
use function reset;
|
||||
use function spl_object_hash;
|
||||
use function uasort;
|
||||
|
||||
/**
|
||||
* An ArrayCollection is a Collection implementation that wraps a regular PHP array.
|
||||
|
|
@ -31,16 +32,18 @@ use Doctrine\Common\Collections\Expr\ClosureExpressionVisitor;
|
|||
* 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>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @phpstan-template TKey
|
||||
* @psalm-template TKey of array-key
|
||||
* @psalm-template T
|
||||
* @template-implements Collection<TKey,T>
|
||||
* @template-implements Selectable<TKey,T>
|
||||
*/
|
||||
class ArrayCollection implements Collection, Selectable
|
||||
{
|
||||
/**
|
||||
* An array containing the entries of this collection.
|
||||
*
|
||||
* @psalm-var array<TKey,T>
|
||||
* @var array
|
||||
*/
|
||||
private $elements;
|
||||
|
|
@ -49,27 +52,14 @@ class ArrayCollection implements Collection, Selectable
|
|||
* Initializes a new ArrayCollection.
|
||||
*
|
||||
* @param array $elements
|
||||
*
|
||||
* @psalm-param array<TKey,T> $elements
|
||||
*/
|
||||
public function __construct(array $elements = array())
|
||||
public function __construct(array $elements = [])
|
||||
{
|
||||
$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}
|
||||
*/
|
||||
|
|
@ -86,6 +76,24 @@ class ArrayCollection implements Collection, Selectable
|
|||
return reset($this->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
|
||||
*
|
||||
* @psalm-param array<TKey,T> $elements
|
||||
* @psalm-return static<TKey,T>
|
||||
*/
|
||||
protected function createFrom(array $elements)
|
||||
{
|
||||
return new static($elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
|
@ -123,7 +131,7 @@ class ArrayCollection implements Collection, Selectable
|
|||
*/
|
||||
public function remove($key)
|
||||
{
|
||||
if ( ! isset($this->elements[$key]) && ! array_key_exists($key, $this->elements)) {
|
||||
if (! isset($this->elements[$key]) && ! array_key_exists($key, $this->elements)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -153,6 +161,8 @@ class ArrayCollection implements Collection, Selectable
|
|||
* Required by interface ArrayAccess.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-param TKey $offset
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
|
|
@ -163,6 +173,8 @@ class ArrayCollection implements Collection, Selectable
|
|||
* Required by interface ArrayAccess.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-param TKey $offset
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
|
|
@ -176,8 +188,10 @@ class ArrayCollection implements Collection, Selectable
|
|||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
if ( ! isset($offset)) {
|
||||
return $this->add($value);
|
||||
if (! isset($offset)) {
|
||||
$this->add($value);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->set($offset, $value);
|
||||
|
|
@ -187,10 +201,12 @@ class ArrayCollection implements Collection, Selectable
|
|||
* Required by interface ArrayAccess.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-param TKey $offset
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
return $this->remove($offset);
|
||||
$this->remove($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -236,7 +252,7 @@ class ArrayCollection implements Collection, Selectable
|
|||
*/
|
||||
public function get($key)
|
||||
{
|
||||
return isset($this->elements[$key]) ? $this->elements[$key] : null;
|
||||
return $this->elements[$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -273,6 +289,11 @@ class ArrayCollection implements Collection, Selectable
|
|||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-suppress InvalidPropertyAssignmentValue
|
||||
*
|
||||
* This breaks assumptions about the template type, but it would
|
||||
* be a backwards-incompatible change to remove this method
|
||||
*/
|
||||
public function add($element)
|
||||
{
|
||||
|
|
@ -301,6 +322,12 @@ class ArrayCollection implements Collection, Selectable
|
|||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return static
|
||||
*
|
||||
* @psalm-template U
|
||||
* @psalm-param Closure(T=):U $func
|
||||
* @psalm-return static<TKey, U>
|
||||
*/
|
||||
public function map(Closure $func)
|
||||
{
|
||||
|
|
@ -309,10 +336,14 @@ class ArrayCollection implements Collection, Selectable
|
|||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return static
|
||||
*
|
||||
* @psalm-return static<TKey,T>
|
||||
*/
|
||||
public function filter(Closure $p)
|
||||
{
|
||||
return $this->createFrom(array_filter($this->elements, $p));
|
||||
return $this->createFrom(array_filter($this->elements, $p, ARRAY_FILTER_USE_BOTH));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -321,7 +352,7 @@ class ArrayCollection implements Collection, Selectable
|
|||
public function forAll(Closure $p)
|
||||
{
|
||||
foreach ($this->elements as $key => $element) {
|
||||
if ( ! $p($key, $element)) {
|
||||
if (! $p($key, $element)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -334,7 +365,7 @@ class ArrayCollection implements Collection, Selectable
|
|||
*/
|
||||
public function partition(Closure $p)
|
||||
{
|
||||
$matches = $noMatches = array();
|
||||
$matches = $noMatches = [];
|
||||
|
||||
foreach ($this->elements as $key => $element) {
|
||||
if ($p($key, $element)) {
|
||||
|
|
@ -344,7 +375,7 @@ class ArrayCollection implements Collection, Selectable
|
|||
}
|
||||
}
|
||||
|
||||
return array($this->createFrom($matches), $this->createFrom($noMatches));
|
||||
return [$this->createFrom($matches), $this->createFrom($noMatches)];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -354,7 +385,7 @@ class ArrayCollection implements Collection, Selectable
|
|||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return __CLASS__ . '@' . spl_object_hash($this);
|
||||
return self::class . '@' . spl_object_hash($this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -362,7 +393,7 @@ class ArrayCollection implements Collection, Selectable
|
|||
*/
|
||||
public function clear()
|
||||
{
|
||||
$this->elements = array();
|
||||
$this->elements = [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -387,10 +418,12 @@ class ArrayCollection implements Collection, Selectable
|
|||
$filtered = array_filter($filtered, $filter);
|
||||
}
|
||||
|
||||
if ($orderings = $criteria->getOrderings()) {
|
||||
$orderings = $criteria->getOrderings();
|
||||
|
||||
if ($orderings) {
|
||||
$next = null;
|
||||
foreach (array_reverse($orderings) as $field => $ordering) {
|
||||
$next = ClosureExpressionVisitor::sortByField($field, $ordering == Criteria::DESC ? -1 : 1, $next);
|
||||
$next = ClosureExpressionVisitor::sortByField($field, $ordering === Criteria::DESC ? -1 : 1, $next);
|
||||
}
|
||||
|
||||
uasort($filtered, $next);
|
||||
|
|
@ -400,7 +433,7 @@ class ArrayCollection implements Collection, Selectable
|
|||
$length = $criteria->getMaxResults();
|
||||
|
||||
if ($offset || $length) {
|
||||
$filtered = array_slice($filtered, (int)$offset, $length);
|
||||
$filtered = array_slice($filtered, (int) $offset, $length);
|
||||
}
|
||||
|
||||
return $this->createFrom($filtered);
|
||||
|
|
|
|||
|
|
@ -1,21 +1,4 @@
|
|||
<?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\Collections;
|
||||
|
||||
|
|
@ -41,10 +24,11 @@ use IteratorAggregate;
|
|||
* position unless you explicitly positioned it before. Prefer iteration with
|
||||
* external iterators.
|
||||
*
|
||||
* @since 2.0
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @phpstan-template TKey
|
||||
* @psalm-template TKey of array-key
|
||||
* @psalm-template T
|
||||
* @template-extends IteratorAggregate<TKey, T>
|
||||
* @template-extends ArrayAccess<TKey|null, T>
|
||||
*/
|
||||
interface Collection extends Countable, IteratorAggregate, ArrayAccess
|
||||
{
|
||||
|
|
@ -53,7 +37,9 @@ interface Collection extends Countable, IteratorAggregate, ArrayAccess
|
|||
*
|
||||
* @param mixed $element The element to add.
|
||||
*
|
||||
* @return boolean Always TRUE.
|
||||
* @return true Always TRUE.
|
||||
*
|
||||
* @psalm-param T $element
|
||||
*/
|
||||
public function add($element);
|
||||
|
||||
|
|
@ -70,23 +56,28 @@ interface Collection extends Countable, IteratorAggregate, ArrayAccess
|
|||
*
|
||||
* @param mixed $element The element to search for.
|
||||
*
|
||||
* @return boolean TRUE if the collection contains the element, FALSE otherwise.
|
||||
* @return bool TRUE if the collection contains the element, FALSE otherwise.
|
||||
*
|
||||
* @psalm-param T $element
|
||||
*/
|
||||
public function contains($element);
|
||||
|
||||
/**
|
||||
* Checks whether the collection is empty (contains no elements).
|
||||
*
|
||||
* @return boolean TRUE if the collection is empty, FALSE otherwise.
|
||||
* @return bool TRUE if the collection is empty, FALSE otherwise.
|
||||
*/
|
||||
public function isEmpty();
|
||||
|
||||
/**
|
||||
* Removes the element at the specified index from the collection.
|
||||
*
|
||||
* @param string|integer $key The kex/index of the element to remove.
|
||||
* @param string|int $key The key/index of the element to remove.
|
||||
*
|
||||
* @return mixed The removed element or NULL, if the collection did not contain the element.
|
||||
*
|
||||
* @psalm-param TKey $key
|
||||
* @psalm-return T|null
|
||||
*/
|
||||
public function remove($key);
|
||||
|
||||
|
|
@ -95,34 +86,43 @@ interface Collection extends Countable, IteratorAggregate, ArrayAccess
|
|||
*
|
||||
* @param mixed $element The element to remove.
|
||||
*
|
||||
* @return boolean TRUE if this collection contained the specified element, FALSE otherwise.
|
||||
* @return bool TRUE if this collection contained the specified element, FALSE otherwise.
|
||||
*
|
||||
* @psalm-param T $element
|
||||
*/
|
||||
public function removeElement($element);
|
||||
|
||||
/**
|
||||
* Checks whether the collection contains an element with the specified key/index.
|
||||
*
|
||||
* @param string|integer $key The key/index to check for.
|
||||
* @param string|int $key The key/index to check for.
|
||||
*
|
||||
* @return boolean TRUE if the collection contains an element with the specified key/index,
|
||||
* FALSE otherwise.
|
||||
* @return bool TRUE if the collection contains an element with the specified key/index,
|
||||
* FALSE otherwise.
|
||||
*
|
||||
* @psalm-param TKey $key
|
||||
*/
|
||||
public function containsKey($key);
|
||||
|
||||
/**
|
||||
* Gets the element at the specified key/index.
|
||||
*
|
||||
* @param string|integer $key The key/index of the element to retrieve.
|
||||
* @param string|int $key The key/index of the element to retrieve.
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @psalm-param TKey $key
|
||||
* @psalm-return T|null
|
||||
*/
|
||||
public function get($key);
|
||||
|
||||
/**
|
||||
* Gets all keys/indices of the collection.
|
||||
*
|
||||
* @return array The keys/indices of the collection, in the order of the corresponding
|
||||
* @return int[]|string[] The keys/indices of the collection, in the order of the corresponding
|
||||
* elements in the collection.
|
||||
*
|
||||
* @psalm-return TKey[]
|
||||
*/
|
||||
public function getKeys();
|
||||
|
||||
|
|
@ -131,16 +131,21 @@ interface Collection extends Countable, IteratorAggregate, ArrayAccess
|
|||
*
|
||||
* @return array The values of all elements in the collection, in the order they
|
||||
* appear in the collection.
|
||||
*
|
||||
* @psalm-return T[]
|
||||
*/
|
||||
public function getValues();
|
||||
|
||||
/**
|
||||
* Sets an element in the collection at the specified key/index.
|
||||
*
|
||||
* @param string|integer $key The key/index of the element to set.
|
||||
* @param mixed $value The element to set.
|
||||
* @param string|int $key The key/index of the element to set.
|
||||
* @param mixed $value The element to set.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param TKey $key
|
||||
* @psalm-param T $value
|
||||
*/
|
||||
public function set($key, $value);
|
||||
|
||||
|
|
@ -148,6 +153,8 @@ interface Collection extends Countable, IteratorAggregate, ArrayAccess
|
|||
* Gets a native PHP array representation of the collection.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @psalm-return array<TKey,T>
|
||||
*/
|
||||
public function toArray();
|
||||
|
||||
|
|
@ -155,6 +162,8 @@ interface Collection extends Countable, IteratorAggregate, ArrayAccess
|
|||
* Sets the internal iterator to the first element in the collection and returns this element.
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @psalm-return T|false
|
||||
*/
|
||||
public function first();
|
||||
|
||||
|
|
@ -162,13 +171,17 @@ interface Collection extends Countable, IteratorAggregate, ArrayAccess
|
|||
* Sets the internal iterator to the last element in the collection and returns this element.
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @psalm-return T|false
|
||||
*/
|
||||
public function last();
|
||||
|
||||
/**
|
||||
* Gets the key/index of the element at the current iterator position.
|
||||
*
|
||||
* @return int|string
|
||||
* @return int|string|null
|
||||
*
|
||||
* @psalm-return TKey|null
|
||||
*/
|
||||
public function key();
|
||||
|
||||
|
|
@ -176,6 +189,8 @@ interface Collection extends Countable, IteratorAggregate, ArrayAccess
|
|||
* Gets the element of the collection at the current iterator position.
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @psalm-return T|false
|
||||
*/
|
||||
public function current();
|
||||
|
||||
|
|
@ -183,6 +198,8 @@ interface Collection extends Countable, IteratorAggregate, ArrayAccess
|
|||
* Moves the internal iterator position to the next element and returns this element.
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @psalm-return T|false
|
||||
*/
|
||||
public function next();
|
||||
|
||||
|
|
@ -191,7 +208,9 @@ interface Collection extends Countable, IteratorAggregate, ArrayAccess
|
|||
*
|
||||
* @param Closure $p The predicate.
|
||||
*
|
||||
* @return boolean TRUE if the predicate is TRUE for at least one element, FALSE otherwise.
|
||||
* @return bool TRUE if the predicate is TRUE for at least one element, FALSE otherwise.
|
||||
*
|
||||
* @psalm-param Closure(TKey=, T=):bool $p
|
||||
*/
|
||||
public function exists(Closure $p);
|
||||
|
||||
|
|
@ -202,6 +221,9 @@ interface Collection extends Countable, IteratorAggregate, ArrayAccess
|
|||
* @param Closure $p The predicate used for filtering.
|
||||
*
|
||||
* @return Collection A collection with the results of the filter operation.
|
||||
*
|
||||
* @psalm-param Closure(T=):bool $p
|
||||
* @psalm-return Collection<TKey, T>
|
||||
*/
|
||||
public function filter(Closure $p);
|
||||
|
||||
|
|
@ -210,7 +232,9 @@ interface Collection extends Countable, IteratorAggregate, ArrayAccess
|
|||
*
|
||||
* @param Closure $p The predicate.
|
||||
*
|
||||
* @return boolean TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.
|
||||
* @return bool TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.
|
||||
*
|
||||
* @psalm-param Closure(TKey=, T=):bool $p
|
||||
*/
|
||||
public function forAll(Closure $p);
|
||||
|
||||
|
|
@ -218,9 +242,11 @@ interface Collection extends Countable, IteratorAggregate, ArrayAccess
|
|||
* Applies the given function to each element in the collection and returns
|
||||
* a new collection with the elements returned by the function.
|
||||
*
|
||||
* @param Closure $func
|
||||
*
|
||||
* @return Collection
|
||||
*
|
||||
* @psalm-template U
|
||||
* @psalm-param Closure(T=):U $func
|
||||
* @psalm-return Collection<TKey, U>
|
||||
*/
|
||||
public function map(Closure $func);
|
||||
|
||||
|
|
@ -230,9 +256,12 @@ interface Collection extends Countable, IteratorAggregate, ArrayAccess
|
|||
*
|
||||
* @param Closure $p The predicate on which to partition.
|
||||
*
|
||||
* @return array An array with two elements. The first element contains the collection
|
||||
* of elements where the predicate returned TRUE, the second element
|
||||
* contains the collection of elements where the predicate returned FALSE.
|
||||
* @return Collection[] An array with two elements. The first element contains the collection
|
||||
* of elements where the predicate returned TRUE, the second element
|
||||
* contains the collection of elements where the predicate returned FALSE.
|
||||
*
|
||||
* @psalm-param Closure(TKey=, T=):bool $p
|
||||
* @psalm-return array{0: Collection<TKey, T>, 1: Collection<TKey, T>}
|
||||
*/
|
||||
public function partition(Closure $p);
|
||||
|
||||
|
|
@ -244,6 +273,9 @@ interface Collection extends Countable, IteratorAggregate, ArrayAccess
|
|||
* @param mixed $element The element to search for.
|
||||
*
|
||||
* @return int|string|bool The key/index of the element or FALSE if the element was not found.
|
||||
*
|
||||
* @psalm-param T $element
|
||||
* @psalm-return TKey|false
|
||||
*/
|
||||
public function indexOf($element);
|
||||
|
||||
|
|
@ -258,6 +290,8 @@ interface Collection extends Countable, IteratorAggregate, ArrayAccess
|
|||
* @param int|null $length The maximum number of elements to return, or null for no limit.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @psalm-return array<TKey,T>
|
||||
*/
|
||||
public function slice($offset, $length = null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,68 +1,34 @@
|
|||
<?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\Collections;
|
||||
|
||||
use Doctrine\Common\Collections\Expr\Expression;
|
||||
use Doctrine\Common\Collections\Expr\CompositeExpression;
|
||||
use Doctrine\Common\Collections\Expr\Expression;
|
||||
use function array_map;
|
||||
use function strtoupper;
|
||||
|
||||
/**
|
||||
* Criteria for filtering Selectable collections.
|
||||
*
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @since 2.3
|
||||
*/
|
||||
class Criteria
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
const ASC = 'ASC';
|
||||
public const ASC = 'ASC';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
const DESC = 'DESC';
|
||||
public const DESC = 'DESC';
|
||||
|
||||
/**
|
||||
* @var \Doctrine\Common\Collections\ExpressionBuilder|null
|
||||
*/
|
||||
/** @var ExpressionBuilder|null */
|
||||
private static $expressionBuilder;
|
||||
|
||||
/**
|
||||
* @var \Doctrine\Common\Collections\Expr\Expression|null
|
||||
*/
|
||||
/** @var Expression|null */
|
||||
private $expression;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $orderings = array();
|
||||
/** @var string[] */
|
||||
private $orderings = [];
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
/** @var int|null */
|
||||
private $firstResult;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
/** @var int|null */
|
||||
private $maxResults;
|
||||
|
||||
/**
|
||||
|
|
@ -78,7 +44,7 @@ class Criteria
|
|||
/**
|
||||
* Returns the expression builder.
|
||||
*
|
||||
* @return \Doctrine\Common\Collections\ExpressionBuilder
|
||||
* @return ExpressionBuilder
|
||||
*/
|
||||
public static function expr()
|
||||
{
|
||||
|
|
@ -92,28 +58,27 @@ class Criteria
|
|||
/**
|
||||
* Construct a new Criteria.
|
||||
*
|
||||
* @param Expression $expression
|
||||
* @param string[]|null $orderings
|
||||
* @param int|null $firstResult
|
||||
* @param int|null $maxResults
|
||||
*/
|
||||
public function __construct(Expression $expression = null, array $orderings = null, $firstResult = null, $maxResults = null)
|
||||
public function __construct(?Expression $expression = null, ?array $orderings = null, $firstResult = null, $maxResults = null)
|
||||
{
|
||||
$this->expression = $expression;
|
||||
|
||||
$this->setFirstResult($firstResult);
|
||||
$this->setMaxResults($maxResults);
|
||||
|
||||
if (null !== $orderings) {
|
||||
$this->orderBy($orderings);
|
||||
if ($orderings === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->orderBy($orderings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the where expression to evaluate when this Criteria is searched for.
|
||||
*
|
||||
* @param Expression $expression
|
||||
*
|
||||
* @return Criteria
|
||||
*/
|
||||
public function where(Expression $expression)
|
||||
|
|
@ -127,8 +92,6 @@ class Criteria
|
|||
* Appends the where expression to evaluate when this Criteria is searched for
|
||||
* using an AND with previous expression.
|
||||
*
|
||||
* @param Expression $expression
|
||||
*
|
||||
* @return Criteria
|
||||
*/
|
||||
public function andWhere(Expression $expression)
|
||||
|
|
@ -137,9 +100,10 @@ class Criteria
|
|||
return $this->where($expression);
|
||||
}
|
||||
|
||||
$this->expression = new CompositeExpression(CompositeExpression::TYPE_AND, array(
|
||||
$this->expression, $expression
|
||||
));
|
||||
$this->expression = new CompositeExpression(
|
||||
CompositeExpression::TYPE_AND,
|
||||
[$this->expression, $expression]
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
|
@ -148,8 +112,6 @@ class Criteria
|
|||
* Appends the where expression to evaluate when this Criteria is searched for
|
||||
* using an OR with previous expression.
|
||||
*
|
||||
* @param Expression $expression
|
||||
*
|
||||
* @return Criteria
|
||||
*/
|
||||
public function orWhere(Expression $expression)
|
||||
|
|
@ -158,9 +120,10 @@ class Criteria
|
|||
return $this->where($expression);
|
||||
}
|
||||
|
||||
$this->expression = new CompositeExpression(CompositeExpression::TYPE_OR, array(
|
||||
$this->expression, $expression
|
||||
));
|
||||
$this->expression = new CompositeExpression(
|
||||
CompositeExpression::TYPE_OR,
|
||||
[$this->expression, $expression]
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
|
@ -200,7 +163,7 @@ class Criteria
|
|||
public function orderBy(array $orderings)
|
||||
{
|
||||
$this->orderings = array_map(
|
||||
function ($ordering) {
|
||||
static function (string $ordering) : string {
|
||||
return strtoupper($ordering) === Criteria::ASC ? Criteria::ASC : Criteria::DESC;
|
||||
},
|
||||
$orderings
|
||||
|
|
@ -228,7 +191,7 @@ class Criteria
|
|||
*/
|
||||
public function setFirstResult($firstResult)
|
||||
{
|
||||
$this->firstResult = null === $firstResult ? null : (int) $firstResult;
|
||||
$this->firstResult = $firstResult === null ? null : (int) $firstResult;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
|
@ -252,7 +215,7 @@ class Criteria
|
|||
*/
|
||||
public function setMaxResults($maxResults)
|
||||
{
|
||||
$this->maxResults = null === $maxResults ? null : (int) $maxResults;
|
||||
$this->maxResults = $maxResults === null ? null : (int) $maxResults;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +1,27 @@
|
|||
<?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\Collections\Expr;
|
||||
|
||||
use ArrayAccess;
|
||||
use Closure;
|
||||
use RuntimeException;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
use function is_scalar;
|
||||
use function iterator_to_array;
|
||||
use function method_exists;
|
||||
use function preg_match;
|
||||
use function preg_replace_callback;
|
||||
use function strlen;
|
||||
use function strpos;
|
||||
use function strtoupper;
|
||||
use function substr;
|
||||
|
||||
/**
|
||||
* Walks an expression graph and turns it into a PHP closure.
|
||||
*
|
||||
* This closure can be used with {@Collection#filter()} and is used internally
|
||||
* by {@ArrayCollection#select()}.
|
||||
*
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @since 2.3
|
||||
*/
|
||||
class ClosureExpressionVisitor extends ExpressionVisitor
|
||||
{
|
||||
|
|
@ -35,8 +30,8 @@ class ClosureExpressionVisitor extends ExpressionVisitor
|
|||
* directly or indirectly (through an accessor get*, is*, or a magic
|
||||
* method, __get, __call).
|
||||
*
|
||||
* @param object $object
|
||||
* @param string $field
|
||||
* @param object|array $object
|
||||
* @param string $field
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
|
|
@ -46,16 +41,18 @@ class ClosureExpressionVisitor extends ExpressionVisitor
|
|||
return $object[$field];
|
||||
}
|
||||
|
||||
$accessors = array('get', 'is');
|
||||
$accessors = ['get', 'is'];
|
||||
|
||||
foreach ($accessors as $accessor) {
|
||||
$accessor .= $field;
|
||||
|
||||
if ( ! method_exists($object, $accessor)) {
|
||||
continue;
|
||||
if (method_exists($object, $accessor)) {
|
||||
return $object->$accessor();
|
||||
}
|
||||
}
|
||||
|
||||
return $object->$accessor();
|
||||
if (preg_match('/^is[A-Z]+/', $field) === 1 && method_exists($object, $field)) {
|
||||
return $object->$field();
|
||||
}
|
||||
|
||||
// __call should be triggered for get.
|
||||
|
|
@ -65,7 +62,7 @@ class ClosureExpressionVisitor extends ExpressionVisitor
|
|||
return $object->$accessor();
|
||||
}
|
||||
|
||||
if ($object instanceof \ArrayAccess) {
|
||||
if ($object instanceof ArrayAccess) {
|
||||
return $object[$field];
|
||||
}
|
||||
|
||||
|
|
@ -74,17 +71,16 @@ class ClosureExpressionVisitor extends ExpressionVisitor
|
|||
}
|
||||
|
||||
// camelcase field name to support different variable naming conventions
|
||||
$ccField = preg_replace_callback('/_(.?)/', function($matches) { return strtoupper($matches[1]); }, $field);
|
||||
$ccField = preg_replace_callback('/_(.?)/', static function ($matches) {
|
||||
return strtoupper($matches[1]);
|
||||
}, $field);
|
||||
|
||||
foreach ($accessors as $accessor) {
|
||||
$accessor .= $ccField;
|
||||
|
||||
|
||||
if ( ! method_exists($object, $accessor)) {
|
||||
continue;
|
||||
if (method_exists($object, $accessor)) {
|
||||
return $object->$accessor();
|
||||
}
|
||||
|
||||
return $object->$accessor();
|
||||
}
|
||||
|
||||
return $object->$field;
|
||||
|
|
@ -93,29 +89,29 @@ class ClosureExpressionVisitor extends ExpressionVisitor
|
|||
/**
|
||||
* Helper for sorting arrays of objects based on multiple fields + orientations.
|
||||
*
|
||||
* @param string $name
|
||||
* @param int $orientation
|
||||
* @param \Closure $next
|
||||
* @param string $name
|
||||
* @param int $orientation
|
||||
*
|
||||
* @return \Closure
|
||||
* @return Closure
|
||||
*/
|
||||
public static function sortByField($name, $orientation = 1, \Closure $next = null)
|
||||
public static function sortByField($name, $orientation = 1, ?Closure $next = null)
|
||||
{
|
||||
if ( ! $next) {
|
||||
$next = function() {
|
||||
if (! $next) {
|
||||
$next = static function () : int {
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
return function ($a, $b) use ($name, $next, $orientation) {
|
||||
return static function ($a, $b) use ($name, $next, $orientation) : int {
|
||||
$aValue = ClosureExpressionVisitor::getObjectFieldValue($a, $name);
|
||||
|
||||
$bValue = ClosureExpressionVisitor::getObjectFieldValue($b, $name);
|
||||
|
||||
if ($aValue === $bValue) {
|
||||
return $next($a, $b);
|
||||
}
|
||||
|
||||
return (($aValue > $bValue) ? 1 : -1) * $orientation;
|
||||
return ($aValue > $bValue ? 1 : -1) * $orientation;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -129,72 +125,77 @@ class ClosureExpressionVisitor extends ExpressionVisitor
|
|||
|
||||
switch ($comparison->getOperator()) {
|
||||
case Comparison::EQ:
|
||||
return function ($object) use ($field, $value) {
|
||||
return static function ($object) use ($field, $value) : bool {
|
||||
return ClosureExpressionVisitor::getObjectFieldValue($object, $field) === $value;
|
||||
};
|
||||
|
||||
case Comparison::NEQ:
|
||||
return function ($object) use ($field, $value) {
|
||||
return static function ($object) use ($field, $value) : bool {
|
||||
return ClosureExpressionVisitor::getObjectFieldValue($object, $field) !== $value;
|
||||
};
|
||||
|
||||
case Comparison::LT:
|
||||
return function ($object) use ($field, $value) {
|
||||
return static function ($object) use ($field, $value) : bool {
|
||||
return ClosureExpressionVisitor::getObjectFieldValue($object, $field) < $value;
|
||||
};
|
||||
|
||||
case Comparison::LTE:
|
||||
return function ($object) use ($field, $value) {
|
||||
return static function ($object) use ($field, $value) : bool {
|
||||
return ClosureExpressionVisitor::getObjectFieldValue($object, $field) <= $value;
|
||||
};
|
||||
|
||||
case Comparison::GT:
|
||||
return function ($object) use ($field, $value) {
|
||||
return static function ($object) use ($field, $value) : bool {
|
||||
return ClosureExpressionVisitor::getObjectFieldValue($object, $field) > $value;
|
||||
};
|
||||
|
||||
case Comparison::GTE:
|
||||
return function ($object) use ($field, $value) {
|
||||
return static function ($object) use ($field, $value) : bool {
|
||||
return ClosureExpressionVisitor::getObjectFieldValue($object, $field) >= $value;
|
||||
};
|
||||
|
||||
case Comparison::IN:
|
||||
return function ($object) use ($field, $value) {
|
||||
return in_array(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value);
|
||||
return static function ($object) use ($field, $value) : bool {
|
||||
$fieldValue = ClosureExpressionVisitor::getObjectFieldValue($object, $field);
|
||||
|
||||
return in_array($fieldValue, $value, is_scalar($fieldValue));
|
||||
};
|
||||
|
||||
case Comparison::NIN:
|
||||
return function ($object) use ($field, $value) {
|
||||
return ! in_array(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value);
|
||||
return static function ($object) use ($field, $value) : bool {
|
||||
$fieldValue = ClosureExpressionVisitor::getObjectFieldValue($object, $field);
|
||||
|
||||
return ! in_array($fieldValue, $value, is_scalar($fieldValue));
|
||||
};
|
||||
|
||||
case Comparison::CONTAINS:
|
||||
return function ($object) use ($field, $value) {
|
||||
return false !== strpos(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value);
|
||||
return static function ($object) use ($field, $value) {
|
||||
return strpos(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value) !== false;
|
||||
};
|
||||
|
||||
case Comparison::MEMBER_OF:
|
||||
return function ($object) use ($field, $value) {
|
||||
return static function ($object) use ($field, $value) : bool {
|
||||
$fieldValues = ClosureExpressionVisitor::getObjectFieldValue($object, $field);
|
||||
if (!is_array($fieldValues)) {
|
||||
|
||||
if (! is_array($fieldValues)) {
|
||||
$fieldValues = iterator_to_array($fieldValues);
|
||||
}
|
||||
return in_array($value, $fieldValues);
|
||||
|
||||
return in_array($value, $fieldValues, true);
|
||||
};
|
||||
|
||||
case Comparison::STARTS_WITH:
|
||||
return function ($object) use ($field, $value) {
|
||||
return 0 === strpos(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value);
|
||||
return static function ($object) use ($field, $value) : bool {
|
||||
return strpos(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value) === 0;
|
||||
};
|
||||
|
||||
case Comparison::ENDS_WITH:
|
||||
return function ($object) use ($field, $value) {
|
||||
return static function ($object) use ($field, $value) : bool {
|
||||
return $value === substr(ClosureExpressionVisitor::getObjectFieldValue($object, $field), -strlen($value));
|
||||
};
|
||||
|
||||
|
||||
default:
|
||||
throw new \RuntimeException("Unknown comparison operator: " . $comparison->getOperator());
|
||||
throw new RuntimeException('Unknown comparison operator: ' . $comparison->getOperator());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -211,54 +212,50 @@ class ClosureExpressionVisitor extends ExpressionVisitor
|
|||
*/
|
||||
public function walkCompositeExpression(CompositeExpression $expr)
|
||||
{
|
||||
$expressionList = array();
|
||||
$expressionList = [];
|
||||
|
||||
foreach ($expr->getExpressionList() as $child) {
|
||||
$expressionList[] = $this->dispatch($child);
|
||||
}
|
||||
|
||||
switch($expr->getType()) {
|
||||
switch ($expr->getType()) {
|
||||
case CompositeExpression::TYPE_AND:
|
||||
return $this->andExpressions($expressionList);
|
||||
|
||||
case CompositeExpression::TYPE_OR:
|
||||
return $this->orExpressions($expressionList);
|
||||
|
||||
default:
|
||||
throw new \RuntimeException("Unknown composite " . $expr->getType());
|
||||
throw new RuntimeException('Unknown composite ' . $expr->getType());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $expressions
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
private function andExpressions($expressions)
|
||||
private function andExpressions(array $expressions) : callable
|
||||
{
|
||||
return function ($object) use ($expressions) {
|
||||
return static function ($object) use ($expressions) : bool {
|
||||
foreach ($expressions as $expression) {
|
||||
if ( ! $expression($object)) {
|
||||
if (! $expression($object)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $expressions
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
private function orExpressions($expressions)
|
||||
private function orExpressions(array $expressions) : callable
|
||||
{
|
||||
return function ($object) use ($expressions) {
|
||||
return static function ($object) use ($expressions) : bool {
|
||||
foreach ($expressions as $expression) {
|
||||
if ($expression($object)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,58 +1,33 @@
|
|||
<?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\Collections\Expr;
|
||||
|
||||
/**
|
||||
* Comparison of a field with a value by the given operator.
|
||||
*
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @since 2.3
|
||||
*/
|
||||
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 MEMBER_OF = 'MEMBER_OF';
|
||||
const STARTS_WITH = 'STARTS_WITH';
|
||||
const ENDS_WITH = 'ENDS_WITH';
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const EQ = '=';
|
||||
public const NEQ = '<>';
|
||||
public const LT = '<';
|
||||
public const LTE = '<=';
|
||||
public const GT = '>';
|
||||
public const GTE = '>=';
|
||||
public const IS = '='; // no difference with EQ
|
||||
public const IN = 'IN';
|
||||
public const NIN = 'NIN';
|
||||
public const CONTAINS = 'CONTAINS';
|
||||
public const MEMBER_OF = 'MEMBER_OF';
|
||||
public const STARTS_WITH = 'STARTS_WITH';
|
||||
public const ENDS_WITH = 'ENDS_WITH';
|
||||
|
||||
/** @var string */
|
||||
private $field;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
/** @var string */
|
||||
private $op;
|
||||
|
||||
/**
|
||||
* @var Value
|
||||
*/
|
||||
/** @var Value */
|
||||
private $value;
|
||||
|
||||
/**
|
||||
|
|
@ -62,12 +37,12 @@ class Comparison implements Expression
|
|||
*/
|
||||
public function __construct($field, $operator, $value)
|
||||
{
|
||||
if ( ! ($value instanceof Value)) {
|
||||
if (! ($value instanceof Value)) {
|
||||
$value = new Value($value);
|
||||
}
|
||||
|
||||
$this->field = $field;
|
||||
$this->op = $operator;
|
||||
$this->op = $operator;
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,50 +1,28 @@
|
|||
<?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\Collections\Expr;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Expression of Expressions combined by AND or OR operation.
|
||||
*
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @since 2.3
|
||||
*/
|
||||
class CompositeExpression implements Expression
|
||||
{
|
||||
const TYPE_AND = 'AND';
|
||||
const TYPE_OR = 'OR';
|
||||
public const TYPE_AND = 'AND';
|
||||
public const TYPE_OR = 'OR';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
/** @var string */
|
||||
private $type;
|
||||
|
||||
/**
|
||||
* @var Expression[]
|
||||
*/
|
||||
private $expressions = array();
|
||||
/** @var Expression[] */
|
||||
private $expressions = [];
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param array $expressions
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function __construct($type, array $expressions)
|
||||
{
|
||||
|
|
@ -52,10 +30,10 @@ class CompositeExpression implements Expression
|
|||
|
||||
foreach ($expressions as $expr) {
|
||||
if ($expr instanceof Value) {
|
||||
throw new \RuntimeException("Values are not supported expressions as children of and/or expressions.");
|
||||
throw new RuntimeException('Values are not supported expressions as children of and/or expressions.');
|
||||
}
|
||||
if ( ! ($expr instanceof Expression)) {
|
||||
throw new \RuntimeException("No expression given to CompositeExpression.");
|
||||
if (! ($expr instanceof Expression)) {
|
||||
throw new RuntimeException('No expression given to CompositeExpression.');
|
||||
}
|
||||
|
||||
$this->expressions[] = $expr;
|
||||
|
|
|
|||
|
|
@ -1,34 +1,13 @@
|
|||
<?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\Collections\Expr;
|
||||
|
||||
/**
|
||||
* Expression for the {@link Selectable} interface.
|
||||
*
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
interface Expression
|
||||
{
|
||||
/**
|
||||
* @param ExpressionVisitor $visitor
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function visit(ExpressionVisitor $visitor);
|
||||
|
|
|
|||
|
|
@ -1,37 +1,19 @@
|
|||
<?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\Collections\Expr;
|
||||
|
||||
use RuntimeException;
|
||||
use function get_class;
|
||||
|
||||
/**
|
||||
* An Expression visitor walks a graph of expressions and turns them into a
|
||||
* query for the underlying implementation.
|
||||
*
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
abstract class ExpressionVisitor
|
||||
{
|
||||
/**
|
||||
* Converts a comparison expression into the target query language output.
|
||||
*
|
||||
* @param Comparison $comparison
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function walkComparison(Comparison $comparison);
|
||||
|
|
@ -39,8 +21,6 @@ abstract class ExpressionVisitor
|
|||
/**
|
||||
* Converts a value expression into the target query language part.
|
||||
*
|
||||
* @param Value $value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function walkValue(Value $value);
|
||||
|
|
@ -48,8 +28,6 @@ abstract class ExpressionVisitor
|
|||
/**
|
||||
* Converts a composite expression into the target query language output.
|
||||
*
|
||||
* @param CompositeExpression $expr
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function walkCompositeExpression(CompositeExpression $expr);
|
||||
|
|
@ -57,26 +35,21 @@ abstract class ExpressionVisitor
|
|||
/**
|
||||
* Dispatches walking an expression to the appropriate handler.
|
||||
*
|
||||
* @param Expression $expr
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function dispatch(Expression $expr)
|
||||
{
|
||||
switch (true) {
|
||||
case ($expr instanceof Comparison):
|
||||
case $expr instanceof Comparison:
|
||||
return $this->walkComparison($expr);
|
||||
|
||||
case ($expr instanceof Value):
|
||||
case $expr instanceof Value:
|
||||
return $this->walkValue($expr);
|
||||
|
||||
case ($expr instanceof CompositeExpression):
|
||||
case $expr instanceof CompositeExpression:
|
||||
return $this->walkCompositeExpression($expr);
|
||||
|
||||
default:
|
||||
throw new \RuntimeException("Unknown Expression " . get_class($expr));
|
||||
throw new RuntimeException('Unknown Expression ' . get_class($expr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,29 +1,10 @@
|
|||
<?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\Collections\Expr;
|
||||
|
||||
class Value implements Expression
|
||||
{
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
/** @var mixed */
|
||||
private $value;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,27 +1,11 @@
|
|||
<?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\Collections;
|
||||
|
||||
use Doctrine\Common\Collections\Expr\Comparison;
|
||||
use Doctrine\Common\Collections\Expr\CompositeExpression;
|
||||
use Doctrine\Common\Collections\Expr\Value;
|
||||
use function func_get_args;
|
||||
|
||||
/**
|
||||
* Builder for Expressions in the {@link Selectable} interface.
|
||||
|
|
@ -29,14 +13,11 @@ use Doctrine\Common\Collections\Expr\Value;
|
|||
* Important Notice for interoperable code: You have to use scalar
|
||||
* 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>
|
||||
* @since 2.3
|
||||
*/
|
||||
class ExpressionBuilder
|
||||
{
|
||||
/**
|
||||
* @param mixed $x
|
||||
* @param mixed ...$x
|
||||
*
|
||||
* @return CompositeExpression
|
||||
*/
|
||||
|
|
@ -46,7 +27,7 @@ class ExpressionBuilder
|
|||
}
|
||||
|
||||
/**
|
||||
* @param mixed $x
|
||||
* @param mixed ...$x
|
||||
*
|
||||
* @return CompositeExpression
|
||||
*/
|
||||
|
|
@ -133,7 +114,7 @@ class ExpressionBuilder
|
|||
|
||||
/**
|
||||
* @param string $field
|
||||
* @param mixed $values
|
||||
* @param array $values
|
||||
*
|
||||
* @return Comparison
|
||||
*/
|
||||
|
|
@ -144,7 +125,7 @@ class ExpressionBuilder
|
|||
|
||||
/**
|
||||
* @param string $field
|
||||
* @param mixed $values
|
||||
* @param array $values
|
||||
*
|
||||
* @return Comparison
|
||||
*/
|
||||
|
|
@ -170,7 +151,7 @@ class ExpressionBuilder
|
|||
*
|
||||
* @return Comparison
|
||||
*/
|
||||
public function memberOf ($field, $value)
|
||||
public function memberOf($field, $value)
|
||||
{
|
||||
return new Comparison($field, Comparison::MEMBER_OF, new Value($value));
|
||||
}
|
||||
|
|
@ -195,6 +176,5 @@ class ExpressionBuilder
|
|||
public function endsWith($field, $value)
|
||||
{
|
||||
return new Comparison($field, Comparison::ENDS_WITH, new Value($value));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,4 @@
|
|||
<?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\Collections;
|
||||
|
||||
|
|
@ -31,8 +14,9 @@ namespace Doctrine\Common\Collections;
|
|||
* this API can implement efficient database access without having to ask the
|
||||
* EntityManager or Repositories.
|
||||
*
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @since 2.3
|
||||
* @phpstan-template TKey
|
||||
* @psalm-template TKey as array-key
|
||||
* @psalm-template T
|
||||
*/
|
||||
interface Selectable
|
||||
{
|
||||
|
|
@ -40,9 +24,9 @@ interface Selectable
|
|||
* Selects all elements from a selectable that match the expression and
|
||||
* returns a new collection containing these elements.
|
||||
*
|
||||
* @param Criteria $criteria
|
||||
*
|
||||
* @return Collection
|
||||
*
|
||||
* @psalm-return Collection<TKey,T>
|
||||
*/
|
||||
public function matching(Criteria $criteria);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
# Doctrine Inflector
|
||||
|
||||
Doctrine Inflector is a small library that can perform string manipulations
|
||||
with regard to upper-/lowercase and singular/plural forms of words.
|
||||
with regard to uppercase/lowercase and singular/plural forms of words.
|
||||
|
||||
[](https://travis-ci.org/doctrine/inflector)
|
||||
[](https://travis-ci.org/doctrine/inflector)
|
||||
[](https://scrutinizer-ci.com/g/doctrine/inflector/?branch=master)
|
||||
[](https://scrutinizer-ci.com/g/doctrine/inflector/?branch=master)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"name": "doctrine/inflector",
|
||||
"type": "library",
|
||||
"description": "Common String Manipulations with regard to casing and singular/plural rules.",
|
||||
"keywords": ["string", "inflection", "singularize", "pluralize"],
|
||||
"homepage": "http://www.doctrine-project.org",
|
||||
"description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.",
|
||||
"keywords": ["php", "strings", "words", "manipulation", "inflector", "inflection", "uppercase", "lowercase", "singular", "plural"],
|
||||
"homepage": "https://www.doctrine-project.org/projects/inflector.html",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
|
||||
|
|
@ -13,20 +13,30 @@
|
|||
{"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.0"
|
||||
"php": "^7.2 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^6.2"
|
||||
"doctrine/coding-standard": "^7.0",
|
||||
"phpstan/phpstan": "^0.11",
|
||||
"phpstan/phpstan-phpunit": "^0.11",
|
||||
"phpstan/phpstan-strict-rules": "^0.11",
|
||||
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Doctrine\\Common\\Inflector\\": "lib/Doctrine/Common/Inflector" }
|
||||
"psr-4": {
|
||||
"Doctrine\\Common\\Inflector\\": "lib/Doctrine/Common/Inflector",
|
||||
"Doctrine\\Inflector\\": "lib/Doctrine/Inflector"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": { "Doctrine\\Tests\\Common\\Inflector\\": "tests/Doctrine/Common/Inflector" }
|
||||
"psr-4": {
|
||||
"Doctrine\\Tests\\Common\\Inflector\\": "tests/Doctrine/Tests/Common/Inflector",
|
||||
"Doctrine\\Tests\\Inflector\\": "tests/Doctrine/Tests/Inflector"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.2.x-dev"
|
||||
"dev-master": "2.0.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,277 +19,97 @@
|
|||
|
||||
namespace Doctrine\Common\Inflector;
|
||||
|
||||
use Doctrine\Inflector\Inflector as InflectorObject;
|
||||
use Doctrine\Inflector\InflectorFactory;
|
||||
use Doctrine\Inflector\LanguageInflectorFactory;
|
||||
use Doctrine\Inflector\Rules\Pattern;
|
||||
use Doctrine\Inflector\Rules\Patterns;
|
||||
use Doctrine\Inflector\Rules\Ruleset;
|
||||
use Doctrine\Inflector\Rules\Substitution;
|
||||
use Doctrine\Inflector\Rules\Substitutions;
|
||||
use Doctrine\Inflector\Rules\Transformation;
|
||||
use Doctrine\Inflector\Rules\Transformations;
|
||||
use Doctrine\Inflector\Rules\Word;
|
||||
use InvalidArgumentException;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function array_unshift;
|
||||
use function array_values;
|
||||
use function sprintf;
|
||||
use function trigger_error;
|
||||
use const E_USER_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Doctrine inflector has static methods for inflecting text.
|
||||
*
|
||||
* The methods in these classes are from several different sources collected
|
||||
* across several different php projects and several different authors. The
|
||||
* original author names and emails are not known.
|
||||
*
|
||||
* Pluralize & Singularize implementation are borrowed from CakePHP with some modifications.
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
* @since 1.0
|
||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||
* @author Jonathan H. Wage <jonwage@gmail.com>
|
||||
* @deprecated
|
||||
*/
|
||||
class Inflector
|
||||
{
|
||||
/**
|
||||
* Plural inflector rules.
|
||||
*
|
||||
* @var array
|
||||
* @var LanguageInflectorFactory|null
|
||||
*/
|
||||
private static $plural = array(
|
||||
'rules' => array(
|
||||
'/(s)tatus$/i' => '\1\2tatuses',
|
||||
'/(quiz)$/i' => '\1zes',
|
||||
'/^(ox)$/i' => '\1\2en',
|
||||
'/([m|l])ouse$/i' => '\1ice',
|
||||
'/(matr|vert|ind)(ix|ex)$/i' => '\1ices',
|
||||
'/(x|ch|ss|sh)$/i' => '\1es',
|
||||
'/([^aeiouy]|qu)y$/i' => '\1ies',
|
||||
'/(hive|gulf)$/i' => '\1s',
|
||||
'/(?:([^f])fe|([lr])f)$/i' => '\1\2ves',
|
||||
'/sis$/i' => 'ses',
|
||||
'/([ti])um$/i' => '\1a',
|
||||
'/(p)erson$/i' => '\1eople',
|
||||
'/(m)an$/i' => '\1en',
|
||||
'/(c)hild$/i' => '\1hildren',
|
||||
'/(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',
|
||||
'/(analys|ax|cris|test|thes)is$/i' => '\1es',
|
||||
'/s$/' => 's',
|
||||
'/^$/' => '',
|
||||
'/$/' => 's',
|
||||
),
|
||||
'uninflected' => array(
|
||||
'.*[nrlm]ese',
|
||||
'.*deer',
|
||||
'.*fish',
|
||||
'.*measles',
|
||||
'.*ois',
|
||||
'.*pox',
|
||||
'.*sheep',
|
||||
'people',
|
||||
'cookie',
|
||||
'police',
|
||||
),
|
||||
'irregular' => array(
|
||||
'atlas' => 'atlases',
|
||||
'axe' => 'axes',
|
||||
'beef' => 'beefs',
|
||||
'brother' => 'brothers',
|
||||
'cafe' => 'cafes',
|
||||
'chateau' => 'chateaux',
|
||||
'niveau' => 'niveaux',
|
||||
'child' => 'children',
|
||||
'cookie' => 'cookies',
|
||||
'corpus' => 'corpuses',
|
||||
'cow' => 'cows',
|
||||
'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',
|
||||
'larva' => 'larvae',
|
||||
'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',
|
||||
'opus' => 'opuses',
|
||||
'ox' => 'oxen',
|
||||
'passerby' => 'passersby',
|
||||
'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',
|
||||
)
|
||||
);
|
||||
private static $factory;
|
||||
|
||||
/**
|
||||
* Singular inflector rules.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $singular = array(
|
||||
'rules' => array(
|
||||
'/(s)tatuses$/i' => '\1\2tatus',
|
||||
'/^(.*)(menu)s$/i' => '\1\2',
|
||||
'/(quiz)zes$/i' => '\\1',
|
||||
'/(matr)ices$/i' => '\1ix',
|
||||
'/(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',
|
||||
'/(analys|ax|cris|test|thes)es$/i' => '\1is',
|
||||
'/(shoe|slave)s$/i' => '\1',
|
||||
'/(o)es$/i' => '\1',
|
||||
'/ouses$/' => 'ouse',
|
||||
'/([^a])uses$/' => '\1us',
|
||||
'/([m|l])ice$/i' => '\1ouse',
|
||||
'/(x|ch|ss|sh)es$/i' => '\1',
|
||||
'/(m)ovies$/i' => '\1\2ovie',
|
||||
'/(s)eries$/i' => '\1\2eries',
|
||||
'/([^aeiouy]|qu)ies$/i' => '\1y',
|
||||
'/([lr])ves$/i' => '\1f',
|
||||
'/(tive)s$/i' => '\1',
|
||||
'/(hive)s$/i' => '\1',
|
||||
'/(drive)s$/i' => '\1',
|
||||
'/(dive)s$/i' => '\1',
|
||||
'/([^fo])ves$/i' => '\1fe',
|
||||
'/(^analy)ses$/i' => '\1sis',
|
||||
'/(analy|diagno|^ba|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis',
|
||||
'/([ti])a$/i' => '\1um',
|
||||
'/(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',
|
||||
'/s$/i' => '',
|
||||
),
|
||||
'uninflected' => array(
|
||||
'.*[nrlm]ese',
|
||||
'.*deer',
|
||||
'.*fish',
|
||||
'.*measles',
|
||||
'.*ois',
|
||||
'.*pox',
|
||||
'.*sheep',
|
||||
'.*ss',
|
||||
'police',
|
||||
'pants',
|
||||
'clothes',
|
||||
),
|
||||
'irregular' => array(
|
||||
'caches' => 'cache',
|
||||
'criteria' => 'criterion',
|
||||
'curves' => 'curve',
|
||||
'emphases' => 'emphasis',
|
||||
'foes' => 'foe',
|
||||
'hoaxes' => 'hoax',
|
||||
'media' => 'medium',
|
||||
'neuroses' => 'neurosis',
|
||||
'waves' => 'wave',
|
||||
'oases' => 'oasis',
|
||||
)
|
||||
);
|
||||
/** @var InflectorObject|null */
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Words that should not be inflected.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $uninflected = array(
|
||||
'Amoyese', 'bison', 'Borghese', 'bream', 'breeches', 'britches', 'buffalo', 'cantus',
|
||||
'carp', 'chassis', 'clippers', 'cod', 'coitus', 'Congoese', 'contretemps', 'corps',
|
||||
'debris', 'diabetes', 'djinn', 'eland', 'elk', 'equipment', 'Faroese', 'flounder',
|
||||
'Foochowese', 'Furniture', 'gallows', 'Genevese', 'Genoese', 'Gilbertese', 'graffiti',
|
||||
'headquarters', 'herpes', 'hijinks', 'Hottentotese', 'information', 'innings',
|
||||
'jackanapes', 'Kiplingese', 'Kongoese', 'Lucchese', 'Luggage', 'mackerel', 'Maltese', '.*?media',
|
||||
'mews', 'moose', 'mumps', 'Nankingese', 'news', 'nexus', 'Niasese',
|
||||
'Pekingese', 'Piedmontese', 'pincers', 'Pistoiese', 'pliers', 'Portuguese',
|
||||
'proceedings', 'rabies', 'rice', 'rhinoceros', 'salmon', 'Sarawakese', 'scissors',
|
||||
'sea[- ]bass', 'series', 'Shavese', 'shears', 'siemens', 'species', 'staff', 'swine',
|
||||
'testes', 'trousers', 'trout', 'tuna', 'Vermontese', 'Wenchowese', 'whiting',
|
||||
'wildebeest', 'Yengeese'
|
||||
);
|
||||
private static function getInstance() : InflectorObject
|
||||
{
|
||||
if (self::$factory === null) {
|
||||
self::$factory = self::createFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method cache array.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $cache = array();
|
||||
if (self::$instance === null) {
|
||||
self::$instance = self::$factory->build();
|
||||
}
|
||||
|
||||
/**
|
||||
* The initial state of Inflector so reset() works.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $initialState = array();
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
private static function createFactory() : LanguageInflectorFactory
|
||||
{
|
||||
return InflectorFactory::create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a word into the format for a Doctrine table name. Converts 'ModelName' to 'model_name'.
|
||||
*
|
||||
* @param string $word The word to tableize.
|
||||
*
|
||||
* @return string The tableized word.
|
||||
* @deprecated
|
||||
*/
|
||||
public static function tableize($word)
|
||||
public static function tableize(string $word) : string
|
||||
{
|
||||
return strtolower(preg_replace('~(?<=\\w)([A-Z])~', '_$1', $word));
|
||||
@trigger_error(sprintf('The "%s" method is deprecated and will be dropped in doctrine/inflector 2.0. Please update to the new Inflector API.', __METHOD__), E_USER_DEPRECATED);
|
||||
|
||||
return self::getInstance()->tableize($word);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a word into the format for a Doctrine class name. Converts 'table_name' to 'TableName'.
|
||||
*
|
||||
* @param string $word The word to classify.
|
||||
*
|
||||
* @return string The classified word.
|
||||
*/
|
||||
public static function classify($word)
|
||||
public static function classify(string $word) : string
|
||||
{
|
||||
return str_replace(' ', '', ucwords(strtr($word, '_-', ' ')));
|
||||
@trigger_error(sprintf('The "%s" method is deprecated and will be dropped in doctrine/inflector 2.0. Please update to the new Inflector API.', __METHOD__), E_USER_DEPRECATED);
|
||||
|
||||
return self::getInstance()->classify($word);
|
||||
}
|
||||
|
||||
/**
|
||||
* Camelizes a word. This uses the classify() method and turns the first character to lowercase.
|
||||
*
|
||||
* @param string $word The word to camelize.
|
||||
*
|
||||
* @return string The camelized word.
|
||||
* @deprecated
|
||||
*/
|
||||
public static function camelize($word)
|
||||
public static function camelize(string $word) : string
|
||||
{
|
||||
return lcfirst(self::classify($word));
|
||||
@trigger_error(sprintf('The "%s" method is deprecated and will be dropped in doctrine/inflector 2.0. Please update to the new Inflector API.', __METHOD__), E_USER_DEPRECATED);
|
||||
|
||||
return self::getInstance()->camelize($word);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uppercases words with configurable delimeters between words.
|
||||
* Uppercases words with configurable delimiters 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
|
||||
* ucwords function. This extends that behavior, however, by allowing the
|
||||
* word delimiters to be configured, rather than only separating on
|
||||
* whitespace.
|
||||
*
|
||||
* Here is an example:
|
||||
|
|
@ -307,38 +127,29 @@ class Inflector
|
|||
* @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.
|
||||
* @return string The string with all delimiter-separated words capitalized.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public static function ucwords($string, $delimiters = " \n\t\r\0\x0B-")
|
||||
public static function ucwords(string $string, string $delimiters = " \n\t\r\0\x0B-") : string
|
||||
{
|
||||
return preg_replace_callback(
|
||||
'/[^' . preg_quote($delimiters, '/') . ']+/',
|
||||
function($matches) {
|
||||
return ucfirst($matches[0]);
|
||||
},
|
||||
$string
|
||||
);
|
||||
@trigger_error(sprintf('The "%s" method is deprecated and will be dropped in doctrine/inflector 2.0. Please use the "ucwords" function instead.', __METHOD__), E_USER_DEPRECATED);
|
||||
|
||||
return ucwords($string, $delimiters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears Inflectors inflected value caches, and resets the inflection
|
||||
* rules to the initial values.
|
||||
*
|
||||
* @return void
|
||||
* @deprecated
|
||||
*/
|
||||
public static function reset()
|
||||
public static function reset() : void
|
||||
{
|
||||
if (empty(self::$initialState)) {
|
||||
self::$initialState = get_class_vars('Inflector');
|
||||
@trigger_error(sprintf('The "%s" method is deprecated and will be dropped in doctrine/inflector 2.0. Please update to the new Inflector API.', __METHOD__), E_USER_DEPRECATED);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (self::$initialState as $key => $val) {
|
||||
if ($key != 'initialState') {
|
||||
self::${$key} = $val;
|
||||
}
|
||||
}
|
||||
self::$factory = null;
|
||||
self::$instance = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -355,42 +166,85 @@ class Inflector
|
|||
* ));
|
||||
* }}}
|
||||
*
|
||||
* @param string $type The type of inflection, either 'plural' or 'singular'
|
||||
* @param array $rules An array of rules to be added.
|
||||
* @param boolean $reset If true, will unset default inflections for all
|
||||
* new rules that are being defined in $rules.
|
||||
* @param string $type The type of inflection, either 'plural' or 'singular'
|
||||
* @param array|iterable $rules An array of rules to be added.
|
||||
* @param boolean $reset If true, will unset default inflections for all
|
||||
* new rules that are being defined in $rules.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public static function rules($type, $rules, $reset = false)
|
||||
public static function rules(string $type, iterable $rules, bool $reset = false) : void
|
||||
{
|
||||
@trigger_error(sprintf('The "%s" method is deprecated and will be dropped in doctrine/inflector 2.0. Please update to the new Inflector API.', __METHOD__), E_USER_DEPRECATED);
|
||||
|
||||
if (self::$factory === null) {
|
||||
self::$factory = self::createFactory();
|
||||
}
|
||||
|
||||
self::$instance = null;
|
||||
|
||||
switch ($type) {
|
||||
case 'singular':
|
||||
self::$factory->withSingularRules(self::buildRuleset($rules), $reset);
|
||||
break;
|
||||
case 'plural':
|
||||
self::$factory->withPluralRules(self::buildRuleset($rules), $reset);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidArgumentException(sprintf('Cannot define custom inflection rules for type "%s".', $type));
|
||||
}
|
||||
}
|
||||
|
||||
private static function buildRuleset(iterable $rules) : Ruleset
|
||||
{
|
||||
$regular = [];
|
||||
$irregular = [];
|
||||
$uninflected = [];
|
||||
|
||||
foreach ($rules as $rule => $pattern) {
|
||||
if ( ! is_array($pattern)) {
|
||||
$regular[$rule] = $pattern;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($reset) {
|
||||
self::${$type}[$rule] = $pattern;
|
||||
} else {
|
||||
self::${$type}[$rule] = ($rule === 'uninflected')
|
||||
? array_merge($pattern, self::${$type}[$rule])
|
||||
: $pattern + self::${$type}[$rule];
|
||||
}
|
||||
|
||||
unset($rules[$rule], self::${$type}['cache' . ucfirst($rule)]);
|
||||
|
||||
if (isset(self::${$type}['merged'][$rule])) {
|
||||
unset(self::${$type}['merged'][$rule]);
|
||||
}
|
||||
|
||||
if ($type === 'plural') {
|
||||
self::$cache['pluralize'] = self::$cache['tableize'] = array();
|
||||
} elseif ($type === 'singular') {
|
||||
self::$cache['singularize'] = array();
|
||||
switch ($rule) {
|
||||
case 'uninflected':
|
||||
$uninflected = $pattern;
|
||||
break;
|
||||
case 'irregular':
|
||||
$irregular = $pattern;
|
||||
break;
|
||||
case 'rules':
|
||||
$regular = $pattern;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self::${$type}['rules'] = $rules + self::${$type}['rules'];
|
||||
return new Ruleset(
|
||||
new Transformations(...array_map(
|
||||
static function (string $pattern, string $replacement) : Transformation {
|
||||
return new Transformation(new Pattern($pattern), $replacement);
|
||||
},
|
||||
array_keys($regular),
|
||||
array_values($regular)
|
||||
)),
|
||||
new Patterns(...array_map(
|
||||
static function (string $pattern) : Pattern {
|
||||
return new Pattern($pattern);
|
||||
},
|
||||
$uninflected
|
||||
)),
|
||||
new Substitutions(...array_map(
|
||||
static function (string $word, string $to) : Substitution {
|
||||
return new Substitution(new Word($word), new Word($to));
|
||||
},
|
||||
array_keys($irregular),
|
||||
array_values($irregular)
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -399,45 +253,14 @@ class Inflector
|
|||
* @param string $word The word in singular form.
|
||||
*
|
||||
* @return string The word in plural form.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public static function pluralize($word)
|
||||
public static function pluralize(string $word) : string
|
||||
{
|
||||
if (isset(self::$cache['pluralize'][$word])) {
|
||||
return self::$cache['pluralize'][$word];
|
||||
}
|
||||
@trigger_error(sprintf('The "%s" method is deprecated and will be dropped in doctrine/inflector 2.0. Please update to the new Inflector API.', __METHOD__), E_USER_DEPRECATED);
|
||||
|
||||
if (!isset(self::$plural['merged']['irregular'])) {
|
||||
self::$plural['merged']['irregular'] = self::$plural['irregular'];
|
||||
}
|
||||
|
||||
if (!isset(self::$plural['merged']['uninflected'])) {
|
||||
self::$plural['merged']['uninflected'] = array_merge(self::$plural['uninflected'], self::$uninflected);
|
||||
}
|
||||
|
||||
if (!isset(self::$plural['cacheUninflected']) || !isset(self::$plural['cacheIrregular'])) {
|
||||
self::$plural['cacheUninflected'] = '(?:' . implode('|', self::$plural['merged']['uninflected']) . ')';
|
||||
self::$plural['cacheIrregular'] = '(?:' . implode('|', array_keys(self::$plural['merged']['irregular'])) . ')';
|
||||
}
|
||||
|
||||
if (preg_match('/(.*)\\b(' . self::$plural['cacheIrregular'] . ')$/i', $word, $regs)) {
|
||||
self::$cache['pluralize'][$word] = $regs[1] . substr($word, 0, 1) . substr(self::$plural['merged']['irregular'][strtolower($regs[2])], 1);
|
||||
|
||||
return self::$cache['pluralize'][$word];
|
||||
}
|
||||
|
||||
if (preg_match('/^(' . self::$plural['cacheUninflected'] . ')$/i', $word, $regs)) {
|
||||
self::$cache['pluralize'][$word] = $word;
|
||||
|
||||
return $word;
|
||||
}
|
||||
|
||||
foreach (self::$plural['rules'] as $rule => $replacement) {
|
||||
if (preg_match($rule, $word)) {
|
||||
self::$cache['pluralize'][$word] = preg_replace($rule, $replacement, $word);
|
||||
|
||||
return self::$cache['pluralize'][$word];
|
||||
}
|
||||
}
|
||||
return self::getInstance()->pluralize($word);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -446,54 +269,13 @@ class Inflector
|
|||
* @param string $word The word in plural form.
|
||||
*
|
||||
* @return string The word in singular form.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public static function singularize($word)
|
||||
public static function singularize(string $word) : string
|
||||
{
|
||||
if (isset(self::$cache['singularize'][$word])) {
|
||||
return self::$cache['singularize'][$word];
|
||||
}
|
||||
@trigger_error(sprintf('The "%s" method is deprecated and will be dropped in doctrine/inflector 2.0. Please update to the new Inflector API.', __METHOD__), E_USER_DEPRECATED);
|
||||
|
||||
if (!isset(self::$singular['merged']['uninflected'])) {
|
||||
self::$singular['merged']['uninflected'] = array_merge(
|
||||
self::$singular['uninflected'],
|
||||
self::$uninflected
|
||||
);
|
||||
}
|
||||
|
||||
if (!isset(self::$singular['merged']['irregular'])) {
|
||||
self::$singular['merged']['irregular'] = array_merge(
|
||||
self::$singular['irregular'],
|
||||
array_flip(self::$plural['irregular'])
|
||||
);
|
||||
}
|
||||
|
||||
if (!isset(self::$singular['cacheUninflected']) || !isset(self::$singular['cacheIrregular'])) {
|
||||
self::$singular['cacheUninflected'] = '(?:' . join('|', self::$singular['merged']['uninflected']) . ')';
|
||||
self::$singular['cacheIrregular'] = '(?:' . join('|', array_keys(self::$singular['merged']['irregular'])) . ')';
|
||||
}
|
||||
|
||||
if (preg_match('/(.*)\\b(' . self::$singular['cacheIrregular'] . ')$/i', $word, $regs)) {
|
||||
self::$cache['singularize'][$word] = $regs[1] . substr($word, 0, 1) . substr(self::$singular['merged']['irregular'][strtolower($regs[2])], 1);
|
||||
|
||||
return self::$cache['singularize'][$word];
|
||||
}
|
||||
|
||||
if (preg_match('/^(' . self::$singular['cacheUninflected'] . ')$/i', $word, $regs)) {
|
||||
self::$cache['singularize'][$word] = $word;
|
||||
|
||||
return $word;
|
||||
}
|
||||
|
||||
foreach (self::$singular['rules'] as $rule => $replacement) {
|
||||
if (preg_match($rule, $word)) {
|
||||
self::$cache['singularize'][$word] = preg_replace($rule, $replacement, $word);
|
||||
|
||||
return self::$cache['singularize'][$word];
|
||||
}
|
||||
}
|
||||
|
||||
self::$cache['singularize'][$word] = $word;
|
||||
|
||||
return $word;
|
||||
return self::getInstance()->singularize($word);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
# Doctrine Lexer
|
||||
|
||||
Build Status: [](https://travis-ci.org/doctrine/lexer)
|
||||
|
||||
Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.
|
||||
|
||||
This lexer is used in Doctrine Annotations and in Doctrine ORM (DQL).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,12 @@
|
|||
{"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.2"
|
||||
"php": "^7.2 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.5"
|
||||
"doctrine/coding-standard": "^6.0",
|
||||
"phpstan/phpstan": "^0.11.8",
|
||||
"phpunit/phpunit": "^8.2"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" }
|
||||
|
|
@ -30,7 +32,10 @@
|
|||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
"dev-master": "1.2.x-dev"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,31 +1,21 @@
|
|||
<?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>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Common\Lexer;
|
||||
|
||||
use ReflectionClass;
|
||||
use const PREG_SPLIT_DELIM_CAPTURE;
|
||||
use const PREG_SPLIT_NO_EMPTY;
|
||||
use const PREG_SPLIT_OFFSET_CAPTURE;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function preg_split;
|
||||
use function sprintf;
|
||||
use function substr;
|
||||
|
||||
/**
|
||||
* Base class for writing simple lexers, i.e. for creating small DSLs.
|
||||
*
|
||||
* @since 2.0
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
abstract class AbstractLexer
|
||||
{
|
||||
|
|
@ -47,36 +37,43 @@ abstract class AbstractLexer
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
private $tokens = array();
|
||||
private $tokens = [];
|
||||
|
||||
/**
|
||||
* Current lexer position in input string.
|
||||
*
|
||||
* @var integer
|
||||
* @var int
|
||||
*/
|
||||
private $position = 0;
|
||||
|
||||
/**
|
||||
* Current peek of current lexer position.
|
||||
*
|
||||
* @var integer
|
||||
* @var int
|
||||
*/
|
||||
private $peek = 0;
|
||||
|
||||
/**
|
||||
* The next token in the input.
|
||||
*
|
||||
* @var array
|
||||
* @var array|null
|
||||
*/
|
||||
public $lookahead;
|
||||
|
||||
/**
|
||||
* The last matched/seen token.
|
||||
*
|
||||
* @var array
|
||||
* @var array|null
|
||||
*/
|
||||
public $token;
|
||||
|
||||
/**
|
||||
* Composed regex for input parsing.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $regex;
|
||||
|
||||
/**
|
||||
* Sets the input data to be tokenized.
|
||||
*
|
||||
|
|
@ -90,7 +87,7 @@ abstract class AbstractLexer
|
|||
public function setInput($input)
|
||||
{
|
||||
$this->input = $input;
|
||||
$this->tokens = array();
|
||||
$this->tokens = [];
|
||||
|
||||
$this->reset();
|
||||
$this->scan($input);
|
||||
|
|
@ -104,9 +101,9 @@ abstract class AbstractLexer
|
|||
public function reset()
|
||||
{
|
||||
$this->lookahead = null;
|
||||
$this->token = null;
|
||||
$this->peek = 0;
|
||||
$this->position = 0;
|
||||
$this->token = null;
|
||||
$this->peek = 0;
|
||||
$this->position = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -122,7 +119,7 @@ abstract class AbstractLexer
|
|||
/**
|
||||
* Resets the lexer position on the input to the given position.
|
||||
*
|
||||
* @param integer $position Position to place the lexical scanner.
|
||||
* @param int $position Position to place the lexical scanner.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
|
@ -134,7 +131,7 @@ abstract class AbstractLexer
|
|||
/**
|
||||
* Retrieve the original lexer's input until a given position.
|
||||
*
|
||||
* @param integer $position
|
||||
* @param int $position
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
|
@ -146,13 +143,13 @@ abstract class AbstractLexer
|
|||
/**
|
||||
* Checks whether a given token matches the current lookahead.
|
||||
*
|
||||
* @param integer|string $token
|
||||
* @param int|string $token
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
*/
|
||||
public function isNextToken($token)
|
||||
{
|
||||
return null !== $this->lookahead && $this->lookahead['type'] === $token;
|
||||
return $this->lookahead !== null && $this->lookahead['type'] === $token;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -160,23 +157,23 @@ abstract class AbstractLexer
|
|||
*
|
||||
* @param array $tokens
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
*/
|
||||
public function isNextTokenAny(array $tokens)
|
||||
{
|
||||
return null !== $this->lookahead && in_array($this->lookahead['type'], $tokens, true);
|
||||
return $this->lookahead !== null && in_array($this->lookahead['type'], $tokens, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves to the next token in the input string.
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
*/
|
||||
public function moveNext()
|
||||
{
|
||||
$this->peek = 0;
|
||||
$this->token = $this->lookahead;
|
||||
$this->lookahead = (isset($this->tokens[$this->position]))
|
||||
$this->peek = 0;
|
||||
$this->token = $this->lookahead;
|
||||
$this->lookahead = isset($this->tokens[$this->position])
|
||||
? $this->tokens[$this->position++] : null;
|
||||
|
||||
return $this->lookahead !== null;
|
||||
|
|
@ -199,10 +196,10 @@ abstract class AbstractLexer
|
|||
/**
|
||||
* Checks if given value is identical to the given token.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param integer $token
|
||||
* @param mixed $value
|
||||
* @param int|string $token
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
*/
|
||||
public function isA($value, $token)
|
||||
{
|
||||
|
|
@ -218,9 +215,9 @@ abstract class AbstractLexer
|
|||
{
|
||||
if (isset($this->tokens[$this->position + $this->peek])) {
|
||||
return $this->tokens[$this->position + $this->peek++];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -230,8 +227,9 @@ abstract class AbstractLexer
|
|||
*/
|
||||
public function glimpse()
|
||||
{
|
||||
$peek = $this->peek();
|
||||
$peek = $this->peek();
|
||||
$this->peek = 0;
|
||||
|
||||
return $peek;
|
||||
}
|
||||
|
||||
|
|
@ -244,10 +242,8 @@ abstract class AbstractLexer
|
|||
*/
|
||||
protected function scan($input)
|
||||
{
|
||||
static $regex;
|
||||
|
||||
if ( ! isset($regex)) {
|
||||
$regex = sprintf(
|
||||
if (! isset($this->regex)) {
|
||||
$this->regex = sprintf(
|
||||
'/(%s)|%s/%s',
|
||||
implode(')|(', $this->getCatchablePatterns()),
|
||||
implode('|', $this->getNonCatchablePatterns()),
|
||||
|
|
@ -255,37 +251,37 @@ abstract class AbstractLexer
|
|||
);
|
||||
}
|
||||
|
||||
$flags = PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE;
|
||||
$matches = preg_split($regex, $input, -1, $flags);
|
||||
$flags = PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE;
|
||||
$matches = preg_split($this->regex, $input, -1, $flags);
|
||||
|
||||
if (false === $matches) {
|
||||
if ($matches === false) {
|
||||
// Work around https://bugs.php.net/78122
|
||||
$matches = array(array($input, 0));
|
||||
$matches = [[$input, 0]];
|
||||
}
|
||||
|
||||
foreach ($matches as $match) {
|
||||
// Must remain before 'value' assignment since it can change content
|
||||
$type = $this->getType($match[0]);
|
||||
|
||||
$this->tokens[] = array(
|
||||
$this->tokens[] = [
|
||||
'value' => $match[0],
|
||||
'type' => $type,
|
||||
'position' => $match[1],
|
||||
);
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the literal for a given token.
|
||||
*
|
||||
* @param integer $token
|
||||
* @param int|string $token
|
||||
*
|
||||
* @return string
|
||||
* @return int|string
|
||||
*/
|
||||
public function getLiteral($token)
|
||||
{
|
||||
$className = get_class($this);
|
||||
$reflClass = new \ReflectionClass($className);
|
||||
$className = static::class;
|
||||
$reflClass = new ReflectionClass($className);
|
||||
$constants = $reflClass->getConstants();
|
||||
|
||||
foreach ($constants as $name => $value) {
|
||||
|
|
@ -304,7 +300,7 @@ abstract class AbstractLexer
|
|||
*/
|
||||
protected function getModifiers()
|
||||
{
|
||||
return 'i';
|
||||
return 'iu';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -326,7 +322,7 @@ abstract class AbstractLexer
|
|||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return integer
|
||||
* @return int|string|null
|
||||
*/
|
||||
abstract protected function getType(&$value);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2013-2016 Eduardo Gulias Davis
|
||||
Copyright (c) 2013-2021 Eduardo Gulias Davis
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
|||
|
|
@ -9,18 +9,18 @@
|
|||
],
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.1.x-dev"
|
||||
"dev-master": "3.0.x-dev"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.5",
|
||||
"doctrine/lexer": "^1.0.1",
|
||||
"symfony/polyfill-intl-idn": "^1.10"
|
||||
"php": ">=7.2",
|
||||
"doctrine/lexer": "^1.2",
|
||||
"symfony/polyfill-intl-idn": "^1.15"
|
||||
},
|
||||
"require-dev": {
|
||||
"dominicsayers/isemail": "^3.0.7",
|
||||
"phpunit/phpunit": "^4.8.36|^7.5.15",
|
||||
"satooshi/php-coveralls": "^1.0.1"
|
||||
"php-coveralls/php-coveralls": "^2.2",
|
||||
"phpunit/phpunit": "^8.5.8|^9.3.3",
|
||||
"vimeo/psalm": "^4"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation"
|
||||
|
|
|
|||
|
|
@ -7,37 +7,52 @@ use Doctrine\Common\Lexer\AbstractLexer;
|
|||
class EmailLexer extends AbstractLexer
|
||||
{
|
||||
//ASCII values
|
||||
const C_DEL = 127;
|
||||
const C_NUL = 0;
|
||||
const S_AT = 64;
|
||||
const S_BACKSLASH = 92;
|
||||
const S_DOT = 46;
|
||||
const S_DQUOTE = 34;
|
||||
const S_SQUOTE = 39;
|
||||
const S_BACKTICK = 96;
|
||||
const S_OPENPARENTHESIS = 49;
|
||||
const S_CLOSEPARENTHESIS = 261;
|
||||
const S_OPENBRACKET = 262;
|
||||
const S_CLOSEBRACKET = 263;
|
||||
const S_HYPHEN = 264;
|
||||
const S_COLON = 265;
|
||||
const S_DOUBLECOLON = 266;
|
||||
const S_SP = 267;
|
||||
const S_HTAB = 268;
|
||||
const S_CR = 269;
|
||||
const S_LF = 270;
|
||||
const S_IPV6TAG = 271;
|
||||
const S_LOWERTHAN = 272;
|
||||
const S_GREATERTHAN = 273;
|
||||
const S_COMMA = 274;
|
||||
const S_SEMICOLON = 275;
|
||||
const S_OPENQBRACKET = 276;
|
||||
const S_CLOSEQBRACKET = 277;
|
||||
const S_SLASH = 278;
|
||||
const S_EMPTY = null;
|
||||
const C_NUL = 0;
|
||||
const S_HTAB = 9;
|
||||
const S_LF = 10;
|
||||
const S_CR = 13;
|
||||
const S_SP = 32;
|
||||
const EXCLAMATION = 33;
|
||||
const S_DQUOTE = 34;
|
||||
const NUMBER_SIGN = 35;
|
||||
const DOLLAR = 36;
|
||||
const PERCENTAGE = 37;
|
||||
const AMPERSAND = 38;
|
||||
const S_SQUOTE = 39;
|
||||
const S_OPENPARENTHESIS = 40;
|
||||
const S_CLOSEPARENTHESIS = 41;
|
||||
const ASTERISK = 42;
|
||||
const S_PLUS = 43;
|
||||
const S_COMMA = 44;
|
||||
const S_HYPHEN = 45;
|
||||
const S_DOT = 46;
|
||||
const S_SLASH = 47;
|
||||
const S_COLON = 58;
|
||||
const S_SEMICOLON = 59;
|
||||
const S_LOWERTHAN = 60;
|
||||
const S_EQUAL = 61;
|
||||
const S_GREATERTHAN = 62;
|
||||
const QUESTIONMARK = 63;
|
||||
const S_AT = 64;
|
||||
const S_OPENBRACKET = 91;
|
||||
const S_BACKSLASH = 92;
|
||||
const S_CLOSEBRACKET = 93;
|
||||
const CARET = 94;
|
||||
const S_UNDERSCORE = 95;
|
||||
const S_BACKTICK = 96;
|
||||
const S_OPENCURLYBRACES = 123;
|
||||
const S_PIPE = 124;
|
||||
const S_CLOSECURLYBRACES = 125;
|
||||
const S_TILDE = 126;
|
||||
const C_DEL = 127;
|
||||
const INVERT_QUESTIONMARK= 168;
|
||||
const INVERT_EXCLAMATION = 173;
|
||||
const GENERIC = 300;
|
||||
const CRLF = 301;
|
||||
const S_IPV6TAG = 301;
|
||||
const INVALID = 302;
|
||||
const CRLF = 1310;
|
||||
const S_DOUBLECOLON = 5858;
|
||||
const ASCII_INVALID_FROM = 127;
|
||||
const ASCII_INVALID_TO = 199;
|
||||
|
||||
|
|
@ -47,6 +62,8 @@ class EmailLexer extends AbstractLexer
|
|||
* @var array
|
||||
*/
|
||||
protected $charValue = array(
|
||||
'{' => self::S_OPENCURLYBRACES,
|
||||
'}' => self::S_CLOSECURLYBRACES,
|
||||
'(' => self::S_OPENPARENTHESIS,
|
||||
')' => self::S_CLOSEPARENTHESIS,
|
||||
'<' => self::S_LOWERTHAN,
|
||||
|
|
@ -71,10 +88,23 @@ class EmailLexer extends AbstractLexer
|
|||
"\n" => self::S_LF,
|
||||
"\r\n" => self::CRLF,
|
||||
'IPv6' => self::S_IPV6TAG,
|
||||
'{' => self::S_OPENQBRACKET,
|
||||
'}' => self::S_CLOSEQBRACKET,
|
||||
'' => self::S_EMPTY,
|
||||
'\0' => self::C_NUL,
|
||||
'*' => self::ASTERISK,
|
||||
'!' => self::EXCLAMATION,
|
||||
'&' => self::AMPERSAND,
|
||||
'^' => self::CARET,
|
||||
'$' => self::DOLLAR,
|
||||
'%' => self::PERCENTAGE,
|
||||
'~' => self::S_TILDE,
|
||||
'|' => self::S_PIPE,
|
||||
'_' => self::S_UNDERSCORE,
|
||||
'=' => self::S_EQUAL,
|
||||
'+' => self::S_PLUS,
|
||||
'¿' => self::INVERT_QUESTIONMARK,
|
||||
'?' => self::QUESTIONMARK,
|
||||
'#' => self::NUMBER_SIGN,
|
||||
'¡' => self::INVERT_EXCLAMATION,
|
||||
);
|
||||
|
||||
/**
|
||||
|
|
@ -94,7 +124,9 @@ class EmailLexer extends AbstractLexer
|
|||
*
|
||||
* @var array
|
||||
*
|
||||
* @psalm-suppress NonInvariantDocblockPropertyType
|
||||
* @psalm-var array{value:string, type:null|int, position:int}
|
||||
* @psalm-suppress NonInvariantDocblockPropertyType
|
||||
*/
|
||||
public $token;
|
||||
|
||||
|
|
@ -114,6 +146,16 @@ class EmailLexer extends AbstractLexer
|
|||
'position' => 0,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $accumulator = '';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $hasToRecord = false;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->previous = $this->token = self::$nullToken;
|
||||
|
|
@ -173,10 +215,18 @@ class EmailLexer extends AbstractLexer
|
|||
*/
|
||||
public function moveNext()
|
||||
{
|
||||
if ($this->hasToRecord && $this->previous === self::$nullToken) {
|
||||
$this->accumulator .= $this->token['value'];
|
||||
}
|
||||
|
||||
$this->previous = $this->token;
|
||||
$hasNext = parent::moveNext();
|
||||
$this->token = $this->token ?: self::$nullToken;
|
||||
|
||||
if ($this->hasToRecord) {
|
||||
$this->accumulator .= $this->token['value'];
|
||||
}
|
||||
|
||||
return $hasNext;
|
||||
}
|
||||
|
||||
|
|
@ -188,7 +238,7 @@ class EmailLexer extends AbstractLexer
|
|||
protected function getCatchablePatterns()
|
||||
{
|
||||
return array(
|
||||
'[a-zA-Z_]+[46]?', //ASCII and domain literal
|
||||
'[a-zA-Z]+[46]?', //ASCII and domain literal
|
||||
'[^\x00-\x7F]', //UTF-8
|
||||
'[0-9]+',
|
||||
'\r\n',
|
||||
|
|
@ -205,7 +255,9 @@ class EmailLexer extends AbstractLexer
|
|||
*/
|
||||
protected function getNonCatchablePatterns()
|
||||
{
|
||||
return array('[\xA0-\xff]+');
|
||||
return [
|
||||
'[\xA0-\xff]+',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -217,28 +269,38 @@ class EmailLexer extends AbstractLexer
|
|||
*/
|
||||
protected function getType(&$value)
|
||||
{
|
||||
if ($this->isNullType($value)) {
|
||||
$encoded = $value;
|
||||
|
||||
if (mb_detect_encoding($value, 'auto', true) !== 'UTF-8') {
|
||||
$encoded = utf8_encode($value);
|
||||
}
|
||||
|
||||
if ($this->isValid($encoded)) {
|
||||
return $this->charValue[$encoded];
|
||||
}
|
||||
|
||||
if ($this->isNullType($encoded)) {
|
||||
return self::C_NUL;
|
||||
}
|
||||
|
||||
if ($this->isValid($value)) {
|
||||
return $this->charValue[$value];
|
||||
}
|
||||
|
||||
if ($this->isUTF8Invalid($value)) {
|
||||
if ($this->isInvalidChar($encoded)) {
|
||||
$this->hasInvalidTokens = true;
|
||||
return self::INVALID;
|
||||
}
|
||||
|
||||
|
||||
return self::GENERIC;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isValid($value)
|
||||
protected function isInvalidChar(string $value) : bool
|
||||
{
|
||||
if(preg_match("/[^\p{S}\p{C}\p{Cc}]+/iu", $value) ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function isValid(string $value) : bool
|
||||
{
|
||||
if (isset($this->charValue[$value])) {
|
||||
return true;
|
||||
|
|
@ -260,11 +322,7 @@ class EmailLexer extends AbstractLexer
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return bool
|
||||
*/
|
||||
protected function isUTF8Invalid($value)
|
||||
protected function isUTF8Invalid(string $value) : bool
|
||||
{
|
||||
if (preg_match('/\p{Cc}+/u', $value)) {
|
||||
return true;
|
||||
|
|
@ -280,4 +338,24 @@ class EmailLexer extends AbstractLexer
|
|||
{
|
||||
return 'iu';
|
||||
}
|
||||
|
||||
public function getAccumulatedValues() : string
|
||||
{
|
||||
return $this->accumulator;
|
||||
}
|
||||
|
||||
public function startRecording() : void
|
||||
{
|
||||
$this->hasToRecord = true;
|
||||
}
|
||||
|
||||
public function stopRecording() : void
|
||||
{
|
||||
$this->hasToRecord = false;
|
||||
}
|
||||
|
||||
public function clearRecorded() : void
|
||||
{
|
||||
$this->accumulator = '';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,26 +2,19 @@
|
|||
|
||||
namespace Egulias\EmailValidator;
|
||||
|
||||
use Egulias\EmailValidator\Exception\ExpectingATEXT;
|
||||
use Egulias\EmailValidator\Exception\NoLocalPart;
|
||||
use Egulias\EmailValidator\Parser\DomainPart;
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Result\Result;
|
||||
use Egulias\EmailValidator\Parser\LocalPart;
|
||||
use Egulias\EmailValidator\Parser\DomainPart;
|
||||
use Egulias\EmailValidator\Result\ValidEmail;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Warning\EmailTooLong;
|
||||
use Egulias\EmailValidator\Result\Reason\NoLocalPart;
|
||||
|
||||
/**
|
||||
* EmailParser
|
||||
*
|
||||
* @author Eduardo Gulias Davis <me@egulias.com>
|
||||
*/
|
||||
class EmailParser
|
||||
class EmailParser extends Parser
|
||||
{
|
||||
const EMAIL_MAX_LENGTH = 254;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $warnings = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
|
|
@ -31,104 +24,65 @@ class EmailParser
|
|||
* @var string
|
||||
*/
|
||||
protected $localPart = '';
|
||||
/**
|
||||
* @var EmailLexer
|
||||
*/
|
||||
protected $lexer;
|
||||
|
||||
/**
|
||||
* @var LocalPart
|
||||
*/
|
||||
protected $localPartParser;
|
||||
|
||||
/**
|
||||
* @var DomainPart
|
||||
*/
|
||||
protected $domainPartParser;
|
||||
|
||||
public function __construct(EmailLexer $lexer)
|
||||
public function parse(string $str) : Result
|
||||
{
|
||||
$this->lexer = $lexer;
|
||||
$this->localPartParser = new LocalPart($this->lexer);
|
||||
$this->domainPartParser = new DomainPart($this->lexer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $str
|
||||
* @return array
|
||||
*/
|
||||
public function parse($str)
|
||||
{
|
||||
$this->lexer->setInput($str);
|
||||
|
||||
if (!$this->hasAtToken()) {
|
||||
throw new NoLocalPart();
|
||||
}
|
||||
|
||||
|
||||
$this->localPartParser->parse($str);
|
||||
$this->domainPartParser->parse($str);
|
||||
|
||||
$this->setParts($str);
|
||||
|
||||
if ($this->lexer->hasInvalidTokens()) {
|
||||
throw new ExpectingATEXT();
|
||||
}
|
||||
|
||||
return array('local' => $this->localPart, 'domain' => $this->domainPart);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Warning\Warning[]
|
||||
*/
|
||||
public function getWarnings()
|
||||
{
|
||||
$localPartWarnings = $this->localPartParser->getWarnings();
|
||||
$domainPartWarnings = $this->domainPartParser->getWarnings();
|
||||
$this->warnings = array_merge($localPartWarnings, $domainPartWarnings);
|
||||
$result = parent::parse($str);
|
||||
|
||||
$this->addLongEmailWarning($this->localPart, $this->domainPart);
|
||||
|
||||
return $this->warnings;
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function preLeftParsing(): Result
|
||||
{
|
||||
if (!$this->hasAtToken()) {
|
||||
return new InvalidEmail(new NoLocalPart(), $this->lexer->token["value"]);
|
||||
}
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getParsedDomainPart()
|
||||
protected function parseLeftFromAt(): Result
|
||||
{
|
||||
return $this->processLocalPart();
|
||||
}
|
||||
|
||||
protected function parseRightFromAt(): Result
|
||||
{
|
||||
return $this->processDomainPart();
|
||||
}
|
||||
|
||||
private function processLocalPart() : Result
|
||||
{
|
||||
$localPartParser = new LocalPart($this->lexer);
|
||||
$localPartResult = $localPartParser->parse();
|
||||
$this->localPart = $localPartParser->localPart();
|
||||
$this->warnings = array_merge($localPartParser->getWarnings(), $this->warnings);
|
||||
|
||||
return $localPartResult;
|
||||
}
|
||||
|
||||
private function processDomainPart() : Result
|
||||
{
|
||||
$domainPartParser = new DomainPart($this->lexer);
|
||||
$domainPartResult = $domainPartParser->parse();
|
||||
$this->domainPart = $domainPartParser->domainPart();
|
||||
$this->warnings = array_merge($domainPartParser->getWarnings(), $this->warnings);
|
||||
|
||||
return $domainPartResult;
|
||||
}
|
||||
|
||||
public function getDomainPart() : string
|
||||
{
|
||||
return $this->domainPart;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $email
|
||||
*/
|
||||
protected function setParts($email)
|
||||
public function getLocalPart() : string
|
||||
{
|
||||
$parts = explode('@', $email);
|
||||
$this->domainPart = $this->domainPartParser->getDomainPart();
|
||||
$this->localPart = $parts[0];
|
||||
return $this->localPart;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasAtToken()
|
||||
{
|
||||
$this->lexer->moveNext();
|
||||
$this->lexer->moveNext();
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_AT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $localPart
|
||||
* @param string $parsedDomainPart
|
||||
*/
|
||||
protected function addLongEmailWarning($localPart, $parsedDomainPart)
|
||||
private function addLongEmailWarning(string $localPart, string $parsedDomainPart) : void
|
||||
{
|
||||
if (strlen($localPart . '@' . $parsedDomainPart) > self::EMAIL_MAX_LENGTH) {
|
||||
$this->warnings[EmailTooLong::CODE] = new EmailTooLong();
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace Egulias\EmailValidator;
|
||||
|
||||
use Egulias\EmailValidator\Exception\InvalidEmail;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Validation\EmailValidation;
|
||||
|
||||
class EmailValidator
|
||||
|
|
@ -15,12 +15,12 @@ class EmailValidator
|
|||
/**
|
||||
* @var Warning\Warning[]
|
||||
*/
|
||||
protected $warnings = [];
|
||||
private $warnings = [];
|
||||
|
||||
/**
|
||||
* @var InvalidEmail|null
|
||||
* @var ?InvalidEmail
|
||||
*/
|
||||
protected $error;
|
||||
private $error;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
|
@ -32,7 +32,7 @@ class EmailValidator
|
|||
* @param EmailValidation $emailValidation
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($email, EmailValidation $emailValidation)
|
||||
public function isValid(string $email, EmailValidation $emailValidation)
|
||||
{
|
||||
$isValid = $emailValidation->isValid($email, $this->lexer);
|
||||
$this->warnings = $emailValidation->getWarnings();
|
||||
|
|
|
|||
|
|
@ -3,417 +3,310 @@
|
|||
namespace Egulias\EmailValidator\Parser;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Exception\CharNotAllowed;
|
||||
use Egulias\EmailValidator\Exception\CommaInDomain;
|
||||
use Egulias\EmailValidator\Exception\ConsecutiveAt;
|
||||
use Egulias\EmailValidator\Exception\CRLFAtTheEnd;
|
||||
use Egulias\EmailValidator\Exception\CRNoLF;
|
||||
use Egulias\EmailValidator\Exception\DomainHyphened;
|
||||
use Egulias\EmailValidator\Exception\DotAtEnd;
|
||||
use Egulias\EmailValidator\Exception\DotAtStart;
|
||||
use Egulias\EmailValidator\Exception\ExpectingATEXT;
|
||||
use Egulias\EmailValidator\Exception\ExpectingDomainLiteralClose;
|
||||
use Egulias\EmailValidator\Exception\ExpectingDTEXT;
|
||||
use Egulias\EmailValidator\Exception\NoDomainPart;
|
||||
use Egulias\EmailValidator\Exception\UnopenedComment;
|
||||
use Egulias\EmailValidator\Warning\AddressLiteral;
|
||||
use Egulias\EmailValidator\Warning\CFWSWithFWS;
|
||||
use Egulias\EmailValidator\Warning\DeprecatedComment;
|
||||
use Egulias\EmailValidator\Warning\DomainLiteral;
|
||||
use Egulias\EmailValidator\Warning\DomainTooLong;
|
||||
use Egulias\EmailValidator\Warning\IPV6BadChar;
|
||||
use Egulias\EmailValidator\Warning\IPV6ColonEnd;
|
||||
use Egulias\EmailValidator\Warning\IPV6ColonStart;
|
||||
use Egulias\EmailValidator\Warning\IPV6Deprecated;
|
||||
use Egulias\EmailValidator\Warning\IPV6DoubleColon;
|
||||
use Egulias\EmailValidator\Warning\IPV6GroupCount;
|
||||
use Egulias\EmailValidator\Warning\IPV6MaxGroups;
|
||||
use Egulias\EmailValidator\Warning\LabelTooLong;
|
||||
use Egulias\EmailValidator\Warning\ObsoleteDTEXT;
|
||||
use Egulias\EmailValidator\Warning\TLD;
|
||||
use Egulias\EmailValidator\Result\Result;
|
||||
use Egulias\EmailValidator\Result\ValidEmail;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Result\Reason\DotAtEnd;
|
||||
use Egulias\EmailValidator\Result\Reason\DotAtStart;
|
||||
use Egulias\EmailValidator\Warning\DeprecatedComment;
|
||||
use Egulias\EmailValidator\Result\Reason\CRLFAtTheEnd;
|
||||
use Egulias\EmailValidator\Result\Reason\LabelTooLong;
|
||||
use Egulias\EmailValidator\Result\Reason\NoDomainPart;
|
||||
use Egulias\EmailValidator\Result\Reason\ConsecutiveAt;
|
||||
use Egulias\EmailValidator\Result\Reason\DomainTooLong;
|
||||
use Egulias\EmailValidator\Result\Reason\CharNotAllowed;
|
||||
use Egulias\EmailValidator\Result\Reason\DomainHyphened;
|
||||
use Egulias\EmailValidator\Result\Reason\ExpectingATEXT;
|
||||
use Egulias\EmailValidator\Parser\CommentStrategy\DomainComment;
|
||||
use Egulias\EmailValidator\Result\Reason\ExpectingDomainLiteralClose;
|
||||
use Egulias\EmailValidator\Parser\DomainLiteral as DomainLiteralParser;
|
||||
|
||||
class DomainPart extends Parser
|
||||
class DomainPart extends PartParser
|
||||
{
|
||||
const DOMAIN_MAX_LENGTH = 254;
|
||||
const DOMAIN_MAX_LENGTH = 253;
|
||||
const LABEL_MAX_LENGTH = 63;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $domainPart = '';
|
||||
|
||||
public function parse($domainPart)
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $label = '';
|
||||
|
||||
public function parse() : Result
|
||||
{
|
||||
$this->lexer->clearRecorded();
|
||||
$this->lexer->startRecording();
|
||||
|
||||
$this->lexer->moveNext();
|
||||
|
||||
$this->performDomainStartChecks();
|
||||
|
||||
$domain = $this->doParseDomainPart();
|
||||
|
||||
$prev = $this->lexer->getPrevious();
|
||||
$length = strlen($domain);
|
||||
|
||||
if ($prev['type'] === EmailLexer::S_DOT) {
|
||||
throw new DotAtEnd();
|
||||
$domainChecks = $this->performDomainStartChecks();
|
||||
if ($domainChecks->isInvalid()) {
|
||||
return $domainChecks;
|
||||
}
|
||||
if ($prev['type'] === EmailLexer::S_HYPHEN) {
|
||||
throw new DomainHyphened();
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_AT) {
|
||||
return new InvalidEmail(new ConsecutiveAt(), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
$result = $this->doParseDomainPart();
|
||||
if ($result->isInvalid()) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$end = $this->checkEndOfDomain();
|
||||
if ($end->isInvalid()) {
|
||||
return $end;
|
||||
}
|
||||
|
||||
$this->lexer->stopRecording();
|
||||
$this->domainPart = $this->lexer->getAccumulatedValues();
|
||||
|
||||
$length = strlen($this->domainPart);
|
||||
if ($length > self::DOMAIN_MAX_LENGTH) {
|
||||
$this->warnings[DomainTooLong::CODE] = new DomainTooLong();
|
||||
return new InvalidEmail(new DomainTooLong(), $this->lexer->token['value']);
|
||||
}
|
||||
if ($prev['type'] === EmailLexer::S_CR) {
|
||||
throw new CRLFAtTheEnd();
|
||||
}
|
||||
$this->domainPart = $domain;
|
||||
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
private function performDomainStartChecks()
|
||||
private function checkEndOfDomain() : Result
|
||||
{
|
||||
$this->checkInvalidTokensAfterAT();
|
||||
$this->checkEmptyDomain();
|
||||
$prev = $this->lexer->getPrevious();
|
||||
if ($prev['type'] === EmailLexer::S_DOT) {
|
||||
return new InvalidEmail(new DotAtEnd(), $this->lexer->token['value']);
|
||||
}
|
||||
if ($prev['type'] === EmailLexer::S_HYPHEN) {
|
||||
return new InvalidEmail(new DomainHyphened('Hypen found at the end of the domain'), $prev['value']);
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_SP) {
|
||||
return new InvalidEmail(new CRLFAtTheEnd(), $prev['value']);
|
||||
}
|
||||
return new ValidEmail();
|
||||
|
||||
}
|
||||
|
||||
private function performDomainStartChecks() : Result
|
||||
{
|
||||
$invalidTokens = $this->checkInvalidTokensAfterAT();
|
||||
if ($invalidTokens->isInvalid()) {
|
||||
return $invalidTokens;
|
||||
}
|
||||
|
||||
$missingDomain = $this->checkEmptyDomain();
|
||||
if ($missingDomain->isInvalid()) {
|
||||
return $missingDomain;
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
|
||||
$this->warnings[DeprecatedComment::CODE] = new DeprecatedComment();
|
||||
$this->parseDomainComments();
|
||||
}
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
private function checkEmptyDomain()
|
||||
private function checkEmptyDomain() : Result
|
||||
{
|
||||
$thereIsNoDomain = $this->lexer->token['type'] === EmailLexer::S_EMPTY ||
|
||||
($this->lexer->token['type'] === EmailLexer::S_SP &&
|
||||
!$this->lexer->isNextToken(EmailLexer::GENERIC));
|
||||
|
||||
if ($thereIsNoDomain) {
|
||||
throw new NoDomainPart();
|
||||
return new InvalidEmail(new NoDomainPart(), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
private function checkInvalidTokensAfterAT()
|
||||
private function checkInvalidTokensAfterAT() : Result
|
||||
{
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_DOT) {
|
||||
throw new DotAtStart();
|
||||
return new InvalidEmail(new DotAtStart(), $this->lexer->token['value']);
|
||||
}
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_HYPHEN) {
|
||||
throw new DomainHyphened();
|
||||
return new InvalidEmail(new DomainHyphened('After AT'), $this->lexer->token['value']);
|
||||
}
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDomainPart()
|
||||
protected function parseComments(): Result
|
||||
{
|
||||
return $this->domainPart;
|
||||
$commentParser = new Comment($this->lexer, new DomainComment());
|
||||
$result = $commentParser->parse();
|
||||
$this->warnings = array_merge($this->warnings, $commentParser->getWarnings());
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $addressLiteral
|
||||
* @param int $maxGroups
|
||||
*/
|
||||
public function checkIPV6Tag($addressLiteral, $maxGroups = 8)
|
||||
{
|
||||
$prev = $this->lexer->getPrevious();
|
||||
if ($prev['type'] === EmailLexer::S_COLON) {
|
||||
$this->warnings[IPV6ColonEnd::CODE] = new IPV6ColonEnd();
|
||||
}
|
||||
|
||||
$IPv6 = substr($addressLiteral, 5);
|
||||
//Daniel Marschall's new IPv6 testing strategy
|
||||
$matchesIP = explode(':', $IPv6);
|
||||
$groupCount = count($matchesIP);
|
||||
$colons = strpos($IPv6, '::');
|
||||
|
||||
if (count(preg_grep('/^[0-9A-Fa-f]{0,4}$/', $matchesIP, PREG_GREP_INVERT)) !== 0) {
|
||||
$this->warnings[IPV6BadChar::CODE] = new IPV6BadChar();
|
||||
}
|
||||
|
||||
if ($colons === false) {
|
||||
// We need exactly the right number of groups
|
||||
if ($groupCount !== $maxGroups) {
|
||||
$this->warnings[IPV6GroupCount::CODE] = new IPV6GroupCount();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ($colons !== strrpos($IPv6, '::')) {
|
||||
$this->warnings[IPV6DoubleColon::CODE] = new IPV6DoubleColon();
|
||||
return;
|
||||
}
|
||||
|
||||
if ($colons === 0 || $colons === (strlen($IPv6) - 2)) {
|
||||
// RFC 4291 allows :: at the start or end of an address
|
||||
//with 7 other groups in addition
|
||||
++$maxGroups;
|
||||
}
|
||||
|
||||
if ($groupCount > $maxGroups) {
|
||||
$this->warnings[IPV6MaxGroups::CODE] = new IPV6MaxGroups();
|
||||
} elseif ($groupCount === $maxGroups) {
|
||||
$this->warnings[IPV6Deprecated::CODE] = new IPV6Deprecated();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function doParseDomainPart()
|
||||
protected function doParseDomainPart() : Result
|
||||
{
|
||||
$tldMissing = true;
|
||||
$hasComments = false;
|
||||
$domain = '';
|
||||
$openedParenthesis = 0;
|
||||
do {
|
||||
$prev = $this->lexer->getPrevious();
|
||||
|
||||
$this->checkNotAllowedChars($this->lexer->token);
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
|
||||
$this->parseComments();
|
||||
$openedParenthesis += $this->getOpenedParenthesis();
|
||||
$this->lexer->moveNext();
|
||||
$tmpPrev = $this->lexer->getPrevious();
|
||||
if ($tmpPrev['type'] === EmailLexer::S_CLOSEPARENTHESIS) {
|
||||
$openedParenthesis--;
|
||||
}
|
||||
$notAllowedChars = $this->checkNotAllowedChars($this->lexer->token);
|
||||
if ($notAllowedChars->isInvalid()) {
|
||||
return $notAllowedChars;
|
||||
}
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS) {
|
||||
if ($openedParenthesis === 0) {
|
||||
throw new UnopenedComment();
|
||||
} else {
|
||||
$openedParenthesis--;
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS ||
|
||||
$this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS ) {
|
||||
$hasComments = true;
|
||||
$commentsResult = $this->parseComments();
|
||||
|
||||
//Invalid comment parsing
|
||||
if($commentsResult->isInvalid()) {
|
||||
return $commentsResult;
|
||||
}
|
||||
}
|
||||
|
||||
$this->checkConsecutiveDots();
|
||||
$this->checkDomainPartExceptions($prev);
|
||||
|
||||
if ($this->hasBrackets()) {
|
||||
$this->parseDomainLiteral();
|
||||
$dotsResult = $this->checkConsecutiveDots();
|
||||
if ($dotsResult->isInvalid()) {
|
||||
return $dotsResult;
|
||||
}
|
||||
|
||||
$this->checkLabelLength($prev);
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_OPENBRACKET) {
|
||||
$literalResult = $this->parseDomainLiteral();
|
||||
|
||||
if ($this->isFWS()) {
|
||||
$this->parseFWS();
|
||||
$this->addTLDWarnings($tldMissing);
|
||||
return $literalResult;
|
||||
}
|
||||
|
||||
$labelCheck = $this->checkLabelLength();
|
||||
if ($labelCheck->isInvalid()) {
|
||||
return $labelCheck;
|
||||
}
|
||||
|
||||
$FwsResult = $this->parseFWS();
|
||||
if($FwsResult->isInvalid()) {
|
||||
return $FwsResult;
|
||||
}
|
||||
|
||||
$domain .= $this->lexer->token['value'];
|
||||
$this->lexer->moveNext();
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_SP) {
|
||||
throw new CharNotAllowed();
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::GENERIC)) {
|
||||
$tldMissing = false;
|
||||
}
|
||||
|
||||
$exceptionsResult = $this->checkDomainPartExceptions($prev, $hasComments);
|
||||
if ($exceptionsResult->isInvalid()) {
|
||||
return $exceptionsResult;
|
||||
}
|
||||
$this->lexer->moveNext();
|
||||
|
||||
} while (null !== $this->lexer->token['type']);
|
||||
|
||||
return $domain;
|
||||
$labelCheck = $this->checkLabelLength(true);
|
||||
if ($labelCheck->isInvalid()) {
|
||||
return $labelCheck;
|
||||
}
|
||||
$this->addTLDWarnings($tldMissing);
|
||||
|
||||
$this->domainPart = $domain;
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
private function checkNotAllowedChars(array $token)
|
||||
private function checkNotAllowedChars(array $token) : Result
|
||||
{
|
||||
$notAllowed = [EmailLexer::S_BACKSLASH => true, EmailLexer::S_SLASH=> true];
|
||||
if (isset($notAllowed[$token['type']])) {
|
||||
throw new CharNotAllowed();
|
||||
return new InvalidEmail(new CharNotAllowed(), $token['value']);
|
||||
}
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|false
|
||||
* @return Result
|
||||
*/
|
||||
protected function parseDomainLiteral()
|
||||
protected function parseDomainLiteral() : Result
|
||||
{
|
||||
if ($this->lexer->isNextToken(EmailLexer::S_COLON)) {
|
||||
$this->warnings[IPV6ColonStart::CODE] = new IPV6ColonStart();
|
||||
}
|
||||
if ($this->lexer->isNextToken(EmailLexer::S_IPV6TAG)) {
|
||||
$lexer = clone $this->lexer;
|
||||
$lexer->moveNext();
|
||||
if ($lexer->isNextToken(EmailLexer::S_DOUBLECOLON)) {
|
||||
$this->warnings[IPV6ColonStart::CODE] = new IPV6ColonStart();
|
||||
}
|
||||
try {
|
||||
$this->lexer->find(EmailLexer::S_CLOSEBRACKET);
|
||||
} catch (\RuntimeException $e) {
|
||||
return new InvalidEmail(new ExpectingDomainLiteralClose(), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
return $this->doParseDomainLiteral();
|
||||
$domainLiteralParser = new DomainLiteralParser($this->lexer);
|
||||
$result = $domainLiteralParser->parse();
|
||||
$this->warnings = array_merge($this->warnings, $domainLiteralParser->getWarnings());
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|false
|
||||
*/
|
||||
protected function doParseDomainLiteral()
|
||||
protected function checkDomainPartExceptions(array $prev, bool $hasComments) : Result
|
||||
{
|
||||
$IPv6TAG = false;
|
||||
$addressLiteral = '';
|
||||
do {
|
||||
if ($this->lexer->token['type'] === EmailLexer::C_NUL) {
|
||||
throw new ExpectingDTEXT();
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::INVALID ||
|
||||
$this->lexer->token['type'] === EmailLexer::C_DEL ||
|
||||
$this->lexer->token['type'] === EmailLexer::S_LF
|
||||
) {
|
||||
$this->warnings[ObsoleteDTEXT::CODE] = new ObsoleteDTEXT();
|
||||
}
|
||||
|
||||
if ($this->lexer->isNextTokenAny(array(EmailLexer::S_OPENQBRACKET, EmailLexer::S_OPENBRACKET))) {
|
||||
throw new ExpectingDTEXT();
|
||||
}
|
||||
|
||||
if ($this->lexer->isNextTokenAny(
|
||||
array(EmailLexer::S_HTAB, EmailLexer::S_SP, $this->lexer->token['type'] === EmailLexer::CRLF)
|
||||
)) {
|
||||
$this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS();
|
||||
$this->parseFWS();
|
||||
}
|
||||
|
||||
if ($this->lexer->isNextToken(EmailLexer::S_CR)) {
|
||||
throw new CRNoLF();
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH) {
|
||||
$this->warnings[ObsoleteDTEXT::CODE] = new ObsoleteDTEXT();
|
||||
$addressLiteral .= $this->lexer->token['value'];
|
||||
$this->lexer->moveNext();
|
||||
$this->validateQuotedPair();
|
||||
}
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_IPV6TAG) {
|
||||
$IPv6TAG = true;
|
||||
}
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_CLOSEQBRACKET) {
|
||||
break;
|
||||
}
|
||||
|
||||
$addressLiteral .= $this->lexer->token['value'];
|
||||
|
||||
} while ($this->lexer->moveNext());
|
||||
|
||||
$addressLiteral = str_replace('[', '', $addressLiteral);
|
||||
$addressLiteral = $this->checkIPV4Tag($addressLiteral);
|
||||
|
||||
if (false === $addressLiteral) {
|
||||
return $addressLiteral;
|
||||
}
|
||||
|
||||
if (!$IPv6TAG) {
|
||||
$this->warnings[DomainLiteral::CODE] = new DomainLiteral();
|
||||
return $addressLiteral;
|
||||
}
|
||||
|
||||
$this->warnings[AddressLiteral::CODE] = new AddressLiteral();
|
||||
|
||||
$this->checkIPV6Tag($addressLiteral);
|
||||
|
||||
return $addressLiteral;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $addressLiteral
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
protected function checkIPV4Tag($addressLiteral)
|
||||
{
|
||||
$matchesIP = array();
|
||||
|
||||
// Extract IPv4 part from the end of the address-literal (if there is one)
|
||||
if (preg_match(
|
||||
'/\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/',
|
||||
$addressLiteral,
|
||||
$matchesIP
|
||||
) > 0
|
||||
) {
|
||||
$index = strrpos($addressLiteral, $matchesIP[0]);
|
||||
if ($index === 0) {
|
||||
$this->warnings[AddressLiteral::CODE] = new AddressLiteral();
|
||||
return false;
|
||||
}
|
||||
// Convert IPv4 part to IPv6 format for further testing
|
||||
$addressLiteral = substr($addressLiteral, 0, (int) $index) . '0:0';
|
||||
}
|
||||
|
||||
return $addressLiteral;
|
||||
}
|
||||
|
||||
protected function checkDomainPartExceptions(array $prev)
|
||||
{
|
||||
$invalidDomainTokens = array(
|
||||
EmailLexer::S_DQUOTE => true,
|
||||
EmailLexer::S_SQUOTE => true,
|
||||
EmailLexer::S_BACKTICK => true,
|
||||
EmailLexer::S_SEMICOLON => true,
|
||||
EmailLexer::S_GREATERTHAN => true,
|
||||
EmailLexer::S_LOWERTHAN => true,
|
||||
);
|
||||
|
||||
if (isset($invalidDomainTokens[$this->lexer->token['type']])) {
|
||||
throw new ExpectingATEXT();
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_COMMA) {
|
||||
throw new CommaInDomain();
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_AT) {
|
||||
throw new ConsecutiveAt();
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_OPENQBRACKET && $prev['type'] !== EmailLexer::S_AT) {
|
||||
throw new ExpectingATEXT();
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_OPENBRACKET && $prev['type'] !== EmailLexer::S_AT) {
|
||||
return new InvalidEmail(new ExpectingATEXT('OPENBRACKET not after AT'), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_HYPHEN && $this->lexer->isNextToken(EmailLexer::S_DOT)) {
|
||||
throw new DomainHyphened();
|
||||
return new InvalidEmail(new DomainHyphened('Hypen found near DOT'), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH
|
||||
&& $this->lexer->isNextToken(EmailLexer::GENERIC)) {
|
||||
throw new ExpectingATEXT();
|
||||
return new InvalidEmail(new ExpectingATEXT('Escaping following "ATOM"'), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
return $this->validateTokens($hasComments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasBrackets()
|
||||
protected function validateTokens(bool $hasComments) : Result
|
||||
{
|
||||
if ($this->lexer->token['type'] !== EmailLexer::S_OPENBRACKET) {
|
||||
return false;
|
||||
$validDomainTokens = array(
|
||||
EmailLexer::GENERIC => true,
|
||||
EmailLexer::S_HYPHEN => true,
|
||||
EmailLexer::S_DOT => true,
|
||||
);
|
||||
|
||||
if ($hasComments) {
|
||||
$validDomainTokens[EmailLexer::S_OPENPARENTHESIS] = true;
|
||||
$validDomainTokens[EmailLexer::S_CLOSEPARENTHESIS] = true;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->lexer->find(EmailLexer::S_CLOSEBRACKET);
|
||||
} catch (\RuntimeException $e) {
|
||||
throw new ExpectingDomainLiteralClose();
|
||||
if (!isset($validDomainTokens[$this->lexer->token['type']])) {
|
||||
return new InvalidEmail(new ExpectingATEXT('Invalid token in domain: ' . $this->lexer->token['value']), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
return true;
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
protected function checkLabelLength(array $prev)
|
||||
private function checkLabelLength(bool $isEndOfDomain = false) : Result
|
||||
{
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_DOT &&
|
||||
$prev['type'] === EmailLexer::GENERIC &&
|
||||
strlen($prev['value']) > 63
|
||||
) {
|
||||
$this->warnings[LabelTooLong::CODE] = new LabelTooLong();
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_DOT || $isEndOfDomain) {
|
||||
if ($this->isLabelTooLong($this->label)) {
|
||||
return new InvalidEmail(new LabelTooLong(), $this->lexer->token['value']);
|
||||
}
|
||||
$this->label = '';
|
||||
}
|
||||
$this->label .= $this->lexer->token['value'];
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
protected function parseDomainComments()
|
||||
{
|
||||
$this->isUnclosedComment();
|
||||
while (!$this->lexer->isNextToken(EmailLexer::S_CLOSEPARENTHESIS)) {
|
||||
$this->warnEscaping();
|
||||
$this->lexer->moveNext();
|
||||
}
|
||||
|
||||
$this->lexer->moveNext();
|
||||
if ($this->lexer->isNextToken(EmailLexer::S_DOT)) {
|
||||
throw new ExpectingATEXT();
|
||||
private function isLabelTooLong(string $label) : bool
|
||||
{
|
||||
if (preg_match('/[^\x00-\x7F]/', $label)) {
|
||||
idn_to_ascii(utf8_decode($label), IDNA_DEFAULT, INTL_IDNA_VARIANT_UTS46, $idnaInfo);
|
||||
return (bool) ($idnaInfo['errors'] & IDNA_ERROR_LABEL_TOO_LONG);
|
||||
}
|
||||
return strlen($label) > self::LABEL_MAX_LENGTH;
|
||||
}
|
||||
|
||||
protected function addTLDWarnings()
|
||||
private function addTLDWarnings(bool $isTLDMissing) : void
|
||||
{
|
||||
if ($this->warnings[DomainLiteral::CODE]) {
|
||||
if ($isTLDMissing) {
|
||||
$this->warnings[TLD::CODE] = new TLD();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function domainPart() : string
|
||||
{
|
||||
return $this->domainPart;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,144 +2,163 @@
|
|||
|
||||
namespace Egulias\EmailValidator\Parser;
|
||||
|
||||
use Egulias\EmailValidator\Exception\DotAtEnd;
|
||||
use Egulias\EmailValidator\Exception\DotAtStart;
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Exception\ExpectingAT;
|
||||
use Egulias\EmailValidator\Exception\ExpectingATEXT;
|
||||
use Egulias\EmailValidator\Exception\UnclosedQuotedString;
|
||||
use Egulias\EmailValidator\Exception\UnopenedComment;
|
||||
use Egulias\EmailValidator\Warning\CFWSWithFWS;
|
||||
use Egulias\EmailValidator\Result\Result;
|
||||
use Egulias\EmailValidator\Result\ValidEmail;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Warning\LocalTooLong;
|
||||
use Egulias\EmailValidator\Result\Reason\DotAtEnd;
|
||||
use Egulias\EmailValidator\Result\Reason\DotAtStart;
|
||||
use Egulias\EmailValidator\Result\Reason\ConsecutiveDot;
|
||||
use Egulias\EmailValidator\Result\Reason\ExpectingATEXT;
|
||||
use Egulias\EmailValidator\Parser\CommentStrategy\LocalComment;
|
||||
|
||||
class LocalPart extends Parser
|
||||
class LocalPart extends PartParser
|
||||
{
|
||||
public function parse($localPart)
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $localPart = '';
|
||||
|
||||
|
||||
public function parse() : Result
|
||||
{
|
||||
$parseDQuote = true;
|
||||
$closingQuote = false;
|
||||
$openedParenthesis = 0;
|
||||
$totalLength = 0;
|
||||
$this->lexer->startRecording();
|
||||
|
||||
while ($this->lexer->token['type'] !== EmailLexer::S_AT && null !== $this->lexer->token['type']) {
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_DOT && null === $this->lexer->getPrevious()['type']) {
|
||||
throw new DotAtStart();
|
||||
if ($this->hasDotAtStart()) {
|
||||
return new InvalidEmail(new DotAtStart(), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
$closingQuote = $this->checkDQUOTE($closingQuote);
|
||||
if ($closingQuote && $parseDQuote) {
|
||||
$parseDQuote = $this->parseDoubleQuote();
|
||||
}
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_DQUOTE) {
|
||||
$dquoteParsingResult = $this->parseDoubleQuote();
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
|
||||
$this->parseComments();
|
||||
$openedParenthesis += $this->getOpenedParenthesis();
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS) {
|
||||
if ($openedParenthesis === 0) {
|
||||
throw new UnopenedComment();
|
||||
//Invalid double quote parsing
|
||||
if($dquoteParsingResult->isInvalid()) {
|
||||
return $dquoteParsingResult;
|
||||
}
|
||||
|
||||
$openedParenthesis--;
|
||||
}
|
||||
|
||||
$this->checkConsecutiveDots();
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS ||
|
||||
$this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS ) {
|
||||
$commentsResult = $this->parseComments();
|
||||
|
||||
//Invalid comment parsing
|
||||
if($commentsResult->isInvalid()) {
|
||||
return $commentsResult;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::S_DOT)) {
|
||||
return new InvalidEmail(new ConsecutiveDot(), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_DOT &&
|
||||
$this->lexer->isNextToken(EmailLexer::S_AT)
|
||||
) {
|
||||
throw new DotAtEnd();
|
||||
return new InvalidEmail(new DotAtEnd(), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
$this->warnEscaping();
|
||||
$this->isInvalidToken($this->lexer->token, $closingQuote);
|
||||
|
||||
if ($this->isFWS()) {
|
||||
$this->parseFWS();
|
||||
$resultEscaping = $this->validateEscaping();
|
||||
if ($resultEscaping->isInvalid()) {
|
||||
return $resultEscaping;
|
||||
}
|
||||
|
||||
$resultToken = $this->validateTokens(false);
|
||||
if ($resultToken->isInvalid()) {
|
||||
return $resultToken;
|
||||
}
|
||||
|
||||
$resultFWS = $this->parseLocalFWS();
|
||||
if($resultFWS->isInvalid()) {
|
||||
return $resultFWS;
|
||||
}
|
||||
|
||||
$totalLength += strlen($this->lexer->token['value']);
|
||||
$this->lexer->moveNext();
|
||||
}
|
||||
|
||||
if ($totalLength > LocalTooLong::LOCAL_PART_LENGTH) {
|
||||
$this->lexer->stopRecording();
|
||||
$this->localPart = rtrim($this->lexer->getAccumulatedValues(), '@');
|
||||
if (strlen($this->localPart) > LocalTooLong::LOCAL_PART_LENGTH) {
|
||||
$this->warnings[LocalTooLong::CODE] = new LocalTooLong();
|
||||
}
|
||||
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function parseDoubleQuote()
|
||||
protected function validateTokens(bool $hasComments) : Result
|
||||
{
|
||||
$parseAgain = true;
|
||||
$special = array(
|
||||
EmailLexer::S_CR => true,
|
||||
EmailLexer::S_HTAB => true,
|
||||
EmailLexer::S_LF => true
|
||||
$invalidTokens = array(
|
||||
EmailLexer::S_COMMA => EmailLexer::S_COMMA,
|
||||
EmailLexer::S_CLOSEBRACKET => EmailLexer::S_CLOSEBRACKET,
|
||||
EmailLexer::S_OPENBRACKET => EmailLexer::S_OPENBRACKET,
|
||||
EmailLexer::S_GREATERTHAN => EmailLexer::S_GREATERTHAN,
|
||||
EmailLexer::S_LOWERTHAN => EmailLexer::S_LOWERTHAN,
|
||||
EmailLexer::S_COLON => EmailLexer::S_COLON,
|
||||
EmailLexer::S_SEMICOLON => EmailLexer::S_SEMICOLON,
|
||||
EmailLexer::INVALID => EmailLexer::INVALID
|
||||
);
|
||||
|
||||
$invalid = array(
|
||||
EmailLexer::C_NUL => true,
|
||||
EmailLexer::S_HTAB => true,
|
||||
EmailLexer::S_CR => true,
|
||||
EmailLexer::S_LF => true
|
||||
);
|
||||
$setSpecialsWarning = true;
|
||||
|
||||
$this->lexer->moveNext();
|
||||
|
||||
while ($this->lexer->token['type'] !== EmailLexer::S_DQUOTE && null !== $this->lexer->token['type']) {
|
||||
$parseAgain = false;
|
||||
if (isset($special[$this->lexer->token['type']]) && $setSpecialsWarning) {
|
||||
$this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS();
|
||||
$setSpecialsWarning = false;
|
||||
}
|
||||
if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH && $this->lexer->isNextToken(EmailLexer::S_DQUOTE)) {
|
||||
$this->lexer->moveNext();
|
||||
}
|
||||
|
||||
$this->lexer->moveNext();
|
||||
|
||||
if (!$this->escaped() && isset($invalid[$this->lexer->token['type']])) {
|
||||
throw new ExpectingATEXT();
|
||||
}
|
||||
if (isset($invalidTokens[$this->lexer->token['type']])) {
|
||||
return new InvalidEmail(new ExpectingATEXT('Invalid token found'), $this->lexer->token['value']);
|
||||
}
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
$prev = $this->lexer->getPrevious();
|
||||
public function localPart() : string
|
||||
{
|
||||
return $this->localPart;
|
||||
}
|
||||
|
||||
if ($prev['type'] === EmailLexer::S_BACKSLASH) {
|
||||
if (!$this->checkDQUOTE(false)) {
|
||||
throw new UnclosedQuotedString();
|
||||
}
|
||||
private function parseLocalFWS() : Result
|
||||
{
|
||||
$foldingWS = new FoldingWhiteSpace($this->lexer);
|
||||
$resultFWS = $foldingWS->parse();
|
||||
if ($resultFWS->isValid()) {
|
||||
$this->warnings = array_merge($this->warnings, $foldingWS->getWarnings());
|
||||
}
|
||||
return $resultFWS;
|
||||
}
|
||||
|
||||
if (!$this->lexer->isNextToken(EmailLexer::S_AT) && $prev['type'] !== EmailLexer::S_BACKSLASH) {
|
||||
throw new ExpectingAT();
|
||||
}
|
||||
private function hasDotAtStart() : bool
|
||||
{
|
||||
return $this->lexer->token['type'] === EmailLexer::S_DOT && null === $this->lexer->getPrevious()['type'];
|
||||
}
|
||||
|
||||
private function parseDoubleQuote() : Result
|
||||
{
|
||||
$dquoteParser = new DoubleQuote($this->lexer);
|
||||
$parseAgain = $dquoteParser->parse();
|
||||
$this->warnings = array_merge($this->warnings, $dquoteParser->getWarnings());
|
||||
|
||||
return $parseAgain;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $closingQuote
|
||||
*/
|
||||
protected function isInvalidToken(array $token, $closingQuote)
|
||||
protected function parseComments(): Result
|
||||
{
|
||||
$forbidden = array(
|
||||
EmailLexer::S_COMMA,
|
||||
EmailLexer::S_CLOSEBRACKET,
|
||||
EmailLexer::S_OPENBRACKET,
|
||||
EmailLexer::S_GREATERTHAN,
|
||||
EmailLexer::S_LOWERTHAN,
|
||||
EmailLexer::S_COLON,
|
||||
EmailLexer::S_SEMICOLON,
|
||||
EmailLexer::INVALID
|
||||
);
|
||||
|
||||
if (in_array($token['type'], $forbidden) && !$closingQuote) {
|
||||
throw new ExpectingATEXT();
|
||||
$commentParser = new Comment($this->lexer, new LocalComment());
|
||||
$result = $commentParser->parse();
|
||||
$this->warnings = array_merge($this->warnings, $commentParser->getWarnings());
|
||||
if($result->isInvalid()) {
|
||||
return $result;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
private function validateEscaping() : Result
|
||||
{
|
||||
//Backslash found
|
||||
if ($this->lexer->token['type'] !== EmailLexer::S_BACKSLASH) {
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
if ($this->lexer->isNextToken(EmailLexer::GENERIC)) {
|
||||
return new InvalidEmail(new ExpectingATEXT('Found ATOM after escaping'), $this->lexer->token['value']);
|
||||
}
|
||||
|
||||
if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB, EmailLexer::C_DEL))) {
|
||||
return new ValidEmail();
|
||||
}
|
||||
|
||||
return new ValidEmail();
|
||||
}
|
||||
}
|
||||
|
|
@ -3,14 +3,20 @@
|
|||
namespace Egulias\EmailValidator\Validation;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Exception\InvalidEmail;
|
||||
use Egulias\EmailValidator\Exception\LocalOrReservedDomain;
|
||||
use Egulias\EmailValidator\Exception\DomainAcceptsNoMail;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Result\Reason\DomainAcceptsNoMail;
|
||||
use Egulias\EmailValidator\Result\Reason\LocalOrReservedDomain;
|
||||
use Egulias\EmailValidator\Result\Reason\NoDNSRecord as ReasonNoDNSRecord;
|
||||
use Egulias\EmailValidator\Result\Reason\UnableToGetDNSRecord;
|
||||
use Egulias\EmailValidator\Warning\NoDNSMXRecord;
|
||||
use Egulias\EmailValidator\Exception\NoDNSRecord;
|
||||
|
||||
class DNSCheckValidation implements EmailValidation
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected const DNS_RECORD_TYPES_TO_CHECK = DNS_MX + DNS_A + DNS_AAAA;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
|
|
@ -34,7 +40,7 @@ class DNSCheckValidation implements EmailValidation
|
|||
}
|
||||
}
|
||||
|
||||
public function isValid($email, EmailLexer $emailLexer)
|
||||
public function isValid(string $email, EmailLexer $emailLexer) : bool
|
||||
{
|
||||
// use the input to check DNS if we cannot extract something similar to a domain
|
||||
$host = $email;
|
||||
|
|
@ -73,19 +79,19 @@ class DNSCheckValidation implements EmailValidation
|
|||
|
||||
// Exclude reserved top level DNS names
|
||||
if ($isLocalDomain || $isReservedTopLevel) {
|
||||
$this->error = new LocalOrReservedDomain();
|
||||
$this->error = new InvalidEmail(new LocalOrReservedDomain(), $host);
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->checkDns($host);
|
||||
}
|
||||
|
||||
public function getError()
|
||||
public function getError() : ?InvalidEmail
|
||||
{
|
||||
return $this->error;
|
||||
}
|
||||
|
||||
public function getWarnings()
|
||||
public function getWarnings() : array
|
||||
{
|
||||
return $this->warnings;
|
||||
}
|
||||
|
|
@ -112,31 +118,43 @@ class DNSCheckValidation implements EmailValidation
|
|||
*
|
||||
* @return bool True on success.
|
||||
*/
|
||||
private function validateDnsRecords($host)
|
||||
private function validateDnsRecords($host) : bool
|
||||
{
|
||||
// Get all MX, A and AAAA DNS records for host
|
||||
// Using @ as workaround to fix https://bugs.php.net/bug.php?id=73149
|
||||
$dnsRecords = @dns_get_record($host, DNS_MX + DNS_A + DNS_AAAA);
|
||||
// A workaround to fix https://bugs.php.net/bug.php?id=73149
|
||||
/** @psalm-suppress InvalidArgument */
|
||||
set_error_handler(
|
||||
static function (int $errorLevel, string $errorMessage): ?bool {
|
||||
throw new \RuntimeException("Unable to get DNS record for the host: $errorMessage");
|
||||
}
|
||||
);
|
||||
|
||||
try {
|
||||
// Get all MX, A and AAAA DNS records for host
|
||||
$dnsRecords = dns_get_record($host, static::DNS_RECORD_TYPES_TO_CHECK);
|
||||
} catch (\RuntimeException $exception) {
|
||||
$this->error = new InvalidEmail(new UnableToGetDNSRecord(), '');
|
||||
|
||||
return false;
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
// No MX, A or AAAA DNS records
|
||||
if (empty($dnsRecords)) {
|
||||
$this->error = new NoDNSRecord();
|
||||
if ($dnsRecords === [] || $dnsRecords === false) {
|
||||
$this->error = new InvalidEmail(new ReasonNoDNSRecord(), '');
|
||||
return false;
|
||||
}
|
||||
|
||||
// For each DNS record
|
||||
foreach ($dnsRecords as $dnsRecord) {
|
||||
if (!$this->validateMXRecord($dnsRecord)) {
|
||||
// No MX records (fallback to A or AAAA records)
|
||||
if (empty($this->mxRecords)) {
|
||||
$this->warnings[NoDNSMXRecord::CODE] = new NoDNSMXRecord();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// No MX records (fallback to A or AAAA records)
|
||||
if (empty($this->mxRecords)) {
|
||||
$this->warnings[NoDNSMXRecord::CODE] = new NoDNSMXRecord();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -147,7 +165,7 @@ class DNSCheckValidation implements EmailValidation
|
|||
*
|
||||
* @return bool True if valid.
|
||||
*/
|
||||
private function validateMxRecord($dnsRecord)
|
||||
private function validateMxRecord($dnsRecord) : bool
|
||||
{
|
||||
if ($dnsRecord['type'] !== 'MX') {
|
||||
return true;
|
||||
|
|
@ -155,7 +173,7 @@ class DNSCheckValidation implements EmailValidation
|
|||
|
||||
// "Null MX" record indicates the domain accepts no mail (https://tools.ietf.org/html/rfc7505)
|
||||
if (empty($dnsRecord['target']) || $dnsRecord['target'] === '.') {
|
||||
$this->error = new DomainAcceptsNoMail();
|
||||
$this->error = new InvalidEmail(new DomainAcceptsNoMail(), "");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -163,4 +181,4 @@ class DNSCheckValidation implements EmailValidation
|
|||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
namespace Egulias\EmailValidator\Validation;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Exception\InvalidEmail;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Warning\Warning;
|
||||
|
||||
interface EmailValidation
|
||||
|
|
@ -16,19 +16,19 @@ interface EmailValidation
|
|||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($email, EmailLexer $emailLexer);
|
||||
public function isValid(string $email, EmailLexer $emailLexer) : bool;
|
||||
|
||||
/**
|
||||
* Returns the validation error.
|
||||
*
|
||||
* @return InvalidEmail|null
|
||||
*/
|
||||
public function getError();
|
||||
public function getError() : ?InvalidEmail;
|
||||
|
||||
/**
|
||||
* Returns the validation warnings.
|
||||
*
|
||||
* @return Warning[]
|
||||
*/
|
||||
public function getWarnings();
|
||||
public function getWarnings() : array;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,13 +3,15 @@
|
|||
namespace Egulias\EmailValidator\Validation;
|
||||
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Validation\Exception\EmptyValidationList;
|
||||
use Egulias\EmailValidator\Result\MultipleErrors;
|
||||
|
||||
class MultipleValidationWithAnd implements EmailValidation
|
||||
{
|
||||
/**
|
||||
* If one of validations gets failure skips all succeeding validation.
|
||||
* This means MultipleErrors will only contain a single error which first found.
|
||||
* If one of validations fails, the remaining validations will be skept.
|
||||
* This means MultipleErrors will only contain a single error, the first found.
|
||||
*/
|
||||
const STOP_ON_ERROR = 0;
|
||||
|
||||
|
|
@ -56,60 +58,51 @@ class MultipleValidationWithAnd implements EmailValidation
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isValid($email, EmailLexer $emailLexer)
|
||||
public function isValid(string $email, EmailLexer $emailLexer) : bool
|
||||
{
|
||||
$result = true;
|
||||
$errors = [];
|
||||
foreach ($this->validations as $validation) {
|
||||
$emailLexer->reset();
|
||||
$validationResult = $validation->isValid($email, $emailLexer);
|
||||
$result = $result && $validationResult;
|
||||
$this->warnings = array_merge($this->warnings, $validation->getWarnings());
|
||||
$errors = $this->addNewError($validation->getError(), $errors);
|
||||
if (!$validationResult) {
|
||||
$this->processError($validation);
|
||||
}
|
||||
|
||||
if ($this->shouldStop($result)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
$this->error = new MultipleErrors($errors);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Egulias\EmailValidator\Exception\InvalidEmail|null $possibleError
|
||||
* @param \Egulias\EmailValidator\Exception\InvalidEmail[] $errors
|
||||
*
|
||||
* @return \Egulias\EmailValidator\Exception\InvalidEmail[]
|
||||
*/
|
||||
private function addNewError($possibleError, array $errors)
|
||||
private function initErrorStorage() : void
|
||||
{
|
||||
if (null !== $possibleError) {
|
||||
$errors[] = $possibleError;
|
||||
if (null === $this->error) {
|
||||
$this->error = new MultipleErrors();
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $result
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function shouldStop($result)
|
||||
private function processError(EmailValidation $validation) : void
|
||||
{
|
||||
if (null !== $validation->getError()) {
|
||||
$this->initErrorStorage();
|
||||
/** @psalm-suppress PossiblyNullReference */
|
||||
$this->error->addReason($validation->getError()->reason());
|
||||
}
|
||||
}
|
||||
|
||||
private function shouldStop(bool $result) : bool
|
||||
{
|
||||
return !$result && $this->mode === self::STOP_ON_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the validation errors.
|
||||
*
|
||||
* @return MultipleErrors|null
|
||||
*/
|
||||
public function getError()
|
||||
public function getError() : ?InvalidEmail
|
||||
{
|
||||
return $this->error;
|
||||
}
|
||||
|
|
@ -117,7 +110,7 @@ class MultipleValidationWithAnd implements EmailValidation
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getWarnings()
|
||||
public function getWarnings() : array
|
||||
{
|
||||
return $this->warnings;
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue