KodeStar
7 years ago
6392 changed files with 700972 additions and 2 deletions
@ -0,0 +1,31 @@ |
|||||
|
APP_NAME=IFAI |
||||
|
APP_ENV=local |
||||
|
APP_KEY=base64:I206O8ibx+GQyRE7BeOxDobn04Mfmyyc5Ptzns/C0mY= |
||||
|
APP_DEBUG=true |
||||
|
APP_LOG_LEVEL=debug |
||||
|
APP_URL=http://localhost |
||||
|
|
||||
|
DB_CONNECTION=sqlite |
||||
|
DB_DATABASE=app.sqlite |
||||
|
|
||||
|
BROADCAST_DRIVER=log |
||||
|
CACHE_DRIVER=file |
||||
|
SESSION_DRIVER=file |
||||
|
SESSION_LIFETIME=120 |
||||
|
QUEUE_DRIVER=sync |
||||
|
|
||||
|
REDIS_HOST=127.0.0.1 |
||||
|
REDIS_PASSWORD=null |
||||
|
REDIS_PORT=6379 |
||||
|
|
||||
|
MAIL_DRIVER=smtp |
||||
|
MAIL_HOST=smtp.mailtrap.io |
||||
|
MAIL_PORT=2525 |
||||
|
MAIL_USERNAME=null |
||||
|
MAIL_PASSWORD=null |
||||
|
MAIL_ENCRYPTION=null |
||||
|
|
||||
|
PUSHER_APP_ID= |
||||
|
PUSHER_APP_KEY= |
||||
|
PUSHER_APP_SECRET= |
||||
|
PUSHER_APP_CLUSTER=mt1 |
@ -0,0 +1,7 @@ |
|||||
|
<?php |
||||
|
|
||||
|
// autoload.php @generated by Composer |
||||
|
|
||||
|
require_once __DIR__ . '/composer/autoload_real.php'; |
||||
|
|
||||
|
return ComposerAutoloaderInit4b6fb9210a1ea37c2db27b8ff53a1ecf::getLoader(); |
@ -0,0 +1 @@ |
|||||
|
../nikic/php-parser/bin/php-parse |
@ -0,0 +1 @@ |
|||||
|
../phpunit/phpunit/phpunit |
@ -0,0 +1 @@ |
|||||
|
../psy/psysh/bin/psysh |
@ -0,0 +1,445 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of Composer. |
||||
|
* |
||||
|
* (c) Nils Adermann <naderman@naderman.de> |
||||
|
* Jordi Boggiano <j.boggiano@seld.be> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Composer\Autoload; |
||||
|
|
||||
|
/** |
||||
|
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader. |
||||
|
* |
||||
|
* $loader = new \Composer\Autoload\ClassLoader(); |
||||
|
* |
||||
|
* // register classes with namespaces |
||||
|
* $loader->add('Symfony\Component', __DIR__.'/component'); |
||||
|
* $loader->add('Symfony', __DIR__.'/framework'); |
||||
|
* |
||||
|
* // activate the autoloader |
||||
|
* $loader->register(); |
||||
|
* |
||||
|
* // to enable searching the include path (eg. for PEAR packages) |
||||
|
* $loader->setUseIncludePath(true); |
||||
|
* |
||||
|
* In this example, if you try to use a class in the Symfony\Component |
||||
|
* namespace or one of its children (Symfony\Component\Console for instance), |
||||
|
* the autoloader will first look for the class under the component/ |
||||
|
* directory, and it will then fallback to the framework/ directory if not |
||||
|
* found before giving up. |
||||
|
* |
||||
|
* This class is loosely based on the Symfony UniversalClassLoader. |
||||
|
* |
||||
|
* @author Fabien Potencier <fabien@symfony.com> |
||||
|
* @author Jordi Boggiano <j.boggiano@seld.be> |
||||
|
* @see http://www.php-fig.org/psr/psr-0/ |
||||
|
* @see http://www.php-fig.org/psr/psr-4/ |
||||
|
*/ |
||||
|
class ClassLoader |
||||
|
{ |
||||
|
// PSR-4 |
||||
|
private $prefixLengthsPsr4 = array(); |
||||
|
private $prefixDirsPsr4 = array(); |
||||
|
private $fallbackDirsPsr4 = array(); |
||||
|
|
||||
|
// PSR-0 |
||||
|
private $prefixesPsr0 = array(); |
||||
|
private $fallbackDirsPsr0 = array(); |
||||
|
|
||||
|
private $useIncludePath = false; |
||||
|
private $classMap = array(); |
||||
|
private $classMapAuthoritative = false; |
||||
|
private $missingClasses = array(); |
||||
|
private $apcuPrefix; |
||||
|
|
||||
|
public function getPrefixes() |
||||
|
{ |
||||
|
if (!empty($this->prefixesPsr0)) { |
||||
|
return call_user_func_array('array_merge', $this->prefixesPsr0); |
||||
|
} |
||||
|
|
||||
|
return array(); |
||||
|
} |
||||
|
|
||||
|
public function getPrefixesPsr4() |
||||
|
{ |
||||
|
return $this->prefixDirsPsr4; |
||||
|
} |
||||
|
|
||||
|
public function getFallbackDirs() |
||||
|
{ |
||||
|
return $this->fallbackDirsPsr0; |
||||
|
} |
||||
|
|
||||
|
public function getFallbackDirsPsr4() |
||||
|
{ |
||||
|
return $this->fallbackDirsPsr4; |
||||
|
} |
||||
|
|
||||
|
public function getClassMap() |
||||
|
{ |
||||
|
return $this->classMap; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param array $classMap Class to filename map |
||||
|
*/ |
||||
|
public function addClassMap(array $classMap) |
||||
|
{ |
||||
|
if ($this->classMap) { |
||||
|
$this->classMap = array_merge($this->classMap, $classMap); |
||||
|
} else { |
||||
|
$this->classMap = $classMap; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Registers a set of PSR-0 directories for a given prefix, either |
||||
|
* appending or prepending to the ones previously set for this prefix. |
||||
|
* |
||||
|
* @param string $prefix The prefix |
||||
|
* @param array|string $paths The PSR-0 root directories |
||||
|
* @param bool $prepend Whether to prepend the directories |
||||
|
*/ |
||||
|
public function add($prefix, $paths, $prepend = false) |
||||
|
{ |
||||
|
if (!$prefix) { |
||||
|
if ($prepend) { |
||||
|
$this->fallbackDirsPsr0 = array_merge( |
||||
|
(array) $paths, |
||||
|
$this->fallbackDirsPsr0 |
||||
|
); |
||||
|
} else { |
||||
|
$this->fallbackDirsPsr0 = array_merge( |
||||
|
$this->fallbackDirsPsr0, |
||||
|
(array) $paths |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
$first = $prefix[0]; |
||||
|
if (!isset($this->prefixesPsr0[$first][$prefix])) { |
||||
|
$this->prefixesPsr0[$first][$prefix] = (array) $paths; |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
if ($prepend) { |
||||
|
$this->prefixesPsr0[$first][$prefix] = array_merge( |
||||
|
(array) $paths, |
||||
|
$this->prefixesPsr0[$first][$prefix] |
||||
|
); |
||||
|
} else { |
||||
|
$this->prefixesPsr0[$first][$prefix] = array_merge( |
||||
|
$this->prefixesPsr0[$first][$prefix], |
||||
|
(array) $paths |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Registers a set of PSR-4 directories for a given namespace, either |
||||
|
* appending or prepending to the ones previously set for this namespace. |
||||
|
* |
||||
|
* @param string $prefix The prefix/namespace, with trailing '\\' |
||||
|
* @param array|string $paths The PSR-4 base directories |
||||
|
* @param bool $prepend Whether to prepend the directories |
||||
|
* |
||||
|
* @throws \InvalidArgumentException |
||||
|
*/ |
||||
|
public function addPsr4($prefix, $paths, $prepend = false) |
||||
|
{ |
||||
|
if (!$prefix) { |
||||
|
// Register directories for the root namespace. |
||||
|
if ($prepend) { |
||||
|
$this->fallbackDirsPsr4 = array_merge( |
||||
|
(array) $paths, |
||||
|
$this->fallbackDirsPsr4 |
||||
|
); |
||||
|
} else { |
||||
|
$this->fallbackDirsPsr4 = array_merge( |
||||
|
$this->fallbackDirsPsr4, |
||||
|
(array) $paths |
||||
|
); |
||||
|
} |
||||
|
} elseif (!isset($this->prefixDirsPsr4[$prefix])) { |
||||
|
// Register directories for a new namespace. |
||||
|
$length = strlen($prefix); |
||||
|
if ('\\' !== $prefix[$length - 1]) { |
||||
|
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); |
||||
|
} |
||||
|
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; |
||||
|
$this->prefixDirsPsr4[$prefix] = (array) $paths; |
||||
|
} elseif ($prepend) { |
||||
|
// Prepend directories for an already registered namespace. |
||||
|
$this->prefixDirsPsr4[$prefix] = array_merge( |
||||
|
(array) $paths, |
||||
|
$this->prefixDirsPsr4[$prefix] |
||||
|
); |
||||
|
} else { |
||||
|
// Append directories for an already registered namespace. |
||||
|
$this->prefixDirsPsr4[$prefix] = array_merge( |
||||
|
$this->prefixDirsPsr4[$prefix], |
||||
|
(array) $paths |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Registers a set of PSR-0 directories for a given prefix, |
||||
|
* replacing any others previously set for this prefix. |
||||
|
* |
||||
|
* @param string $prefix The prefix |
||||
|
* @param array|string $paths The PSR-0 base directories |
||||
|
*/ |
||||
|
public function set($prefix, $paths) |
||||
|
{ |
||||
|
if (!$prefix) { |
||||
|
$this->fallbackDirsPsr0 = (array) $paths; |
||||
|
} else { |
||||
|
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Registers a set of PSR-4 directories for a given namespace, |
||||
|
* replacing any others previously set for this namespace. |
||||
|
* |
||||
|
* @param string $prefix The prefix/namespace, with trailing '\\' |
||||
|
* @param array|string $paths The PSR-4 base directories |
||||
|
* |
||||
|
* @throws \InvalidArgumentException |
||||
|
*/ |
||||
|
public function setPsr4($prefix, $paths) |
||||
|
{ |
||||
|
if (!$prefix) { |
||||
|
$this->fallbackDirsPsr4 = (array) $paths; |
||||
|
} else { |
||||
|
$length = strlen($prefix); |
||||
|
if ('\\' !== $prefix[$length - 1]) { |
||||
|
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); |
||||
|
} |
||||
|
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; |
||||
|
$this->prefixDirsPsr4[$prefix] = (array) $paths; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Turns on searching the include path for class files. |
||||
|
* |
||||
|
* @param bool $useIncludePath |
||||
|
*/ |
||||
|
public function setUseIncludePath($useIncludePath) |
||||
|
{ |
||||
|
$this->useIncludePath = $useIncludePath; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Can be used to check if the autoloader uses the include path to check |
||||
|
* for classes. |
||||
|
* |
||||
|
* @return bool |
||||
|
*/ |
||||
|
public function getUseIncludePath() |
||||
|
{ |
||||
|
return $this->useIncludePath; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Turns off searching the prefix and fallback directories for classes |
||||
|
* that have not been registered with the class map. |
||||
|
* |
||||
|
* @param bool $classMapAuthoritative |
||||
|
*/ |
||||
|
public function setClassMapAuthoritative($classMapAuthoritative) |
||||
|
{ |
||||
|
$this->classMapAuthoritative = $classMapAuthoritative; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Should class lookup fail if not found in the current class map? |
||||
|
* |
||||
|
* @return bool |
||||
|
*/ |
||||
|
public function isClassMapAuthoritative() |
||||
|
{ |
||||
|
return $this->classMapAuthoritative; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* APCu prefix to use to cache found/not-found classes, if the extension is enabled. |
||||
|
* |
||||
|
* @param string|null $apcuPrefix |
||||
|
*/ |
||||
|
public function setApcuPrefix($apcuPrefix) |
||||
|
{ |
||||
|
$this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* The APCu prefix in use, or null if APCu caching is not enabled. |
||||
|
* |
||||
|
* @return string|null |
||||
|
*/ |
||||
|
public function getApcuPrefix() |
||||
|
{ |
||||
|
return $this->apcuPrefix; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Registers this instance as an autoloader. |
||||
|
* |
||||
|
* @param bool $prepend Whether to prepend the autoloader or not |
||||
|
*/ |
||||
|
public function register($prepend = false) |
||||
|
{ |
||||
|
spl_autoload_register(array($this, 'loadClass'), true, $prepend); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Unregisters this instance as an autoloader. |
||||
|
*/ |
||||
|
public function unregister() |
||||
|
{ |
||||
|
spl_autoload_unregister(array($this, 'loadClass')); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Loads the given class or interface. |
||||
|
* |
||||
|
* @param string $class The name of the class |
||||
|
* @return bool|null True if loaded, null otherwise |
||||
|
*/ |
||||
|
public function loadClass($class) |
||||
|
{ |
||||
|
if ($file = $this->findFile($class)) { |
||||
|
includeFile($file); |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Finds the path to the file where the class is defined. |
||||
|
* |
||||
|
* @param string $class The name of the class |
||||
|
* |
||||
|
* @return string|false The path if found, false otherwise |
||||
|
*/ |
||||
|
public function findFile($class) |
||||
|
{ |
||||
|
// class map lookup |
||||
|
if (isset($this->classMap[$class])) { |
||||
|
return $this->classMap[$class]; |
||||
|
} |
||||
|
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { |
||||
|
return false; |
||||
|
} |
||||
|
if (null !== $this->apcuPrefix) { |
||||
|
$file = apcu_fetch($this->apcuPrefix.$class, $hit); |
||||
|
if ($hit) { |
||||
|
return $file; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
$file = $this->findFileWithExtension($class, '.php'); |
||||
|
|
||||
|
// Search for Hack files if we are running on HHVM |
||||
|
if (false === $file && defined('HHVM_VERSION')) { |
||||
|
$file = $this->findFileWithExtension($class, '.hh'); |
||||
|
} |
||||
|
|
||||
|
if (null !== $this->apcuPrefix) { |
||||
|
apcu_add($this->apcuPrefix.$class, $file); |
||||
|
} |
||||
|
|
||||
|
if (false === $file) { |
||||
|
// Remember that this class does not exist. |
||||
|
$this->missingClasses[$class] = true; |
||||
|
} |
||||
|
|
||||
|
return $file; |
||||
|
} |
||||
|
|
||||
|
private function findFileWithExtension($class, $ext) |
||||
|
{ |
||||
|
// PSR-4 lookup |
||||
|
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; |
||||
|
|
||||
|
$first = $class[0]; |
||||
|
if (isset($this->prefixLengthsPsr4[$first])) { |
||||
|
$subPath = $class; |
||||
|
while (false !== $lastPos = strrpos($subPath, '\\')) { |
||||
|
$subPath = substr($subPath, 0, $lastPos); |
||||
|
$search = $subPath.'\\'; |
||||
|
if (isset($this->prefixDirsPsr4[$search])) { |
||||
|
foreach ($this->prefixDirsPsr4[$search] as $dir) { |
||||
|
$length = $this->prefixLengthsPsr4[$first][$search]; |
||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { |
||||
|
return $file; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// PSR-4 fallback dirs |
||||
|
foreach ($this->fallbackDirsPsr4 as $dir) { |
||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { |
||||
|
return $file; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// PSR-0 lookup |
||||
|
if (false !== $pos = strrpos($class, '\\')) { |
||||
|
// namespaced class name |
||||
|
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) |
||||
|
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); |
||||
|
} else { |
||||
|
// PEAR-like class name |
||||
|
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; |
||||
|
} |
||||
|
|
||||
|
if (isset($this->prefixesPsr0[$first])) { |
||||
|
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { |
||||
|
if (0 === strpos($class, $prefix)) { |
||||
|
foreach ($dirs as $dir) { |
||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { |
||||
|
return $file; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// PSR-0 fallback dirs |
||||
|
foreach ($this->fallbackDirsPsr0 as $dir) { |
||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { |
||||
|
return $file; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// PSR-0 include paths. |
||||
|
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { |
||||
|
return $file; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Scope isolated include. |
||||
|
* |
||||
|
* Prevents access to $this/self from included files. |
||||
|
*/ |
||||
|
function includeFile($file) |
||||
|
{ |
||||
|
include $file; |
||||
|
} |
@ -0,0 +1,21 @@ |
|||||
|
|
||||
|
Copyright (c) Nils Adermann, Jordi Boggiano |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
of this software and associated documentation files (the "Software"), to deal |
||||
|
in the Software without restriction, including without limitation the rights |
||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
copies of the Software, and to permit persons to whom the Software is furnished |
||||
|
to do so, subject to the following conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be included in all |
||||
|
copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||
|
THE SOFTWARE. |
||||
|
|
File diff suppressed because it is too large
@ -0,0 +1,19 @@ |
|||||
|
<?php |
||||
|
|
||||
|
// autoload_files.php @generated by Composer |
||||
|
|
||||
|
$vendorDir = dirname(dirname(__FILE__)); |
||||
|
$baseDir = dirname($vendorDir); |
||||
|
|
||||
|
return array( |
||||
|
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', |
||||
|
'5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php', |
||||
|
'023d27dca8066ef29e6739335ea73bad' => $vendorDir . '/symfony/polyfill-php70/bootstrap.php', |
||||
|
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php', |
||||
|
'2c102faa651ef8ea5874edb585946bce' => $vendorDir . '/swiftmailer/swiftmailer/lib/swift_required.php', |
||||
|
'e7223560d890eab89cda23685e711e2c' => $vendorDir . '/psy/psysh/src/Psy/functions.php', |
||||
|
'6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php', |
||||
|
'f0906e6318348a765ffb6eb24e0d0938' => $vendorDir . '/laravel/framework/src/Illuminate/Foundation/helpers.php', |
||||
|
'58571171fd5812e6e447dce228f52f4d' => $vendorDir . '/laravel/framework/src/Illuminate/Support/helpers.php', |
||||
|
'f18cc91337d49233e5754e93f3ed9ec3' => $vendorDir . '/laravelcollective/html/src/helpers.php', |
||||
|
); |
@ -0,0 +1,15 @@ |
|||||
|
<?php |
||||
|
|
||||
|
// autoload_namespaces.php @generated by Composer |
||||
|
|
||||
|
$vendorDir = dirname(dirname(__FILE__)); |
||||
|
$baseDir = dirname($vendorDir); |
||||
|
|
||||
|
return array( |
||||
|
'Prophecy\\' => array($vendorDir . '/phpspec/prophecy/src'), |
||||
|
'Parsedown' => array($vendorDir . '/erusev/parsedown'), |
||||
|
'Mockery' => array($vendorDir . '/mockery/mockery/library'), |
||||
|
'JakubOnderka\\PhpConsoleHighlighter' => array($vendorDir . '/jakub-onderka/php-console-highlighter/src'), |
||||
|
'JakubOnderka\\PhpConsoleColor' => array($vendorDir . '/jakub-onderka/php-console-color/src'), |
||||
|
'Doctrine\\Common\\Lexer\\' => array($vendorDir . '/doctrine/lexer/lib'), |
||||
|
); |
@ -0,0 +1,50 @@ |
|||||
|
<?php |
||||
|
|
||||
|
// autoload_psr4.php @generated by Composer |
||||
|
|
||||
|
$vendorDir = dirname(dirname(__FILE__)); |
||||
|
$baseDir = dirname($vendorDir); |
||||
|
|
||||
|
return array( |
||||
|
'phpDocumentor\\Reflection\\' => array($vendorDir . '/phpdocumentor/reflection-common/src', $vendorDir . '/phpdocumentor/type-resolver/src', $vendorDir . '/phpdocumentor/reflection-docblock/src'), |
||||
|
'XdgBaseDir\\' => array($vendorDir . '/dnoegel/php-xdg-base-dir/src'), |
||||
|
'Whoops\\' => array($vendorDir . '/filp/whoops/src/Whoops'), |
||||
|
'Webmozart\\Assert\\' => array($vendorDir . '/webmozart/assert/src'), |
||||
|
'TijsVerkoyen\\CssToInlineStyles\\' => array($vendorDir . '/tijsverkoyen/css-to-inline-styles/src'), |
||||
|
'Tests\\' => array($baseDir . '/tests'), |
||||
|
'Symfony\\Thanks\\' => array($vendorDir . '/symfony/thanks/src'), |
||||
|
'Symfony\\Polyfill\\Php70\\' => array($vendorDir . '/symfony/polyfill-php70'), |
||||
|
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'), |
||||
|
'Symfony\\Component\\VarDumper\\' => array($vendorDir . '/symfony/var-dumper'), |
||||
|
'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'), |
||||
|
'Symfony\\Component\\Routing\\' => array($vendorDir . '/symfony/routing'), |
||||
|
'Symfony\\Component\\Process\\' => array($vendorDir . '/symfony/process'), |
||||
|
'Symfony\\Component\\HttpKernel\\' => array($vendorDir . '/symfony/http-kernel'), |
||||
|
'Symfony\\Component\\HttpFoundation\\' => array($vendorDir . '/symfony/http-foundation'), |
||||
|
'Symfony\\Component\\Finder\\' => array($vendorDir . '/symfony/finder'), |
||||
|
'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'), |
||||
|
'Symfony\\Component\\Debug\\' => array($vendorDir . '/symfony/debug'), |
||||
|
'Symfony\\Component\\CssSelector\\' => array($vendorDir . '/symfony/css-selector'), |
||||
|
'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'), |
||||
|
'Ramsey\\Uuid\\' => array($vendorDir . '/ramsey/uuid/src'), |
||||
|
'Psy\\' => array($vendorDir . '/psy/psysh/src/Psy'), |
||||
|
'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'), |
||||
|
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), |
||||
|
'Psr\\Container\\' => array($vendorDir . '/psr/container/src'), |
||||
|
'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'), |
||||
|
'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'), |
||||
|
'League\\Flysystem\\' => array($vendorDir . '/league/flysystem/src'), |
||||
|
'Laravel\\Tinker\\' => array($vendorDir . '/laravel/tinker/src'), |
||||
|
'Illuminate\\' => array($vendorDir . '/laravel/framework/src/Illuminate'), |
||||
|
'Fideloper\\Proxy\\' => array($vendorDir . '/fideloper/proxy/src'), |
||||
|
'Faker\\' => array($vendorDir . '/fzaninotto/faker/src/Faker'), |
||||
|
'Egulias\\EmailValidator\\' => array($vendorDir . '/egulias/email-validator/EmailValidator'), |
||||
|
'Dotenv\\' => array($vendorDir . '/vlucas/phpdotenv/src'), |
||||
|
'Doctrine\\Instantiator\\' => array($vendorDir . '/doctrine/instantiator/src/Doctrine/Instantiator'), |
||||
|
'Doctrine\\Common\\Inflector\\' => array($vendorDir . '/doctrine/inflector/lib/Doctrine/Common/Inflector'), |
||||
|
'DeepCopy\\' => array($vendorDir . '/myclabs/deep-copy/src/DeepCopy'), |
||||
|
'Cron\\' => array($vendorDir . '/mtdowling/cron-expression/src/Cron'), |
||||
|
'Collective\\Html\\' => array($vendorDir . '/laravelcollective/html/src'), |
||||
|
'Carbon\\' => array($vendorDir . '/nesbot/carbon/src/Carbon'), |
||||
|
'App\\' => array($baseDir . '/app'), |
||||
|
); |
@ -0,0 +1,70 @@ |
|||||
|
<?php |
||||
|
|
||||
|
// autoload_real.php @generated by Composer |
||||
|
|
||||
|
class ComposerAutoloaderInit4b6fb9210a1ea37c2db27b8ff53a1ecf |
||||
|
{ |
||||
|
private static $loader; |
||||
|
|
||||
|
public static function loadClassLoader($class) |
||||
|
{ |
||||
|
if ('Composer\Autoload\ClassLoader' === $class) { |
||||
|
require __DIR__ . '/ClassLoader.php'; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static function getLoader() |
||||
|
{ |
||||
|
if (null !== self::$loader) { |
||||
|
return self::$loader; |
||||
|
} |
||||
|
|
||||
|
spl_autoload_register(array('ComposerAutoloaderInit4b6fb9210a1ea37c2db27b8ff53a1ecf', 'loadClassLoader'), true, true); |
||||
|
self::$loader = $loader = new \Composer\Autoload\ClassLoader(); |
||||
|
spl_autoload_unregister(array('ComposerAutoloaderInit4b6fb9210a1ea37c2db27b8ff53a1ecf', 'loadClassLoader')); |
||||
|
|
||||
|
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); |
||||
|
if ($useStaticLoader) { |
||||
|
require_once __DIR__ . '/autoload_static.php'; |
||||
|
|
||||
|
call_user_func(\Composer\Autoload\ComposerStaticInit4b6fb9210a1ea37c2db27b8ff53a1ecf::getInitializer($loader)); |
||||
|
} else { |
||||
|
$map = require __DIR__ . '/autoload_namespaces.php'; |
||||
|
foreach ($map as $namespace => $path) { |
||||
|
$loader->set($namespace, $path); |
||||
|
} |
||||
|
|
||||
|
$map = require __DIR__ . '/autoload_psr4.php'; |
||||
|
foreach ($map as $namespace => $path) { |
||||
|
$loader->setPsr4($namespace, $path); |
||||
|
} |
||||
|
|
||||
|
$classMap = require __DIR__ . '/autoload_classmap.php'; |
||||
|
if ($classMap) { |
||||
|
$loader->addClassMap($classMap); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
$loader->register(true); |
||||
|
|
||||
|
if ($useStaticLoader) { |
||||
|
$includeFiles = Composer\Autoload\ComposerStaticInit4b6fb9210a1ea37c2db27b8ff53a1ecf::$files; |
||||
|
} else { |
||||
|
$includeFiles = require __DIR__ . '/autoload_files.php'; |
||||
|
} |
||||
|
foreach ($includeFiles as $fileIdentifier => $file) { |
||||
|
composerRequire4b6fb9210a1ea37c2db27b8ff53a1ecf($fileIdentifier, $file); |
||||
|
} |
||||
|
|
||||
|
return $loader; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function composerRequire4b6fb9210a1ea37c2db27b8ff53a1ecf($fileIdentifier, $file) |
||||
|
{ |
||||
|
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { |
||||
|
require $file; |
||||
|
|
||||
|
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; |
||||
|
} |
||||
|
} |
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1 @@ |
|||||
|
/vendor/ |
@ -0,0 +1,19 @@ |
|||||
|
Copyright (c) 2014 Daniel Nögel |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
of this software and associated documentation files (the "Software"), to deal |
||||
|
in the Software without restriction, including without limitation the rights |
||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
copies of the Software, and to permit persons to whom the Software is |
||||
|
furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be included in |
||||
|
all copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||
|
THE SOFTWARE. |
@ -0,0 +1,38 @@ |
|||||
|
# XDG Base Directory |
||||
|
|
||||
|
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md) |
||||
|
|
||||
|
Implementation of XDG Base Directory specification for php |
||||
|
|
||||
|
## Install |
||||
|
|
||||
|
Via Composer |
||||
|
|
||||
|
``` bash |
||||
|
$ composer require dnoegel/php-xdg-base-dir |
||||
|
``` |
||||
|
|
||||
|
## Usage |
||||
|
|
||||
|
``` php |
||||
|
$xdg = \XdgBaseDir\Xdg(); |
||||
|
|
||||
|
echo $xdg->getHomeDir(); |
||||
|
echo $xdg->getHomeConfigDir() |
||||
|
echo $xdg->getHomeDataDir() |
||||
|
echo $xdg->getHomeCacheDir() |
||||
|
echo $xdg->getRuntimeDir() |
||||
|
|
||||
|
$xdg->getDataDirs() // returns array |
||||
|
$xdg->getConfigDirs() // returns array |
||||
|
``` |
||||
|
|
||||
|
## Testing |
||||
|
|
||||
|
``` bash |
||||
|
$ phpunit |
||||
|
``` |
||||
|
|
||||
|
## License |
||||
|
|
||||
|
The MIT License (MIT). Please see [License File](https://github.com/dnoegel/php-xdg-base-dir/blob/master/LICENSE) for more information. |
@ -0,0 +1,17 @@ |
|||||
|
{ |
||||
|
"name": "dnoegel/php-xdg-base-dir", |
||||
|
"description": "implementation of xdg base directory specification for php", |
||||
|
"type": "project", |
||||
|
"license": "MIT", |
||||
|
"require": { |
||||
|
"php": ">=5.3.2" |
||||
|
}, |
||||
|
"require-dev": { |
||||
|
"phpunit/phpunit": "@stable" |
||||
|
}, |
||||
|
"autoload": { |
||||
|
"psr-4": { |
||||
|
"XdgBaseDir\\": "src/" |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,24 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
|
||||
|
<phpunit colors="true" |
||||
|
convertErrorsToExceptions="true" |
||||
|
convertNoticesToExceptions="true" |
||||
|
convertWarningsToExceptions="true" |
||||
|
processIsolation="false" |
||||
|
stopOnFailure="false" |
||||
|
syntaxCheck="false" |
||||
|
bootstrap="vendor/autoload.php" |
||||
|
> |
||||
|
|
||||
|
<testsuites> |
||||
|
<testsuite name="php-xdg-base-dir unit tests"> |
||||
|
<directory>./tests/</directory> |
||||
|
</testsuite> |
||||
|
</testsuites> |
||||
|
|
||||
|
<filter> |
||||
|
<whitelist> |
||||
|
<directory>./src/</directory> |
||||
|
</whitelist> |
||||
|
</filter> |
||||
|
</phpunit> |
@ -0,0 +1,121 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace XdgBaseDir; |
||||
|
|
||||
|
/** |
||||
|
* Simple implementation of the XDG standard http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html |
||||
|
* |
||||
|
* Based on the python implementation https://github.com/takluyver/pyxdg/blob/master/xdg/BaseDirectory.py |
||||
|
* |
||||
|
* Class Xdg |
||||
|
* @package ShopwareCli\Application |
||||
|
*/ |
||||
|
class Xdg |
||||
|
{ |
||||
|
const S_IFDIR = 040000; // directory |
||||
|
const S_IRWXO = 00007; // rwx other |
||||
|
const S_IRWXG = 00056; // rwx group |
||||
|
const RUNTIME_DIR_FALLBACK = 'php-xdg-runtime-dir-fallback-'; |
||||
|
|
||||
|
/** |
||||
|
* @return string |
||||
|
*/ |
||||
|
public function getHomeDir() |
||||
|
{ |
||||
|
return getenv('HOME') ?: (getenv('HOMEDRIVE') . DIRECTORY_SEPARATOR . getenv('HOMEPATH')); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @return string |
||||
|
*/ |
||||
|
public function getHomeConfigDir() |
||||
|
{ |
||||
|
$path = getenv('XDG_CONFIG_HOME') ?: $this->getHomeDir() . DIRECTORY_SEPARATOR . '.config'; |
||||
|
|
||||
|
return $path; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @return string |
||||
|
*/ |
||||
|
public function getHomeDataDir() |
||||
|
{ |
||||
|
$path = getenv('XDG_DATA_HOME') ?: $this->getHomeDir() . DIRECTORY_SEPARATOR . '.local' . DIRECTORY_SEPARATOR . 'share'; |
||||
|
|
||||
|
return $path; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @return array |
||||
|
*/ |
||||
|
public function getConfigDirs() |
||||
|
{ |
||||
|
$configDirs = getenv('XDG_CONFIG_DIRS') ? explode(':', getenv('XDG_CONFIG_DIRS')) : array('/etc/xdg'); |
||||
|
|
||||
|
$paths = array_merge(array($this->getHomeConfigDir()), $configDirs); |
||||
|
|
||||
|
return $paths; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @return array |
||||
|
*/ |
||||
|
public function getDataDirs() |
||||
|
{ |
||||
|
$dataDirs = getenv('XDG_DATA_DIRS') ? explode(':', getenv('XDG_DATA_DIRS')) : array('/usr/local/share', '/usr/share'); |
||||
|
|
||||
|
$paths = array_merge(array($this->getHomeDataDir()), $dataDirs); |
||||
|
|
||||
|
return $paths; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @return string |
||||
|
*/ |
||||
|
public function getHomeCacheDir() |
||||
|
{ |
||||
|
$path = getenv('XDG_CACHE_HOME') ?: $this->getHomeDir() . DIRECTORY_SEPARATOR . '.cache'; |
||||
|
|
||||
|
return $path; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public function getRuntimeDir($strict=true) |
||||
|
{ |
||||
|
if ($runtimeDir = getenv('XDG_RUNTIME_DIR')) { |
||||
|
return $runtimeDir; |
||||
|
} |
||||
|
|
||||
|
if ($strict) { |
||||
|
throw new \RuntimeException('XDG_RUNTIME_DIR was not set'); |
||||
|
} |
||||
|
|
||||
|
$fallback = sys_get_temp_dir() . DIRECTORY_SEPARATOR . self::RUNTIME_DIR_FALLBACK . getenv('USER'); |
||||
|
|
||||
|
$create = false; |
||||
|
|
||||
|
if (!is_dir($fallback)) { |
||||
|
mkdir($fallback, 0700, true); |
||||
|
} |
||||
|
|
||||
|
$st = lstat($fallback); |
||||
|
|
||||
|
# The fallback must be a directory |
||||
|
if (!$st['mode'] & self::S_IFDIR) { |
||||
|
rmdir($fallback); |
||||
|
$create = true; |
||||
|
} elseif ($st['uid'] != getmyuid() || |
||||
|
$st['mode'] & (self::S_IRWXG | self::S_IRWXO) |
||||
|
) { |
||||
|
rmdir($fallback); |
||||
|
$create = true; |
||||
|
} |
||||
|
|
||||
|
if ($create) { |
||||
|
mkdir($fallback, 0700, true); |
||||
|
} |
||||
|
|
||||
|
return $fallback; |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,116 @@ |
|||||
|
<?php |
||||
|
|
||||
|
class XdgTest extends PHPUnit_Framework_TestCase |
||||
|
{ |
||||
|
/** |
||||
|
* @return \XdgBaseDir\Xdg |
||||
|
*/ |
||||
|
public function getXdg() |
||||
|
{ |
||||
|
return new \XdgBaseDir\Xdg(); |
||||
|
} |
||||
|
|
||||
|
public function testGetHomeDir() |
||||
|
{ |
||||
|
putenv('HOME=/fake-dir'); |
||||
|
$this->assertEquals('/fake-dir', $this->getXdg()->getHomeDir()); |
||||
|
} |
||||
|
|
||||
|
public function testGetFallbackHomeDir() |
||||
|
{ |
||||
|
putenv('HOME='); |
||||
|
putenv('HOMEDRIVE=C:'); |
||||
|
putenv('HOMEPATH=fake-dir'); |
||||
|
$this->assertEquals('C:/fake-dir', $this->getXdg()->getHomeDir()); |
||||
|
} |
||||
|
|
||||
|
public function testXdgPutCache() |
||||
|
{ |
||||
|
putenv('XDG_DATA_HOME=tmp/'); |
||||
|
putenv('XDG_CONFIG_HOME=tmp/'); |
||||
|
putenv('XDG_CACHE_HOME=tmp/'); |
||||
|
$this->assertEquals('tmp/', $this->getXdg()->getHomeCacheDir()); |
||||
|
} |
||||
|
|
||||
|
public function testXdgPutData() |
||||
|
{ |
||||
|
putenv('XDG_DATA_HOME=tmp/'); |
||||
|
$this->assertEquals('tmp/', $this->getXdg()->getHomeDataDir()); |
||||
|
} |
||||
|
|
||||
|
public function testXdgPutConfig() |
||||
|
{ |
||||
|
putenv('XDG_CONFIG_HOME=tmp/'); |
||||
|
$this->assertEquals('tmp/', $this->getXdg()->getHomeConfigDir()); |
||||
|
} |
||||
|
|
||||
|
public function testXdgDataDirsShouldIncludeHomeDataDir() |
||||
|
{ |
||||
|
putenv('XDG_DATA_HOME=tmp/'); |
||||
|
putenv('XDG_CONFIG_HOME=tmp/'); |
||||
|
|
||||
|
$this->assertArrayHasKey('tmp/', array_flip($this->getXdg()->getDataDirs())); |
||||
|
} |
||||
|
|
||||
|
public function testXdgConfigDirsShouldIncludeHomeConfigDir() |
||||
|
{ |
||||
|
putenv('XDG_CONFIG_HOME=tmp/'); |
||||
|
|
||||
|
$this->assertArrayHasKey('tmp/', array_flip($this->getXdg()->getConfigDirs())); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* If XDG_RUNTIME_DIR is set, it should be returned |
||||
|
*/ |
||||
|
public function testGetRuntimeDir() |
||||
|
{ |
||||
|
putenv('XDG_RUNTIME_DIR=/tmp/'); |
||||
|
$runtimeDir = $this->getXdg()->getRuntimeDir(); |
||||
|
|
||||
|
$this->assertEquals(is_dir($runtimeDir), true); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* In strict mode, an exception should be shown if XDG_RUNTIME_DIR does not exist |
||||
|
* |
||||
|
* @expectedException \RuntimeException |
||||
|
*/ |
||||
|
public function testGetRuntimeDirShouldThrowException() |
||||
|
{ |
||||
|
putenv('XDG_RUNTIME_DIR='); |
||||
|
$this->getXdg()->getRuntimeDir(true); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* In fallback mode a directory should be created |
||||
|
*/ |
||||
|
public function testGetRuntimeDirShouldCreateDirectory() |
||||
|
{ |
||||
|
putenv('XDG_RUNTIME_DIR='); |
||||
|
$dir = $this->getXdg()->getRuntimeDir(false); |
||||
|
$permission = decoct(fileperms($dir) & 0777); |
||||
|
$this->assertEquals(700, $permission); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Ensure, that the fallback directories are created with correct permission |
||||
|
*/ |
||||
|
public function testGetRuntimeShouldDeleteDirsWithWrongPermission() |
||||
|
{ |
||||
|
$runtimeDir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . XdgBaseDir\Xdg::RUNTIME_DIR_FALLBACK . getenv('USER'); |
||||
|
|
||||
|
rmdir($runtimeDir); |
||||
|
mkdir($runtimeDir, 0764, true); |
||||
|
|
||||
|
// Permission should be wrong now |
||||
|
$permission = decoct(fileperms($runtimeDir) & 0777); |
||||
|
$this->assertEquals(764, $permission); |
||||
|
|
||||
|
putenv('XDG_RUNTIME_DIR='); |
||||
|
$dir = $this->getXdg()->getRuntimeDir(false); |
||||
|
|
||||
|
// Permission should be fixed |
||||
|
$permission = decoct(fileperms($dir) & 0777); |
||||
|
$this->assertEquals(700, $permission); |
||||
|
} |
||||
|
} |
@ -0,0 +1,19 @@ |
|||||
|
Copyright (c) 2006-2015 Doctrine Project |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of |
||||
|
this software and associated documentation files (the "Software"), to deal in |
||||
|
the Software without restriction, including without limitation the rights to |
||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies |
||||
|
of the Software, and to permit persons to whom the Software is furnished to do |
||||
|
so, subject to the following conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be included in all |
||||
|
copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
|
SOFTWARE. |
@ -0,0 +1,6 @@ |
|||||
|
# Doctrine Inflector |
||||
|
|
||||
|
Doctrine Inflector is a small library that can perform string manipulations |
||||
|
with regard to upper-/lowercase and singular/plural forms of words. |
||||
|
|
||||
|
[![Build Status](https://travis-ci.org/doctrine/inflector.svg?branch=master)](https://travis-ci.org/doctrine/inflector) |
@ -0,0 +1,32 @@ |
|||||
|
{ |
||||
|
"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", |
||||
|
"license": "MIT", |
||||
|
"authors": [ |
||||
|
{"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, |
||||
|
{"name": "Roman Borschel", "email": "roman@code-factory.org"}, |
||||
|
{"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"}, |
||||
|
{"name": "Jonathan Wage", "email": "jonwage@gmail.com"}, |
||||
|
{"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"} |
||||
|
], |
||||
|
"require": { |
||||
|
"php": "^7.1" |
||||
|
}, |
||||
|
"require-dev": { |
||||
|
"phpunit/phpunit": "^6.2" |
||||
|
}, |
||||
|
"autoload": { |
||||
|
"psr-4": { "Doctrine\\Common\\Inflector\\": "lib/Doctrine/Common/Inflector" } |
||||
|
}, |
||||
|
"autoload-dev": { |
||||
|
"psr-4": { "Doctrine\\Tests\\Common\\Inflector\\": "tests/Doctrine/Tests/Common/Inflector" } |
||||
|
}, |
||||
|
"extra": { |
||||
|
"branch-alias": { |
||||
|
"dev-master": "1.3.x-dev" |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,490 @@ |
|||||
|
<?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\Inflector; |
||||
|
|
||||
|
/** |
||||
|
* 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> |
||||
|
*/ |
||||
|
class Inflector |
||||
|
{ |
||||
|
/** |
||||
|
* Plural inflector rules. |
||||
|
* |
||||
|
* @var string[][] |
||||
|
*/ |
||||
|
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', |
||||
|
'/(c)riterion$/i' => '\1riteria', |
||||
|
'/(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', |
||||
|
'goose' => 'geese', |
||||
|
'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', |
||||
|
'valve' => 'valves', |
||||
|
'volcano' => 'volcanoes', |
||||
|
) |
||||
|
); |
||||
|
|
||||
|
/** |
||||
|
* Singular inflector rules. |
||||
|
* |
||||
|
* @var string[][] |
||||
|
*/ |
||||
|
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', |
||||
|
'/(olive)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', |
||||
|
'/(c)riteria$/i' => '\1riterion', |
||||
|
'/([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', |
||||
|
'data', |
||||
|
'police', |
||||
|
'pants', |
||||
|
'clothes', |
||||
|
), |
||||
|
'irregular' => array( |
||||
|
'abuses' => 'abuse', |
||||
|
'avalanches' => 'avalanche', |
||||
|
'caches' => 'cache', |
||||
|
'criteria' => 'criterion', |
||||
|
'curves' => 'curve', |
||||
|
'emphases' => 'emphasis', |
||||
|
'foes' => 'foe', |
||||
|
'geese' => 'goose', |
||||
|
'graves' => 'grave', |
||||
|
'hoaxes' => 'hoax', |
||||
|
'media' => 'medium', |
||||
|
'neuroses' => 'neurosis', |
||||
|
'waves' => 'wave', |
||||
|
'oases' => 'oasis', |
||||
|
'valves' => 'valve', |
||||
|
) |
||||
|
); |
||||
|
|
||||
|
/** |
||||
|
* Words that should not be inflected. |
||||
|
* |
||||
|
* @var array |
||||
|
*/ |
||||
|
private static $uninflected = array( |
||||
|
'.*?media', 'Amoyese', 'audio', 'bison', 'Borghese', 'bream', 'breeches', |
||||
|
'britches', 'buffalo', 'cantus', 'carp', 'chassis', 'clippers', 'cod', 'coitus', 'compensation', 'Congoese', |
||||
|
'contretemps', 'coreopsis', 'corps', 'data', 'debris', 'deer', 'diabetes', 'djinn', 'education', 'eland', |
||||
|
'elk', 'emoji', 'equipment', 'evidence', 'Faroese', 'feedback', 'fish', 'flounder', 'Foochowese', |
||||
|
'Furniture', 'furniture', 'gallows', 'Genevese', 'Genoese', 'Gilbertese', 'gold', |
||||
|
'headquarters', 'herpes', 'hijinks', 'Hottentotese', 'information', 'innings', 'jackanapes', 'jedi', |
||||
|
'Kiplingese', 'knowledge', 'Kongoese', 'love', 'Lucchese', 'Luggage', 'mackerel', 'Maltese', 'metadata', |
||||
|
'mews', 'moose', 'mumps', 'Nankingese', 'news', 'nexus', 'Niasese', 'nutrition', 'offspring', |
||||
|
'Pekingese', 'Piedmontese', 'pincers', 'Pistoiese', 'plankton', 'pliers', 'pokemon', 'police', 'Portuguese', |
||||
|
'proceedings', 'rabies', 'rain', 'rhinoceros', 'rice', 'salmon', 'Sarawakese', 'scissors', 'sea[- ]bass', |
||||
|
'series', 'Shavese', 'shears', 'sheep', 'siemens', 'species', 'staff', 'swine', 'traffic', |
||||
|
'trousers', 'trout', 'tuna', 'us', 'Vermontese', 'Wenchowese', 'wheat', 'whiting', 'wildebeest', 'Yengeese' |
||||
|
); |
||||
|
|
||||
|
/** |
||||
|
* Method cache array. |
||||
|
* |
||||
|
* @var array |
||||
|
*/ |
||||
|
private static $cache = array(); |
||||
|
|
||||
|
/** |
||||
|
* The initial state of Inflector so reset() works. |
||||
|
* |
||||
|
* @var array |
||||
|
*/ |
||||
|
private static $initialState = array(); |
||||
|
|
||||
|
/** |
||||
|
* Converts a word into the format for a Doctrine table name. Converts 'ModelName' to 'model_name'. |
||||
|
*/ |
||||
|
public static function tableize(string $word) : string |
||||
|
{ |
||||
|
return strtolower(preg_replace('~(?<=\\w)([A-Z])~', '_$1', $word)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Converts a word into the format for a Doctrine class name. Converts 'table_name' to 'TableName'. |
||||
|
*/ |
||||
|
public static function classify(string $word) : string |
||||
|
{ |
||||
|
return str_replace([' ', '_', '-'], '', ucwords($word, ' _-')); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Camelizes a word. This uses the classify() method and turns the first character to lowercase. |
||||
|
*/ |
||||
|
public static function camelize(string $word) : string |
||||
|
{ |
||||
|
return lcfirst(self::classify($word)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Uppercases words with configurable delimeters between words. |
||||
|
* |
||||
|
* Takes a string and capitalizes all of the words, like PHP's built-in |
||||
|
* ucwords function. This extends that behavior, however, by allowing the |
||||
|
* word delimeters to be configured, rather than only separating on |
||||
|
* whitespace. |
||||
|
* |
||||
|
* Here is an example: |
||||
|
* <code> |
||||
|
* <?php |
||||
|
* $string = 'top-o-the-morning to all_of_you!'; |
||||
|
* echo \Doctrine\Common\Inflector\Inflector::ucwords($string); |
||||
|
* // Top-O-The-Morning To All_of_you! |
||||
|
* |
||||
|
* echo \Doctrine\Common\Inflector\Inflector::ucwords($string, '-_ '); |
||||
|
* // Top-O-The-Morning To All_Of_You! |
||||
|
* ?> |
||||
|
* </code> |
||||
|
* |
||||
|
* @param string $string The string to operate on. |
||||
|
* @param string $delimiters A list of word separators. |
||||
|
* |
||||
|
* @return string The string with all delimeter-separated words capitalized. |
||||
|
*/ |
||||
|
public static function ucwords(string $string, string $delimiters = " \n\t\r\0\x0B-") : string |
||||
|
{ |
||||
|
return ucwords($string, $delimiters); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Clears Inflectors inflected value caches, and resets the inflection |
||||
|
* rules to the initial values. |
||||
|
*/ |
||||
|
public static function reset() : void |
||||
|
{ |
||||
|
if (empty(self::$initialState)) { |
||||
|
self::$initialState = get_class_vars('Inflector'); |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
foreach (self::$initialState as $key => $val) { |
||||
|
if ($key !== 'initialState') { |
||||
|
self::${$key} = $val; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Adds custom inflection $rules, of either 'plural' or 'singular' $type. |
||||
|
* |
||||
|
* ### Usage: |
||||
|
* |
||||
|
* {{{ |
||||
|
* Inflector::rules('plural', array('/^(inflect)or$/i' => '\1ables')); |
||||
|
* Inflector::rules('plural', array( |
||||
|
* 'rules' => array('/^(inflect)ors$/i' => '\1ables'), |
||||
|
* 'uninflected' => array('dontinflectme'), |
||||
|
* 'irregular' => array('red' => 'redlings') |
||||
|
* )); |
||||
|
* }}} |
||||
|
* |
||||
|
* @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 |
||||
|
*/ |
||||
|
public static function rules(string $type, iterable $rules, bool $reset = false) : void |
||||
|
{ |
||||
|
foreach ($rules as $rule => $pattern) { |
||||
|
if ( ! is_array($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(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
self::${$type}['rules'] = $rules + self::${$type}['rules']; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns a word in plural form. |
||||
|
* |
||||
|
* @param string $word The word in singular form. |
||||
|
* |
||||
|
* @return string The word in plural form. |
||||
|
*/ |
||||
|
public static function pluralize(string $word) : string |
||||
|
{ |
||||
|
if (isset(self::$cache['pluralize'][$word])) { |
||||
|
return self::$cache['pluralize'][$word]; |
||||
|
} |
||||
|
|
||||
|
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] . $word[0] . 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]; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns a word in singular form. |
||||
|
* |
||||
|
* @param string $word The word in plural form. |
||||
|
* |
||||
|
* @return string The word in singular form. |
||||
|
*/ |
||||
|
public static function singularize(string $word) : string |
||||
|
{ |
||||
|
if (isset(self::$cache['singularize'][$word])) { |
||||
|
return self::$cache['singularize'][$word]; |
||||
|
} |
||||
|
|
||||
|
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'] = '(?:' . implode('|', self::$singular['merged']['uninflected']) . ')'; |
||||
|
self::$singular['cacheIrregular'] = '(?:' . implode('|', array_keys(self::$singular['merged']['irregular'])) . ')'; |
||||
|
} |
||||
|
|
||||
|
if (preg_match('/(.*)\\b(' . self::$singular['cacheIrregular'] . ')$/i', $word, $regs)) { |
||||
|
self::$cache['singularize'][$word] = $regs[1] . $word[0] . 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; |
||||
|
} |
||||
|
} |
@ -0,0 +1,35 @@ |
|||||
|
# Contributing |
||||
|
|
||||
|
* Coding standard for the project is [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) |
||||
|
* The project will follow strict [object calisthenics](http://www.slideshare.net/guilhermeblanco/object-calisthenics-applied-to-php) |
||||
|
* Any contribution must provide tests for additional introduced conditions |
||||
|
* Any un-confirmed issue needs a failing test case before being accepted |
||||
|
* Pull requests must be sent from a new hotfix/feature branch, not from `master`. |
||||
|
|
||||
|
## Installation |
||||
|
|
||||
|
To install the project and run the tests, you need to clone it first: |
||||
|
|
||||
|
```sh |
||||
|
$ git clone git://github.com/doctrine/instantiator.git |
||||
|
``` |
||||
|
|
||||
|
You will then need to run a composer installation: |
||||
|
|
||||
|
```sh |
||||
|
$ cd Instantiator |
||||
|
$ curl -s https://getcomposer.org/installer | php |
||||
|
$ php composer.phar update |
||||
|
``` |
||||
|
|
||||
|
## Testing |
||||
|
|
||||
|
The PHPUnit version to be used is the one installed as a dev- dependency via composer: |
||||
|
|
||||
|
```sh |
||||
|
$ ./vendor/bin/phpunit |
||||
|
``` |
||||
|
|
||||
|
Accepted coverage for new contributions is 80%. Any contribution not satisfying this requirement |
||||
|
won't be merged. |
||||
|
|
@ -0,0 +1,19 @@ |
|||||
|
Copyright (c) 2014 Doctrine Project |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of |
||||
|
this software and associated documentation files (the "Software"), to deal in |
||||
|
the Software without restriction, including without limitation the rights to |
||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies |
||||
|
of the Software, and to permit persons to whom the Software is furnished to do |
||||
|
so, subject to the following conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be included in all |
||||
|
copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
|
SOFTWARE. |
@ -0,0 +1,40 @@ |
|||||
|
# Instantiator |
||||
|
|
||||
|
This library provides a way of avoiding usage of constructors when instantiating PHP classes. |
||||
|
|
||||
|
[![Build Status](https://travis-ci.org/doctrine/instantiator.svg?branch=master)](https://travis-ci.org/doctrine/instantiator) |
||||
|
[![Code Coverage](https://scrutinizer-ci.com/g/doctrine/instantiator/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/doctrine/instantiator/?branch=master) |
||||
|
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/doctrine/instantiator/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/doctrine/instantiator/?branch=master) |
||||
|
[![Dependency Status](https://www.versioneye.com/package/php--doctrine--instantiator/badge.svg)](https://www.versioneye.com/package/php--doctrine--instantiator) |
||||
|
[![HHVM Status](http://hhvm.h4cc.de/badge/doctrine/instantiator.png)](http://hhvm.h4cc.de/package/doctrine/instantiator) |
||||
|
|
||||
|
[![Latest Stable Version](https://poser.pugx.org/doctrine/instantiator/v/stable.png)](https://packagist.org/packages/doctrine/instantiator) |
||||
|
[![Latest Unstable Version](https://poser.pugx.org/doctrine/instantiator/v/unstable.png)](https://packagist.org/packages/doctrine/instantiator) |
||||
|
|
||||
|
## Installation |
||||
|
|
||||
|
The suggested installation method is via [composer](https://getcomposer.org/): |
||||
|
|
||||
|
```sh |
||||
|
php composer.phar require "doctrine/instantiator:~1.0.3" |
||||
|
``` |
||||
|
|
||||
|
## Usage |
||||
|
|
||||
|
The instantiator is able to create new instances of any class without using the constructor or any API of the class |
||||
|
itself: |
||||
|
|
||||
|
```php |
||||
|
$instantiator = new \Doctrine\Instantiator\Instantiator(); |
||||
|
|
||||
|
$instance = $instantiator->instantiate(\My\ClassName\Here::class); |
||||
|
``` |
||||
|
|
||||
|
## Contributing |
||||
|
|
||||
|
Please read the [CONTRIBUTING.md](CONTRIBUTING.md) contents if you wish to help out! |
||||
|
|
||||
|
## Credits |
||||
|
|
||||
|
This library was migrated from [ocramius/instantiator](https://github.com/Ocramius/Instantiator), which |
||||
|
has been donated to the doctrine organization, and which is now deprecated in favour of this package. |
@ -0,0 +1,45 @@ |
|||||
|
{ |
||||
|
"name": "doctrine/instantiator", |
||||
|
"description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", |
||||
|
"type": "library", |
||||
|
"license": "MIT", |
||||
|
"homepage": "https://github.com/doctrine/instantiator", |
||||
|
"keywords": [ |
||||
|
"instantiate", |
||||
|
"constructor" |
||||
|
], |
||||
|
"authors": [ |
||||
|
{ |
||||
|
"name": "Marco Pivetta", |
||||
|
"email": "ocramius@gmail.com", |
||||
|
"homepage": "http://ocramius.github.com/" |
||||
|
} |
||||
|
], |
||||
|
"require": { |
||||
|
"php": "^7.1" |
||||
|
}, |
||||
|
"require-dev": { |
||||
|
"ext-phar": "*", |
||||
|
"ext-pdo": "*", |
||||
|
"phpunit/phpunit": "^6.2.3", |
||||
|
"squizlabs/php_codesniffer": "^3.0.2", |
||||
|
"athletic/athletic": "~0.1.8" |
||||
|
}, |
||||
|
"autoload": { |
||||
|
"psr-4": { |
||||
|
"Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" |
||||
|
} |
||||
|
}, |
||||
|
"autoload-dev": { |
||||
|
"psr-0": { |
||||
|
"DoctrineTest\\InstantiatorPerformance\\": "tests", |
||||
|
"DoctrineTest\\InstantiatorTest\\": "tests", |
||||
|
"DoctrineTest\\InstantiatorTestAsset\\": "tests" |
||||
|
} |
||||
|
}, |
||||
|
"extra": { |
||||
|
"branch-alias": { |
||||
|
"dev-master": "1.2.x-dev" |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +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\Instantiator\Exception; |
||||
|
|
||||
|
/** |
||||
|
* Base exception marker interface for the instantiator component |
||||
|
* |
||||
|
* @author Marco Pivetta <ocramius@gmail.com> |
||||
|
*/ |
||||
|
interface ExceptionInterface |
||||
|
{ |
||||
|
} |
@ -0,0 +1,52 @@ |
|||||
|
<?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\Instantiator\Exception; |
||||
|
|
||||
|
use InvalidArgumentException as BaseInvalidArgumentException; |
||||
|
use ReflectionClass; |
||||
|
|
||||
|
/** |
||||
|
* Exception for invalid arguments provided to the instantiator |
||||
|
* |
||||
|
* @author Marco Pivetta <ocramius@gmail.com> |
||||
|
*/ |
||||
|
class InvalidArgumentException extends BaseInvalidArgumentException implements ExceptionInterface |
||||
|
{ |
||||
|
public static function fromNonExistingClass(string $className) : self |
||||
|
{ |
||||
|
if (interface_exists($className)) { |
||||
|
return new self(sprintf('The provided type "%s" is an interface, and can not be instantiated', $className)); |
||||
|
} |
||||
|
|
||||
|
if (PHP_VERSION_ID >= 50400 && trait_exists($className)) { |
||||
|
return new self(sprintf('The provided type "%s" is a trait, and can not be instantiated', $className)); |
||||
|
} |
||||
|
|
||||
|
return new self(sprintf('The provided class "%s" does not exist', $className)); |
||||
|
} |
||||
|
|
||||
|
public static function fromAbstractClass(ReflectionClass $reflectionClass) : self |
||||
|
{ |
||||
|
return new self(sprintf( |
||||
|
'The provided class "%s" is abstract, and can not be instantiated', |
||||
|
$reflectionClass->getName() |
||||
|
)); |
||||
|
} |
||||
|
} |
@ -0,0 +1,66 @@ |
|||||
|
<?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\Instantiator\Exception; |
||||
|
|
||||
|
use Exception; |
||||
|
use ReflectionClass; |
||||
|
use UnexpectedValueException as BaseUnexpectedValueException; |
||||
|
|
||||
|
/** |
||||
|
* Exception for given parameters causing invalid/unexpected state on instantiation |
||||
|
* |
||||
|
* @author Marco Pivetta <ocramius@gmail.com> |
||||
|
*/ |
||||
|
class UnexpectedValueException extends BaseUnexpectedValueException implements ExceptionInterface |
||||
|
{ |
||||
|
public static function fromSerializationTriggeredException( |
||||
|
ReflectionClass $reflectionClass, |
||||
|
Exception $exception |
||||
|
) : self { |
||||
|
return new self( |
||||
|
sprintf( |
||||
|
'An exception was raised while trying to instantiate an instance of "%s" via un-serialization', |
||||
|
$reflectionClass->getName() |
||||
|
), |
||||
|
0, |
||||
|
$exception |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
public static function fromUncleanUnSerialization( |
||||
|
ReflectionClass $reflectionClass, |
||||
|
string $errorString, |
||||
|
int $errorCode, |
||||
|
string $errorFile, |
||||
|
int $errorLine |
||||
|
) : self { |
||||
|
return new self( |
||||
|
sprintf( |
||||
|
'Could not produce an instance of "%s" via un-serialization, since an error was triggered ' |
||||
|
. 'in file "%s" at line "%d"', |
||||
|
$reflectionClass->getName(), |
||||
|
$errorFile, |
||||
|
$errorLine |
||||
|
), |
||||
|
0, |
||||
|
new Exception($errorString, $errorCode) |
||||
|
); |
||||
|
} |
||||
|
} |
@ -0,0 +1,216 @@ |
|||||
|
<?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\Instantiator; |
||||
|
|
||||
|
use Doctrine\Instantiator\Exception\InvalidArgumentException; |
||||
|
use Doctrine\Instantiator\Exception\UnexpectedValueException; |
||||
|
use Exception; |
||||
|
use ReflectionClass; |
||||
|
|
||||
|
/** |
||||
|
* {@inheritDoc} |
||||
|
* |
||||
|
* @author Marco Pivetta <ocramius@gmail.com> |
||||
|
*/ |
||||
|
final class Instantiator implements InstantiatorInterface |
||||
|
{ |
||||
|
/** |
||||
|
* Markers used internally by PHP to define whether {@see \unserialize} should invoke |
||||
|
* the method {@see \Serializable::unserialize()} when dealing with classes implementing |
||||
|
* the {@see \Serializable} interface. |
||||
|
*/ |
||||
|
const SERIALIZATION_FORMAT_USE_UNSERIALIZER = 'C'; |
||||
|
const SERIALIZATION_FORMAT_AVOID_UNSERIALIZER = 'O'; |
||||
|
|
||||
|
/** |
||||
|
* @var \callable[] used to instantiate specific classes, indexed by class name |
||||
|
*/ |
||||
|
private static $cachedInstantiators = []; |
||||
|
|
||||
|
/** |
||||
|
* @var object[] of objects that can directly be cloned, indexed by class name |
||||
|
*/ |
||||
|
private static $cachedCloneables = []; |
||||
|
|
||||
|
/** |
||||
|
* {@inheritDoc} |
||||
|
*/ |
||||
|
public function instantiate($className) |
||||
|
{ |
||||
|
if (isset(self::$cachedCloneables[$className])) { |
||||
|
return clone self::$cachedCloneables[$className]; |
||||
|
} |
||||
|
|
||||
|
if (isset(self::$cachedInstantiators[$className])) { |
||||
|
$factory = self::$cachedInstantiators[$className]; |
||||
|
|
||||
|
return $factory(); |
||||
|
} |
||||
|
|
||||
|
return $this->buildAndCacheFromFactory($className); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Builds the requested object and caches it in static properties for performance |
||||
|
* |
||||
|
* @return object |
||||
|
*/ |
||||
|
private function buildAndCacheFromFactory(string $className) |
||||
|
{ |
||||
|
$factory = self::$cachedInstantiators[$className] = $this->buildFactory($className); |
||||
|
$instance = $factory(); |
||||
|
|
||||
|
if ($this->isSafeToClone(new ReflectionClass($instance))) { |
||||
|
self::$cachedCloneables[$className] = clone $instance; |
||||
|
} |
||||
|
|
||||
|
return $instance; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Builds a callable capable of instantiating the given $className without |
||||
|
* invoking its constructor. |
||||
|
* |
||||
|
* @throws InvalidArgumentException |
||||
|
* @throws UnexpectedValueException |
||||
|
* @throws \ReflectionException |
||||
|
*/ |
||||
|
private function buildFactory(string $className) : callable |
||||
|
{ |
||||
|
$reflectionClass = $this->getReflectionClass($className); |
||||
|
|
||||
|
if ($this->isInstantiableViaReflection($reflectionClass)) { |
||||
|
return [$reflectionClass, 'newInstanceWithoutConstructor']; |
||||
|
} |
||||
|
|
||||
|
$serializedString = sprintf( |
||||
|
'%s:%d:"%s":0:{}', |
||||
|
self::SERIALIZATION_FORMAT_AVOID_UNSERIALIZER, |
||||
|
strlen($className), |
||||
|
$className |
||||
|
); |
||||
|
|
||||
|
$this->checkIfUnSerializationIsSupported($reflectionClass, $serializedString); |
||||
|
|
||||
|
return function () use ($serializedString) { |
||||
|
return unserialize($serializedString); |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param string $className |
||||
|
* |
||||
|
* @return ReflectionClass |
||||
|
* |
||||
|
* @throws InvalidArgumentException |
||||
|
* @throws \ReflectionException |
||||
|
*/ |
||||
|
private function getReflectionClass($className) : ReflectionClass |
||||
|
{ |
||||
|
if (! class_exists($className)) { |
||||
|
throw InvalidArgumentException::fromNonExistingClass($className); |
||||
|
} |
||||
|
|
||||
|
$reflection = new ReflectionClass($className); |
||||
|
|
||||
|
if ($reflection->isAbstract()) { |
||||
|
throw InvalidArgumentException::fromAbstractClass($reflection); |
||||
|
} |
||||
|
|
||||
|
return $reflection; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param ReflectionClass $reflectionClass |
||||
|
* @param string $serializedString |
||||
|
* |
||||
|
* @throws UnexpectedValueException |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
private function checkIfUnSerializationIsSupported(ReflectionClass $reflectionClass, $serializedString) : void |
||||
|
{ |
||||
|
set_error_handler(function ($code, $message, $file, $line) use ($reflectionClass, & $error) : void { |
||||
|
$error = UnexpectedValueException::fromUncleanUnSerialization( |
||||
|
$reflectionClass, |
||||
|
$message, |
||||
|
$code, |
||||
|
$file, |
||||
|
$line |
||||
|
); |
||||
|
}); |
||||
|
|
||||
|
$this->attemptInstantiationViaUnSerialization($reflectionClass, $serializedString); |
||||
|
|
||||
|
restore_error_handler(); |
||||
|
|
||||
|
if ($error) { |
||||
|
throw $error; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param ReflectionClass $reflectionClass |
||||
|
* @param string $serializedString |
||||
|
* |
||||
|
* @throws UnexpectedValueException |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
private function attemptInstantiationViaUnSerialization(ReflectionClass $reflectionClass, $serializedString) : void |
||||
|
{ |
||||
|
try { |
||||
|
unserialize($serializedString); |
||||
|
} catch (Exception $exception) { |
||||
|
restore_error_handler(); |
||||
|
|
||||
|
throw UnexpectedValueException::fromSerializationTriggeredException($reflectionClass, $exception); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private function isInstantiableViaReflection(ReflectionClass $reflectionClass) : bool |
||||
|
{ |
||||
|
return ! ($this->hasInternalAncestors($reflectionClass) && $reflectionClass->isFinal()); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Verifies whether the given class is to be considered internal |
||||
|
*/ |
||||
|
private function hasInternalAncestors(ReflectionClass $reflectionClass) : bool |
||||
|
{ |
||||
|
do { |
||||
|
if ($reflectionClass->isInternal()) { |
||||
|
return true; |
||||
|
} |
||||
|
} while ($reflectionClass = $reflectionClass->getParentClass()); |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Checks if a class is cloneable |
||||
|
* |
||||
|
* Classes implementing `__clone` cannot be safely cloned, as that may cause side-effects. |
||||
|
*/ |
||||
|
private function isSafeToClone(ReflectionClass $reflection) : bool |
||||
|
{ |
||||
|
return $reflection->isCloneable() && ! $reflection->hasMethod('__clone'); |
||||
|
} |
||||
|
} |
@ -0,0 +1,37 @@ |
|||||
|
<?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\Instantiator; |
||||
|
|
||||
|
/** |
||||
|
* Instantiator provides utility methods to build objects without invoking their constructors |
||||
|
* |
||||
|
* @author Marco Pivetta <ocramius@gmail.com> |
||||
|
*/ |
||||
|
interface InstantiatorInterface |
||||
|
{ |
||||
|
/** |
||||
|
* @param string $className |
||||
|
* |
||||
|
* @return object |
||||
|
* |
||||
|
* @throws \Doctrine\Instantiator\Exception\ExceptionInterface |
||||
|
*/ |
||||
|
public function instantiate($className); |
||||
|
} |
@ -0,0 +1,19 @@ |
|||||
|
Copyright (c) 2006-2013 Doctrine Project |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of |
||||
|
this software and associated documentation files (the "Software"), to deal in |
||||
|
the Software without restriction, including without limitation the rights to |
||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies |
||||
|
of the Software, and to permit persons to whom the Software is furnished to do |
||||
|
so, subject to the following conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be included in all |
||||
|
copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
|
SOFTWARE. |
@ -0,0 +1,5 @@ |
|||||
|
# 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). |
@ -0,0 +1,24 @@ |
|||||
|
{ |
||||
|
"name": "doctrine/lexer", |
||||
|
"type": "library", |
||||
|
"description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", |
||||
|
"keywords": ["lexer", "parser"], |
||||
|
"homepage": "http://www.doctrine-project.org", |
||||
|
"license": "MIT", |
||||
|
"authors": [ |
||||
|
{"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, |
||||
|
{"name": "Roman Borschel", "email": "roman@code-factory.org"}, |
||||
|
{"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"} |
||||
|
], |
||||
|
"require": { |
||||
|
"php": ">=5.3.2" |
||||
|
}, |
||||
|
"autoload": { |
||||
|
"psr-0": { "Doctrine\\Common\\Lexer\\": "lib/" } |
||||
|
}, |
||||
|
"extra": { |
||||
|
"branch-alias": { |
||||
|
"dev-master": "1.0.x-dev" |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,327 @@ |
|||||
|
<?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\Lexer; |
||||
|
|
||||
|
/** |
||||
|
* 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 |
||||
|
{ |
||||
|
/** |
||||
|
* Lexer original input string. |
||||
|
* |
||||
|
* @var string |
||||
|
*/ |
||||
|
private $input; |
||||
|
|
||||
|
/** |
||||
|
* Array of scanned tokens. |
||||
|
* |
||||
|
* Each token is an associative array containing three items: |
||||
|
* - 'value' : the string value of the token in the input string |
||||
|
* - 'type' : the type of the token (identifier, numeric, string, input |
||||
|
* parameter, none) |
||||
|
* - 'position' : the position of the token in the input string |
||||
|
* |
||||
|
* @var array |
||||
|
*/ |
||||
|
private $tokens = array(); |
||||
|
|
||||
|
/** |
||||
|
* Current lexer position in input string. |
||||
|
* |
||||
|
* @var integer |
||||
|
*/ |
||||
|
private $position = 0; |
||||
|
|
||||
|
/** |
||||
|
* Current peek of current lexer position. |
||||
|
* |
||||
|
* @var integer |
||||
|
*/ |
||||
|
private $peek = 0; |
||||
|
|
||||
|
/** |
||||
|
* The next token in the input. |
||||
|
* |
||||
|
* @var array |
||||
|
*/ |
||||
|
public $lookahead; |
||||
|
|
||||
|
/** |
||||
|
* The last matched/seen token. |
||||
|
* |
||||
|
* @var array |
||||
|
*/ |
||||
|
public $token; |
||||
|
|
||||
|
/** |
||||
|
* Sets the input data to be tokenized. |
||||
|
* |
||||
|
* The Lexer is immediately reset and the new input tokenized. |
||||
|
* Any unprocessed tokens from any previous input are lost. |
||||
|
* |
||||
|
* @param string $input The input to be tokenized. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function setInput($input) |
||||
|
{ |
||||
|
$this->input = $input; |
||||
|
$this->tokens = array(); |
||||
|
|
||||
|
$this->reset(); |
||||
|
$this->scan($input); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Resets the lexer. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function reset() |
||||
|
{ |
||||
|
$this->lookahead = null; |
||||
|
$this->token = null; |
||||
|
$this->peek = 0; |
||||
|
$this->position = 0; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Resets the peek pointer to 0. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function resetPeek() |
||||
|
{ |
||||
|
$this->peek = 0; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Resets the lexer position on the input to the given position. |
||||
|
* |
||||
|
* @param integer $position Position to place the lexical scanner. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function resetPosition($position = 0) |
||||
|
{ |
||||
|
$this->position = $position; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Retrieve the original lexer's input until a given position. |
||||
|
* |
||||
|
* @param integer $position |
||||
|
* |
||||
|
* @return string |
||||
|
*/ |
||||
|
public function getInputUntilPosition($position) |
||||
|
{ |
||||
|
return substr($this->input, 0, $position); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Checks whether a given token matches the current lookahead. |
||||
|
* |
||||
|
* @param integer|string $token |
||||
|
* |
||||
|
* @return boolean |
||||
|
*/ |
||||
|
public function isNextToken($token) |
||||
|
{ |
||||
|
return null !== $this->lookahead && $this->lookahead['type'] === $token; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Checks whether any of the given tokens matches the current lookahead. |
||||
|
* |
||||
|
* @param array $tokens |
||||
|
* |
||||
|
* @return boolean |
||||
|
*/ |
||||
|
public function isNextTokenAny(array $tokens) |
||||
|
{ |
||||
|
return null !== $this->lookahead && in_array($this->lookahead['type'], $tokens, true); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Moves to the next token in the input string. |
||||
|
* |
||||
|
* @return boolean |
||||
|
*/ |
||||
|
public function moveNext() |
||||
|
{ |
||||
|
$this->peek = 0; |
||||
|
$this->token = $this->lookahead; |
||||
|
$this->lookahead = (isset($this->tokens[$this->position])) |
||||
|
? $this->tokens[$this->position++] : null; |
||||
|
|
||||
|
return $this->lookahead !== null; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Tells the lexer to skip input tokens until it sees a token with the given value. |
||||
|
* |
||||
|
* @param string $type The token type to skip until. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function skipUntil($type) |
||||
|
{ |
||||
|
while ($this->lookahead !== null && $this->lookahead['type'] !== $type) { |
||||
|
$this->moveNext(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Checks if given value is identical to the given token. |
||||
|
* |
||||
|
* @param mixed $value |
||||
|
* @param integer $token |
||||
|
* |
||||
|
* @return boolean |
||||
|
*/ |
||||
|
public function isA($value, $token) |
||||
|
{ |
||||
|
return $this->getType($value) === $token; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Moves the lookahead token forward. |
||||
|
* |
||||
|
* @return array|null The next token or NULL if there are no more tokens ahead. |
||||
|
*/ |
||||
|
public function peek() |
||||
|
{ |
||||
|
if (isset($this->tokens[$this->position + $this->peek])) { |
||||
|
return $this->tokens[$this->position + $this->peek++]; |
||||
|
} else { |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Peeks at the next token, returns it and immediately resets the peek. |
||||
|
* |
||||
|
* @return array|null The next token or NULL if there are no more tokens ahead. |
||||
|
*/ |
||||
|
public function glimpse() |
||||
|
{ |
||||
|
$peek = $this->peek(); |
||||
|
$this->peek = 0; |
||||
|
return $peek; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Scans the input string for tokens. |
||||
|
* |
||||
|
* @param string $input A query string. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
protected function scan($input) |
||||
|
{ |
||||
|
static $regex; |
||||
|
|
||||
|
if ( ! isset($regex)) { |
||||
|
$regex = sprintf( |
||||
|
'/(%s)|%s/%s', |
||||
|
implode(')|(', $this->getCatchablePatterns()), |
||||
|
implode('|', $this->getNonCatchablePatterns()), |
||||
|
$this->getModifiers() |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
$flags = PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE; |
||||
|
$matches = preg_split($regex, $input, -1, $flags); |
||||
|
|
||||
|
foreach ($matches as $match) { |
||||
|
// Must remain before 'value' assignment since it can change content |
||||
|
$type = $this->getType($match[0]); |
||||
|
|
||||
|
$this->tokens[] = array( |
||||
|
'value' => $match[0], |
||||
|
'type' => $type, |
||||
|
'position' => $match[1], |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Gets the literal for a given token. |
||||
|
* |
||||
|
* @param integer $token |
||||
|
* |
||||
|
* @return string |
||||
|
*/ |
||||
|
public function getLiteral($token) |
||||
|
{ |
||||
|
$className = get_class($this); |
||||
|
$reflClass = new \ReflectionClass($className); |
||||
|
$constants = $reflClass->getConstants(); |
||||
|
|
||||
|
foreach ($constants as $name => $value) { |
||||
|
if ($value === $token) { |
||||
|
return $className . '::' . $name; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return $token; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Regex modifiers |
||||
|
* |
||||
|
* @return string |
||||
|
*/ |
||||
|
protected function getModifiers() |
||||
|
{ |
||||
|
return 'i'; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Lexical catchable patterns. |
||||
|
* |
||||
|
* @return array |
||||
|
*/ |
||||
|
abstract protected function getCatchablePatterns(); |
||||
|
|
||||
|
/** |
||||
|
* Lexical non-catchable patterns. |
||||
|
* |
||||
|
* @return array |
||||
|
*/ |
||||
|
abstract protected function getNonCatchablePatterns(); |
||||
|
|
||||
|
/** |
||||
|
* Retrieve token type. Also processes the token value if necessary. |
||||
|
* |
||||
|
* @param string $value |
||||
|
* |
||||
|
* @return integer |
||||
|
*/ |
||||
|
abstract protected function getType(&$value); |
||||
|
} |
@ -0,0 +1,221 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator; |
||||
|
|
||||
|
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_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 GENERIC = 300; |
||||
|
const CRLF = 301; |
||||
|
const INVALID = 302; |
||||
|
const ASCII_INVALID_FROM = 127; |
||||
|
const ASCII_INVALID_TO = 199; |
||||
|
|
||||
|
/** |
||||
|
* US-ASCII visible characters not valid for atext (@link http://tools.ietf.org/html/rfc5322#section-3.2.3) |
||||
|
* |
||||
|
* @var array |
||||
|
*/ |
||||
|
protected $charValue = array( |
||||
|
'(' => self::S_OPENPARENTHESIS, |
||||
|
')' => self::S_CLOSEPARENTHESIS, |
||||
|
'<' => self::S_LOWERTHAN, |
||||
|
'>' => self::S_GREATERTHAN, |
||||
|
'[' => self::S_OPENBRACKET, |
||||
|
']' => self::S_CLOSEBRACKET, |
||||
|
':' => self::S_COLON, |
||||
|
';' => self::S_SEMICOLON, |
||||
|
'@' => self::S_AT, |
||||
|
'\\' => self::S_BACKSLASH, |
||||
|
'/' => self::S_SLASH, |
||||
|
',' => self::S_COMMA, |
||||
|
'.' => self::S_DOT, |
||||
|
'"' => self::S_DQUOTE, |
||||
|
'-' => self::S_HYPHEN, |
||||
|
'::' => self::S_DOUBLECOLON, |
||||
|
' ' => self::S_SP, |
||||
|
"\t" => self::S_HTAB, |
||||
|
"\r" => self::S_CR, |
||||
|
"\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, |
||||
|
); |
||||
|
|
||||
|
protected $hasInvalidTokens = false; |
||||
|
|
||||
|
protected $previous; |
||||
|
|
||||
|
public function reset() |
||||
|
{ |
||||
|
$this->hasInvalidTokens = false; |
||||
|
parent::reset(); |
||||
|
} |
||||
|
|
||||
|
public function hasInvalidTokens() |
||||
|
{ |
||||
|
return $this->hasInvalidTokens; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param $type |
||||
|
* @throws \UnexpectedValueException |
||||
|
* @return boolean |
||||
|
*/ |
||||
|
public function find($type) |
||||
|
{ |
||||
|
$search = clone $this; |
||||
|
$search->skipUntil($type); |
||||
|
|
||||
|
if (!$search->lookahead) { |
||||
|
throw new \UnexpectedValueException($type . ' not found'); |
||||
|
} |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* getPrevious |
||||
|
* |
||||
|
* @return array token |
||||
|
*/ |
||||
|
public function getPrevious() |
||||
|
{ |
||||
|
return $this->previous; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* moveNext |
||||
|
* |
||||
|
* @return boolean |
||||
|
*/ |
||||
|
public function moveNext() |
||||
|
{ |
||||
|
$this->previous = $this->token; |
||||
|
|
||||
|
return parent::moveNext(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Lexical catchable patterns. |
||||
|
* |
||||
|
* @return string[] |
||||
|
*/ |
||||
|
protected function getCatchablePatterns() |
||||
|
{ |
||||
|
return array( |
||||
|
'[a-zA-Z_]+[46]?', //ASCII and domain literal |
||||
|
'[^\x00-\x7F]', //UTF-8 |
||||
|
'[0-9]+', |
||||
|
'\r\n', |
||||
|
'::', |
||||
|
'\s+?', |
||||
|
'.', |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Lexical non-catchable patterns. |
||||
|
* |
||||
|
* @return string[] |
||||
|
*/ |
||||
|
protected function getNonCatchablePatterns() |
||||
|
{ |
||||
|
return array('[\xA0-\xff]+'); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Retrieve token type. Also processes the token value if necessary. |
||||
|
* |
||||
|
* @param string $value |
||||
|
* @throws \InvalidArgumentException |
||||
|
* @return integer |
||||
|
*/ |
||||
|
protected function getType(&$value) |
||||
|
{ |
||||
|
if ($this->isNullType($value)) { |
||||
|
return self::C_NUL; |
||||
|
} |
||||
|
|
||||
|
if ($this->isValid($value)) { |
||||
|
return $this->charValue[$value]; |
||||
|
} |
||||
|
|
||||
|
if ($this->isUTF8Invalid($value)) { |
||||
|
$this->hasInvalidTokens = true; |
||||
|
return self::INVALID; |
||||
|
} |
||||
|
|
||||
|
return self::GENERIC; |
||||
|
} |
||||
|
|
||||
|
protected function isValid($value) |
||||
|
{ |
||||
|
if (isset($this->charValue[$value])) { |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param $value |
||||
|
* @return bool |
||||
|
*/ |
||||
|
protected function isNullType($value) |
||||
|
{ |
||||
|
if ($value === "\0") { |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param $value |
||||
|
* @return bool |
||||
|
*/ |
||||
|
protected function isUTF8Invalid($value) |
||||
|
{ |
||||
|
if (preg_match('/\p{Cc}+/u', $value)) { |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
protected function getModifiers() |
||||
|
{ |
||||
|
return 'iu'; |
||||
|
} |
||||
|
} |
@ -0,0 +1,104 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator; |
||||
|
|
||||
|
use Egulias\EmailValidator\Exception\ExpectingATEXT; |
||||
|
use Egulias\EmailValidator\Exception\NoLocalPart; |
||||
|
use Egulias\EmailValidator\Parser\DomainPart; |
||||
|
use Egulias\EmailValidator\Parser\LocalPart; |
||||
|
use Egulias\EmailValidator\Warning\EmailTooLong; |
||||
|
|
||||
|
/** |
||||
|
* EmailParser |
||||
|
* |
||||
|
* @author Eduardo Gulias Davis <me@egulias.com> |
||||
|
*/ |
||||
|
class EmailParser |
||||
|
{ |
||||
|
const EMAIL_MAX_LENGTH = 254; |
||||
|
|
||||
|
protected $warnings; |
||||
|
protected $domainPart = ''; |
||||
|
protected $localPart = ''; |
||||
|
protected $lexer; |
||||
|
protected $localPartParser; |
||||
|
protected $domainPartParser; |
||||
|
|
||||
|
public function __construct(EmailLexer $lexer) |
||||
|
{ |
||||
|
$this->lexer = $lexer; |
||||
|
$this->localPartParser = new LocalPart($this->lexer); |
||||
|
$this->domainPartParser = new DomainPart($this->lexer); |
||||
|
$this->warnings = new \SplObjectStorage(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param $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); |
||||
|
} |
||||
|
|
||||
|
public function getWarnings() |
||||
|
{ |
||||
|
$localPartWarnings = $this->localPartParser->getWarnings(); |
||||
|
$domainPartWarnings = $this->domainPartParser->getWarnings(); |
||||
|
$this->warnings = array_merge($localPartWarnings, $domainPartWarnings); |
||||
|
|
||||
|
$this->addLongEmailWarning($this->localPart, $this->domainPart); |
||||
|
|
||||
|
return $this->warnings; |
||||
|
} |
||||
|
|
||||
|
public function getParsedDomainPart() |
||||
|
{ |
||||
|
return $this->domainPart; |
||||
|
} |
||||
|
|
||||
|
protected function setParts($email) |
||||
|
{ |
||||
|
$parts = explode('@', $email); |
||||
|
$this->domainPart = $this->domainPartParser->getDomainPart(); |
||||
|
$this->localPart = $parts[0]; |
||||
|
} |
||||
|
|
||||
|
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) |
||||
|
{ |
||||
|
if (strlen($localPart . '@' . $parsedDomainPart) > self::EMAIL_MAX_LENGTH) { |
||||
|
$this->warnings[EmailTooLong::CODE] = new EmailTooLong(); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,67 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator; |
||||
|
|
||||
|
use Egulias\EmailValidator\Exception\InvalidEmail; |
||||
|
use Egulias\EmailValidator\Validation\EmailValidation; |
||||
|
|
||||
|
class EmailValidator |
||||
|
{ |
||||
|
/** |
||||
|
* @var EmailLexer |
||||
|
*/ |
||||
|
private $lexer; |
||||
|
|
||||
|
/** |
||||
|
* @var array |
||||
|
*/ |
||||
|
protected $warnings; |
||||
|
|
||||
|
/** |
||||
|
* @var InvalidEmail |
||||
|
*/ |
||||
|
protected $error; |
||||
|
|
||||
|
public function __construct() |
||||
|
{ |
||||
|
$this->lexer = new EmailLexer(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param $email |
||||
|
* @param EmailValidation $emailValidation |
||||
|
* @return bool |
||||
|
*/ |
||||
|
public function isValid($email, EmailValidation $emailValidation) |
||||
|
{ |
||||
|
$isValid = $emailValidation->isValid($email, $this->lexer); |
||||
|
$this->warnings = $emailValidation->getWarnings(); |
||||
|
$this->error = $emailValidation->getError(); |
||||
|
|
||||
|
return $isValid; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @return boolean |
||||
|
*/ |
||||
|
public function hasWarnings() |
||||
|
{ |
||||
|
return !empty($this->warnings); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @return array |
||||
|
*/ |
||||
|
public function getWarnings() |
||||
|
{ |
||||
|
return $this->warnings; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @return InvalidEmail |
||||
|
*/ |
||||
|
public function getError() |
||||
|
{ |
||||
|
return $this->error; |
||||
|
} |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Exception; |
||||
|
|
||||
|
class AtextAfterCFWS extends InvalidEmail |
||||
|
{ |
||||
|
const CODE = 133; |
||||
|
const REASON = "ATEXT found after CFWS"; |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Exception; |
||||
|
|
||||
|
class CRLFAtTheEnd extends InvalidEmail |
||||
|
{ |
||||
|
const CODE = 149; |
||||
|
const REASON = "CRLF at the end"; |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Exception; |
||||
|
|
||||
|
class CRLFX2 extends InvalidEmail |
||||
|
{ |
||||
|
const CODE = 148; |
||||
|
const REASON = "Folding whitespace CR LF found twice"; |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Exception; |
||||
|
|
||||
|
class CRNoLF extends InvalidEmail |
||||
|
{ |
||||
|
const CODE = 150; |
||||
|
const REASON = "Missing LF after CR"; |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Exception; |
||||
|
|
||||
|
class CharNotAllowed extends InvalidEmail |
||||
|
{ |
||||
|
const CODE = 201; |
||||
|
const REASON = "Non allowed character in domain"; |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Exception; |
||||
|
|
||||
|
class CommaInDomain extends InvalidEmail |
||||
|
{ |
||||
|
const CODE = 200; |
||||
|
const REASON = "Comma ',' is not allowed in domain part"; |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Exception; |
||||
|
|
||||
|
class ConsecutiveAt extends InvalidEmail |
||||
|
{ |
||||
|
const CODE = 128; |
||||
|
const REASON = "Consecutive AT"; |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Exception; |
||||
|
|
||||
|
class ConsecutiveDot extends InvalidEmail |
||||
|
{ |
||||
|
const CODE = 132; |
||||
|
const REASON = "Consecutive DOT"; |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Exception; |
||||
|
|
||||
|
class DomainHyphened extends InvalidEmail |
||||
|
{ |
||||
|
const CODE = 144; |
||||
|
const REASON = "Hyphen found in domain"; |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Exception; |
||||
|
|
||||
|
class DotAtEnd extends InvalidEmail |
||||
|
{ |
||||
|
const CODE = 142; |
||||
|
const REASON = "Dot at the end"; |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Exception; |
||||
|
|
||||
|
class DotAtStart extends InvalidEmail |
||||
|
{ |
||||
|
const CODE = 141; |
||||
|
const REASON = "Found DOT at start"; |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Exception; |
||||
|
|
||||
|
class ExpectingAT extends InvalidEmail |
||||
|
{ |
||||
|
const CODE = 202; |
||||
|
const REASON = "Expecting AT '@' "; |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Exception; |
||||
|
|
||||
|
class ExpectingATEXT extends InvalidEmail |
||||
|
{ |
||||
|
const CODE = 137; |
||||
|
const REASON = "Expecting ATEXT"; |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Exception; |
||||
|
|
||||
|
class ExpectingCTEXT extends InvalidEmail |
||||
|
{ |
||||
|
const CODE = 139; |
||||
|
const REASON = "Expecting CTEXT"; |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Exception; |
||||
|
|
||||
|
class ExpectingDTEXT extends InvalidEmail |
||||
|
{ |
||||
|
const CODE = 129; |
||||
|
const REASON = "Expected DTEXT"; |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Exception; |
||||
|
|
||||
|
class ExpectingDomainLiteralClose extends InvalidEmail |
||||
|
{ |
||||
|
const CODE = 137; |
||||
|
const REASON = "Closing bracket ']' for domain literal not found"; |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Exception; |
||||
|
|
||||
|
class ExpectedQPair extends InvalidEmail |
||||
|
{ |
||||
|
const CODE = 136; |
||||
|
const REASON = "Expecting QPAIR"; |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Exception; |
||||
|
|
||||
|
abstract class InvalidEmail extends \InvalidArgumentException |
||||
|
{ |
||||
|
const REASON = "Invalid email"; |
||||
|
const CODE = 0; |
||||
|
|
||||
|
public function __construct() |
||||
|
{ |
||||
|
parent::__construct(static::REASON, static::CODE); |
||||
|
} |
||||
|
} |
@ -0,0 +1,11 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Exception; |
||||
|
|
||||
|
use Egulias\EmailValidator\Exception\InvalidEmail; |
||||
|
|
||||
|
class NoDNSRecord extends InvalidEmail |
||||
|
{ |
||||
|
const CODE = 5; |
||||
|
const REASON = 'No MX or A DSN record was found for this email'; |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Exception; |
||||
|
|
||||
|
class NoDomainPart extends InvalidEmail |
||||
|
{ |
||||
|
const CODE = 131; |
||||
|
const REASON = "No Domain part"; |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Exception; |
||||
|
|
||||
|
class NoLocalPart extends InvalidEmail |
||||
|
{ |
||||
|
const CODE = 130; |
||||
|
const REASON = "No local part"; |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Exception; |
||||
|
|
||||
|
class UnclosedComment extends InvalidEmail |
||||
|
{ |
||||
|
const CODE = 146; |
||||
|
const REASON = "No colosing comment token found"; |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Exception; |
||||
|
|
||||
|
class UnclosedQuotedString extends InvalidEmail |
||||
|
{ |
||||
|
const CODE = 145; |
||||
|
const REASON = "Unclosed quoted string"; |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Exception; |
||||
|
|
||||
|
class UnopenedComment extends InvalidEmail |
||||
|
{ |
||||
|
const CODE = 152; |
||||
|
const REASON = "No opening comment token found"; |
||||
|
} |
@ -0,0 +1,368 @@ |
|||||
|
<?php |
||||
|
|
||||
|
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; |
||||
|
|
||||
|
class DomainPart extends Parser |
||||
|
{ |
||||
|
const DOMAIN_MAX_LENGTH = 254; |
||||
|
protected $domainPart = ''; |
||||
|
|
||||
|
public function parse($domainPart) |
||||
|
{ |
||||
|
$this->lexer->moveNext(); |
||||
|
|
||||
|
if ($this->lexer->token['type'] === EmailLexer::S_DOT) { |
||||
|
throw new DotAtStart(); |
||||
|
} |
||||
|
|
||||
|
if ($this->lexer->token['type'] === EmailLexer::S_EMPTY) { |
||||
|
throw new NoDomainPart(); |
||||
|
} |
||||
|
if ($this->lexer->token['type'] === EmailLexer::S_HYPHEN) { |
||||
|
throw new DomainHyphened(); |
||||
|
} |
||||
|
|
||||
|
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) { |
||||
|
$this->warnings[DeprecatedComment::CODE] = new DeprecatedComment(); |
||||
|
$this->parseDomainComments(); |
||||
|
} |
||||
|
|
||||
|
$domain = $this->doParseDomainPart(); |
||||
|
|
||||
|
$prev = $this->lexer->getPrevious(); |
||||
|
$length = strlen($domain); |
||||
|
|
||||
|
if ($prev['type'] === EmailLexer::S_DOT) { |
||||
|
throw new DotAtEnd(); |
||||
|
} |
||||
|
if ($prev['type'] === EmailLexer::S_HYPHEN) { |
||||
|
throw new DomainHyphened(); |
||||
|
} |
||||
|
if ($length > self::DOMAIN_MAX_LENGTH) { |
||||
|
$this->warnings[DomainTooLong::CODE] = new DomainTooLong(); |
||||
|
} |
||||
|
if ($prev['type'] === EmailLexer::S_CR) { |
||||
|
throw new CRLFAtTheEnd(); |
||||
|
} |
||||
|
$this->domainPart = $domain; |
||||
|
} |
||||
|
|
||||
|
public function getDomainPart() |
||||
|
{ |
||||
|
return $this->domainPart; |
||||
|
} |
||||
|
|
||||
|
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(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected function doParseDomainPart() |
||||
|
{ |
||||
|
$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--; |
||||
|
} |
||||
|
} |
||||
|
if ($this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS) { |
||||
|
if ($openedParenthesis === 0) { |
||||
|
throw new UnopenedComment(); |
||||
|
} else { |
||||
|
$openedParenthesis--; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
$this->checkConsecutiveDots(); |
||||
|
$this->checkDomainPartExceptions($prev); |
||||
|
|
||||
|
if ($this->hasBrackets()) { |
||||
|
$this->parseDomainLiteral(); |
||||
|
} |
||||
|
|
||||
|
$this->checkLabelLength($prev); |
||||
|
|
||||
|
if ($this->isFWS()) { |
||||
|
$this->parseFWS(); |
||||
|
} |
||||
|
|
||||
|
$domain .= $this->lexer->token['value']; |
||||
|
$this->lexer->moveNext(); |
||||
|
} while ($this->lexer->token); |
||||
|
|
||||
|
return $domain; |
||||
|
} |
||||
|
|
||||
|
private function checkNotAllowedChars($token) |
||||
|
{ |
||||
|
$notAllowed = [EmailLexer::S_BACKSLASH => true, EmailLexer::S_SLASH=> true]; |
||||
|
if (isset($notAllowed[$token['type']])) { |
||||
|
throw new CharNotAllowed(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected function parseDomainLiteral() |
||||
|
{ |
||||
|
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(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return $this->doParseDomainLiteral(); |
||||
|
} |
||||
|
|
||||
|
protected function doParseDomainLiteral() |
||||
|
{ |
||||
|
$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; |
||||
|
} |
||||
|
|
||||
|
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, $index) . '0:0'; |
||||
|
} |
||||
|
|
||||
|
return $addressLiteral; |
||||
|
} |
||||
|
|
||||
|
protected function checkDomainPartExceptions($prev) |
||||
|
{ |
||||
|
$invalidDomainTokens = array( |
||||
|
EmailLexer::S_DQUOTE => 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_HYPHEN && $this->lexer->isNextToken(EmailLexer::S_DOT)) { |
||||
|
throw new DomainHyphened(); |
||||
|
} |
||||
|
|
||||
|
if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH |
||||
|
&& $this->lexer->isNextToken(EmailLexer::GENERIC)) { |
||||
|
throw new ExpectingATEXT(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected function hasBrackets() |
||||
|
{ |
||||
|
if ($this->lexer->token['type'] !== EmailLexer::S_OPENBRACKET) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
$this->lexer->find(EmailLexer::S_CLOSEBRACKET); |
||||
|
} catch (\RuntimeException $e) { |
||||
|
throw new ExpectingDomainLiteralClose(); |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
protected function checkLabelLength($prev) |
||||
|
{ |
||||
|
if ($this->lexer->token['type'] === EmailLexer::S_DOT && |
||||
|
$prev['type'] === EmailLexer::GENERIC && |
||||
|
strlen($prev['value']) > 63 |
||||
|
) { |
||||
|
$this->warnings[LabelTooLong::CODE] = new LabelTooLong(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
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(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected function addTLDWarnings() |
||||
|
{ |
||||
|
if ($this->warnings[DomainLiteral::CODE]) { |
||||
|
$this->warnings[TLD::CODE] = new TLD(); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,138 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Parser; |
||||
|
|
||||
|
use Egulias\EmailValidator\Exception\DotAtEnd; |
||||
|
use Egulias\EmailValidator\Exception\DotAtStart; |
||||
|
use Egulias\EmailValidator\EmailLexer; |
||||
|
use Egulias\EmailValidator\EmailValidator; |
||||
|
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\Warning\LocalTooLong; |
||||
|
|
||||
|
class LocalPart extends Parser |
||||
|
{ |
||||
|
public function parse($localPart) |
||||
|
{ |
||||
|
$parseDQuote = true; |
||||
|
$closingQuote = false; |
||||
|
$openedParenthesis = 0; |
||||
|
|
||||
|
while ($this->lexer->token['type'] !== EmailLexer::S_AT && $this->lexer->token) { |
||||
|
if ($this->lexer->token['type'] === EmailLexer::S_DOT && !$this->lexer->getPrevious()) { |
||||
|
throw new DotAtStart(); |
||||
|
} |
||||
|
|
||||
|
$closingQuote = $this->checkDQUOTE($closingQuote); |
||||
|
if ($closingQuote && $parseDQuote) { |
||||
|
$parseDQuote = $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(); |
||||
|
} else { |
||||
|
$openedParenthesis--; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
$this->checkConsecutiveDots(); |
||||
|
|
||||
|
if ($this->lexer->token['type'] === EmailLexer::S_DOT && |
||||
|
$this->lexer->isNextToken(EmailLexer::S_AT) |
||||
|
) { |
||||
|
throw new DotAtEnd(); |
||||
|
} |
||||
|
|
||||
|
$this->warnEscaping(); |
||||
|
$this->isInvalidToken($this->lexer->token, $closingQuote); |
||||
|
|
||||
|
if ($this->isFWS()) { |
||||
|
$this->parseFWS(); |
||||
|
} |
||||
|
|
||||
|
$this->lexer->moveNext(); |
||||
|
} |
||||
|
|
||||
|
$prev = $this->lexer->getPrevious(); |
||||
|
if (strlen($prev['value']) > LocalTooLong::LOCAL_PART_LENGTH) { |
||||
|
$this->warnings[LocalTooLong::CODE] = new LocalTooLong(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected function parseDoubleQuote() |
||||
|
{ |
||||
|
$parseAgain = true; |
||||
|
$special = array( |
||||
|
EmailLexer::S_CR => true, |
||||
|
EmailLexer::S_HTAB => true, |
||||
|
EmailLexer::S_LF => true |
||||
|
); |
||||
|
|
||||
|
$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 && $this->lexer->token) { |
||||
|
$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(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
$prev = $this->lexer->getPrevious(); |
||||
|
|
||||
|
if ($prev['type'] === EmailLexer::S_BACKSLASH) { |
||||
|
if (!$this->checkDQUOTE(false)) { |
||||
|
throw new UnclosedQuotedString(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (!$this->lexer->isNextToken(EmailLexer::S_AT) && $prev['type'] !== EmailLexer::S_BACKSLASH) { |
||||
|
throw new ExpectingAT(); |
||||
|
} |
||||
|
|
||||
|
return $parseAgain; |
||||
|
} |
||||
|
|
||||
|
protected function isInvalidToken($token, $closingQuote) |
||||
|
{ |
||||
|
$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(); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,215 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Parser; |
||||
|
|
||||
|
use Egulias\EmailValidator\EmailLexer; |
||||
|
use Egulias\EmailValidator\Exception\AtextAfterCFWS; |
||||
|
use Egulias\EmailValidator\Exception\ConsecutiveDot; |
||||
|
use Egulias\EmailValidator\Exception\CRLFAtTheEnd; |
||||
|
use Egulias\EmailValidator\Exception\CRLFX2; |
||||
|
use Egulias\EmailValidator\Exception\CRNoLF; |
||||
|
use Egulias\EmailValidator\Exception\ExpectedQPair; |
||||
|
use Egulias\EmailValidator\Exception\ExpectingATEXT; |
||||
|
use Egulias\EmailValidator\Exception\ExpectingCTEXT; |
||||
|
use Egulias\EmailValidator\Exception\UnclosedComment; |
||||
|
use Egulias\EmailValidator\Exception\UnclosedQuotedString; |
||||
|
use Egulias\EmailValidator\Warning\CFWSNearAt; |
||||
|
use Egulias\EmailValidator\Warning\CFWSWithFWS; |
||||
|
use Egulias\EmailValidator\Warning\Comment; |
||||
|
use Egulias\EmailValidator\Warning\QuotedPart; |
||||
|
use Egulias\EmailValidator\Warning\QuotedString; |
||||
|
|
||||
|
abstract class Parser |
||||
|
{ |
||||
|
protected $warnings = []; |
||||
|
protected $lexer; |
||||
|
protected $openedParenthesis = 0; |
||||
|
|
||||
|
public function __construct(EmailLexer $lexer) |
||||
|
{ |
||||
|
$this->lexer = $lexer; |
||||
|
} |
||||
|
|
||||
|
public function getWarnings() |
||||
|
{ |
||||
|
return $this->warnings; |
||||
|
} |
||||
|
|
||||
|
abstract public function parse($str); |
||||
|
|
||||
|
/** @return int */ |
||||
|
public function getOpenedParenthesis() |
||||
|
{ |
||||
|
return $this->openedParenthesis; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* validateQuotedPair |
||||
|
*/ |
||||
|
protected function validateQuotedPair() |
||||
|
{ |
||||
|
if (!($this->lexer->token['type'] === EmailLexer::INVALID |
||||
|
|| $this->lexer->token['type'] === EmailLexer::C_DEL)) { |
||||
|
throw new ExpectedQPair(); |
||||
|
} |
||||
|
|
||||
|
$this->warnings[QuotedPart::CODE] = |
||||
|
new QuotedPart($this->lexer->getPrevious()['type'], $this->lexer->token['type']); |
||||
|
} |
||||
|
|
||||
|
protected function parseComments() |
||||
|
{ |
||||
|
$this->openedParenthesis = 1; |
||||
|
$this->isUnclosedComment(); |
||||
|
$this->warnings[Comment::CODE] = new Comment(); |
||||
|
while (!$this->lexer->isNextToken(EmailLexer::S_CLOSEPARENTHESIS)) { |
||||
|
if ($this->lexer->isNextToken(EmailLexer::S_OPENPARENTHESIS)) { |
||||
|
$this->openedParenthesis++; |
||||
|
} |
||||
|
$this->warnEscaping(); |
||||
|
$this->lexer->moveNext(); |
||||
|
} |
||||
|
|
||||
|
$this->lexer->moveNext(); |
||||
|
if ($this->lexer->isNextTokenAny(array(EmailLexer::GENERIC, EmailLexer::S_EMPTY))) { |
||||
|
throw new ExpectingATEXT(); |
||||
|
} |
||||
|
|
||||
|
if ($this->lexer->isNextToken(EmailLexer::S_AT)) { |
||||
|
$this->warnings[CFWSNearAt::CODE] = new CFWSNearAt(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected function isUnclosedComment() |
||||
|
{ |
||||
|
try { |
||||
|
$this->lexer->find(EmailLexer::S_CLOSEPARENTHESIS); |
||||
|
return true; |
||||
|
} catch (\RuntimeException $e) { |
||||
|
throw new UnclosedComment(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected function parseFWS() |
||||
|
{ |
||||
|
$previous = $this->lexer->getPrevious(); |
||||
|
|
||||
|
$this->checkCRLFInFWS(); |
||||
|
|
||||
|
if ($this->lexer->token['type'] === EmailLexer::S_CR) { |
||||
|
throw new CRNoLF(); |
||||
|
} |
||||
|
|
||||
|
if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] !== EmailLexer::S_AT) { |
||||
|
throw new AtextAfterCFWS(); |
||||
|
} |
||||
|
|
||||
|
if ($this->lexer->token['type'] === EmailLexer::S_LF || $this->lexer->token['type'] === EmailLexer::C_NUL) { |
||||
|
throw new ExpectingCTEXT(); |
||||
|
} |
||||
|
|
||||
|
if ($this->lexer->isNextToken(EmailLexer::S_AT) || $previous['type'] === EmailLexer::S_AT) { |
||||
|
$this->warnings[CFWSNearAt::CODE] = new CFWSNearAt(); |
||||
|
} else { |
||||
|
$this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected function checkConsecutiveDots() |
||||
|
{ |
||||
|
if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::S_DOT)) { |
||||
|
throw new ConsecutiveDot(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected function isFWS() |
||||
|
{ |
||||
|
if ($this->escaped()) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if ($this->lexer->token['type'] === EmailLexer::S_SP || |
||||
|
$this->lexer->token['type'] === EmailLexer::S_HTAB || |
||||
|
$this->lexer->token['type'] === EmailLexer::S_CR || |
||||
|
$this->lexer->token['type'] === EmailLexer::S_LF || |
||||
|
$this->lexer->token['type'] === EmailLexer::CRLF |
||||
|
) { |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
protected function escaped() |
||||
|
{ |
||||
|
$previous = $this->lexer->getPrevious(); |
||||
|
|
||||
|
if ($previous['type'] === EmailLexer::S_BACKSLASH |
||||
|
&& |
||||
|
$this->lexer->token['type'] !== EmailLexer::GENERIC |
||||
|
) { |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
protected function warnEscaping() |
||||
|
{ |
||||
|
if ($this->lexer->token['type'] !== EmailLexer::S_BACKSLASH) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if ($this->lexer->isNextToken(EmailLexer::GENERIC)) { |
||||
|
throw new ExpectingATEXT(); |
||||
|
} |
||||
|
|
||||
|
if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB, EmailLexer::C_DEL))) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
$this->warnings[QuotedPart::CODE] = |
||||
|
new QuotedPart($this->lexer->getPrevious()['type'], $this->lexer->token['type']); |
||||
|
return true; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
protected function checkDQUOTE($hasClosingQuote) |
||||
|
{ |
||||
|
if ($this->lexer->token['type'] !== EmailLexer::S_DQUOTE) { |
||||
|
return $hasClosingQuote; |
||||
|
} |
||||
|
if ($hasClosingQuote) { |
||||
|
return $hasClosingQuote; |
||||
|
} |
||||
|
$previous = $this->lexer->getPrevious(); |
||||
|
if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] === EmailLexer::GENERIC) { |
||||
|
throw new ExpectingATEXT(); |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
$this->lexer->find(EmailLexer::S_DQUOTE); |
||||
|
$hasClosingQuote = true; |
||||
|
} catch (\Exception $e) { |
||||
|
throw new UnclosedQuotedString(); |
||||
|
} |
||||
|
$this->warnings[QuotedString::CODE] = new QuotedString($previous['value'], $this->lexer->token['value']); |
||||
|
|
||||
|
return $hasClosingQuote; |
||||
|
} |
||||
|
|
||||
|
protected function checkCRLFInFWS() |
||||
|
{ |
||||
|
if ($this->lexer->token['type'] !== EmailLexer::CRLF) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) { |
||||
|
throw new CRLFX2(); |
||||
|
} |
||||
|
|
||||
|
if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) { |
||||
|
throw new CRLFAtTheEnd(); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,61 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Validation; |
||||
|
|
||||
|
use Egulias\EmailValidator\EmailLexer; |
||||
|
use Egulias\EmailValidator\Exception\InvalidEmail; |
||||
|
use Egulias\EmailValidator\Warning\NoDNSMXRecord; |
||||
|
use Egulias\EmailValidator\Exception\NoDNSRecord; |
||||
|
|
||||
|
class DNSCheckValidation implements EmailValidation |
||||
|
{ |
||||
|
/** |
||||
|
* @var array |
||||
|
*/ |
||||
|
private $warnings = []; |
||||
|
|
||||
|
/** |
||||
|
* @var InvalidEmail |
||||
|
*/ |
||||
|
private $error; |
||||
|
|
||||
|
public function isValid($email, EmailLexer $emailLexer) |
||||
|
{ |
||||
|
// use the input to check DNS if we cannot extract something similar to a domain |
||||
|
$host = $email; |
||||
|
|
||||
|
// Arguable pattern to extract the domain. Not aiming to validate the domain nor the email |
||||
|
if (false !== $lastAtPos = strrpos($email, '@')) { |
||||
|
$host = substr($email, $lastAtPos + 1); |
||||
|
} |
||||
|
|
||||
|
return $this->checkDNS($host); |
||||
|
} |
||||
|
|
||||
|
public function getError() |
||||
|
{ |
||||
|
return $this->error; |
||||
|
} |
||||
|
|
||||
|
public function getWarnings() |
||||
|
{ |
||||
|
return $this->warnings; |
||||
|
} |
||||
|
|
||||
|
protected function checkDNS($host) |
||||
|
{ |
||||
|
$host = rtrim($host, '.') . '.'; |
||||
|
|
||||
|
$Aresult = true; |
||||
|
$MXresult = checkdnsrr($host, 'MX'); |
||||
|
|
||||
|
if (!$MXresult) { |
||||
|
$this->warnings[NoDNSMXRecord::CODE] = new NoDNSMXRecord(); |
||||
|
$Aresult = checkdnsrr($host, 'A') || checkdnsrr($host, 'AAAA'); |
||||
|
if (!$Aresult) { |
||||
|
$this->error = new NoDNSRecord(); |
||||
|
} |
||||
|
} |
||||
|
return $MXresult || $Aresult; |
||||
|
} |
||||
|
} |
@ -0,0 +1,34 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Validation; |
||||
|
|
||||
|
use Egulias\EmailValidator\EmailLexer; |
||||
|
use Egulias\EmailValidator\Exception\InvalidEmail; |
||||
|
use Egulias\EmailValidator\Warning\Warning; |
||||
|
|
||||
|
interface EmailValidation |
||||
|
{ |
||||
|
/** |
||||
|
* Returns true if the given email is valid. |
||||
|
* |
||||
|
* @param string $email The email you want to validate. |
||||
|
* @param EmailLexer $emailLexer The email lexer. |
||||
|
* |
||||
|
* @return bool |
||||
|
*/ |
||||
|
public function isValid($email, EmailLexer $emailLexer); |
||||
|
|
||||
|
/** |
||||
|
* Returns the validation error. |
||||
|
* |
||||
|
* @return InvalidEmail|null |
||||
|
*/ |
||||
|
public function getError(); |
||||
|
|
||||
|
/** |
||||
|
* Returns the validation warnings. |
||||
|
* |
||||
|
* @return Warning[] |
||||
|
*/ |
||||
|
public function getWarnings(); |
||||
|
} |
@ -0,0 +1,11 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Validation\Error; |
||||
|
|
||||
|
use Egulias\EmailValidator\Exception\InvalidEmail; |
||||
|
|
||||
|
class RFCWarnings extends InvalidEmail |
||||
|
{ |
||||
|
const CODE = 997; |
||||
|
const REASON = 'Warnings were found.'; |
||||
|
} |
@ -0,0 +1,11 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Validation\Error; |
||||
|
|
||||
|
use Egulias\EmailValidator\Exception\InvalidEmail; |
||||
|
|
||||
|
class SpoofEmail extends InvalidEmail |
||||
|
{ |
||||
|
const CODE = 998; |
||||
|
const REASON = "The email contains mixed UTF8 chars that makes it suspicious"; |
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Validation\Exception; |
||||
|
|
||||
|
use Exception; |
||||
|
|
||||
|
class EmptyValidationList extends \InvalidArgumentException |
||||
|
{ |
||||
|
public function __construct($code = 0, Exception $previous = null) |
||||
|
{ |
||||
|
parent::__construct("Empty validation list is not allowed", $code, $previous); |
||||
|
} |
||||
|
} |
@ -0,0 +1,26 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Validation; |
||||
|
|
||||
|
use Egulias\EmailValidator\Exception\InvalidEmail; |
||||
|
|
||||
|
class MultipleErrors extends InvalidEmail |
||||
|
{ |
||||
|
const CODE = 999; |
||||
|
const REASON = "Accumulated errors for multiple validations"; |
||||
|
/** |
||||
|
* @var array |
||||
|
*/ |
||||
|
private $errors = []; |
||||
|
|
||||
|
public function __construct(array $errors) |
||||
|
{ |
||||
|
$this->errors = $errors; |
||||
|
parent::__construct(); |
||||
|
} |
||||
|
|
||||
|
public function getErrors() |
||||
|
{ |
||||
|
return $this->errors; |
||||
|
} |
||||
|
} |
@ -0,0 +1,110 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Validation; |
||||
|
|
||||
|
use Egulias\EmailValidator\EmailLexer; |
||||
|
use Egulias\EmailValidator\Validation\Exception\EmptyValidationList; |
||||
|
|
||||
|
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. |
||||
|
*/ |
||||
|
const STOP_ON_ERROR = 0; |
||||
|
|
||||
|
/** |
||||
|
* All of validations will be invoked even if one of them got failure. |
||||
|
* So MultipleErrors will contain all causes. |
||||
|
*/ |
||||
|
const ALLOW_ALL_ERRORS = 1; |
||||
|
|
||||
|
/** |
||||
|
* @var EmailValidation[] |
||||
|
*/ |
||||
|
private $validations = []; |
||||
|
|
||||
|
/** |
||||
|
* @var array |
||||
|
*/ |
||||
|
private $warnings = []; |
||||
|
|
||||
|
/** |
||||
|
* @var MultipleErrors |
||||
|
*/ |
||||
|
private $error; |
||||
|
|
||||
|
/** |
||||
|
* @var bool |
||||
|
*/ |
||||
|
private $mode; |
||||
|
|
||||
|
/** |
||||
|
* @param EmailValidation[] $validations The validations. |
||||
|
* @param int $mode The validation mode (one of the constants). |
||||
|
*/ |
||||
|
public function __construct(array $validations, $mode = self::ALLOW_ALL_ERRORS) |
||||
|
{ |
||||
|
if (count($validations) == 0) { |
||||
|
throw new EmptyValidationList(); |
||||
|
} |
||||
|
|
||||
|
$this->validations = $validations; |
||||
|
$this->mode = $mode; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* {@inheritdoc} |
||||
|
*/ |
||||
|
public function isValid($email, EmailLexer $emailLexer) |
||||
|
{ |
||||
|
$result = true; |
||||
|
$errors = []; |
||||
|
foreach ($this->validations as $validation) { |
||||
|
$emailLexer->reset(); |
||||
|
$result = $result && $validation->isValid($email, $emailLexer); |
||||
|
$this->warnings = array_merge($this->warnings, $validation->getWarnings()); |
||||
|
$errors = $this->addNewError($validation->getError(), $errors); |
||||
|
|
||||
|
if ($this->shouldStop($result)) { |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (!empty($errors)) { |
||||
|
$this->error = new MultipleErrors($errors); |
||||
|
} |
||||
|
|
||||
|
return $result; |
||||
|
} |
||||
|
|
||||
|
private function addNewError($possibleError, array $errors) |
||||
|
{ |
||||
|
if (null !== $possibleError) { |
||||
|
$errors[] = $possibleError; |
||||
|
} |
||||
|
|
||||
|
return $errors; |
||||
|
} |
||||
|
|
||||
|
private function shouldStop($result) |
||||
|
{ |
||||
|
return !$result && $this->mode === self::STOP_ON_ERROR; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* {@inheritdoc} |
||||
|
*/ |
||||
|
public function getError() |
||||
|
{ |
||||
|
return $this->error; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* {@inheritdoc} |
||||
|
*/ |
||||
|
public function getWarnings() |
||||
|
{ |
||||
|
return $this->warnings; |
||||
|
} |
||||
|
} |
@ -0,0 +1,41 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Validation; |
||||
|
|
||||
|
use Egulias\EmailValidator\EmailLexer; |
||||
|
use Egulias\EmailValidator\Exception\InvalidEmail; |
||||
|
use Egulias\EmailValidator\Validation\Error\RFCWarnings; |
||||
|
|
||||
|
class NoRFCWarningsValidation extends RFCValidation |
||||
|
{ |
||||
|
/** |
||||
|
* @var InvalidEmail |
||||
|
*/ |
||||
|
private $error; |
||||
|
|
||||
|
/** |
||||
|
* {@inheritdoc} |
||||
|
*/ |
||||
|
public function isValid($email, EmailLexer $emailLexer) |
||||
|
{ |
||||
|
if (!parent::isValid($email, $emailLexer)) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (empty($this->getWarnings())) { |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
$this->error = new RFCWarnings(); |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* {@inheritdoc} |
||||
|
*/ |
||||
|
public function getError() |
||||
|
{ |
||||
|
return $this->error ?: parent::getError(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,49 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Validation; |
||||
|
|
||||
|
use Egulias\EmailValidator\EmailLexer; |
||||
|
use Egulias\EmailValidator\EmailParser; |
||||
|
use Egulias\EmailValidator\Exception\InvalidEmail; |
||||
|
|
||||
|
class RFCValidation implements EmailValidation |
||||
|
{ |
||||
|
/** |
||||
|
* @var EmailParser |
||||
|
*/ |
||||
|
private $parser; |
||||
|
|
||||
|
/** |
||||
|
* @var array |
||||
|
*/ |
||||
|
private $warnings = []; |
||||
|
|
||||
|
/** |
||||
|
* @var InvalidEmail |
||||
|
*/ |
||||
|
private $error; |
||||
|
|
||||
|
public function isValid($email, EmailLexer $emailLexer) |
||||
|
{ |
||||
|
$this->parser = new EmailParser($emailLexer); |
||||
|
try { |
||||
|
$this->parser->parse((string)$email); |
||||
|
} catch (InvalidEmail $invalid) { |
||||
|
$this->error = $invalid; |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
$this->warnings = $this->parser->getWarnings(); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
public function getError() |
||||
|
{ |
||||
|
return $this->error; |
||||
|
} |
||||
|
|
||||
|
public function getWarnings() |
||||
|
{ |
||||
|
return $this->warnings; |
||||
|
} |
||||
|
} |
@ -0,0 +1,45 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Validation; |
||||
|
|
||||
|
use Egulias\EmailValidator\EmailLexer; |
||||
|
use Egulias\EmailValidator\Exception\InvalidEmail; |
||||
|
use Egulias\EmailValidator\Validation\Error\SpoofEmail; |
||||
|
use \Spoofchecker; |
||||
|
|
||||
|
class SpoofCheckValidation implements EmailValidation |
||||
|
{ |
||||
|
/** |
||||
|
* @var InvalidEmail |
||||
|
*/ |
||||
|
private $error; |
||||
|
|
||||
|
public function __construct() |
||||
|
{ |
||||
|
if (!class_exists(Spoofchecker::class)) { |
||||
|
throw new \LogicException(sprintf('The %s class requires the Intl extension.', __CLASS__)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public function isValid($email, EmailLexer $emailLexer) |
||||
|
{ |
||||
|
$checker = new Spoofchecker(); |
||||
|
$checker->setChecks(Spoofchecker::SINGLE_SCRIPT); |
||||
|
|
||||
|
if ($checker->isSuspicious($email)) { |
||||
|
$this->error = new SpoofEmail(); |
||||
|
} |
||||
|
|
||||
|
return $this->error === null; |
||||
|
} |
||||
|
|
||||
|
public function getError() |
||||
|
{ |
||||
|
return $this->error; |
||||
|
} |
||||
|
|
||||
|
public function getWarnings() |
||||
|
{ |
||||
|
return []; |
||||
|
} |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Warning; |
||||
|
|
||||
|
class AddressLiteral extends Warning |
||||
|
{ |
||||
|
const CODE = 12; |
||||
|
|
||||
|
public function __construct() |
||||
|
{ |
||||
|
$this->message = 'Address literal in domain part'; |
||||
|
$this->rfcNumber = 5321; |
||||
|
} |
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Warning; |
||||
|
|
||||
|
class CFWSNearAt extends Warning |
||||
|
{ |
||||
|
const CODE = 49; |
||||
|
|
||||
|
public function __construct() |
||||
|
{ |
||||
|
$this->message = "Deprecated folding white space near @"; |
||||
|
} |
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Warning; |
||||
|
|
||||
|
class CFWSWithFWS extends Warning |
||||
|
{ |
||||
|
const CODE = 18; |
||||
|
|
||||
|
public function __construct() |
||||
|
{ |
||||
|
$this->message = 'Folding whites space followed by folding white space'; |
||||
|
} |
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Warning; |
||||
|
|
||||
|
class Comment extends Warning |
||||
|
{ |
||||
|
const CODE = 17; |
||||
|
|
||||
|
public function __construct() |
||||
|
{ |
||||
|
$this->message = "Comments found in this email"; |
||||
|
} |
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Warning; |
||||
|
|
||||
|
class DeprecatedComment extends Warning |
||||
|
{ |
||||
|
const CODE = 37; |
||||
|
|
||||
|
public function __construct() |
||||
|
{ |
||||
|
$this->message = 'Deprecated comments'; |
||||
|
} |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Warning; |
||||
|
|
||||
|
class DomainLiteral extends Warning |
||||
|
{ |
||||
|
const CODE = 70; |
||||
|
|
||||
|
public function __construct() |
||||
|
{ |
||||
|
$this->message = 'Domain Literal'; |
||||
|
$this->rfcNumber = 5322; |
||||
|
} |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Warning; |
||||
|
|
||||
|
class DomainTooLong extends Warning |
||||
|
{ |
||||
|
const CODE = 255; |
||||
|
|
||||
|
public function __construct() |
||||
|
{ |
||||
|
$this->message = 'Domain is too long, exceeds 255 chars'; |
||||
|
$this->rfcNumber = 5322; |
||||
|
} |
||||
|
} |
@ -0,0 +1,15 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Warning; |
||||
|
|
||||
|
use Egulias\EmailValidator\EmailParser; |
||||
|
|
||||
|
class EmailTooLong extends Warning |
||||
|
{ |
||||
|
const CODE = 66; |
||||
|
|
||||
|
public function __construct() |
||||
|
{ |
||||
|
$this->message = 'Email is too long, exceeds ' . EmailParser::EMAIL_MAX_LENGTH; |
||||
|
} |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Warning; |
||||
|
|
||||
|
class IPV6BadChar extends Warning |
||||
|
{ |
||||
|
const CODE = 74; |
||||
|
|
||||
|
public function __construct() |
||||
|
{ |
||||
|
$this->message = 'Bad char in IPV6 domain literal'; |
||||
|
$this->rfcNumber = 5322; |
||||
|
} |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Warning; |
||||
|
|
||||
|
class IPV6ColonEnd extends Warning |
||||
|
{ |
||||
|
const CODE = 77; |
||||
|
|
||||
|
public function __construct() |
||||
|
{ |
||||
|
$this->message = ':: found at the end of the domain literal'; |
||||
|
$this->rfcNumber = 5322; |
||||
|
} |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Warning; |
||||
|
|
||||
|
class IPV6ColonStart extends Warning |
||||
|
{ |
||||
|
const CODE = 76; |
||||
|
|
||||
|
public function __construct() |
||||
|
{ |
||||
|
$this->message = ':: found at the start of the domain literal'; |
||||
|
$this->rfcNumber = 5322; |
||||
|
} |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Warning; |
||||
|
|
||||
|
class IPV6Deprecated extends Warning |
||||
|
{ |
||||
|
const CODE = 13; |
||||
|
|
||||
|
public function __construct() |
||||
|
{ |
||||
|
$this->message = 'Deprecated form of IPV6'; |
||||
|
$this->rfcNumber = 5321; |
||||
|
} |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Warning; |
||||
|
|
||||
|
class IPV6DoubleColon extends Warning |
||||
|
{ |
||||
|
const CODE = 73; |
||||
|
|
||||
|
public function __construct() |
||||
|
{ |
||||
|
$this->message = 'Double colon found after IPV6 tag'; |
||||
|
$this->rfcNumber = 5322; |
||||
|
} |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Warning; |
||||
|
|
||||
|
class IPV6GroupCount extends Warning |
||||
|
{ |
||||
|
const CODE = 72; |
||||
|
|
||||
|
public function __construct() |
||||
|
{ |
||||
|
$this->message = 'Group count is not IPV6 valid'; |
||||
|
$this->rfcNumber = 5322; |
||||
|
} |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Warning; |
||||
|
|
||||
|
class IPV6MaxGroups extends Warning |
||||
|
{ |
||||
|
const CODE = 75; |
||||
|
|
||||
|
public function __construct() |
||||
|
{ |
||||
|
$this->message = 'Reached the maximum number of IPV6 groups allowed'; |
||||
|
$this->rfcNumber = 5321; |
||||
|
} |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Warning; |
||||
|
|
||||
|
class LabelTooLong extends Warning |
||||
|
{ |
||||
|
const CODE = 63; |
||||
|
|
||||
|
public function __construct() |
||||
|
{ |
||||
|
$this->message = 'Label too long'; |
||||
|
$this->rfcNumber = 5322; |
||||
|
} |
||||
|
} |
@ -0,0 +1,15 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Warning; |
||||
|
|
||||
|
class LocalTooLong extends Warning |
||||
|
{ |
||||
|
const CODE = 64; |
||||
|
const LOCAL_PART_LENGTH = 64; |
||||
|
|
||||
|
public function __construct() |
||||
|
{ |
||||
|
$this->message = 'Local part is too long, exceeds 64 chars (octets)'; |
||||
|
$this->rfcNumber = 5322; |
||||
|
} |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Warning; |
||||
|
|
||||
|
class NoDNSMXRecord extends Warning |
||||
|
{ |
||||
|
const CODE = 6; |
||||
|
|
||||
|
public function __construct() |
||||
|
{ |
||||
|
$this->message = 'No MX DSN record was found for this email'; |
||||
|
$this->rfcNumber = 5321; |
||||
|
} |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Warning; |
||||
|
|
||||
|
class ObsoleteDTEXT extends Warning |
||||
|
{ |
||||
|
const CODE = 71; |
||||
|
|
||||
|
public function __construct() |
||||
|
{ |
||||
|
$this->rfcNumber = 5322; |
||||
|
$this->message = 'Obsolete DTEXT in domain literal'; |
||||
|
} |
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Warning; |
||||
|
|
||||
|
class QuotedPart extends Warning |
||||
|
{ |
||||
|
const CODE = 36; |
||||
|
|
||||
|
public function __construct($prevToken, $postToken) |
||||
|
{ |
||||
|
$this->message = "Deprecated Quoted String found between $prevToken and $postToken"; |
||||
|
} |
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Egulias\EmailValidator\Warning; |
||||
|
|
||||
|
class QuotedString extends Warning |
||||
|
{ |
||||
|
const CODE = 11; |
||||
|
|
||||
|
public function __construct($prevToken, $postToken) |
||||
|
{ |
||||
|
$this->message = "Quoted String found between $prevToken and $postToken"; |
||||
|
} |
||||
|
} |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue