v1.0 Initial commit of project

This commit is contained in:
2026-01-01 10:54:18 +01:00
commit 768cf78b57
990 changed files with 241213 additions and 0 deletions

View File

@@ -0,0 +1 @@
/.git* export-ignore

View File

@@ -0,0 +1,8 @@
Please do not submit any Pull Requests here. They will be closed.
---
Please submit your PR here instead:
https://github.com/symfony/symfony
This repository is what we call a "subtree split": a read-only subset of that main repository.
We're looking forward to your PR there!

View File

@@ -0,0 +1,20 @@
name: Close Pull Request
on:
pull_request_target:
types: [opened]
jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: superbrothers/close-pull-request@v3
with:
comment: |
Thanks for your Pull Request! We love contributions.
However, you should instead open your PR on the main repository:
https://github.com/symfony/symfony
This repository is what we call a "subtree split": a read-only subset of that main repository.
We're looking forward to your PR there!

View File

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

View File

@@ -0,0 +1,5 @@
CHANGELOG
=========
The changelog is maintained for all Symfony contracts at the following URL:
https://github.com/symfony/contracts/blob/main/CHANGELOG.md

View File

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

View File

@@ -0,0 +1,26 @@
Symfony Deprecation Contracts
=============================
A generic function and convention to trigger deprecation notices.
This package provides a single global function named `trigger_deprecation()` that triggers silenced deprecation notices.
By using a custom PHP error handler such as the one provided by the Symfony ErrorHandler component,
the triggered deprecations can be caught and logged for later discovery, both on dev and prod environments.
The function requires at least 3 arguments:
- the name of the Composer package that is triggering the deprecation
- the version of the package that introduced the deprecation
- the message of the deprecation
- more arguments can be provided: they will be inserted in the message using `printf()` formatting
Example:
```php
trigger_deprecation('symfony/blockchain', '8.9', 'Using "%s" is deprecated, use "%s" instead.', 'bitcoin', 'fabcoin');
```
This will generate the following message:
`Since symfony/blockchain 8.9: Using "bitcoin" is deprecated, use "fabcoin" instead.`
While not recommended, the deprecation notices can be completely ignored by declaring an empty
`function trigger_deprecation() {}` in your application.

View File

@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (!function_exists('trigger_deprecation')) {
/**
* Triggers a silenced deprecation notice.
*
* @param string $package The name of the Composer package that is triggering the deprecation
* @param string $version The version of the package that introduced the deprecation
* @param string $message The message of the deprecation
* @param mixed ...$args Values to insert in the message using printf() formatting
*
* @author Nicolas Grekas <p@tchwork.com>
*/
function trigger_deprecation(string $package, string $version, string $message, mixed ...$args): void
{
@trigger_error(($package || $version ? "Since $package $version: " : '').($args ? vsprintf($message, $args) : $message), \E_USER_DEPRECATED);
}
}

View File

@@ -0,0 +1,3 @@
/Tests export-ignore
/phpunit.xml.dist export-ignore
/.git* export-ignore

View File

@@ -0,0 +1,8 @@
Please do not submit any Pull Requests here. They will be closed.
---
Please submit your PR here instead:
https://github.com/symfony/symfony
This repository is what we call a "subtree split": a read-only subset of that main repository.
We're looking forward to your PR there!

View File

@@ -0,0 +1,20 @@
name: Close Pull Request
on:
pull_request_target:
types: [opened]
jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: superbrothers/close-pull-request@v3
with:
comment: |
Thanks for your Pull Request! We love contributions.
However, you should instead open your PR on the main repository:
https://github.com/symfony/symfony
This repository is what we call a "subtree split": a read-only subset of that main repository.
We're looking forward to your PR there!

View File

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

View File

@@ -0,0 +1,81 @@
CHANGELOG
=========
5.3
---
* Add prototype definition for nested options
5.1.0
-----
* added fluent configuration of options using `OptionResolver::define()`
* added `setInfo()` and `getInfo()` methods
* updated the signature of method `OptionsResolver::setDeprecated()` to `OptionsResolver::setDeprecation(string $option, string $package, string $version, $message)`
* deprecated `OptionsResolverIntrospector::getDeprecationMessage()`, use `OptionsResolverIntrospector::getDeprecation()` instead
5.0.0
-----
* added argument `$triggerDeprecation` to `OptionsResolver::offsetGet()`
4.3.0
-----
* added `OptionsResolver::addNormalizer` method
4.2.0
-----
* added support for nested options definition
* added `setDeprecated` and `isDeprecated` methods
3.4.0
-----
* added `OptionsResolverIntrospector` to inspect options definitions inside an `OptionsResolver` instance
* added array of types support in allowed types (e.g int[])
2.6.0
-----
* deprecated OptionsResolverInterface
* [BC BREAK] removed "array" type hint from OptionsResolverInterface methods
setRequired(), setAllowedValues(), addAllowedValues(), setAllowedTypes() and
addAllowedTypes()
* added OptionsResolver::setDefault()
* added OptionsResolver::hasDefault()
* added OptionsResolver::setNormalizer()
* added OptionsResolver::isRequired()
* added OptionsResolver::getRequiredOptions()
* added OptionsResolver::isMissing()
* added OptionsResolver::getMissingOptions()
* added OptionsResolver::setDefined()
* added OptionsResolver::isDefined()
* added OptionsResolver::getDefinedOptions()
* added OptionsResolver::remove()
* added OptionsResolver::clear()
* deprecated OptionsResolver::replaceDefaults()
* deprecated OptionsResolver::setOptional() in favor of setDefined()
* deprecated OptionsResolver::isKnown() in favor of isDefined()
* [BC BREAK] OptionsResolver::isRequired() returns true now if a required
option has a default value set
* [BC BREAK] merged Options into OptionsResolver and turned Options into an
interface
* deprecated Options::overload() (now in OptionsResolver)
* deprecated Options::set() (now in OptionsResolver)
* deprecated Options::get() (now in OptionsResolver)
* deprecated Options::has() (now in OptionsResolver)
* deprecated Options::replace() (now in OptionsResolver)
* [BC BREAK] Options::get() (now in OptionsResolver) can only be used within
lazy option/normalizer closures now
* [BC BREAK] removed Traversable interface from Options since using within
lazy option/normalizer closures resulted in exceptions
* [BC BREAK] removed Options::all() since using within lazy option/normalizer
closures resulted in exceptions
* [BC BREAK] OptionDefinitionException now extends LogicException instead of
RuntimeException
* [BC BREAK] normalizers are not executed anymore for unset options
* normalizers are executed after validating the options now
* [BC BREAK] an UndefinedOptionsException is now thrown instead of an
InvalidOptionsException when non-existing options are passed

View File

@@ -0,0 +1,120 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\OptionsResolver\Debug;
use Symfony\Component\OptionsResolver\Exception\NoConfigurationException;
use Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
*
* @final
*/
class OptionsResolverIntrospector
{
private $get;
public function __construct(OptionsResolver $optionsResolver)
{
$this->get = \Closure::bind(function ($property, $option, $message) {
/** @var OptionsResolver $this */
if (!$this->isDefined($option)) {
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist.', $option));
}
if (!\array_key_exists($option, $this->{$property})) {
throw new NoConfigurationException($message);
}
return $this->{$property}[$option];
}, $optionsResolver, $optionsResolver);
}
/**
* @return mixed
*
* @throws NoConfigurationException on no configured value
*/
public function getDefault(string $option)
{
return ($this->get)('defaults', $option, sprintf('No default value was set for the "%s" option.', $option));
}
/**
* @return \Closure[]
*
* @throws NoConfigurationException on no configured closures
*/
public function getLazyClosures(string $option): array
{
return ($this->get)('lazy', $option, sprintf('No lazy closures were set for the "%s" option.', $option));
}
/**
* @return string[]
*
* @throws NoConfigurationException on no configured types
*/
public function getAllowedTypes(string $option): array
{
return ($this->get)('allowedTypes', $option, sprintf('No allowed types were set for the "%s" option.', $option));
}
/**
* @return mixed[]
*
* @throws NoConfigurationException on no configured values
*/
public function getAllowedValues(string $option): array
{
return ($this->get)('allowedValues', $option, sprintf('No allowed values were set for the "%s" option.', $option));
}
/**
* @throws NoConfigurationException on no configured normalizer
*/
public function getNormalizer(string $option): \Closure
{
return current($this->getNormalizers($option));
}
/**
* @throws NoConfigurationException when no normalizer is configured
*/
public function getNormalizers(string $option): array
{
return ($this->get)('normalizers', $option, sprintf('No normalizer was set for the "%s" option.', $option));
}
/**
* @return string|\Closure
*
* @throws NoConfigurationException on no configured deprecation
*
* @deprecated since Symfony 5.1, use "getDeprecation()" instead.
*/
public function getDeprecationMessage(string $option)
{
trigger_deprecation('symfony/options-resolver', '5.1', 'The "%s()" method is deprecated, use "getDeprecation()" instead.', __METHOD__);
return $this->getDeprecation($option)['message'];
}
/**
* @throws NoConfigurationException on no configured deprecation
*/
public function getDeprecation(string $option): array
{
return ($this->get)('deprecated', $option, sprintf('No deprecation was set for the "%s" option.', $option));
}
}

View File

@@ -0,0 +1,22 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\OptionsResolver\Exception;
/**
* Thrown when trying to read an option outside of or write it inside of
* {@link \Symfony\Component\OptionsResolver\Options::resolve()}.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class AccessException extends \LogicException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\OptionsResolver\Exception;
/**
* Marker interface for all exceptions thrown by the OptionsResolver component.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
interface ExceptionInterface extends \Throwable
{
}

View File

@@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\OptionsResolver\Exception;
/**
* Thrown when an argument is invalid.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,23 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\OptionsResolver\Exception;
/**
* Thrown when the value of an option does not match its validation rules.
*
* You should make sure a valid value is passed to the option.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class InvalidOptionsException extends InvalidArgumentException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\OptionsResolver\Exception;
/**
* Exception thrown when a required option is missing.
*
* Add the option to the passed options array.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class MissingOptionsException extends InvalidArgumentException
{
}

View File

@@ -0,0 +1,26 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\OptionsResolver\Exception;
use Symfony\Component\OptionsResolver\Debug\OptionsResolverIntrospector;
/**
* Thrown when trying to introspect an option definition property
* for which no value was configured inside the OptionsResolver instance.
*
* @see OptionsResolverIntrospector
*
* @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
*/
class NoConfigurationException extends \RuntimeException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,26 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\OptionsResolver\Exception;
/**
* Thrown when trying to read an option that has no value set.
*
* When accessing optional options from within a lazy option or normalizer you should first
* check whether the optional option is set. You can do this with `isset($options['optional'])`.
* In contrast to the {@link UndefinedOptionsException}, this is a runtime exception that can
* occur when evaluating lazy options.
*
* @author Tobias Schultze <http://tobion.de>
*/
class NoSuchOptionException extends \OutOfBoundsException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\OptionsResolver\Exception;
/**
* Thrown when two lazy options have a cyclic dependency.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class OptionDefinitionException extends \LogicException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\OptionsResolver\Exception;
/**
* Exception thrown when an undefined option is passed.
*
* You should remove the options in question from your code or define them
* beforehand.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class UndefinedOptionsException extends InvalidArgumentException
{
}

19
vendor/symfony/options-resolver/LICENSE vendored Normal file
View File

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

View File

@@ -0,0 +1,139 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\OptionsResolver;
use Symfony\Component\OptionsResolver\Exception\AccessException;
final class OptionConfigurator
{
private $name;
private $resolver;
public function __construct(string $name, OptionsResolver $resolver)
{
$this->name = $name;
$this->resolver = $resolver;
$this->resolver->setDefined($name);
}
/**
* Adds allowed types for this option.
*
* @return $this
*
* @throws AccessException If called from a lazy option or normalizer
*/
public function allowedTypes(string ...$types): self
{
$this->resolver->setAllowedTypes($this->name, $types);
return $this;
}
/**
* Sets allowed values for this option.
*
* @param mixed ...$values One or more acceptable values/closures
*
* @return $this
*
* @throws AccessException If called from a lazy option or normalizer
*/
public function allowedValues(...$values): self
{
$this->resolver->setAllowedValues($this->name, $values);
return $this;
}
/**
* Sets the default value for this option.
*
* @param mixed $value The default value of the option
*
* @return $this
*
* @throws AccessException If called from a lazy option or normalizer
*/
public function default($value): self
{
$this->resolver->setDefault($this->name, $value);
return $this;
}
/**
* Defines an option configurator with the given name.
*/
public function define(string $option): self
{
return $this->resolver->define($option);
}
/**
* Marks this option as deprecated.
*
* @param string $package The name of the composer package that is triggering the deprecation
* @param string $version The version of the package that introduced the deprecation
* @param string|\Closure $message The deprecation message to use
*
* @return $this
*/
public function deprecated(string $package, string $version, $message = 'The option "%name%" is deprecated.'): self
{
$this->resolver->setDeprecated($this->name, $package, $version, $message);
return $this;
}
/**
* Sets the normalizer for this option.
*
* @return $this
*
* @throws AccessException If called from a lazy option or normalizer
*/
public function normalize(\Closure $normalizer): self
{
$this->resolver->setNormalizer($this->name, $normalizer);
return $this;
}
/**
* Marks this option as required.
*
* @return $this
*
* @throws AccessException If called from a lazy option or normalizer
*/
public function required(): self
{
$this->resolver->setRequired($this->name);
return $this;
}
/**
* Sets an info message for an option.
*
* @return $this
*
* @throws AccessException If called from a lazy option or normalizer
*/
public function info(string $info): self
{
$this->resolver->setInfo($this->name, $info);
return $this;
}
}

View File

@@ -0,0 +1,22 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\OptionsResolver;
/**
* Contains resolved option values.
*
* @author Bernhard Schussek <bschussek@gmail.com>
* @author Tobias Schultze <http://tobion.de>
*/
interface Options extends \ArrayAccess, \Countable
{
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,15 @@
OptionsResolver Component
=========================
The OptionsResolver component is `array_replace` on steroids. It allows you to
create an options system with required options, defaults, validation (type,
value), normalization and more.
Resources
---------
* [Documentation](https://symfony.com/doc/current/components/options_resolver.html)
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)
in the [main Symfony repository](https://github.com/symfony/symfony)

View File

@@ -0,0 +1,292 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\OptionsResolver\Tests\Debug;
use PHPUnit\Framework\TestCase;
use Symfony\Component\OptionsResolver\Debug\OptionsResolverIntrospector;
use Symfony\Component\OptionsResolver\Exception\NoConfigurationException;
use Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
class OptionsResolverIntrospectorTest extends TestCase
{
public function testGetDefault()
{
$resolver = new OptionsResolver();
$resolver->setDefault($option = 'foo', 'bar');
$debug = new OptionsResolverIntrospector($resolver);
$this->assertSame('bar', $debug->getDefault($option));
}
public function testGetDefaultNull()
{
$resolver = new OptionsResolver();
$resolver->setDefault($option = 'foo', null);
$debug = new OptionsResolverIntrospector($resolver);
$this->assertNull($debug->getDefault($option));
}
public function testGetDefaultThrowsOnNoConfiguredValue()
{
$this->expectException(NoConfigurationException::class);
$this->expectExceptionMessage('No default value was set for the "foo" option.');
$resolver = new OptionsResolver();
$resolver->setDefined($option = 'foo');
$debug = new OptionsResolverIntrospector($resolver);
$debug->getDefault($option);
}
public function testGetDefaultThrowsOnNotDefinedOption()
{
$this->expectException(UndefinedOptionsException::class);
$this->expectExceptionMessage('The option "foo" does not exist.');
$resolver = new OptionsResolver();
$debug = new OptionsResolverIntrospector($resolver);
$debug->getDefault('foo');
}
public function testGetLazyClosures()
{
$resolver = new OptionsResolver();
$closures = [];
$resolver->setDefault($option = 'foo', $closures[] = function (Options $options) {});
$debug = new OptionsResolverIntrospector($resolver);
$this->assertSame($closures, $debug->getLazyClosures($option));
}
public function testGetLazyClosuresThrowsOnNoConfiguredValue()
{
$this->expectException(NoConfigurationException::class);
$this->expectExceptionMessage('No lazy closures were set for the "foo" option.');
$resolver = new OptionsResolver();
$resolver->setDefined($option = 'foo');
$debug = new OptionsResolverIntrospector($resolver);
$debug->getLazyClosures($option);
}
public function testGetLazyClosuresThrowsOnNotDefinedOption()
{
$this->expectException(UndefinedOptionsException::class);
$this->expectExceptionMessage('The option "foo" does not exist.');
$resolver = new OptionsResolver();
$debug = new OptionsResolverIntrospector($resolver);
$debug->getLazyClosures('foo');
}
public function testGetAllowedTypes()
{
$resolver = new OptionsResolver();
$resolver->setDefined($option = 'foo');
$resolver->setAllowedTypes($option = 'foo', $allowedTypes = ['string', 'bool']);
$debug = new OptionsResolverIntrospector($resolver);
$this->assertSame($allowedTypes, $debug->getAllowedTypes($option));
}
public function testGetAllowedTypesThrowsOnNoConfiguredValue()
{
$this->expectException(NoConfigurationException::class);
$this->expectExceptionMessage('No allowed types were set for the "foo" option.');
$resolver = new OptionsResolver();
$resolver->setDefined($option = 'foo');
$debug = new OptionsResolverIntrospector($resolver);
$this->assertSame('bar', $debug->getAllowedTypes($option));
}
public function testGetAllowedTypesThrowsOnNotDefinedOption()
{
$this->expectException(UndefinedOptionsException::class);
$this->expectExceptionMessage('The option "foo" does not exist.');
$resolver = new OptionsResolver();
$debug = new OptionsResolverIntrospector($resolver);
$this->assertSame('bar', $debug->getAllowedTypes('foo'));
}
public function testGetAllowedValues()
{
$resolver = new OptionsResolver();
$resolver->setDefined($option = 'foo');
$resolver->setAllowedValues($option = 'foo', $allowedValues = ['bar', 'baz']);
$debug = new OptionsResolverIntrospector($resolver);
$this->assertSame($allowedValues, $debug->getAllowedValues($option));
}
public function testGetAllowedValuesThrowsOnNoConfiguredValue()
{
$this->expectException(NoConfigurationException::class);
$this->expectExceptionMessage('No allowed values were set for the "foo" option.');
$resolver = new OptionsResolver();
$resolver->setDefined($option = 'foo');
$debug = new OptionsResolverIntrospector($resolver);
$this->assertSame('bar', $debug->getAllowedValues($option));
}
public function testGetAllowedValuesThrowsOnNotDefinedOption()
{
$this->expectException(UndefinedOptionsException::class);
$this->expectExceptionMessage('The option "foo" does not exist.');
$resolver = new OptionsResolver();
$debug = new OptionsResolverIntrospector($resolver);
$this->assertSame('bar', $debug->getAllowedValues('foo'));
}
public function testGetNormalizer()
{
$resolver = new OptionsResolver();
$resolver->setDefined($option = 'foo');
$resolver->setNormalizer($option = 'foo', $normalizer = function () {});
$debug = new OptionsResolverIntrospector($resolver);
$this->assertSame($normalizer, $debug->getNormalizer($option));
}
public function testGetNormalizerThrowsOnNoConfiguredValue()
{
$this->expectException(NoConfigurationException::class);
$this->expectExceptionMessage('No normalizer was set for the "foo" option.');
$resolver = new OptionsResolver();
$resolver->setDefined($option = 'foo');
$debug = new OptionsResolverIntrospector($resolver);
$this->assertSame('bar', $debug->getNormalizer($option));
}
public function testGetNormalizerThrowsOnNotDefinedOption()
{
$this->expectException(UndefinedOptionsException::class);
$this->expectExceptionMessage('The option "foo" does not exist.');
$resolver = new OptionsResolver();
$debug = new OptionsResolverIntrospector($resolver);
$this->assertSame('bar', $debug->getNormalizer('foo'));
}
public function testGetNormalizers()
{
$resolver = new OptionsResolver();
$resolver->setDefined('foo');
$resolver->addNormalizer('foo', $normalizer1 = function () {});
$resolver->addNormalizer('foo', $normalizer2 = function () {});
$debug = new OptionsResolverIntrospector($resolver);
$this->assertSame([$normalizer1, $normalizer2], $debug->getNormalizers('foo'));
}
public function testGetNormalizersThrowsOnNoConfiguredValue()
{
$this->expectException(NoConfigurationException::class);
$this->expectExceptionMessage('No normalizer was set for the "foo" option.');
$resolver = new OptionsResolver();
$resolver->setDefined('foo');
$debug = new OptionsResolverIntrospector($resolver);
$debug->getNormalizers('foo');
}
public function testGetNormalizersThrowsOnNotDefinedOption()
{
$this->expectException(UndefinedOptionsException::class);
$this->expectExceptionMessage('The option "foo" does not exist.');
$resolver = new OptionsResolver();
$debug = new OptionsResolverIntrospector($resolver);
$debug->getNormalizers('foo');
}
/**
* @group legacy
*/
public function testGetDeprecationMessage()
{
$resolver = new OptionsResolver();
$resolver->setDefined('foo');
$resolver->setDeprecated('foo', 'The option "foo" is deprecated.');
$debug = new OptionsResolverIntrospector($resolver);
$this->assertSame('The option "foo" is deprecated.', $debug->getDeprecationMessage('foo'));
}
/**
* @group legacy
*/
public function testGetClosureDeprecationMessage()
{
$resolver = new OptionsResolver();
$resolver->setDefined('foo');
$resolver->setDeprecated('foo', $closure = function (Options $options, $value) {});
$debug = new OptionsResolverIntrospector($resolver);
$this->assertSame($closure, $debug->getDeprecationMessage('foo'));
}
public function testGetDeprecation()
{
$resolver = new OptionsResolver();
$resolver->setDefined('foo');
$resolver->setDeprecated('foo', 'vendor/package', '1.1', 'The option "foo" is deprecated.');
$debug = new OptionsResolverIntrospector($resolver);
$this->assertSame([
'package' => 'vendor/package',
'version' => '1.1',
'message' => 'The option "foo" is deprecated.',
], $debug->getDeprecation('foo'));
}
public function testGetClosureDeprecation()
{
$resolver = new OptionsResolver();
$resolver->setDefined('foo');
$resolver->setDeprecated('foo', 'vendor/package', '1.1', $closure = function (Options $options, $value) {});
$debug = new OptionsResolverIntrospector($resolver);
$this->assertSame([
'package' => 'vendor/package',
'version' => '1.1',
'message' => $closure,
], $debug->getDeprecation('foo'));
}
public function testGetDeprecationMessageThrowsOnNoConfiguredValue()
{
$this->expectException(NoConfigurationException::class);
$this->expectExceptionMessage('No deprecation was set for the "foo" option.');
$resolver = new OptionsResolver();
$resolver->setDefined('foo');
$debug = new OptionsResolverIntrospector($resolver);
$debug->getDeprecation('foo');
}
public function testGetDeprecationMessageThrowsOnNotDefinedOption()
{
$this->expectException(UndefinedOptionsException::class);
$this->expectExceptionMessage('The option "foo" does not exist.');
$resolver = new OptionsResolver();
$debug = new OptionsResolverIntrospector($resolver);
$debug->getDeprecation('foo');
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/5.2/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="vendor/autoload.php"
failOnRisky="true"
failOnWarning="true"
>
<php>
<ini name="error_reporting" value="-1" />
</php>
<testsuites>
<testsuite name="Symfony OptionsResolver Component Test Suite">
<directory>./Tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./</directory>
<exclude>
<directory>./Resources</directory>
<directory>./Tests</directory>
<directory>./vendor</directory>
</exclude>
</whitelist>
</filter>
</phpunit>

232
vendor/symfony/polyfill-ctype/Ctype.php vendored Normal file
View File

@@ -0,0 +1,232 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Ctype;
/**
* Ctype implementation through regex.
*
* @internal
*
* @author Gert de Pagter <BackEndTea@gmail.com>
*/
final class Ctype
{
/**
* Returns TRUE if every character in text is either a letter or a digit, FALSE otherwise.
*
* @see https://php.net/ctype-alnum
*
* @param mixed $text
*
* @return bool
*/
public static function ctype_alnum($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z0-9]/', $text);
}
/**
* Returns TRUE if every character in text is a letter, FALSE otherwise.
*
* @see https://php.net/ctype-alpha
*
* @param mixed $text
*
* @return bool
*/
public static function ctype_alpha($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z]/', $text);
}
/**
* Returns TRUE if every character in text is a control character from the current locale, FALSE otherwise.
*
* @see https://php.net/ctype-cntrl
*
* @param mixed $text
*
* @return bool
*/
public static function ctype_cntrl($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^\x00-\x1f\x7f]/', $text);
}
/**
* Returns TRUE if every character in the string text is a decimal digit, FALSE otherwise.
*
* @see https://php.net/ctype-digit
*
* @param mixed $text
*
* @return bool
*/
public static function ctype_digit($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^0-9]/', $text);
}
/**
* Returns TRUE if every character in text is printable and actually creates visible output (no white space), FALSE otherwise.
*
* @see https://php.net/ctype-graph
*
* @param mixed $text
*
* @return bool
*/
public static function ctype_graph($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^!-~]/', $text);
}
/**
* Returns TRUE if every character in text is a lowercase letter.
*
* @see https://php.net/ctype-lower
*
* @param mixed $text
*
* @return bool
*/
public static function ctype_lower($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^a-z]/', $text);
}
/**
* Returns TRUE if every character in text will actually create output (including blanks). Returns FALSE if text contains control characters or characters that do not have any output or control function at all.
*
* @see https://php.net/ctype-print
*
* @param mixed $text
*
* @return bool
*/
public static function ctype_print($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^ -~]/', $text);
}
/**
* Returns TRUE if every character in text is printable, but neither letter, digit or blank, FALSE otherwise.
*
* @see https://php.net/ctype-punct
*
* @param mixed $text
*
* @return bool
*/
public static function ctype_punct($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^!-\/\:-@\[-`\{-~]/', $text);
}
/**
* Returns TRUE if every character in text creates some sort of white space, FALSE otherwise. Besides the blank character this also includes tab, vertical tab, line feed, carriage return and form feed characters.
*
* @see https://php.net/ctype-space
*
* @param mixed $text
*
* @return bool
*/
public static function ctype_space($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^\s]/', $text);
}
/**
* Returns TRUE if every character in text is an uppercase letter.
*
* @see https://php.net/ctype-upper
*
* @param mixed $text
*
* @return bool
*/
public static function ctype_upper($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^A-Z]/', $text);
}
/**
* Returns TRUE if every character in text is a hexadecimal 'digit', that is a decimal digit or a character from [A-Fa-f] , FALSE otherwise.
*
* @see https://php.net/ctype-xdigit
*
* @param mixed $text
*
* @return bool
*/
public static function ctype_xdigit($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^A-Fa-f0-9]/', $text);
}
/**
* Converts integers to their char versions according to normal ctype behaviour, if needed.
*
* If an integer between -128 and 255 inclusive is provided,
* it is interpreted as the ASCII value of a single character
* (negative values have 256 added in order to allow characters in the Extended ASCII range).
* Any other integer is interpreted as a string containing the decimal digits of the integer.
*
* @param mixed $int
* @param string $function
*
* @return mixed
*/
private static function convert_int_to_char_for_ctype($int, $function)
{
if (!\is_int($int)) {
return $int;
}
if ($int < -128 || $int > 255) {
return (string) $int;
}
if (\PHP_VERSION_ID >= 80100) {
@trigger_error($function.'(): Argument of type int will be interpreted as string in the future', \E_USER_DEPRECATED);
}
if ($int < 0) {
$int += 256;
}
return \chr($int);
}
}

19
vendor/symfony/polyfill-ctype/LICENSE vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2018-present Fabien Potencier
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.

12
vendor/symfony/polyfill-ctype/README.md vendored Normal file
View File

@@ -0,0 +1,12 @@
Symfony Polyfill / Ctype
========================
This component provides `ctype_*` functions to users who run php versions without the ctype extension.
More information can be found in the
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).
License
=======
This library is released under the [MIT license](LICENSE).

View File

@@ -0,0 +1,50 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Ctype as p;
if (\PHP_VERSION_ID >= 80000) {
return require __DIR__.'/bootstrap80.php';
}
if (!function_exists('ctype_alnum')) {
function ctype_alnum($text) { return p\Ctype::ctype_alnum($text); }
}
if (!function_exists('ctype_alpha')) {
function ctype_alpha($text) { return p\Ctype::ctype_alpha($text); }
}
if (!function_exists('ctype_cntrl')) {
function ctype_cntrl($text) { return p\Ctype::ctype_cntrl($text); }
}
if (!function_exists('ctype_digit')) {
function ctype_digit($text) { return p\Ctype::ctype_digit($text); }
}
if (!function_exists('ctype_graph')) {
function ctype_graph($text) { return p\Ctype::ctype_graph($text); }
}
if (!function_exists('ctype_lower')) {
function ctype_lower($text) { return p\Ctype::ctype_lower($text); }
}
if (!function_exists('ctype_print')) {
function ctype_print($text) { return p\Ctype::ctype_print($text); }
}
if (!function_exists('ctype_punct')) {
function ctype_punct($text) { return p\Ctype::ctype_punct($text); }
}
if (!function_exists('ctype_space')) {
function ctype_space($text) { return p\Ctype::ctype_space($text); }
}
if (!function_exists('ctype_upper')) {
function ctype_upper($text) { return p\Ctype::ctype_upper($text); }
}
if (!function_exists('ctype_xdigit')) {
function ctype_xdigit($text) { return p\Ctype::ctype_xdigit($text); }
}

View File

@@ -0,0 +1,46 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Ctype as p;
if (!function_exists('ctype_alnum')) {
function ctype_alnum(mixed $text): bool { return p\Ctype::ctype_alnum($text); }
}
if (!function_exists('ctype_alpha')) {
function ctype_alpha(mixed $text): bool { return p\Ctype::ctype_alpha($text); }
}
if (!function_exists('ctype_cntrl')) {
function ctype_cntrl(mixed $text): bool { return p\Ctype::ctype_cntrl($text); }
}
if (!function_exists('ctype_digit')) {
function ctype_digit(mixed $text): bool { return p\Ctype::ctype_digit($text); }
}
if (!function_exists('ctype_graph')) {
function ctype_graph(mixed $text): bool { return p\Ctype::ctype_graph($text); }
}
if (!function_exists('ctype_lower')) {
function ctype_lower(mixed $text): bool { return p\Ctype::ctype_lower($text); }
}
if (!function_exists('ctype_print')) {
function ctype_print(mixed $text): bool { return p\Ctype::ctype_print($text); }
}
if (!function_exists('ctype_punct')) {
function ctype_punct(mixed $text): bool { return p\Ctype::ctype_punct($text); }
}
if (!function_exists('ctype_space')) {
function ctype_space(mixed $text): bool { return p\Ctype::ctype_space($text); }
}
if (!function_exists('ctype_upper')) {
function ctype_upper(mixed $text): bool { return p\Ctype::ctype_upper($text); }
}
if (!function_exists('ctype_xdigit')) {
function ctype_xdigit(mixed $text): bool { return p\Ctype::ctype_xdigit($text); }
}

View File

@@ -0,0 +1,247 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Grapheme;
\define('SYMFONY_GRAPHEME_CLUSTER_RX', ((float) \PCRE_VERSION < 10 ? (float) \PCRE_VERSION >= 8.32 : (float) \PCRE_VERSION >= 10.39) ? '\X' : Grapheme::GRAPHEME_CLUSTER_RX);
/**
* Partial intl implementation in pure PHP.
*
* Implemented:
* - grapheme_extract - Extract a sequence of grapheme clusters from a text buffer, which must be encoded in UTF-8
* - grapheme_stripos - Find position (in grapheme units) of first occurrence of a case-insensitive string
* - grapheme_stristr - Returns part of haystack string from the first occurrence of case-insensitive needle to the end of haystack
* - grapheme_strlen - Get string length in grapheme units
* - grapheme_strpos - Find position (in grapheme units) of first occurrence of a string
* - grapheme_strripos - Find position (in grapheme units) of last occurrence of a case-insensitive string
* - grapheme_strrpos - Find position (in grapheme units) of last occurrence of a string
* - grapheme_strstr - Returns part of haystack string from the first occurrence of needle to the end of haystack
* - grapheme_substr - Return part of a string
*
* @author Nicolas Grekas <p@tchwork.com>
*
* @internal
*/
final class Grapheme
{
// (CRLF|([ZWNJ-ZWJ]|T+|L*(LV?V+|LV|LVT)T*|L+|[^Control])[Extend]*|[Control])
// This regular expression is a work around for http://bugs.exim.org/1279
public const GRAPHEME_CLUSTER_RX = '(?:\r\n|(?:[ -~\x{200C}\x{200D}]|[ᆨ-ᇹ]+|[ᄀ-]*(?:[가개갸걔거게겨계고과괘괴교구궈궤귀규그긔기까깨꺄꺠꺼께껴꼐꼬꽈꽤꾀꾜꾸꿔꿰뀌뀨끄끠끼나내냐냬너네녀녜노놔놰뇌뇨누눠눼뉘뉴느늬니다대댜댸더데뎌뎨도돠돼되됴두둬뒈뒤듀드듸디따때땨떄떠떼뗘뗴또똬뙈뙤뚀뚜뚸뛔뛰뜌뜨띄띠라래랴럐러레려례로롸뢔뢰료루뤄뤠뤼류르릐리마매먀먜머메며몌모뫄뫠뫼묘무뭐뭬뮈뮤므믜미바배뱌뱨버베벼볘보봐봬뵈뵤부붜붸뷔뷰브븨비빠빼뺘뺴뻐뻬뼈뼤뽀뽜뽸뾔뾰뿌뿨쀄쀠쀼쁘쁴삐사새샤섀서세셔셰소솨쇄쇠쇼수숴쉐쉬슈스싀시싸쌔쌰썌써쎄쎠쎼쏘쏴쐐쐬쑈쑤쒀쒜쒸쓔쓰씌씨아애야얘어에여예오와왜외요우워웨위유으의이자재쟈쟤저제져졔조좌좨죄죠주줘줴쥐쥬즈즤지짜째쨔쨰쩌쩨쪄쪠쪼쫘쫴쬐쬬쭈쭤쮀쮜쮸쯔쯰찌차채챠챼처체쳐쳬초촤쵀최쵸추춰췌취츄츠츼치카캐캬컈커케켜켸코콰쾌쾨쿄쿠쿼퀘퀴큐크킈키타태탸턔터테텨톄토톼퇘퇴툐투퉈퉤튀튜트틔티파패퍄퍠퍼페펴폐포퐈퐤푀표푸풔풰퓌퓨프픠피하해햐햬허헤혀혜호화홰회효후훠훼휘휴흐희히]?[-ᆢ]+|[가-힣])[ᆨ-ᇹ]*|[ᄀ-]+|[^\p{Cc}\p{Cf}\p{Zl}\p{Zp}])[\p{Mn}\p{Me}\x{09BE}\x{09D7}\x{0B3E}\x{0B57}\x{0BBE}\x{0BD7}\x{0CC2}\x{0CD5}\x{0CD6}\x{0D3E}\x{0D57}\x{0DCF}\x{0DDF}\x{200C}\x{200D}\x{1D165}\x{1D16E}-\x{1D172}]*|[\p{Cc}\p{Cf}\p{Zl}\p{Zp}])';
private const CASE_FOLD = [
['µ', 'ſ', "\xCD\x85", 'ς', "\xCF\x90", "\xCF\x91", "\xCF\x95", "\xCF\x96", "\xCF\xB0", "\xCF\xB1", "\xCF\xB5", "\xE1\xBA\x9B", "\xE1\xBE\xBE"],
['μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "\xE1\xB9\xA1", 'ι'],
];
public static function grapheme_extract($s, $size, $type = \GRAPHEME_EXTR_COUNT, $start = 0, &$next = 0)
{
if (0 > $start) {
$start = \strlen($s) + $start;
}
if (!\is_scalar($s)) {
$hasError = false;
set_error_handler(function () use (&$hasError) { $hasError = true; });
$next = substr($s, $start);
restore_error_handler();
if ($hasError) {
substr($s, $start);
$s = '';
} else {
$s = $next;
}
} else {
$s = substr($s, $start);
}
$size = (int) $size;
$type = (int) $type;
$start = (int) $start;
if (\GRAPHEME_EXTR_COUNT !== $type && \GRAPHEME_EXTR_MAXBYTES !== $type && \GRAPHEME_EXTR_MAXCHARS !== $type) {
if (80000 > \PHP_VERSION_ID) {
return false;
}
throw new \ValueError('grapheme_extract(): Argument #3 ($type) must be one of GRAPHEME_EXTR_COUNT, GRAPHEME_EXTR_MAXBYTES, or GRAPHEME_EXTR_MAXCHARS');
}
if (!isset($s[0]) || 0 > $size || 0 > $start) {
return false;
}
if (0 === $size) {
return '';
}
$next = $start;
$s = preg_split('/('.SYMFONY_GRAPHEME_CLUSTER_RX.')/u', "\r\n".$s, $size + 1, \PREG_SPLIT_NO_EMPTY | \PREG_SPLIT_DELIM_CAPTURE);
if (!isset($s[1])) {
return false;
}
$i = 1;
$ret = '';
do {
if (\GRAPHEME_EXTR_COUNT === $type) {
--$size;
} elseif (\GRAPHEME_EXTR_MAXBYTES === $type) {
$size -= \strlen($s[$i]);
} else {
$size -= iconv_strlen($s[$i], 'UTF-8//IGNORE');
}
if ($size >= 0) {
$ret .= $s[$i];
}
} while (isset($s[++$i]) && $size > 0);
$next += \strlen($ret);
return $ret;
}
public static function grapheme_strlen($s)
{
preg_replace('/'.SYMFONY_GRAPHEME_CLUSTER_RX.'/u', '', $s, -1, $len);
return 0 === $len && '' !== $s ? null : $len;
}
public static function grapheme_substr($s, $start, $len = null)
{
if (null === $len) {
$len = 2147483647;
}
preg_match_all('/'.SYMFONY_GRAPHEME_CLUSTER_RX.'/u', $s, $s);
$slen = \count($s[0]);
$start = (int) $start;
if (0 > $start) {
$start += $slen;
}
if (0 > $start) {
if (\PHP_VERSION_ID < 80000) {
return false;
}
$start = 0;
}
if ($start >= $slen) {
return \PHP_VERSION_ID >= 80000 ? '' : false;
}
$rem = $slen - $start;
if (0 > $len) {
$len += $rem;
}
if (0 === $len) {
return '';
}
if (0 > $len) {
return \PHP_VERSION_ID >= 80000 ? '' : false;
}
if ($len > $rem) {
$len = $rem;
}
return implode('', \array_slice($s[0], $start, $len));
}
public static function grapheme_strpos($s, $needle, $offset = 0)
{
return self::grapheme_position($s, $needle, $offset, 0);
}
public static function grapheme_stripos($s, $needle, $offset = 0)
{
return self::grapheme_position($s, $needle, $offset, 1);
}
public static function grapheme_strrpos($s, $needle, $offset = 0)
{
return self::grapheme_position($s, $needle, $offset, 2);
}
public static function grapheme_strripos($s, $needle, $offset = 0)
{
return self::grapheme_position($s, $needle, $offset, 3);
}
public static function grapheme_stristr($s, $needle, $beforeNeedle = false)
{
return mb_stristr($s, $needle, $beforeNeedle, 'UTF-8');
}
public static function grapheme_strstr($s, $needle, $beforeNeedle = false)
{
return mb_strstr($s, $needle, $beforeNeedle, 'UTF-8');
}
private static function grapheme_position($s, $needle, $offset, $mode)
{
$needle = (string) $needle;
if (80000 > \PHP_VERSION_ID && !preg_match('/./us', $needle)) {
return false;
}
$s = (string) $s;
if (!preg_match('/./us', $s)) {
return false;
}
if ($offset > 0) {
$s = self::grapheme_substr($s, $offset);
} elseif ($offset < 0) {
if (2 > $mode) {
$offset += self::grapheme_strlen($s);
$s = self::grapheme_substr($s, $offset);
if (0 > $offset) {
$offset = 0;
}
} elseif (0 > $offset += self::grapheme_strlen($needle)) {
$s = self::grapheme_substr($s, 0, $offset);
$offset = 0;
} else {
$offset = 0;
}
}
// As UTF-8 is self-synchronizing, and we have ensured the strings are valid UTF-8,
// we can use normal binary string functions here. For case-insensitive searches,
// case fold the strings first.
$caseInsensitive = $mode & 1;
$reverse = $mode & 2;
if ($caseInsensitive) {
// Use the same case folding mode as mbstring does for mb_stripos().
// Stick to SIMPLE case folding to avoid changing the length of the string, which
// might result in offsets being shifted.
$mode = \defined('MB_CASE_FOLD_SIMPLE') ? \MB_CASE_FOLD_SIMPLE : \MB_CASE_LOWER;
$s = mb_convert_case($s, $mode, 'UTF-8');
$needle = mb_convert_case($needle, $mode, 'UTF-8');
if (!\defined('MB_CASE_FOLD_SIMPLE')) {
$s = str_replace(self::CASE_FOLD[0], self::CASE_FOLD[1], $s);
$needle = str_replace(self::CASE_FOLD[0], self::CASE_FOLD[1], $needle);
}
}
if ($reverse) {
$needlePos = strrpos($s, $needle);
} else {
$needlePos = strpos($s, $needle);
}
return false !== $needlePos ? self::grapheme_strlen(substr($s, 0, $needlePos)) + $offset : false;
}
}

View File

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

View File

@@ -0,0 +1,31 @@
Symfony Polyfill / Intl: Grapheme
=================================
This component provides a partial, native PHP implementation of the
[Grapheme functions](https://php.net/intl.grapheme) from the
[Intl](https://php.net/intl) extension.
- [`grapheme_extract`](https://php.net/grapheme_extract): Extract a sequence of grapheme
clusters from a text buffer, which must be encoded in UTF-8
- [`grapheme_stripos`](https://php.net/grapheme_stripos): Find position (in grapheme units)
of first occurrence of a case-insensitive string
- [`grapheme_stristr`](https://php.net/grapheme_stristr): Returns part of haystack string
from the first occurrence of case-insensitive needle to the end of haystack
- [`grapheme_strlen`](https://php.net/grapheme_strlen): Get string length in grapheme units
- [`grapheme_strpos`](https://php.net/grapheme_strpos): Find position (in grapheme units)
of first occurrence of a string
- [`grapheme_strripos`](https://php.net/grapheme_strripos): Find position (in grapheme units)
of last occurrence of a case-insensitive string
- [`grapheme_strrpos`](https://php.net/grapheme_strrpos): Find position (in grapheme units)
of last occurrence of a string
- [`grapheme_strstr`](https://php.net/grapheme_strstr): Returns part of haystack string from
the first occurrence of needle to the end of haystack
- [`grapheme_substr`](https://php.net/grapheme_substr): Return part of a string
More information can be found in the
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).
License
=======
This library is released under the [MIT license](LICENSE).

View File

@@ -0,0 +1,58 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Intl\Grapheme as p;
if (extension_loaded('intl')) {
return;
}
if (\PHP_VERSION_ID >= 80000) {
return require __DIR__.'/bootstrap80.php';
}
if (!defined('GRAPHEME_EXTR_COUNT')) {
define('GRAPHEME_EXTR_COUNT', 0);
}
if (!defined('GRAPHEME_EXTR_MAXBYTES')) {
define('GRAPHEME_EXTR_MAXBYTES', 1);
}
if (!defined('GRAPHEME_EXTR_MAXCHARS')) {
define('GRAPHEME_EXTR_MAXCHARS', 2);
}
if (!function_exists('grapheme_extract')) {
function grapheme_extract($haystack, $size, $type = 0, $start = 0, &$next = 0) { return p\Grapheme::grapheme_extract($haystack, $size, $type, $start, $next); }
}
if (!function_exists('grapheme_stripos')) {
function grapheme_stripos($haystack, $needle, $offset = 0) { return p\Grapheme::grapheme_stripos($haystack, $needle, $offset); }
}
if (!function_exists('grapheme_stristr')) {
function grapheme_stristr($haystack, $needle, $beforeNeedle = false) { return p\Grapheme::grapheme_stristr($haystack, $needle, $beforeNeedle); }
}
if (!function_exists('grapheme_strlen')) {
function grapheme_strlen($input) { return p\Grapheme::grapheme_strlen($input); }
}
if (!function_exists('grapheme_strpos')) {
function grapheme_strpos($haystack, $needle, $offset = 0) { return p\Grapheme::grapheme_strpos($haystack, $needle, $offset); }
}
if (!function_exists('grapheme_strripos')) {
function grapheme_strripos($haystack, $needle, $offset = 0) { return p\Grapheme::grapheme_strripos($haystack, $needle, $offset); }
}
if (!function_exists('grapheme_strrpos')) {
function grapheme_strrpos($haystack, $needle, $offset = 0) { return p\Grapheme::grapheme_strrpos($haystack, $needle, $offset); }
}
if (!function_exists('grapheme_strstr')) {
function grapheme_strstr($haystack, $needle, $beforeNeedle = false) { return p\Grapheme::grapheme_strstr($haystack, $needle, $beforeNeedle); }
}
if (!function_exists('grapheme_substr')) {
function grapheme_substr($string, $offset, $length = null) { return p\Grapheme::grapheme_substr($string, $offset, $length); }
}

View File

@@ -0,0 +1,50 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Intl\Grapheme as p;
if (!defined('GRAPHEME_EXTR_COUNT')) {
define('GRAPHEME_EXTR_COUNT', 0);
}
if (!defined('GRAPHEME_EXTR_MAXBYTES')) {
define('GRAPHEME_EXTR_MAXBYTES', 1);
}
if (!defined('GRAPHEME_EXTR_MAXCHARS')) {
define('GRAPHEME_EXTR_MAXCHARS', 2);
}
if (!function_exists('grapheme_extract')) {
function grapheme_extract(?string $haystack, ?int $size, ?int $type = GRAPHEME_EXTR_COUNT, ?int $offset = 0, &$next = null): string|false { return p\Grapheme::grapheme_extract((string) $haystack, (int) $size, (int) $type, (int) $offset, $next); }
}
if (!function_exists('grapheme_stripos')) {
function grapheme_stripos(?string $haystack, ?string $needle, ?int $offset = 0): int|false { return p\Grapheme::grapheme_stripos((string) $haystack, (string) $needle, (int) $offset); }
}
if (!function_exists('grapheme_stristr')) {
function grapheme_stristr(?string $haystack, ?string $needle, ?bool $beforeNeedle = false): string|false { return p\Grapheme::grapheme_stristr((string) $haystack, (string) $needle, (bool) $beforeNeedle); }
}
if (!function_exists('grapheme_strlen')) {
function grapheme_strlen(?string $string): int|false|null { return p\Grapheme::grapheme_strlen((string) $string); }
}
if (!function_exists('grapheme_strpos')) {
function grapheme_strpos(?string $haystack, ?string $needle, ?int $offset = 0): int|false { return p\Grapheme::grapheme_strpos((string) $haystack, (string) $needle, (int) $offset); }
}
if (!function_exists('grapheme_strripos')) {
function grapheme_strripos(?string $haystack, ?string $needle, ?int $offset = 0): int|false { return p\Grapheme::grapheme_strripos((string) $haystack, (string) $needle, (int) $offset); }
}
if (!function_exists('grapheme_strrpos')) {
function grapheme_strrpos(?string $haystack, ?string $needle, ?int $offset = 0): int|false { return p\Grapheme::grapheme_strrpos((string) $haystack, (string) $needle, (int) $offset); }
}
if (!function_exists('grapheme_strstr')) {
function grapheme_strstr(?string $haystack, ?string $needle, ?bool $beforeNeedle = false): string|false { return p\Grapheme::grapheme_strstr((string) $haystack, (string) $needle, (bool) $beforeNeedle); }
}
if (!function_exists('grapheme_substr')) {
function grapheme_substr(?string $string, ?int $offset, ?int $length = null): string|false { return p\Grapheme::grapheme_substr((string) $string, (int) $offset, $length); }
}

View File

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

View File

@@ -0,0 +1,310 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Normalizer;
/**
* Normalizer is a PHP fallback implementation of the Normalizer class provided by the intl extension.
*
* It has been validated with Unicode 6.3 Normalization Conformance Test.
* See http://www.unicode.org/reports/tr15/ for detailed info about Unicode normalizations.
*
* @author Nicolas Grekas <p@tchwork.com>
*
* @internal
*/
class Normalizer
{
public const FORM_D = \Normalizer::FORM_D;
public const FORM_KD = \Normalizer::FORM_KD;
public const FORM_C = \Normalizer::FORM_C;
public const FORM_KC = \Normalizer::FORM_KC;
public const NFD = \Normalizer::NFD;
public const NFKD = \Normalizer::NFKD;
public const NFC = \Normalizer::NFC;
public const NFKC = \Normalizer::NFKC;
private static $C;
private static $D;
private static $KD;
private static $cC;
private static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4];
private static $ASCII = "\x20\x65\x69\x61\x73\x6E\x74\x72\x6F\x6C\x75\x64\x5D\x5B\x63\x6D\x70\x27\x0A\x67\x7C\x68\x76\x2E\x66\x62\x2C\x3A\x3D\x2D\x71\x31\x30\x43\x32\x2A\x79\x78\x29\x28\x4C\x39\x41\x53\x2F\x50\x22\x45\x6A\x4D\x49\x6B\x33\x3E\x35\x54\x3C\x44\x34\x7D\x42\x7B\x38\x46\x77\x52\x36\x37\x55\x47\x4E\x3B\x4A\x7A\x56\x23\x48\x4F\x57\x5F\x26\x21\x4B\x3F\x58\x51\x25\x59\x5C\x09\x5A\x2B\x7E\x5E\x24\x40\x60\x7F\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F";
public static function isNormalized(string $s, int $form = self::FORM_C)
{
if (!\in_array($form, [self::NFD, self::NFKD, self::NFC, self::NFKC])) {
return false;
}
if (!isset($s[strspn($s, self::$ASCII)])) {
return true;
}
if (self::NFC == $form && preg_match('//u', $s) && !preg_match('/[^\x00-\x{2FF}]/u', $s)) {
return true;
}
return self::normalize($s, $form) === $s;
}
public static function normalize(string $s, int $form = self::FORM_C)
{
if (!preg_match('//u', $s)) {
return false;
}
switch ($form) {
case self::NFC: $C = true; $K = false; break;
case self::NFD: $C = false; $K = false; break;
case self::NFKC: $C = true; $K = true; break;
case self::NFKD: $C = false; $K = true; break;
default:
if (\defined('Normalizer::NONE') && \Normalizer::NONE == $form) {
return $s;
}
if (80000 > \PHP_VERSION_ID) {
return false;
}
throw new \ValueError('normalizer_normalize(): Argument #2 ($form) must be a a valid normalization form');
}
if ('' === $s) {
return '';
}
if ($K && null === self::$KD) {
self::$KD = self::getData('compatibilityDecomposition');
}
if (null === self::$D) {
self::$D = self::getData('canonicalDecomposition');
self::$cC = self::getData('combiningClass');
}
if (null !== $mbEncoding = (2 /* MB_OVERLOAD_STRING */ & (int) \ini_get('mbstring.func_overload')) ? mb_internal_encoding() : null) {
mb_internal_encoding('8bit');
}
$r = self::decompose($s, $K);
if ($C) {
if (null === self::$C) {
self::$C = self::getData('canonicalComposition');
}
$r = self::recompose($r);
}
if (null !== $mbEncoding) {
mb_internal_encoding($mbEncoding);
}
return $r;
}
private static function recompose($s)
{
$ASCII = self::$ASCII;
$compMap = self::$C;
$combClass = self::$cC;
$ulenMask = self::$ulenMask;
$result = $tail = '';
$i = $s[0] < "\x80" ? 1 : $ulenMask[$s[0] & "\xF0"];
$len = \strlen($s);
$lastUchr = substr($s, 0, $i);
$lastUcls = isset($combClass[$lastUchr]) ? 256 : 0;
while ($i < $len) {
if ($s[$i] < "\x80") {
// ASCII chars
if ($tail) {
$lastUchr .= $tail;
$tail = '';
}
if ($j = strspn($s, $ASCII, $i + 1)) {
$lastUchr .= substr($s, $i, $j);
$i += $j;
}
$result .= $lastUchr;
$lastUchr = $s[$i];
$lastUcls = 0;
++$i;
continue;
}
$ulen = $ulenMask[$s[$i] & "\xF0"];
$uchr = substr($s, $i, $ulen);
if ($lastUchr < "\xE1\x84\x80" || "\xE1\x84\x92" < $lastUchr
|| $uchr < "\xE1\x85\xA1" || "\xE1\x85\xB5" < $uchr
|| $lastUcls) {
// Table lookup and combining chars composition
$ucls = $combClass[$uchr] ?? 0;
if (isset($compMap[$lastUchr.$uchr]) && (!$lastUcls || $lastUcls < $ucls)) {
$lastUchr = $compMap[$lastUchr.$uchr];
} elseif ($lastUcls = $ucls) {
$tail .= $uchr;
} else {
if ($tail) {
$lastUchr .= $tail;
$tail = '';
}
$result .= $lastUchr;
$lastUchr = $uchr;
}
} else {
// Hangul chars
$L = \ord($lastUchr[2]) - 0x80;
$V = \ord($uchr[2]) - 0xA1;
$T = 0;
$uchr = substr($s, $i + $ulen, 3);
if ("\xE1\x86\xA7" <= $uchr && $uchr <= "\xE1\x87\x82") {
$T = \ord($uchr[2]) - 0xA7;
0 > $T && $T += 0x40;
$ulen += 3;
}
$L = 0xAC00 + ($L * 21 + $V) * 28 + $T;
$lastUchr = \chr(0xE0 | $L >> 12).\chr(0x80 | $L >> 6 & 0x3F).\chr(0x80 | $L & 0x3F);
}
$i += $ulen;
}
return $result.$lastUchr.$tail;
}
private static function decompose($s, $c)
{
$result = '';
$ASCII = self::$ASCII;
$decompMap = self::$D;
$combClass = self::$cC;
$ulenMask = self::$ulenMask;
if ($c) {
$compatMap = self::$KD;
}
$c = [];
$i = 0;
$len = \strlen($s);
while ($i < $len) {
if ($s[$i] < "\x80") {
// ASCII chars
if ($c) {
ksort($c);
$result .= implode('', $c);
$c = [];
}
$j = 1 + strspn($s, $ASCII, $i + 1);
$result .= substr($s, $i, $j);
$i += $j;
continue;
}
$ulen = $ulenMask[$s[$i] & "\xF0"];
$uchr = substr($s, $i, $ulen);
$i += $ulen;
if ($uchr < "\xEA\xB0\x80" || "\xED\x9E\xA3" < $uchr) {
// Table lookup
if ($uchr !== $j = $compatMap[$uchr] ?? ($decompMap[$uchr] ?? $uchr)) {
$uchr = $j;
$j = \strlen($uchr);
$ulen = $uchr[0] < "\x80" ? 1 : $ulenMask[$uchr[0] & "\xF0"];
if ($ulen != $j) {
// Put trailing chars in $s
$j -= $ulen;
$i -= $j;
if (0 > $i) {
$s = str_repeat(' ', -$i).$s;
$len -= $i;
$i = 0;
}
while ($j--) {
$s[$i + $j] = $uchr[$ulen + $j];
}
$uchr = substr($uchr, 0, $ulen);
}
}
if (isset($combClass[$uchr])) {
// Combining chars, for sorting
if (!isset($c[$combClass[$uchr]])) {
$c[$combClass[$uchr]] = '';
}
$c[$combClass[$uchr]] .= $uchr;
continue;
}
} else {
// Hangul chars
$uchr = unpack('C*', $uchr);
$j = (($uchr[1] - 224) << 12) + (($uchr[2] - 128) << 6) + $uchr[3] - 0xAC80;
$uchr = "\xE1\x84".\chr(0x80 + (int) ($j / 588))
."\xE1\x85".\chr(0xA1 + (int) (($j % 588) / 28));
if ($j %= 28) {
$uchr .= $j < 25
? ("\xE1\x86".\chr(0xA7 + $j))
: ("\xE1\x87".\chr(0x67 + $j));
}
}
if ($c) {
ksort($c);
$result .= implode('', $c);
$c = [];
}
$result .= $uchr;
}
if ($c) {
ksort($c);
$result .= implode('', $c);
}
return $result;
}
private static function getData($file)
{
if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) {
return require $file;
}
return false;
}
}

View File

@@ -0,0 +1,14 @@
Symfony Polyfill / Intl: Normalizer
===================================
This component provides a fallback implementation for the
[`Normalizer`](https://php.net/Normalizer) class provided
by the [Intl](https://php.net/intl) extension.
More information can be found in the
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).
License
=======
This library is released under the [MIT license](LICENSE).

View File

@@ -0,0 +1,17 @@
<?php
class Normalizer extends Symfony\Polyfill\Intl\Normalizer\Normalizer
{
/**
* @deprecated since ICU 56 and removed in PHP 8
*/
public const NONE = 2;
public const FORM_D = 4;
public const FORM_KD = 8;
public const FORM_C = 16;
public const FORM_KC = 32;
public const NFD = 4;
public const NFKD = 8;
public const NFC = 16;
public const NFKC = 32;
}

View File

@@ -0,0 +1,945 @@
<?php
return array (
'À' => 'À',
'Á' => 'Á',
'Â' => 'Â',
'Ã' => 'Ã',
'Ä' => 'Ä',
'Å' => 'Å',
'Ç' => 'Ç',
'È' => 'È',
'É' => 'É',
'Ê' => 'Ê',
'Ë' => 'Ë',
'Ì' => 'Ì',
'Í' => 'Í',
'Î' => 'Î',
'Ï' => 'Ï',
'Ñ' => 'Ñ',
'Ò' => 'Ò',
'Ó' => 'Ó',
'Ô' => 'Ô',
'Õ' => 'Õ',
'Ö' => 'Ö',
'Ù' => 'Ù',
'Ú' => 'Ú',
'Û' => 'Û',
'Ü' => 'Ü',
'Ý' => 'Ý',
'à' => 'à',
'á' => 'á',
'â' => 'â',
'ã' => 'ã',
'ä' => 'ä',
'å' => 'å',
'ç' => 'ç',
'è' => 'è',
'é' => 'é',
'ê' => 'ê',
'ë' => 'ë',
'ì' => 'ì',
'í' => 'í',
'î' => 'î',
'ï' => 'ï',
'ñ' => 'ñ',
'ò' => 'ò',
'ó' => 'ó',
'ô' => 'ô',
'õ' => 'õ',
'ö' => 'ö',
'ù' => 'ù',
'ú' => 'ú',
'û' => 'û',
'ü' => 'ü',
'ý' => 'ý',
'ÿ' => 'ÿ',
'Ā' => 'Ā',
'ā' => 'ā',
'Ă' => 'Ă',
'ă' => 'ă',
'Ą' => 'Ą',
'ą' => 'ą',
'Ć' => 'Ć',
'ć' => 'ć',
'Ĉ' => 'Ĉ',
'ĉ' => 'ĉ',
'Ċ' => 'Ċ',
'ċ' => 'ċ',
'Č' => 'Č',
'č' => 'č',
'Ď' => 'Ď',
'ď' => 'ď',
'Ē' => 'Ē',
'ē' => 'ē',
'Ĕ' => 'Ĕ',
'ĕ' => 'ĕ',
'Ė' => 'Ė',
'ė' => 'ė',
'Ę' => 'Ę',
'ę' => 'ę',
'Ě' => 'Ě',
'ě' => 'ě',
'Ĝ' => 'Ĝ',
'ĝ' => 'ĝ',
'Ğ' => 'Ğ',
'ğ' => 'ğ',
'Ġ' => 'Ġ',
'ġ' => 'ġ',
'Ģ' => 'Ģ',
'ģ' => 'ģ',
'Ĥ' => 'Ĥ',
'ĥ' => 'ĥ',
'Ĩ' => 'Ĩ',
'ĩ' => 'ĩ',
'Ī' => 'Ī',
'ī' => 'ī',
'Ĭ' => 'Ĭ',
'ĭ' => 'ĭ',
'Į' => 'Į',
'į' => 'į',
'İ' => 'İ',
'Ĵ' => 'Ĵ',
'ĵ' => 'ĵ',
'Ķ' => 'Ķ',
'ķ' => 'ķ',
'Ĺ' => 'Ĺ',
'ĺ' => 'ĺ',
'Ļ' => 'Ļ',
'ļ' => 'ļ',
'Ľ' => 'Ľ',
'ľ' => 'ľ',
'Ń' => 'Ń',
'ń' => 'ń',
'Ņ' => 'Ņ',
'ņ' => 'ņ',
'Ň' => 'Ň',
'ň' => 'ň',
'Ō' => 'Ō',
'ō' => 'ō',
'Ŏ' => 'Ŏ',
'ŏ' => 'ŏ',
'Ő' => 'Ő',
'ő' => 'ő',
'Ŕ' => 'Ŕ',
'ŕ' => 'ŕ',
'Ŗ' => 'Ŗ',
'ŗ' => 'ŗ',
'Ř' => 'Ř',
'ř' => 'ř',
'Ś' => 'Ś',
'ś' => 'ś',
'Ŝ' => 'Ŝ',
'ŝ' => 'ŝ',
'Ş' => 'Ş',
'ş' => 'ş',
'Š' => 'Š',
'š' => 'š',
'Ţ' => 'Ţ',
'ţ' => 'ţ',
'Ť' => 'Ť',
'ť' => 'ť',
'Ũ' => 'Ũ',
'ũ' => 'ũ',
'Ū' => 'Ū',
'ū' => 'ū',
'Ŭ' => 'Ŭ',
'ŭ' => 'ŭ',
'Ů' => 'Ů',
'ů' => 'ů',
'Ű' => 'Ű',
'ű' => 'ű',
'Ų' => 'Ų',
'ų' => 'ų',
'Ŵ' => 'Ŵ',
'ŵ' => 'ŵ',
'Ŷ' => 'Ŷ',
'ŷ' => 'ŷ',
'Ÿ' => 'Ÿ',
'Ź' => 'Ź',
'ź' => 'ź',
'Ż' => 'Ż',
'ż' => 'ż',
'Ž' => 'Ž',
'ž' => 'ž',
'Ơ' => 'Ơ',
'ơ' => 'ơ',
'Ư' => 'Ư',
'ư' => 'ư',
'Ǎ' => 'Ǎ',
'ǎ' => 'ǎ',
'Ǐ' => 'Ǐ',
'ǐ' => 'ǐ',
'Ǒ' => 'Ǒ',
'ǒ' => 'ǒ',
'Ǔ' => 'Ǔ',
'ǔ' => 'ǔ',
'Ǖ' => 'Ǖ',
'ǖ' => 'ǖ',
'Ǘ' => 'Ǘ',
'ǘ' => 'ǘ',
'Ǚ' => 'Ǚ',
'ǚ' => 'ǚ',
'Ǜ' => 'Ǜ',
'ǜ' => 'ǜ',
'Ǟ' => 'Ǟ',
'ǟ' => 'ǟ',
'Ǡ' => 'Ǡ',
'ǡ' => 'ǡ',
'Ǣ' => 'Ǣ',
'ǣ' => 'ǣ',
'Ǧ' => 'Ǧ',
'ǧ' => 'ǧ',
'Ǩ' => 'Ǩ',
'ǩ' => 'ǩ',
'Ǫ' => 'Ǫ',
'ǫ' => 'ǫ',
'Ǭ' => 'Ǭ',
'ǭ' => 'ǭ',
'Ǯ' => 'Ǯ',
'ǯ' => 'ǯ',
'ǰ' => 'ǰ',
'Ǵ' => 'Ǵ',
'ǵ' => 'ǵ',
'Ǹ' => 'Ǹ',
'ǹ' => 'ǹ',
'Ǻ' => 'Ǻ',
'ǻ' => 'ǻ',
'Ǽ' => 'Ǽ',
'ǽ' => 'ǽ',
'Ǿ' => 'Ǿ',
'ǿ' => 'ǿ',
'Ȁ' => 'Ȁ',
'ȁ' => 'ȁ',
'Ȃ' => 'Ȃ',
'ȃ' => 'ȃ',
'Ȅ' => 'Ȅ',
'ȅ' => 'ȅ',
'Ȇ' => 'Ȇ',
'ȇ' => 'ȇ',
'Ȉ' => 'Ȉ',
'ȉ' => 'ȉ',
'Ȋ' => 'Ȋ',
'ȋ' => 'ȋ',
'Ȍ' => 'Ȍ',
'ȍ' => 'ȍ',
'Ȏ' => 'Ȏ',
'ȏ' => 'ȏ',
'Ȑ' => 'Ȑ',
'ȑ' => 'ȑ',
'Ȓ' => 'Ȓ',
'ȓ' => 'ȓ',
'Ȕ' => 'Ȕ',
'ȕ' => 'ȕ',
'Ȗ' => 'Ȗ',
'ȗ' => 'ȗ',
'Ș' => 'Ș',
'ș' => 'ș',
'Ț' => 'Ț',
'ț' => 'ț',
'Ȟ' => 'Ȟ',
'ȟ' => 'ȟ',
'Ȧ' => 'Ȧ',
'ȧ' => 'ȧ',
'Ȩ' => 'Ȩ',
'ȩ' => 'ȩ',
'Ȫ' => 'Ȫ',
'ȫ' => 'ȫ',
'Ȭ' => 'Ȭ',
'ȭ' => 'ȭ',
'Ȯ' => 'Ȯ',
'ȯ' => 'ȯ',
'Ȱ' => 'Ȱ',
'ȱ' => 'ȱ',
'Ȳ' => 'Ȳ',
'ȳ' => 'ȳ',
'΅' => '΅',
'Ά' => 'Ά',
'Έ' => 'Έ',
'Ή' => 'Ή',
'Ί' => 'Ί',
'Ό' => 'Ό',
'Ύ' => 'Ύ',
'Ώ' => 'Ώ',
'ΐ' => 'ΐ',
'Ϊ' => 'Ϊ',
'Ϋ' => 'Ϋ',
'ά' => 'ά',
'έ' => 'έ',
'ή' => 'ή',
'ί' => 'ί',
'ΰ' => 'ΰ',
'ϊ' => 'ϊ',
'ϋ' => 'ϋ',
'ό' => 'ό',
'ύ' => 'ύ',
'ώ' => 'ώ',
'ϓ' => 'ϓ',
'ϔ' => 'ϔ',
'Ѐ' => 'Ѐ',
'Ё' => 'Ё',
'Ѓ' => 'Ѓ',
'Ї' => 'Ї',
'Ќ' => 'Ќ',
'Ѝ' => 'Ѝ',
'Ў' => 'Ў',
'Й' => 'Й',
'й' => 'й',
'ѐ' => 'ѐ',
'ё' => 'ё',
'ѓ' => 'ѓ',
'ї' => 'ї',
'ќ' => 'ќ',
'ѝ' => 'ѝ',
'ў' => 'ў',
'Ѷ' => 'Ѷ',
'ѷ' => 'ѷ',
'Ӂ' => 'Ӂ',
'ӂ' => 'ӂ',
'Ӑ' => 'Ӑ',
'ӑ' => 'ӑ',
'Ӓ' => 'Ӓ',
'ӓ' => 'ӓ',
'Ӗ' => 'Ӗ',
'ӗ' => 'ӗ',
'Ӛ' => 'Ӛ',
'ӛ' => 'ӛ',
'Ӝ' => 'Ӝ',
'ӝ' => 'ӝ',
'Ӟ' => 'Ӟ',
'ӟ' => 'ӟ',
'Ӣ' => 'Ӣ',
'ӣ' => 'ӣ',
'Ӥ' => 'Ӥ',
'ӥ' => 'ӥ',
'Ӧ' => 'Ӧ',
'ӧ' => 'ӧ',
'Ӫ' => 'Ӫ',
'ӫ' => 'ӫ',
'Ӭ' => 'Ӭ',
'ӭ' => 'ӭ',
'Ӯ' => 'Ӯ',
'ӯ' => 'ӯ',
'Ӱ' => 'Ӱ',
'ӱ' => 'ӱ',
'Ӳ' => 'Ӳ',
'ӳ' => 'ӳ',
'Ӵ' => 'Ӵ',
'ӵ' => 'ӵ',
'Ӹ' => 'Ӹ',
'ӹ' => 'ӹ',
'آ' => 'آ',
'أ' => 'أ',
'ؤ' => 'ؤ',
'إ' => 'إ',
'ئ' => 'ئ',
'ۀ' => 'ۀ',
'ۂ' => 'ۂ',
'ۓ' => 'ۓ',
'ऩ' => 'ऩ',
'ऱ' => 'ऱ',
'ऴ' => 'ऴ',
'ো' => 'ো',
'ৌ' => 'ৌ',
'ୈ' => 'ୈ',
'ୋ' => 'ୋ',
'ୌ' => 'ୌ',
'ஔ' => 'ஔ',
'ொ' => 'ொ',
'ோ' => 'ோ',
'ௌ' => 'ௌ',
'ై' => 'ై',
'ೀ' => 'ೀ',
'ೇ' => 'ೇ',
'ೈ' => 'ೈ',
'ೊ' => 'ೊ',
'ೋ' => 'ೋ',
'ൊ' => 'ൊ',
'ോ' => 'ോ',
'ൌ' => 'ൌ',
'ේ' => 'ේ',
'ො' => 'ො',
'ෝ' => 'ෝ',
'ෞ' => 'ෞ',
'ဦ' => 'ဦ',
'ᬆ' => 'ᬆ',
'ᬈ' => 'ᬈ',
'ᬊ' => 'ᬊ',
'ᬌ' => 'ᬌ',
'ᬎ' => 'ᬎ',
'ᬒ' => 'ᬒ',
'ᬻ' => 'ᬻ',
'ᬽ' => 'ᬽ',
'ᭀ' => 'ᭀ',
'ᭁ' => 'ᭁ',
'ᭃ' => 'ᭃ',
'Ḁ' => 'Ḁ',
'ḁ' => 'ḁ',
'Ḃ' => 'Ḃ',
'ḃ' => 'ḃ',
'Ḅ' => 'Ḅ',
'ḅ' => 'ḅ',
'Ḇ' => 'Ḇ',
'ḇ' => 'ḇ',
'Ḉ' => 'Ḉ',
'ḉ' => 'ḉ',
'Ḋ' => 'Ḋ',
'ḋ' => 'ḋ',
'Ḍ' => 'Ḍ',
'ḍ' => 'ḍ',
'Ḏ' => 'Ḏ',
'ḏ' => 'ḏ',
'Ḑ' => 'Ḑ',
'ḑ' => 'ḑ',
'Ḓ' => 'Ḓ',
'ḓ' => 'ḓ',
'Ḕ' => 'Ḕ',
'ḕ' => 'ḕ',
'Ḗ' => 'Ḗ',
'ḗ' => 'ḗ',
'Ḙ' => 'Ḙ',
'ḙ' => 'ḙ',
'Ḛ' => 'Ḛ',
'ḛ' => 'ḛ',
'Ḝ' => 'Ḝ',
'ḝ' => 'ḝ',
'Ḟ' => 'Ḟ',
'ḟ' => 'ḟ',
'Ḡ' => 'Ḡ',
'ḡ' => 'ḡ',
'Ḣ' => 'Ḣ',
'ḣ' => 'ḣ',
'Ḥ' => 'Ḥ',
'ḥ' => 'ḥ',
'Ḧ' => 'Ḧ',
'ḧ' => 'ḧ',
'Ḩ' => 'Ḩ',
'ḩ' => 'ḩ',
'Ḫ' => 'Ḫ',
'ḫ' => 'ḫ',
'Ḭ' => 'Ḭ',
'ḭ' => 'ḭ',
'Ḯ' => 'Ḯ',
'ḯ' => 'ḯ',
'Ḱ' => 'Ḱ',
'ḱ' => 'ḱ',
'Ḳ' => 'Ḳ',
'ḳ' => 'ḳ',
'Ḵ' => 'Ḵ',
'ḵ' => 'ḵ',
'Ḷ' => 'Ḷ',
'ḷ' => 'ḷ',
'Ḹ' => 'Ḹ',
'ḹ' => 'ḹ',
'Ḻ' => 'Ḻ',
'ḻ' => 'ḻ',
'Ḽ' => 'Ḽ',
'ḽ' => 'ḽ',
'Ḿ' => 'Ḿ',
'ḿ' => 'ḿ',
'Ṁ' => 'Ṁ',
'ṁ' => 'ṁ',
'Ṃ' => 'Ṃ',
'ṃ' => 'ṃ',
'Ṅ' => 'Ṅ',
'ṅ' => 'ṅ',
'Ṇ' => 'Ṇ',
'ṇ' => 'ṇ',
'Ṉ' => 'Ṉ',
'ṉ' => 'ṉ',
'Ṋ' => 'Ṋ',
'ṋ' => 'ṋ',
'Ṍ' => 'Ṍ',
'ṍ' => 'ṍ',
'Ṏ' => 'Ṏ',
'ṏ' => 'ṏ',
'Ṑ' => 'Ṑ',
'ṑ' => 'ṑ',
'Ṓ' => 'Ṓ',
'ṓ' => 'ṓ',
'Ṕ' => 'Ṕ',
'ṕ' => 'ṕ',
'Ṗ' => 'Ṗ',
'ṗ' => 'ṗ',
'Ṙ' => 'Ṙ',
'ṙ' => 'ṙ',
'Ṛ' => 'Ṛ',
'ṛ' => 'ṛ',
'Ṝ' => 'Ṝ',
'ṝ' => 'ṝ',
'Ṟ' => 'Ṟ',
'ṟ' => 'ṟ',
'Ṡ' => 'Ṡ',
'ṡ' => 'ṡ',
'Ṣ' => 'Ṣ',
'ṣ' => 'ṣ',
'Ṥ' => 'Ṥ',
'ṥ' => 'ṥ',
'Ṧ' => 'Ṧ',
'ṧ' => 'ṧ',
'Ṩ' => 'Ṩ',
'ṩ' => 'ṩ',
'Ṫ' => 'Ṫ',
'ṫ' => 'ṫ',
'Ṭ' => 'Ṭ',
'ṭ' => 'ṭ',
'Ṯ' => 'Ṯ',
'ṯ' => 'ṯ',
'Ṱ' => 'Ṱ',
'ṱ' => 'ṱ',
'Ṳ' => 'Ṳ',
'ṳ' => 'ṳ',
'Ṵ' => 'Ṵ',
'ṵ' => 'ṵ',
'Ṷ' => 'Ṷ',
'ṷ' => 'ṷ',
'Ṹ' => 'Ṹ',
'ṹ' => 'ṹ',
'Ṻ' => 'Ṻ',
'ṻ' => 'ṻ',
'Ṽ' => 'Ṽ',
'ṽ' => 'ṽ',
'Ṿ' => 'Ṿ',
'ṿ' => 'ṿ',
'Ẁ' => 'Ẁ',
'ẁ' => 'ẁ',
'Ẃ' => 'Ẃ',
'ẃ' => 'ẃ',
'Ẅ' => 'Ẅ',
'ẅ' => 'ẅ',
'Ẇ' => 'Ẇ',
'ẇ' => 'ẇ',
'Ẉ' => 'Ẉ',
'ẉ' => 'ẉ',
'Ẋ' => 'Ẋ',
'ẋ' => 'ẋ',
'Ẍ' => 'Ẍ',
'ẍ' => 'ẍ',
'Ẏ' => 'Ẏ',
'ẏ' => 'ẏ',
'Ẑ' => 'Ẑ',
'ẑ' => 'ẑ',
'Ẓ' => 'Ẓ',
'ẓ' => 'ẓ',
'Ẕ' => 'Ẕ',
'ẕ' => 'ẕ',
'ẖ' => 'ẖ',
'ẗ' => 'ẗ',
'ẘ' => 'ẘ',
'ẙ' => 'ẙ',
'ẛ' => 'ẛ',
'Ạ' => 'Ạ',
'ạ' => 'ạ',
'Ả' => 'Ả',
'ả' => 'ả',
'Ấ' => 'Ấ',
'ấ' => 'ấ',
'Ầ' => 'Ầ',
'ầ' => 'ầ',
'Ẩ' => 'Ẩ',
'ẩ' => 'ẩ',
'Ẫ' => 'Ẫ',
'ẫ' => 'ẫ',
'Ậ' => 'Ậ',
'ậ' => 'ậ',
'Ắ' => 'Ắ',
'ắ' => 'ắ',
'Ằ' => 'Ằ',
'ằ' => 'ằ',
'Ẳ' => 'Ẳ',
'ẳ' => 'ẳ',
'Ẵ' => 'Ẵ',
'ẵ' => 'ẵ',
'Ặ' => 'Ặ',
'ặ' => 'ặ',
'Ẹ' => 'Ẹ',
'ẹ' => 'ẹ',
'Ẻ' => 'Ẻ',
'ẻ' => 'ẻ',
'Ẽ' => 'Ẽ',
'ẽ' => 'ẽ',
'Ế' => 'Ế',
'ế' => 'ế',
'Ề' => 'Ề',
'ề' => 'ề',
'Ể' => 'Ể',
'ể' => 'ể',
'Ễ' => 'Ễ',
'ễ' => 'ễ',
'Ệ' => 'Ệ',
'ệ' => 'ệ',
'Ỉ' => 'Ỉ',
'ỉ' => 'ỉ',
'Ị' => 'Ị',
'ị' => 'ị',
'Ọ' => 'Ọ',
'ọ' => 'ọ',
'Ỏ' => 'Ỏ',
'ỏ' => 'ỏ',
'Ố' => 'Ố',
'ố' => 'ố',
'Ồ' => 'Ồ',
'ồ' => 'ồ',
'Ổ' => 'Ổ',
'ổ' => 'ổ',
'Ỗ' => 'Ỗ',
'ỗ' => 'ỗ',
'Ộ' => 'Ộ',
'ộ' => 'ộ',
'Ớ' => 'Ớ',
'ớ' => 'ớ',
'Ờ' => 'Ờ',
'ờ' => 'ờ',
'Ở' => 'Ở',
'ở' => 'ở',
'Ỡ' => 'Ỡ',
'ỡ' => 'ỡ',
'Ợ' => 'Ợ',
'ợ' => 'ợ',
'Ụ' => 'Ụ',
'ụ' => 'ụ',
'Ủ' => 'Ủ',
'ủ' => 'ủ',
'Ứ' => 'Ứ',
'ứ' => 'ứ',
'Ừ' => 'Ừ',
'ừ' => 'ừ',
'Ử' => 'Ử',
'ử' => 'ử',
'Ữ' => 'Ữ',
'ữ' => 'ữ',
'Ự' => 'Ự',
'ự' => 'ự',
'Ỳ' => 'Ỳ',
'ỳ' => 'ỳ',
'Ỵ' => 'Ỵ',
'ỵ' => 'ỵ',
'Ỷ' => 'Ỷ',
'ỷ' => 'ỷ',
'Ỹ' => 'Ỹ',
'ỹ' => 'ỹ',
'ἀ' => 'ἀ',
'ἁ' => 'ἁ',
'ἂ' => 'ἂ',
'ἃ' => 'ἃ',
'ἄ' => 'ἄ',
'ἅ' => 'ἅ',
'ἆ' => 'ἆ',
'ἇ' => 'ἇ',
'Ἀ' => 'Ἀ',
'Ἁ' => 'Ἁ',
'Ἂ' => 'Ἂ',
'Ἃ' => 'Ἃ',
'Ἄ' => 'Ἄ',
'Ἅ' => 'Ἅ',
'Ἆ' => 'Ἆ',
'Ἇ' => 'Ἇ',
'ἐ' => 'ἐ',
'ἑ' => 'ἑ',
'ἒ' => 'ἒ',
'ἓ' => 'ἓ',
'ἔ' => 'ἔ',
'ἕ' => 'ἕ',
'Ἐ' => 'Ἐ',
'Ἑ' => 'Ἑ',
'Ἒ' => 'Ἒ',
'Ἓ' => 'Ἓ',
'Ἔ' => 'Ἔ',
'Ἕ' => 'Ἕ',
'ἠ' => 'ἠ',
'ἡ' => 'ἡ',
'ἢ' => 'ἢ',
'ἣ' => 'ἣ',
'ἤ' => 'ἤ',
'ἥ' => 'ἥ',
'ἦ' => 'ἦ',
'ἧ' => 'ἧ',
'Ἠ' => 'Ἠ',
'Ἡ' => 'Ἡ',
'Ἢ' => 'Ἢ',
'Ἣ' => 'Ἣ',
'Ἤ' => 'Ἤ',
'Ἥ' => 'Ἥ',
'Ἦ' => 'Ἦ',
'Ἧ' => 'Ἧ',
'ἰ' => 'ἰ',
'ἱ' => 'ἱ',
'ἲ' => 'ἲ',
'ἳ' => 'ἳ',
'ἴ' => 'ἴ',
'ἵ' => 'ἵ',
'ἶ' => 'ἶ',
'ἷ' => 'ἷ',
'Ἰ' => 'Ἰ',
'Ἱ' => 'Ἱ',
'Ἲ' => 'Ἲ',
'Ἳ' => 'Ἳ',
'Ἴ' => 'Ἴ',
'Ἵ' => 'Ἵ',
'Ἶ' => 'Ἶ',
'Ἷ' => 'Ἷ',
'ὀ' => 'ὀ',
'ὁ' => 'ὁ',
'ὂ' => 'ὂ',
'ὃ' => 'ὃ',
'ὄ' => 'ὄ',
'ὅ' => 'ὅ',
'Ὀ' => 'Ὀ',
'Ὁ' => 'Ὁ',
'Ὂ' => 'Ὂ',
'Ὃ' => 'Ὃ',
'Ὄ' => 'Ὄ',
'Ὅ' => 'Ὅ',
'ὐ' => 'ὐ',
'ὑ' => 'ὑ',
'ὒ' => 'ὒ',
'ὓ' => 'ὓ',
'ὔ' => 'ὔ',
'ὕ' => 'ὕ',
'ὖ' => 'ὖ',
'ὗ' => 'ὗ',
'Ὑ' => 'Ὑ',
'Ὓ' => 'Ὓ',
'Ὕ' => 'Ὕ',
'Ὗ' => 'Ὗ',
'ὠ' => 'ὠ',
'ὡ' => 'ὡ',
'ὢ' => 'ὢ',
'ὣ' => 'ὣ',
'ὤ' => 'ὤ',
'ὥ' => 'ὥ',
'ὦ' => 'ὦ',
'ὧ' => 'ὧ',
'Ὠ' => 'Ὠ',
'Ὡ' => 'Ὡ',
'Ὢ' => 'Ὢ',
'Ὣ' => 'Ὣ',
'Ὤ' => 'Ὤ',
'Ὥ' => 'Ὥ',
'Ὦ' => 'Ὦ',
'Ὧ' => 'Ὧ',
'ὰ' => 'ὰ',
'ὲ' => 'ὲ',
'ὴ' => 'ὴ',
'ὶ' => 'ὶ',
'ὸ' => 'ὸ',
'ὺ' => 'ὺ',
'ὼ' => 'ὼ',
'ᾀ' => 'ᾀ',
'ᾁ' => 'ᾁ',
'ᾂ' => 'ᾂ',
'ᾃ' => 'ᾃ',
'ᾄ' => 'ᾄ',
'ᾅ' => 'ᾅ',
'ᾆ' => 'ᾆ',
'ᾇ' => 'ᾇ',
'ᾈ' => 'ᾈ',
'ᾉ' => 'ᾉ',
'ᾊ' => 'ᾊ',
'ᾋ' => 'ᾋ',
'ᾌ' => 'ᾌ',
'ᾍ' => 'ᾍ',
'ᾎ' => 'ᾎ',
'ᾏ' => 'ᾏ',
'ᾐ' => 'ᾐ',
'ᾑ' => 'ᾑ',
'ᾒ' => 'ᾒ',
'ᾓ' => 'ᾓ',
'ᾔ' => 'ᾔ',
'ᾕ' => 'ᾕ',
'ᾖ' => 'ᾖ',
'ᾗ' => 'ᾗ',
'ᾘ' => 'ᾘ',
'ᾙ' => 'ᾙ',
'ᾚ' => 'ᾚ',
'ᾛ' => 'ᾛ',
'ᾜ' => 'ᾜ',
'ᾝ' => 'ᾝ',
'ᾞ' => 'ᾞ',
'ᾟ' => 'ᾟ',
'ᾠ' => 'ᾠ',
'ᾡ' => 'ᾡ',
'ᾢ' => 'ᾢ',
'ᾣ' => 'ᾣ',
'ᾤ' => 'ᾤ',
'ᾥ' => 'ᾥ',
'ᾦ' => 'ᾦ',
'ᾧ' => 'ᾧ',
'ᾨ' => 'ᾨ',
'ᾩ' => 'ᾩ',
'ᾪ' => 'ᾪ',
'ᾫ' => 'ᾫ',
'ᾬ' => 'ᾬ',
'ᾭ' => 'ᾭ',
'ᾮ' => 'ᾮ',
'ᾯ' => 'ᾯ',
'ᾰ' => 'ᾰ',
'ᾱ' => 'ᾱ',
'ᾲ' => 'ᾲ',
'ᾳ' => 'ᾳ',
'ᾴ' => 'ᾴ',
'ᾶ' => 'ᾶ',
'ᾷ' => 'ᾷ',
'Ᾰ' => 'Ᾰ',
'Ᾱ' => 'Ᾱ',
'Ὰ' => 'Ὰ',
'ᾼ' => 'ᾼ',
'῁' => '῁',
'ῂ' => 'ῂ',
'ῃ' => 'ῃ',
'ῄ' => 'ῄ',
'ῆ' => 'ῆ',
'ῇ' => 'ῇ',
'Ὲ' => 'Ὲ',
'Ὴ' => 'Ὴ',
'ῌ' => 'ῌ',
'῍' => '῍',
'῎' => '῎',
'῏' => '῏',
'ῐ' => 'ῐ',
'ῑ' => 'ῑ',
'ῒ' => 'ῒ',
'ῖ' => 'ῖ',
'ῗ' => 'ῗ',
'Ῐ' => 'Ῐ',
'Ῑ' => 'Ῑ',
'Ὶ' => 'Ὶ',
'῝' => '῝',
'῞' => '῞',
'῟' => '῟',
'ῠ' => 'ῠ',
'ῡ' => 'ῡ',
'ῢ' => 'ῢ',
'ῤ' => 'ῤ',
'ῥ' => 'ῥ',
'ῦ' => 'ῦ',
'ῧ' => 'ῧ',
'Ῠ' => 'Ῠ',
'Ῡ' => 'Ῡ',
'Ὺ' => 'Ὺ',
'Ῥ' => 'Ῥ',
'῭' => '῭',
'ῲ' => 'ῲ',
'ῳ' => 'ῳ',
'ῴ' => 'ῴ',
'ῶ' => 'ῶ',
'ῷ' => 'ῷ',
'Ὸ' => 'Ὸ',
'Ὼ' => 'Ὼ',
'ῼ' => 'ῼ',
'↚' => '↚',
'↛' => '↛',
'↮' => '↮',
'⇍' => '⇍',
'⇎' => '⇎',
'⇏' => '⇏',
'∄' => '∄',
'∉' => '∉',
'∌' => '∌',
'∤' => '∤',
'∦' => '∦',
'≁' => '≁',
'≄' => '≄',
'≇' => '≇',
'≉' => '≉',
'≠' => '≠',
'≢' => '≢',
'≭' => '≭',
'≮' => '≮',
'≯' => '≯',
'≰' => '≰',
'≱' => '≱',
'≴' => '≴',
'≵' => '≵',
'≸' => '≸',
'≹' => '≹',
'⊀' => '⊀',
'⊁' => '⊁',
'⊄' => '⊄',
'⊅' => '⊅',
'⊈' => '⊈',
'⊉' => '⊉',
'⊬' => '⊬',
'⊭' => '⊭',
'⊮' => '⊮',
'⊯' => '⊯',
'⋠' => '⋠',
'⋡' => '⋡',
'⋢' => '⋢',
'⋣' => '⋣',
'⋪' => '⋪',
'⋫' => '⋫',
'⋬' => '⋬',
'⋭' => '⋭',
'が' => 'が',
'ぎ' => 'ぎ',
'ぐ' => 'ぐ',
'げ' => 'げ',
'ご' => 'ご',
'ざ' => 'ざ',
'じ' => 'じ',
'ず' => 'ず',
'ぜ' => 'ぜ',
'ぞ' => 'ぞ',
'だ' => 'だ',
'ぢ' => 'ぢ',
'づ' => 'づ',
'で' => 'で',
'ど' => 'ど',
'ば' => 'ば',
'ぱ' => 'ぱ',
'び' => 'び',
'ぴ' => 'ぴ',
'ぶ' => 'ぶ',
'ぷ' => 'ぷ',
'べ' => 'べ',
'ぺ' => 'ぺ',
'ぼ' => 'ぼ',
'ぽ' => 'ぽ',
'ゔ' => 'ゔ',
'ゞ' => 'ゞ',
'ガ' => 'ガ',
'ギ' => 'ギ',
'グ' => 'グ',
'ゲ' => 'ゲ',
'ゴ' => 'ゴ',
'ザ' => 'ザ',
'ジ' => 'ジ',
'ズ' => 'ズ',
'ゼ' => 'ゼ',
'ゾ' => 'ゾ',
'ダ' => 'ダ',
'ヂ' => 'ヂ',
'ヅ' => 'ヅ',
'デ' => 'デ',
'ド' => 'ド',
'バ' => 'バ',
'パ' => 'パ',
'ビ' => 'ビ',
'ピ' => 'ピ',
'ブ' => 'ブ',
'プ' => 'プ',
'ベ' => 'ベ',
'ペ' => 'ペ',
'ボ' => 'ボ',
'ポ' => 'ポ',
'ヴ' => 'ヴ',
'ヷ' => 'ヷ',
'ヸ' => 'ヸ',
'ヹ' => 'ヹ',
'ヺ' => 'ヺ',
'ヾ' => 'ヾ',
'𑂚' => '𑂚',
'𑂜' => '𑂜',
'𑂫' => '𑂫',
'𑄮' => '𑄮',
'𑄯' => '𑄯',
'𑍋' => '𑍋',
'𑍌' => '𑍌',
'𑒻' => '𑒻',
'𑒼' => '𑒼',
'𑒾' => '𑒾',
'𑖺' => '𑖺',
'𑖻' => '𑖻',
'𑤸' => '𑤸',
);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,876 @@
<?php
return array (
'̀' => 230,
'́' => 230,
'̂' => 230,
'̃' => 230,
'̄' => 230,
'̅' => 230,
'̆' => 230,
'̇' => 230,
'̈' => 230,
'̉' => 230,
'̊' => 230,
'̋' => 230,
'̌' => 230,
'̍' => 230,
'̎' => 230,
'̏' => 230,
'̐' => 230,
'̑' => 230,
'̒' => 230,
'̓' => 230,
'̔' => 230,
'̕' => 232,
'̖' => 220,
'̗' => 220,
'̘' => 220,
'̙' => 220,
'̚' => 232,
'̛' => 216,
'̜' => 220,
'̝' => 220,
'̞' => 220,
'̟' => 220,
'̠' => 220,
'̡' => 202,
'̢' => 202,
'̣' => 220,
'̤' => 220,
'̥' => 220,
'̦' => 220,
'̧' => 202,
'̨' => 202,
'̩' => 220,
'̪' => 220,
'̫' => 220,
'̬' => 220,
'̭' => 220,
'̮' => 220,
'̯' => 220,
'̰' => 220,
'̱' => 220,
'̲' => 220,
'̳' => 220,
'̴' => 1,
'̵' => 1,
'̶' => 1,
'̷' => 1,
'̸' => 1,
'̹' => 220,
'̺' => 220,
'̻' => 220,
'̼' => 220,
'̽' => 230,
'̾' => 230,
'̿' => 230,
'̀' => 230,
'́' => 230,
'͂' => 230,
'̓' => 230,
'̈́' => 230,
'ͅ' => 240,
'͆' => 230,
'͇' => 220,
'͈' => 220,
'͉' => 220,
'͊' => 230,
'͋' => 230,
'͌' => 230,
'͍' => 220,
'͎' => 220,
'͐' => 230,
'͑' => 230,
'͒' => 230,
'͓' => 220,
'͔' => 220,
'͕' => 220,
'͖' => 220,
'͗' => 230,
'͘' => 232,
'͙' => 220,
'͚' => 220,
'͛' => 230,
'͜' => 233,
'͝' => 234,
'͞' => 234,
'͟' => 233,
'͠' => 234,
'͡' => 234,
'͢' => 233,
'ͣ' => 230,
'ͤ' => 230,
'ͥ' => 230,
'ͦ' => 230,
'ͧ' => 230,
'ͨ' => 230,
'ͩ' => 230,
'ͪ' => 230,
'ͫ' => 230,
'ͬ' => 230,
'ͭ' => 230,
'ͮ' => 230,
'ͯ' => 230,
'҃' => 230,
'҄' => 230,
'҅' => 230,
'҆' => 230,
'҇' => 230,
'֑' => 220,
'֒' => 230,
'֓' => 230,
'֔' => 230,
'֕' => 230,
'֖' => 220,
'֗' => 230,
'֘' => 230,
'֙' => 230,
'֚' => 222,
'֛' => 220,
'֜' => 230,
'֝' => 230,
'֞' => 230,
'֟' => 230,
'֠' => 230,
'֡' => 230,
'֢' => 220,
'֣' => 220,
'֤' => 220,
'֥' => 220,
'֦' => 220,
'֧' => 220,
'֨' => 230,
'֩' => 230,
'֪' => 220,
'֫' => 230,
'֬' => 230,
'֭' => 222,
'֮' => 228,
'֯' => 230,
'ְ' => 10,
'ֱ' => 11,
'ֲ' => 12,
'ֳ' => 13,
'ִ' => 14,
'ֵ' => 15,
'ֶ' => 16,
'ַ' => 17,
'ָ' => 18,
'ֹ' => 19,
'ֺ' => 19,
'ֻ' => 20,
'ּ' => 21,
'ֽ' => 22,
'ֿ' => 23,
'ׁ' => 24,
'ׂ' => 25,
'ׄ' => 230,
'ׅ' => 220,
'ׇ' => 18,
'ؐ' => 230,
'ؑ' => 230,
'ؒ' => 230,
'ؓ' => 230,
'ؔ' => 230,
'ؕ' => 230,
'ؖ' => 230,
'ؗ' => 230,
'ؘ' => 30,
'ؙ' => 31,
'ؚ' => 32,
'ً' => 27,
'ٌ' => 28,
'ٍ' => 29,
'َ' => 30,
'ُ' => 31,
'ِ' => 32,
'ّ' => 33,
'ْ' => 34,
'ٓ' => 230,
'ٔ' => 230,
'ٕ' => 220,
'ٖ' => 220,
'ٗ' => 230,
'٘' => 230,
'ٙ' => 230,
'ٚ' => 230,
'ٛ' => 230,
'ٜ' => 220,
'ٝ' => 230,
'ٞ' => 230,
'ٟ' => 220,
'ٰ' => 35,
'ۖ' => 230,
'ۗ' => 230,
'ۘ' => 230,
'ۙ' => 230,
'ۚ' => 230,
'ۛ' => 230,
'ۜ' => 230,
'۟' => 230,
'۠' => 230,
'ۡ' => 230,
'ۢ' => 230,
'ۣ' => 220,
'ۤ' => 230,
'ۧ' => 230,
'ۨ' => 230,
'۪' => 220,
'۫' => 230,
'۬' => 230,
'ۭ' => 220,
'ܑ' => 36,
'ܰ' => 230,
'ܱ' => 220,
'ܲ' => 230,
'ܳ' => 230,
'ܴ' => 220,
'ܵ' => 230,
'ܶ' => 230,
'ܷ' => 220,
'ܸ' => 220,
'ܹ' => 220,
'ܺ' => 230,
'ܻ' => 220,
'ܼ' => 220,
'ܽ' => 230,
'ܾ' => 220,
'ܿ' => 230,
'݀' => 230,
'݁' => 230,
'݂' => 220,
'݃' => 230,
'݄' => 220,
'݅' => 230,
'݆' => 220,
'݇' => 230,
'݈' => 220,
'݉' => 230,
'݊' => 230,
'߫' => 230,
'߬' => 230,
'߭' => 230,
'߮' => 230,
'߯' => 230,
'߰' => 230,
'߱' => 230,
'߲' => 220,
'߳' => 230,
'߽' => 220,
'ࠖ' => 230,
'ࠗ' => 230,
'࠘' => 230,
'࠙' => 230,
'ࠛ' => 230,
'ࠜ' => 230,
'ࠝ' => 230,
'ࠞ' => 230,
'ࠟ' => 230,
'ࠠ' => 230,
'ࠡ' => 230,
'ࠢ' => 230,
'ࠣ' => 230,
'ࠥ' => 230,
'ࠦ' => 230,
'ࠧ' => 230,
'ࠩ' => 230,
'ࠪ' => 230,
'ࠫ' => 230,
'ࠬ' => 230,
'࠭' => 230,
'࡙' => 220,
'࡚' => 220,
'࡛' => 220,
'࣓' => 220,
'ࣔ' => 230,
'ࣕ' => 230,
'ࣖ' => 230,
'ࣗ' => 230,
'ࣘ' => 230,
'ࣙ' => 230,
'ࣚ' => 230,
'ࣛ' => 230,
'ࣜ' => 230,
'ࣝ' => 230,
'ࣞ' => 230,
'ࣟ' => 230,
'࣠' => 230,
'࣡' => 230,
'ࣣ' => 220,
'ࣤ' => 230,
'ࣥ' => 230,
'ࣦ' => 220,
'ࣧ' => 230,
'ࣨ' => 230,
'ࣩ' => 220,
'࣪' => 230,
'࣫' => 230,
'࣬' => 230,
'࣭' => 220,
'࣮' => 220,
'࣯' => 220,
'ࣰ' => 27,
'ࣱ' => 28,
'ࣲ' => 29,
'ࣳ' => 230,
'ࣴ' => 230,
'ࣵ' => 230,
'ࣶ' => 220,
'ࣷ' => 230,
'ࣸ' => 230,
'ࣹ' => 220,
'ࣺ' => 220,
'ࣻ' => 230,
'ࣼ' => 230,
'ࣽ' => 230,
'ࣾ' => 230,
'ࣿ' => 230,
'़' => 7,
'्' => 9,
'॑' => 230,
'॒' => 220,
'॓' => 230,
'॔' => 230,
'়' => 7,
'্' => 9,
'৾' => 230,
'਼' => 7,
'੍' => 9,
'઼' => 7,
'્' => 9,
'଼' => 7,
'୍' => 9,
'்' => 9,
'్' => 9,
'ౕ' => 84,
'ౖ' => 91,
'಼' => 7,
'್' => 9,
'഻' => 9,
'഼' => 9,
'്' => 9,
'්' => 9,
'ุ' => 103,
'ู' => 103,
'ฺ' => 9,
'่' => 107,
'้' => 107,
'๊' => 107,
'๋' => 107,
'ຸ' => 118,
'ູ' => 118,
'຺' => 9,
'່' => 122,
'້' => 122,
'໊' => 122,
'໋' => 122,
'༘' => 220,
'༙' => 220,
'༵' => 220,
'༷' => 220,
'༹' => 216,
'ཱ' => 129,
'ི' => 130,
'ུ' => 132,
'ེ' => 130,
'ཻ' => 130,
'ོ' => 130,
'ཽ' => 130,
'ྀ' => 130,
'ྂ' => 230,
'ྃ' => 230,
'྄' => 9,
'྆' => 230,
'྇' => 230,
'࿆' => 220,
'့' => 7,
'္' => 9,
'်' => 9,
'ႍ' => 220,
'፝' => 230,
'፞' => 230,
'፟' => 230,
'᜔' => 9,
'᜴' => 9,
'្' => 9,
'៝' => 230,
'ᢩ' => 228,
'᤹' => 222,
'᤺' => 230,
'᤻' => 220,
'ᨗ' => 230,
'ᨘ' => 220,
'᩠' => 9,
'᩵' => 230,
'᩶' => 230,
'᩷' => 230,
'᩸' => 230,
'᩹' => 230,
'᩺' => 230,
'᩻' => 230,
'᩼' => 230,
'᩿' => 220,
'᪰' => 230,
'᪱' => 230,
'᪲' => 230,
'᪳' => 230,
'᪴' => 230,
'᪵' => 220,
'᪶' => 220,
'᪷' => 220,
'᪸' => 220,
'᪹' => 220,
'᪺' => 220,
'᪻' => 230,
'᪼' => 230,
'᪽' => 220,
'ᪿ' => 220,
'ᫀ' => 220,
'᬴' => 7,
'᭄' => 9,
'᭫' => 230,
'᭬' => 220,
'᭭' => 230,
'᭮' => 230,
'᭯' => 230,
'᭰' => 230,
'᭱' => 230,
'᭲' => 230,
'᭳' => 230,
'᮪' => 9,
'᮫' => 9,
'᯦' => 7,
'᯲' => 9,
'᯳' => 9,
'᰷' => 7,
'᳐' => 230,
'᳑' => 230,
'᳒' => 230,
'᳔' => 1,
'᳕' => 220,
'᳖' => 220,
'᳗' => 220,
'᳘' => 220,
'᳙' => 220,
'᳚' => 230,
'᳛' => 230,
'᳜' => 220,
'᳝' => 220,
'᳞' => 220,
'᳟' => 220,
'᳠' => 230,
'᳢' => 1,
'᳣' => 1,
'᳤' => 1,
'᳥' => 1,
'᳦' => 1,
'᳧' => 1,
'᳨' => 1,
'᳭' => 220,
'᳴' => 230,
'᳸' => 230,
'᳹' => 230,
'᷀' => 230,
'᷁' => 230,
'᷂' => 220,
'᷃' => 230,
'᷄' => 230,
'᷅' => 230,
'᷆' => 230,
'᷇' => 230,
'᷈' => 230,
'᷉' => 230,
'᷊' => 220,
'᷋' => 230,
'᷌' => 230,
'᷍' => 234,
'᷎' => 214,
'᷏' => 220,
'᷐' => 202,
'᷑' => 230,
'᷒' => 230,
'ᷓ' => 230,
'ᷔ' => 230,
'ᷕ' => 230,
'ᷖ' => 230,
'ᷗ' => 230,
'ᷘ' => 230,
'ᷙ' => 230,
'ᷚ' => 230,
'ᷛ' => 230,
'ᷜ' => 230,
'ᷝ' => 230,
'ᷞ' => 230,
'ᷟ' => 230,
'ᷠ' => 230,
'ᷡ' => 230,
'ᷢ' => 230,
'ᷣ' => 230,
'ᷤ' => 230,
'ᷥ' => 230,
'ᷦ' => 230,
'ᷧ' => 230,
'ᷨ' => 230,
'ᷩ' => 230,
'ᷪ' => 230,
'ᷫ' => 230,
'ᷬ' => 230,
'ᷭ' => 230,
'ᷮ' => 230,
'ᷯ' => 230,
'ᷰ' => 230,
'ᷱ' => 230,
'ᷲ' => 230,
'ᷳ' => 230,
'ᷴ' => 230,
'᷵' => 230,
'᷶' => 232,
'᷷' => 228,
'᷸' => 228,
'᷹' => 220,
'᷻' => 230,
'᷼' => 233,
'᷽' => 220,
'᷾' => 230,
'᷿' => 220,
'⃐' => 230,
'⃑' => 230,
'⃒' => 1,
'⃓' => 1,
'⃔' => 230,
'⃕' => 230,
'⃖' => 230,
'⃗' => 230,
'⃘' => 1,
'⃙' => 1,
'⃚' => 1,
'⃛' => 230,
'⃜' => 230,
'⃡' => 230,
'⃥' => 1,
'⃦' => 1,
'⃧' => 230,
'⃨' => 220,
'⃩' => 230,
'⃪' => 1,
'⃫' => 1,
'⃬' => 220,
'⃭' => 220,
'⃮' => 220,
'⃯' => 220,
'⃰' => 230,
'⳯' => 230,
'⳰' => 230,
'⳱' => 230,
'⵿' => 9,
'ⷠ' => 230,
'ⷡ' => 230,
'ⷢ' => 230,
'ⷣ' => 230,
'ⷤ' => 230,
'ⷥ' => 230,
'ⷦ' => 230,
'ⷧ' => 230,
'ⷨ' => 230,
'ⷩ' => 230,
'ⷪ' => 230,
'ⷫ' => 230,
'ⷬ' => 230,
'ⷭ' => 230,
'ⷮ' => 230,
'ⷯ' => 230,
'ⷰ' => 230,
'ⷱ' => 230,
'ⷲ' => 230,
'ⷳ' => 230,
'ⷴ' => 230,
'ⷵ' => 230,
'ⷶ' => 230,
'ⷷ' => 230,
'ⷸ' => 230,
'ⷹ' => 230,
'ⷺ' => 230,
'ⷻ' => 230,
'ⷼ' => 230,
'ⷽ' => 230,
'ⷾ' => 230,
'ⷿ' => 230,
'〪' => 218,
'〫' => 228,
'〬' => 232,
'〭' => 222,
'〮' => 224,
'〯' => 224,
'゙' => 8,
'゚' => 8,
'꙯' => 230,
'ꙴ' => 230,
'ꙵ' => 230,
'ꙶ' => 230,
'ꙷ' => 230,
'ꙸ' => 230,
'ꙹ' => 230,
'ꙺ' => 230,
'ꙻ' => 230,
'꙼' => 230,
'꙽' => 230,
'ꚞ' => 230,
'ꚟ' => 230,
'꛰' => 230,
'꛱' => 230,
'꠆' => 9,
'꠬' => 9,
'꣄' => 9,
'꣠' => 230,
'꣡' => 230,
'꣢' => 230,
'꣣' => 230,
'꣤' => 230,
'꣥' => 230,
'꣦' => 230,
'꣧' => 230,
'꣨' => 230,
'꣩' => 230,
'꣪' => 230,
'꣫' => 230,
'꣬' => 230,
'꣭' => 230,
'꣮' => 230,
'꣯' => 230,
'꣰' => 230,
'꣱' => 230,
'꤫' => 220,
'꤬' => 220,
'꤭' => 220,
'꥓' => 9,
'꦳' => 7,
'꧀' => 9,
'ꪰ' => 230,
'ꪲ' => 230,
'ꪳ' => 230,
'ꪴ' => 220,
'ꪷ' => 230,
'ꪸ' => 230,
'ꪾ' => 230,
'꪿' => 230,
'꫁' => 230,
'꫶' => 9,
'꯭' => 9,
'ﬞ' => 26,
'︠' => 230,
'︡' => 230,
'︢' => 230,
'︣' => 230,
'︤' => 230,
'︥' => 230,
'︦' => 230,
'︧' => 220,
'︨' => 220,
'︩' => 220,
'︪' => 220,
'︫' => 220,
'︬' => 220,
'︭' => 220,
'︮' => 230,
'︯' => 230,
'𐇽' => 220,
'𐋠' => 220,
'𐍶' => 230,
'𐍷' => 230,
'𐍸' => 230,
'𐍹' => 230,
'𐍺' => 230,
'𐨍' => 220,
'𐨏' => 230,
'𐨸' => 230,
'𐨹' => 1,
'𐨺' => 220,
'𐨿' => 9,
'𐫥' => 230,
'𐫦' => 220,
'𐴤' => 230,
'𐴥' => 230,
'𐴦' => 230,
'𐴧' => 230,
'𐺫' => 230,
'𐺬' => 230,
'𐽆' => 220,
'𐽇' => 220,
'𐽈' => 230,
'𐽉' => 230,
'𐽊' => 230,
'𐽋' => 220,
'𐽌' => 230,
'𐽍' => 220,
'𐽎' => 220,
'𐽏' => 220,
'𐽐' => 220,
'𑁆' => 9,
'𑁿' => 9,
'𑂹' => 9,
'𑂺' => 7,
'𑄀' => 230,
'𑄁' => 230,
'𑄂' => 230,
'𑄳' => 9,
'𑄴' => 9,
'𑅳' => 7,
'𑇀' => 9,
'𑇊' => 7,
'𑈵' => 9,
'𑈶' => 7,
'𑋩' => 7,
'𑋪' => 9,
'𑌻' => 7,
'𑌼' => 7,
'𑍍' => 9,
'𑍦' => 230,
'𑍧' => 230,
'𑍨' => 230,
'𑍩' => 230,
'𑍪' => 230,
'𑍫' => 230,
'𑍬' => 230,
'𑍰' => 230,
'𑍱' => 230,
'𑍲' => 230,
'𑍳' => 230,
'𑍴' => 230,
'𑑂' => 9,
'𑑆' => 7,
'𑑞' => 230,
'𑓂' => 9,
'𑓃' => 7,
'𑖿' => 9,
'𑗀' => 7,
'𑘿' => 9,
'𑚶' => 9,
'𑚷' => 7,
'𑜫' => 9,
'𑠹' => 9,
'𑠺' => 7,
'𑤽' => 9,
'𑤾' => 9,
'𑥃' => 7,
'𑧠' => 9,
'𑨴' => 9,
'𑩇' => 9,
'𑪙' => 9,
'𑰿' => 9,
'𑵂' => 7,
'𑵄' => 9,
'𑵅' => 9,
'𑶗' => 9,
'𖫰' => 1,
'𖫱' => 1,
'𖫲' => 1,
'𖫳' => 1,
'𖫴' => 1,
'𖬰' => 230,
'𖬱' => 230,
'𖬲' => 230,
'𖬳' => 230,
'𖬴' => 230,
'𖬵' => 230,
'𖬶' => 230,
'𖿰' => 6,
'𖿱' => 6,
'𛲞' => 1,
'𝅥' => 216,
'𝅦' => 216,
'𝅧' => 1,
'𝅨' => 1,
'𝅩' => 1,
'𝅭' => 226,
'𝅮' => 216,
'𝅯' => 216,
'𝅰' => 216,
'𝅱' => 216,
'𝅲' => 216,
'𝅻' => 220,
'𝅼' => 220,
'𝅽' => 220,
'𝅾' => 220,
'𝅿' => 220,
'𝆀' => 220,
'𝆁' => 220,
'𝆂' => 220,
'𝆅' => 230,
'𝆆' => 230,
'𝆇' => 230,
'𝆈' => 230,
'𝆉' => 230,
'𝆊' => 220,
'𝆋' => 220,
'𝆪' => 230,
'𝆫' => 230,
'𝆬' => 230,
'𝆭' => 230,
'𝉂' => 230,
'𝉃' => 230,
'𝉄' => 230,
'𞀀' => 230,
'𞀁' => 230,
'𞀂' => 230,
'𞀃' => 230,
'𞀄' => 230,
'𞀅' => 230,
'𞀆' => 230,
'𞀈' => 230,
'𞀉' => 230,
'𞀊' => 230,
'𞀋' => 230,
'𞀌' => 230,
'𞀍' => 230,
'𞀎' => 230,
'𞀏' => 230,
'𞀐' => 230,
'𞀑' => 230,
'𞀒' => 230,
'𞀓' => 230,
'𞀔' => 230,
'𞀕' => 230,
'𞀖' => 230,
'𞀗' => 230,
'𞀘' => 230,
'𞀛' => 230,
'𞀜' => 230,
'𞀝' => 230,
'𞀞' => 230,
'𞀟' => 230,
'𞀠' => 230,
'𞀡' => 230,
'𞀣' => 230,
'𞀤' => 230,
'𞀦' => 230,
'𞀧' => 230,
'𞀨' => 230,
'𞀩' => 230,
'𞀪' => 230,
'𞄰' => 230,
'𞄱' => 230,
'𞄲' => 230,
'𞄳' => 230,
'𞄴' => 230,
'𞄵' => 230,
'𞄶' => 230,
'𞋬' => 230,
'𞋭' => 230,
'𞋮' => 230,
'𞋯' => 230,
'𞣐' => 220,
'𞣑' => 220,
'𞣒' => 220,
'𞣓' => 220,
'𞣔' => 220,
'𞣕' => 220,
'𞣖' => 220,
'𞥄' => 230,
'𞥅' => 230,
'𞥆' => 230,
'𞥇' => 230,
'𞥈' => 230,
'𞥉' => 230,
'𞥊' => 7,
);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,23 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Intl\Normalizer as p;
if (\PHP_VERSION_ID >= 80000) {
return require __DIR__.'/bootstrap80.php';
}
if (!function_exists('normalizer_is_normalized')) {
function normalizer_is_normalized($string, $form = p\Normalizer::FORM_C) { return p\Normalizer::isNormalized($string, $form); }
}
if (!function_exists('normalizer_normalize')) {
function normalizer_normalize($string, $form = p\Normalizer::FORM_C) { return p\Normalizer::normalize($string, $form); }
}

View File

@@ -0,0 +1,19 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Intl\Normalizer as p;
if (!function_exists('normalizer_is_normalized')) {
function normalizer_is_normalized(?string $string, ?int $form = p\Normalizer::FORM_C): bool { return p\Normalizer::isNormalized((string) $string, (int) $form); }
}
if (!function_exists('normalizer_normalize')) {
function normalizer_normalize(?string $string, ?int $form = p\Normalizer::FORM_C): string|false { return p\Normalizer::normalize((string) $string, (int) $form); }
}

View File

@@ -0,0 +1,19 @@
Copyright (c) 2015-present Fabien Potencier
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 Load Diff

View File

@@ -0,0 +1,13 @@
Symfony Polyfill / Mbstring
===========================
This component provides a partial, native PHP implementation for the
[Mbstring](https://php.net/mbstring) extension.
More information can be found in the
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).
License
=======
This library is released under the [MIT license](LICENSE).

View File

@@ -0,0 +1,119 @@
<?php
return [
'İ' => 'i̇',
'µ' => 'μ',
'ſ' => 's',
'ͅ' => 'ι',
'ς' => 'σ',
'ϐ' => 'β',
'ϑ' => 'θ',
'ϕ' => 'φ',
'ϖ' => 'π',
'ϰ' => 'κ',
'ϱ' => 'ρ',
'ϵ' => 'ε',
'ẛ' => 'ṡ',
'' => 'ι',
'ß' => 'ss',
'ʼn' => 'ʼn',
'ǰ' => 'ǰ',
'ΐ' => 'ΐ',
'ΰ' => 'ΰ',
'և' => 'եւ',
'ẖ' => 'ẖ',
'ẗ' => 'ẗ',
'ẘ' => 'ẘ',
'ẙ' => 'ẙ',
'ẚ' => 'aʾ',
'ẞ' => 'ss',
'ὐ' => 'ὐ',
'ὒ' => 'ὒ',
'ὔ' => 'ὔ',
'ὖ' => 'ὖ',
'ᾀ' => 'ἀι',
'ᾁ' => 'ἁι',
'ᾂ' => 'ἂι',
'ᾃ' => 'ἃι',
'ᾄ' => 'ἄι',
'ᾅ' => 'ἅι',
'ᾆ' => 'ἆι',
'ᾇ' => 'ἇι',
'ᾈ' => 'ἀι',
'ᾉ' => 'ἁι',
'ᾊ' => 'ἂι',
'ᾋ' => 'ἃι',
'ᾌ' => 'ἄι',
'ᾍ' => 'ἅι',
'ᾎ' => 'ἆι',
'ᾏ' => 'ἇι',
'ᾐ' => 'ἠι',
'ᾑ' => 'ἡι',
'ᾒ' => 'ἢι',
'ᾓ' => 'ἣι',
'ᾔ' => 'ἤι',
'ᾕ' => 'ἥι',
'ᾖ' => 'ἦι',
'ᾗ' => 'ἧι',
'ᾘ' => 'ἠι',
'ᾙ' => 'ἡι',
'ᾚ' => 'ἢι',
'ᾛ' => 'ἣι',
'ᾜ' => 'ἤι',
'ᾝ' => 'ἥι',
'ᾞ' => 'ἦι',
'ᾟ' => 'ἧι',
'ᾠ' => 'ὠι',
'ᾡ' => 'ὡι',
'ᾢ' => 'ὢι',
'ᾣ' => 'ὣι',
'ᾤ' => 'ὤι',
'ᾥ' => 'ὥι',
'ᾦ' => 'ὦι',
'ᾧ' => 'ὧι',
'ᾨ' => 'ὠι',
'ᾩ' => 'ὡι',
'ᾪ' => 'ὢι',
'ᾫ' => 'ὣι',
'ᾬ' => 'ὤι',
'ᾭ' => 'ὥι',
'ᾮ' => 'ὦι',
'ᾯ' => 'ὧι',
'ᾲ' => 'ὰι',
'ᾳ' => 'αι',
'ᾴ' => 'άι',
'ᾶ' => 'ᾶ',
'ᾷ' => 'ᾶι',
'ᾼ' => 'αι',
'ῂ' => 'ὴι',
'ῃ' => 'ηι',
'ῄ' => 'ήι',
'ῆ' => 'ῆ',
'ῇ' => 'ῆι',
'ῌ' => 'ηι',
'ῒ' => 'ῒ',
'ῖ' => 'ῖ',
'ῗ' => 'ῗ',
'ῢ' => 'ῢ',
'ῤ' => 'ῤ',
'ῦ' => 'ῦ',
'ῧ' => 'ῧ',
'ῲ' => 'ὼι',
'ῳ' => 'ωι',
'ῴ' => 'ώι',
'ῶ' => 'ῶ',
'ῷ' => 'ῶι',
'ῼ' => 'ωι',
'ff' => 'ff',
'fi' => 'fi',
'fl' => 'fl',
'ffi' => 'ffi',
'ffl' => 'ffl',
'ſt' => 'st',
'st' => 'st',
'ﬓ' => 'մն',
'ﬔ' => 'մե',
'ﬕ' => 'մի',
'ﬖ' => 'վն',
'ﬗ' => 'մխ',
];

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,172 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Mbstring as p;
if (\PHP_VERSION_ID >= 80000) {
return require __DIR__.'/bootstrap80.php';
}
if (!function_exists('mb_convert_encoding')) {
function mb_convert_encoding($string, $to_encoding, $from_encoding = null) { return p\Mbstring::mb_convert_encoding($string, $to_encoding, $from_encoding); }
}
if (!function_exists('mb_decode_mimeheader')) {
function mb_decode_mimeheader($string) { return p\Mbstring::mb_decode_mimeheader($string); }
}
if (!function_exists('mb_encode_mimeheader')) {
function mb_encode_mimeheader($string, $charset = null, $transfer_encoding = null, $newline = "\r\n", $indent = 0) { return p\Mbstring::mb_encode_mimeheader($string, $charset, $transfer_encoding, $newline, $indent); }
}
if (!function_exists('mb_decode_numericentity')) {
function mb_decode_numericentity($string, $map, $encoding = null) { return p\Mbstring::mb_decode_numericentity($string, $map, $encoding); }
}
if (!function_exists('mb_encode_numericentity')) {
function mb_encode_numericentity($string, $map, $encoding = null, $hex = false) { return p\Mbstring::mb_encode_numericentity($string, $map, $encoding, $hex); }
}
if (!function_exists('mb_convert_case')) {
function mb_convert_case($string, $mode, $encoding = null) { return p\Mbstring::mb_convert_case($string, $mode, $encoding); }
}
if (!function_exists('mb_internal_encoding')) {
function mb_internal_encoding($encoding = null) { return p\Mbstring::mb_internal_encoding($encoding); }
}
if (!function_exists('mb_language')) {
function mb_language($language = null) { return p\Mbstring::mb_language($language); }
}
if (!function_exists('mb_list_encodings')) {
function mb_list_encodings() { return p\Mbstring::mb_list_encodings(); }
}
if (!function_exists('mb_encoding_aliases')) {
function mb_encoding_aliases($encoding) { return p\Mbstring::mb_encoding_aliases($encoding); }
}
if (!function_exists('mb_check_encoding')) {
function mb_check_encoding($value = null, $encoding = null) { return p\Mbstring::mb_check_encoding($value, $encoding); }
}
if (!function_exists('mb_detect_encoding')) {
function mb_detect_encoding($string, $encodings = null, $strict = false) { return p\Mbstring::mb_detect_encoding($string, $encodings, $strict); }
}
if (!function_exists('mb_detect_order')) {
function mb_detect_order($encoding = null) { return p\Mbstring::mb_detect_order($encoding); }
}
if (!function_exists('mb_parse_str')) {
function mb_parse_str($string, &$result = []) { parse_str($string, $result); return (bool) $result; }
}
if (!function_exists('mb_strlen')) {
function mb_strlen($string, $encoding = null) { return p\Mbstring::mb_strlen($string, $encoding); }
}
if (!function_exists('mb_strpos')) {
function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strpos($haystack, $needle, $offset, $encoding); }
}
if (!function_exists('mb_strtolower')) {
function mb_strtolower($string, $encoding = null) { return p\Mbstring::mb_strtolower($string, $encoding); }
}
if (!function_exists('mb_strtoupper')) {
function mb_strtoupper($string, $encoding = null) { return p\Mbstring::mb_strtoupper($string, $encoding); }
}
if (!function_exists('mb_substitute_character')) {
function mb_substitute_character($substitute_character = null) { return p\Mbstring::mb_substitute_character($substitute_character); }
}
if (!function_exists('mb_substr')) {
function mb_substr($string, $start, $length = 2147483647, $encoding = null) { return p\Mbstring::mb_substr($string, $start, $length, $encoding); }
}
if (!function_exists('mb_stripos')) {
function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_stripos($haystack, $needle, $offset, $encoding); }
}
if (!function_exists('mb_stristr')) {
function mb_stristr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_stristr($haystack, $needle, $before_needle, $encoding); }
}
if (!function_exists('mb_strrchr')) {
function mb_strrchr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strrchr($haystack, $needle, $before_needle, $encoding); }
}
if (!function_exists('mb_strrichr')) {
function mb_strrichr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strrichr($haystack, $needle, $before_needle, $encoding); }
}
if (!function_exists('mb_strripos')) {
function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strripos($haystack, $needle, $offset, $encoding); }
}
if (!function_exists('mb_strrpos')) {
function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strrpos($haystack, $needle, $offset, $encoding); }
}
if (!function_exists('mb_strstr')) {
function mb_strstr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strstr($haystack, $needle, $before_needle, $encoding); }
}
if (!function_exists('mb_get_info')) {
function mb_get_info($type = 'all') { return p\Mbstring::mb_get_info($type); }
}
if (!function_exists('mb_http_output')) {
function mb_http_output($encoding = null) { return p\Mbstring::mb_http_output($encoding); }
}
if (!function_exists('mb_strwidth')) {
function mb_strwidth($string, $encoding = null) { return p\Mbstring::mb_strwidth($string, $encoding); }
}
if (!function_exists('mb_substr_count')) {
function mb_substr_count($haystack, $needle, $encoding = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $encoding); }
}
if (!function_exists('mb_output_handler')) {
function mb_output_handler($string, $status) { return p\Mbstring::mb_output_handler($string, $status); }
}
if (!function_exists('mb_http_input')) {
function mb_http_input($type = null) { return p\Mbstring::mb_http_input($type); }
}
if (!function_exists('mb_convert_variables')) {
function mb_convert_variables($to_encoding, $from_encoding, &...$vars) { return p\Mbstring::mb_convert_variables($to_encoding, $from_encoding, ...$vars); }
}
if (!function_exists('mb_ord')) {
function mb_ord($string, $encoding = null) { return p\Mbstring::mb_ord($string, $encoding); }
}
if (!function_exists('mb_chr')) {
function mb_chr($codepoint, $encoding = null) { return p\Mbstring::mb_chr($codepoint, $encoding); }
}
if (!function_exists('mb_scrub')) {
function mb_scrub($string, $encoding = null) { $encoding = null === $encoding ? mb_internal_encoding() : $encoding; return mb_convert_encoding($string, $encoding, $encoding); }
}
if (!function_exists('mb_str_split')) {
function mb_str_split($string, $length = 1, $encoding = null) { return p\Mbstring::mb_str_split($string, $length, $encoding); }
}
if (!function_exists('mb_str_pad')) {
function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = STR_PAD_RIGHT, ?string $encoding = null): string { return p\Mbstring::mb_str_pad($string, $length, $pad_string, $pad_type, $encoding); }
}
if (!function_exists('mb_ucfirst')) {
function mb_ucfirst(string $string, ?string $encoding = null): string { return p\Mbstring::mb_ucfirst($string, $encoding); }
}
if (!function_exists('mb_lcfirst')) {
function mb_lcfirst(string $string, ?string $encoding = null): string { return p\Mbstring::mb_lcfirst($string, $encoding); }
}
if (!function_exists('mb_trim')) {
function mb_trim(string $string, ?string $characters = null, ?string $encoding = null): string { return p\Mbstring::mb_trim($string, $characters, $encoding); }
}
if (!function_exists('mb_ltrim')) {
function mb_ltrim(string $string, ?string $characters = null, ?string $encoding = null): string { return p\Mbstring::mb_ltrim($string, $characters, $encoding); }
}
if (!function_exists('mb_rtrim')) {
function mb_rtrim(string $string, ?string $characters = null, ?string $encoding = null): string { return p\Mbstring::mb_rtrim($string, $characters, $encoding); }
}
if (extension_loaded('mbstring')) {
return;
}
if (!defined('MB_CASE_UPPER')) {
define('MB_CASE_UPPER', 0);
}
if (!defined('MB_CASE_LOWER')) {
define('MB_CASE_LOWER', 1);
}
if (!defined('MB_CASE_TITLE')) {
define('MB_CASE_TITLE', 2);
}

View File

@@ -0,0 +1,167 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Mbstring as p;
if (!function_exists('mb_convert_encoding')) {
function mb_convert_encoding(array|string|null $string, ?string $to_encoding, array|string|null $from_encoding = null): array|string|false { return p\Mbstring::mb_convert_encoding($string ?? '', (string) $to_encoding, $from_encoding); }
}
if (!function_exists('mb_decode_mimeheader')) {
function mb_decode_mimeheader(?string $string): string { return p\Mbstring::mb_decode_mimeheader((string) $string); }
}
if (!function_exists('mb_encode_mimeheader')) {
function mb_encode_mimeheader(?string $string, ?string $charset = null, ?string $transfer_encoding = null, ?string $newline = "\r\n", ?int $indent = 0): string { return p\Mbstring::mb_encode_mimeheader((string) $string, $charset, $transfer_encoding, (string) $newline, (int) $indent); }
}
if (!function_exists('mb_decode_numericentity')) {
function mb_decode_numericentity(?string $string, array $map, ?string $encoding = null): string { return p\Mbstring::mb_decode_numericentity((string) $string, $map, $encoding); }
}
if (!function_exists('mb_encode_numericentity')) {
function mb_encode_numericentity(?string $string, array $map, ?string $encoding = null, ?bool $hex = false): string { return p\Mbstring::mb_encode_numericentity((string) $string, $map, $encoding, (bool) $hex); }
}
if (!function_exists('mb_convert_case')) {
function mb_convert_case(?string $string, ?int $mode, ?string $encoding = null): string { return p\Mbstring::mb_convert_case((string) $string, (int) $mode, $encoding); }
}
if (!function_exists('mb_internal_encoding')) {
function mb_internal_encoding(?string $encoding = null): string|bool { return p\Mbstring::mb_internal_encoding($encoding); }
}
if (!function_exists('mb_language')) {
function mb_language(?string $language = null): string|bool { return p\Mbstring::mb_language($language); }
}
if (!function_exists('mb_list_encodings')) {
function mb_list_encodings(): array { return p\Mbstring::mb_list_encodings(); }
}
if (!function_exists('mb_encoding_aliases')) {
function mb_encoding_aliases(?string $encoding): array { return p\Mbstring::mb_encoding_aliases((string) $encoding); }
}
if (!function_exists('mb_check_encoding')) {
function mb_check_encoding(array|string|null $value = null, ?string $encoding = null): bool { return p\Mbstring::mb_check_encoding($value, $encoding); }
}
if (!function_exists('mb_detect_encoding')) {
function mb_detect_encoding(?string $string, array|string|null $encodings = null, ?bool $strict = false): string|false { return p\Mbstring::mb_detect_encoding((string) $string, $encodings, (bool) $strict); }
}
if (!function_exists('mb_detect_order')) {
function mb_detect_order(array|string|null $encoding = null): array|bool { return p\Mbstring::mb_detect_order($encoding); }
}
if (!function_exists('mb_parse_str')) {
function mb_parse_str(?string $string, &$result = []): bool { parse_str((string) $string, $result); return (bool) $result; }
}
if (!function_exists('mb_strlen')) {
function mb_strlen(?string $string, ?string $encoding = null): int { return p\Mbstring::mb_strlen((string) $string, $encoding); }
}
if (!function_exists('mb_strpos')) {
function mb_strpos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_strpos((string) $haystack, (string) $needle, (int) $offset, $encoding); }
}
if (!function_exists('mb_strtolower')) {
function mb_strtolower(?string $string, ?string $encoding = null): string { return p\Mbstring::mb_strtolower((string) $string, $encoding); }
}
if (!function_exists('mb_strtoupper')) {
function mb_strtoupper(?string $string, ?string $encoding = null): string { return p\Mbstring::mb_strtoupper((string) $string, $encoding); }
}
if (!function_exists('mb_substitute_character')) {
function mb_substitute_character(string|int|null $substitute_character = null): string|int|bool { return p\Mbstring::mb_substitute_character($substitute_character); }
}
if (!function_exists('mb_substr')) {
function mb_substr(?string $string, ?int $start, ?int $length = null, ?string $encoding = null): string { return p\Mbstring::mb_substr((string) $string, (int) $start, $length, $encoding); }
}
if (!function_exists('mb_stripos')) {
function mb_stripos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_stripos((string) $haystack, (string) $needle, (int) $offset, $encoding); }
}
if (!function_exists('mb_stristr')) {
function mb_stristr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_stristr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); }
}
if (!function_exists('mb_strrchr')) {
function mb_strrchr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_strrchr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); }
}
if (!function_exists('mb_strrichr')) {
function mb_strrichr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_strrichr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); }
}
if (!function_exists('mb_strripos')) {
function mb_strripos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_strripos((string) $haystack, (string) $needle, (int) $offset, $encoding); }
}
if (!function_exists('mb_strrpos')) {
function mb_strrpos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_strrpos((string) $haystack, (string) $needle, (int) $offset, $encoding); }
}
if (!function_exists('mb_strstr')) {
function mb_strstr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_strstr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); }
}
if (!function_exists('mb_get_info')) {
function mb_get_info(?string $type = 'all'): array|string|int|false|null { return p\Mbstring::mb_get_info((string) $type); }
}
if (!function_exists('mb_http_output')) {
function mb_http_output(?string $encoding = null): string|bool { return p\Mbstring::mb_http_output($encoding); }
}
if (!function_exists('mb_strwidth')) {
function mb_strwidth(?string $string, ?string $encoding = null): int { return p\Mbstring::mb_strwidth((string) $string, $encoding); }
}
if (!function_exists('mb_substr_count')) {
function mb_substr_count(?string $haystack, ?string $needle, ?string $encoding = null): int { return p\Mbstring::mb_substr_count((string) $haystack, (string) $needle, $encoding); }
}
if (!function_exists('mb_output_handler')) {
function mb_output_handler(?string $string, ?int $status): string { return p\Mbstring::mb_output_handler((string) $string, (int) $status); }
}
if (!function_exists('mb_http_input')) {
function mb_http_input(?string $type = null): array|string|false { return p\Mbstring::mb_http_input($type); }
}
if (!function_exists('mb_convert_variables')) {
function mb_convert_variables(?string $to_encoding, array|string|null $from_encoding, mixed &$var, mixed &...$vars): string|false { return p\Mbstring::mb_convert_variables((string) $to_encoding, $from_encoding ?? '', $var, ...$vars); }
}
if (!function_exists('mb_ord')) {
function mb_ord(?string $string, ?string $encoding = null): int|false { return p\Mbstring::mb_ord((string) $string, $encoding); }
}
if (!function_exists('mb_chr')) {
function mb_chr(?int $codepoint, ?string $encoding = null): string|false { return p\Mbstring::mb_chr((int) $codepoint, $encoding); }
}
if (!function_exists('mb_scrub')) {
function mb_scrub(?string $string, ?string $encoding = null): string { $encoding ??= mb_internal_encoding(); return mb_convert_encoding((string) $string, $encoding, $encoding); }
}
if (!function_exists('mb_str_split')) {
function mb_str_split(?string $string, ?int $length = 1, ?string $encoding = null): array { return p\Mbstring::mb_str_split((string) $string, (int) $length, $encoding); }
}
if (!function_exists('mb_str_pad')) {
function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = STR_PAD_RIGHT, ?string $encoding = null): string { return p\Mbstring::mb_str_pad($string, $length, $pad_string, $pad_type, $encoding); }
}
if (!function_exists('mb_ucfirst')) {
function mb_ucfirst($string, ?string $encoding = null): string { return p\Mbstring::mb_ucfirst($string, $encoding); }
}
if (!function_exists('mb_lcfirst')) {
function mb_lcfirst($string, ?string $encoding = null): string { return p\Mbstring::mb_lcfirst($string, $encoding); }
}
if (!function_exists('mb_trim')) {
function mb_trim(string $string, ?string $characters = null, ?string $encoding = null): string { return p\Mbstring::mb_trim($string, $characters, $encoding); }
}
if (!function_exists('mb_ltrim')) {
function mb_ltrim(string $string, ?string $characters = null, ?string $encoding = null): string { return p\Mbstring::mb_ltrim($string, $characters, $encoding); }
}
if (!function_exists('mb_rtrim')) {
function mb_rtrim(string $string, ?string $characters = null, ?string $encoding = null): string { return p\Mbstring::mb_rtrim($string, $characters, $encoding); }
}
if (extension_loaded('mbstring')) {
return;
}
if (!defined('MB_CASE_UPPER')) {
define('MB_CASE_UPPER', 0);
}
if (!defined('MB_CASE_LOWER')) {
define('MB_CASE_LOWER', 1);
}
if (!defined('MB_CASE_TITLE')) {
define('MB_CASE_TITLE', 2);
}

19
vendor/symfony/polyfill-php73/LICENSE vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2018-present Fabien Potencier
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.

43
vendor/symfony/polyfill-php73/Php73.php vendored Normal file
View File

@@ -0,0 +1,43 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Php73;
/**
* @author Gabriel Caruso <carusogabriel34@gmail.com>
* @author Ion Bazan <ion.bazan@gmail.com>
*
* @internal
*/
final class Php73
{
public static $startAt = 1533462603;
/**
* @param bool $asNum
*
* @return array|float|int
*/
public static function hrtime($asNum = false)
{
$ns = microtime(false);
$s = substr($ns, 11) - self::$startAt;
$ns = 1E9 * (float) $ns;
if ($asNum) {
$ns += $s * 1E9;
return \PHP_INT_SIZE === 4 ? $ns : (int) $ns;
}
return [$s, (int) $ns];
}
}

18
vendor/symfony/polyfill-php73/README.md vendored Normal file
View File

@@ -0,0 +1,18 @@
Symfony Polyfill / Php73
========================
This component provides functions added to PHP 7.3 core:
- [`array_key_first`](https://php.net/array_key_first)
- [`array_key_last`](https://php.net/array_key_last)
- [`hrtime`](https://php.net/function.hrtime)
- [`is_countable`](https://php.net/is_countable)
- [`JsonException`](https://php.net/JsonException)
More information can be found in the
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).
License
=======
This library is released under the [MIT license](LICENSE).

View File

@@ -0,0 +1,16 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (\PHP_VERSION_ID < 70300) {
class JsonException extends Exception
{
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Php73 as p;
if (\PHP_VERSION_ID >= 70300) {
return;
}
if (!function_exists('is_countable')) {
function is_countable($value) { return is_array($value) || $value instanceof Countable || $value instanceof ResourceBundle || $value instanceof SimpleXmlElement; }
}
if (!function_exists('hrtime')) {
require_once __DIR__.'/Php73.php';
p\Php73::$startAt = (int) microtime(true);
function hrtime($as_number = false) { return p\Php73::hrtime($as_number); }
}
if (!function_exists('array_key_first')) {
function array_key_first(array $array) { foreach ($array as $key => $value) { return $key; } }
}
if (!function_exists('array_key_last')) {
function array_key_last(array $array) { return key(array_slice($array, -1, 1, true)); }
}

19
vendor/symfony/polyfill-php80/LICENSE vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2020-present Fabien Potencier
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.

115
vendor/symfony/polyfill-php80/Php80.php vendored Normal file
View File

@@ -0,0 +1,115 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Php80;
/**
* @author Ion Bazan <ion.bazan@gmail.com>
* @author Nico Oelgart <nicoswd@gmail.com>
* @author Nicolas Grekas <p@tchwork.com>
*
* @internal
*/
final class Php80
{
public static function fdiv(float $dividend, float $divisor): float
{
return @($dividend / $divisor);
}
public static function get_debug_type($value): string
{
switch (true) {
case null === $value: return 'null';
case \is_bool($value): return 'bool';
case \is_string($value): return 'string';
case \is_array($value): return 'array';
case \is_int($value): return 'int';
case \is_float($value): return 'float';
case \is_object($value): break;
case $value instanceof \__PHP_Incomplete_Class: return '__PHP_Incomplete_Class';
default:
if (null === $type = @get_resource_type($value)) {
return 'unknown';
}
if ('Unknown' === $type) {
$type = 'closed';
}
return "resource ($type)";
}
$class = \get_class($value);
if (false === strpos($class, '@')) {
return $class;
}
return (get_parent_class($class) ?: key(class_implements($class)) ?: 'class').'@anonymous';
}
public static function get_resource_id($res): int
{
if (!\is_resource($res) && null === @get_resource_type($res)) {
throw new \TypeError(sprintf('Argument 1 passed to get_resource_id() must be of the type resource, %s given', get_debug_type($res)));
}
return (int) $res;
}
public static function preg_last_error_msg(): string
{
switch (preg_last_error()) {
case \PREG_INTERNAL_ERROR:
return 'Internal error';
case \PREG_BAD_UTF8_ERROR:
return 'Malformed UTF-8 characters, possibly incorrectly encoded';
case \PREG_BAD_UTF8_OFFSET_ERROR:
return 'The offset did not correspond to the beginning of a valid UTF-8 code point';
case \PREG_BACKTRACK_LIMIT_ERROR:
return 'Backtrack limit exhausted';
case \PREG_RECURSION_LIMIT_ERROR:
return 'Recursion limit exhausted';
case \PREG_JIT_STACKLIMIT_ERROR:
return 'JIT stack limit exhausted';
case \PREG_NO_ERROR:
return 'No error';
default:
return 'Unknown error';
}
}
public static function str_contains(string $haystack, string $needle): bool
{
return '' === $needle || false !== strpos($haystack, $needle);
}
public static function str_starts_with(string $haystack, string $needle): bool
{
return 0 === strncmp($haystack, $needle, \strlen($needle));
}
public static function str_ends_with(string $haystack, string $needle): bool
{
if ('' === $needle || $needle === $haystack) {
return true;
}
if ('' === $haystack) {
return false;
}
$needleLength = \strlen($needle);
return $needleLength <= \strlen($haystack) && 0 === substr_compare($haystack, $needle, -$needleLength);
}
}

View File

@@ -0,0 +1,103 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Php80;
/**
* @author Fedonyuk Anton <info@ensostudio.ru>
*
* @internal
*/
class PhpToken implements \Stringable
{
/**
* @var int
*/
public $id;
/**
* @var string
*/
public $text;
/**
* @var int
*/
public $line;
/**
* @var int
*/
public $pos;
public function __construct(int $id, string $text, int $line = -1, int $position = -1)
{
$this->id = $id;
$this->text = $text;
$this->line = $line;
$this->pos = $position;
}
public function getTokenName(): ?string
{
if ('UNKNOWN' === $name = token_name($this->id)) {
$name = \strlen($this->text) > 1 || \ord($this->text) < 32 ? null : $this->text;
}
return $name;
}
/**
* @param int|string|array $kind
*/
public function is($kind): bool
{
foreach ((array) $kind as $value) {
if (\in_array($value, [$this->id, $this->text], true)) {
return true;
}
}
return false;
}
public function isIgnorable(): bool
{
return \in_array($this->id, [\T_WHITESPACE, \T_COMMENT, \T_DOC_COMMENT, \T_OPEN_TAG], true);
}
public function __toString(): string
{
return (string) $this->text;
}
/**
* @return static[]
*/
public static function tokenize(string $code, int $flags = 0): array
{
$line = 1;
$position = 0;
$tokens = token_get_all($code, $flags);
foreach ($tokens as $index => $token) {
if (\is_string($token)) {
$id = \ord($token);
$text = $token;
} else {
[$id, $text, $line] = $token;
}
$tokens[$index] = new static($id, $text, $line, $position);
$position += \strlen($text);
}
return $tokens;
}
}

25
vendor/symfony/polyfill-php80/README.md vendored Normal file
View File

@@ -0,0 +1,25 @@
Symfony Polyfill / Php80
========================
This component provides features added to PHP 8.0 core:
- [`Stringable`](https://php.net/stringable) interface
- [`fdiv`](https://php.net/fdiv)
- [`ValueError`](https://php.net/valueerror) class
- [`UnhandledMatchError`](https://php.net/unhandledmatcherror) class
- `FILTER_VALIDATE_BOOL` constant
- [`get_debug_type`](https://php.net/get_debug_type)
- [`PhpToken`](https://php.net/phptoken) class
- [`preg_last_error_msg`](https://php.net/preg_last_error_msg)
- [`str_contains`](https://php.net/str_contains)
- [`str_starts_with`](https://php.net/str_starts_with)
- [`str_ends_with`](https://php.net/str_ends_with)
- [`get_resource_id`](https://php.net/get_resource_id)
More information can be found in the
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).
License
=======
This library is released under the [MIT license](LICENSE).

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#[Attribute(Attribute::TARGET_CLASS)]
final class Attribute
{
public const TARGET_CLASS = 1;
public const TARGET_FUNCTION = 2;
public const TARGET_METHOD = 4;
public const TARGET_PROPERTY = 8;
public const TARGET_CLASS_CONSTANT = 16;
public const TARGET_PARAMETER = 32;
public const TARGET_ALL = 63;
public const IS_REPEATABLE = 64;
/** @var int */
public $flags;
public function __construct(int $flags = self::TARGET_ALL)
{
$this->flags = $flags;
}
}

View File

@@ -0,0 +1,16 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (\PHP_VERSION_ID < 80000 && extension_loaded('tokenizer')) {
class PhpToken extends Symfony\Polyfill\Php80\PhpToken
{
}
}

View File

@@ -0,0 +1,20 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (\PHP_VERSION_ID < 80000) {
interface Stringable
{
/**
* @return string
*/
public function __toString();
}
}

View File

@@ -0,0 +1,16 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (\PHP_VERSION_ID < 80000) {
class UnhandledMatchError extends Error
{
}
}

View File

@@ -0,0 +1,16 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (\PHP_VERSION_ID < 80000) {
class ValueError extends Error
{
}
}

View File

@@ -0,0 +1,42 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Php80 as p;
if (\PHP_VERSION_ID >= 80000) {
return;
}
if (!defined('FILTER_VALIDATE_BOOL') && defined('FILTER_VALIDATE_BOOLEAN')) {
define('FILTER_VALIDATE_BOOL', \FILTER_VALIDATE_BOOLEAN);
}
if (!function_exists('fdiv')) {
function fdiv(float $num1, float $num2): float { return p\Php80::fdiv($num1, $num2); }
}
if (!function_exists('preg_last_error_msg')) {
function preg_last_error_msg(): string { return p\Php80::preg_last_error_msg(); }
}
if (!function_exists('str_contains')) {
function str_contains(?string $haystack, ?string $needle): bool { return p\Php80::str_contains($haystack ?? '', $needle ?? ''); }
}
if (!function_exists('str_starts_with')) {
function str_starts_with(?string $haystack, ?string $needle): bool { return p\Php80::str_starts_with($haystack ?? '', $needle ?? ''); }
}
if (!function_exists('str_ends_with')) {
function str_ends_with(?string $haystack, ?string $needle): bool { return p\Php80::str_ends_with($haystack ?? '', $needle ?? ''); }
}
if (!function_exists('get_debug_type')) {
function get_debug_type($value): string { return p\Php80::get_debug_type($value); }
}
if (!function_exists('get_resource_id')) {
function get_resource_id($resource): int { return p\Php80::get_resource_id($resource); }
}

View File

@@ -0,0 +1,3 @@
/Tests export-ignore
/phpunit.xml.dist export-ignore
/.git* export-ignore

View File

@@ -0,0 +1,8 @@
Please do not submit any Pull Requests here. They will be closed.
---
Please submit your PR here instead:
https://github.com/symfony/symfony
This repository is what we call a "subtree split": a read-only subset of that main repository.
We're looking forward to your PR there!

View File

@@ -0,0 +1,20 @@
name: Close Pull Request
on:
pull_request_target:
types: [opened]
jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: superbrothers/close-pull-request@v3
with:
comment: |
Thanks for your Pull Request! We love contributions.
However, you should instead open your PR on the main repository:
https://github.com/symfony/symfony
This repository is what we call a "subtree split": a read-only subset of that main repository.
We're looking forward to your PR there!

View File

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

View File

@@ -0,0 +1,70 @@
CHANGELOG
=========
5.3.0
-----
* deprecate passing a boolean as the second argument of `PropertyAccessor::__construct()`, expecting a combination of bitwise flags instead
5.2.0
-----
* deprecated passing a boolean as the first argument of `PropertyAccessor::__construct()`, expecting a combination of bitwise flags instead
* added the ability to disable usage of the magic `__get` & `__set` methods
5.1.0
-----
* Added an `UninitializedPropertyException`
* Linking to PropertyInfo extractor to remove a lot of duplicate code
4.4.0
-----
* deprecated passing `null` as `$defaultLifetime` 2nd argument of `PropertyAccessor::createCache()` method,
pass `0` instead
4.3.0
-----
* added a `$throwExceptionOnInvalidPropertyPath` argument to the PropertyAccessor constructor.
* added `enableExceptionOnInvalidPropertyPath()`, `disableExceptionOnInvalidPropertyPath()` and
`isExceptionOnInvalidPropertyPath()` methods to `PropertyAccessorBuilder`
4.0.0
-----
* removed the `StringUtil` class, use `Symfony\Component\Inflector\Inflector`
3.1.0
-----
* deprecated the `StringUtil` class, use `Symfony\Component\Inflector\Inflector`
instead
2.7.0
------
* `UnexpectedTypeException` now expects three constructor arguments: The invalid property value,
the `PropertyPathInterface` object and the current index of the property path.
2.5.0
------
* allowed non alpha numeric characters in second level and deeper object properties names
* [BC BREAK] when accessing an index on an object that does not implement
ArrayAccess, a NoSuchIndexException is now thrown instead of the
semantically wrong NoSuchPropertyException
* [BC BREAK] added isReadable() and isWritable() to PropertyAccessorInterface
2.3.0
------
* added PropertyAccessorBuilder, to enable or disable the support of "__call"
* added support for "__call" in the PropertyAccessor (disabled by default)
* [BC BREAK] changed PropertyAccessor to continue its search for a property or
method even if a non-public match was found. Before, a PropertyAccessDeniedException
was thrown in this case. Class PropertyAccessDeniedException was removed
now.
* deprecated PropertyAccess::getPropertyAccessor
* added PropertyAccess::createPropertyAccessor and PropertyAccess::createPropertyAccessorBuilder

View File

@@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\PropertyAccess\Exception;
/**
* Thrown when a property path is not available.
*
* @author Stéphane Escandell <stephane.escandell@gmail.com>
*/
class AccessException extends RuntimeException
{
}

View File

@@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\PropertyAccess\Exception;
/**
* Marker interface for the PropertyAccess component.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
interface ExceptionInterface extends \Throwable
{
}

View File

@@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\PropertyAccess\Exception;
/**
* Base InvalidArgumentException for the PropertyAccess component.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\PropertyAccess\Exception;
/**
* Thrown when a property path is malformed.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class InvalidPropertyPathException extends RuntimeException
{
}

View File

@@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\PropertyAccess\Exception;
/**
* Thrown when an index cannot be found.
*
* @author Stéphane Escandell <stephane.escandell@gmail.com>
*/
class NoSuchIndexException extends AccessException
{
}

View File

@@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\PropertyAccess\Exception;
/**
* Thrown when a property cannot be found.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class NoSuchPropertyException extends AccessException
{
}

View File

@@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\PropertyAccess\Exception;
/**
* Base OutOfBoundsException for the PropertyAccess component.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class OutOfBoundsException extends \OutOfBoundsException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\PropertyAccess\Exception;
/**
* Base RuntimeException for the PropertyAccess component.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,39 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\PropertyAccess\Exception;
use Symfony\Component\PropertyAccess\PropertyPathInterface;
/**
* Thrown when a value does not match an expected type.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class UnexpectedTypeException extends RuntimeException
{
/**
* @param mixed $value The unexpected value found while traversing property path
* @param int $pathIndex The property path index when the unexpected value was found
*/
public function __construct($value, PropertyPathInterface $path, int $pathIndex)
{
$message = sprintf(
'PropertyAccessor requires a graph of objects or arrays to operate on, '.
'but it found type "%s" while trying to traverse path "%s" at property "%s".',
\gettype($value),
(string) $path,
$path->getElement($pathIndex)
);
parent::__construct($message);
}
}

View File

@@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\PropertyAccess\Exception;
/**
* Thrown when a property is not initialized.
*
* @author Jules Pietri <jules@heahprod.com>
*/
class UninitializedPropertyException extends AccessException
{
}

19
vendor/symfony/property-access/LICENSE vendored Normal file
View File

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

View File

@@ -0,0 +1,40 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\PropertyAccess;
/**
* Entry point of the PropertyAccess component.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
final class PropertyAccess
{
/**
* Creates a property accessor with the default configuration.
*/
public static function createPropertyAccessor(): PropertyAccessor
{
return self::createPropertyAccessorBuilder()->getPropertyAccessor();
}
public static function createPropertyAccessorBuilder(): PropertyAccessorBuilder
{
return new PropertyAccessorBuilder();
}
/**
* This class cannot be instantiated.
*/
private function __construct()
{
}
}

View File

@@ -0,0 +1,762 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\PropertyAccess;
use Psr\Cache\CacheItemPoolInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Symfony\Component\Cache\Adapter\AdapterInterface;
use Symfony\Component\Cache\Adapter\ApcuAdapter;
use Symfony\Component\Cache\Adapter\NullAdapter;
use Symfony\Component\PropertyAccess\Exception\AccessException;
use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException;
use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException;
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException;
use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException;
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
use Symfony\Component\PropertyInfo\PropertyReadInfo;
use Symfony\Component\PropertyInfo\PropertyReadInfoExtractorInterface;
use Symfony\Component\PropertyInfo\PropertyWriteInfo;
use Symfony\Component\PropertyInfo\PropertyWriteInfoExtractorInterface;
/**
* Default implementation of {@link PropertyAccessorInterface}.
*
* @author Bernhard Schussek <bschussek@gmail.com>
* @author Kévin Dunglas <dunglas@gmail.com>
* @author Nicolas Grekas <p@tchwork.com>
*/
class PropertyAccessor implements PropertyAccessorInterface
{
/** @var int Allow none of the magic methods */
public const DISALLOW_MAGIC_METHODS = ReflectionExtractor::DISALLOW_MAGIC_METHODS;
/** @var int Allow magic __get methods */
public const MAGIC_GET = ReflectionExtractor::ALLOW_MAGIC_GET;
/** @var int Allow magic __set methods */
public const MAGIC_SET = ReflectionExtractor::ALLOW_MAGIC_SET;
/** @var int Allow magic __call methods */
public const MAGIC_CALL = ReflectionExtractor::ALLOW_MAGIC_CALL;
public const DO_NOT_THROW = 0;
public const THROW_ON_INVALID_INDEX = 1;
public const THROW_ON_INVALID_PROPERTY_PATH = 2;
private const VALUE = 0;
private const REF = 1;
private const IS_REF_CHAINED = 2;
private const CACHE_PREFIX_READ = 'r';
private const CACHE_PREFIX_WRITE = 'w';
private const CACHE_PREFIX_PROPERTY_PATH = 'p';
private $magicMethodsFlags;
private $ignoreInvalidIndices;
private $ignoreInvalidProperty;
/**
* @var CacheItemPoolInterface
*/
private $cacheItemPool;
private $propertyPathCache = [];
/**
* @var PropertyReadInfoExtractorInterface
*/
private $readInfoExtractor;
/**
* @var PropertyWriteInfoExtractorInterface
*/
private $writeInfoExtractor;
private $readPropertyCache = [];
private $writePropertyCache = [];
private const RESULT_PROTO = [self::VALUE => null];
/**
* Should not be used by application code. Use
* {@link PropertyAccess::createPropertyAccessor()} instead.
*
* @param int $magicMethods A bitwise combination of the MAGIC_* constants
* to specify the allowed magic methods (__get, __set, __call)
* or self::DISALLOW_MAGIC_METHODS for none
* @param int $throw A bitwise combination of the THROW_* constants
* to specify when exceptions should be thrown
* @param PropertyReadInfoExtractorInterface $readInfoExtractor
* @param PropertyWriteInfoExtractorInterface $writeInfoExtractor
*/
public function __construct($magicMethods = self::MAGIC_GET | self::MAGIC_SET, $throw = self::THROW_ON_INVALID_PROPERTY_PATH, ?CacheItemPoolInterface $cacheItemPool = null, $readInfoExtractor = null, $writeInfoExtractor = null)
{
if (\is_bool($magicMethods)) {
trigger_deprecation('symfony/property-access', '5.2', 'Passing a boolean as the first argument to "%s()" is deprecated. Pass a combination of bitwise flags instead (i.e an integer).', __METHOD__);
$magicMethods = ($magicMethods ? self::MAGIC_CALL : 0) | self::MAGIC_GET | self::MAGIC_SET;
} elseif (!\is_int($magicMethods)) {
throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be an integer, "%s" given.', __METHOD__, get_debug_type($readInfoExtractor)));
}
if (\is_bool($throw)) {
trigger_deprecation('symfony/property-access', '5.3', 'Passing a boolean as the second argument to "%s()" is deprecated. Pass a combination of bitwise flags instead (i.e an integer).', __METHOD__);
$throw = $throw ? self::THROW_ON_INVALID_INDEX : self::DO_NOT_THROW;
if (!\is_bool($readInfoExtractor)) {
$throw |= self::THROW_ON_INVALID_PROPERTY_PATH;
}
}
if (\is_bool($readInfoExtractor)) {
trigger_deprecation('symfony/property-access', '5.3', 'Passing a boolean as the fourth argument to "%s()" is deprecated. Pass a combination of bitwise flags as the second argument instead (i.e an integer).', __METHOD__);
if ($readInfoExtractor) {
$throw |= self::THROW_ON_INVALID_PROPERTY_PATH;
}
$readInfoExtractor = $writeInfoExtractor;
$writeInfoExtractor = 4 < \func_num_args() ? func_get_arg(4) : null;
}
if (null !== $readInfoExtractor && !$readInfoExtractor instanceof PropertyReadInfoExtractorInterface) {
throw new \TypeError(sprintf('Argument 4 passed to "%s()" must be null or an instance of "%s", "%s" given.', __METHOD__, PropertyReadInfoExtractorInterface::class, get_debug_type($readInfoExtractor)));
}
if (null !== $writeInfoExtractor && !$writeInfoExtractor instanceof PropertyWriteInfoExtractorInterface) {
throw new \TypeError(sprintf('Argument 5 passed to "%s()" must be null or an instance of "%s", "%s" given.', __METHOD__, PropertyWriteInfoExtractorInterface::class, get_debug_type($writeInfoExtractor)));
}
$this->magicMethodsFlags = $magicMethods;
$this->ignoreInvalidIndices = 0 === ($throw & self::THROW_ON_INVALID_INDEX);
$this->cacheItemPool = $cacheItemPool instanceof NullAdapter ? null : $cacheItemPool; // Replace the NullAdapter by the null value
$this->ignoreInvalidProperty = 0 === ($throw & self::THROW_ON_INVALID_PROPERTY_PATH);
$this->readInfoExtractor = $readInfoExtractor ?? new ReflectionExtractor([], null, null, false);
$this->writeInfoExtractor = $writeInfoExtractor ?? new ReflectionExtractor(['set'], null, null, false);
}
/**
* {@inheritdoc}
*/
public function getValue($objectOrArray, $propertyPath)
{
$zval = [
self::VALUE => $objectOrArray,
];
if (\is_object($objectOrArray) && (false === strpbrk((string) $propertyPath, '.[') || $objectOrArray instanceof \stdClass && property_exists($objectOrArray, $propertyPath))) {
return $this->readProperty($zval, $propertyPath, $this->ignoreInvalidProperty)[self::VALUE];
}
$propertyPath = $this->getPropertyPath($propertyPath);
$propertyValues = $this->readPropertiesUntil($zval, $propertyPath, $propertyPath->getLength(), $this->ignoreInvalidIndices);
return $propertyValues[\count($propertyValues) - 1][self::VALUE];
}
/**
* {@inheritdoc}
*/
public function setValue(&$objectOrArray, $propertyPath, $value)
{
if (\is_object($objectOrArray) && (false === strpbrk((string) $propertyPath, '.[') || $objectOrArray instanceof \stdClass && property_exists($objectOrArray, $propertyPath))) {
$zval = [
self::VALUE => $objectOrArray,
];
try {
$this->writeProperty($zval, $propertyPath, $value);
return;
} catch (\TypeError $e) {
self::throwInvalidArgumentException($e->getMessage(), $e->getTrace(), 0, $propertyPath, $e);
// It wasn't thrown in this class so rethrow it
throw $e;
}
}
$propertyPath = $this->getPropertyPath($propertyPath);
$zval = [
self::VALUE => $objectOrArray,
self::REF => &$objectOrArray,
];
$propertyValues = $this->readPropertiesUntil($zval, $propertyPath, $propertyPath->getLength() - 1);
$overwrite = true;
try {
for ($i = \count($propertyValues) - 1; 0 <= $i; --$i) {
$zval = $propertyValues[$i];
unset($propertyValues[$i]);
// You only need set value for current element if:
// 1. it's the parent of the last index element
// OR
// 2. its child is not passed by reference
//
// This may avoid unnecessary value setting process for array elements.
// For example:
// '[a][b][c]' => 'old-value'
// If you want to change its value to 'new-value',
// you only need set value for '[a][b][c]' and it's safe to ignore '[a][b]' and '[a]'
if ($overwrite) {
$property = $propertyPath->getElement($i);
if ($propertyPath->isIndex($i)) {
if ($overwrite = !isset($zval[self::REF])) {
$ref = &$zval[self::REF];
$ref = $zval[self::VALUE];
}
$this->writeIndex($zval, $property, $value);
if ($overwrite) {
$zval[self::VALUE] = $zval[self::REF];
}
} else {
$this->writeProperty($zval, $property, $value);
}
// if current element is an object
// OR
// if current element's reference chain is not broken - current element
// as well as all its ancients in the property path are all passed by reference,
// then there is no need to continue the value setting process
if (\is_object($zval[self::VALUE]) || isset($zval[self::IS_REF_CHAINED])) {
break;
}
}
$value = $zval[self::VALUE];
}
} catch (\TypeError $e) {
self::throwInvalidArgumentException($e->getMessage(), $e->getTrace(), 0, $propertyPath, $e);
// It wasn't thrown in this class so rethrow it
throw $e;
}
}
private static function throwInvalidArgumentException(string $message, array $trace, int $i, string $propertyPath, ?\Throwable $previous = null): void
{
if (!isset($trace[$i]['file']) || __FILE__ !== $trace[$i]['file']) {
return;
}
if (\PHP_VERSION_ID < 80000) {
if (preg_match('/^Typed property \S+::\$\S+ must be (\S+), (\S+) used$/', $message, $matches)) {
[, $expectedType, $actualType] = $matches;
throw new InvalidArgumentException(sprintf('Expected argument of type "%s", "%s" given at property path "%s".', $expectedType, 'NULL' === $actualType ? 'null' : $actualType, $propertyPath), 0, $previous);
}
if (!str_starts_with($message, 'Argument ')) {
return;
}
$pos = strpos($message, $delim = 'must be of the type ') ?: (strpos($message, $delim = 'must be an instance of ') ?: strpos($message, $delim = 'must implement interface '));
$pos += \strlen($delim);
$j = strpos($message, ',', $pos);
$type = substr($message, 2 + $j, strpos($message, ' given', $j) - $j - 2);
$message = substr($message, $pos, $j - $pos);
throw new InvalidArgumentException(sprintf('Expected argument of type "%s", "%s" given at property path "%s".', $message, 'NULL' === $type ? 'null' : $type, $propertyPath), 0, $previous);
}
if (preg_match('/^\S+::\S+\(\): Argument #\d+ \(\$\S+\) must be of type (\S+), (\S+) given/', $message, $matches)) {
[, $expectedType, $actualType] = $matches;
throw new InvalidArgumentException(sprintf('Expected argument of type "%s", "%s" given at property path "%s".', $expectedType, 'NULL' === $actualType ? 'null' : $actualType, $propertyPath), 0, $previous);
}
if (preg_match('/^Cannot assign (\S+) to property \S+::\$\S+ of type (\S+)$/', $message, $matches)) {
[, $actualType, $expectedType] = $matches;
throw new InvalidArgumentException(sprintf('Expected argument of type "%s", "%s" given at property path "%s".', $expectedType, 'NULL' === $actualType ? 'null' : $actualType, $propertyPath), 0, $previous);
}
}
/**
* {@inheritdoc}
*/
public function isReadable($objectOrArray, $propertyPath)
{
if (!$propertyPath instanceof PropertyPathInterface) {
$propertyPath = new PropertyPath($propertyPath);
}
try {
$zval = [
self::VALUE => $objectOrArray,
];
// handle stdClass with properties with a dot in the name
if ($objectOrArray instanceof \stdClass && str_contains($propertyPath, '.') && property_exists($objectOrArray, $propertyPath)) {
$this->readProperty($zval, $propertyPath, $this->ignoreInvalidProperty);
} else {
$this->readPropertiesUntil($zval, $propertyPath, $propertyPath->getLength(), $this->ignoreInvalidIndices);
}
return true;
} catch (AccessException $e) {
return false;
} catch (UnexpectedTypeException $e) {
return false;
}
}
/**
* {@inheritdoc}
*/
public function isWritable($objectOrArray, $propertyPath)
{
$propertyPath = $this->getPropertyPath($propertyPath);
try {
$zval = [
self::VALUE => $objectOrArray,
];
// handle stdClass with properties with a dot in the name
if ($objectOrArray instanceof \stdClass && str_contains($propertyPath, '.') && property_exists($objectOrArray, $propertyPath)) {
$this->readProperty($zval, $propertyPath, $this->ignoreInvalidProperty);
return true;
}
$propertyValues = $this->readPropertiesUntil($zval, $propertyPath, $propertyPath->getLength() - 1);
for ($i = \count($propertyValues) - 1; 0 <= $i; --$i) {
$zval = $propertyValues[$i];
unset($propertyValues[$i]);
if ($propertyPath->isIndex($i)) {
if (!$zval[self::VALUE] instanceof \ArrayAccess && !\is_array($zval[self::VALUE])) {
return false;
}
} elseif (!\is_object($zval[self::VALUE]) || !$this->isPropertyWritable($zval[self::VALUE], $propertyPath->getElement($i))) {
return false;
}
if (\is_object($zval[self::VALUE])) {
return true;
}
}
return true;
} catch (AccessException $e) {
return false;
} catch (UnexpectedTypeException $e) {
return false;
}
}
/**
* Reads the path from an object up to a given path index.
*
* @throws UnexpectedTypeException if a value within the path is neither object nor array
* @throws NoSuchIndexException If a non-existing index is accessed
*/
private function readPropertiesUntil(array $zval, PropertyPathInterface $propertyPath, int $lastIndex, bool $ignoreInvalidIndices = true): array
{
if (!\is_object($zval[self::VALUE]) && !\is_array($zval[self::VALUE])) {
throw new UnexpectedTypeException($zval[self::VALUE], $propertyPath, 0);
}
// Add the root object to the list
$propertyValues = [$zval];
for ($i = 0; $i < $lastIndex; ++$i) {
$property = $propertyPath->getElement($i);
$isIndex = $propertyPath->isIndex($i);
if ($isIndex) {
// Create missing nested arrays on demand
if (($zval[self::VALUE] instanceof \ArrayAccess && !$zval[self::VALUE]->offsetExists($property)) ||
(\is_array($zval[self::VALUE]) && !isset($zval[self::VALUE][$property]) && !\array_key_exists($property, $zval[self::VALUE]))
) {
if (!$ignoreInvalidIndices) {
if (!\is_array($zval[self::VALUE])) {
if (!$zval[self::VALUE] instanceof \Traversable) {
throw new NoSuchIndexException(sprintf('Cannot read index "%s" while trying to traverse path "%s".', $property, (string) $propertyPath));
}
$zval[self::VALUE] = iterator_to_array($zval[self::VALUE]);
}
throw new NoSuchIndexException(sprintf('Cannot read index "%s" while trying to traverse path "%s". Available indices are "%s".', $property, (string) $propertyPath, print_r(array_keys($zval[self::VALUE]), true)));
}
if ($i + 1 < $propertyPath->getLength()) {
if (isset($zval[self::REF])) {
$zval[self::VALUE][$property] = [];
$zval[self::REF] = $zval[self::VALUE];
} else {
$zval[self::VALUE] = [$property => []];
}
}
}
$zval = $this->readIndex($zval, $property);
} else {
$zval = $this->readProperty($zval, $property, $this->ignoreInvalidProperty);
}
// the final value of the path must not be validated
if ($i + 1 < $propertyPath->getLength() && !\is_object($zval[self::VALUE]) && !\is_array($zval[self::VALUE])) {
throw new UnexpectedTypeException($zval[self::VALUE], $propertyPath, $i + 1);
}
if (isset($zval[self::REF]) && (0 === $i || isset($propertyValues[$i - 1][self::IS_REF_CHAINED]))) {
// Set the IS_REF_CHAINED flag to true if:
// current property is passed by reference and
// it is the first element in the property path or
// the IS_REF_CHAINED flag of its parent element is true
// Basically, this flag is true only when the reference chain from the top element to current element is not broken
$zval[self::IS_REF_CHAINED] = true;
}
$propertyValues[] = $zval;
}
return $propertyValues;
}
/**
* Reads a key from an array-like structure.
*
* @param string|int $index The key to read
*
* @throws NoSuchIndexException If the array does not implement \ArrayAccess or it is not an array
*/
private function readIndex(array $zval, $index): array
{
if (!$zval[self::VALUE] instanceof \ArrayAccess && !\is_array($zval[self::VALUE])) {
throw new NoSuchIndexException(sprintf('Cannot read index "%s" from object of type "%s" because it doesn\'t implement \ArrayAccess.', $index, get_debug_type($zval[self::VALUE])));
}
$result = self::RESULT_PROTO;
if (isset($zval[self::VALUE][$index])) {
$result[self::VALUE] = $zval[self::VALUE][$index];
if (!isset($zval[self::REF])) {
// Save creating references when doing read-only lookups
} elseif (\is_array($zval[self::VALUE])) {
$result[self::REF] = &$zval[self::REF][$index];
} elseif (\is_object($result[self::VALUE])) {
$result[self::REF] = $result[self::VALUE];
}
}
return $result;
}
/**
* Reads the value of a property from an object.
*
* @throws NoSuchPropertyException If $ignoreInvalidProperty is false and the property does not exist or is not public
*/
private function readProperty(array $zval, string $property, bool $ignoreInvalidProperty = false): array
{
if (!\is_object($zval[self::VALUE])) {
throw new NoSuchPropertyException(sprintf('Cannot read property "%s" from an array. Maybe you intended to write the property path as "[%1$s]" instead.', $property));
}
$result = self::RESULT_PROTO;
$object = $zval[self::VALUE];
$class = \get_class($object);
$access = $this->getReadInfo($class, $property);
if (null !== $access) {
$name = $access->getName();
$type = $access->getType();
try {
if (PropertyReadInfo::TYPE_METHOD === $type) {
try {
$result[self::VALUE] = $object->$name();
} catch (\TypeError $e) {
[$trace] = $e->getTrace();
// handle uninitialized properties in PHP >= 7
if (__FILE__ === ($trace['file'] ?? null)
&& $name === $trace['function']
&& $object instanceof $trace['class']
&& preg_match('/Return value (?:of .*::\w+\(\) )?must be of (?:the )?type (\w+), null returned$/', $e->getMessage(), $matches)
) {
throw new UninitializedPropertyException(sprintf('The method "%s::%s()" returned "null", but expected type "%3$s". Did you forget to initialize a property or to make the return type nullable using "?%3$s"?', get_debug_type($object), $name, $matches[1]), 0, $e);
}
throw $e;
}
} elseif (PropertyReadInfo::TYPE_PROPERTY === $type) {
if ($access->canBeReference() && !isset($object->$name) && !\array_key_exists($name, (array) $object) && (\PHP_VERSION_ID < 70400 || !(new \ReflectionProperty($class, $name))->hasType())) {
throw new UninitializedPropertyException(sprintf('The property "%s::$%s" is not initialized.', $class, $name));
}
$result[self::VALUE] = $object->$name;
if (isset($zval[self::REF]) && $access->canBeReference()) {
$result[self::REF] = &$object->$name;
}
}
} catch (\Error $e) {
// handle uninitialized properties in PHP >= 7.4
if (\PHP_VERSION_ID >= 70400 && preg_match('/^Typed property ([\w\\\\@]+)::\$(\w+) must not be accessed before initialization$/', $e->getMessage(), $matches)) {
$r = new \ReflectionProperty(str_contains($matches[1], '@anonymous') ? $class : $matches[1], $matches[2]);
$type = ($type = $r->getType()) instanceof \ReflectionNamedType ? $type->getName() : (string) $type;
throw new UninitializedPropertyException(sprintf('The property "%s::$%s" is not readable because it is typed "%s". You should initialize it or declare a default value instead.', $matches[1], $r->getName(), $type), 0, $e);
}
throw $e;
}
} elseif (property_exists($object, $property) && \array_key_exists($property, (array) $object)) {
$result[self::VALUE] = $object->$property;
if (isset($zval[self::REF])) {
$result[self::REF] = &$object->$property;
}
} elseif (!$ignoreInvalidProperty) {
throw new NoSuchPropertyException(sprintf('Can\'t get a way to read the property "%s" in class "%s".', $property, $class));
}
// Objects are always passed around by reference
if (isset($zval[self::REF]) && \is_object($result[self::VALUE])) {
$result[self::REF] = $result[self::VALUE];
}
return $result;
}
/**
* Guesses how to read the property value.
*/
private function getReadInfo(string $class, string $property): ?PropertyReadInfo
{
$key = str_replace('\\', '.', $class).'..'.$property;
if (isset($this->readPropertyCache[$key])) {
return $this->readPropertyCache[$key];
}
if ($this->cacheItemPool) {
$item = $this->cacheItemPool->getItem(self::CACHE_PREFIX_READ.rawurlencode($key));
if ($item->isHit()) {
return $this->readPropertyCache[$key] = $item->get();
}
}
$accessor = $this->readInfoExtractor->getReadInfo($class, $property, [
'enable_getter_setter_extraction' => true,
'enable_magic_methods_extraction' => $this->magicMethodsFlags,
'enable_constructor_extraction' => false,
]);
if (isset($item)) {
$this->cacheItemPool->save($item->set($accessor));
}
return $this->readPropertyCache[$key] = $accessor;
}
/**
* Sets the value of an index in a given array-accessible value.
*
* @param string|int $index The index to write at
* @param mixed $value The value to write
*
* @throws NoSuchIndexException If the array does not implement \ArrayAccess or it is not an array
*/
private function writeIndex(array $zval, $index, $value)
{
if (!$zval[self::VALUE] instanceof \ArrayAccess && !\is_array($zval[self::VALUE])) {
throw new NoSuchIndexException(sprintf('Cannot modify index "%s" in object of type "%s" because it doesn\'t implement \ArrayAccess.', $index, get_debug_type($zval[self::VALUE])));
}
$zval[self::REF][$index] = $value;
}
/**
* Sets the value of a property in the given object.
*
* @param mixed $value The value to write
*
* @throws NoSuchPropertyException if the property does not exist or is not public
*/
private function writeProperty(array $zval, string $property, $value)
{
if (!\is_object($zval[self::VALUE])) {
throw new NoSuchPropertyException(sprintf('Cannot write property "%s" to an array. Maybe you should write the property path as "[%1$s]" instead?', $property));
}
$object = $zval[self::VALUE];
$class = \get_class($object);
$mutator = $this->getWriteInfo($class, $property, $value);
if (PropertyWriteInfo::TYPE_NONE !== $mutator->getType()) {
$type = $mutator->getType();
if (PropertyWriteInfo::TYPE_METHOD === $type) {
$object->{$mutator->getName()}($value);
} elseif (PropertyWriteInfo::TYPE_PROPERTY === $type) {
$object->{$mutator->getName()} = $value;
} elseif (PropertyWriteInfo::TYPE_ADDER_AND_REMOVER === $type) {
$this->writeCollection($zval, $property, $value, $mutator->getAdderInfo(), $mutator->getRemoverInfo());
}
} elseif ($object instanceof \stdClass && property_exists($object, $property)) {
$object->$property = $value;
} elseif (!$this->ignoreInvalidProperty) {
if ($mutator->hasErrors()) {
throw new NoSuchPropertyException(implode('. ', $mutator->getErrors()).'.');
}
throw new NoSuchPropertyException(sprintf('Could not determine access type for property "%s" in class "%s".', $property, get_debug_type($object)));
}
}
/**
* Adjusts a collection-valued property by calling add*() and remove*() methods.
*/
private function writeCollection(array $zval, string $property, iterable $collection, PropertyWriteInfo $addMethod, PropertyWriteInfo $removeMethod)
{
// At this point the add and remove methods have been found
$previousValue = $this->readProperty($zval, $property);
$previousValue = $previousValue[self::VALUE];
$removeMethodName = $removeMethod->getName();
$addMethodName = $addMethod->getName();
if ($previousValue instanceof \Traversable) {
$previousValue = iterator_to_array($previousValue);
}
if ($previousValue && \is_array($previousValue)) {
if (\is_object($collection)) {
$collection = iterator_to_array($collection);
}
foreach ($previousValue as $key => $item) {
if (!\in_array($item, $collection, true)) {
unset($previousValue[$key]);
$zval[self::VALUE]->$removeMethodName($item);
}
}
} else {
$previousValue = false;
}
foreach ($collection as $item) {
if (!$previousValue || !\in_array($item, $previousValue, true)) {
$zval[self::VALUE]->$addMethodName($item);
}
}
}
private function getWriteInfo(string $class, string $property, $value): PropertyWriteInfo
{
$useAdderAndRemover = is_iterable($value);
$key = str_replace('\\', '.', $class).'..'.$property.'..'.(int) $useAdderAndRemover;
if (isset($this->writePropertyCache[$key])) {
return $this->writePropertyCache[$key];
}
if ($this->cacheItemPool) {
$item = $this->cacheItemPool->getItem(self::CACHE_PREFIX_WRITE.rawurlencode($key));
if ($item->isHit()) {
return $this->writePropertyCache[$key] = $item->get();
}
}
$mutator = $this->writeInfoExtractor->getWriteInfo($class, $property, [
'enable_getter_setter_extraction' => true,
'enable_magic_methods_extraction' => $this->magicMethodsFlags,
'enable_constructor_extraction' => false,
'enable_adder_remover_extraction' => $useAdderAndRemover,
]);
if (isset($item)) {
$this->cacheItemPool->save($item->set($mutator));
}
return $this->writePropertyCache[$key] = $mutator;
}
/**
* Returns whether a property is writable in the given object.
*/
private function isPropertyWritable(object $object, string $property): bool
{
$mutatorForArray = $this->getWriteInfo(\get_class($object), $property, []);
if (PropertyWriteInfo::TYPE_NONE !== $mutatorForArray->getType() || ($object instanceof \stdClass && property_exists($object, $property))) {
return true;
}
$mutator = $this->getWriteInfo(\get_class($object), $property, '');
return PropertyWriteInfo::TYPE_NONE !== $mutator->getType() || ($object instanceof \stdClass && property_exists($object, $property));
}
/**
* Gets a PropertyPath instance and caches it.
*
* @param string|PropertyPath $propertyPath
*/
private function getPropertyPath($propertyPath): PropertyPath
{
if ($propertyPath instanceof PropertyPathInterface) {
// Don't call the copy constructor has it is not needed here
return $propertyPath;
}
if (isset($this->propertyPathCache[$propertyPath])) {
return $this->propertyPathCache[$propertyPath];
}
if ($this->cacheItemPool) {
$item = $this->cacheItemPool->getItem(self::CACHE_PREFIX_PROPERTY_PATH.rawurlencode($propertyPath));
if ($item->isHit()) {
return $this->propertyPathCache[$propertyPath] = $item->get();
}
}
$propertyPathInstance = new PropertyPath($propertyPath);
if (isset($item)) {
$item->set($propertyPathInstance);
$this->cacheItemPool->save($item);
}
return $this->propertyPathCache[$propertyPath] = $propertyPathInstance;
}
/**
* Creates the APCu adapter if applicable.
*
* @return AdapterInterface
*
* @throws \LogicException When the Cache Component isn't available
*/
public static function createCache(string $namespace, int $defaultLifetime, string $version, ?LoggerInterface $logger = null)
{
if (!class_exists(ApcuAdapter::class)) {
throw new \LogicException(sprintf('The Symfony Cache component must be installed to use "%s()".', __METHOD__));
}
if (!ApcuAdapter::isSupported()) {
return new NullAdapter();
}
$apcu = new ApcuAdapter($namespace, $defaultLifetime / 5, $version);
if ('cli' === \PHP_SAPI && !filter_var(\ini_get('apc.enable_cli'), \FILTER_VALIDATE_BOOLEAN)) {
$apcu->setLogger(new NullLogger());
} elseif (null !== $logger) {
$apcu->setLogger($logger);
}
return $apcu;
}
}

View File

@@ -0,0 +1,308 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\PropertyAccess;
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\PropertyInfo\PropertyReadInfoExtractorInterface;
use Symfony\Component\PropertyInfo\PropertyWriteInfoExtractorInterface;
/**
* A configurable builder to create a PropertyAccessor.
*
* @author Jérémie Augustin <jeremie.augustin@pixel-cookers.com>
*/
class PropertyAccessorBuilder
{
/** @var int */
private $magicMethods = PropertyAccessor::MAGIC_GET | PropertyAccessor::MAGIC_SET;
private $throwExceptionOnInvalidIndex = false;
private $throwExceptionOnInvalidPropertyPath = true;
/**
* @var CacheItemPoolInterface|null
*/
private $cacheItemPool;
/**
* @var PropertyReadInfoExtractorInterface|null
*/
private $readInfoExtractor;
/**
* @var PropertyWriteInfoExtractorInterface|null
*/
private $writeInfoExtractor;
/**
* Enables the use of all magic methods by the PropertyAccessor.
*
* @return $this
*/
public function enableMagicMethods(): self
{
$this->magicMethods = PropertyAccessor::MAGIC_GET | PropertyAccessor::MAGIC_SET | PropertyAccessor::MAGIC_CALL;
return $this;
}
/**
* Disable the use of all magic methods by the PropertyAccessor.
*
* @return $this
*/
public function disableMagicMethods(): self
{
$this->magicMethods = PropertyAccessor::DISALLOW_MAGIC_METHODS;
return $this;
}
/**
* Enables the use of "__call" by the PropertyAccessor.
*
* @return $this
*/
public function enableMagicCall()
{
$this->magicMethods |= PropertyAccessor::MAGIC_CALL;
return $this;
}
/**
* Enables the use of "__get" by the PropertyAccessor.
*/
public function enableMagicGet(): self
{
$this->magicMethods |= PropertyAccessor::MAGIC_GET;
return $this;
}
/**
* Enables the use of "__set" by the PropertyAccessor.
*
* @return $this
*/
public function enableMagicSet(): self
{
$this->magicMethods |= PropertyAccessor::MAGIC_SET;
return $this;
}
/**
* Disables the use of "__call" by the PropertyAccessor.
*
* @return $this
*/
public function disableMagicCall()
{
$this->magicMethods &= ~PropertyAccessor::MAGIC_CALL;
return $this;
}
/**
* Disables the use of "__get" by the PropertyAccessor.
*
* @return $this
*/
public function disableMagicGet(): self
{
$this->magicMethods &= ~PropertyAccessor::MAGIC_GET;
return $this;
}
/**
* Disables the use of "__set" by the PropertyAccessor.
*
* @return $this
*/
public function disableMagicSet(): self
{
$this->magicMethods &= ~PropertyAccessor::MAGIC_SET;
return $this;
}
/**
* @return bool whether the use of "__call" by the PropertyAccessor is enabled
*/
public function isMagicCallEnabled()
{
return (bool) ($this->magicMethods & PropertyAccessor::MAGIC_CALL);
}
/**
* @return bool whether the use of "__get" by the PropertyAccessor is enabled
*/
public function isMagicGetEnabled(): bool
{
return $this->magicMethods & PropertyAccessor::MAGIC_GET;
}
/**
* @return bool whether the use of "__set" by the PropertyAccessor is enabled
*/
public function isMagicSetEnabled(): bool
{
return $this->magicMethods & PropertyAccessor::MAGIC_SET;
}
/**
* Enables exceptions when reading a non-existing index.
*
* This has no influence on writing non-existing indices with PropertyAccessorInterface::setValue()
* which are always created on-the-fly.
*
* @return $this
*/
public function enableExceptionOnInvalidIndex()
{
$this->throwExceptionOnInvalidIndex = true;
return $this;
}
/**
* Disables exceptions when reading a non-existing index.
*
* Instead, null is returned when calling PropertyAccessorInterface::getValue() on a non-existing index.
*
* @return $this
*/
public function disableExceptionOnInvalidIndex()
{
$this->throwExceptionOnInvalidIndex = false;
return $this;
}
/**
* @return bool whether an exception is thrown or null is returned when reading a non-existing index
*/
public function isExceptionOnInvalidIndexEnabled()
{
return $this->throwExceptionOnInvalidIndex;
}
/**
* Enables exceptions when reading a non-existing property.
*
* This has no influence on writing non-existing indices with PropertyAccessorInterface::setValue()
* which are always created on-the-fly.
*
* @return $this
*/
public function enableExceptionOnInvalidPropertyPath()
{
$this->throwExceptionOnInvalidPropertyPath = true;
return $this;
}
/**
* Disables exceptions when reading a non-existing index.
*
* Instead, null is returned when calling PropertyAccessorInterface::getValue() on a non-existing index.
*
* @return $this
*/
public function disableExceptionOnInvalidPropertyPath()
{
$this->throwExceptionOnInvalidPropertyPath = false;
return $this;
}
/**
* @return bool whether an exception is thrown or null is returned when reading a non-existing property
*/
public function isExceptionOnInvalidPropertyPath()
{
return $this->throwExceptionOnInvalidPropertyPath;
}
/**
* Sets a cache system.
*
* @return $this
*/
public function setCacheItemPool(?CacheItemPoolInterface $cacheItemPool = null)
{
$this->cacheItemPool = $cacheItemPool;
return $this;
}
/**
* Gets the used cache system.
*
* @return CacheItemPoolInterface|null
*/
public function getCacheItemPool()
{
return $this->cacheItemPool;
}
/**
* @return $this
*/
public function setReadInfoExtractor(?PropertyReadInfoExtractorInterface $readInfoExtractor)
{
$this->readInfoExtractor = $readInfoExtractor;
return $this;
}
public function getReadInfoExtractor(): ?PropertyReadInfoExtractorInterface
{
return $this->readInfoExtractor;
}
/**
* @return $this
*/
public function setWriteInfoExtractor(?PropertyWriteInfoExtractorInterface $writeInfoExtractor)
{
$this->writeInfoExtractor = $writeInfoExtractor;
return $this;
}
public function getWriteInfoExtractor(): ?PropertyWriteInfoExtractorInterface
{
return $this->writeInfoExtractor;
}
/**
* Builds and returns a new PropertyAccessor object.
*
* @return PropertyAccessorInterface
*/
public function getPropertyAccessor()
{
$throw = PropertyAccessor::DO_NOT_THROW;
if ($this->throwExceptionOnInvalidIndex) {
$throw |= PropertyAccessor::THROW_ON_INVALID_INDEX;
}
if ($this->throwExceptionOnInvalidPropertyPath) {
$throw |= PropertyAccessor::THROW_ON_INVALID_PROPERTY_PATH;
}
return new PropertyAccessor($this->magicMethods, $throw, $this->cacheItemPool, $this->readInfoExtractor, $this->writeInfoExtractor);
}
}

View File

@@ -0,0 +1,114 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\PropertyAccess;
/**
* Writes and reads values to/from an object/array graph.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
interface PropertyAccessorInterface
{
/**
* Sets the value at the end of the property path of the object graph.
*
* Example:
*
* use Symfony\Component\PropertyAccess\PropertyAccess;
*
* $propertyAccessor = PropertyAccess::createPropertyAccessor();
*
* echo $propertyAccessor->setValue($object, 'child.name', 'Fabien');
* // equals echo $object->getChild()->setName('Fabien');
*
* This method first tries to find a public setter for each property in the
* path. The name of the setter must be the camel-cased property name
* prefixed with "set".
*
* If the setter does not exist, this method tries to find a public
* property. The value of the property is then changed.
*
* If neither is found, an exception is thrown.
*
* @param object|array $objectOrArray The object or array to modify
* @param string|PropertyPathInterface $propertyPath The property path to modify
* @param mixed $value The value to set at the end of the property path
*
* @throws Exception\InvalidArgumentException If the property path is invalid
* @throws Exception\AccessException If a property/index does not exist or is not public
* @throws Exception\UnexpectedTypeException If a value within the path is neither object nor array
*/
public function setValue(&$objectOrArray, $propertyPath, $value);
/**
* Returns the value at the end of the property path of the object graph.
*
* Example:
*
* use Symfony\Component\PropertyAccess\PropertyAccess;
*
* $propertyAccessor = PropertyAccess::createPropertyAccessor();
*
* echo $propertyAccessor->getValue($object, 'child.name');
* // equals echo $object->getChild()->getName();
*
* This method first tries to find a public getter for each property in the
* path. The name of the getter must be the camel-cased property name
* prefixed with "get", "is", or "has".
*
* If the getter does not exist, this method tries to find a public
* property. The value of the property is then returned.
*
* If none of them are found, an exception is thrown.
*
* @param object|array $objectOrArray The object or array to traverse
* @param string|PropertyPathInterface $propertyPath The property path to read
*
* @return mixed
*
* @throws Exception\InvalidArgumentException If the property path is invalid
* @throws Exception\AccessException If a property/index does not exist or is not public
* @throws Exception\UnexpectedTypeException If a value within the path is neither object
* nor array
*/
public function getValue($objectOrArray, $propertyPath);
/**
* Returns whether a value can be written at a given property path.
*
* Whenever this method returns true, {@link setValue()} is guaranteed not
* to throw an exception when called with the same arguments.
*
* @param object|array $objectOrArray The object or array to check
* @param string|PropertyPathInterface $propertyPath The property path to check
*
* @return bool
*
* @throws Exception\InvalidArgumentException If the property path is invalid
*/
public function isWritable($objectOrArray, $propertyPath);
/**
* Returns whether a property path can be read from an object graph.
*
* Whenever this method returns true, {@link getValue()} is guaranteed not
* to throw an exception when called with the same arguments.
*
* @param object|array $objectOrArray The object or array to check
* @param string|PropertyPathInterface $propertyPath The property path to check
*
* @return bool
*
* @throws Exception\InvalidArgumentException If the property path is invalid
*/
public function isReadable($objectOrArray, $propertyPath);
}

View File

@@ -0,0 +1,208 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\PropertyAccess;
use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException;
use Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException;
use Symfony\Component\PropertyAccess\Exception\OutOfBoundsException;
/**
* Default implementation of {@link PropertyPathInterface}.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @implements \IteratorAggregate<int, string>
*/
class PropertyPath implements \IteratorAggregate, PropertyPathInterface
{
/**
* Character used for separating between plural and singular of an element.
*/
public const SINGULAR_SEPARATOR = '|';
/**
* The elements of the property path.
*
* @var list<string>
*/
private $elements = [];
/**
* The number of elements in the property path.
*
* @var int
*/
private $length;
/**
* Contains a Boolean for each property in $elements denoting whether this
* element is an index. It is a property otherwise.
*
* @var array
*/
private $isIndex = [];
/**
* String representation of the path.
*
* @var string
*/
private $pathAsString;
/**
* Constructs a property path from a string.
*
* @param PropertyPath|string $propertyPath The property path as string or instance
*
* @throws InvalidArgumentException If the given path is not a string
* @throws InvalidPropertyPathException If the syntax of the property path is not valid
*/
public function __construct($propertyPath)
{
// Can be used as copy constructor
if ($propertyPath instanceof self) {
/* @var PropertyPath $propertyPath */
$this->elements = $propertyPath->elements;
$this->length = $propertyPath->length;
$this->isIndex = $propertyPath->isIndex;
$this->pathAsString = $propertyPath->pathAsString;
return;
}
if (!\is_string($propertyPath)) {
throw new InvalidArgumentException(sprintf('The property path constructor needs a string or an instance of "Symfony\Component\PropertyAccess\PropertyPath". Got: "%s".', get_debug_type($propertyPath)));
}
if ('' === $propertyPath) {
throw new InvalidPropertyPathException('The property path should not be empty.');
}
$this->pathAsString = $propertyPath;
$position = 0;
$remaining = $propertyPath;
// first element is evaluated differently - no leading dot for properties
$pattern = '/^(([^\.\[]++)|\[([^\]]++)\])(.*)/';
while (preg_match($pattern, $remaining, $matches)) {
if ('' !== $matches[2]) {
$element = $matches[2];
$this->isIndex[] = false;
} else {
$element = $matches[3];
$this->isIndex[] = true;
}
$this->elements[] = $element;
$position += \strlen($matches[1]);
$remaining = $matches[4];
$pattern = '/^(\.([^\.|\[]++)|\[([^\]]++)\])(.*)/';
}
if ('' !== $remaining) {
throw new InvalidPropertyPathException(sprintf('Could not parse property path "%s". Unexpected token "%s" at position %d.', $propertyPath, $remaining[0], $position));
}
$this->length = \count($this->elements);
}
/**
* {@inheritdoc}
*/
public function __toString()
{
return $this->pathAsString;
}
/**
* {@inheritdoc}
*/
public function getLength()
{
return $this->length;
}
/**
* {@inheritdoc}
*/
public function getParent()
{
if ($this->length <= 1) {
return null;
}
$parent = clone $this;
--$parent->length;
$parent->pathAsString = substr($parent->pathAsString, 0, max(strrpos($parent->pathAsString, '.'), strrpos($parent->pathAsString, '[')));
array_pop($parent->elements);
array_pop($parent->isIndex);
return $parent;
}
/**
* Returns a new iterator for this path.
*
* @return PropertyPathIteratorInterface
*/
#[\ReturnTypeWillChange]
public function getIterator()
{
return new PropertyPathIterator($this);
}
/**
* {@inheritdoc}
*/
public function getElements()
{
return $this->elements;
}
/**
* {@inheritdoc}
*/
public function getElement(int $index)
{
if (!isset($this->elements[$index])) {
throw new OutOfBoundsException(sprintf('The index "%s" is not within the property path.', $index));
}
return $this->elements[$index];
}
/**
* {@inheritdoc}
*/
public function isProperty(int $index)
{
if (!isset($this->isIndex[$index])) {
throw new OutOfBoundsException(sprintf('The index "%s" is not within the property path.', $index));
}
return !$this->isIndex[$index];
}
/**
* {@inheritdoc}
*/
public function isIndex(int $index)
{
if (!isset($this->isIndex[$index])) {
throw new OutOfBoundsException(sprintf('The index "%s" is not within the property path.', $index));
}
return $this->isIndex[$index];
}
}

View File

@@ -0,0 +1,281 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\PropertyAccess;
use Symfony\Component\PropertyAccess\Exception\OutOfBoundsException;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class PropertyPathBuilder
{
private $elements = [];
private $isIndex = [];
/**
* Creates a new property path builder.
*
* @param PropertyPathInterface|string|null $path The path to initially store
* in the builder. Optional.
*/
public function __construct($path = null)
{
if (null !== $path) {
$this->append($path);
}
}
/**
* Appends a (sub-) path to the current path.
*
* @param PropertyPathInterface|string $path The path to append
* @param int $offset The offset where the appended
* piece starts in $path
* @param int $length The length of the appended piece
* If 0, the full path is appended
*/
public function append($path, int $offset = 0, int $length = 0)
{
if (\is_string($path)) {
$path = new PropertyPath($path);
}
if (0 === $length) {
$end = $path->getLength();
} else {
$end = $offset + $length;
}
for (; $offset < $end; ++$offset) {
$this->elements[] = $path->getElement($offset);
$this->isIndex[] = $path->isIndex($offset);
}
}
/**
* Appends an index element to the current path.
*/
public function appendIndex(string $name)
{
$this->elements[] = $name;
$this->isIndex[] = true;
}
/**
* Appends a property element to the current path.
*/
public function appendProperty(string $name)
{
$this->elements[] = $name;
$this->isIndex[] = false;
}
/**
* Removes elements from the current path.
*
* @throws OutOfBoundsException if offset is invalid
*/
public function remove(int $offset, int $length = 1)
{
if (!isset($this->elements[$offset])) {
throw new OutOfBoundsException(sprintf('The offset "%s" is not within the property path.', $offset));
}
$this->resize($offset, $length, 0);
}
/**
* Replaces a sub-path by a different (sub-) path.
*
* @param int $offset The offset at which to replace
* @param int $length The length of the piece to replace
* @param PropertyPathInterface|string $path The path to insert
* @param int $pathOffset The offset where the inserted piece
* starts in $path
* @param int $pathLength The length of the inserted piece
* If 0, the full path is inserted
*
* @throws OutOfBoundsException If the offset is invalid
*/
public function replace(int $offset, int $length, $path, int $pathOffset = 0, int $pathLength = 0)
{
if (\is_string($path)) {
$path = new PropertyPath($path);
}
if ($offset < 0 && abs($offset) <= $this->getLength()) {
$offset = $this->getLength() + $offset;
} elseif (!isset($this->elements[$offset])) {
throw new OutOfBoundsException('The offset '.$offset.' is not within the property path');
}
if (0 === $pathLength) {
$pathLength = $path->getLength() - $pathOffset;
}
$this->resize($offset, $length, $pathLength);
for ($i = 0; $i < $pathLength; ++$i) {
$this->elements[$offset + $i] = $path->getElement($pathOffset + $i);
$this->isIndex[$offset + $i] = $path->isIndex($pathOffset + $i);
}
ksort($this->elements);
}
/**
* Replaces a property element by an index element.
*
* @throws OutOfBoundsException If the offset is invalid
*/
public function replaceByIndex(int $offset, ?string $name = null)
{
if (!isset($this->elements[$offset])) {
throw new OutOfBoundsException(sprintf('The offset "%s" is not within the property path.', $offset));
}
if (null !== $name) {
$this->elements[$offset] = $name;
}
$this->isIndex[$offset] = true;
}
/**
* Replaces an index element by a property element.
*
* @throws OutOfBoundsException If the offset is invalid
*/
public function replaceByProperty(int $offset, ?string $name = null)
{
if (!isset($this->elements[$offset])) {
throw new OutOfBoundsException(sprintf('The offset "%s" is not within the property path.', $offset));
}
if (null !== $name) {
$this->elements[$offset] = $name;
}
$this->isIndex[$offset] = false;
}
/**
* Returns the length of the current path.
*
* @return int
*/
public function getLength()
{
return \count($this->elements);
}
/**
* Returns the current property path.
*
* @return PropertyPathInterface|null
*/
public function getPropertyPath()
{
$pathAsString = $this->__toString();
return '' !== $pathAsString ? new PropertyPath($pathAsString) : null;
}
/**
* Returns the current property path as string.
*
* @return string
*/
public function __toString()
{
$string = '';
foreach ($this->elements as $offset => $element) {
if ($this->isIndex[$offset]) {
$element = '['.$element.']';
} elseif ('' !== $string) {
$string .= '.';
}
$string .= $element;
}
return $string;
}
/**
* Resizes the path so that a chunk of length $cutLength is
* removed at $offset and another chunk of length $insertionLength
* can be inserted.
*/
private function resize(int $offset, int $cutLength, int $insertionLength)
{
// Nothing else to do in this case
if ($insertionLength === $cutLength) {
return;
}
$length = \count($this->elements);
if ($cutLength > $insertionLength) {
// More elements should be removed than inserted
$diff = $cutLength - $insertionLength;
$newLength = $length - $diff;
// Shift elements to the left (left-to-right until the new end)
// Max allowed offset to be shifted is such that
// $offset + $diff < $length (otherwise invalid index access)
// i.e. $offset < $length - $diff = $newLength
for ($i = $offset; $i < $newLength; ++$i) {
$this->elements[$i] = $this->elements[$i + $diff];
$this->isIndex[$i] = $this->isIndex[$i + $diff];
}
// All remaining elements should be removed
$this->elements = \array_slice($this->elements, 0, $i);
$this->isIndex = \array_slice($this->isIndex, 0, $i);
} else {
$diff = $insertionLength - $cutLength;
$newLength = $length + $diff;
$indexAfterInsertion = $offset + $insertionLength;
// $diff <= $insertionLength
// $indexAfterInsertion >= $insertionLength
// => $diff <= $indexAfterInsertion
// In each of the following loops, $i >= $diff must hold,
// otherwise ($i - $diff) becomes negative.
// Shift old elements to the right to make up space for the
// inserted elements. This needs to be done left-to-right in
// order to preserve an ascending array index order
// Since $i = max($length, $indexAfterInsertion) and $indexAfterInsertion >= $diff,
// $i >= $diff is guaranteed.
for ($i = max($length, $indexAfterInsertion); $i < $newLength; ++$i) {
$this->elements[$i] = $this->elements[$i - $diff];
$this->isIndex[$i] = $this->isIndex[$i - $diff];
}
// Shift remaining elements to the right. Do this right-to-left
// so we don't overwrite elements before copying them
// The last written index is the immediate index after the inserted
// string, because the indices before that will be overwritten
// anyway.
// Since $i >= $indexAfterInsertion and $indexAfterInsertion >= $diff,
// $i >= $diff is guaranteed.
for ($i = $length - 1; $i >= $indexAfterInsertion; --$i) {
$this->elements[$i] = $this->elements[$i - $diff];
$this->isIndex[$i] = $this->isIndex[$i - $diff];
}
}
}
}

View File

@@ -0,0 +1,88 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\PropertyAccess;
/**
* A sequence of property names or array indices.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @extends \Traversable<int, string>
*/
interface PropertyPathInterface extends \Traversable
{
/**
* Returns the string representation of the property path.
*
* @return string
*/
public function __toString();
/**
* Returns the length of the property path, i.e. the number of elements.
*
* @return int
*/
public function getLength();
/**
* Returns the parent property path.
*
* The parent property path is the one that contains the same items as
* this one except for the last one.
*
* If this property path only contains one item, null is returned.
*
* @return self|null
*/
public function getParent();
/**
* Returns the elements of the property path as array.
*
* @return list<string>
*/
public function getElements();
/**
* Returns the element at the given index in the property path.
*
* @param int $index The index key
*
* @return string
*
* @throws Exception\OutOfBoundsException If the offset is invalid
*/
public function getElement(int $index);
/**
* Returns whether the element at the given index is a property.
*
* @param int $index The index in the property path
*
* @return bool
*
* @throws Exception\OutOfBoundsException If the offset is invalid
*/
public function isProperty(int $index);
/**
* Returns whether the element at the given index is an array index.
*
* @param int $index The index in the property path
*
* @return bool
*
* @throws Exception\OutOfBoundsException If the offset is invalid
*/
public function isIndex(int $index);
}

View File

@@ -0,0 +1,48 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\PropertyAccess;
/**
* Traverses a property path and provides additional methods to find out
* information about the current element.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @extends \ArrayIterator<int, string>
*/
class PropertyPathIterator extends \ArrayIterator implements PropertyPathIteratorInterface
{
protected $path;
public function __construct(PropertyPathInterface $path)
{
parent::__construct($path->getElements());
$this->path = $path;
}
/**
* {@inheritdoc}
*/
public function isIndex()
{
return $this->path->isIndex($this->key());
}
/**
* {@inheritdoc}
*/
public function isProperty()
{
return $this->path->isProperty($this->key());
}
}

View File

@@ -0,0 +1,36 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\PropertyAccess;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @extends \SeekableIterator<int, string>
*/
interface PropertyPathIteratorInterface extends \SeekableIterator
{
/**
* Returns whether the current element in the property path is an array
* index.
*
* @return bool
*/
public function isIndex();
/**
* Returns whether the current element in the property path is a property
* name.
*
* @return bool
*/
public function isProperty();
}

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