improve/inc/module/phpcsfixer/libs/php-cs-fixer.phar

135854 lines
2.6 MiB
Raw Blame History

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#!/usr/bin/env php
<?php
/*
* This file is part of PHP CS Fixer.
*
* (c) Fabien Potencier <fabien@symfony.com>
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
error_reporting(E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED);
set_error_handler(static function ($severity, $message, $file, $line) {
if ($severity & error_reporting()) {
throw new ErrorException($message, 0, $severity, $file, $line);
}
});
// check environment requirements
(function () {
if (\PHP_VERSION_ID === 80000) {
fwrite(STDERR, "PHP CS Fixer is not able run on PHP 8.0.0 due to bug in PHP tokenizer (https://bugs.php.net/bug.php?id=80462).\n");
fwrite(STDERR, "Update PHP version to unblock execution.\n");
exit(1);
}
if (\PHP_VERSION_ID < 70400 || \PHP_VERSION_ID >= 80200) {
fwrite(STDERR, "PHP needs to be a minimum version of PHP 7.4.0 and maximum version of PHP 8.1.*.\n");
fwrite(STDERR, 'Current PHP version: '.PHP_VERSION.".\n");
if (getenv('PHP_CS_FIXER_IGNORE_ENV')) {
fwrite(STDERR, "Ignoring environment requirements because `PHP_CS_FIXER_IGNORE_ENV` is set. Execution may be unstable.\n");
} else {
fwrite(STDERR, "To ignore this requirement please set `PHP_CS_FIXER_IGNORE_ENV`.\n");
fwrite(STDERR, "If you use PHP version higher than supported, you may experience code modified in a wrong way.\n");
fwrite(STDERR, "Please report such cases at https://github.com/PHP-CS-Fixer/PHP-CS-Fixer .\n");
exit(1);
}
}
foreach (['json', 'tokenizer'] as $extension) {
if (!extension_loaded($extension)) {
fwrite(STDERR, sprintf("PHP extension ext-%s is missing from your system. Install or enable it.\n", $extension));
if (getenv('PHP_CS_FIXER_IGNORE_ENV')) {
fwrite(STDERR, "Ignoring environment requirements because `PHP_CS_FIXER_IGNORE_ENV` is set. Execution may be unstable.\n");
} else {
exit(1);
}
}
}
})();
// load dependencies
(function () {
$require = true;
if (class_exists('Phar')) {
// Maybe this file is used as phar-stub? Let's try!
try {
Phar::mapPhar('php-cs-fixer.phar');
require_once 'phar://php-cs-fixer.phar/vendor/autoload.php';
$require = false;
} catch (PharException $e) {
}
}
if ($require) {
// OK, it's not, let give Composer autoloader a try!
$possibleFiles = [__DIR__.'/../../autoload.php', __DIR__.'/../autoload.php', __DIR__.'/vendor/autoload.php'];
$file = null;
foreach ($possibleFiles as $possibleFile) {
if (file_exists($possibleFile)) {
$file = $possibleFile;
break;
}
}
if (null === $file) {
throw new RuntimeException('Unable to locate autoload.php file.');
}
require_once $file;
}
})();
use Composer\XdebugHandler\XdebugHandler;
use PhpCsFixer\Console\Application;
// Restart if xdebug is loaded, unless the environment variable PHP_CS_FIXER_ALLOW_XDEBUG is set.
$xdebug = new XdebugHandler('PHP_CS_FIXER');
$xdebug->check();
unset($xdebug);
$application = new Application();
$application->run();
__HALT_COMPILER(); ?>
<14>? php-cs-fixerj "`cj <00>J<8B>&<26>LICENSE< "`c<}+>i<>ci-integration.sh<73> "`c<><00><><C1><91><87>vendor/autoload.php} "`c}R7<52><37><E2>'vendor/composer/autoload_namespaces.phpZ "`cZ<00><>0vendor/composer/xdebug-handler/src/PhpConfig.php<68> "`c<>K<>D{<7B>-vendor/composer/xdebug-handler/src/Status.php<68> "`c<> xf17<31>4vendor/composer/xdebug-handler/src/XdebugHandler.php<68>) "`c<>)K)<29><1E>.vendor/composer/xdebug-handler/src/Process.php<68> "`c<>o7<><37>vendor/composer/ClassLoader.php "`cΆ<><CE86>!vendor/composer/autoload_psr4.php<68> "`c<>Q<><51>"<22>%vendor/composer/autoload_classmap.php1s "`c1s<00><><06><>"vendor/composer/platform_check.php  "`c <00>/<2F>i<B6>#vendor/composer/autoload_static.phpg<70> "`cg<63><00><>2,<2C>!vendor/composer/autoload_real.phpI "`cI<00><><B2><A2><FF>/vendor/composer/semver/src/CompilingMatcher.php<68> "`c<>9`<60><><BD>'vendor/composer/semver/src/Interval.php<68> "`c<>=[i<>)vendor/composer/semver/src/Comparator.php# "`c#*A<><41><94>(vendor/composer/semver/src/Intervals.php, "`c,"<22>T<8A><54>%vendor/composer/semver/src/Semver.php<68> "`c<><00>-i<>,vendor/composer/semver/src/VersionParser.php - "`c -<00><>m<FD><6D>=vendor/composer/semver/src/Constraint/ConstraintInterface.php<68> "`c<>"<22><>E<BC>9vendor/composer/semver/src/Constraint/MultiConstraint.php<68> "`c<><11><>x<CD>4vendor/composer/semver/src/Constraint/Constraint.php<68> "`c<><00>3l<33><6C><vendor/composer/semver/src/Constraint/MatchAllConstraint.php<68> "`c<><00>E<17><>=vendor/composer/semver/src/Constraint/MatchNoneConstraint.php<68> "`c<>֯خ<D6AF>/vendor/composer/semver/src/Constraint/Bound.phpw "`cwW4]W<>"vendor/composer/autoload_files.php<68> "`c<>}q<><71>%vendor/composer/InstalledVersions.php "`c<00><><BC>T<8A>vendor/composer/installed.php<68>! "`c<>!h<><68>Y<8C>(vendor/composer/pcre/src/MatchResult.php "`c/[}ۤ"vendor/composer/pcre/src/Regex.php<68> "`c<> @<40>g<81><67>6vendor/composer/pcre/src/MatchAllWithOffsetsResult.phpo "`co<00>e⬤3vendor/composer/pcre/src/MatchWithOffsetsResult.php3 "`c3<00>a<CC>}<7D>+vendor/composer/pcre/src/MatchAllResult.phpC "`cC<00>I<E1>4<EB>*vendor/composer/pcre/src/ReplaceResult.php6 "`c67--<2D>!vendor/composer/pcre/src/Preg.php1 "`c1<13>*vendor/composer/pcre/src/PcreException.phpI "`cI<00><>
7<A4>Avendor/doctrine/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.phpw "`cw J?설Ivendor/doctrine/annotations/lib/Doctrine/Common/Annotations/PhpParser.php "`c<00>X<0E><>Svendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationException.php<68> "`c<><00>2l<32>Ovendor/doctrine/annotations/lib/Doctrine/Common/Annotations/FileCacheReader.php<68> "`c<> Hk<48><6B>cvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/NamedArgumentConstructor.phpi "`ci?<3F>7<>[vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.phpu "`cu<00>SD<53><44>Uvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attributes.phpk "`ck<00>َ<89><D98E>Ovendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Enum.php<68> "`c<><00>J<ED>`<60>Tvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attribute.php<68> "`c<><00> q=<3D>Qvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Target.php<68> "`c<><00><D5>ΤSvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Required.phpY "`cY<00><>"<22><>Mvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/IndexedReader.php7 "`c7<00><>><07>Fvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Reader.php<68> "`c<><00>#<23><><A1>Jvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation.php= "`c=Ī<> <20>Vvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.phpz "`czx<><78>ͤOvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/PsrCachedReader.phpe "`ce<00><>Ivendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php8j "`c8j<00>-<2D>D<97>Lvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/CachedReader.php<68> "`c<><11>=<3D>Kvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/TokenParser.php* "`c* ^<5E>Ť`vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/ImplicitlyIgnoredAnnotationNames.php<68> "`c<> J<><4A>#<23>Pvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationReader.php\ "`c\<00>&<26>a<D0>Rvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationRegistry.phpK "`cK n<>]<5D>Hvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocLexer.phpc "`cc <00>I<88>ݤbvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/NamedArgumentConstructorAnnotation.phpg "`cg<00>7<89><37><D7>/vendor/psr/cache/src/CacheItemPoolInterface.php "`cf<><66><AB>+vendor/psr/cache/src/CacheItemInterface.phpN "`cN<<3C><><8B><B0>1vendor/psr/cache/src/InvalidArgumentException.phpa "`ca-<2D><> <0C>'vendor/psr/cache/src/CacheException.php= "`c=<18>A<F4>=vendor/psr/event-dispatcher/src/ListenerProviderInterface.php<68> "`c<>7
<19><vendor/psr/event-dispatcher/src/EventDispatcherInterface.php<68> "`c<><00>ISؤ;vendor/psr/event-dispatcher/src/StoppableEventInterface.php<68> "`c<><00><19>.<2E>7vendor/psr/container/src/NotFoundExceptionInterface.phpq "`cqR<52><7F><FA>/vendor/psr/container/src/ContainerInterface.php<68> "`c<><00><><01><>8vendor/psr/container/src/ContainerExceptionInterface.phpp "`cp;Z<>
<A4>/vendor/psr/log/Psr/Log/LoggerAwareInterface.php| "`c|$<13><><A3>#vendor/psr/log/Psr/Log/LogLevel.php<68> "`c<>j<><6A>8<F1>+vendor/psr/log/Psr/Log/LoggerAwareTrait.php<68> "`c<>T<><54>B<FA>3vendor/psr/log/Psr/Log/InvalidArgumentException.php` "`c` <20>X1<58>%vendor/psr/log/Psr/Log/NullLogger.php<68> "`c<><00>X<81><58><F3>*vendor/psr/log/Psr/Log/LoggerInterface.php<68> "`c<><00>x<1D>&vendor/psr/log/Psr/Log/LoggerTrait.phpk "`ck<00>}<7D><><89>)vendor/psr/log/Psr/Log/AbstractLogger.php; "`c;<00>>3[<5B>'vendor/symfony/polyfill-php73/Php73.phpn "`cnCs<43>l<A5>?vendor/symfony/polyfill-php73/Resources/stubs/JsonException.php[ "`c[M<18>ܤ+vendor/symfony/polyfill-php73/bootstrap.php<68> "`c<>AY8Ƥ*vendor/symfony/polyfill-php80/PhpToken.php "`cM<>I<>:vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php<68> "`c<>}<7D>GG<47><vendor/symfony/polyfill-php80/Resources/stubs/ValueError.phpK "`cK<00><><8A><1C>;vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php<68> "`c<><05><>ФEvendor/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.phpT "`cT<00><>O<D3><4F><vendor/symfony/polyfill-php80/Resources/stubs/Stringable.phpb "`cb<00>ћ<<3C>+vendor/symfony/polyfill-php80/bootstrap.php<68> "`c<><00><><D7><02>'vendor/symfony/polyfill-php80/Php80.php<68> "`c<> [<5B>k <0B>-vendor/symfony/polyfill-ctype/bootstrap80.phph "`chK<>y<BF><79>+vendor/symfony/polyfill-ctype/bootstrap.php2 "`c2<00>a<C8>8<AD>'vendor/symfony/polyfill-ctype/Ctype.php<68>
"`c<>
~<7E>d<B6><64>Fvendor/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php<68> "`c<> f<><18>+vendor/symfony/polyfill-php81/bootstrap.php<68> "`c<><00><><88>\<5C>'vendor/symfony/polyfill-php81/Php81.php; "`c;<16><>d<AB><vendor/symfony/event-dispatcher/EventDispatcherInterface.php  "`c <00>-hM<68>=vendor/symfony/event-dispatcher/Attribute/AsEventListener.phpd "`cd<00><><C0><FA><AE>>vendor/symfony/event-dispatcher/LegacyEventDispatcherProxy.php<68> "`c<><10><>b<A7>3vendor/symfony/event-dispatcher/EventDispatcher.php; "`c;<\<5C>w<B0><vendor/symfony/event-dispatcher/EventSubscriberInterface.php<68> "`c<>!jcK<63><vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php<68> "`c<>Yi<59>v<B8>0vendor/symfony/event-dispatcher/GenericEvent.phpG "`cG<00>.Y<>Mvendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php<68> "`c<><00>̶5<CCB6>Kvendor/symfony/event-dispatcher/DependencyInjection/AddEventAliasesPass.php<68> "`c<>'"<22>L<94>Bvendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.phpz "`cz <09><>T<CD>9vendor/symfony/event-dispatcher/Debug/WrappedListener.php<68> "`c<> }<7D><><A9><80>(vendor/symfony/filesystem/Filesystem.php<68>@ "`c<>@)<29>6b<36>"vendor/symfony/filesystem/Path.php=' "`c='<00><>4L<34><vendor/symfony/filesystem/Exception/IOExceptionInterface.php<68> "`c<>j<>wM<77>:vendor/symfony/filesystem/Exception/ExceptionInterface.php| "`c|<00><05>D<E9>8vendor/symfony/filesystem/Exception/RuntimeException.php<68> "`c<><00>UUH<55>3vendor/symfony/filesystem/Exception/IOException.php<68> "`c<><00><>\Τ@vendor/symfony/filesystem/Exception/InvalidArgumentException.php<68> "`c<><00>!Ǥ=vendor/symfony/filesystem/Exception/FileNotFoundException.php<68> "`c<>&<26><><90><F2>6vendor/symfony/options-resolver/OptionConfigurator.php<68> "`c<>z<>g<A4><67>3vendor/symfony/options-resolver/OptionsResolver.php%W "`c%W)<29><>y<E6>+vendor/symfony/options-resolver/Options.php{ "`c{<00>|<7C><><B7>Evendor/symfony/options-resolver/Exception/InvalidOptionsException.php<68> "`c<><00><>k <0A>Evendor/symfony/options-resolver/Exception/MissingOptionsException.php<68> "`c<>=<3D><><BC><BD>=vendor/symfony/options-resolver/Exception/AccessException.php<68> "`c<><00>5<13>Gvendor/symfony/options-resolver/Exception/OptionDefinitionException.php<68> "`c<><00>@4<><34>@vendor/symfony/options-resolver/Exception/ExceptionInterface.php<68> "`c<><00><><8C>&<26>Fvendor/symfony/options-resolver/Exception/InvalidArgumentException.php<68> "`c<><1C>G<B2><47>Cvendor/symfony/options-resolver/Exception/NoSuchOptionException.php<68> "`c<>g]<5D>ȤFvendor/symfony/options-resolver/Exception/NoConfigurationException.php<68> "`c<>j<><6A>E<AE>Gvendor/symfony/options-resolver/Exception/UndefinedOptionsException.php<68> "`c<>p ͐<>Evendor/symfony/options-resolver/Debug/OptionsResolverIntrospector.php<68> "`c<><00>$-0<>7vendor/symfony/polyfill-intl-normalizer/bootstrap80.php<68> "`c<>=<3D>r<E5><72>6vendor/symfony/polyfill-intl-normalizer/Normalizer.php<68> "`c<>b<>"q<>Fvendor/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php. "`c.Qs$<24><>Lvendor/symfony/polyfill-intl-normalizer/Resources/unidata/combiningClass.phpt. "`ct. q<>ܤTvendor/symfony/polyfill-intl-normalizer/Resources/unidata/canonicalDecomposition.phpa<70> "`ca<63><00>R<E7>}<7D>Rvendor/symfony/polyfill-intl-normalizer/Resources/unidata/canonicalComposition.php<68>= "`c<>=*<2A>o?<3F>Xvendor/symfony/polyfill-intl-normalizer/Resources/unidata/compatibilityDecomposition.php<68><70> "`c<><63>o<>e)<29>5vendor/symfony/polyfill-intl-normalizer/bootstrap.php<68> "`c<>P<><50>D<A3>Fvendor/symfony/event-dispatcher-contracts/EventDispatcherInterface.php2 "`c2s<><73>v<EE>3vendor/symfony/event-dispatcher-contracts/Event.php<68> "`c<><00><>ְ<BB>%vendor/symfony/finder/SplFileInfo.php<68> "`c<>y<1B>5vendor/symfony/finder/Comparator/NumberComparator.phpz "`cz+<2B>h<>/vendor/symfony/finder/Comparator/Comparator.php3 "`c3<12><>h<C8>3vendor/symfony/finder/Comparator/DateComparator.php "`c<00>%<25>e<FA> vendor/symfony/finder/Finder.php<68>' "`c<>'<00>vendor/symfony/finder/Glob.php<68> "`c<>Db<44><62><87>/vendor/symfony/finder/Iterator/LazyIterator.phpQ "`cQn]<5D><><BF>;vendor/symfony/finder/Iterator/VcsIgnoredFilterIterator.phpw
"`cw
ŵ3<><vendor/symfony/finder/Iterator/FilecontentFilterIterator.phpd "`cd<00>0<99>"<22>=vendor/symfony/finder/Iterator/MultiplePcreFilterIterator.php<68> "`c<>0<><30><9A>:vendor/symfony/finder/Iterator/SizeRangeFilterIterator.php<68> "`c<>(j<><6A>9vendor/symfony/finder/Iterator/FilenameFilterIterator.php<68> "`c<>Ү<><D2AE><C1>:vendor/symfony/finder/Iterator/DateRangeFilterIterator.php<68> "`c<>q<><71>X<E3>7vendor/symfony/finder/Iterator/CustomFilterIterator.phpp "`cp.E<><45><DA>=vendor/symfony/finder/Iterator/RecursiveDirectoryIterator.php<68> "`c<> <00><>v2<76>Avendor/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.phpG "`cG<00>wT0<54>3vendor/symfony/finder/Iterator/SortableIterator.php8
"`c8
<00>QYf<59>;vendor/symfony/finder/Iterator/DepthRangeFilterIterator.phpJ "`cJ<00><>s<<3C>9vendor/symfony/finder/Iterator/FileTypeFilterIterator.php<68> "`c<>< Hu<48>5vendor/symfony/finder/Iterator/PathFilterIterator.php<68> "`c<>Y\<5C>d<8F>#vendor/symfony/finder/Gitignore.php<68> "`c<>s 4<>9vendor/symfony/finder/Exception/AccessDeniedException.php<68> "`c<><00><>s<BE><73>>vendor/symfony/finder/Exception/DirectoryNotFoundException.php<68> "`c<>a)<29><1B>&vendor/symfony/stopwatch/Stopwatch.php<68> "`c<>f<00><><83>$vendor/symfony/stopwatch/Section.php<68> "`c<>9E߽<45>,vendor/symfony/stopwatch/StopwatchPeriod.php<68> "`c<><00>ѳH<D1B3>+vendor/symfony/stopwatch/StopwatchEvent.php
"`c
A<><41><8D><84>)vendor/symfony/string/CodePointString.php<68> "`c<><00>;3ˤ(vendor/symfony/string/AbstractString.php<68>3 "`c<>3<00><><98>֤-vendor/symfony/string/Resources/functions.php "`ccڪ"<22><vendor/symfony/string/Resources/data/wcswidth_table_zero.php "`c k<><vendor/symfony/string/Resources/data/wcswidth_table_wide.php<68> "`c<>-<2D><>A<A6>3vendor/symfony/string/Inflector/FrenchInflector.php<68>
"`c<>
<00><>H<>6vendor/symfony/string/Inflector/InflectorInterface.php<68> "`c<>hD<68><17>4vendor/symfony/string/Inflector/EnglishInflector.php<68> "`c<><00>3<85>d<A7>/vendor/symfony/string/AbstractUnicodeString.php<68>R "`c<>REh<>$vendor/symfony/string/LazyString.php "`c (<28><>L<DC>$vendor/symfony/string/ByteString.php<68>+ "`c<>+@<40><><C0><BB>'vendor/symfony/string/UnicodeString.php<68>% "`c<>%<00><> פ6vendor/symfony/string/Exception/ExceptionInterface.phps "`csqHV<48><56>4vendor/symfony/string/Exception/RuntimeException.php<68> "`c<>u<>g<><vendor/symfony/string/Exception/InvalidArgumentException.php<68> "`c<><05>A<A5>2vendor/symfony/string/Slugger/SluggerInterface.php "`c<00><>Q<>.vendor/symfony/string/Slugger/AsciiSlugger.php<68> "`c<><15><>3<DA>1vendor/symfony/deprecation-contracts/function.php= "`c=<00><><9E> <0A>?vendor/symfony/service-contracts/ServiceSubscriberInterface.php<68> "`c<><00><>d <0C>@vendor/symfony/service-contracts/Attribute/SubscribedService.php
 "`c
1I<31>T<AE>7vendor/symfony/service-contracts/Attribute/Required.php<68> "`c<>xj네3vendor/symfony/service-contracts/ResetInterface.phpy "`cy<00><>j<B7><6A>=vendor/symfony/service-contracts/ServiceProviderInterface.php<68> "`c<><00>R<FA>k<E8>;vendor/symfony/service-contracts/ServiceSubscriberTrait.phpS
"`cS
<00><>4<EB><34>8vendor/symfony/service-contracts/ServiceLocatorTrait.php<68> "`c<> "<22>5<8F><35>+vendor/symfony/process/ExecutableFinder.php<68> "`c<><00><><93>=<3D>*vendor/symfony/process/Pipes/UnixPipes.php<68> "`c<><00><>Vv<56>-vendor/symfony/process/Pipes/WindowsPipes.php, "`c, xQjä.vendor/symfony/process/Pipes/AbstractPipes.php "`c kSQ<53>/vendor/symfony/process/Pipes/PipesInterface.php<68> "`c<>fQ<66> <0C>&vendor/symfony/process/InputStream.php? "`c? V`<60><>%vendor/symfony/process/PhpProcess.php<68> "`c<><00>s<11><>.vendor/symfony/process/PhpExecutableFinder.php<68> "`c<><00>9<C4>e<CF>=vendor/symfony/process/Exception/ProcessSignaledException.php "`c<59><D7A9>3vendor/symfony/process/Exception/LogicException.php<68> "`c<> <20><><B3><E3>7vendor/symfony/process/Exception/ExceptionInterface.phpy "`cyqVXJ<58>;vendor/symfony/process/Exception/ProcessFailedException.phpx "`cx<00><>zy<7A>5vendor/symfony/process/Exception/RuntimeException.php<68> "`c<><00><1B>:<3A>=vendor/symfony/process/Exception/ProcessTimedOutException.php1 "`c1<00>'Z<15>=vendor/symfony/process/Exception/InvalidArgumentException.php<68> "`c<><00><>+_<>'vendor/symfony/process/ProcessUtils.php "`c<00>X<><58>"vendor/symfony/process/Process.phpf "`cfb)OU<4F>(vendor/symfony/console/ConsoleEvents.php<68> "`c<>>c<>A<C5>!vendor/symfony/console/Cursor.php<68> "`c<> ƫ<><C6AB><A7>&vendor/symfony/console/Application.phpq "`cq{0<>9<B3>/vendor/symfony/console/Logger/ConsoleLogger.php "`c <00> Q<>.vendor/symfony/console/Attribute/AsCommand.php<68> "`c<><00>3<BD><33><DC>9vendor/symfony/console/Input/StreamableInputInterface.php<68> "`c<><00><>B<D5><42>+vendor/symfony/console/Input/ArrayInput.php "`c <00>$<24><>*vendor/symfony/console/Input/ArgvInput.php) "`c)<00>d<C0><64><A3>.vendor/symfony/console/Input/InputArgument.php "`c<00><>& <0B>4vendor/symfony/console/Input/InputAwareInterface.php<68> "`c<><00>O<><4F>,vendor/symfony/console/Input/InputOption.php<68> "`c<> <00><>B@<40>0vendor/symfony/console/Input/InputDefinition.php# "`c# <09>!3<>,vendor/symfony/console/Input/StringInput.php? "`c?<00><>ˤ&vendor/symfony/console/Input/Input.php<68> "`c<> <08>1<04>/vendor/symfony/console/Input/InputInterface.php<68> "`c<>G<><47><B6><F4>#vendor/symfony/console/Terminal.php<68>
"`c<>
g<>2vendor/symfony/console/CI/GithubActionReporter.php<68> "`c<><00> 5<>5vendor/symfony/console/Output/TrimmedBufferOutput.php<68> "`c<>ű<> <0C>8vendor/symfony/console/Output/ConsoleOutputInterface.php "`c<00><><89><EA><F7>0vendor/symfony/console/Output/BufferedOutput.phpl "`cly:<3A><10>1vendor/symfony/console/Output/OutputInterface.php<68> "`c<><00><><F0><05>(vendor/symfony/console/Output/Output.php<68> "`c<> <00>C,<2C><>6vendor/symfony/console/Output/ConsoleSectionOutput.php<68> "`c<> O<>/vendor/symfony/console/Output/ConsoleOutput.php<68> "`c<> <00><>¤,vendor/symfony/console/Output/NullOutput.phpB "`cB%#RƤ.vendor/symfony/console/Output/StreamOutput.php<68> "`c<>1X<31><58><F2>Avendor/symfony/console/Completion/Output/BashCompletionOutput.phpg "`cg<00><><87>W<F9>Fvendor/symfony/console/Completion/Output/CompletionOutputInterface.phpF "`cF<00>2nM<6E>0vendor/symfony/console/Completion/Suggestion.php3 "`c3̆ <1D>;vendor/symfony/console/Completion/CompletionSuggestions.php<68> "`c<>A<><1F>5vendor/symfony/console/Completion/CompletionInput.php "`c<00>$<24>k<84>,vendor/symfony/console/Style/OutputStyle.phpt "`ctO-vendor/symfony/console/Style/SymfonyStyle.php-' "`c-'<00><><8D><BD><C0>/vendor/symfony/console/Style/StyleInterface.php  "`c ݡ<><17>2vendor/symfony/console/Question/ChoiceQuestion.php<68>
"`c<>
c䦤,vendor/symfony/console/Question/Question.php "`c <00><><B5>T<B5>8vendor/symfony/console/Question/ConfirmationQuestion.php "`c<00><79>9vendor/symfony/console/Formatter/OutputFormatterStyle.phpH "`cH<00><>2b<32>Bvendor/symfony/console/Formatter/OutputFormatterStyleInterface.php<68> "`c<><00>Zä=vendor/symfony/console/Formatter/NullOutputFormatterStyle.php
 "`c
<00><>%h<>=vendor/symfony/console/Formatter/OutputFormatterInterface.php<68> "`c<>Y <20>ߤ4vendor/symfony/console/Formatter/OutputFormatter.php<68> "`c<><00><>B<D8><42>8vendor/symfony/console/Formatter/NullOutputFormatter.php<68> "`c<>!& u<>Fvendor/symfony/console/Formatter/WrappableOutputFormatterInterface.php<68> "`c<><00>Z<01><>>vendor/symfony/console/Formatter/OutputFormatterStyleStack.php<68> "`c<>F<>x<97><78> vendor/symfony/console/Color.phph "`chGb痤8vendor/symfony/console/SignalRegistry/SignalRegistry.php! "`c!~.:<3A><>3vendor/symfony/console/SingleCommandApplication.php5 "`c5<17>?vendor/symfony/console/CommandLoader/CommandLoaderInterface.phpQ "`cQ<00><10>H<9E>?vendor/symfony/console/CommandLoader/ContainerCommandLoader.phpU "`cU<1B>_<CB><5F>=vendor/symfony/console/CommandLoader/FactoryCommandLoader.php<68> "`c<>d<>"z<>4vendor/symfony/console/Descriptor/TextDescriptor.php<68>" "`c<>"ȸ<>ˤ<vendor/symfony/console/Descriptor/ApplicationDescription.php<68> "`c<> =<3D>ĥ<AC>3vendor/symfony/console/Descriptor/XmlDescriptor.php "`c<00><02><>9vendor/symfony/console/Descriptor/DescriptorInterface.php<68> "`c<><00><>@<40><>0vendor/symfony/console/Descriptor/Descriptor.php} "`c}<00><>3^<5E>4vendor/symfony/console/Descriptor/JsonDescriptor.php<68> "`c<>1%菤8vendor/symfony/console/Descriptor/MarkdownDescriptor.phpQ "`cQt<><74>v<88>/vendor/symfony/console/Tester/CommandTester.php<68> "`c<>D<08><07>-vendor/symfony/console/Tester/TesterTrait.php<68> "`c<> 2'L<><4C>3vendor/symfony/console/Tester/ApplicationTester.php<68> "`c<>I<><49>9vendor/symfony/console/Tester/CommandCompletionTester.php<68> "`c<>ʛ;<3B><>@vendor/symfony/console/Tester/Constraint/CommandIsSuccessful.php<68> "`c<>ƃ<><C683>.vendor/symfony/console/Command/LazyCommand.php<68> "`c<><00>~<7E>0vendor/symfony/console/Command/LockableTrait.php "`c.<1F>V<BA>.vendor/symfony/console/Command/HelpCommand.php "`c }<7D><><97>2vendor/symfony/console/Command/CompleteCommand.php<68> "`c<><00><><A0> <0B>.vendor/symfony/console/Command/ListCommand.php1 "`c1 $D<>|<7C>8vendor/symfony/console/Command/DumpCompletionCommand.php<68> "`c<>Xw6<16>=vendor/symfony/console/Command/SignalableCommandInterface.php<68> "`c<><00>B`<60><>*vendor/symfony/console/Command/Command.phpF' "`cF'<05><><FB><B9>,vendor/symfony/console/Helper/TableStyle.php<68> "`c<><0E>֤+vendor/symfony/console/Helper/TableRows.php) "`c)W<>3z<33>7vendor/symfony/console/Helper/SymfonyQuestionHelper.php "`c <00><>d<9D><64>1vendor/symfony/console/Helper/HelperInterface.php<68> "`c<><1F><><18>+vendor/symfony/console/Helper/TableCell.phpA "`cA<00>Ӡ<D3A0>/vendor/symfony/console/Helper/ProcessHelper.phpW "`cW e<><65> <0A>0vendor/symfony/console/Helper/TableSeparator.php<68> "`c<><00><><BC><F5><CB>1vendor/symfony/console/Helper/FormatterHelper.phpi "`ci<00><><A4>w<80>0vendor/symfony/console/Helper/TableCellStyle.php<68> "`c<>э<>_<B9>-vendor/symfony/console/Helper/ProgressBar.phpg/ "`cg/w1<11><>6vendor/symfony/console/Helper/DebugFormatterHelper.php<68> "`c<><00><>6<F0><36>+vendor/symfony/console/Helper/HelperSet.phpW "`cW<00><><90><17>(vendor/symfony/console/Helper/Dumper.php "`c<00><>ܽ<D2>2vendor/symfony/console/Helper/InputAwareHelper.phpc "`cc<06><><CC><F8>0vendor/symfony/console/Helper/QuestionHelper.php<68>- "`c<>-<00>n<1E><>(vendor/symfony/console/Helper/Helper.php<68> "`c<> 4<><34>e<E1>'vendor/symfony/console/Helper/Table.php7J "`c7JL<><4C>ˤ3vendor/symfony/console/Helper/ProgressIndicator.php<68> "`c<><00>=<3D>֤2vendor/symfony/console/Helper/DescriptorHelper.php<68> "`c<><00><67>Dvendor/symfony/console/DependencyInjection/AddConsoleCommandPass.phpf "`cf#<23> $<24>3vendor/symfony/console/Exception/LogicException.php<68> "`c<><00>O\e<>;vendor/symfony/console/Exception/InvalidOptionException.php<68> "`c<><13><>H<D7>7vendor/symfony/console/Exception/ExceptionInterface.phpy "`cy<00>9[&<26>5vendor/symfony/console/Exception/RuntimeException.php<68> "`c<><00><>,6<>?vendor/symfony/console/Exception/NamespaceNotFoundException.php<68> "`c<><00><>n<F3><6E>=vendor/symfony/console/Exception/InvalidArgumentException.php<68> "`c<><00>̽Z<CCBD>:vendor/symfony/console/Exception/MissingInputException.php<68> "`c<><00>S
<FE><A4>=vendor/symfony/console/Exception/CommandNotFoundException.php<68> "`c<><00>w<88><77><EA>6vendor/symfony/console/Event/ConsoleTerminateEvent.php~ "`c~hr<68>֤2vendor/symfony/console/Event/ConsoleErrorEvent.php<68> "`c<><00><><C0><A9><96>4vendor/symfony/console/Event/ConsoleCommandEvent.php<68> "`c<> <20>O~<7E>-vendor/symfony/console/Event/ConsoleEvent.php<68> "`c<><00>o<C5>٤3vendor/symfony/console/Event/ConsoleSignalEvent.phpG "`cG<00><> <0B>6vendor/symfony/console/EventListener/ErrorListener.php& "`c&X<>0vendor/symfony/polyfill-mbstring/bootstrap80.php<68> "`c<> D<> פ@vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php<68>T "`c<>T<03><>+<2B>Fvendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php<68> "`c<><00>y_<79><5F>@vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php8[ "`c8[+R<>*<2A>-vendor/symfony/polyfill-mbstring/Mbstring.phptJ "`ctJ<00>ڟV<DA9F>.vendor/symfony/polyfill-mbstring/bootstrap.php<68> "`c<>NZ^<1B>5vendor/symfony/polyfill-intl-grapheme/bootstrap80.phpY "`cY <00>!<21><>2vendor/symfony/polyfill-intl-grapheme/Grapheme.php<68> "`c<>̻9<CCBB><39>3vendor/symfony/polyfill-intl-grapheme/bootstrap.php<68> "`c<><00>9<FD>|<7C>"vendor/sebastian/diff/src/Diff.php+ "`c+<00>tg/<2F>$vendor/sebastian/diff/src/Parser.php<68> "`c<>13[<1F>Mvendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.phpa "`ca<00>;Eʤ:vendor/sebastian/diff/src/Output/DiffOnlyOutputBuilder.php<68> "`c<><>&<26><>?vendor/sebastian/diff/src/Output/DiffOutputBuilderInterface.php<68> "`c<>2G\+<2B>=vendor/sebastian/diff/src/Output/UnifiedDiffOutputBuilder.php<68> "`c<>b<>\,<2C>Cvendor/sebastian/diff/src/Output/StrictUnifiedDiffOutputBuilder.php_ "`c_1<><31><1B>?vendor/sebastian/diff/src/Output/AbstractChunkOutputBuilder.phpD "`cDtH*<2A><>@vendor/sebastian/diff/src/LongestCommonSubsequenceCalculator.php<68> "`c<><00>/<2F><0F>#vendor/sebastian/diff/src/Chunk.php<68> "`c<>Ejt<>$vendor/sebastian/diff/src/Differ.php= "`c=]<5D>N<AC>"vendor/sebastian/diff/src/Line.php<68> "`c<><00>E_M<5F>>vendor/sebastian/diff/src/Exception/ConfigurationException.phpI "`cI<00><><F5><F8><B3>@vendor/sebastian/diff/src/Exception/InvalidArgumentException.php<68> "`c<>=<3D>|<7C><>1vendor/sebastian/diff/src/Exception/Exception.php<68> "`c<>V<><56>j<89>Ovendor/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php<68> "`c<><00>C<C2>z<EB>src/Finder.php "`c<00><><B5>t<98>3src/Fixer/ClassNotation/ProtectedToPrivateFixer.php "`c <00>ƄѤ0src/Fixer/ClassNotation/ClassDefinitionFixer.php/ "`c/<00><><1A><>?src/Fixer/ClassNotation/SingleClassElementPerStatementFixer.php<68> "`c<><00>S<97><14>5src/Fixer/ClassNotation/OrderedClassElementsFixer.phps, "`cs,6<>k<>2src/Fixer/ClassNotation/OrderedInterfacesFixer.phpZ "`cZ<00>ȫ/<2F>2src/Fixer/ClassNotation/NoPhp4ConstructorFixer.php<68> "`c<><00>g<D2><67><D5>3src/Fixer/ClassNotation/FinalInternalClassFixer.php<68> "`c<><00><>T<FC><54>=src/Fixer/ClassNotation/NoNullPropertyInitializationFixer.php<68> "`c<> <0E>٤+src/Fixer/ClassNotation/FinalClassFixer.php<68> "`c<>]<5D>l<D4><6C>>src/Fixer/ClassNotation/NoBlankLinesAfterClassOpeningFixer.php$ "`c$W<>Ț<E4>6src/Fixer/ClassNotation/NoUnneededFinalMethodFixer.php3 "`c3M 8t<38>:src/Fixer/ClassNotation/ClassAttributesSeparationFixer.php<68>5 "`c<>5z<><16><>.src/Fixer/ClassNotation/OrderedTraitsFixer.php "`c <04><><B6><C1>-src/Fixer/ClassNotation/SelfAccessorFixer.phpp "`cpB<><42><12>Bsrc/Fixer/ClassNotation/FinalPublicMethodForAbstractClassFixer.phpW "`cW [6<ޤ3src/Fixer/ClassNotation/SelfStaticAccessorFixer.php<68> "`c<> eQܻ<51>3src/Fixer/ClassNotation/VisibilityRequiredFixer.php<68> "`c<>HX<11>>src/Fixer/ClassNotation/SingleTraitInsertPerStatementFixer.phpt "`ct<00><><98><8D><E8>9src/Fixer/ControlStructure/NoUnneededCurlyBracesFixer.phpy "`cy <00>D3<44><33>7src/Fixer/ControlStructure/NoAlternativeSyntaxFixer.php<68> "`c<>c<>>o<>-src/Fixer/ControlStructure/YodaStyleFixer.php<68>7 "`c<>7<07><><1A>6src/Fixer/ControlStructure/EmptyLoopConditionFixer.php  "`c <00><><E3><E6><A4>>src/Fixer/ControlStructure/SwitchCaseSemicolonToColonFixer.php  "`c ,<2C>:F<>Hsrc/Fixer/ControlStructure/ControlStructureContinuationPositionFixer.php "`c #9S<>7src/Fixer/ControlStructure/NoSuperfluousElseifFixer.phpL "`cL<00>OJ<4F><4A>3src/Fixer/ControlStructure/SwitchCaseSpaceFixer.php, "`c,M<><4D>\<5C>1src/Fixer/ControlStructure/EmptyLoopBodyFixer.php<68> "`c<> <00><><95><BB><A6>9src/Fixer/ControlStructure/SwitchContinueToBreakFixer.php<68> "`c<>XTJѤ6src/Fixer/ControlStructure/SimplifiedIfReturnFixer.php<68>
"`c<>
U<>$<>:src/Fixer/ControlStructure/ControlStructureBracesFixer.php<68> "`c<><00><><D7>Ԥ1src/Fixer/ControlStructure/NoUselessElseFixer.phpi "`ci<12>u=<3D>*src/Fixer/ControlStructure/ElseifFixer.php<68> "`c<>5<>.m<><src/Fixer/ControlStructure/TrailingCommaInMultilineFixer.php "`c 2-~<7E>+src/Fixer/ControlStructure/IncludeFixer.phpI "`cI<00>٭<9D><D9AD>=src/Fixer/ControlStructure/NoTrailingCommaInListCallFixer.php<68> "`c<>{<7B><>i<D4>@src/Fixer/ControlStructure/NoUnneededControlParenthesesFixer.php;I "`c;I<00>4r<34><72>2src/Fixer/ControlStructure/NoBreakCommentFixer.phpc" "`cc"<00>o<8E><16>7src/Fixer/FunctionNotation/RegularCallableCallFixer.php<68> "`c<>]<5D><>-<2D>7src/Fixer/FunctionNotation/LambdaNotUsedImportFixer.php<68> "`c<><00><06>ޤ.src/Fixer/FunctionNotation/VoidReturnFixer.php "`c<00>:<16>Ksrc/Fixer/FunctionNotation/NoTrailingCommaInSinglelineFunctionCallFixer.phpN "`cNT<>
<A4>8src/Fixer/FunctionNotation/PhpdocToPropertyTypeFixer.php<68> "`c<>z<><7A>0src/Fixer/FunctionNotation/StaticLambdaFixer.php "`c <00>m<A3><6D><D0>9src/Fixer/FunctionNotation/ReturnTypeDeclarationFixer.php<68> "`c<> ܲj<01>9src/Fixer/FunctionNotation/FunctionTypehintSpaceFixer.php<68> "`c<><00><>/<2F>7src/Fixer/FunctionNotation/MethodArgumentSpaceFixer.php<68>+ "`c<>+"$<24>o<FF>@src/Fixer/FunctionNotation/DateTimeCreateFromFormatCallFixer.php<68> "`c<><00>R<E7>0<D5>4src/Fixer/FunctionNotation/NoUselessSprintfFixer.php<68> "`c<> a<18>٤2src/Fixer/FunctionNotation/FopenFlagOrderFixer.php<68> "`c<>H<>ʴ<D4>8src/Fixer/FunctionNotation/CombineNestedDirnameFixer.phpU "`cU'zj<7A><6A>=src/Fixer/FunctionNotation/NoSpacesAfterFunctionNameFixer.php<68> "`c<> =<3D>E<9C><45>6src/Fixer/FunctionNotation/PhpdocToReturnTypeFixer.php<68> "`c<><00><><AC><13><src/Fixer/FunctionNotation/NativeFunctionInvocationFixer.phpi "`ci <00>?KT<4B>.src/Fixer/FunctionNotation/FopenFlagsFixer.php<68> "`c<> <00>P:<3A>Nsrc/Fixer/FunctionNotation/NullableTypeDeclarationForDefaultNullValueFixer.phpQ "`cQd<><64><C1><BE>Esrc/Fixer/FunctionNotation/NoUnreachableDefaultArgumentValueFixer.php "`c<1D>
ʤ5src/Fixer/FunctionNotation/PhpdocToParamTypeFixer.php<68> "`c<><00><><D7><D7>/src/Fixer/FunctionNotation/ImplodeCallFixer.php<68> "`c<> <00>U$<00>5src/Fixer/FunctionNotation/UseArrowFunctionsFixer.php  "`c -?<3F>N<AD>3src/Fixer/FunctionNotation/SingleLineThrowFixer.php<68> "`c<> W<><57>U<C5>7src/Fixer/FunctionNotation/FunctionDeclarationFixer.php "`cܬ<><DCAC>0src/Fixer/Phpdoc/GeneralPhpdocTagRenameFixer.php<68> "`c<><00><>j<>3src/Fixer/Phpdoc/PhpdocReturnSelfReferenceFixer.php "`cmJ<6D><1F>9src/Fixer/Phpdoc/PhpdocAddMissingParamAnnotationFixer.php<68> "`c<><00><><E3>(<28>Bsrc/Fixer/Phpdoc/PhpdocTrimConsecutiveBlankLineSeparationFixer.php<68> "`c<><00>.<2E>$<24>'src/Fixer/Phpdoc/PhpdocTagTypeFixer.php<68> "`c<><00><> <0B><>'src/Fixer/Phpdoc/PhpdocSummaryFixer.php<68> "`c<><00>e<93><<3C>)src/Fixer/Phpdoc/PhpdocNoPackageFixer.phpc "`cc<00> <0C><><89>'src/Fixer/Phpdoc/NoEmptyPhpdocFixer.php<68> "`c<><00><><D2>><3E>(src/Fixer/Phpdoc/PhpdocNoAccessFixer.phpS "`cS3<>&src/Fixer/Phpdoc/PhpdocScalarFixer.php<68> "`c<><00>?<3F>-<2D>7src/Fixer/Phpdoc/GeneralPhpdocAnnotationRemoveFixer.phpo "`co <00>ca<63><61>,src/Fixer/Phpdoc/PhpdocOrderByValueFixer.php} "`c}<1C><>`<60>4src/Fixer/Phpdoc/PhpdocAnnotationWithoutDotFixer.php
"`c
8<>t<>(src/Fixer/Phpdoc/PhpdocLineSpanFixer.php<68> "`c<><00>?<06>)src/Fixer/Phpdoc/PhpdocTagCasingFixer.phpY "`cY<00><>՚<FA>/src/Fixer/Phpdoc/AlignMultilineCommentFixer.php "`c(yQܤ*src/Fixer/Phpdoc/PhpdocNoAliasTagFixer.php# "`c# <00>.]<5D><>*src/Fixer/Phpdoc/PhpdocTypesOrderFixer.php "`cP|<7C>l<E4>$src/Fixer/Phpdoc/PhpdocTrimFixer.php "`c5R4<52><34>9src/Fixer/Phpdoc/PhpdocVarAnnotationCorrectOrderFixer.phpV "`cV<00><<3C><1E>4src/Fixer/Phpdoc/PhpdocSingleLineVarSpacingFixer.php<68> "`c<> N<><4E><F3>1src/Fixer/Phpdoc/NoBlankLinesAfterPhpdocFixer.php* "`c*<00><>%src/Fixer/Phpdoc/PhpdocTypesFixer.php<68> "`c<> <00><>~<07>-src/Fixer/Phpdoc/PhpdocNoEmptyReturnFixer.php "`cɪ n<>*src/Fixer/Phpdoc/PhpdocSeparationFixer.phpL "`cLӅ,<2C><>%src/Fixer/Phpdoc/PhpdocOrderFixer.phpJ "`cJC<>w<12>3src/Fixer/Phpdoc/PhpdocNoUselessInheritdocFixer.php<68> "`c<> ov£<76>.src/Fixer/Phpdoc/PhpdocVarWithoutNameFixer.phpQ
"`cQ
0>Q<><51>%src/Fixer/Phpdoc/PhpdocAlignFixer.php<68>! "`c<>!y<>K<05>)src/Fixer/Phpdoc/PhpdocToCommentFixer.phpE "`cE F{<7B>j<C6>1src/Fixer/Phpdoc/NoSuperfluousPhpdocTagsFixer.phpR8 "`cR8<00>ONW<4E>&src/Fixer/Phpdoc/PhpdocIndentFixer.php<68> "`c<><00><>+<2B><>3src/Fixer/Phpdoc/PhpdocInlineTagNormalizerFixer.php<68> "`c<><04>V<>/src/Fixer/ClassUsage/DateTimeImmutableFixer.php<68> "`c<> S<><53><13>src/Fixer/FixerInterface.php; "`c;8<>=src/Fixer/LanguageConstruct/CombineConsecutiveIssetsFixer.php- "`c- !qy<71>=src/Fixer/LanguageConstruct/ExplicitIndirectVariableFixer.php_ "`c_4 <04>5src/Fixer/LanguageConstruct/ErrorSuppressionFixer.php "`c6g<36>ʤ:src/Fixer/LanguageConstruct/DeclareEqualNormalizeFixer.php "`c <00><><A3><A3><83>7src/Fixer/LanguageConstruct/DeclareParenthesesFixer.php<68> "`c<>y<><79><B9><92>7src/Fixer/LanguageConstruct/FunctionToConstantFixer.php<68> "`c<>Bb(ɤ>src/Fixer/LanguageConstruct/SingleSpaceAfterConstructFixer.phpd "`cdv<>J2<4A>;src/Fixer/LanguageConstruct/GetClassToClassKeywordFixer.php<68> "`c<> <00><>]'<27>+src/Fixer/LanguageConstruct/IsNullFixer.php* "`c*<18>z<C5>=src/Fixer/LanguageConstruct/CombineConsecutiveUnsetsFixer.php+ "`c+<00><>0src/Fixer/LanguageConstruct/DirConstantFixer.php<68> "`c<> <00>"r<>7src/Fixer/LanguageConstruct/ClassKeywordRemoveFixer.php<68> "`c<><00><>d<82><64>6src/Fixer/LanguageConstruct/NoUnsetOnPropertyFixer.php<68> "`c<><01><><E0><86>,src/Fixer/Alias/BacktickToShellExecFixer.php<68> "`c<> i<><69>"src/Fixer/Alias/ArrayPushFixer.php9 "`c9K<>y<E2><79>&src/Fixer/Alias/SetTypeToCastFixer.phpd "`cd<00>5<D7>6<A2>+src/Fixer/Alias/RandomApiMigrationFixer.php<68> "`c<>]?<3F><>'src/Fixer/Alias/MbStrFunctionsFixer.phpW "`cW <10>ux<75>)src/Fixer/Alias/NoMixedEchoPrintFixer.php] "`c] m<><6D><07>)src/Fixer/Alias/NoAliasFunctionsFixer.php<68> "`c<><00><>X<A7><58>5src/Fixer/Alias/NoAliasLanguageConstructCallFixer.php<68> "`c<><00>I1e<31>,src/Fixer/Alias/PowToExponentiationFixer.php4 "`c4<00>V1<1D>(src/Fixer/Alias/ModernizeStrposFixer.php  "`c S<>]<5D>#src/Fixer/Alias/EregToPregFixer.php/ "`c/ <00>r<F9><72><FE>src/Fixer/Indentation.phpm "`cm<18><src/Fixer/NamespaceNotation/BlankLineAfterNamespaceFixer.php<68> "`c<> <00>RB<>Asrc/Fixer/NamespaceNotation/NoLeadingNamespaceWhitespaceFixer.php<68> "`c<><00>{<<3C>Csrc/Fixer/NamespaceNotation/SingleBlankLineBeforeNamespaceFixer.phpS "`cS=<3D>+<2B>@src/Fixer/NamespaceNotation/NoBlankLinesBeforeNamespaceFixer.php> "`c><00>%6<><36>3src/Fixer/NamespaceNotation/CleanNamespaceFixer.php<68> "`c<>x<>H <0B>+src/Fixer/CastNotation/NoUnsetCastFixer.php[ "`c[<00><><EA>$<24>*src/Fixer/CastNotation/CastSpacesFixer.php "`c <00><1F>G<87>/src/Fixer/CastNotation/NoShortBoolCastFixer.php: "`c:><3E>9c<39>/src/Fixer/CastNotation/ShortScalarCastFixer.php "`c<00>w<B5>}<7D>5src/Fixer/CastNotation/ModernizeTypesCastingFixer.php<68> "`c<> <00>ȼ#<23>-src/Fixer/CastNotation/LowercaseCastFixer.php<68> "`c<><00>1<><31>&src/Fixer/Casing/ConstantCaseFixer.phpG "`cG i <0C>g<A3>2src/Fixer/Casing/ClassReferenceNameCasingFixer.php<68> "`c<><00>2src/Fixer/Casing/LowercaseStaticReferenceFixer.php<68> "`c<><00><>{<7B><>,src/Fixer/Casing/IntegerLiteralCaseFixer.php<68> "`c<><00>Ê<>+src/Fixer/Casing/LowercaseKeywordsFixer.php<68> "`c<><00>'Eפ=src/Fixer/Casing/NativeFunctionTypeDeclarationCasingFixer.php<68> "`c<> <00>H<A0>u<B8>-src/Fixer/Casing/MagicConstantCasingFixer.php<68> "`c<>Z<>#`<60>+src/Fixer/Casing/MagicMethodCasingFixer.phpA "`cA
u<><75>.src/Fixer/Casing/NativeFunctionCasingFixer.phpp "`cp<00>m<BC><6D><F0>)src/Fixer/Comment/NoEmptyCommentFixer.php<68>
"`c<>
.<2E><><1B>8src/Fixer/Comment/NoTrailingWhitespaceInCommentFixer.php<68> "`c<><00>G <0A><>(src/Fixer/Comment/HeaderCommentFixer.phpG( "`cG(ڥ<>*src/Fixer/Comment/CommentToPhpdocFixer.php<68> "`c<><00><>e#<23>1src/Fixer/Comment/SingleLineCommentStyleFixer.php "`c <00>sXi<58>3src/Fixer/Comment/SingleLineCommentSpacingFixer.php<68> "`c<><1F>y<CC><79>9src/Fixer/Comment/MultilineCommentOpeningClosingFixer.php<68> "`c<><00><><03>2src/Fixer/PhpTag/LinebreakAfterOpeningTagFixer.php; "`c;<00>2<><32>2src/Fixer/PhpTag/BlankLineAfterOpeningTagFixer.php<68> "`c<><00><>aE<61>(src/Fixer/PhpTag/FullOpeningTagFixer.php6 "`c6 <00>
<0B><>&src/Fixer/PhpTag/NoClosingTagFixer.php "`c<00>j<92>դ'src/Fixer/PhpTag/EchoTagSyntaxFixer.php  "`c <16><06><>6src/Fixer/ReturnNotation/SimplifiedNullReturnFixer.php<68> "`c<> Ɲ<0E><>2src/Fixer/ReturnNotation/ReturnAssignmentFixer.php<68>% "`c<>%38<16><>1src/Fixer/ReturnNotation/NoUselessReturnFixer.php<68> "`c<><00><><FC>{<7B>,src/Fixer/AbstractIncrementOperatorFixer.php<68> "`c<><00><>a-<2D>&src/Fixer/DeprecatedFixerInterface.php<68> "`c<>A<><41><B5>4src/Fixer/Basic/NoTrailingCommaInSinglelineFixer.php: "`c:<00><>j<>!src/Fixer/Basic/EncodingFixer.php<68> "`c<>4<><34>%<25>.src/Fixer/Basic/NonPrintableCharacterFixer.php= "`c=<00>ep<06>,src/Fixer/Basic/CurlyBracesPositionFixer.php<68>- "`c<>-<00>MMR<4D>src/Fixer/Basic/BracesFixer.phpN "`cN<00>C<9D>z<D5>'src/Fixer/Basic/PsrAutoloadingFixer.phpX "`cX<00>l<DE>X<FC>&src/Fixer/Basic/OctalNotationFixer.php<68> "`c<><00><><BA>d<CB>4src/Fixer/Basic/NoMultipleStatementsPerLineFixer.phpb "`cb<00><>
$<24>,src/Fixer/WhitespacesAwareFixerInterface.php "`c <0C><>ڤ>src/Fixer/StringNotation/NoTrailingWhitespaceInStringFixer.phpf "`cf,o><00>?src/Fixer/StringNotation/SimpleToComplexStringVariableFixer.php+ "`c+<00><><FF><F5><A8>0src/Fixer/StringNotation/NoBinaryStringFixer.php<68> "`c<>~<7E>8n<38>8src/Fixer/StringNotation/ExplicitStringVariableFixer.phpi "`ci<00><>1src/Fixer/StringNotation/HeredocToNowdocFixer.php<68> "`c<><00> <0C><><97>5src/Fixer/StringNotation/StringLengthToEmptyFixer.phph "`ch hsP<73>-src/Fixer/StringNotation/SingleQuoteFixer.php<68> "`c<>@~P)<29>;src/Fixer/StringNotation/EscapeImplicitBackslashesFixer.php<68> "`c<>eG3Ф2src/Fixer/StringNotation/StringLineEndingFixer.php<68> "`c<><00><><1D><>Csrc/Fixer/DoctrineAnnotation/DoctrineAnnotationIndentationFixer.php<68> "`c<>%<25>Gsrc/Fixer/DoctrineAnnotation/DoctrineAnnotationArrayAssignmentFixer.php<68> "`c<>ҝ&B<>>src/Fixer/DoctrineAnnotation/DoctrineAnnotationSpacesFixer.php<68> "`c<>x<> G<>>src/Fixer/DoctrineAnnotation/DoctrineAnnotationBracesFixer.phpA "`cA <00><>`<60><>*src/Fixer/PhpUnit/PhpUnitTargetVersion.php` "`c`k<>
<A4>(src/Fixer/PhpUnit/PhpUnitStrictFixer.php<68> "`c<> <00>{s<><73>0src/Fixer/PhpUnit/PhpUnitDedicateAssertFixer.phpF8 "`cF8<00><1C><><A0>+src/Fixer/PhpUnit/PhpUnitConstructFixer.phpH "`cH<00><>i<FA><69>9src/Fixer/PhpUnit/PhpUnitSetUpTearDownVisibilityFixer.php<68> "`c<>xC<18><>0src/Fixer/PhpUnit/PhpUnitTestAnnotationFixer.php& "`c&ڬ<><10>+src/Fixer/PhpUnit/PhpUnitSizeClassFixer.php* "`c*M^<5E><><E8>9src/Fixer/PhpUnit/PhpUnitNoExpectationAnnotationFixer.phpe "`cel,<2C>\<5C><src/Fixer/PhpUnit/PhpUnitDedicateAssertInternalTypeFixer.php<68> "`c<>Y<><08>&src/Fixer/PhpUnit/PhpUnitMockFixer.php "`c<00><><E5>K<CE>/src/Fixer/PhpUnit/PhpUnitInternalClassFixer.phpv "`cv8<><38><0E>-src/Fixer/PhpUnit/PhpUnitExpectationFixer.php<68> "`c<>nq<6E><71><B1>0src/Fixer/PhpUnit/PhpUnitFqcnAnnotationFixer.php<68> "`c<>[<5B>Y$<24>,src/Fixer/PhpUnit/PhpUnitNamespacedFixer.php<68> "`c<>2<>B<16>9src/Fixer/PhpUnit/PhpUnitTestClassRequiresCoversFixer.php<68>
"`c<>
<00><>$<24><>;src/Fixer/PhpUnit/PhpUnitTestCaseStaticMethodCallsFixer.php%2 "`c%2Q.UB<55>5src/Fixer/PhpUnit/PhpUnitMockShortWillReturnFixer.php "`c<x?¤.src/Fixer/PhpUnit/PhpUnitMethodCasingFixer.php<68> "`c<><00>v M<>(src/Fixer/ConfigurableFixerInterface.php<68> "`c<>`<60><>4<BB>5src/Fixer/Whitespace/CompactNullableTypehintFixer.php<68> "`c<>3;<3B>T<CC>(src/Fixer/Whitespace/LineEndingFixer.php<68> "`c<><00>R<87><52><C2>2src/Fixer/Whitespace/SingleBlankLineAtEofFixer.phpq "`cq<00>w'<05>-src/Fixer/Whitespace/IndentationTypeFixer.php<68>
"`c<>
oO<6F><4F><EC>.src/Fixer/Whitespace/ArrayIndentationFixer.php<68> "`c<>9W <0C>2src/Fixer/Whitespace/NoSpacesAroundOffsetFixer.php<68> "`c<> <00>q^<5E><>5src/Fixer/Whitespace/NoWhitespaceInBlankLineFixer.php<68> "`c<>62<>6src/Fixer/Whitespace/BlankLineBeforeStatementFixer.php0 "`c0z<><E2>0src/Fixer/Whitespace/HeredocIndentationFixer.php<68> "`c<>A<>VH<56>)src/Fixer/Whitespace/TypesSpacesFixer.phpo "`co<00>5_<35><5F>:src/Fixer/Whitespace/BlankLineBetweenImportGroupsFixer.php<68> "`c<> <00>P;<3B><>7src/Fixer/Whitespace/NoSpacesInsideParenthesisFixer.php<68> "`c<>դ<>6<DB>2src/Fixer/Whitespace/NoTrailingWhitespaceFixer.php<68> "`c<><00><11>$<24>7src/Fixer/Whitespace/MethodChainingIndentationFixer.phpg "`cg*<2A>3<AF><33>/src/Fixer/Whitespace/NoExtraBlankLinesFixer.php<68>! "`c<>!y"ɤ2src/Fixer/Whitespace/StatementIndentationFixer.php<68>6 "`c<>6Dy9@<40>*src/Fixer/Naming/NoHomoglyphNamesFixer.php. "`c. <00>˺<>,src/Fixer/Strict/DeclareStrictTypesFixer.phpn
"`cn
T<>P<D0><50>*src/Fixer/Strict/StrictComparisonFixer.php<68> "`c<>`1<>ʤ%src/Fixer/Strict/StrictParamFixer.php<68> "`c<>t<10><>Csrc/Fixer/Semicolon/NoSinglelineWhitespaceBeforeSemicolonsFixer.php^ "`c^<00>A<B2><41><FA>-src/Fixer/Semicolon/NoEmptyStatementFixer.php<68> "`c<>y<>¡<DE>0src/Fixer/Semicolon/SpaceAfterSemicolonFixer.php<68> "`c<> <00>7<90>]<5D>@src/Fixer/Semicolon/MultilineWhitespaceBeforeSemicolonsFixer.php, "`c,!<12>A<AF>6src/Fixer/Semicolon/SemicolonAfterInstructionFixer.phpa "`cah<><68>s<C3>"src/Fixer/AbstractPhpUnitFixer.php| "`c|ir<69>1<D9>3src/Fixer/Import/FullyQualifiedStrictTypesFixer.php<68> "`c<><00>OUZ<55>)src/Fixer/Import/NoUnusedImportsFixer.phpo "`cor<>/src/Fixer/Import/GlobalNamespaceImportFixer.phpF? "`cF?<00> (<28>(src/Fixer/Import/OrderedImportsFixer.php<) "`c<)<1F><> <20>/src/Fixer/Import/NoUnneededImportAliasFixer.php/ "`c/[<5B>ܤ2src/Fixer/Import/SingleImportPerStatementFixer.php<68> "`c<>*D<>ؤ.src/Fixer/Import/NoLeadingImportSlashFixer.php "`ct<>vR<76>%src/Fixer/Import/GroupImportFixer.php<68> "`c<>҂<>*<2A>0src/Fixer/Import/SingleLineAfterImportsFixer.php<68> "`c<> <00>,src/Fixer/ArrayNotation/ArraySyntaxFixer.php<68> "`c<> \<03><1F>0src/Fixer/ArrayNotation/TrimArraySpacesFixer.php "`c C<><43>M<F1>?src/Fixer/ArrayNotation/NoWhitespaceBeforeCommaInArrayFixer.php<68> "`c<><00><>L<>Gsrc/Fixer/ArrayNotation/NoMultilineWhitespaceAroundDoubleArrowFixer.php<68> "`c<>.$<24>e<9E>Asrc/Fixer/ArrayNotation/NoTrailingCommaInSinglelineArrayFixer.php<68> "`c<><00>tj<><src/Fixer/ArrayNotation/WhitespaceAfterCommaInArrayFixer.php. "`c.p<>D<14>4src/Fixer/ArrayNotation/NormalizeIndexBraceFixer.phpA "`cA<00><>#u<>*src/Fixer/ListNotation/ListSyntaxFixer.php<68> "`c<> -J<>̤'src/Fixer/Operator/ConcatSpaceFixer.php<68> "`c<> <00>*C<><43>9src/Fixer/Operator/NotOperatorWithSuccessorSpaceFixer.php<68> "`c<><00>B<><42>0src/Fixer/Operator/BinaryOperatorSpacesFixer.phpG "`cGL<18>D<DB>1src/Fixer/Operator/TernaryOperatorSpacesFixer.php` "`c` <00>ؒ<F5><D892>3src/Fixer/Operator/TernaryToNullCoalescingFixer.phpk "`ck<00><>=\<5C>/src/Fixer/Operator/UnaryOperatorSpacesFixer.php
 "`c
<00>ȽϤ2src/Fixer/Operator/TernaryToElvisOperatorFixer.php<68> "`c<>mC<6D><43><A3>*src/Fixer/Operator/IncrementStyleFixer.php<68> "`c<><00>%<25><><A1>-src/Fixer/Operator/OperatorLinebreakFixer.php<68> "`c<>!<21><><8F><94>,src/Fixer/Operator/LogicalOperatorsFixer.php<68> "`c<>8Xš<58>)src/Fixer/Operator/NewWithBracesFixer.php "`c<19><> <0A>?src/Fixer/Operator/AssignNullCoalescingToCoalesceEqualFixer.phpI "`cI#TQ<54>;src/Fixer/Operator/ObjectOperatorWithoutWhitespaceFixer.php<68> "`c<>%|
<1F>5src/Fixer/Operator/NoUselessNullsafeOperatorFixer.php<68> "`c<><00>3src/Fixer/Operator/NoUselessConcatOperatorFixer.php<68> "`c<> )E7<45>0src/Fixer/Operator/StandardizeIncrementFixer.php<68> "`c<><00>s<A5>Ť0src/Fixer/Operator/StandardizeNotEqualsFixer.php<68> "`c<>*<2A>0src/Fixer/Operator/NotOperatorWithSpaceFixer.php<68> "`c<><00>K2<4B>4src/Fixer/Operator/NoSpaceAroundDoubleColonFixer.php "`c<00><>-"<22><src/Fixer/ConstantNotation/NativeConstantInvocationFixer.php* "`c*<00>6c<36><63>Bsrc/ConfigurationException/RequiredFixerConfigurationException.php<68> "`c<><00>[^<5E><>Gsrc/ConfigurationException/InvalidForEnvFixerConfigurationException.php<68> "`c<><00>Ț<D8><C89A><src/ConfigurationException/InvalidConfigurationException.php<68> "`c<>V<>wT<77>Asrc/ConfigurationException/InvalidFixerConfigurationException.phpm "`cm<00><>0<04>*src/Documentation/DocumentationLocator.phpr "`cr<1F>3src/Documentation/RuleSetDocumentationGenerator.php<68> "`c<><00>M<BB><07>+src/Documentation/ListDocumentGenerator.php= "`c=7jp/<2F>src/Documentation/RstUtils.php% "`c%<00><><FB>d<D6>,src/Documentation/FixerDocumentGenerator.php-" "`c-"Ak<41>m<D8>src/RuleSet/RuleSets.php<68> "`c<><00>b6<62> src/RuleSet/RuleSetInterface.php= "`c= <20><>f<CC>+src/RuleSet/RuleSetDescriptionInterface.php "`cf<>I<18>src/RuleSet/RuleSet.phpw "`cw l<>/src/RuleSet/Sets/PHPUnit32MigrationRiskySet.php<68> "`c<><00><><F1>4<D9>&src/RuleSet/Sets/PHP80MigrationSet.phpV "`cV<00>ʲԤ$src/RuleSet/Sets/SymfonyRiskySet.php= "`c=}t<><74>"src/RuleSet/Sets/PhpCsFixerSet.php<68> "`c<>|<7C><><A2>src/RuleSet/Sets/PSR2Set.php "`c<00><>0<03>+src/RuleSet/Sets/PHP71MigrationRiskySet.phpD "`cDh<><1D>/src/RuleSet/Sets/PHPUnit56MigrationRiskySet.php "`c<00><>:<3A><>&src/RuleSet/Sets/PHP54MigrationSet.php "`cSmڞ<6D>&src/RuleSet/Sets/PHP70MigrationSet.phpH "`cH<00><><85><14>+src/RuleSet/Sets/PHP74MigrationRiskySet.php<68> "`c<>Cɪ<43><C9AA>src/RuleSet/Sets/SymfonySet.php "`c<00>\<5C><>src/RuleSet/Sets/PSR12Set.php~ "`c~s<>g<F0><67>&src/RuleSet/Sets/PHP71MigrationSet.phpX "`cX<1C>B<FD><42>/src/RuleSet/Sets/PHPUnit84MigrationRiskySet.php<68> "`c<>ґ<>\<5C>&src/RuleSet/Sets/PHP81MigrationSet.php< "`c<<00><><BF>,<2C>/src/RuleSet/Sets/PHPUnit60MigrationRiskySet.php<68> "`c<>@q<><<3C>/src/RuleSet/Sets/PHPUnit57MigrationRiskySet.php<68> "`c<>F<><46><9D><A7>+src/RuleSet/Sets/PHP70MigrationRiskySet.php<68> "`c<><00><>(L<> src/RuleSet/Sets/PERRiskySet.php<68> "`c<> ɐ<><C990>/src/RuleSet/Sets/PHPUnit52MigrationRiskySet.php<68> "`c<><00>X3ä*src/RuleSet/Sets/DoctrineAnnotationSet.phpj "`cj<00><>S<C8><53>'src/RuleSet/Sets/PhpCsFixerRiskySet.php<68> "`c<>g<><67>&src/RuleSet/Sets/PHP73MigrationSet.php<68> "`c<>O9<13><>+src/RuleSet/Sets/PHP80MigrationRiskySet.php "`c<00>ù<9B><C3B9>/src/RuleSet/Sets/PHPUnit54MigrationRiskySet.php<68> "`c<>$[v<>/src/RuleSet/Sets/PHPUnit35MigrationRiskySet.php<68> "`c<>p<07><><F1>+src/RuleSet/Sets/PHP56MigrationRiskySet.php- "`c-<00><><C3><FE><B7>/src/RuleSet/Sets/PHPUnit30MigrationRiskySet.php<68> "`c<>ѽn<>/src/RuleSet/Sets/PHPUnit43MigrationRiskySet.php<68> "`c<><00>&<26><><81>&src/RuleSet/Sets/PHP74MigrationSet.php<68> "`c<><00>q<9D>ڤsrc/RuleSet/Sets/PSR1Set.php<68> "`c<>o<><6F><1A>"src/RuleSet/Sets/PSR12RiskySet.php<68> "`c<>f<16>&src/RuleSet/Sets/PHP82MigrationSet.phpO "`cO`<60>&<26>/src/RuleSet/Sets/PHPUnit50MigrationRiskySet.php<68> "`c<><00><10>d<A5>src/RuleSet/Sets/PERSet.php<68> "`c<><00>0F<30><46>/src/RuleSet/Sets/PHPUnit55MigrationRiskySet.php<68> "`c<><00> <0B>><3E>/src/RuleSet/Sets/PHPUnit75MigrationRiskySet.php<68> "`c<><00><>/<2F><>/src/RuleSet/Sets/PHPUnit48MigrationRiskySet.php<68> "`c<>1
l<BD><A4>*src/RuleSet/AbstractRuleSetDescription.php<68> "`c<><00>}<03><>/src/RuleSet/AbstractMigrationSetDescription.php<68> "`c<>mɤ"src/AbstractNoUselessElseFixer.php<68> "`c<> <00><>WL<57>*src/Indicator/PhpUnitTestCaseIndicator.php<68> "`c<>:<3A>;<3B><>src/Cache/FileHandler.php  "`c <00><><AA>o<CE>src/Cache/FileCacheManager.phpD "`cD շ6<D5B7>src/Cache/Cache.php "`c <00><><F1>K<E3>src/Cache/CacheInterface.php| "`c|<1B>src/Cache/NullCacheManager.php* "`c*v<><76><A8><B9>#src/Cache/CacheManagerInterface.php<68> "`c<><00><><C2>
<A4>"src/Cache/FileHandlerInterface.php<68> "`c<><00>
~<7E>src/Cache/Directory.php<68> "`c<><00><DB> src/Cache/SignatureInterface.phpj "`cj)wv<77><76> src/Cache/DirectoryInterface.php<68> "`c<><00><><F3><14>src/Cache/Signature.php<68> "`c<><00>Db<>#src/Linter/ProcessLintingResult.php<68> "`c<>]<5D>x<>%src/Linter/LintingResultInterface.php<68> "`c<>W<><57>/<2F>src/Linter/ProcessLinter.php<68>
"`c<>
$e<><65><BF>%src/Linter/TokenizerLintingResult.phpt "`ctN<><4E><EE>)src/Linter/UnavailableLinterException.php<68> "`c<>$src/Linter/LintingException.php<68> "`c<>J<>*src/Linter/ProcessLinterProcessBuilder.php<68> "`c<><00><>r<E6><72>src/Linter/LinterInterface.php "`c<00><><98>8<B4>src/Linter/Linter.php) "`c)
v&8<>src/Linter/TokenizerLinter.php<68> "`c<><00>
8<90><A4>src/Linter/CachingLinter.phpW "`cWn<>R9<52>src/PregException.php<68> "`c<>l|7<><37>src/ConfigInterface.php "`ct<><74>v<85>src/FileReader.php) "`c)%<25>v!<21> src/AbstractPhpdocTypesFixer.php+ "`c+ <0B>Lu<4C> src/Tokenizer/TokensAnalyzer.php<68>9 "`c<>9<00>.<2E><><C0>src/Tokenizer/Tokens.phpU "`cU<00><><B9>¤src/Tokenizer/Token.php<68> "`c<><17><>?<3F>src/Tokenizer/CodeHasher.php<68> "`c<>6<>pa<70>6src/Tokenizer/Transformer/NameQualifiedTransformer.phpt "`ct<00>0X<30><58>6src/Tokenizer/Transformer/ArrayTypehintTransformer.php  "`c ?<3F><>Ť/src/Tokenizer/Transformer/ImportTransformer.phpT "`cT<00><>%y<>4src/Tokenizer/Transformer/SquareBraceTransformer.phpr "`cr<00><>ؓ<D9>2src/Tokenizer/Transformer/TypeColonTransformer.phpO "`cO<00>><3E><><B5>2src/Tokenizer/Transformer/ReturnRefTransformer.php<68> "`c<>Kള<4B>:src/Tokenizer/Transformer/WhitespacyCommentTransformer.php<68> "`c<>10<13>;src/Tokenizer/Transformer/FirstClassCallableTransformer.php "`c<00>Ni<4E><69>:src/Tokenizer/Transformer/NamespaceOperatorTransformer.php "`c<00>!<21><>5src/Tokenizer/Transformer/NullableTypeTransformer.php<68> "`c<>_<>=<3D>9src/Tokenizer/Transformer/TypeIntersectionTransformer.php3 "`c3z<><7A><82><94>3src/Tokenizer/Transformer/CurlyBraceTransformer.php0 "`c0W<>G<F9><47>8src/Tokenizer/Transformer/TypeAlternationTransformer.php "`cQ<>{<7B><>2src/Tokenizer/Transformer/AttributeTransformer.php[ "`c[f<>;)<29>@src/Tokenizer/Transformer/BraceClassInstantiationTransformer.php "`c<00><<3C>w<E5>6src/Tokenizer/Transformer/ClassConstantTransformer.phpD "`cDE<>G<FF>,src/Tokenizer/Transformer/UseTransformer.phpb "`cbE<><45><F8><85>6src/Tokenizer/Transformer/NamedArgumentTransformer.phpF "`cF<00>~<7E>T<FB>=src/Tokenizer/Transformer/ConstructorPromotionTransformer.php+ "`c+?><3E>?<3F>)src/Tokenizer/AbstractTypeTransformer.php<68> "`c<>F<15>c<ED>src/Tokenizer/Transformers.php "`c<00><>&<26><>&src/Tokenizer/TransformerInterface.php<68> "`c<>·<><C2B7><B6>%src/Tokenizer/AbstractTransformer.php<68> "`c<>I<><49>/<2F>,src/Tokenizer/Analyzer/ReferenceAnalyzer.php<68> "`c<>PCLo<4C>1src/Tokenizer/Analyzer/Analysis/MatchAnalysis.php<68> "`c<><00><1D><>2src/Tokenizer/Analyzer/Analysis/SwitchAnalysis.phpt "`ct<00><> m<>8src/Tokenizer/Analyzer/Analysis/NamespaceUseAnalysis.php] "`c]<00>Ph<50><68>Isrc/Tokenizer/Analyzer/Analysis/AbstractControlCaseStructuresAnalysis.php "`c<00>R<AB><52><E6>>src/Tokenizer/Analyzer/Analysis/StartEndTokenAwareAnalysis.php<68> "`c<><00><>™<A6>0src/Tokenizer/Analyzer/Analysis/TypeAnalysis.phpB "`cB<00><> L<>3src/Tokenizer/Analyzer/Analysis/DefaultAnalysis.php<68> "`c<><00>r<8F><01>5src/Tokenizer/Analyzer/Analysis/NamespaceAnalysis.php<68> "`c<>[<5B>0src/Tokenizer/Analyzer/Analysis/EnumAnalysis.php<68> "`c<>K<><18><>0src/Tokenizer/Analyzer/Analysis/CaseAnalysis.php<68> "`c<><00>u<D3><08>4src/Tokenizer/Analyzer/Analysis/ArgumentAnalysis.php<68> "`c<>߻<>n<C5>8src/Tokenizer/Analyzer/ControlCaseStructuresAnalyzer.php<68> "`c<><04><19>)src/Tokenizer/Analyzer/BlocksAnalyzer.phpK "`cK{\դ,src/Tokenizer/Analyzer/GotoLabelAnalyzer.phpg "`cg@<40><><04>,src/Tokenizer/Analyzer/FunctionsAnalyzer.php<68> "`c<>t<>j<F6><6A>-src/Tokenizer/Analyzer/NamespacesAnalyzer.php<68> "`c<><00>Pu<>,src/Tokenizer/Analyzer/ArgumentsAnalyzer.php<68> "`c<> <00><>o<12>4src/Tokenizer/Analyzer/AlternativeSyntaxAnalyzer.phpy "`cy n<>g4<67>(src/Tokenizer/Analyzer/RangeAnalyzer.php<68> "`c<>p<>ƶ<E1>0src/Tokenizer/Analyzer/NamespaceUsesAnalyzer.php* "`c* 7<><37>w<B9>)src/Tokenizer/Analyzer/ClassyAnalyzer.php<68> "`c<><00>p4R<34>.src/Tokenizer/Analyzer/WhitespacesAnalyzer.php "`c<00><><FB><A6><96>+src/Tokenizer/Analyzer/CommentsAnalyzer.php. "`c.<00><>c<02>,src/Tokenizer/Analyzer/AttributeAnalyzer.php<68> "`c<><14><><C0><87>src/Tokenizer/CT.php' "`c' sB<73>Ф'src/AbstractDoctrineAnnotationFixer.php= "`c=D<><44><16>!src/Runner/FileFilterIterator.php<68> "`c<>T<>EU<45>"src/Runner/FileLintingIterator.php<68> "`c<><06>l<8E>src/Runner/Runner.php<68> "`c<>r<>)src/Runner/FileCachingLintingIterator.phpu "`cu<00><>7?<3F>-src/FixerConfiguration/AllowedValueSubset.phpX "`cX<00>S<><53>5src/FixerConfiguration/FixerConfigurationResolver.phpm "`cm
<F3><1B><>9src/FixerConfiguration/DeprecatedFixerOptionInterface.php<68> "`c<><00><>&src/FixerConfiguration/FixerOption.php<68> "`c<>]T<06>/src/FixerConfiguration/FixerOptionInterface.php<68> "`c<>=<3D>k<A8><6B>-src/FixerConfiguration/FixerOptionBuilder.php<68> "`c<>
x<FC>;<3B>8src/FixerConfiguration/InvalidOptionsForEnvException.php<68> "`c<><00><><98><16>-src/FixerConfiguration/AliasedFixerOption.php "`c<00><><F8>Τ4src/FixerConfiguration/AliasedFixerOptionBuilder.php<68> "`c<><11>;<3B><>>src/FixerConfiguration/FixerConfigurationResolverInterface.php<68> "`c<>L<1C>p<EF>0src/FixerConfiguration/DeprecatedFixerOption.php: "`c:<00><><9B> <20>src/FixerFileProcessedEvent.php< "`c<x<><78>Ҥ)src/AbstractLinesBeforeNamespaceFixer.phph "`ch;Xj<00>src/AbstractFopenFlagFixer.php "`c:<3A>]<5D>src/PharChecker.php<68> "`c<>K<>ʤsrc/Differ/FullDiffer.phpj "`cjx<><78><88><AE>#src/Differ/DiffConsoleFormatter.php<68> "`c<><00><01>J<A4>src/Differ/DifferInterface.php<68> "`c<>̇G<1F>src/Differ/NullDiffer.php<68> "`c<><00>=<3D><><9F>src/Differ/UnifiedDiffer.php<68> "`c<><00><11>G<EA>src/FixerNameValidator.phpB "`cB<00><>UO<55>src/Config.php<68> "`c<> <00><> <0A>"src/Doctrine/Annotation/Tokens.php<68> "`c<><00>"<00><>!src/Doctrine/Annotation/Token.php. "`c.o<><6F><05>src/WordMatcher.php "`c<00>P<80><50><A1>src/AbstractProxyFixer.php<68> "`c<><00><18>V<C0>src/StdinFileInfo.php "`cMdHs<48>src/DocBlock/Tag.php<68> "`c<><00>P<15><>src/DocBlock/TagComparator.php<68> "`c<>D<>}<7D><>src/DocBlock/Annotation.php= "`c=<00>h<D1><68><ED>src/DocBlock/TypeExpression.php<68># "`c<>#wV<><56>!src/DocBlock/ShortDescription.php3 "`c3<00>1<9A><31><A0>src/DocBlock/DocBlock.php<68> "`c<>wSb<53>src/DocBlock/Line.php<68> "`c<>n9m~<7E>"src/FixerDefinition/CodeSample.php<68> "`c<>T<>)<29>+src/FixerDefinition/CodeSampleInterface.php<68> "`c<><><7F>[<5B>:src/FixerDefinition/VersionSpecificCodeSampleInterface.php<68> "`c<><00><>4=<3D>'src/FixerDefinition/FixerDefinition.phpU "`cU<00>Z<9A><5A><AC>.src/FixerDefinition/FileSpecificCodeSample.php<68> "`c<><00>~5.<2E>5src/FixerDefinition/VersionSpecificationInterface.php<68> "`c<>|@<00><>0src/FixerDefinition/FixerDefinitionInterface.php1 "`c1<00><><82>ݤ1src/FixerDefinition/VersionSpecificCodeSample.php2 "`c2=f+\<5C>,src/FixerDefinition/VersionSpecification.phpy "`cy<00><>e/<2F>7src/FixerDefinition/FileSpecificCodeSampleInterface.php<68> "`c<><00><10><08>src/FixerFactory.php  "`c ` <0A><><8C> src/Utils.phpp
"`cp
<0A>src/ToolInfoInterface.php<68> "`c<>2<>i<93><69>,src/AbstractPhpdocToTypeDeclarationFixer.php<68> "`c<><00><>B$<24>src/WhitespacesFixerConfig.php<68> "`c<>~<7E>;<3B>src/AbstractFixer.php "`c&<26>)V<> src/Preg.phpi "`ciI<><49><F1>src/Error/Error.php<68> "`c<>1<15><><AA>src/Error/ErrorsManager.php5 "`c5Ҽ޹<D2BC>&src/AbstractFunctionReferenceFixer.php<68> "`c<>-󂪤src/ToolInfo.php3 "`c3<00>%)=<3D>src/Console/Application.php "`c a<>C<1E>5src/Console/SelfUpdate/NewVersionCheckerInterface.phpH "`cH<00><><ED><12>0src/Console/SelfUpdate/GithubClientInterface.php<68> "`c<>܅<><DC85><84>'src/Console/SelfUpdate/GithubClient.php<68> "`c<>C<><43><B3><9A>,src/Console/SelfUpdate/NewVersionChecker.php<68> "`c<><00><><98>p<D1> src/Console/WarningsDetector.php "`cP+<2B><><FC>$src/Console/Output/ProcessOutput.php<68> "`c<> <00><>],<2C>"src/Console/Output/ErrorOutput.php<68> "`c<> h=<3D><>-src/Console/Output/ProcessOutputInterface.php<68> "`c<><00>?<3F> <0B>!src/Console/Output/NullOutput.php<68> "`c<>դ@<40><>%src/Console/ConfigurationResolver.php<68>J "`c<>JO5S<35><53>3src/Console/Report/ListSetsReport/ReportSummary.phpS "`cS}<12><>2src/Console/Report/ListSetsReport/TextReporter.php  "`c -<2D><>P<C0>2src/Console/Report/ListSetsReport/JsonReporter.php
 "`c
<00><><CF><FF><C1>5src/Console/Report/ListSetsReport/ReporterFactory.php<68> "`c<>GE\<5C>7src/Console/Report/ListSetsReport/ReporterInterface.php<68> "`c<><00><>|@<40>.src/Console/Report/FixReport/JunitReporter.php "`c )<29><>F<CA>,src/Console/Report/FixReport/XmlReporter.php<68>
"`c<>
Le<4C>٤.src/Console/Report/FixReport/ReportSummary.php  "`c <00><><FC><7F>/src/Console/Report/FixReport/GitlabReporter.php "`cC8rߤ3src/Console/Report/FixReport/CheckstyleReporter.phpQ "`cQ/<2F>-src/Console/Report/FixReport/TextReporter.php<68> "`c<><0F>@<40><>-src/Console/Report/FixReport/JsonReporter.php<68> "`c<>,<2C><><A7><F7>0src/Console/Report/FixReport/ReporterFactory.php<68> "`c<><00>–<87><C296>2src/Console/Report/FixReport/ReporterInterface.php<68> "`c<>j<>5src/Console/Command/DescribeNameNotFoundException.php<68> "`c<>9c<13>#src/Console/Command/HelpCommand.php "`c<03>|<7C><>6src/Console/Command/FixCommandExitStatusCalculator.php<68> "`c<>77H<37><48>"src/Console/Command/FixCommand.php/ "`c/<00><>;<3B><>(src/Console/Command/ListFilesCommand.phpe "`ce<00><><B5>E<DE>)src/Console/Command/SelfUpdateCommand.php<68> "`c<><00>؞W<D89E>,src/Console/Command/DocumentationCommand.php? "`c? 5<>,$<24>'src/Console/Command/ListSetsCommand.php "`cQȘ<>'src/Console/Command/DescribeCommand.php<68>' "`c<>';n<><6E><9C>src/FileRemoval.php~ "`c~Uh❤src/PharCheckerInterface.php<68> "`c<>m<10>*<2A><?php
/*
* This file is part of PHP CS Fixer.
*
* (c) Fabien Potencier <fabien@symfony.com>
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
error_reporting(E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED);
set_error_handler(static function ($severity, $message, $file, $line) {
if ($severity & error_reporting()) {
throw new ErrorException($message, 0, $severity, $file, $line);
}
});
// check environment requirements
(function () {
if (\PHP_VERSION_ID === 80000) {
fwrite(STDERR, "PHP CS Fixer is not able run on PHP 8.0.0 due to bug in PHP tokenizer (https://bugs.php.net/bug.php?id=80462).\n");
fwrite(STDERR, "Update PHP version to unblock execution.\n");
exit(1);
}
if (\PHP_VERSION_ID < 70400 || \PHP_VERSION_ID >= 80200) {
fwrite(STDERR, "PHP needs to be a minimum version of PHP 7.4.0 and maximum version of PHP 8.1.*.\n");
fwrite(STDERR, 'Current PHP version: '.PHP_VERSION.".\n");
if (getenv('PHP_CS_FIXER_IGNORE_ENV')) {
fwrite(STDERR, "Ignoring environment requirements because `PHP_CS_FIXER_IGNORE_ENV` is set. Execution may be unstable.\n");
} else {
fwrite(STDERR, "To ignore this requirement please set `PHP_CS_FIXER_IGNORE_ENV`.\n");
fwrite(STDERR, "If you use PHP version higher than supported, you may experience code modified in a wrong way.\n");
fwrite(STDERR, "Please report such cases at https://github.com/PHP-CS-Fixer/PHP-CS-Fixer .\n");
exit(1);
}
}
foreach (['json', 'tokenizer'] as $extension) {
if (!extension_loaded($extension)) {
fwrite(STDERR, sprintf("PHP extension ext-%s is missing from your system. Install or enable it.\n", $extension));
if (getenv('PHP_CS_FIXER_IGNORE_ENV')) {
fwrite(STDERR, "Ignoring environment requirements because `PHP_CS_FIXER_IGNORE_ENV` is set. Execution may be unstable.\n");
} else {
exit(1);
}
}
}
})();
// load dependencies
(function () {
$require = true;
if (class_exists('Phar')) {
// Maybe this file is used as phar-stub? Let's try!
try {
Phar::mapPhar('php-cs-fixer.phar');
require_once 'phar://php-cs-fixer.phar/vendor/autoload.php';
$require = false;
} catch (PharException $e) {
}
}
if ($require) {
// OK, it's not, let give Composer autoloader a try!
$possibleFiles = [__DIR__.'/../../autoload.php', __DIR__.'/../autoload.php', __DIR__.'/vendor/autoload.php'];
$file = null;
foreach ($possibleFiles as $possibleFile) {
if (file_exists($possibleFile)) {
$file = $possibleFile;
break;
}
}
if (null === $file) {
throw new RuntimeException('Unable to locate autoload.php file.');
}
require_once $file;
}
})();
use Composer\XdebugHandler\XdebugHandler;
use PhpCsFixer\Console\Application;
// Restart if xdebug is loaded, unless the environment variable PHP_CS_FIXER_ALLOW_XDEBUG is set.
$xdebug = new XdebugHandler('PHP_CS_FIXER');
$xdebug->check();
unset($xdebug);
$application = new Application();
$application->run();
__HALT_COMPILER();
Copyright (c) 2012-2022 Fabien Potencier, Dariusz Rumiński
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.
#!/bin/sh
set -eu
IFS='
'
CHANGED_FILES=$(git diff --name-only --diff-filter=ACMRTUXB "${COMMIT_RANGE}")
if ! echo "${CHANGED_FILES}" | grep -qE "^(\\.php-cs-fixer(\\.dist)?\\.php|composer\\.lock)$"; then EXTRA_ARGS=$(printf -- '--path-mode=intersection\n--\n%s' "${CHANGED_FILES}"); else EXTRA_ARGS=''; fi
vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php -v --dry-run --stop-on-violation --using-cache=no ${EXTRA_ARGS}
<?php
if (PHP_VERSION_ID < 50600) {
echo 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
exit(1);
}
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit43f33e2f7f39ed6f3341269681ebd52e::getLoader();
<?php
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
);
<?php
declare(strict_types=1);
namespace Composer\XdebugHandler;
/**
@phpstan-type
*/
class PhpConfig
{
public function useOriginal(): array
{
$this->getDataAndReset();
return [];
}
public function useStandard(): array
{
$data = $this->getDataAndReset();
if ($data !== null) {
return ['-n', '-c', $data['tmpIni']];
}
return [];
}
public function usePersistent(): array
{
$data = $this->getDataAndReset();
if ($data !== null) {
$this->updateEnv('PHPRC', $data['tmpIni']);
$this->updateEnv('PHP_INI_SCAN_DIR', '');
}
return [];
}
/**
@phpstan-return
*/
private function getDataAndReset(): ?array
{
$data = XdebugHandler::getRestartSettings();
if ($data !== null) {
$this->updateEnv('PHPRC', $data['phprc']);
$this->updateEnv('PHP_INI_SCAN_DIR', $data['scanDir']);
}
return $data;
}
private function updateEnv(string $name, $value): void
{
Process::setEnv($name, false !== $value ? $value : null);
}
}
<?php
declare(strict_types=1);
namespace Composer\XdebugHandler;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
class Status
{
const ENV_RESTART = 'XDEBUG_HANDLER_RESTART';
const CHECK = 'Check';
const ERROR = 'Error';
const INFO = 'Info';
const NORESTART = 'NoRestart';
const RESTART = 'Restart';
const RESTARTING = 'Restarting';
const RESTARTED = 'Restarted';
private $debug;
private $envAllowXdebug;
private $loaded;
private $logger;
private $modeOff;
private $time;
public function __construct(string $envAllowXdebug, bool $debug)
{
$start = getenv(self::ENV_RESTART);
Process::setEnv(self::ENV_RESTART);
$this->time = is_numeric($start) ? round((microtime(true) - $start) * 1000) : 0;
$this->envAllowXdebug = $envAllowXdebug;
$this->debug = $debug && defined('STDERR');
$this->modeOff = false;
}
public function setLogger(LoggerInterface $logger): void
{
$this->logger = $logger;
}
public function report(string $op, ?string $data): void
{
if ($this->logger !== null || $this->debug) {
$callable = [$this, 'report'.$op];
if (!is_callable($callable)) {
throw new \InvalidArgumentException('Unknown op handler: '.$op);
}
$params = $data !== null ? [$data] : [];
call_user_func_array($callable, $params);
}
}
private function output(string $text, ?string $level = null): void
{
if ($this->logger !== null) {
$this->logger->log($level !== null ? $level: LogLevel::DEBUG, $text);
}
if ($this->debug) {
fwrite(STDERR, sprintf('xdebug-handler[%d] %s', getmypid(), $text.PHP_EOL));
}
}
private function reportCheck(string $loaded): void
{
list($version, $mode) = explode('|', $loaded);
if ($version !== '') {
$this->loaded = '('.$version.')'.($mode !== '' ? ' xdebug.mode='.$mode : '');
}
$this->modeOff = $mode === 'off';
$this->output('Checking '.$this->envAllowXdebug);
}
private function reportError(string $error): void
{
$this->output(sprintf('No restart (%s)', $error), LogLevel::WARNING);
}
private function reportInfo(string $info): void
{
$this->output($info);
}
private function reportNoRestart(): void
{
$this->output($this->getLoadedMessage());
if ($this->loaded !== null) {
$text = sprintf('No restart (%s)', $this->getEnvAllow());
if (!((bool) getenv($this->envAllowXdebug))) {
$text .= ' Allowed by '.($this->modeOff ? 'xdebug.mode' : 'application');
}
$this->output($text);
}
}
private function reportRestart(): void
{
$this->output($this->getLoadedMessage());
Process::setEnv(self::ENV_RESTART, (string) microtime(true));
}
private function reportRestarted(): void
{
$loaded = $this->getLoadedMessage();
$text = sprintf('Restarted (%d ms). %s', $this->time, $loaded);
$level = $this->loaded !== null ? LogLevel::WARNING : null;
$this->output($text, $level);
}
private function reportRestarting(string $command): void
{
$text = sprintf('Process restarting (%s)', $this->getEnvAllow());
$this->output($text);
$text = 'Running '.$command;
$this->output($text);
}
private function getEnvAllow(): string
{
return $this->envAllowXdebug.'='.getenv($this->envAllowXdebug);
}
private function getLoadedMessage(): string
{
$loaded = $this->loaded !== null ? sprintf('loaded %s', $this->loaded) : 'not loaded';
return 'The Xdebug extension is '.$loaded;
}
}
<?php
declare(strict_types=1);
namespace Composer\XdebugHandler;
use Composer\Pcre\Preg;
use Psr\Log\LoggerInterface;
/**
@phpstan-import-type
*/
class XdebugHandler
{
const SUFFIX_ALLOW = '_ALLOW_XDEBUG';
const SUFFIX_INIS = '_ORIGINAL_INIS';
const RESTART_ID = 'internal';
const RESTART_SETTINGS = 'XDEBUG_HANDLER_SETTINGS';
const DEBUG = 'XDEBUG_HANDLER_DEBUG';
protected $tmpIni;
private static $inRestart;
private static $name;
private static $skipped;
private static $xdebugActive;
private static $xdebugMode;
private static $xdebugVersion;
private $cli;
private $debug;
private $envAllowXdebug;
private $envOriginalInis;
private $persistent;
private $script;
private $statusWriter;
public function __construct(string $envPrefix)
{
if ($envPrefix === '') {
throw new \RuntimeException('Invalid constructor parameter');
}
self::$name = strtoupper($envPrefix);
$this->envAllowXdebug = self::$name.self::SUFFIX_ALLOW;
$this->envOriginalInis = self::$name.self::SUFFIX_INIS;
self::setXdebugDetails();
self::$inRestart = false;
if ($this->cli = PHP_SAPI === 'cli') {
$this->debug = (string) getenv(self::DEBUG);
}
$this->statusWriter = new Status($this->envAllowXdebug, (bool) $this->debug);
}
public function setLogger(LoggerInterface $logger): self
{
$this->statusWriter->setLogger($logger);
return $this;
}
public function setMainScript(string $script): self
{
$this->script = $script;
return $this;
}
public function setPersistent(): self
{
$this->persistent = true;
return $this;
}
public function check(): void
{
$this->notify(Status::CHECK, self::$xdebugVersion.'|'.self::$xdebugMode);
$envArgs = explode('|', (string) getenv($this->envAllowXdebug));
if (!((bool) $envArgs[0]) && $this->requiresRestart(self::$xdebugActive)) {
$this->notify(Status::RESTART);
if ($this->prepareRestart()) {
$command = $this->getCommand();
$this->restart($command);
}
return;
}
if (self::RESTART_ID === $envArgs[0] && count($envArgs) === 5) {
$this->notify(Status::RESTARTED);
Process::setEnv($this->envAllowXdebug);
self::$inRestart = true;
if (self::$xdebugVersion === null) {
self::$skipped = $envArgs[1];
}
$this->tryEnableSignals();
$this->setEnvRestartSettings($envArgs);
return;
}
$this->notify(Status::NORESTART);
$settings = self::getRestartSettings();
if ($settings !== null) {
$this->syncSettings($settings);
}
}
public static function getAllIniFiles(): array
{
if (self::$name !== null) {
$env = getenv(self::$name.self::SUFFIX_INIS);
if (false !== $env) {
return explode(PATH_SEPARATOR, $env);
}
}
$paths = [(string) php_ini_loaded_file()];
$scanned = php_ini_scanned_files();
if ($scanned !== false) {
$paths = array_merge($paths, array_map('trim', explode(',', $scanned)));
}
return $paths;
}
/**
@phpstan-return
*/
public static function getRestartSettings(): ?array
{
$envArgs = explode('|', (string) getenv(self::RESTART_SETTINGS));
if (count($envArgs) !== 6
|| (!self::$inRestart && php_ini_loaded_file() !== $envArgs[0])) {
return null;
}
return [
'tmpIni' => $envArgs[0],
'scannedInis' => (bool) $envArgs[1],
'scanDir' => '*' === $envArgs[2] ? false : $envArgs[2],
'phprc' => '*' === $envArgs[3] ? false : $envArgs[3],
'inis' => explode(PATH_SEPARATOR, $envArgs[4]),
'skipped' => $envArgs[5],
];
}
public static function getSkippedVersion(): string
{
return (string) self::$skipped;
}
public static function isXdebugActive(): bool
{
self::setXdebugDetails();
return self::$xdebugActive;
}
protected function requiresRestart(bool $default): bool
{
return $default;
}
protected function restart(array $command): void
{
$this->doRestart($command);
}
/**
@phpstan-return
*/
private function doRestart(array $command): void
{
$this->tryEnableSignals();
$this->notify(Status::RESTARTING, implode(' ', $command));
if (PHP_VERSION_ID >= 70400) {
$cmd = $command;
} else {
$cmd = Process::escapeShellCommand($command);
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
$cmd = '"'.$cmd.'"';
}
}
$process = proc_open($cmd, [], $pipes);
if (is_resource($process)) {
$exitCode = proc_close($process);
}
if (!isset($exitCode)) {
$this->notify(Status::ERROR, 'Unable to restart process');
$exitCode = -1;
} else {
$this->notify(Status::INFO, 'Restarted process exited '.$exitCode);
}
if ($this->debug === '2') {
$this->notify(Status::INFO, 'Temp ini saved: '.$this->tmpIni);
} else {
@unlink((string) $this->tmpIni);
}
exit($exitCode);
}
private function prepareRestart(): bool
{
$error = null;
$iniFiles = self::getAllIniFiles();
$scannedInis = count($iniFiles) > 1;
$tmpDir = sys_get_temp_dir();
if (!$this->cli) {
$error = 'Unsupported SAPI: '.PHP_SAPI;
} elseif (!$this->checkConfiguration($info)) {
$error = $info;
} elseif (!$this->checkMainScript()) {
$error = 'Unable to access main script: '.$this->script;
} elseif (!$this->writeTmpIni($iniFiles, $tmpDir, $error)) {
$error = $error !== null ? $error : 'Unable to create temp ini file at: '.$tmpDir;
} elseif (!$this->setEnvironment($scannedInis, $iniFiles)) {
$error = 'Unable to set environment variables';
}
if ($error !== null) {
$this->notify(Status::ERROR, $error);
}
return $error === null;
}
private function writeTmpIni(array $iniFiles, string $tmpDir, ?string &$error): bool
{
if (($tmpfile = @tempnam($tmpDir, '')) === false) {
return false;
}
$this->tmpIni = $tmpfile;
if ($iniFiles[0] === '') {
array_shift($iniFiles);
}
$content = '';
$sectionRegex = '/^\s*\[(?:PATH|HOST)\s*=/mi';
$xdebugRegex = '/^\s*(zend_extension\s*=.*xdebug.*)$/mi';
foreach ($iniFiles as $file) {
if (($data = @file_get_contents($file)) === false) {
$error = 'Unable to read ini: '.$file;
return false;
}
if (Preg::isMatchWithOffsets($sectionRegex, $data, $matches, PREG_OFFSET_CAPTURE)) {
$data = substr($data, 0, $matches[0][1]);
}
$content .= Preg::replace($xdebugRegex, ';$1', $data).PHP_EOL;
}
$config = parse_ini_string($content);
$loaded = ini_get_all(null, false);
if (false === $config || false === $loaded) {
$error = 'Unable to parse ini data';
return false;
}
$content .= $this->mergeLoadedConfig($loaded, $config);
$content .= 'opcache.enable_cli=0'.PHP_EOL;
return (bool) @file_put_contents($this->tmpIni, $content);
}
private function getCommand(): array
{
$php = [PHP_BINARY];
$args = array_slice($_SERVER['argv'], 1);
if (!$this->persistent) {
array_push($php, '-n', '-c', $this->tmpIni);
}
return array_merge($php, [$this->script], $args);
}
private function setEnvironment(bool $scannedInis, array $iniFiles): bool
{
$scanDir = getenv('PHP_INI_SCAN_DIR');
$phprc = getenv('PHPRC');
if (!putenv($this->envOriginalInis.'='.implode(PATH_SEPARATOR, $iniFiles))) {
return false;
}
if ($this->persistent) {
if (!putenv('PHP_INI_SCAN_DIR=') || !putenv('PHPRC='.$this->tmpIni)) {
return false;
}
}
$envArgs = [
self::RESTART_ID,
self::$xdebugVersion,
(int) $scannedInis,
false === $scanDir ? '*' : $scanDir,
false === $phprc ? '*' : $phprc,
];
return putenv($this->envAllowXdebug.'='.implode('|', $envArgs));
}
private function notify(string $op, ?string $data = null): void
{
$this->statusWriter->report($op, $data);
}
private function mergeLoadedConfig(array $loadedConfig, array $iniConfig): string
{
$content = '';
foreach ($loadedConfig as $name => $value) {
if (!is_string($value)
|| strpos($name, 'xdebug') === 0
|| $name === 'apc.mmap_file_mask') {
continue;
}
if (!isset($iniConfig[$name]) || $iniConfig[$name] !== $value) {
$content .= $name.'="'.addcslashes($value, '\\"').'"'.PHP_EOL;
}
}
return $content;
}
private function checkMainScript(): bool
{
if ($this->script !== null) {
return file_exists($this->script) || '--' === $this->script;
}
if (file_exists($this->script = $_SERVER['argv'][0])) {
return true;
}
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$main = end($trace);
if ($main !== false && isset($main['file'])) {
return file_exists($this->script = $main['file']);
}
return false;
}
private function setEnvRestartSettings(array $envArgs): void
{
$settings = [
php_ini_loaded_file(),
$envArgs[2],
$envArgs[3],
$envArgs[4],
getenv($this->envOriginalInis),
self::$skipped,
];
Process::setEnv(self::RESTART_SETTINGS, implode('|', $settings));
}
/**
@phpstan-param
*/
private function syncSettings(array $settings): void
{
if (false === getenv($this->envOriginalInis)) {
Process::setEnv($this->envOriginalInis, implode(PATH_SEPARATOR, $settings['inis']));
}
self::$skipped = $settings['skipped'];
$this->notify(Status::INFO, 'Process called with existing restart settings');
}
private function checkConfiguration(?string &$info): bool
{
if (!function_exists('proc_open')) {
$info = 'proc_open function is disabled';
return false;
}
if (extension_loaded('uopz') && !((bool) ini_get('uopz.disable'))) {
if (function_exists('uopz_allow_exit')) {
@uopz_allow_exit(true);
} else {
$info = 'uopz extension is not compatible';
return false;
}
}
if (defined('PHP_WINDOWS_VERSION_BUILD') && PHP_VERSION_ID < 70400) {
$workingDir = getcwd();
if ($workingDir === false) {
$info = 'unable to determine working directory';
return false;
}
if (0 === strpos($workingDir, '\\\\')) {
$info = 'cmd.exe does not support UNC paths: '.$workingDir;
return false;
}
}
return true;
}
private function tryEnableSignals(): void
{
if (function_exists('pcntl_async_signals') && function_exists('pcntl_signal')) {
pcntl_async_signals(true);
$message = 'Async signals enabled';
if (!self::$inRestart) {
pcntl_signal(SIGINT, SIG_IGN);
} elseif (is_int(pcntl_signal_get_handler(SIGINT))) {
pcntl_signal(SIGINT, SIG_DFL);
}
}
if (!self::$inRestart && function_exists('sapi_windows_set_ctrl_handler')) {
sapi_windows_set_ctrl_handler(function ($evt) {});
}
}
private static function setXdebugDetails(): void
{
if (self::$xdebugActive !== null) {
return;
}
self::$xdebugActive = false;
if (!extension_loaded('xdebug')) {
return;
}
$version = phpversion('xdebug');
self::$xdebugVersion = $version !== false ? $version : 'unknown';
if (version_compare(self::$xdebugVersion, '3.1', '>=')) {
$modes = xdebug_info('mode');
self::$xdebugMode = count($modes) === 0 ? 'off' : implode(',', $modes);
self::$xdebugActive = self::$xdebugMode !== 'off';
return;
}
$iniMode = ini_get('xdebug.mode');
if ($iniMode === false) {
self::$xdebugActive = true;
return;
}
$envMode = (string) getenv('XDEBUG_MODE');
if ($envMode !== '') {
self::$xdebugMode = $envMode;
} else {
self::$xdebugMode = $iniMode !== '' ? $iniMode : 'off';
}
if (Preg::isMatch('/^,+$/', str_replace(' ', '', self::$xdebugMode))) {
self::$xdebugMode = 'off';
}
self::$xdebugActive = self::$xdebugMode !== 'off';
}
}
<?php
declare(strict_types=1);
namespace Composer\XdebugHandler;
use Composer\Pcre\Preg;
class Process
{
public static function escape(string $arg, bool $meta = true, bool $module = false): string
{
if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
return "'".str_replace("'", "'\\''", $arg)."'";
}
$quote = strpbrk($arg, " \t") !== false || $arg === '';
$arg = Preg::replace('/(\\\\*)"/', '$1$1\\"', $arg, -1, $dquotes);
if ($meta) {
$meta = $dquotes || Preg::isMatch('/%[^%]+%/', $arg);
if (!$meta) {
$quote = $quote || strpbrk($arg, '^&|<>()') !== false;
} elseif ($module && !$dquotes && $quote) {
$meta = false;
}
}
if ($quote) {
$arg = '"'.(Preg::replace('/(\\\\*)$/', '$1$1', $arg)).'"';
}
if ($meta) {
$arg = Preg::replace('/(["^&|<>()%])/', '^$1', $arg);
}
return $arg;
}
public static function escapeShellCommand(array $args): string
{
$command = '';
$module = array_shift($args);
if ($module !== null) {
$command = self::escape($module, true, true);
foreach ($args as $arg) {
$command .= ' '.self::escape($arg);
}
}
return $command;
}
public static function setEnv(string $name, ?string $value = null): bool
{
$unset = null === $value;
if (!putenv($unset ? $name : $name.'='.$value)) {
return false;
}
if ($unset) {
unset($_SERVER[$name]);
} else {
$_SERVER[$name] = $value;
}
if (false !== stripos((string) ini_get('variables_order'), 'E')) {
if ($unset) {
unset($_ENV[$name]);
} else {
$_ENV[$name] = $value;
}
}
return true;
}
}
<?php
namespace Composer\Autoload;
class ClassLoader
{
private $vendorDir;
/**
@psalm-var
*/
private $prefixLengthsPsr4 = array();
/**
@psalm-var
*/
private $prefixDirsPsr4 = array();
/**
@psalm-var
*/
private $fallbackDirsPsr4 = array();
/**
@psalm-var
*/
private $prefixesPsr0 = array();
/**
@psalm-var
*/
private $fallbackDirsPsr0 = array();
private $useIncludePath = false;
/**
@psalm-var
*/
private $classMap = array();
private $classMapAuthoritative = false;
/**
@psalm-var
*/
private $missingClasses = array();
private $apcuPrefix;
private static $registeredLoaders = array();
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
}
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
}
return array();
}
/**
@psalm-return
*/
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
/**
@psalm-return
*/
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
/**
@psalm-return
*/
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
/**
@psalm-return
*/
public function getClassMap()
{
return $this->classMap;
}
/**
@psalm-param
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
public function getUseIncludePath()
{
return $this->useIncludePath;
}
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
if (null === $this->vendorDir) {
return;
}
if ($prepend) {
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
} else {
unset(self::$registeredLoaders[$this->vendorDir]);
self::$registeredLoaders[$this->vendorDir] = $this;
}
}
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
if (null !== $this->vendorDir) {
unset(self::$registeredLoaders[$this->vendorDir]);
}
}
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
return null;
}
public function findFile($class)
{
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
$this->missingClasses[$class] = true;
}
return $file;
}
public static function getRegisteredLoaders()
{
return self::$registeredLoaders;
}
private function findFileWithExtension($class, $ext)
{
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
if (false !== $pos = strrpos($class, '\\')) {
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
}
function includeFile($file)
{
include $file;
}
<?php
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'Symfony\\Polyfill\\Php81\\' => array($vendorDir . '/symfony/polyfill-php81'),
'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
'Symfony\\Polyfill\\Php73\\' => array($vendorDir . '/symfony/polyfill-php73'),
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
'Symfony\\Polyfill\\Intl\\Normalizer\\' => array($vendorDir . '/symfony/polyfill-intl-normalizer'),
'Symfony\\Polyfill\\Intl\\Grapheme\\' => array($vendorDir . '/symfony/polyfill-intl-grapheme'),
'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'),
'Symfony\\Contracts\\Service\\' => array($vendorDir . '/symfony/service-contracts'),
'Symfony\\Contracts\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher-contracts'),
'Symfony\\Component\\String\\' => array($vendorDir . '/symfony/string'),
'Symfony\\Component\\Stopwatch\\' => array($vendorDir . '/symfony/stopwatch'),
'Symfony\\Component\\Process\\' => array($vendorDir . '/symfony/process'),
'Symfony\\Component\\OptionsResolver\\' => array($vendorDir . '/symfony/options-resolver'),
'Symfony\\Component\\Finder\\' => array($vendorDir . '/symfony/finder'),
'Symfony\\Component\\Filesystem\\' => array($vendorDir . '/symfony/filesystem'),
'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'),
'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'),
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
'Psr\\EventDispatcher\\' => array($vendorDir . '/psr/event-dispatcher/src'),
'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
'PhpCsFixer\\' => array($baseDir . '/src'),
'Doctrine\\Common\\Lexer\\' => array($vendorDir . '/doctrine/lexer/lib/Doctrine/Common/Lexer'),
'Doctrine\\Common\\Annotations\\' => array($vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations'),
'Composer\\XdebugHandler\\' => array($vendorDir . '/composer/xdebug-handler/src'),
'Composer\\Semver\\' => array($vendorDir . '/composer/semver/src'),
'Composer\\Pcre\\' => array($vendorDir . '/composer/pcre/src'),
);
<?php
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
'Composer\\Pcre\\MatchAllResult' => $vendorDir . '/composer/pcre/src/MatchAllResult.php',
'Composer\\Pcre\\MatchAllWithOffsetsResult' => $vendorDir . '/composer/pcre/src/MatchAllWithOffsetsResult.php',
'Composer\\Pcre\\MatchResult' => $vendorDir . '/composer/pcre/src/MatchResult.php',
'Composer\\Pcre\\MatchWithOffsetsResult' => $vendorDir . '/composer/pcre/src/MatchWithOffsetsResult.php',
'Composer\\Pcre\\PcreException' => $vendorDir . '/composer/pcre/src/PcreException.php',
'Composer\\Pcre\\Preg' => $vendorDir . '/composer/pcre/src/Preg.php',
'Composer\\Pcre\\Regex' => $vendorDir . '/composer/pcre/src/Regex.php',
'Composer\\Pcre\\ReplaceResult' => $vendorDir . '/composer/pcre/src/ReplaceResult.php',
'Composer\\Semver\\Comparator' => $vendorDir . '/composer/semver/src/Comparator.php',
'Composer\\Semver\\CompilingMatcher' => $vendorDir . '/composer/semver/src/CompilingMatcher.php',
'Composer\\Semver\\Constraint\\Bound' => $vendorDir . '/composer/semver/src/Constraint/Bound.php',
'Composer\\Semver\\Constraint\\Constraint' => $vendorDir . '/composer/semver/src/Constraint/Constraint.php',
'Composer\\Semver\\Constraint\\ConstraintInterface' => $vendorDir . '/composer/semver/src/Constraint/ConstraintInterface.php',
'Composer\\Semver\\Constraint\\MatchAllConstraint' => $vendorDir . '/composer/semver/src/Constraint/MatchAllConstraint.php',
'Composer\\Semver\\Constraint\\MatchNoneConstraint' => $vendorDir . '/composer/semver/src/Constraint/MatchNoneConstraint.php',
'Composer\\Semver\\Constraint\\MultiConstraint' => $vendorDir . '/composer/semver/src/Constraint/MultiConstraint.php',
'Composer\\Semver\\Interval' => $vendorDir . '/composer/semver/src/Interval.php',
'Composer\\Semver\\Intervals' => $vendorDir . '/composer/semver/src/Intervals.php',
'Composer\\Semver\\Semver' => $vendorDir . '/composer/semver/src/Semver.php',
'Composer\\Semver\\VersionParser' => $vendorDir . '/composer/semver/src/VersionParser.php',
'Composer\\XdebugHandler\\PhpConfig' => $vendorDir . '/composer/xdebug-handler/src/PhpConfig.php',
'Composer\\XdebugHandler\\Process' => $vendorDir . '/composer/xdebug-handler/src/Process.php',
'Composer\\XdebugHandler\\Status' => $vendorDir . '/composer/xdebug-handler/src/Status.php',
'Composer\\XdebugHandler\\XdebugHandler' => $vendorDir . '/composer/xdebug-handler/src/XdebugHandler.php',
'Doctrine\\Common\\Annotations\\Annotation' => $vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation.php',
'Doctrine\\Common\\Annotations\\AnnotationException' => $vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationException.php',
'Doctrine\\Common\\Annotations\\AnnotationReader' => $vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationReader.php',
'Doctrine\\Common\\Annotations\\AnnotationRegistry' => $vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationRegistry.php',
'Doctrine\\Common\\Annotations\\Annotation\\Attribute' => $vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attribute.php',
'Doctrine\\Common\\Annotations\\Annotation\\Attributes' => $vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attributes.php',
'Doctrine\\Common\\Annotations\\Annotation\\Enum' => $vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Enum.php',
'Doctrine\\Common\\Annotations\\Annotation\\IgnoreAnnotation' => $vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.php',
'Doctrine\\Common\\Annotations\\Annotation\\NamedArgumentConstructor' => $vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/NamedArgumentConstructor.php',
'Doctrine\\Common\\Annotations\\Annotation\\Required' => $vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Required.php',
'Doctrine\\Common\\Annotations\\Annotation\\Target' => $vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Target.php',
'Doctrine\\Common\\Annotations\\CachedReader' => $vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations/CachedReader.php',
'Doctrine\\Common\\Annotations\\DocLexer' => $vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations/DocLexer.php',
'Doctrine\\Common\\Annotations\\DocParser' => $vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php',
'Doctrine\\Common\\Annotations\\FileCacheReader' => $vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations/FileCacheReader.php',
'Doctrine\\Common\\Annotations\\ImplicitlyIgnoredAnnotationNames' => $vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations/ImplicitlyIgnoredAnnotationNames.php',
'Doctrine\\Common\\Annotations\\IndexedReader' => $vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations/IndexedReader.php',
'Doctrine\\Common\\Annotations\\NamedArgumentConstructorAnnotation' => $vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations/NamedArgumentConstructorAnnotation.php',
'Doctrine\\Common\\Annotations\\PhpParser' => $vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations/PhpParser.php',
'Doctrine\\Common\\Annotations\\PsrCachedReader' => $vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations/PsrCachedReader.php',
'Doctrine\\Common\\Annotations\\Reader' => $vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations/Reader.php',
'Doctrine\\Common\\Annotations\\SimpleAnnotationReader' => $vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php',
'Doctrine\\Common\\Annotations\\TokenParser' => $vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations/TokenParser.php',
'Doctrine\\Common\\Lexer\\AbstractLexer' => $vendorDir . '/doctrine/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.php',
'JsonException' => $vendorDir . '/symfony/polyfill-php73/Resources/stubs/JsonException.php',
'Normalizer' => $vendorDir . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php',
'PhpCsFixer\\AbstractDoctrineAnnotationFixer' => $baseDir . '/src/AbstractDoctrineAnnotationFixer.php',
'PhpCsFixer\\AbstractFixer' => $baseDir . '/src/AbstractFixer.php',
'PhpCsFixer\\AbstractFopenFlagFixer' => $baseDir . '/src/AbstractFopenFlagFixer.php',
'PhpCsFixer\\AbstractFunctionReferenceFixer' => $baseDir . '/src/AbstractFunctionReferenceFixer.php',
'PhpCsFixer\\AbstractLinesBeforeNamespaceFixer' => $baseDir . '/src/AbstractLinesBeforeNamespaceFixer.php',
'PhpCsFixer\\AbstractNoUselessElseFixer' => $baseDir . '/src/AbstractNoUselessElseFixer.php',
'PhpCsFixer\\AbstractPhpdocToTypeDeclarationFixer' => $baseDir . '/src/AbstractPhpdocToTypeDeclarationFixer.php',
'PhpCsFixer\\AbstractPhpdocTypesFixer' => $baseDir . '/src/AbstractPhpdocTypesFixer.php',
'PhpCsFixer\\AbstractProxyFixer' => $baseDir . '/src/AbstractProxyFixer.php',
'PhpCsFixer\\Cache\\Cache' => $baseDir . '/src/Cache/Cache.php',
'PhpCsFixer\\Cache\\CacheInterface' => $baseDir . '/src/Cache/CacheInterface.php',
'PhpCsFixer\\Cache\\CacheManagerInterface' => $baseDir . '/src/Cache/CacheManagerInterface.php',
'PhpCsFixer\\Cache\\Directory' => $baseDir . '/src/Cache/Directory.php',
'PhpCsFixer\\Cache\\DirectoryInterface' => $baseDir . '/src/Cache/DirectoryInterface.php',
'PhpCsFixer\\Cache\\FileCacheManager' => $baseDir . '/src/Cache/FileCacheManager.php',
'PhpCsFixer\\Cache\\FileHandler' => $baseDir . '/src/Cache/FileHandler.php',
'PhpCsFixer\\Cache\\FileHandlerInterface' => $baseDir . '/src/Cache/FileHandlerInterface.php',
'PhpCsFixer\\Cache\\NullCacheManager' => $baseDir . '/src/Cache/NullCacheManager.php',
'PhpCsFixer\\Cache\\Signature' => $baseDir . '/src/Cache/Signature.php',
'PhpCsFixer\\Cache\\SignatureInterface' => $baseDir . '/src/Cache/SignatureInterface.php',
'PhpCsFixer\\Config' => $baseDir . '/src/Config.php',
'PhpCsFixer\\ConfigInterface' => $baseDir . '/src/ConfigInterface.php',
'PhpCsFixer\\ConfigurationException\\InvalidConfigurationException' => $baseDir . '/src/ConfigurationException/InvalidConfigurationException.php',
'PhpCsFixer\\ConfigurationException\\InvalidFixerConfigurationException' => $baseDir . '/src/ConfigurationException/InvalidFixerConfigurationException.php',
'PhpCsFixer\\ConfigurationException\\InvalidForEnvFixerConfigurationException' => $baseDir . '/src/ConfigurationException/InvalidForEnvFixerConfigurationException.php',
'PhpCsFixer\\ConfigurationException\\RequiredFixerConfigurationException' => $baseDir . '/src/ConfigurationException/RequiredFixerConfigurationException.php',
'PhpCsFixer\\Console\\Application' => $baseDir . '/src/Console/Application.php',
'PhpCsFixer\\Console\\Command\\DescribeCommand' => $baseDir . '/src/Console/Command/DescribeCommand.php',
'PhpCsFixer\\Console\\Command\\DescribeNameNotFoundException' => $baseDir . '/src/Console/Command/DescribeNameNotFoundException.php',
'PhpCsFixer\\Console\\Command\\DocumentationCommand' => $baseDir . '/src/Console/Command/DocumentationCommand.php',
'PhpCsFixer\\Console\\Command\\FixCommand' => $baseDir . '/src/Console/Command/FixCommand.php',
'PhpCsFixer\\Console\\Command\\FixCommandExitStatusCalculator' => $baseDir . '/src/Console/Command/FixCommandExitStatusCalculator.php',
'PhpCsFixer\\Console\\Command\\HelpCommand' => $baseDir . '/src/Console/Command/HelpCommand.php',
'PhpCsFixer\\Console\\Command\\ListFilesCommand' => $baseDir . '/src/Console/Command/ListFilesCommand.php',
'PhpCsFixer\\Console\\Command\\ListSetsCommand' => $baseDir . '/src/Console/Command/ListSetsCommand.php',
'PhpCsFixer\\Console\\Command\\SelfUpdateCommand' => $baseDir . '/src/Console/Command/SelfUpdateCommand.php',
'PhpCsFixer\\Console\\ConfigurationResolver' => $baseDir . '/src/Console/ConfigurationResolver.php',
'PhpCsFixer\\Console\\Output\\ErrorOutput' => $baseDir . '/src/Console/Output/ErrorOutput.php',
'PhpCsFixer\\Console\\Output\\NullOutput' => $baseDir . '/src/Console/Output/NullOutput.php',
'PhpCsFixer\\Console\\Output\\ProcessOutput' => $baseDir . '/src/Console/Output/ProcessOutput.php',
'PhpCsFixer\\Console\\Output\\ProcessOutputInterface' => $baseDir . '/src/Console/Output/ProcessOutputInterface.php',
'PhpCsFixer\\Console\\Report\\FixReport\\CheckstyleReporter' => $baseDir . '/src/Console/Report/FixReport/CheckstyleReporter.php',
'PhpCsFixer\\Console\\Report\\FixReport\\GitlabReporter' => $baseDir . '/src/Console/Report/FixReport/GitlabReporter.php',
'PhpCsFixer\\Console\\Report\\FixReport\\JsonReporter' => $baseDir . '/src/Console/Report/FixReport/JsonReporter.php',
'PhpCsFixer\\Console\\Report\\FixReport\\JunitReporter' => $baseDir . '/src/Console/Report/FixReport/JunitReporter.php',
'PhpCsFixer\\Console\\Report\\FixReport\\ReportSummary' => $baseDir . '/src/Console/Report/FixReport/ReportSummary.php',
'PhpCsFixer\\Console\\Report\\FixReport\\ReporterFactory' => $baseDir . '/src/Console/Report/FixReport/ReporterFactory.php',
'PhpCsFixer\\Console\\Report\\FixReport\\ReporterInterface' => $baseDir . '/src/Console/Report/FixReport/ReporterInterface.php',
'PhpCsFixer\\Console\\Report\\FixReport\\TextReporter' => $baseDir . '/src/Console/Report/FixReport/TextReporter.php',
'PhpCsFixer\\Console\\Report\\FixReport\\XmlReporter' => $baseDir . '/src/Console/Report/FixReport/XmlReporter.php',
'PhpCsFixer\\Console\\Report\\ListSetsReport\\JsonReporter' => $baseDir . '/src/Console/Report/ListSetsReport/JsonReporter.php',
'PhpCsFixer\\Console\\Report\\ListSetsReport\\ReportSummary' => $baseDir . '/src/Console/Report/ListSetsReport/ReportSummary.php',
'PhpCsFixer\\Console\\Report\\ListSetsReport\\ReporterFactory' => $baseDir . '/src/Console/Report/ListSetsReport/ReporterFactory.php',
'PhpCsFixer\\Console\\Report\\ListSetsReport\\ReporterInterface' => $baseDir . '/src/Console/Report/ListSetsReport/ReporterInterface.php',
'PhpCsFixer\\Console\\Report\\ListSetsReport\\TextReporter' => $baseDir . '/src/Console/Report/ListSetsReport/TextReporter.php',
'PhpCsFixer\\Console\\SelfUpdate\\GithubClient' => $baseDir . '/src/Console/SelfUpdate/GithubClient.php',
'PhpCsFixer\\Console\\SelfUpdate\\GithubClientInterface' => $baseDir . '/src/Console/SelfUpdate/GithubClientInterface.php',
'PhpCsFixer\\Console\\SelfUpdate\\NewVersionChecker' => $baseDir . '/src/Console/SelfUpdate/NewVersionChecker.php',
'PhpCsFixer\\Console\\SelfUpdate\\NewVersionCheckerInterface' => $baseDir . '/src/Console/SelfUpdate/NewVersionCheckerInterface.php',
'PhpCsFixer\\Console\\WarningsDetector' => $baseDir . '/src/Console/WarningsDetector.php',
'PhpCsFixer\\Differ\\DiffConsoleFormatter' => $baseDir . '/src/Differ/DiffConsoleFormatter.php',
'PhpCsFixer\\Differ\\DifferInterface' => $baseDir . '/src/Differ/DifferInterface.php',
'PhpCsFixer\\Differ\\FullDiffer' => $baseDir . '/src/Differ/FullDiffer.php',
'PhpCsFixer\\Differ\\NullDiffer' => $baseDir . '/src/Differ/NullDiffer.php',
'PhpCsFixer\\Differ\\UnifiedDiffer' => $baseDir . '/src/Differ/UnifiedDiffer.php',
'PhpCsFixer\\DocBlock\\Annotation' => $baseDir . '/src/DocBlock/Annotation.php',
'PhpCsFixer\\DocBlock\\DocBlock' => $baseDir . '/src/DocBlock/DocBlock.php',
'PhpCsFixer\\DocBlock\\Line' => $baseDir . '/src/DocBlock/Line.php',
'PhpCsFixer\\DocBlock\\ShortDescription' => $baseDir . '/src/DocBlock/ShortDescription.php',
'PhpCsFixer\\DocBlock\\Tag' => $baseDir . '/src/DocBlock/Tag.php',
'PhpCsFixer\\DocBlock\\TagComparator' => $baseDir . '/src/DocBlock/TagComparator.php',
'PhpCsFixer\\DocBlock\\TypeExpression' => $baseDir . '/src/DocBlock/TypeExpression.php',
'PhpCsFixer\\Doctrine\\Annotation\\Token' => $baseDir . '/src/Doctrine/Annotation/Token.php',
'PhpCsFixer\\Doctrine\\Annotation\\Tokens' => $baseDir . '/src/Doctrine/Annotation/Tokens.php',
'PhpCsFixer\\Documentation\\DocumentationLocator' => $baseDir . '/src/Documentation/DocumentationLocator.php',
'PhpCsFixer\\Documentation\\FixerDocumentGenerator' => $baseDir . '/src/Documentation/FixerDocumentGenerator.php',
'PhpCsFixer\\Documentation\\ListDocumentGenerator' => $baseDir . '/src/Documentation/ListDocumentGenerator.php',
'PhpCsFixer\\Documentation\\RstUtils' => $baseDir . '/src/Documentation/RstUtils.php',
'PhpCsFixer\\Documentation\\RuleSetDocumentationGenerator' => $baseDir . '/src/Documentation/RuleSetDocumentationGenerator.php',
'PhpCsFixer\\Error\\Error' => $baseDir . '/src/Error/Error.php',
'PhpCsFixer\\Error\\ErrorsManager' => $baseDir . '/src/Error/ErrorsManager.php',
'PhpCsFixer\\FileReader' => $baseDir . '/src/FileReader.php',
'PhpCsFixer\\FileRemoval' => $baseDir . '/src/FileRemoval.php',
'PhpCsFixer\\Finder' => $baseDir . '/src/Finder.php',
'PhpCsFixer\\FixerConfiguration\\AliasedFixerOption' => $baseDir . '/src/FixerConfiguration/AliasedFixerOption.php',
'PhpCsFixer\\FixerConfiguration\\AliasedFixerOptionBuilder' => $baseDir . '/src/FixerConfiguration/AliasedFixerOptionBuilder.php',
'PhpCsFixer\\FixerConfiguration\\AllowedValueSubset' => $baseDir . '/src/FixerConfiguration/AllowedValueSubset.php',
'PhpCsFixer\\FixerConfiguration\\DeprecatedFixerOption' => $baseDir . '/src/FixerConfiguration/DeprecatedFixerOption.php',
'PhpCsFixer\\FixerConfiguration\\DeprecatedFixerOptionInterface' => $baseDir . '/src/FixerConfiguration/DeprecatedFixerOptionInterface.php',
'PhpCsFixer\\FixerConfiguration\\FixerConfigurationResolver' => $baseDir . '/src/FixerConfiguration/FixerConfigurationResolver.php',
'PhpCsFixer\\FixerConfiguration\\FixerConfigurationResolverInterface' => $baseDir . '/src/FixerConfiguration/FixerConfigurationResolverInterface.php',
'PhpCsFixer\\FixerConfiguration\\FixerOption' => $baseDir . '/src/FixerConfiguration/FixerOption.php',
'PhpCsFixer\\FixerConfiguration\\FixerOptionBuilder' => $baseDir . '/src/FixerConfiguration/FixerOptionBuilder.php',
'PhpCsFixer\\FixerConfiguration\\FixerOptionInterface' => $baseDir . '/src/FixerConfiguration/FixerOptionInterface.php',
'PhpCsFixer\\FixerConfiguration\\InvalidOptionsForEnvException' => $baseDir . '/src/FixerConfiguration/InvalidOptionsForEnvException.php',
'PhpCsFixer\\FixerDefinition\\CodeSample' => $baseDir . '/src/FixerDefinition/CodeSample.php',
'PhpCsFixer\\FixerDefinition\\CodeSampleInterface' => $baseDir . '/src/FixerDefinition/CodeSampleInterface.php',
'PhpCsFixer\\FixerDefinition\\FileSpecificCodeSample' => $baseDir . '/src/FixerDefinition/FileSpecificCodeSample.php',
'PhpCsFixer\\FixerDefinition\\FileSpecificCodeSampleInterface' => $baseDir . '/src/FixerDefinition/FileSpecificCodeSampleInterface.php',
'PhpCsFixer\\FixerDefinition\\FixerDefinition' => $baseDir . '/src/FixerDefinition/FixerDefinition.php',
'PhpCsFixer\\FixerDefinition\\FixerDefinitionInterface' => $baseDir . '/src/FixerDefinition/FixerDefinitionInterface.php',
'PhpCsFixer\\FixerDefinition\\VersionSpecificCodeSample' => $baseDir . '/src/FixerDefinition/VersionSpecificCodeSample.php',
'PhpCsFixer\\FixerDefinition\\VersionSpecificCodeSampleInterface' => $baseDir . '/src/FixerDefinition/VersionSpecificCodeSampleInterface.php',
'PhpCsFixer\\FixerDefinition\\VersionSpecification' => $baseDir . '/src/FixerDefinition/VersionSpecification.php',
'PhpCsFixer\\FixerDefinition\\VersionSpecificationInterface' => $baseDir . '/src/FixerDefinition/VersionSpecificationInterface.php',
'PhpCsFixer\\FixerFactory' => $baseDir . '/src/FixerFactory.php',
'PhpCsFixer\\FixerFileProcessedEvent' => $baseDir . '/src/FixerFileProcessedEvent.php',
'PhpCsFixer\\FixerNameValidator' => $baseDir . '/src/FixerNameValidator.php',
'PhpCsFixer\\Fixer\\AbstractIncrementOperatorFixer' => $baseDir . '/src/Fixer/AbstractIncrementOperatorFixer.php',
'PhpCsFixer\\Fixer\\AbstractPhpUnitFixer' => $baseDir . '/src/Fixer/AbstractPhpUnitFixer.php',
'PhpCsFixer\\Fixer\\Alias\\ArrayPushFixer' => $baseDir . '/src/Fixer/Alias/ArrayPushFixer.php',
'PhpCsFixer\\Fixer\\Alias\\BacktickToShellExecFixer' => $baseDir . '/src/Fixer/Alias/BacktickToShellExecFixer.php',
'PhpCsFixer\\Fixer\\Alias\\EregToPregFixer' => $baseDir . '/src/Fixer/Alias/EregToPregFixer.php',
'PhpCsFixer\\Fixer\\Alias\\MbStrFunctionsFixer' => $baseDir . '/src/Fixer/Alias/MbStrFunctionsFixer.php',
'PhpCsFixer\\Fixer\\Alias\\ModernizeStrposFixer' => $baseDir . '/src/Fixer/Alias/ModernizeStrposFixer.php',
'PhpCsFixer\\Fixer\\Alias\\NoAliasFunctionsFixer' => $baseDir . '/src/Fixer/Alias/NoAliasFunctionsFixer.php',
'PhpCsFixer\\Fixer\\Alias\\NoAliasLanguageConstructCallFixer' => $baseDir . '/src/Fixer/Alias/NoAliasLanguageConstructCallFixer.php',
'PhpCsFixer\\Fixer\\Alias\\NoMixedEchoPrintFixer' => $baseDir . '/src/Fixer/Alias/NoMixedEchoPrintFixer.php',
'PhpCsFixer\\Fixer\\Alias\\PowToExponentiationFixer' => $baseDir . '/src/Fixer/Alias/PowToExponentiationFixer.php',
'PhpCsFixer\\Fixer\\Alias\\RandomApiMigrationFixer' => $baseDir . '/src/Fixer/Alias/RandomApiMigrationFixer.php',
'PhpCsFixer\\Fixer\\Alias\\SetTypeToCastFixer' => $baseDir . '/src/Fixer/Alias/SetTypeToCastFixer.php',
'PhpCsFixer\\Fixer\\ArrayNotation\\ArraySyntaxFixer' => $baseDir . '/src/Fixer/ArrayNotation/ArraySyntaxFixer.php',
'PhpCsFixer\\Fixer\\ArrayNotation\\NoMultilineWhitespaceAroundDoubleArrowFixer' => $baseDir . '/src/Fixer/ArrayNotation/NoMultilineWhitespaceAroundDoubleArrowFixer.php',
'PhpCsFixer\\Fixer\\ArrayNotation\\NoTrailingCommaInSinglelineArrayFixer' => $baseDir . '/src/Fixer/ArrayNotation/NoTrailingCommaInSinglelineArrayFixer.php',
'PhpCsFixer\\Fixer\\ArrayNotation\\NoWhitespaceBeforeCommaInArrayFixer' => $baseDir . '/src/Fixer/ArrayNotation/NoWhitespaceBeforeCommaInArrayFixer.php',
'PhpCsFixer\\Fixer\\ArrayNotation\\NormalizeIndexBraceFixer' => $baseDir . '/src/Fixer/ArrayNotation/NormalizeIndexBraceFixer.php',
'PhpCsFixer\\Fixer\\ArrayNotation\\TrimArraySpacesFixer' => $baseDir . '/src/Fixer/ArrayNotation/TrimArraySpacesFixer.php',
'PhpCsFixer\\Fixer\\ArrayNotation\\WhitespaceAfterCommaInArrayFixer' => $baseDir . '/src/Fixer/ArrayNotation/WhitespaceAfterCommaInArrayFixer.php',
'PhpCsFixer\\Fixer\\Basic\\BracesFixer' => $baseDir . '/src/Fixer/Basic/BracesFixer.php',
'PhpCsFixer\\Fixer\\Basic\\CurlyBracesPositionFixer' => $baseDir . '/src/Fixer/Basic/CurlyBracesPositionFixer.php',
'PhpCsFixer\\Fixer\\Basic\\EncodingFixer' => $baseDir . '/src/Fixer/Basic/EncodingFixer.php',
'PhpCsFixer\\Fixer\\Basic\\NoMultipleStatementsPerLineFixer' => $baseDir . '/src/Fixer/Basic/NoMultipleStatementsPerLineFixer.php',
'PhpCsFixer\\Fixer\\Basic\\NoTrailingCommaInSinglelineFixer' => $baseDir . '/src/Fixer/Basic/NoTrailingCommaInSinglelineFixer.php',
'PhpCsFixer\\Fixer\\Basic\\NonPrintableCharacterFixer' => $baseDir . '/src/Fixer/Basic/NonPrintableCharacterFixer.php',
'PhpCsFixer\\Fixer\\Basic\\OctalNotationFixer' => $baseDir . '/src/Fixer/Basic/OctalNotationFixer.php',
'PhpCsFixer\\Fixer\\Basic\\PsrAutoloadingFixer' => $baseDir . '/src/Fixer/Basic/PsrAutoloadingFixer.php',
'PhpCsFixer\\Fixer\\Casing\\ClassReferenceNameCasingFixer' => $baseDir . '/src/Fixer/Casing/ClassReferenceNameCasingFixer.php',
'PhpCsFixer\\Fixer\\Casing\\ConstantCaseFixer' => $baseDir . '/src/Fixer/Casing/ConstantCaseFixer.php',
'PhpCsFixer\\Fixer\\Casing\\IntegerLiteralCaseFixer' => $baseDir . '/src/Fixer/Casing/IntegerLiteralCaseFixer.php',
'PhpCsFixer\\Fixer\\Casing\\LowercaseKeywordsFixer' => $baseDir . '/src/Fixer/Casing/LowercaseKeywordsFixer.php',
'PhpCsFixer\\Fixer\\Casing\\LowercaseStaticReferenceFixer' => $baseDir . '/src/Fixer/Casing/LowercaseStaticReferenceFixer.php',
'PhpCsFixer\\Fixer\\Casing\\MagicConstantCasingFixer' => $baseDir . '/src/Fixer/Casing/MagicConstantCasingFixer.php',
'PhpCsFixer\\Fixer\\Casing\\MagicMethodCasingFixer' => $baseDir . '/src/Fixer/Casing/MagicMethodCasingFixer.php',
'PhpCsFixer\\Fixer\\Casing\\NativeFunctionCasingFixer' => $baseDir . '/src/Fixer/Casing/NativeFunctionCasingFixer.php',
'PhpCsFixer\\Fixer\\Casing\\NativeFunctionTypeDeclarationCasingFixer' => $baseDir . '/src/Fixer/Casing/NativeFunctionTypeDeclarationCasingFixer.php',
'PhpCsFixer\\Fixer\\CastNotation\\CastSpacesFixer' => $baseDir . '/src/Fixer/CastNotation/CastSpacesFixer.php',
'PhpCsFixer\\Fixer\\CastNotation\\LowercaseCastFixer' => $baseDir . '/src/Fixer/CastNotation/LowercaseCastFixer.php',
'PhpCsFixer\\Fixer\\CastNotation\\ModernizeTypesCastingFixer' => $baseDir . '/src/Fixer/CastNotation/ModernizeTypesCastingFixer.php',
'PhpCsFixer\\Fixer\\CastNotation\\NoShortBoolCastFixer' => $baseDir . '/src/Fixer/CastNotation/NoShortBoolCastFixer.php',
'PhpCsFixer\\Fixer\\CastNotation\\NoUnsetCastFixer' => $baseDir . '/src/Fixer/CastNotation/NoUnsetCastFixer.php',
'PhpCsFixer\\Fixer\\CastNotation\\ShortScalarCastFixer' => $baseDir . '/src/Fixer/CastNotation/ShortScalarCastFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\ClassAttributesSeparationFixer' => $baseDir . '/src/Fixer/ClassNotation/ClassAttributesSeparationFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\ClassDefinitionFixer' => $baseDir . '/src/Fixer/ClassNotation/ClassDefinitionFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\FinalClassFixer' => $baseDir . '/src/Fixer/ClassNotation/FinalClassFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\FinalInternalClassFixer' => $baseDir . '/src/Fixer/ClassNotation/FinalInternalClassFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\FinalPublicMethodForAbstractClassFixer' => $baseDir . '/src/Fixer/ClassNotation/FinalPublicMethodForAbstractClassFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\NoBlankLinesAfterClassOpeningFixer' => $baseDir . '/src/Fixer/ClassNotation/NoBlankLinesAfterClassOpeningFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\NoNullPropertyInitializationFixer' => $baseDir . '/src/Fixer/ClassNotation/NoNullPropertyInitializationFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\NoPhp4ConstructorFixer' => $baseDir . '/src/Fixer/ClassNotation/NoPhp4ConstructorFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\NoUnneededFinalMethodFixer' => $baseDir . '/src/Fixer/ClassNotation/NoUnneededFinalMethodFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\OrderedClassElementsFixer' => $baseDir . '/src/Fixer/ClassNotation/OrderedClassElementsFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\OrderedInterfacesFixer' => $baseDir . '/src/Fixer/ClassNotation/OrderedInterfacesFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\OrderedTraitsFixer' => $baseDir . '/src/Fixer/ClassNotation/OrderedTraitsFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\ProtectedToPrivateFixer' => $baseDir . '/src/Fixer/ClassNotation/ProtectedToPrivateFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\SelfAccessorFixer' => $baseDir . '/src/Fixer/ClassNotation/SelfAccessorFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\SelfStaticAccessorFixer' => $baseDir . '/src/Fixer/ClassNotation/SelfStaticAccessorFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\SingleClassElementPerStatementFixer' => $baseDir . '/src/Fixer/ClassNotation/SingleClassElementPerStatementFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\SingleTraitInsertPerStatementFixer' => $baseDir . '/src/Fixer/ClassNotation/SingleTraitInsertPerStatementFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\VisibilityRequiredFixer' => $baseDir . '/src/Fixer/ClassNotation/VisibilityRequiredFixer.php',
'PhpCsFixer\\Fixer\\ClassUsage\\DateTimeImmutableFixer' => $baseDir . '/src/Fixer/ClassUsage/DateTimeImmutableFixer.php',
'PhpCsFixer\\Fixer\\Comment\\CommentToPhpdocFixer' => $baseDir . '/src/Fixer/Comment/CommentToPhpdocFixer.php',
'PhpCsFixer\\Fixer\\Comment\\HeaderCommentFixer' => $baseDir . '/src/Fixer/Comment/HeaderCommentFixer.php',
'PhpCsFixer\\Fixer\\Comment\\MultilineCommentOpeningClosingFixer' => $baseDir . '/src/Fixer/Comment/MultilineCommentOpeningClosingFixer.php',
'PhpCsFixer\\Fixer\\Comment\\NoEmptyCommentFixer' => $baseDir . '/src/Fixer/Comment/NoEmptyCommentFixer.php',
'PhpCsFixer\\Fixer\\Comment\\NoTrailingWhitespaceInCommentFixer' => $baseDir . '/src/Fixer/Comment/NoTrailingWhitespaceInCommentFixer.php',
'PhpCsFixer\\Fixer\\Comment\\SingleLineCommentSpacingFixer' => $baseDir . '/src/Fixer/Comment/SingleLineCommentSpacingFixer.php',
'PhpCsFixer\\Fixer\\Comment\\SingleLineCommentStyleFixer' => $baseDir . '/src/Fixer/Comment/SingleLineCommentStyleFixer.php',
'PhpCsFixer\\Fixer\\ConfigurableFixerInterface' => $baseDir . '/src/Fixer/ConfigurableFixerInterface.php',
'PhpCsFixer\\Fixer\\ConstantNotation\\NativeConstantInvocationFixer' => $baseDir . '/src/Fixer/ConstantNotation/NativeConstantInvocationFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\ControlStructureBracesFixer' => $baseDir . '/src/Fixer/ControlStructure/ControlStructureBracesFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\ControlStructureContinuationPositionFixer' => $baseDir . '/src/Fixer/ControlStructure/ControlStructureContinuationPositionFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\ElseifFixer' => $baseDir . '/src/Fixer/ControlStructure/ElseifFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\EmptyLoopBodyFixer' => $baseDir . '/src/Fixer/ControlStructure/EmptyLoopBodyFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\EmptyLoopConditionFixer' => $baseDir . '/src/Fixer/ControlStructure/EmptyLoopConditionFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\IncludeFixer' => $baseDir . '/src/Fixer/ControlStructure/IncludeFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\NoAlternativeSyntaxFixer' => $baseDir . '/src/Fixer/ControlStructure/NoAlternativeSyntaxFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\NoBreakCommentFixer' => $baseDir . '/src/Fixer/ControlStructure/NoBreakCommentFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\NoSuperfluousElseifFixer' => $baseDir . '/src/Fixer/ControlStructure/NoSuperfluousElseifFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\NoTrailingCommaInListCallFixer' => $baseDir . '/src/Fixer/ControlStructure/NoTrailingCommaInListCallFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\NoUnneededControlParenthesesFixer' => $baseDir . '/src/Fixer/ControlStructure/NoUnneededControlParenthesesFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\NoUnneededCurlyBracesFixer' => $baseDir . '/src/Fixer/ControlStructure/NoUnneededCurlyBracesFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\NoUselessElseFixer' => $baseDir . '/src/Fixer/ControlStructure/NoUselessElseFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\SimplifiedIfReturnFixer' => $baseDir . '/src/Fixer/ControlStructure/SimplifiedIfReturnFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\SwitchCaseSemicolonToColonFixer' => $baseDir . '/src/Fixer/ControlStructure/SwitchCaseSemicolonToColonFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\SwitchCaseSpaceFixer' => $baseDir . '/src/Fixer/ControlStructure/SwitchCaseSpaceFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\SwitchContinueToBreakFixer' => $baseDir . '/src/Fixer/ControlStructure/SwitchContinueToBreakFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\TrailingCommaInMultilineFixer' => $baseDir . '/src/Fixer/ControlStructure/TrailingCommaInMultilineFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\YodaStyleFixer' => $baseDir . '/src/Fixer/ControlStructure/YodaStyleFixer.php',
'PhpCsFixer\\Fixer\\DeprecatedFixerInterface' => $baseDir . '/src/Fixer/DeprecatedFixerInterface.php',
'PhpCsFixer\\Fixer\\DoctrineAnnotation\\DoctrineAnnotationArrayAssignmentFixer' => $baseDir . '/src/Fixer/DoctrineAnnotation/DoctrineAnnotationArrayAssignmentFixer.php',
'PhpCsFixer\\Fixer\\DoctrineAnnotation\\DoctrineAnnotationBracesFixer' => $baseDir . '/src/Fixer/DoctrineAnnotation/DoctrineAnnotationBracesFixer.php',
'PhpCsFixer\\Fixer\\DoctrineAnnotation\\DoctrineAnnotationIndentationFixer' => $baseDir . '/src/Fixer/DoctrineAnnotation/DoctrineAnnotationIndentationFixer.php',
'PhpCsFixer\\Fixer\\DoctrineAnnotation\\DoctrineAnnotationSpacesFixer' => $baseDir . '/src/Fixer/DoctrineAnnotation/DoctrineAnnotationSpacesFixer.php',
'PhpCsFixer\\Fixer\\FixerInterface' => $baseDir . '/src/Fixer/FixerInterface.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\CombineNestedDirnameFixer' => $baseDir . '/src/Fixer/FunctionNotation/CombineNestedDirnameFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\DateTimeCreateFromFormatCallFixer' => $baseDir . '/src/Fixer/FunctionNotation/DateTimeCreateFromFormatCallFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\FopenFlagOrderFixer' => $baseDir . '/src/Fixer/FunctionNotation/FopenFlagOrderFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\FopenFlagsFixer' => $baseDir . '/src/Fixer/FunctionNotation/FopenFlagsFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\FunctionDeclarationFixer' => $baseDir . '/src/Fixer/FunctionNotation/FunctionDeclarationFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\FunctionTypehintSpaceFixer' => $baseDir . '/src/Fixer/FunctionNotation/FunctionTypehintSpaceFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\ImplodeCallFixer' => $baseDir . '/src/Fixer/FunctionNotation/ImplodeCallFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\LambdaNotUsedImportFixer' => $baseDir . '/src/Fixer/FunctionNotation/LambdaNotUsedImportFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\MethodArgumentSpaceFixer' => $baseDir . '/src/Fixer/FunctionNotation/MethodArgumentSpaceFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\NativeFunctionInvocationFixer' => $baseDir . '/src/Fixer/FunctionNotation/NativeFunctionInvocationFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\NoSpacesAfterFunctionNameFixer' => $baseDir . '/src/Fixer/FunctionNotation/NoSpacesAfterFunctionNameFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\NoTrailingCommaInSinglelineFunctionCallFixer' => $baseDir . '/src/Fixer/FunctionNotation/NoTrailingCommaInSinglelineFunctionCallFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\NoUnreachableDefaultArgumentValueFixer' => $baseDir . '/src/Fixer/FunctionNotation/NoUnreachableDefaultArgumentValueFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\NoUselessSprintfFixer' => $baseDir . '/src/Fixer/FunctionNotation/NoUselessSprintfFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\NullableTypeDeclarationForDefaultNullValueFixer' => $baseDir . '/src/Fixer/FunctionNotation/NullableTypeDeclarationForDefaultNullValueFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\PhpdocToParamTypeFixer' => $baseDir . '/src/Fixer/FunctionNotation/PhpdocToParamTypeFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\PhpdocToPropertyTypeFixer' => $baseDir . '/src/Fixer/FunctionNotation/PhpdocToPropertyTypeFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\PhpdocToReturnTypeFixer' => $baseDir . '/src/Fixer/FunctionNotation/PhpdocToReturnTypeFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\RegularCallableCallFixer' => $baseDir . '/src/Fixer/FunctionNotation/RegularCallableCallFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\ReturnTypeDeclarationFixer' => $baseDir . '/src/Fixer/FunctionNotation/ReturnTypeDeclarationFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\SingleLineThrowFixer' => $baseDir . '/src/Fixer/FunctionNotation/SingleLineThrowFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\StaticLambdaFixer' => $baseDir . '/src/Fixer/FunctionNotation/StaticLambdaFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\UseArrowFunctionsFixer' => $baseDir . '/src/Fixer/FunctionNotation/UseArrowFunctionsFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\VoidReturnFixer' => $baseDir . '/src/Fixer/FunctionNotation/VoidReturnFixer.php',
'PhpCsFixer\\Fixer\\Import\\FullyQualifiedStrictTypesFixer' => $baseDir . '/src/Fixer/Import/FullyQualifiedStrictTypesFixer.php',
'PhpCsFixer\\Fixer\\Import\\GlobalNamespaceImportFixer' => $baseDir . '/src/Fixer/Import/GlobalNamespaceImportFixer.php',
'PhpCsFixer\\Fixer\\Import\\GroupImportFixer' => $baseDir . '/src/Fixer/Import/GroupImportFixer.php',
'PhpCsFixer\\Fixer\\Import\\NoLeadingImportSlashFixer' => $baseDir . '/src/Fixer/Import/NoLeadingImportSlashFixer.php',
'PhpCsFixer\\Fixer\\Import\\NoUnneededImportAliasFixer' => $baseDir . '/src/Fixer/Import/NoUnneededImportAliasFixer.php',
'PhpCsFixer\\Fixer\\Import\\NoUnusedImportsFixer' => $baseDir . '/src/Fixer/Import/NoUnusedImportsFixer.php',
'PhpCsFixer\\Fixer\\Import\\OrderedImportsFixer' => $baseDir . '/src/Fixer/Import/OrderedImportsFixer.php',
'PhpCsFixer\\Fixer\\Import\\SingleImportPerStatementFixer' => $baseDir . '/src/Fixer/Import/SingleImportPerStatementFixer.php',
'PhpCsFixer\\Fixer\\Import\\SingleLineAfterImportsFixer' => $baseDir . '/src/Fixer/Import/SingleLineAfterImportsFixer.php',
'PhpCsFixer\\Fixer\\Indentation' => $baseDir . '/src/Fixer/Indentation.php',
'PhpCsFixer\\Fixer\\LanguageConstruct\\ClassKeywordRemoveFixer' => $baseDir . '/src/Fixer/LanguageConstruct/ClassKeywordRemoveFixer.php',
'PhpCsFixer\\Fixer\\LanguageConstruct\\CombineConsecutiveIssetsFixer' => $baseDir . '/src/Fixer/LanguageConstruct/CombineConsecutiveIssetsFixer.php',
'PhpCsFixer\\Fixer\\LanguageConstruct\\CombineConsecutiveUnsetsFixer' => $baseDir . '/src/Fixer/LanguageConstruct/CombineConsecutiveUnsetsFixer.php',
'PhpCsFixer\\Fixer\\LanguageConstruct\\DeclareEqualNormalizeFixer' => $baseDir . '/src/Fixer/LanguageConstruct/DeclareEqualNormalizeFixer.php',
'PhpCsFixer\\Fixer\\LanguageConstruct\\DeclareParenthesesFixer' => $baseDir . '/src/Fixer/LanguageConstruct/DeclareParenthesesFixer.php',
'PhpCsFixer\\Fixer\\LanguageConstruct\\DirConstantFixer' => $baseDir . '/src/Fixer/LanguageConstruct/DirConstantFixer.php',
'PhpCsFixer\\Fixer\\LanguageConstruct\\ErrorSuppressionFixer' => $baseDir . '/src/Fixer/LanguageConstruct/ErrorSuppressionFixer.php',
'PhpCsFixer\\Fixer\\LanguageConstruct\\ExplicitIndirectVariableFixer' => $baseDir . '/src/Fixer/LanguageConstruct/ExplicitIndirectVariableFixer.php',
'PhpCsFixer\\Fixer\\LanguageConstruct\\FunctionToConstantFixer' => $baseDir . '/src/Fixer/LanguageConstruct/FunctionToConstantFixer.php',
'PhpCsFixer\\Fixer\\LanguageConstruct\\GetClassToClassKeywordFixer' => $baseDir . '/src/Fixer/LanguageConstruct/GetClassToClassKeywordFixer.php',
'PhpCsFixer\\Fixer\\LanguageConstruct\\IsNullFixer' => $baseDir . '/src/Fixer/LanguageConstruct/IsNullFixer.php',
'PhpCsFixer\\Fixer\\LanguageConstruct\\NoUnsetOnPropertyFixer' => $baseDir . '/src/Fixer/LanguageConstruct/NoUnsetOnPropertyFixer.php',
'PhpCsFixer\\Fixer\\LanguageConstruct\\SingleSpaceAfterConstructFixer' => $baseDir . '/src/Fixer/LanguageConstruct/SingleSpaceAfterConstructFixer.php',
'PhpCsFixer\\Fixer\\ListNotation\\ListSyntaxFixer' => $baseDir . '/src/Fixer/ListNotation/ListSyntaxFixer.php',
'PhpCsFixer\\Fixer\\NamespaceNotation\\BlankLineAfterNamespaceFixer' => $baseDir . '/src/Fixer/NamespaceNotation/BlankLineAfterNamespaceFixer.php',
'PhpCsFixer\\Fixer\\NamespaceNotation\\CleanNamespaceFixer' => $baseDir . '/src/Fixer/NamespaceNotation/CleanNamespaceFixer.php',
'PhpCsFixer\\Fixer\\NamespaceNotation\\NoBlankLinesBeforeNamespaceFixer' => $baseDir . '/src/Fixer/NamespaceNotation/NoBlankLinesBeforeNamespaceFixer.php',
'PhpCsFixer\\Fixer\\NamespaceNotation\\NoLeadingNamespaceWhitespaceFixer' => $baseDir . '/src/Fixer/NamespaceNotation/NoLeadingNamespaceWhitespaceFixer.php',
'PhpCsFixer\\Fixer\\NamespaceNotation\\SingleBlankLineBeforeNamespaceFixer' => $baseDir . '/src/Fixer/NamespaceNotation/SingleBlankLineBeforeNamespaceFixer.php',
'PhpCsFixer\\Fixer\\Naming\\NoHomoglyphNamesFixer' => $baseDir . '/src/Fixer/Naming/NoHomoglyphNamesFixer.php',
'PhpCsFixer\\Fixer\\Operator\\AssignNullCoalescingToCoalesceEqualFixer' => $baseDir . '/src/Fixer/Operator/AssignNullCoalescingToCoalesceEqualFixer.php',
'PhpCsFixer\\Fixer\\Operator\\BinaryOperatorSpacesFixer' => $baseDir . '/src/Fixer/Operator/BinaryOperatorSpacesFixer.php',
'PhpCsFixer\\Fixer\\Operator\\ConcatSpaceFixer' => $baseDir . '/src/Fixer/Operator/ConcatSpaceFixer.php',
'PhpCsFixer\\Fixer\\Operator\\IncrementStyleFixer' => $baseDir . '/src/Fixer/Operator/IncrementStyleFixer.php',
'PhpCsFixer\\Fixer\\Operator\\LogicalOperatorsFixer' => $baseDir . '/src/Fixer/Operator/LogicalOperatorsFixer.php',
'PhpCsFixer\\Fixer\\Operator\\NewWithBracesFixer' => $baseDir . '/src/Fixer/Operator/NewWithBracesFixer.php',
'PhpCsFixer\\Fixer\\Operator\\NoSpaceAroundDoubleColonFixer' => $baseDir . '/src/Fixer/Operator/NoSpaceAroundDoubleColonFixer.php',
'PhpCsFixer\\Fixer\\Operator\\NoUselessConcatOperatorFixer' => $baseDir . '/src/Fixer/Operator/NoUselessConcatOperatorFixer.php',
'PhpCsFixer\\Fixer\\Operator\\NoUselessNullsafeOperatorFixer' => $baseDir . '/src/Fixer/Operator/NoUselessNullsafeOperatorFixer.php',
'PhpCsFixer\\Fixer\\Operator\\NotOperatorWithSpaceFixer' => $baseDir . '/src/Fixer/Operator/NotOperatorWithSpaceFixer.php',
'PhpCsFixer\\Fixer\\Operator\\NotOperatorWithSuccessorSpaceFixer' => $baseDir . '/src/Fixer/Operator/NotOperatorWithSuccessorSpaceFixer.php',
'PhpCsFixer\\Fixer\\Operator\\ObjectOperatorWithoutWhitespaceFixer' => $baseDir . '/src/Fixer/Operator/ObjectOperatorWithoutWhitespaceFixer.php',
'PhpCsFixer\\Fixer\\Operator\\OperatorLinebreakFixer' => $baseDir . '/src/Fixer/Operator/OperatorLinebreakFixer.php',
'PhpCsFixer\\Fixer\\Operator\\StandardizeIncrementFixer' => $baseDir . '/src/Fixer/Operator/StandardizeIncrementFixer.php',
'PhpCsFixer\\Fixer\\Operator\\StandardizeNotEqualsFixer' => $baseDir . '/src/Fixer/Operator/StandardizeNotEqualsFixer.php',
'PhpCsFixer\\Fixer\\Operator\\TernaryOperatorSpacesFixer' => $baseDir . '/src/Fixer/Operator/TernaryOperatorSpacesFixer.php',
'PhpCsFixer\\Fixer\\Operator\\TernaryToElvisOperatorFixer' => $baseDir . '/src/Fixer/Operator/TernaryToElvisOperatorFixer.php',
'PhpCsFixer\\Fixer\\Operator\\TernaryToNullCoalescingFixer' => $baseDir . '/src/Fixer/Operator/TernaryToNullCoalescingFixer.php',
'PhpCsFixer\\Fixer\\Operator\\UnaryOperatorSpacesFixer' => $baseDir . '/src/Fixer/Operator/UnaryOperatorSpacesFixer.php',
'PhpCsFixer\\Fixer\\PhpTag\\BlankLineAfterOpeningTagFixer' => $baseDir . '/src/Fixer/PhpTag/BlankLineAfterOpeningTagFixer.php',
'PhpCsFixer\\Fixer\\PhpTag\\EchoTagSyntaxFixer' => $baseDir . '/src/Fixer/PhpTag/EchoTagSyntaxFixer.php',
'PhpCsFixer\\Fixer\\PhpTag\\FullOpeningTagFixer' => $baseDir . '/src/Fixer/PhpTag/FullOpeningTagFixer.php',
'PhpCsFixer\\Fixer\\PhpTag\\LinebreakAfterOpeningTagFixer' => $baseDir . '/src/Fixer/PhpTag/LinebreakAfterOpeningTagFixer.php',
'PhpCsFixer\\Fixer\\PhpTag\\NoClosingTagFixer' => $baseDir . '/src/Fixer/PhpTag/NoClosingTagFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitConstructFixer' => $baseDir . '/src/Fixer/PhpUnit/PhpUnitConstructFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitDedicateAssertFixer' => $baseDir . '/src/Fixer/PhpUnit/PhpUnitDedicateAssertFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitDedicateAssertInternalTypeFixer' => $baseDir . '/src/Fixer/PhpUnit/PhpUnitDedicateAssertInternalTypeFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitExpectationFixer' => $baseDir . '/src/Fixer/PhpUnit/PhpUnitExpectationFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitFqcnAnnotationFixer' => $baseDir . '/src/Fixer/PhpUnit/PhpUnitFqcnAnnotationFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitInternalClassFixer' => $baseDir . '/src/Fixer/PhpUnit/PhpUnitInternalClassFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitMethodCasingFixer' => $baseDir . '/src/Fixer/PhpUnit/PhpUnitMethodCasingFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitMockFixer' => $baseDir . '/src/Fixer/PhpUnit/PhpUnitMockFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitMockShortWillReturnFixer' => $baseDir . '/src/Fixer/PhpUnit/PhpUnitMockShortWillReturnFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitNamespacedFixer' => $baseDir . '/src/Fixer/PhpUnit/PhpUnitNamespacedFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitNoExpectationAnnotationFixer' => $baseDir . '/src/Fixer/PhpUnit/PhpUnitNoExpectationAnnotationFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitSetUpTearDownVisibilityFixer' => $baseDir . '/src/Fixer/PhpUnit/PhpUnitSetUpTearDownVisibilityFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitSizeClassFixer' => $baseDir . '/src/Fixer/PhpUnit/PhpUnitSizeClassFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitStrictFixer' => $baseDir . '/src/Fixer/PhpUnit/PhpUnitStrictFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitTargetVersion' => $baseDir . '/src/Fixer/PhpUnit/PhpUnitTargetVersion.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitTestAnnotationFixer' => $baseDir . '/src/Fixer/PhpUnit/PhpUnitTestAnnotationFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitTestCaseStaticMethodCallsFixer' => $baseDir . '/src/Fixer/PhpUnit/PhpUnitTestCaseStaticMethodCallsFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitTestClassRequiresCoversFixer' => $baseDir . '/src/Fixer/PhpUnit/PhpUnitTestClassRequiresCoversFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\AlignMultilineCommentFixer' => $baseDir . '/src/Fixer/Phpdoc/AlignMultilineCommentFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\GeneralPhpdocAnnotationRemoveFixer' => $baseDir . '/src/Fixer/Phpdoc/GeneralPhpdocAnnotationRemoveFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\GeneralPhpdocTagRenameFixer' => $baseDir . '/src/Fixer/Phpdoc/GeneralPhpdocTagRenameFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\NoBlankLinesAfterPhpdocFixer' => $baseDir . '/src/Fixer/Phpdoc/NoBlankLinesAfterPhpdocFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\NoEmptyPhpdocFixer' => $baseDir . '/src/Fixer/Phpdoc/NoEmptyPhpdocFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\NoSuperfluousPhpdocTagsFixer' => $baseDir . '/src/Fixer/Phpdoc/NoSuperfluousPhpdocTagsFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocAddMissingParamAnnotationFixer' => $baseDir . '/src/Fixer/Phpdoc/PhpdocAddMissingParamAnnotationFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocAlignFixer' => $baseDir . '/src/Fixer/Phpdoc/PhpdocAlignFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocAnnotationWithoutDotFixer' => $baseDir . '/src/Fixer/Phpdoc/PhpdocAnnotationWithoutDotFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocIndentFixer' => $baseDir . '/src/Fixer/Phpdoc/PhpdocIndentFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocInlineTagNormalizerFixer' => $baseDir . '/src/Fixer/Phpdoc/PhpdocInlineTagNormalizerFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocLineSpanFixer' => $baseDir . '/src/Fixer/Phpdoc/PhpdocLineSpanFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocNoAccessFixer' => $baseDir . '/src/Fixer/Phpdoc/PhpdocNoAccessFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocNoAliasTagFixer' => $baseDir . '/src/Fixer/Phpdoc/PhpdocNoAliasTagFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocNoEmptyReturnFixer' => $baseDir . '/src/Fixer/Phpdoc/PhpdocNoEmptyReturnFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocNoPackageFixer' => $baseDir . '/src/Fixer/Phpdoc/PhpdocNoPackageFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocNoUselessInheritdocFixer' => $baseDir . '/src/Fixer/Phpdoc/PhpdocNoUselessInheritdocFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocOrderByValueFixer' => $baseDir . '/src/Fixer/Phpdoc/PhpdocOrderByValueFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocOrderFixer' => $baseDir . '/src/Fixer/Phpdoc/PhpdocOrderFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocReturnSelfReferenceFixer' => $baseDir . '/src/Fixer/Phpdoc/PhpdocReturnSelfReferenceFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocScalarFixer' => $baseDir . '/src/Fixer/Phpdoc/PhpdocScalarFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocSeparationFixer' => $baseDir . '/src/Fixer/Phpdoc/PhpdocSeparationFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocSingleLineVarSpacingFixer' => $baseDir . '/src/Fixer/Phpdoc/PhpdocSingleLineVarSpacingFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocSummaryFixer' => $baseDir . '/src/Fixer/Phpdoc/PhpdocSummaryFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocTagCasingFixer' => $baseDir . '/src/Fixer/Phpdoc/PhpdocTagCasingFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocTagTypeFixer' => $baseDir . '/src/Fixer/Phpdoc/PhpdocTagTypeFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocToCommentFixer' => $baseDir . '/src/Fixer/Phpdoc/PhpdocToCommentFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocTrimConsecutiveBlankLineSeparationFixer' => $baseDir . '/src/Fixer/Phpdoc/PhpdocTrimConsecutiveBlankLineSeparationFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocTrimFixer' => $baseDir . '/src/Fixer/Phpdoc/PhpdocTrimFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocTypesFixer' => $baseDir . '/src/Fixer/Phpdoc/PhpdocTypesFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocTypesOrderFixer' => $baseDir . '/src/Fixer/Phpdoc/PhpdocTypesOrderFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocVarAnnotationCorrectOrderFixer' => $baseDir . '/src/Fixer/Phpdoc/PhpdocVarAnnotationCorrectOrderFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocVarWithoutNameFixer' => $baseDir . '/src/Fixer/Phpdoc/PhpdocVarWithoutNameFixer.php',
'PhpCsFixer\\Fixer\\ReturnNotation\\NoUselessReturnFixer' => $baseDir . '/src/Fixer/ReturnNotation/NoUselessReturnFixer.php',
'PhpCsFixer\\Fixer\\ReturnNotation\\ReturnAssignmentFixer' => $baseDir . '/src/Fixer/ReturnNotation/ReturnAssignmentFixer.php',
'PhpCsFixer\\Fixer\\ReturnNotation\\SimplifiedNullReturnFixer' => $baseDir . '/src/Fixer/ReturnNotation/SimplifiedNullReturnFixer.php',
'PhpCsFixer\\Fixer\\Semicolon\\MultilineWhitespaceBeforeSemicolonsFixer' => $baseDir . '/src/Fixer/Semicolon/MultilineWhitespaceBeforeSemicolonsFixer.php',
'PhpCsFixer\\Fixer\\Semicolon\\NoEmptyStatementFixer' => $baseDir . '/src/Fixer/Semicolon/NoEmptyStatementFixer.php',
'PhpCsFixer\\Fixer\\Semicolon\\NoSinglelineWhitespaceBeforeSemicolonsFixer' => $baseDir . '/src/Fixer/Semicolon/NoSinglelineWhitespaceBeforeSemicolonsFixer.php',
'PhpCsFixer\\Fixer\\Semicolon\\SemicolonAfterInstructionFixer' => $baseDir . '/src/Fixer/Semicolon/SemicolonAfterInstructionFixer.php',
'PhpCsFixer\\Fixer\\Semicolon\\SpaceAfterSemicolonFixer' => $baseDir . '/src/Fixer/Semicolon/SpaceAfterSemicolonFixer.php',
'PhpCsFixer\\Fixer\\Strict\\DeclareStrictTypesFixer' => $baseDir . '/src/Fixer/Strict/DeclareStrictTypesFixer.php',
'PhpCsFixer\\Fixer\\Strict\\StrictComparisonFixer' => $baseDir . '/src/Fixer/Strict/StrictComparisonFixer.php',
'PhpCsFixer\\Fixer\\Strict\\StrictParamFixer' => $baseDir . '/src/Fixer/Strict/StrictParamFixer.php',
'PhpCsFixer\\Fixer\\StringNotation\\EscapeImplicitBackslashesFixer' => $baseDir . '/src/Fixer/StringNotation/EscapeImplicitBackslashesFixer.php',
'PhpCsFixer\\Fixer\\StringNotation\\ExplicitStringVariableFixer' => $baseDir . '/src/Fixer/StringNotation/ExplicitStringVariableFixer.php',
'PhpCsFixer\\Fixer\\StringNotation\\HeredocToNowdocFixer' => $baseDir . '/src/Fixer/StringNotation/HeredocToNowdocFixer.php',
'PhpCsFixer\\Fixer\\StringNotation\\NoBinaryStringFixer' => $baseDir . '/src/Fixer/StringNotation/NoBinaryStringFixer.php',
'PhpCsFixer\\Fixer\\StringNotation\\NoTrailingWhitespaceInStringFixer' => $baseDir . '/src/Fixer/StringNotation/NoTrailingWhitespaceInStringFixer.php',
'PhpCsFixer\\Fixer\\StringNotation\\SimpleToComplexStringVariableFixer' => $baseDir . '/src/Fixer/StringNotation/SimpleToComplexStringVariableFixer.php',
'PhpCsFixer\\Fixer\\StringNotation\\SingleQuoteFixer' => $baseDir . '/src/Fixer/StringNotation/SingleQuoteFixer.php',
'PhpCsFixer\\Fixer\\StringNotation\\StringLengthToEmptyFixer' => $baseDir . '/src/Fixer/StringNotation/StringLengthToEmptyFixer.php',
'PhpCsFixer\\Fixer\\StringNotation\\StringLineEndingFixer' => $baseDir . '/src/Fixer/StringNotation/StringLineEndingFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\ArrayIndentationFixer' => $baseDir . '/src/Fixer/Whitespace/ArrayIndentationFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\BlankLineBeforeStatementFixer' => $baseDir . '/src/Fixer/Whitespace/BlankLineBeforeStatementFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\BlankLineBetweenImportGroupsFixer' => $baseDir . '/src/Fixer/Whitespace/BlankLineBetweenImportGroupsFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\CompactNullableTypehintFixer' => $baseDir . '/src/Fixer/Whitespace/CompactNullableTypehintFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\HeredocIndentationFixer' => $baseDir . '/src/Fixer/Whitespace/HeredocIndentationFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\IndentationTypeFixer' => $baseDir . '/src/Fixer/Whitespace/IndentationTypeFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\LineEndingFixer' => $baseDir . '/src/Fixer/Whitespace/LineEndingFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\MethodChainingIndentationFixer' => $baseDir . '/src/Fixer/Whitespace/MethodChainingIndentationFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\NoExtraBlankLinesFixer' => $baseDir . '/src/Fixer/Whitespace/NoExtraBlankLinesFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\NoSpacesAroundOffsetFixer' => $baseDir . '/src/Fixer/Whitespace/NoSpacesAroundOffsetFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\NoSpacesInsideParenthesisFixer' => $baseDir . '/src/Fixer/Whitespace/NoSpacesInsideParenthesisFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\NoTrailingWhitespaceFixer' => $baseDir . '/src/Fixer/Whitespace/NoTrailingWhitespaceFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\NoWhitespaceInBlankLineFixer' => $baseDir . '/src/Fixer/Whitespace/NoWhitespaceInBlankLineFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\SingleBlankLineAtEofFixer' => $baseDir . '/src/Fixer/Whitespace/SingleBlankLineAtEofFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\StatementIndentationFixer' => $baseDir . '/src/Fixer/Whitespace/StatementIndentationFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\TypesSpacesFixer' => $baseDir . '/src/Fixer/Whitespace/TypesSpacesFixer.php',
'PhpCsFixer\\Fixer\\WhitespacesAwareFixerInterface' => $baseDir . '/src/Fixer/WhitespacesAwareFixerInterface.php',
'PhpCsFixer\\Indicator\\PhpUnitTestCaseIndicator' => $baseDir . '/src/Indicator/PhpUnitTestCaseIndicator.php',
'PhpCsFixer\\Linter\\CachingLinter' => $baseDir . '/src/Linter/CachingLinter.php',
'PhpCsFixer\\Linter\\Linter' => $baseDir . '/src/Linter/Linter.php',
'PhpCsFixer\\Linter\\LinterInterface' => $baseDir . '/src/Linter/LinterInterface.php',
'PhpCsFixer\\Linter\\LintingException' => $baseDir . '/src/Linter/LintingException.php',
'PhpCsFixer\\Linter\\LintingResultInterface' => $baseDir . '/src/Linter/LintingResultInterface.php',
'PhpCsFixer\\Linter\\ProcessLinter' => $baseDir . '/src/Linter/ProcessLinter.php',
'PhpCsFixer\\Linter\\ProcessLinterProcessBuilder' => $baseDir . '/src/Linter/ProcessLinterProcessBuilder.php',
'PhpCsFixer\\Linter\\ProcessLintingResult' => $baseDir . '/src/Linter/ProcessLintingResult.php',
'PhpCsFixer\\Linter\\TokenizerLinter' => $baseDir . '/src/Linter/TokenizerLinter.php',
'PhpCsFixer\\Linter\\TokenizerLintingResult' => $baseDir . '/src/Linter/TokenizerLintingResult.php',
'PhpCsFixer\\Linter\\UnavailableLinterException' => $baseDir . '/src/Linter/UnavailableLinterException.php',
'PhpCsFixer\\PharChecker' => $baseDir . '/src/PharChecker.php',
'PhpCsFixer\\PharCheckerInterface' => $baseDir . '/src/PharCheckerInterface.php',
'PhpCsFixer\\Preg' => $baseDir . '/src/Preg.php',
'PhpCsFixer\\PregException' => $baseDir . '/src/PregException.php',
'PhpCsFixer\\RuleSet\\AbstractMigrationSetDescription' => $baseDir . '/src/RuleSet/AbstractMigrationSetDescription.php',
'PhpCsFixer\\RuleSet\\AbstractRuleSetDescription' => $baseDir . '/src/RuleSet/AbstractRuleSetDescription.php',
'PhpCsFixer\\RuleSet\\RuleSet' => $baseDir . '/src/RuleSet/RuleSet.php',
'PhpCsFixer\\RuleSet\\RuleSetDescriptionInterface' => $baseDir . '/src/RuleSet/RuleSetDescriptionInterface.php',
'PhpCsFixer\\RuleSet\\RuleSetInterface' => $baseDir . '/src/RuleSet/RuleSetInterface.php',
'PhpCsFixer\\RuleSet\\RuleSets' => $baseDir . '/src/RuleSet/RuleSets.php',
'PhpCsFixer\\RuleSet\\Sets\\DoctrineAnnotationSet' => $baseDir . '/src/RuleSet/Sets/DoctrineAnnotationSet.php',
'PhpCsFixer\\RuleSet\\Sets\\PERRiskySet' => $baseDir . '/src/RuleSet/Sets/PERRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PERSet' => $baseDir . '/src/RuleSet/Sets/PERSet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHP54MigrationSet' => $baseDir . '/src/RuleSet/Sets/PHP54MigrationSet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHP56MigrationRiskySet' => $baseDir . '/src/RuleSet/Sets/PHP56MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHP70MigrationRiskySet' => $baseDir . '/src/RuleSet/Sets/PHP70MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHP70MigrationSet' => $baseDir . '/src/RuleSet/Sets/PHP70MigrationSet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHP71MigrationRiskySet' => $baseDir . '/src/RuleSet/Sets/PHP71MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHP71MigrationSet' => $baseDir . '/src/RuleSet/Sets/PHP71MigrationSet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHP73MigrationSet' => $baseDir . '/src/RuleSet/Sets/PHP73MigrationSet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHP74MigrationRiskySet' => $baseDir . '/src/RuleSet/Sets/PHP74MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHP74MigrationSet' => $baseDir . '/src/RuleSet/Sets/PHP74MigrationSet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHP80MigrationRiskySet' => $baseDir . '/src/RuleSet/Sets/PHP80MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHP80MigrationSet' => $baseDir . '/src/RuleSet/Sets/PHP80MigrationSet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHP81MigrationSet' => $baseDir . '/src/RuleSet/Sets/PHP81MigrationSet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHP82MigrationSet' => $baseDir . '/src/RuleSet/Sets/PHP82MigrationSet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHPUnit30MigrationRiskySet' => $baseDir . '/src/RuleSet/Sets/PHPUnit30MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHPUnit32MigrationRiskySet' => $baseDir . '/src/RuleSet/Sets/PHPUnit32MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHPUnit35MigrationRiskySet' => $baseDir . '/src/RuleSet/Sets/PHPUnit35MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHPUnit43MigrationRiskySet' => $baseDir . '/src/RuleSet/Sets/PHPUnit43MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHPUnit48MigrationRiskySet' => $baseDir . '/src/RuleSet/Sets/PHPUnit48MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHPUnit50MigrationRiskySet' => $baseDir . '/src/RuleSet/Sets/PHPUnit50MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHPUnit52MigrationRiskySet' => $baseDir . '/src/RuleSet/Sets/PHPUnit52MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHPUnit54MigrationRiskySet' => $baseDir . '/src/RuleSet/Sets/PHPUnit54MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHPUnit55MigrationRiskySet' => $baseDir . '/src/RuleSet/Sets/PHPUnit55MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHPUnit56MigrationRiskySet' => $baseDir . '/src/RuleSet/Sets/PHPUnit56MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHPUnit57MigrationRiskySet' => $baseDir . '/src/RuleSet/Sets/PHPUnit57MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHPUnit60MigrationRiskySet' => $baseDir . '/src/RuleSet/Sets/PHPUnit60MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHPUnit75MigrationRiskySet' => $baseDir . '/src/RuleSet/Sets/PHPUnit75MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHPUnit84MigrationRiskySet' => $baseDir . '/src/RuleSet/Sets/PHPUnit84MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PSR12RiskySet' => $baseDir . '/src/RuleSet/Sets/PSR12RiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PSR12Set' => $baseDir . '/src/RuleSet/Sets/PSR12Set.php',
'PhpCsFixer\\RuleSet\\Sets\\PSR1Set' => $baseDir . '/src/RuleSet/Sets/PSR1Set.php',
'PhpCsFixer\\RuleSet\\Sets\\PSR2Set' => $baseDir . '/src/RuleSet/Sets/PSR2Set.php',
'PhpCsFixer\\RuleSet\\Sets\\PhpCsFixerRiskySet' => $baseDir . '/src/RuleSet/Sets/PhpCsFixerRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PhpCsFixerSet' => $baseDir . '/src/RuleSet/Sets/PhpCsFixerSet.php',
'PhpCsFixer\\RuleSet\\Sets\\SymfonyRiskySet' => $baseDir . '/src/RuleSet/Sets/SymfonyRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\SymfonySet' => $baseDir . '/src/RuleSet/Sets/SymfonySet.php',
'PhpCsFixer\\Runner\\FileCachingLintingIterator' => $baseDir . '/src/Runner/FileCachingLintingIterator.php',
'PhpCsFixer\\Runner\\FileFilterIterator' => $baseDir . '/src/Runner/FileFilterIterator.php',
'PhpCsFixer\\Runner\\FileLintingIterator' => $baseDir . '/src/Runner/FileLintingIterator.php',
'PhpCsFixer\\Runner\\Runner' => $baseDir . '/src/Runner/Runner.php',
'PhpCsFixer\\StdinFileInfo' => $baseDir . '/src/StdinFileInfo.php',
'PhpCsFixer\\Tokenizer\\AbstractTransformer' => $baseDir . '/src/Tokenizer/AbstractTransformer.php',
'PhpCsFixer\\Tokenizer\\AbstractTypeTransformer' => $baseDir . '/src/Tokenizer/AbstractTypeTransformer.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\AlternativeSyntaxAnalyzer' => $baseDir . '/src/Tokenizer/Analyzer/AlternativeSyntaxAnalyzer.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\Analysis\\AbstractControlCaseStructuresAnalysis' => $baseDir . '/src/Tokenizer/Analyzer/Analysis/AbstractControlCaseStructuresAnalysis.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\Analysis\\ArgumentAnalysis' => $baseDir . '/src/Tokenizer/Analyzer/Analysis/ArgumentAnalysis.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\Analysis\\CaseAnalysis' => $baseDir . '/src/Tokenizer/Analyzer/Analysis/CaseAnalysis.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\Analysis\\DefaultAnalysis' => $baseDir . '/src/Tokenizer/Analyzer/Analysis/DefaultAnalysis.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\Analysis\\EnumAnalysis' => $baseDir . '/src/Tokenizer/Analyzer/Analysis/EnumAnalysis.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\Analysis\\MatchAnalysis' => $baseDir . '/src/Tokenizer/Analyzer/Analysis/MatchAnalysis.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\Analysis\\NamespaceAnalysis' => $baseDir . '/src/Tokenizer/Analyzer/Analysis/NamespaceAnalysis.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\Analysis\\NamespaceUseAnalysis' => $baseDir . '/src/Tokenizer/Analyzer/Analysis/NamespaceUseAnalysis.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\Analysis\\StartEndTokenAwareAnalysis' => $baseDir . '/src/Tokenizer/Analyzer/Analysis/StartEndTokenAwareAnalysis.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\Analysis\\SwitchAnalysis' => $baseDir . '/src/Tokenizer/Analyzer/Analysis/SwitchAnalysis.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\Analysis\\TypeAnalysis' => $baseDir . '/src/Tokenizer/Analyzer/Analysis/TypeAnalysis.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\ArgumentsAnalyzer' => $baseDir . '/src/Tokenizer/Analyzer/ArgumentsAnalyzer.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\AttributeAnalyzer' => $baseDir . '/src/Tokenizer/Analyzer/AttributeAnalyzer.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\BlocksAnalyzer' => $baseDir . '/src/Tokenizer/Analyzer/BlocksAnalyzer.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\ClassyAnalyzer' => $baseDir . '/src/Tokenizer/Analyzer/ClassyAnalyzer.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\CommentsAnalyzer' => $baseDir . '/src/Tokenizer/Analyzer/CommentsAnalyzer.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\ControlCaseStructuresAnalyzer' => $baseDir . '/src/Tokenizer/Analyzer/ControlCaseStructuresAnalyzer.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\FunctionsAnalyzer' => $baseDir . '/src/Tokenizer/Analyzer/FunctionsAnalyzer.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\GotoLabelAnalyzer' => $baseDir . '/src/Tokenizer/Analyzer/GotoLabelAnalyzer.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\NamespaceUsesAnalyzer' => $baseDir . '/src/Tokenizer/Analyzer/NamespaceUsesAnalyzer.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\NamespacesAnalyzer' => $baseDir . '/src/Tokenizer/Analyzer/NamespacesAnalyzer.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\RangeAnalyzer' => $baseDir . '/src/Tokenizer/Analyzer/RangeAnalyzer.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\ReferenceAnalyzer' => $baseDir . '/src/Tokenizer/Analyzer/ReferenceAnalyzer.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\WhitespacesAnalyzer' => $baseDir . '/src/Tokenizer/Analyzer/WhitespacesAnalyzer.php',
'PhpCsFixer\\Tokenizer\\CT' => $baseDir . '/src/Tokenizer/CT.php',
'PhpCsFixer\\Tokenizer\\CodeHasher' => $baseDir . '/src/Tokenizer/CodeHasher.php',
'PhpCsFixer\\Tokenizer\\Token' => $baseDir . '/src/Tokenizer/Token.php',
'PhpCsFixer\\Tokenizer\\Tokens' => $baseDir . '/src/Tokenizer/Tokens.php',
'PhpCsFixer\\Tokenizer\\TokensAnalyzer' => $baseDir . '/src/Tokenizer/TokensAnalyzer.php',
'PhpCsFixer\\Tokenizer\\TransformerInterface' => $baseDir . '/src/Tokenizer/TransformerInterface.php',
'PhpCsFixer\\Tokenizer\\Transformer\\ArrayTypehintTransformer' => $baseDir . '/src/Tokenizer/Transformer/ArrayTypehintTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\AttributeTransformer' => $baseDir . '/src/Tokenizer/Transformer/AttributeTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\BraceClassInstantiationTransformer' => $baseDir . '/src/Tokenizer/Transformer/BraceClassInstantiationTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\ClassConstantTransformer' => $baseDir . '/src/Tokenizer/Transformer/ClassConstantTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\ConstructorPromotionTransformer' => $baseDir . '/src/Tokenizer/Transformer/ConstructorPromotionTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\CurlyBraceTransformer' => $baseDir . '/src/Tokenizer/Transformer/CurlyBraceTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\FirstClassCallableTransformer' => $baseDir . '/src/Tokenizer/Transformer/FirstClassCallableTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\ImportTransformer' => $baseDir . '/src/Tokenizer/Transformer/ImportTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\NameQualifiedTransformer' => $baseDir . '/src/Tokenizer/Transformer/NameQualifiedTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\NamedArgumentTransformer' => $baseDir . '/src/Tokenizer/Transformer/NamedArgumentTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\NamespaceOperatorTransformer' => $baseDir . '/src/Tokenizer/Transformer/NamespaceOperatorTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\NullableTypeTransformer' => $baseDir . '/src/Tokenizer/Transformer/NullableTypeTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\ReturnRefTransformer' => $baseDir . '/src/Tokenizer/Transformer/ReturnRefTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\SquareBraceTransformer' => $baseDir . '/src/Tokenizer/Transformer/SquareBraceTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\TypeAlternationTransformer' => $baseDir . '/src/Tokenizer/Transformer/TypeAlternationTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\TypeColonTransformer' => $baseDir . '/src/Tokenizer/Transformer/TypeColonTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\TypeIntersectionTransformer' => $baseDir . '/src/Tokenizer/Transformer/TypeIntersectionTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\UseTransformer' => $baseDir . '/src/Tokenizer/Transformer/UseTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\WhitespacyCommentTransformer' => $baseDir . '/src/Tokenizer/Transformer/WhitespacyCommentTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformers' => $baseDir . '/src/Tokenizer/Transformers.php',
'PhpCsFixer\\ToolInfo' => $baseDir . '/src/ToolInfo.php',
'PhpCsFixer\\ToolInfoInterface' => $baseDir . '/src/ToolInfoInterface.php',
'PhpCsFixer\\Utils' => $baseDir . '/src/Utils.php',
'PhpCsFixer\\WhitespacesFixerConfig' => $baseDir . '/src/WhitespacesFixerConfig.php',
'PhpCsFixer\\WordMatcher' => $baseDir . '/src/WordMatcher.php',
'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
'Psr\\Cache\\CacheException' => $vendorDir . '/psr/cache/src/CacheException.php',
'Psr\\Cache\\CacheItemInterface' => $vendorDir . '/psr/cache/src/CacheItemInterface.php',
'Psr\\Cache\\CacheItemPoolInterface' => $vendorDir . '/psr/cache/src/CacheItemPoolInterface.php',
'Psr\\Cache\\InvalidArgumentException' => $vendorDir . '/psr/cache/src/InvalidArgumentException.php',
'Psr\\Container\\ContainerExceptionInterface' => $vendorDir . '/psr/container/src/ContainerExceptionInterface.php',
'Psr\\Container\\ContainerInterface' => $vendorDir . '/psr/container/src/ContainerInterface.php',
'Psr\\Container\\NotFoundExceptionInterface' => $vendorDir . '/psr/container/src/NotFoundExceptionInterface.php',
'Psr\\EventDispatcher\\EventDispatcherInterface' => $vendorDir . '/psr/event-dispatcher/src/EventDispatcherInterface.php',
'Psr\\EventDispatcher\\ListenerProviderInterface' => $vendorDir . '/psr/event-dispatcher/src/ListenerProviderInterface.php',
'Psr\\EventDispatcher\\StoppableEventInterface' => $vendorDir . '/psr/event-dispatcher/src/StoppableEventInterface.php',
'Psr\\Log\\AbstractLogger' => $vendorDir . '/psr/log/Psr/Log/AbstractLogger.php',
'Psr\\Log\\InvalidArgumentException' => $vendorDir . '/psr/log/Psr/Log/InvalidArgumentException.php',
'Psr\\Log\\LogLevel' => $vendorDir . '/psr/log/Psr/Log/LogLevel.php',
'Psr\\Log\\LoggerAwareInterface' => $vendorDir . '/psr/log/Psr/Log/LoggerAwareInterface.php',
'Psr\\Log\\LoggerAwareTrait' => $vendorDir . '/psr/log/Psr/Log/LoggerAwareTrait.php',
'Psr\\Log\\LoggerInterface' => $vendorDir . '/psr/log/Psr/Log/LoggerInterface.php',
'Psr\\Log\\LoggerTrait' => $vendorDir . '/psr/log/Psr/Log/LoggerTrait.php',
'Psr\\Log\\NullLogger' => $vendorDir . '/psr/log/Psr/Log/NullLogger.php',
'Psr\\Log\\Test\\DummyTest' => $vendorDir . '/psr/log/Psr/Log/Test/DummyTest.php',
'Psr\\Log\\Test\\LoggerInterfaceTest' => $vendorDir . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php',
'Psr\\Log\\Test\\TestLogger' => $vendorDir . '/psr/log/Psr/Log/Test/TestLogger.php',
'ReturnTypeWillChange' => $vendorDir . '/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php',
'SebastianBergmann\\Diff\\Chunk' => $vendorDir . '/sebastian/diff/src/Chunk.php',
'SebastianBergmann\\Diff\\ConfigurationException' => $vendorDir . '/sebastian/diff/src/Exception/ConfigurationException.php',
'SebastianBergmann\\Diff\\Diff' => $vendorDir . '/sebastian/diff/src/Diff.php',
'SebastianBergmann\\Diff\\Differ' => $vendorDir . '/sebastian/diff/src/Differ.php',
'SebastianBergmann\\Diff\\Exception' => $vendorDir . '/sebastian/diff/src/Exception/Exception.php',
'SebastianBergmann\\Diff\\InvalidArgumentException' => $vendorDir . '/sebastian/diff/src/Exception/InvalidArgumentException.php',
'SebastianBergmann\\Diff\\Line' => $vendorDir . '/sebastian/diff/src/Line.php',
'SebastianBergmann\\Diff\\LongestCommonSubsequenceCalculator' => $vendorDir . '/sebastian/diff/src/LongestCommonSubsequenceCalculator.php',
'SebastianBergmann\\Diff\\MemoryEfficientLongestCommonSubsequenceCalculator' => $vendorDir . '/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php',
'SebastianBergmann\\Diff\\Output\\AbstractChunkOutputBuilder' => $vendorDir . '/sebastian/diff/src/Output/AbstractChunkOutputBuilder.php',
'SebastianBergmann\\Diff\\Output\\DiffOnlyOutputBuilder' => $vendorDir . '/sebastian/diff/src/Output/DiffOnlyOutputBuilder.php',
'SebastianBergmann\\Diff\\Output\\DiffOutputBuilderInterface' => $vendorDir . '/sebastian/diff/src/Output/DiffOutputBuilderInterface.php',
'SebastianBergmann\\Diff\\Output\\StrictUnifiedDiffOutputBuilder' => $vendorDir . '/sebastian/diff/src/Output/StrictUnifiedDiffOutputBuilder.php',
'SebastianBergmann\\Diff\\Output\\UnifiedDiffOutputBuilder' => $vendorDir . '/sebastian/diff/src/Output/UnifiedDiffOutputBuilder.php',
'SebastianBergmann\\Diff\\Parser' => $vendorDir . '/sebastian/diff/src/Parser.php',
'SebastianBergmann\\Diff\\TimeEfficientLongestCommonSubsequenceCalculator' => $vendorDir . '/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php',
'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
'Symfony\\Component\\Console\\Application' => $vendorDir . '/symfony/console/Application.php',
'Symfony\\Component\\Console\\Attribute\\AsCommand' => $vendorDir . '/symfony/console/Attribute/AsCommand.php',
'Symfony\\Component\\Console\\CI\\GithubActionReporter' => $vendorDir . '/symfony/console/CI/GithubActionReporter.php',
'Symfony\\Component\\Console\\Color' => $vendorDir . '/symfony/console/Color.php',
'Symfony\\Component\\Console\\CommandLoader\\CommandLoaderInterface' => $vendorDir . '/symfony/console/CommandLoader/CommandLoaderInterface.php',
'Symfony\\Component\\Console\\CommandLoader\\ContainerCommandLoader' => $vendorDir . '/symfony/console/CommandLoader/ContainerCommandLoader.php',
'Symfony\\Component\\Console\\CommandLoader\\FactoryCommandLoader' => $vendorDir . '/symfony/console/CommandLoader/FactoryCommandLoader.php',
'Symfony\\Component\\Console\\Command\\Command' => $vendorDir . '/symfony/console/Command/Command.php',
'Symfony\\Component\\Console\\Command\\CompleteCommand' => $vendorDir . '/symfony/console/Command/CompleteCommand.php',
'Symfony\\Component\\Console\\Command\\DumpCompletionCommand' => $vendorDir . '/symfony/console/Command/DumpCompletionCommand.php',
'Symfony\\Component\\Console\\Command\\HelpCommand' => $vendorDir . '/symfony/console/Command/HelpCommand.php',
'Symfony\\Component\\Console\\Command\\LazyCommand' => $vendorDir . '/symfony/console/Command/LazyCommand.php',
'Symfony\\Component\\Console\\Command\\ListCommand' => $vendorDir . '/symfony/console/Command/ListCommand.php',
'Symfony\\Component\\Console\\Command\\LockableTrait' => $vendorDir . '/symfony/console/Command/LockableTrait.php',
'Symfony\\Component\\Console\\Command\\SignalableCommandInterface' => $vendorDir . '/symfony/console/Command/SignalableCommandInterface.php',
'Symfony\\Component\\Console\\Completion\\CompletionInput' => $vendorDir . '/symfony/console/Completion/CompletionInput.php',
'Symfony\\Component\\Console\\Completion\\CompletionSuggestions' => $vendorDir . '/symfony/console/Completion/CompletionSuggestions.php',
'Symfony\\Component\\Console\\Completion\\Output\\BashCompletionOutput' => $vendorDir . '/symfony/console/Completion/Output/BashCompletionOutput.php',
'Symfony\\Component\\Console\\Completion\\Output\\CompletionOutputInterface' => $vendorDir . '/symfony/console/Completion/Output/CompletionOutputInterface.php',
'Symfony\\Component\\Console\\Completion\\Suggestion' => $vendorDir . '/symfony/console/Completion/Suggestion.php',
'Symfony\\Component\\Console\\ConsoleEvents' => $vendorDir . '/symfony/console/ConsoleEvents.php',
'Symfony\\Component\\Console\\Cursor' => $vendorDir . '/symfony/console/Cursor.php',
'Symfony\\Component\\Console\\DependencyInjection\\AddConsoleCommandPass' => $vendorDir . '/symfony/console/DependencyInjection/AddConsoleCommandPass.php',
'Symfony\\Component\\Console\\Descriptor\\ApplicationDescription' => $vendorDir . '/symfony/console/Descriptor/ApplicationDescription.php',
'Symfony\\Component\\Console\\Descriptor\\Descriptor' => $vendorDir . '/symfony/console/Descriptor/Descriptor.php',
'Symfony\\Component\\Console\\Descriptor\\DescriptorInterface' => $vendorDir . '/symfony/console/Descriptor/DescriptorInterface.php',
'Symfony\\Component\\Console\\Descriptor\\JsonDescriptor' => $vendorDir . '/symfony/console/Descriptor/JsonDescriptor.php',
'Symfony\\Component\\Console\\Descriptor\\MarkdownDescriptor' => $vendorDir . '/symfony/console/Descriptor/MarkdownDescriptor.php',
'Symfony\\Component\\Console\\Descriptor\\TextDescriptor' => $vendorDir . '/symfony/console/Descriptor/TextDescriptor.php',
'Symfony\\Component\\Console\\Descriptor\\XmlDescriptor' => $vendorDir . '/symfony/console/Descriptor/XmlDescriptor.php',
'Symfony\\Component\\Console\\EventListener\\ErrorListener' => $vendorDir . '/symfony/console/EventListener/ErrorListener.php',
'Symfony\\Component\\Console\\Event\\ConsoleCommandEvent' => $vendorDir . '/symfony/console/Event/ConsoleCommandEvent.php',
'Symfony\\Component\\Console\\Event\\ConsoleErrorEvent' => $vendorDir . '/symfony/console/Event/ConsoleErrorEvent.php',
'Symfony\\Component\\Console\\Event\\ConsoleEvent' => $vendorDir . '/symfony/console/Event/ConsoleEvent.php',
'Symfony\\Component\\Console\\Event\\ConsoleSignalEvent' => $vendorDir . '/symfony/console/Event/ConsoleSignalEvent.php',
'Symfony\\Component\\Console\\Event\\ConsoleTerminateEvent' => $vendorDir . '/symfony/console/Event/ConsoleTerminateEvent.php',
'Symfony\\Component\\Console\\Exception\\CommandNotFoundException' => $vendorDir . '/symfony/console/Exception/CommandNotFoundException.php',
'Symfony\\Component\\Console\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/console/Exception/ExceptionInterface.php',
'Symfony\\Component\\Console\\Exception\\InvalidArgumentException' => $vendorDir . '/symfony/console/Exception/InvalidArgumentException.php',
'Symfony\\Component\\Console\\Exception\\InvalidOptionException' => $vendorDir . '/symfony/console/Exception/InvalidOptionException.php',
'Symfony\\Component\\Console\\Exception\\LogicException' => $vendorDir . '/symfony/console/Exception/LogicException.php',
'Symfony\\Component\\Console\\Exception\\MissingInputException' => $vendorDir . '/symfony/console/Exception/MissingInputException.php',
'Symfony\\Component\\Console\\Exception\\NamespaceNotFoundException' => $vendorDir . '/symfony/console/Exception/NamespaceNotFoundException.php',
'Symfony\\Component\\Console\\Exception\\RuntimeException' => $vendorDir . '/symfony/console/Exception/RuntimeException.php',
'Symfony\\Component\\Console\\Formatter\\NullOutputFormatter' => $vendorDir . '/symfony/console/Formatter/NullOutputFormatter.php',
'Symfony\\Component\\Console\\Formatter\\NullOutputFormatterStyle' => $vendorDir . '/symfony/console/Formatter/NullOutputFormatterStyle.php',
'Symfony\\Component\\Console\\Formatter\\OutputFormatter' => $vendorDir . '/symfony/console/Formatter/OutputFormatter.php',
'Symfony\\Component\\Console\\Formatter\\OutputFormatterInterface' => $vendorDir . '/symfony/console/Formatter/OutputFormatterInterface.php',
'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyle' => $vendorDir . '/symfony/console/Formatter/OutputFormatterStyle.php',
'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyleInterface' => $vendorDir . '/symfony/console/Formatter/OutputFormatterStyleInterface.php',
'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyleStack' => $vendorDir . '/symfony/console/Formatter/OutputFormatterStyleStack.php',
'Symfony\\Component\\Console\\Formatter\\WrappableOutputFormatterInterface' => $vendorDir . '/symfony/console/Formatter/WrappableOutputFormatterInterface.php',
'Symfony\\Component\\Console\\Helper\\DebugFormatterHelper' => $vendorDir . '/symfony/console/Helper/DebugFormatterHelper.php',
'Symfony\\Component\\Console\\Helper\\DescriptorHelper' => $vendorDir . '/symfony/console/Helper/DescriptorHelper.php',
'Symfony\\Component\\Console\\Helper\\Dumper' => $vendorDir . '/symfony/console/Helper/Dumper.php',
'Symfony\\Component\\Console\\Helper\\FormatterHelper' => $vendorDir . '/symfony/console/Helper/FormatterHelper.php',
'Symfony\\Component\\Console\\Helper\\Helper' => $vendorDir . '/symfony/console/Helper/Helper.php',
'Symfony\\Component\\Console\\Helper\\HelperInterface' => $vendorDir . '/symfony/console/Helper/HelperInterface.php',
'Symfony\\Component\\Console\\Helper\\HelperSet' => $vendorDir . '/symfony/console/Helper/HelperSet.php',
'Symfony\\Component\\Console\\Helper\\InputAwareHelper' => $vendorDir . '/symfony/console/Helper/InputAwareHelper.php',
'Symfony\\Component\\Console\\Helper\\ProcessHelper' => $vendorDir . '/symfony/console/Helper/ProcessHelper.php',
'Symfony\\Component\\Console\\Helper\\ProgressBar' => $vendorDir . '/symfony/console/Helper/ProgressBar.php',
'Symfony\\Component\\Console\\Helper\\ProgressIndicator' => $vendorDir . '/symfony/console/Helper/ProgressIndicator.php',
'Symfony\\Component\\Console\\Helper\\QuestionHelper' => $vendorDir . '/symfony/console/Helper/QuestionHelper.php',
'Symfony\\Component\\Console\\Helper\\SymfonyQuestionHelper' => $vendorDir . '/symfony/console/Helper/SymfonyQuestionHelper.php',
'Symfony\\Component\\Console\\Helper\\Table' => $vendorDir . '/symfony/console/Helper/Table.php',
'Symfony\\Component\\Console\\Helper\\TableCell' => $vendorDir . '/symfony/console/Helper/TableCell.php',
'Symfony\\Component\\Console\\Helper\\TableCellStyle' => $vendorDir . '/symfony/console/Helper/TableCellStyle.php',
'Symfony\\Component\\Console\\Helper\\TableRows' => $vendorDir . '/symfony/console/Helper/TableRows.php',
'Symfony\\Component\\Console\\Helper\\TableSeparator' => $vendorDir . '/symfony/console/Helper/TableSeparator.php',
'Symfony\\Component\\Console\\Helper\\TableStyle' => $vendorDir . '/symfony/console/Helper/TableStyle.php',
'Symfony\\Component\\Console\\Input\\ArgvInput' => $vendorDir . '/symfony/console/Input/ArgvInput.php',
'Symfony\\Component\\Console\\Input\\ArrayInput' => $vendorDir . '/symfony/console/Input/ArrayInput.php',
'Symfony\\Component\\Console\\Input\\Input' => $vendorDir . '/symfony/console/Input/Input.php',
'Symfony\\Component\\Console\\Input\\InputArgument' => $vendorDir . '/symfony/console/Input/InputArgument.php',
'Symfony\\Component\\Console\\Input\\InputAwareInterface' => $vendorDir . '/symfony/console/Input/InputAwareInterface.php',
'Symfony\\Component\\Console\\Input\\InputDefinition' => $vendorDir . '/symfony/console/Input/InputDefinition.php',
'Symfony\\Component\\Console\\Input\\InputInterface' => $vendorDir . '/symfony/console/Input/InputInterface.php',
'Symfony\\Component\\Console\\Input\\InputOption' => $vendorDir . '/symfony/console/Input/InputOption.php',
'Symfony\\Component\\Console\\Input\\StreamableInputInterface' => $vendorDir . '/symfony/console/Input/StreamableInputInterface.php',
'Symfony\\Component\\Console\\Input\\StringInput' => $vendorDir . '/symfony/console/Input/StringInput.php',
'Symfony\\Component\\Console\\Logger\\ConsoleLogger' => $vendorDir . '/symfony/console/Logger/ConsoleLogger.php',
'Symfony\\Component\\Console\\Output\\BufferedOutput' => $vendorDir . '/symfony/console/Output/BufferedOutput.php',
'Symfony\\Component\\Console\\Output\\ConsoleOutput' => $vendorDir . '/symfony/console/Output/ConsoleOutput.php',
'Symfony\\Component\\Console\\Output\\ConsoleOutputInterface' => $vendorDir . '/symfony/console/Output/ConsoleOutputInterface.php',
'Symfony\\Component\\Console\\Output\\ConsoleSectionOutput' => $vendorDir . '/symfony/console/Output/ConsoleSectionOutput.php',
'Symfony\\Component\\Console\\Output\\NullOutput' => $vendorDir . '/symfony/console/Output/NullOutput.php',
'Symfony\\Component\\Console\\Output\\Output' => $vendorDir . '/symfony/console/Output/Output.php',
'Symfony\\Component\\Console\\Output\\OutputInterface' => $vendorDir . '/symfony/console/Output/OutputInterface.php',
'Symfony\\Component\\Console\\Output\\StreamOutput' => $vendorDir . '/symfony/console/Output/StreamOutput.php',
'Symfony\\Component\\Console\\Output\\TrimmedBufferOutput' => $vendorDir . '/symfony/console/Output/TrimmedBufferOutput.php',
'Symfony\\Component\\Console\\Question\\ChoiceQuestion' => $vendorDir . '/symfony/console/Question/ChoiceQuestion.php',
'Symfony\\Component\\Console\\Question\\ConfirmationQuestion' => $vendorDir . '/symfony/console/Question/ConfirmationQuestion.php',
'Symfony\\Component\\Console\\Question\\Question' => $vendorDir . '/symfony/console/Question/Question.php',
'Symfony\\Component\\Console\\SignalRegistry\\SignalRegistry' => $vendorDir . '/symfony/console/SignalRegistry/SignalRegistry.php',
'Symfony\\Component\\Console\\SingleCommandApplication' => $vendorDir . '/symfony/console/SingleCommandApplication.php',
'Symfony\\Component\\Console\\Style\\OutputStyle' => $vendorDir . '/symfony/console/Style/OutputStyle.php',
'Symfony\\Component\\Console\\Style\\StyleInterface' => $vendorDir . '/symfony/console/Style/StyleInterface.php',
'Symfony\\Component\\Console\\Style\\SymfonyStyle' => $vendorDir . '/symfony/console/Style/SymfonyStyle.php',
'Symfony\\Component\\Console\\Terminal' => $vendorDir . '/symfony/console/Terminal.php',
'Symfony\\Component\\Console\\Tester\\ApplicationTester' => $vendorDir . '/symfony/console/Tester/ApplicationTester.php',
'Symfony\\Component\\Console\\Tester\\CommandCompletionTester' => $vendorDir . '/symfony/console/Tester/CommandCompletionTester.php',
'Symfony\\Component\\Console\\Tester\\CommandTester' => $vendorDir . '/symfony/console/Tester/CommandTester.php',
'Symfony\\Component\\Console\\Tester\\Constraint\\CommandIsSuccessful' => $vendorDir . '/symfony/console/Tester/Constraint/CommandIsSuccessful.php',
'Symfony\\Component\\Console\\Tester\\TesterTrait' => $vendorDir . '/symfony/console/Tester/TesterTrait.php',
'Symfony\\Component\\EventDispatcher\\Attribute\\AsEventListener' => $vendorDir . '/symfony/event-dispatcher/Attribute/AsEventListener.php',
'Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher' => $vendorDir . '/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php',
'Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener' => $vendorDir . '/symfony/event-dispatcher/Debug/WrappedListener.php',
'Symfony\\Component\\EventDispatcher\\DependencyInjection\\AddEventAliasesPass' => $vendorDir . '/symfony/event-dispatcher/DependencyInjection/AddEventAliasesPass.php',
'Symfony\\Component\\EventDispatcher\\DependencyInjection\\RegisterListenersPass' => $vendorDir . '/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php',
'Symfony\\Component\\EventDispatcher\\EventDispatcher' => $vendorDir . '/symfony/event-dispatcher/EventDispatcher.php',
'Symfony\\Component\\EventDispatcher\\EventDispatcherInterface' => $vendorDir . '/symfony/event-dispatcher/EventDispatcherInterface.php',
'Symfony\\Component\\EventDispatcher\\EventSubscriberInterface' => $vendorDir . '/symfony/event-dispatcher/EventSubscriberInterface.php',
'Symfony\\Component\\EventDispatcher\\GenericEvent' => $vendorDir . '/symfony/event-dispatcher/GenericEvent.php',
'Symfony\\Component\\EventDispatcher\\ImmutableEventDispatcher' => $vendorDir . '/symfony/event-dispatcher/ImmutableEventDispatcher.php',
'Symfony\\Component\\EventDispatcher\\LegacyEventDispatcherProxy' => $vendorDir . '/symfony/event-dispatcher/LegacyEventDispatcherProxy.php',
'Symfony\\Component\\Filesystem\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/filesystem/Exception/ExceptionInterface.php',
'Symfony\\Component\\Filesystem\\Exception\\FileNotFoundException' => $vendorDir . '/symfony/filesystem/Exception/FileNotFoundException.php',
'Symfony\\Component\\Filesystem\\Exception\\IOException' => $vendorDir . '/symfony/filesystem/Exception/IOException.php',
'Symfony\\Component\\Filesystem\\Exception\\IOExceptionInterface' => $vendorDir . '/symfony/filesystem/Exception/IOExceptionInterface.php',
'Symfony\\Component\\Filesystem\\Exception\\InvalidArgumentException' => $vendorDir . '/symfony/filesystem/Exception/InvalidArgumentException.php',
'Symfony\\Component\\Filesystem\\Exception\\RuntimeException' => $vendorDir . '/symfony/filesystem/Exception/RuntimeException.php',
'Symfony\\Component\\Filesystem\\Filesystem' => $vendorDir . '/symfony/filesystem/Filesystem.php',
'Symfony\\Component\\Filesystem\\Path' => $vendorDir . '/symfony/filesystem/Path.php',
'Symfony\\Component\\Finder\\Comparator\\Comparator' => $vendorDir . '/symfony/finder/Comparator/Comparator.php',
'Symfony\\Component\\Finder\\Comparator\\DateComparator' => $vendorDir . '/symfony/finder/Comparator/DateComparator.php',
'Symfony\\Component\\Finder\\Comparator\\NumberComparator' => $vendorDir . '/symfony/finder/Comparator/NumberComparator.php',
'Symfony\\Component\\Finder\\Exception\\AccessDeniedException' => $vendorDir . '/symfony/finder/Exception/AccessDeniedException.php',
'Symfony\\Component\\Finder\\Exception\\DirectoryNotFoundException' => $vendorDir . '/symfony/finder/Exception/DirectoryNotFoundException.php',
'Symfony\\Component\\Finder\\Finder' => $vendorDir . '/symfony/finder/Finder.php',
'Symfony\\Component\\Finder\\Gitignore' => $vendorDir . '/symfony/finder/Gitignore.php',
'Symfony\\Component\\Finder\\Glob' => $vendorDir . '/symfony/finder/Glob.php',
'Symfony\\Component\\Finder\\Iterator\\CustomFilterIterator' => $vendorDir . '/symfony/finder/Iterator/CustomFilterIterator.php',
'Symfony\\Component\\Finder\\Iterator\\DateRangeFilterIterator' => $vendorDir . '/symfony/finder/Iterator/DateRangeFilterIterator.php',
'Symfony\\Component\\Finder\\Iterator\\DepthRangeFilterIterator' => $vendorDir . '/symfony/finder/Iterator/DepthRangeFilterIterator.php',
'Symfony\\Component\\Finder\\Iterator\\ExcludeDirectoryFilterIterator' => $vendorDir . '/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php',
'Symfony\\Component\\Finder\\Iterator\\FileTypeFilterIterator' => $vendorDir . '/symfony/finder/Iterator/FileTypeFilterIterator.php',
'Symfony\\Component\\Finder\\Iterator\\FilecontentFilterIterator' => $vendorDir . '/symfony/finder/Iterator/FilecontentFilterIterator.php',
'Symfony\\Component\\Finder\\Iterator\\FilenameFilterIterator' => $vendorDir . '/symfony/finder/Iterator/FilenameFilterIterator.php',
'Symfony\\Component\\Finder\\Iterator\\LazyIterator' => $vendorDir . '/symfony/finder/Iterator/LazyIterator.php',
'Symfony\\Component\\Finder\\Iterator\\MultiplePcreFilterIterator' => $vendorDir . '/symfony/finder/Iterator/MultiplePcreFilterIterator.php',
'Symfony\\Component\\Finder\\Iterator\\PathFilterIterator' => $vendorDir . '/symfony/finder/Iterator/PathFilterIterator.php',
'Symfony\\Component\\Finder\\Iterator\\RecursiveDirectoryIterator' => $vendorDir . '/symfony/finder/Iterator/RecursiveDirectoryIterator.php',
'Symfony\\Component\\Finder\\Iterator\\SizeRangeFilterIterator' => $vendorDir . '/symfony/finder/Iterator/SizeRangeFilterIterator.php',
'Symfony\\Component\\Finder\\Iterator\\SortableIterator' => $vendorDir . '/symfony/finder/Iterator/SortableIterator.php',
'Symfony\\Component\\Finder\\Iterator\\VcsIgnoredFilterIterator' => $vendorDir . '/symfony/finder/Iterator/VcsIgnoredFilterIterator.php',
'Symfony\\Component\\Finder\\SplFileInfo' => $vendorDir . '/symfony/finder/SplFileInfo.php',
'Symfony\\Component\\OptionsResolver\\Debug\\OptionsResolverIntrospector' => $vendorDir . '/symfony/options-resolver/Debug/OptionsResolverIntrospector.php',
'Symfony\\Component\\OptionsResolver\\Exception\\AccessException' => $vendorDir . '/symfony/options-resolver/Exception/AccessException.php',
'Symfony\\Component\\OptionsResolver\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/options-resolver/Exception/ExceptionInterface.php',
'Symfony\\Component\\OptionsResolver\\Exception\\InvalidArgumentException' => $vendorDir . '/symfony/options-resolver/Exception/InvalidArgumentException.php',
'Symfony\\Component\\OptionsResolver\\Exception\\InvalidOptionsException' => $vendorDir . '/symfony/options-resolver/Exception/InvalidOptionsException.php',
'Symfony\\Component\\OptionsResolver\\Exception\\MissingOptionsException' => $vendorDir . '/symfony/options-resolver/Exception/MissingOptionsException.php',
'Symfony\\Component\\OptionsResolver\\Exception\\NoConfigurationException' => $vendorDir . '/symfony/options-resolver/Exception/NoConfigurationException.php',
'Symfony\\Component\\OptionsResolver\\Exception\\NoSuchOptionException' => $vendorDir . '/symfony/options-resolver/Exception/NoSuchOptionException.php',
'Symfony\\Component\\OptionsResolver\\Exception\\OptionDefinitionException' => $vendorDir . '/symfony/options-resolver/Exception/OptionDefinitionException.php',
'Symfony\\Component\\OptionsResolver\\Exception\\UndefinedOptionsException' => $vendorDir . '/symfony/options-resolver/Exception/UndefinedOptionsException.php',
'Symfony\\Component\\OptionsResolver\\OptionConfigurator' => $vendorDir . '/symfony/options-resolver/OptionConfigurator.php',
'Symfony\\Component\\OptionsResolver\\Options' => $vendorDir . '/symfony/options-resolver/Options.php',
'Symfony\\Component\\OptionsResolver\\OptionsResolver' => $vendorDir . '/symfony/options-resolver/OptionsResolver.php',
'Symfony\\Component\\Process\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/process/Exception/ExceptionInterface.php',
'Symfony\\Component\\Process\\Exception\\InvalidArgumentException' => $vendorDir . '/symfony/process/Exception/InvalidArgumentException.php',
'Symfony\\Component\\Process\\Exception\\LogicException' => $vendorDir . '/symfony/process/Exception/LogicException.php',
'Symfony\\Component\\Process\\Exception\\ProcessFailedException' => $vendorDir . '/symfony/process/Exception/ProcessFailedException.php',
'Symfony\\Component\\Process\\Exception\\ProcessSignaledException' => $vendorDir . '/symfony/process/Exception/ProcessSignaledException.php',
'Symfony\\Component\\Process\\Exception\\ProcessTimedOutException' => $vendorDir . '/symfony/process/Exception/ProcessTimedOutException.php',
'Symfony\\Component\\Process\\Exception\\RuntimeException' => $vendorDir . '/symfony/process/Exception/RuntimeException.php',
'Symfony\\Component\\Process\\ExecutableFinder' => $vendorDir . '/symfony/process/ExecutableFinder.php',
'Symfony\\Component\\Process\\InputStream' => $vendorDir . '/symfony/process/InputStream.php',
'Symfony\\Component\\Process\\PhpExecutableFinder' => $vendorDir . '/symfony/process/PhpExecutableFinder.php',
'Symfony\\Component\\Process\\PhpProcess' => $vendorDir . '/symfony/process/PhpProcess.php',
'Symfony\\Component\\Process\\Pipes\\AbstractPipes' => $vendorDir . '/symfony/process/Pipes/AbstractPipes.php',
'Symfony\\Component\\Process\\Pipes\\PipesInterface' => $vendorDir . '/symfony/process/Pipes/PipesInterface.php',
'Symfony\\Component\\Process\\Pipes\\UnixPipes' => $vendorDir . '/symfony/process/Pipes/UnixPipes.php',
'Symfony\\Component\\Process\\Pipes\\WindowsPipes' => $vendorDir . '/symfony/process/Pipes/WindowsPipes.php',
'Symfony\\Component\\Process\\Process' => $vendorDir . '/symfony/process/Process.php',
'Symfony\\Component\\Process\\ProcessUtils' => $vendorDir . '/symfony/process/ProcessUtils.php',
'Symfony\\Component\\Stopwatch\\Section' => $vendorDir . '/symfony/stopwatch/Section.php',
'Symfony\\Component\\Stopwatch\\Stopwatch' => $vendorDir . '/symfony/stopwatch/Stopwatch.php',
'Symfony\\Component\\Stopwatch\\StopwatchEvent' => $vendorDir . '/symfony/stopwatch/StopwatchEvent.php',
'Symfony\\Component\\Stopwatch\\StopwatchPeriod' => $vendorDir . '/symfony/stopwatch/StopwatchPeriod.php',
'Symfony\\Component\\String\\AbstractString' => $vendorDir . '/symfony/string/AbstractString.php',
'Symfony\\Component\\String\\AbstractUnicodeString' => $vendorDir . '/symfony/string/AbstractUnicodeString.php',
'Symfony\\Component\\String\\ByteString' => $vendorDir . '/symfony/string/ByteString.php',
'Symfony\\Component\\String\\CodePointString' => $vendorDir . '/symfony/string/CodePointString.php',
'Symfony\\Component\\String\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/string/Exception/ExceptionInterface.php',
'Symfony\\Component\\String\\Exception\\InvalidArgumentException' => $vendorDir . '/symfony/string/Exception/InvalidArgumentException.php',
'Symfony\\Component\\String\\Exception\\RuntimeException' => $vendorDir . '/symfony/string/Exception/RuntimeException.php',
'Symfony\\Component\\String\\Inflector\\EnglishInflector' => $vendorDir . '/symfony/string/Inflector/EnglishInflector.php',
'Symfony\\Component\\String\\Inflector\\FrenchInflector' => $vendorDir . '/symfony/string/Inflector/FrenchInflector.php',
'Symfony\\Component\\String\\Inflector\\InflectorInterface' => $vendorDir . '/symfony/string/Inflector/InflectorInterface.php',
'Symfony\\Component\\String\\LazyString' => $vendorDir . '/symfony/string/LazyString.php',
'Symfony\\Component\\String\\Slugger\\AsciiSlugger' => $vendorDir . '/symfony/string/Slugger/AsciiSlugger.php',
'Symfony\\Component\\String\\Slugger\\SluggerInterface' => $vendorDir . '/symfony/string/Slugger/SluggerInterface.php',
'Symfony\\Component\\String\\UnicodeString' => $vendorDir . '/symfony/string/UnicodeString.php',
'Symfony\\Contracts\\EventDispatcher\\Event' => $vendorDir . '/symfony/event-dispatcher-contracts/Event.php',
'Symfony\\Contracts\\EventDispatcher\\EventDispatcherInterface' => $vendorDir . '/symfony/event-dispatcher-contracts/EventDispatcherInterface.php',
'Symfony\\Contracts\\Service\\Attribute\\Required' => $vendorDir . '/symfony/service-contracts/Attribute/Required.php',
'Symfony\\Contracts\\Service\\Attribute\\SubscribedService' => $vendorDir . '/symfony/service-contracts/Attribute/SubscribedService.php',
'Symfony\\Contracts\\Service\\ResetInterface' => $vendorDir . '/symfony/service-contracts/ResetInterface.php',
'Symfony\\Contracts\\Service\\ServiceLocatorTrait' => $vendorDir . '/symfony/service-contracts/ServiceLocatorTrait.php',
'Symfony\\Contracts\\Service\\ServiceProviderInterface' => $vendorDir . '/symfony/service-contracts/ServiceProviderInterface.php',
'Symfony\\Contracts\\Service\\ServiceSubscriberInterface' => $vendorDir . '/symfony/service-contracts/ServiceSubscriberInterface.php',
'Symfony\\Contracts\\Service\\ServiceSubscriberTrait' => $vendorDir . '/symfony/service-contracts/ServiceSubscriberTrait.php',
'Symfony\\Contracts\\Service\\Test\\ServiceLocatorTest' => $vendorDir . '/symfony/service-contracts/Test/ServiceLocatorTest.php',
'Symfony\\Polyfill\\Ctype\\Ctype' => $vendorDir . '/symfony/polyfill-ctype/Ctype.php',
'Symfony\\Polyfill\\Intl\\Grapheme\\Grapheme' => $vendorDir . '/symfony/polyfill-intl-grapheme/Grapheme.php',
'Symfony\\Polyfill\\Intl\\Normalizer\\Normalizer' => $vendorDir . '/symfony/polyfill-intl-normalizer/Normalizer.php',
'Symfony\\Polyfill\\Mbstring\\Mbstring' => $vendorDir . '/symfony/polyfill-mbstring/Mbstring.php',
'Symfony\\Polyfill\\Php73\\Php73' => $vendorDir . '/symfony/polyfill-php73/Php73.php',
'Symfony\\Polyfill\\Php80\\Php80' => $vendorDir . '/symfony/polyfill-php80/Php80.php',
'Symfony\\Polyfill\\Php80\\PhpToken' => $vendorDir . '/symfony/polyfill-php80/PhpToken.php',
'Symfony\\Polyfill\\Php81\\Php81' => $vendorDir . '/symfony/polyfill-php81/Php81.php',
'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
);
<?php
$issues = array();
if (!(PHP_VERSION_ID >= 70400)) {
$issues[] = 'Your Composer dependencies require a PHP version ">= 7.4.0". You are running ' . PHP_VERSION . '.';
}
if ($issues) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
} elseif (!headers_sent()) {
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
}
}
trigger_error(
'Composer detected issues in your platform: ' . implode(' ', $issues),
E_USER_ERROR
);
}
<?php
namespace Composer\Autoload;
class ComposerStaticInit43f33e2f7f39ed6f3341269681ebd52e
{
public static $files = array (
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
'8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php',
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'0d59ee240a4cd96ddbb4ff164fccea4d' => __DIR__ . '/..' . '/symfony/polyfill-php73/bootstrap.php',
'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php',
'23c18046f52bef3eea034657bafda50f' => __DIR__ . '/..' . '/symfony/polyfill-php81/bootstrap.php',
);
public static $prefixLengthsPsr4 = array (
'S' =>
array (
'Symfony\\Polyfill\\Php81\\' => 23,
'Symfony\\Polyfill\\Php80\\' => 23,
'Symfony\\Polyfill\\Php73\\' => 23,
'Symfony\\Polyfill\\Mbstring\\' => 26,
'Symfony\\Polyfill\\Intl\\Normalizer\\' => 33,
'Symfony\\Polyfill\\Intl\\Grapheme\\' => 31,
'Symfony\\Polyfill\\Ctype\\' => 23,
'Symfony\\Contracts\\Service\\' => 26,
'Symfony\\Contracts\\EventDispatcher\\' => 34,
'Symfony\\Component\\String\\' => 25,
'Symfony\\Component\\Stopwatch\\' => 28,
'Symfony\\Component\\Process\\' => 26,
'Symfony\\Component\\OptionsResolver\\' => 34,
'Symfony\\Component\\Finder\\' => 25,
'Symfony\\Component\\Filesystem\\' => 29,
'Symfony\\Component\\EventDispatcher\\' => 34,
'Symfony\\Component\\Console\\' => 26,
),
'P' =>
array (
'Psr\\Log\\' => 8,
'Psr\\EventDispatcher\\' => 20,
'Psr\\Container\\' => 14,
'Psr\\Cache\\' => 10,
'PhpCsFixer\\' => 11,
),
'D' =>
array (
'Doctrine\\Common\\Lexer\\' => 22,
'Doctrine\\Common\\Annotations\\' => 28,
),
'C' =>
array (
'Composer\\XdebugHandler\\' => 23,
'Composer\\Semver\\' => 16,
'Composer\\Pcre\\' => 14,
),
);
public static $prefixDirsPsr4 = array (
'Symfony\\Polyfill\\Php81\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-php81',
),
'Symfony\\Polyfill\\Php80\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-php80',
),
'Symfony\\Polyfill\\Php73\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-php73',
),
'Symfony\\Polyfill\\Mbstring\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
),
'Symfony\\Polyfill\\Intl\\Normalizer\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer',
),
'Symfony\\Polyfill\\Intl\\Grapheme\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme',
),
'Symfony\\Polyfill\\Ctype\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-ctype',
),
'Symfony\\Contracts\\Service\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/service-contracts',
),
'Symfony\\Contracts\\EventDispatcher\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/event-dispatcher-contracts',
),
'Symfony\\Component\\String\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/string',
),
'Symfony\\Component\\Stopwatch\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/stopwatch',
),
'Symfony\\Component\\Process\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/process',
),
'Symfony\\Component\\OptionsResolver\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/options-resolver',
),
'Symfony\\Component\\Finder\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/finder',
),
'Symfony\\Component\\Filesystem\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/filesystem',
),
'Symfony\\Component\\EventDispatcher\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/event-dispatcher',
),
'Symfony\\Component\\Console\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/console',
),
'Psr\\Log\\' =>
array (
0 => __DIR__ . '/..' . '/psr/log/Psr/Log',
),
'Psr\\EventDispatcher\\' =>
array (
0 => __DIR__ . '/..' . '/psr/event-dispatcher/src',
),
'Psr\\Container\\' =>
array (
0 => __DIR__ . '/..' . '/psr/container/src',
),
'Psr\\Cache\\' =>
array (
0 => __DIR__ . '/..' . '/psr/cache/src',
),
'PhpCsFixer\\' =>
array (
0 => __DIR__ . '/../..' . '/src',
),
'Doctrine\\Common\\Lexer\\' =>
array (
0 => __DIR__ . '/..' . '/doctrine/lexer/lib/Doctrine/Common/Lexer',
),
'Doctrine\\Common\\Annotations\\' =>
array (
0 => __DIR__ . '/..' . '/doctrine/annotations/lib/Doctrine/Common/Annotations',
),
'Composer\\XdebugHandler\\' =>
array (
0 => __DIR__ . '/..' . '/composer/xdebug-handler/src',
),
'Composer\\Semver\\' =>
array (
0 => __DIR__ . '/..' . '/composer/semver/src',
),
'Composer\\Pcre\\' =>
array (
0 => __DIR__ . '/..' . '/composer/pcre/src',
),
);
public static $classMap = array (
'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
'Composer\\Pcre\\MatchAllResult' => __DIR__ . '/..' . '/composer/pcre/src/MatchAllResult.php',
'Composer\\Pcre\\MatchAllWithOffsetsResult' => __DIR__ . '/..' . '/composer/pcre/src/MatchAllWithOffsetsResult.php',
'Composer\\Pcre\\MatchResult' => __DIR__ . '/..' . '/composer/pcre/src/MatchResult.php',
'Composer\\Pcre\\MatchWithOffsetsResult' => __DIR__ . '/..' . '/composer/pcre/src/MatchWithOffsetsResult.php',
'Composer\\Pcre\\PcreException' => __DIR__ . '/..' . '/composer/pcre/src/PcreException.php',
'Composer\\Pcre\\Preg' => __DIR__ . '/..' . '/composer/pcre/src/Preg.php',
'Composer\\Pcre\\Regex' => __DIR__ . '/..' . '/composer/pcre/src/Regex.php',
'Composer\\Pcre\\ReplaceResult' => __DIR__ . '/..' . '/composer/pcre/src/ReplaceResult.php',
'Composer\\Semver\\Comparator' => __DIR__ . '/..' . '/composer/semver/src/Comparator.php',
'Composer\\Semver\\CompilingMatcher' => __DIR__ . '/..' . '/composer/semver/src/CompilingMatcher.php',
'Composer\\Semver\\Constraint\\Bound' => __DIR__ . '/..' . '/composer/semver/src/Constraint/Bound.php',
'Composer\\Semver\\Constraint\\Constraint' => __DIR__ . '/..' . '/composer/semver/src/Constraint/Constraint.php',
'Composer\\Semver\\Constraint\\ConstraintInterface' => __DIR__ . '/..' . '/composer/semver/src/Constraint/ConstraintInterface.php',
'Composer\\Semver\\Constraint\\MatchAllConstraint' => __DIR__ . '/..' . '/composer/semver/src/Constraint/MatchAllConstraint.php',
'Composer\\Semver\\Constraint\\MatchNoneConstraint' => __DIR__ . '/..' . '/composer/semver/src/Constraint/MatchNoneConstraint.php',
'Composer\\Semver\\Constraint\\MultiConstraint' => __DIR__ . '/..' . '/composer/semver/src/Constraint/MultiConstraint.php',
'Composer\\Semver\\Interval' => __DIR__ . '/..' . '/composer/semver/src/Interval.php',
'Composer\\Semver\\Intervals' => __DIR__ . '/..' . '/composer/semver/src/Intervals.php',
'Composer\\Semver\\Semver' => __DIR__ . '/..' . '/composer/semver/src/Semver.php',
'Composer\\Semver\\VersionParser' => __DIR__ . '/..' . '/composer/semver/src/VersionParser.php',
'Composer\\XdebugHandler\\PhpConfig' => __DIR__ . '/..' . '/composer/xdebug-handler/src/PhpConfig.php',
'Composer\\XdebugHandler\\Process' => __DIR__ . '/..' . '/composer/xdebug-handler/src/Process.php',
'Composer\\XdebugHandler\\Status' => __DIR__ . '/..' . '/composer/xdebug-handler/src/Status.php',
'Composer\\XdebugHandler\\XdebugHandler' => __DIR__ . '/..' . '/composer/xdebug-handler/src/XdebugHandler.php',
'Doctrine\\Common\\Annotations\\Annotation' => __DIR__ . '/..' . '/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation.php',
'Doctrine\\Common\\Annotations\\AnnotationException' => __DIR__ . '/..' . '/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationException.php',
'Doctrine\\Common\\Annotations\\AnnotationReader' => __DIR__ . '/..' . '/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationReader.php',
'Doctrine\\Common\\Annotations\\AnnotationRegistry' => __DIR__ . '/..' . '/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationRegistry.php',
'Doctrine\\Common\\Annotations\\Annotation\\Attribute' => __DIR__ . '/..' . '/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attribute.php',
'Doctrine\\Common\\Annotations\\Annotation\\Attributes' => __DIR__ . '/..' . '/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attributes.php',
'Doctrine\\Common\\Annotations\\Annotation\\Enum' => __DIR__ . '/..' . '/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Enum.php',
'Doctrine\\Common\\Annotations\\Annotation\\IgnoreAnnotation' => __DIR__ . '/..' . '/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.php',
'Doctrine\\Common\\Annotations\\Annotation\\NamedArgumentConstructor' => __DIR__ . '/..' . '/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/NamedArgumentConstructor.php',
'Doctrine\\Common\\Annotations\\Annotation\\Required' => __DIR__ . '/..' . '/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Required.php',
'Doctrine\\Common\\Annotations\\Annotation\\Target' => __DIR__ . '/..' . '/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Target.php',
'Doctrine\\Common\\Annotations\\CachedReader' => __DIR__ . '/..' . '/doctrine/annotations/lib/Doctrine/Common/Annotations/CachedReader.php',
'Doctrine\\Common\\Annotations\\DocLexer' => __DIR__ . '/..' . '/doctrine/annotations/lib/Doctrine/Common/Annotations/DocLexer.php',
'Doctrine\\Common\\Annotations\\DocParser' => __DIR__ . '/..' . '/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php',
'Doctrine\\Common\\Annotations\\FileCacheReader' => __DIR__ . '/..' . '/doctrine/annotations/lib/Doctrine/Common/Annotations/FileCacheReader.php',
'Doctrine\\Common\\Annotations\\ImplicitlyIgnoredAnnotationNames' => __DIR__ . '/..' . '/doctrine/annotations/lib/Doctrine/Common/Annotations/ImplicitlyIgnoredAnnotationNames.php',
'Doctrine\\Common\\Annotations\\IndexedReader' => __DIR__ . '/..' . '/doctrine/annotations/lib/Doctrine/Common/Annotations/IndexedReader.php',
'Doctrine\\Common\\Annotations\\NamedArgumentConstructorAnnotation' => __DIR__ . '/..' . '/doctrine/annotations/lib/Doctrine/Common/Annotations/NamedArgumentConstructorAnnotation.php',
'Doctrine\\Common\\Annotations\\PhpParser' => __DIR__ . '/..' . '/doctrine/annotations/lib/Doctrine/Common/Annotations/PhpParser.php',
'Doctrine\\Common\\Annotations\\PsrCachedReader' => __DIR__ . '/..' . '/doctrine/annotations/lib/Doctrine/Common/Annotations/PsrCachedReader.php',
'Doctrine\\Common\\Annotations\\Reader' => __DIR__ . '/..' . '/doctrine/annotations/lib/Doctrine/Common/Annotations/Reader.php',
'Doctrine\\Common\\Annotations\\SimpleAnnotationReader' => __DIR__ . '/..' . '/doctrine/annotations/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php',
'Doctrine\\Common\\Annotations\\TokenParser' => __DIR__ . '/..' . '/doctrine/annotations/lib/Doctrine/Common/Annotations/TokenParser.php',
'Doctrine\\Common\\Lexer\\AbstractLexer' => __DIR__ . '/..' . '/doctrine/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.php',
'JsonException' => __DIR__ . '/..' . '/symfony/polyfill-php73/Resources/stubs/JsonException.php',
'Normalizer' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php',
'PhpCsFixer\\AbstractDoctrineAnnotationFixer' => __DIR__ . '/../..' . '/src/AbstractDoctrineAnnotationFixer.php',
'PhpCsFixer\\AbstractFixer' => __DIR__ . '/../..' . '/src/AbstractFixer.php',
'PhpCsFixer\\AbstractFopenFlagFixer' => __DIR__ . '/../..' . '/src/AbstractFopenFlagFixer.php',
'PhpCsFixer\\AbstractFunctionReferenceFixer' => __DIR__ . '/../..' . '/src/AbstractFunctionReferenceFixer.php',
'PhpCsFixer\\AbstractLinesBeforeNamespaceFixer' => __DIR__ . '/../..' . '/src/AbstractLinesBeforeNamespaceFixer.php',
'PhpCsFixer\\AbstractNoUselessElseFixer' => __DIR__ . '/../..' . '/src/AbstractNoUselessElseFixer.php',
'PhpCsFixer\\AbstractPhpdocToTypeDeclarationFixer' => __DIR__ . '/../..' . '/src/AbstractPhpdocToTypeDeclarationFixer.php',
'PhpCsFixer\\AbstractPhpdocTypesFixer' => __DIR__ . '/../..' . '/src/AbstractPhpdocTypesFixer.php',
'PhpCsFixer\\AbstractProxyFixer' => __DIR__ . '/../..' . '/src/AbstractProxyFixer.php',
'PhpCsFixer\\Cache\\Cache' => __DIR__ . '/../..' . '/src/Cache/Cache.php',
'PhpCsFixer\\Cache\\CacheInterface' => __DIR__ . '/../..' . '/src/Cache/CacheInterface.php',
'PhpCsFixer\\Cache\\CacheManagerInterface' => __DIR__ . '/../..' . '/src/Cache/CacheManagerInterface.php',
'PhpCsFixer\\Cache\\Directory' => __DIR__ . '/../..' . '/src/Cache/Directory.php',
'PhpCsFixer\\Cache\\DirectoryInterface' => __DIR__ . '/../..' . '/src/Cache/DirectoryInterface.php',
'PhpCsFixer\\Cache\\FileCacheManager' => __DIR__ . '/../..' . '/src/Cache/FileCacheManager.php',
'PhpCsFixer\\Cache\\FileHandler' => __DIR__ . '/../..' . '/src/Cache/FileHandler.php',
'PhpCsFixer\\Cache\\FileHandlerInterface' => __DIR__ . '/../..' . '/src/Cache/FileHandlerInterface.php',
'PhpCsFixer\\Cache\\NullCacheManager' => __DIR__ . '/../..' . '/src/Cache/NullCacheManager.php',
'PhpCsFixer\\Cache\\Signature' => __DIR__ . '/../..' . '/src/Cache/Signature.php',
'PhpCsFixer\\Cache\\SignatureInterface' => __DIR__ . '/../..' . '/src/Cache/SignatureInterface.php',
'PhpCsFixer\\Config' => __DIR__ . '/../..' . '/src/Config.php',
'PhpCsFixer\\ConfigInterface' => __DIR__ . '/../..' . '/src/ConfigInterface.php',
'PhpCsFixer\\ConfigurationException\\InvalidConfigurationException' => __DIR__ . '/../..' . '/src/ConfigurationException/InvalidConfigurationException.php',
'PhpCsFixer\\ConfigurationException\\InvalidFixerConfigurationException' => __DIR__ . '/../..' . '/src/ConfigurationException/InvalidFixerConfigurationException.php',
'PhpCsFixer\\ConfigurationException\\InvalidForEnvFixerConfigurationException' => __DIR__ . '/../..' . '/src/ConfigurationException/InvalidForEnvFixerConfigurationException.php',
'PhpCsFixer\\ConfigurationException\\RequiredFixerConfigurationException' => __DIR__ . '/../..' . '/src/ConfigurationException/RequiredFixerConfigurationException.php',
'PhpCsFixer\\Console\\Application' => __DIR__ . '/../..' . '/src/Console/Application.php',
'PhpCsFixer\\Console\\Command\\DescribeCommand' => __DIR__ . '/../..' . '/src/Console/Command/DescribeCommand.php',
'PhpCsFixer\\Console\\Command\\DescribeNameNotFoundException' => __DIR__ . '/../..' . '/src/Console/Command/DescribeNameNotFoundException.php',
'PhpCsFixer\\Console\\Command\\DocumentationCommand' => __DIR__ . '/../..' . '/src/Console/Command/DocumentationCommand.php',
'PhpCsFixer\\Console\\Command\\FixCommand' => __DIR__ . '/../..' . '/src/Console/Command/FixCommand.php',
'PhpCsFixer\\Console\\Command\\FixCommandExitStatusCalculator' => __DIR__ . '/../..' . '/src/Console/Command/FixCommandExitStatusCalculator.php',
'PhpCsFixer\\Console\\Command\\HelpCommand' => __DIR__ . '/../..' . '/src/Console/Command/HelpCommand.php',
'PhpCsFixer\\Console\\Command\\ListFilesCommand' => __DIR__ . '/../..' . '/src/Console/Command/ListFilesCommand.php',
'PhpCsFixer\\Console\\Command\\ListSetsCommand' => __DIR__ . '/../..' . '/src/Console/Command/ListSetsCommand.php',
'PhpCsFixer\\Console\\Command\\SelfUpdateCommand' => __DIR__ . '/../..' . '/src/Console/Command/SelfUpdateCommand.php',
'PhpCsFixer\\Console\\ConfigurationResolver' => __DIR__ . '/../..' . '/src/Console/ConfigurationResolver.php',
'PhpCsFixer\\Console\\Output\\ErrorOutput' => __DIR__ . '/../..' . '/src/Console/Output/ErrorOutput.php',
'PhpCsFixer\\Console\\Output\\NullOutput' => __DIR__ . '/../..' . '/src/Console/Output/NullOutput.php',
'PhpCsFixer\\Console\\Output\\ProcessOutput' => __DIR__ . '/../..' . '/src/Console/Output/ProcessOutput.php',
'PhpCsFixer\\Console\\Output\\ProcessOutputInterface' => __DIR__ . '/../..' . '/src/Console/Output/ProcessOutputInterface.php',
'PhpCsFixer\\Console\\Report\\FixReport\\CheckstyleReporter' => __DIR__ . '/../..' . '/src/Console/Report/FixReport/CheckstyleReporter.php',
'PhpCsFixer\\Console\\Report\\FixReport\\GitlabReporter' => __DIR__ . '/../..' . '/src/Console/Report/FixReport/GitlabReporter.php',
'PhpCsFixer\\Console\\Report\\FixReport\\JsonReporter' => __DIR__ . '/../..' . '/src/Console/Report/FixReport/JsonReporter.php',
'PhpCsFixer\\Console\\Report\\FixReport\\JunitReporter' => __DIR__ . '/../..' . '/src/Console/Report/FixReport/JunitReporter.php',
'PhpCsFixer\\Console\\Report\\FixReport\\ReportSummary' => __DIR__ . '/../..' . '/src/Console/Report/FixReport/ReportSummary.php',
'PhpCsFixer\\Console\\Report\\FixReport\\ReporterFactory' => __DIR__ . '/../..' . '/src/Console/Report/FixReport/ReporterFactory.php',
'PhpCsFixer\\Console\\Report\\FixReport\\ReporterInterface' => __DIR__ . '/../..' . '/src/Console/Report/FixReport/ReporterInterface.php',
'PhpCsFixer\\Console\\Report\\FixReport\\TextReporter' => __DIR__ . '/../..' . '/src/Console/Report/FixReport/TextReporter.php',
'PhpCsFixer\\Console\\Report\\FixReport\\XmlReporter' => __DIR__ . '/../..' . '/src/Console/Report/FixReport/XmlReporter.php',
'PhpCsFixer\\Console\\Report\\ListSetsReport\\JsonReporter' => __DIR__ . '/../..' . '/src/Console/Report/ListSetsReport/JsonReporter.php',
'PhpCsFixer\\Console\\Report\\ListSetsReport\\ReportSummary' => __DIR__ . '/../..' . '/src/Console/Report/ListSetsReport/ReportSummary.php',
'PhpCsFixer\\Console\\Report\\ListSetsReport\\ReporterFactory' => __DIR__ . '/../..' . '/src/Console/Report/ListSetsReport/ReporterFactory.php',
'PhpCsFixer\\Console\\Report\\ListSetsReport\\ReporterInterface' => __DIR__ . '/../..' . '/src/Console/Report/ListSetsReport/ReporterInterface.php',
'PhpCsFixer\\Console\\Report\\ListSetsReport\\TextReporter' => __DIR__ . '/../..' . '/src/Console/Report/ListSetsReport/TextReporter.php',
'PhpCsFixer\\Console\\SelfUpdate\\GithubClient' => __DIR__ . '/../..' . '/src/Console/SelfUpdate/GithubClient.php',
'PhpCsFixer\\Console\\SelfUpdate\\GithubClientInterface' => __DIR__ . '/../..' . '/src/Console/SelfUpdate/GithubClientInterface.php',
'PhpCsFixer\\Console\\SelfUpdate\\NewVersionChecker' => __DIR__ . '/../..' . '/src/Console/SelfUpdate/NewVersionChecker.php',
'PhpCsFixer\\Console\\SelfUpdate\\NewVersionCheckerInterface' => __DIR__ . '/../..' . '/src/Console/SelfUpdate/NewVersionCheckerInterface.php',
'PhpCsFixer\\Console\\WarningsDetector' => __DIR__ . '/../..' . '/src/Console/WarningsDetector.php',
'PhpCsFixer\\Differ\\DiffConsoleFormatter' => __DIR__ . '/../..' . '/src/Differ/DiffConsoleFormatter.php',
'PhpCsFixer\\Differ\\DifferInterface' => __DIR__ . '/../..' . '/src/Differ/DifferInterface.php',
'PhpCsFixer\\Differ\\FullDiffer' => __DIR__ . '/../..' . '/src/Differ/FullDiffer.php',
'PhpCsFixer\\Differ\\NullDiffer' => __DIR__ . '/../..' . '/src/Differ/NullDiffer.php',
'PhpCsFixer\\Differ\\UnifiedDiffer' => __DIR__ . '/../..' . '/src/Differ/UnifiedDiffer.php',
'PhpCsFixer\\DocBlock\\Annotation' => __DIR__ . '/../..' . '/src/DocBlock/Annotation.php',
'PhpCsFixer\\DocBlock\\DocBlock' => __DIR__ . '/../..' . '/src/DocBlock/DocBlock.php',
'PhpCsFixer\\DocBlock\\Line' => __DIR__ . '/../..' . '/src/DocBlock/Line.php',
'PhpCsFixer\\DocBlock\\ShortDescription' => __DIR__ . '/../..' . '/src/DocBlock/ShortDescription.php',
'PhpCsFixer\\DocBlock\\Tag' => __DIR__ . '/../..' . '/src/DocBlock/Tag.php',
'PhpCsFixer\\DocBlock\\TagComparator' => __DIR__ . '/../..' . '/src/DocBlock/TagComparator.php',
'PhpCsFixer\\DocBlock\\TypeExpression' => __DIR__ . '/../..' . '/src/DocBlock/TypeExpression.php',
'PhpCsFixer\\Doctrine\\Annotation\\Token' => __DIR__ . '/../..' . '/src/Doctrine/Annotation/Token.php',
'PhpCsFixer\\Doctrine\\Annotation\\Tokens' => __DIR__ . '/../..' . '/src/Doctrine/Annotation/Tokens.php',
'PhpCsFixer\\Documentation\\DocumentationLocator' => __DIR__ . '/../..' . '/src/Documentation/DocumentationLocator.php',
'PhpCsFixer\\Documentation\\FixerDocumentGenerator' => __DIR__ . '/../..' . '/src/Documentation/FixerDocumentGenerator.php',
'PhpCsFixer\\Documentation\\ListDocumentGenerator' => __DIR__ . '/../..' . '/src/Documentation/ListDocumentGenerator.php',
'PhpCsFixer\\Documentation\\RstUtils' => __DIR__ . '/../..' . '/src/Documentation/RstUtils.php',
'PhpCsFixer\\Documentation\\RuleSetDocumentationGenerator' => __DIR__ . '/../..' . '/src/Documentation/RuleSetDocumentationGenerator.php',
'PhpCsFixer\\Error\\Error' => __DIR__ . '/../..' . '/src/Error/Error.php',
'PhpCsFixer\\Error\\ErrorsManager' => __DIR__ . '/../..' . '/src/Error/ErrorsManager.php',
'PhpCsFixer\\FileReader' => __DIR__ . '/../..' . '/src/FileReader.php',
'PhpCsFixer\\FileRemoval' => __DIR__ . '/../..' . '/src/FileRemoval.php',
'PhpCsFixer\\Finder' => __DIR__ . '/../..' . '/src/Finder.php',
'PhpCsFixer\\FixerConfiguration\\AliasedFixerOption' => __DIR__ . '/../..' . '/src/FixerConfiguration/AliasedFixerOption.php',
'PhpCsFixer\\FixerConfiguration\\AliasedFixerOptionBuilder' => __DIR__ . '/../..' . '/src/FixerConfiguration/AliasedFixerOptionBuilder.php',
'PhpCsFixer\\FixerConfiguration\\AllowedValueSubset' => __DIR__ . '/../..' . '/src/FixerConfiguration/AllowedValueSubset.php',
'PhpCsFixer\\FixerConfiguration\\DeprecatedFixerOption' => __DIR__ . '/../..' . '/src/FixerConfiguration/DeprecatedFixerOption.php',
'PhpCsFixer\\FixerConfiguration\\DeprecatedFixerOptionInterface' => __DIR__ . '/../..' . '/src/FixerConfiguration/DeprecatedFixerOptionInterface.php',
'PhpCsFixer\\FixerConfiguration\\FixerConfigurationResolver' => __DIR__ . '/../..' . '/src/FixerConfiguration/FixerConfigurationResolver.php',
'PhpCsFixer\\FixerConfiguration\\FixerConfigurationResolverInterface' => __DIR__ . '/../..' . '/src/FixerConfiguration/FixerConfigurationResolverInterface.php',
'PhpCsFixer\\FixerConfiguration\\FixerOption' => __DIR__ . '/../..' . '/src/FixerConfiguration/FixerOption.php',
'PhpCsFixer\\FixerConfiguration\\FixerOptionBuilder' => __DIR__ . '/../..' . '/src/FixerConfiguration/FixerOptionBuilder.php',
'PhpCsFixer\\FixerConfiguration\\FixerOptionInterface' => __DIR__ . '/../..' . '/src/FixerConfiguration/FixerOptionInterface.php',
'PhpCsFixer\\FixerConfiguration\\InvalidOptionsForEnvException' => __DIR__ . '/../..' . '/src/FixerConfiguration/InvalidOptionsForEnvException.php',
'PhpCsFixer\\FixerDefinition\\CodeSample' => __DIR__ . '/../..' . '/src/FixerDefinition/CodeSample.php',
'PhpCsFixer\\FixerDefinition\\CodeSampleInterface' => __DIR__ . '/../..' . '/src/FixerDefinition/CodeSampleInterface.php',
'PhpCsFixer\\FixerDefinition\\FileSpecificCodeSample' => __DIR__ . '/../..' . '/src/FixerDefinition/FileSpecificCodeSample.php',
'PhpCsFixer\\FixerDefinition\\FileSpecificCodeSampleInterface' => __DIR__ . '/../..' . '/src/FixerDefinition/FileSpecificCodeSampleInterface.php',
'PhpCsFixer\\FixerDefinition\\FixerDefinition' => __DIR__ . '/../..' . '/src/FixerDefinition/FixerDefinition.php',
'PhpCsFixer\\FixerDefinition\\FixerDefinitionInterface' => __DIR__ . '/../..' . '/src/FixerDefinition/FixerDefinitionInterface.php',
'PhpCsFixer\\FixerDefinition\\VersionSpecificCodeSample' => __DIR__ . '/../..' . '/src/FixerDefinition/VersionSpecificCodeSample.php',
'PhpCsFixer\\FixerDefinition\\VersionSpecificCodeSampleInterface' => __DIR__ . '/../..' . '/src/FixerDefinition/VersionSpecificCodeSampleInterface.php',
'PhpCsFixer\\FixerDefinition\\VersionSpecification' => __DIR__ . '/../..' . '/src/FixerDefinition/VersionSpecification.php',
'PhpCsFixer\\FixerDefinition\\VersionSpecificationInterface' => __DIR__ . '/../..' . '/src/FixerDefinition/VersionSpecificationInterface.php',
'PhpCsFixer\\FixerFactory' => __DIR__ . '/../..' . '/src/FixerFactory.php',
'PhpCsFixer\\FixerFileProcessedEvent' => __DIR__ . '/../..' . '/src/FixerFileProcessedEvent.php',
'PhpCsFixer\\FixerNameValidator' => __DIR__ . '/../..' . '/src/FixerNameValidator.php',
'PhpCsFixer\\Fixer\\AbstractIncrementOperatorFixer' => __DIR__ . '/../..' . '/src/Fixer/AbstractIncrementOperatorFixer.php',
'PhpCsFixer\\Fixer\\AbstractPhpUnitFixer' => __DIR__ . '/../..' . '/src/Fixer/AbstractPhpUnitFixer.php',
'PhpCsFixer\\Fixer\\Alias\\ArrayPushFixer' => __DIR__ . '/../..' . '/src/Fixer/Alias/ArrayPushFixer.php',
'PhpCsFixer\\Fixer\\Alias\\BacktickToShellExecFixer' => __DIR__ . '/../..' . '/src/Fixer/Alias/BacktickToShellExecFixer.php',
'PhpCsFixer\\Fixer\\Alias\\EregToPregFixer' => __DIR__ . '/../..' . '/src/Fixer/Alias/EregToPregFixer.php',
'PhpCsFixer\\Fixer\\Alias\\MbStrFunctionsFixer' => __DIR__ . '/../..' . '/src/Fixer/Alias/MbStrFunctionsFixer.php',
'PhpCsFixer\\Fixer\\Alias\\ModernizeStrposFixer' => __DIR__ . '/../..' . '/src/Fixer/Alias/ModernizeStrposFixer.php',
'PhpCsFixer\\Fixer\\Alias\\NoAliasFunctionsFixer' => __DIR__ . '/../..' . '/src/Fixer/Alias/NoAliasFunctionsFixer.php',
'PhpCsFixer\\Fixer\\Alias\\NoAliasLanguageConstructCallFixer' => __DIR__ . '/../..' . '/src/Fixer/Alias/NoAliasLanguageConstructCallFixer.php',
'PhpCsFixer\\Fixer\\Alias\\NoMixedEchoPrintFixer' => __DIR__ . '/../..' . '/src/Fixer/Alias/NoMixedEchoPrintFixer.php',
'PhpCsFixer\\Fixer\\Alias\\PowToExponentiationFixer' => __DIR__ . '/../..' . '/src/Fixer/Alias/PowToExponentiationFixer.php',
'PhpCsFixer\\Fixer\\Alias\\RandomApiMigrationFixer' => __DIR__ . '/../..' . '/src/Fixer/Alias/RandomApiMigrationFixer.php',
'PhpCsFixer\\Fixer\\Alias\\SetTypeToCastFixer' => __DIR__ . '/../..' . '/src/Fixer/Alias/SetTypeToCastFixer.php',
'PhpCsFixer\\Fixer\\ArrayNotation\\ArraySyntaxFixer' => __DIR__ . '/../..' . '/src/Fixer/ArrayNotation/ArraySyntaxFixer.php',
'PhpCsFixer\\Fixer\\ArrayNotation\\NoMultilineWhitespaceAroundDoubleArrowFixer' => __DIR__ . '/../..' . '/src/Fixer/ArrayNotation/NoMultilineWhitespaceAroundDoubleArrowFixer.php',
'PhpCsFixer\\Fixer\\ArrayNotation\\NoTrailingCommaInSinglelineArrayFixer' => __DIR__ . '/../..' . '/src/Fixer/ArrayNotation/NoTrailingCommaInSinglelineArrayFixer.php',
'PhpCsFixer\\Fixer\\ArrayNotation\\NoWhitespaceBeforeCommaInArrayFixer' => __DIR__ . '/../..' . '/src/Fixer/ArrayNotation/NoWhitespaceBeforeCommaInArrayFixer.php',
'PhpCsFixer\\Fixer\\ArrayNotation\\NormalizeIndexBraceFixer' => __DIR__ . '/../..' . '/src/Fixer/ArrayNotation/NormalizeIndexBraceFixer.php',
'PhpCsFixer\\Fixer\\ArrayNotation\\TrimArraySpacesFixer' => __DIR__ . '/../..' . '/src/Fixer/ArrayNotation/TrimArraySpacesFixer.php',
'PhpCsFixer\\Fixer\\ArrayNotation\\WhitespaceAfterCommaInArrayFixer' => __DIR__ . '/../..' . '/src/Fixer/ArrayNotation/WhitespaceAfterCommaInArrayFixer.php',
'PhpCsFixer\\Fixer\\Basic\\BracesFixer' => __DIR__ . '/../..' . '/src/Fixer/Basic/BracesFixer.php',
'PhpCsFixer\\Fixer\\Basic\\CurlyBracesPositionFixer' => __DIR__ . '/../..' . '/src/Fixer/Basic/CurlyBracesPositionFixer.php',
'PhpCsFixer\\Fixer\\Basic\\EncodingFixer' => __DIR__ . '/../..' . '/src/Fixer/Basic/EncodingFixer.php',
'PhpCsFixer\\Fixer\\Basic\\NoMultipleStatementsPerLineFixer' => __DIR__ . '/../..' . '/src/Fixer/Basic/NoMultipleStatementsPerLineFixer.php',
'PhpCsFixer\\Fixer\\Basic\\NoTrailingCommaInSinglelineFixer' => __DIR__ . '/../..' . '/src/Fixer/Basic/NoTrailingCommaInSinglelineFixer.php',
'PhpCsFixer\\Fixer\\Basic\\NonPrintableCharacterFixer' => __DIR__ . '/../..' . '/src/Fixer/Basic/NonPrintableCharacterFixer.php',
'PhpCsFixer\\Fixer\\Basic\\OctalNotationFixer' => __DIR__ . '/../..' . '/src/Fixer/Basic/OctalNotationFixer.php',
'PhpCsFixer\\Fixer\\Basic\\PsrAutoloadingFixer' => __DIR__ . '/../..' . '/src/Fixer/Basic/PsrAutoloadingFixer.php',
'PhpCsFixer\\Fixer\\Casing\\ClassReferenceNameCasingFixer' => __DIR__ . '/../..' . '/src/Fixer/Casing/ClassReferenceNameCasingFixer.php',
'PhpCsFixer\\Fixer\\Casing\\ConstantCaseFixer' => __DIR__ . '/../..' . '/src/Fixer/Casing/ConstantCaseFixer.php',
'PhpCsFixer\\Fixer\\Casing\\IntegerLiteralCaseFixer' => __DIR__ . '/../..' . '/src/Fixer/Casing/IntegerLiteralCaseFixer.php',
'PhpCsFixer\\Fixer\\Casing\\LowercaseKeywordsFixer' => __DIR__ . '/../..' . '/src/Fixer/Casing/LowercaseKeywordsFixer.php',
'PhpCsFixer\\Fixer\\Casing\\LowercaseStaticReferenceFixer' => __DIR__ . '/../..' . '/src/Fixer/Casing/LowercaseStaticReferenceFixer.php',
'PhpCsFixer\\Fixer\\Casing\\MagicConstantCasingFixer' => __DIR__ . '/../..' . '/src/Fixer/Casing/MagicConstantCasingFixer.php',
'PhpCsFixer\\Fixer\\Casing\\MagicMethodCasingFixer' => __DIR__ . '/../..' . '/src/Fixer/Casing/MagicMethodCasingFixer.php',
'PhpCsFixer\\Fixer\\Casing\\NativeFunctionCasingFixer' => __DIR__ . '/../..' . '/src/Fixer/Casing/NativeFunctionCasingFixer.php',
'PhpCsFixer\\Fixer\\Casing\\NativeFunctionTypeDeclarationCasingFixer' => __DIR__ . '/../..' . '/src/Fixer/Casing/NativeFunctionTypeDeclarationCasingFixer.php',
'PhpCsFixer\\Fixer\\CastNotation\\CastSpacesFixer' => __DIR__ . '/../..' . '/src/Fixer/CastNotation/CastSpacesFixer.php',
'PhpCsFixer\\Fixer\\CastNotation\\LowercaseCastFixer' => __DIR__ . '/../..' . '/src/Fixer/CastNotation/LowercaseCastFixer.php',
'PhpCsFixer\\Fixer\\CastNotation\\ModernizeTypesCastingFixer' => __DIR__ . '/../..' . '/src/Fixer/CastNotation/ModernizeTypesCastingFixer.php',
'PhpCsFixer\\Fixer\\CastNotation\\NoShortBoolCastFixer' => __DIR__ . '/../..' . '/src/Fixer/CastNotation/NoShortBoolCastFixer.php',
'PhpCsFixer\\Fixer\\CastNotation\\NoUnsetCastFixer' => __DIR__ . '/../..' . '/src/Fixer/CastNotation/NoUnsetCastFixer.php',
'PhpCsFixer\\Fixer\\CastNotation\\ShortScalarCastFixer' => __DIR__ . '/../..' . '/src/Fixer/CastNotation/ShortScalarCastFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\ClassAttributesSeparationFixer' => __DIR__ . '/../..' . '/src/Fixer/ClassNotation/ClassAttributesSeparationFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\ClassDefinitionFixer' => __DIR__ . '/../..' . '/src/Fixer/ClassNotation/ClassDefinitionFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\FinalClassFixer' => __DIR__ . '/../..' . '/src/Fixer/ClassNotation/FinalClassFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\FinalInternalClassFixer' => __DIR__ . '/../..' . '/src/Fixer/ClassNotation/FinalInternalClassFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\FinalPublicMethodForAbstractClassFixer' => __DIR__ . '/../..' . '/src/Fixer/ClassNotation/FinalPublicMethodForAbstractClassFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\NoBlankLinesAfterClassOpeningFixer' => __DIR__ . '/../..' . '/src/Fixer/ClassNotation/NoBlankLinesAfterClassOpeningFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\NoNullPropertyInitializationFixer' => __DIR__ . '/../..' . '/src/Fixer/ClassNotation/NoNullPropertyInitializationFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\NoPhp4ConstructorFixer' => __DIR__ . '/../..' . '/src/Fixer/ClassNotation/NoPhp4ConstructorFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\NoUnneededFinalMethodFixer' => __DIR__ . '/../..' . '/src/Fixer/ClassNotation/NoUnneededFinalMethodFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\OrderedClassElementsFixer' => __DIR__ . '/../..' . '/src/Fixer/ClassNotation/OrderedClassElementsFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\OrderedInterfacesFixer' => __DIR__ . '/../..' . '/src/Fixer/ClassNotation/OrderedInterfacesFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\OrderedTraitsFixer' => __DIR__ . '/../..' . '/src/Fixer/ClassNotation/OrderedTraitsFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\ProtectedToPrivateFixer' => __DIR__ . '/../..' . '/src/Fixer/ClassNotation/ProtectedToPrivateFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\SelfAccessorFixer' => __DIR__ . '/../..' . '/src/Fixer/ClassNotation/SelfAccessorFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\SelfStaticAccessorFixer' => __DIR__ . '/../..' . '/src/Fixer/ClassNotation/SelfStaticAccessorFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\SingleClassElementPerStatementFixer' => __DIR__ . '/../..' . '/src/Fixer/ClassNotation/SingleClassElementPerStatementFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\SingleTraitInsertPerStatementFixer' => __DIR__ . '/../..' . '/src/Fixer/ClassNotation/SingleTraitInsertPerStatementFixer.php',
'PhpCsFixer\\Fixer\\ClassNotation\\VisibilityRequiredFixer' => __DIR__ . '/../..' . '/src/Fixer/ClassNotation/VisibilityRequiredFixer.php',
'PhpCsFixer\\Fixer\\ClassUsage\\DateTimeImmutableFixer' => __DIR__ . '/../..' . '/src/Fixer/ClassUsage/DateTimeImmutableFixer.php',
'PhpCsFixer\\Fixer\\Comment\\CommentToPhpdocFixer' => __DIR__ . '/../..' . '/src/Fixer/Comment/CommentToPhpdocFixer.php',
'PhpCsFixer\\Fixer\\Comment\\HeaderCommentFixer' => __DIR__ . '/../..' . '/src/Fixer/Comment/HeaderCommentFixer.php',
'PhpCsFixer\\Fixer\\Comment\\MultilineCommentOpeningClosingFixer' => __DIR__ . '/../..' . '/src/Fixer/Comment/MultilineCommentOpeningClosingFixer.php',
'PhpCsFixer\\Fixer\\Comment\\NoEmptyCommentFixer' => __DIR__ . '/../..' . '/src/Fixer/Comment/NoEmptyCommentFixer.php',
'PhpCsFixer\\Fixer\\Comment\\NoTrailingWhitespaceInCommentFixer' => __DIR__ . '/../..' . '/src/Fixer/Comment/NoTrailingWhitespaceInCommentFixer.php',
'PhpCsFixer\\Fixer\\Comment\\SingleLineCommentSpacingFixer' => __DIR__ . '/../..' . '/src/Fixer/Comment/SingleLineCommentSpacingFixer.php',
'PhpCsFixer\\Fixer\\Comment\\SingleLineCommentStyleFixer' => __DIR__ . '/../..' . '/src/Fixer/Comment/SingleLineCommentStyleFixer.php',
'PhpCsFixer\\Fixer\\ConfigurableFixerInterface' => __DIR__ . '/../..' . '/src/Fixer/ConfigurableFixerInterface.php',
'PhpCsFixer\\Fixer\\ConstantNotation\\NativeConstantInvocationFixer' => __DIR__ . '/../..' . '/src/Fixer/ConstantNotation/NativeConstantInvocationFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\ControlStructureBracesFixer' => __DIR__ . '/../..' . '/src/Fixer/ControlStructure/ControlStructureBracesFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\ControlStructureContinuationPositionFixer' => __DIR__ . '/../..' . '/src/Fixer/ControlStructure/ControlStructureContinuationPositionFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\ElseifFixer' => __DIR__ . '/../..' . '/src/Fixer/ControlStructure/ElseifFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\EmptyLoopBodyFixer' => __DIR__ . '/../..' . '/src/Fixer/ControlStructure/EmptyLoopBodyFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\EmptyLoopConditionFixer' => __DIR__ . '/../..' . '/src/Fixer/ControlStructure/EmptyLoopConditionFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\IncludeFixer' => __DIR__ . '/../..' . '/src/Fixer/ControlStructure/IncludeFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\NoAlternativeSyntaxFixer' => __DIR__ . '/../..' . '/src/Fixer/ControlStructure/NoAlternativeSyntaxFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\NoBreakCommentFixer' => __DIR__ . '/../..' . '/src/Fixer/ControlStructure/NoBreakCommentFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\NoSuperfluousElseifFixer' => __DIR__ . '/../..' . '/src/Fixer/ControlStructure/NoSuperfluousElseifFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\NoTrailingCommaInListCallFixer' => __DIR__ . '/../..' . '/src/Fixer/ControlStructure/NoTrailingCommaInListCallFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\NoUnneededControlParenthesesFixer' => __DIR__ . '/../..' . '/src/Fixer/ControlStructure/NoUnneededControlParenthesesFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\NoUnneededCurlyBracesFixer' => __DIR__ . '/../..' . '/src/Fixer/ControlStructure/NoUnneededCurlyBracesFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\NoUselessElseFixer' => __DIR__ . '/../..' . '/src/Fixer/ControlStructure/NoUselessElseFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\SimplifiedIfReturnFixer' => __DIR__ . '/../..' . '/src/Fixer/ControlStructure/SimplifiedIfReturnFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\SwitchCaseSemicolonToColonFixer' => __DIR__ . '/../..' . '/src/Fixer/ControlStructure/SwitchCaseSemicolonToColonFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\SwitchCaseSpaceFixer' => __DIR__ . '/../..' . '/src/Fixer/ControlStructure/SwitchCaseSpaceFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\SwitchContinueToBreakFixer' => __DIR__ . '/../..' . '/src/Fixer/ControlStructure/SwitchContinueToBreakFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\TrailingCommaInMultilineFixer' => __DIR__ . '/../..' . '/src/Fixer/ControlStructure/TrailingCommaInMultilineFixer.php',
'PhpCsFixer\\Fixer\\ControlStructure\\YodaStyleFixer' => __DIR__ . '/../..' . '/src/Fixer/ControlStructure/YodaStyleFixer.php',
'PhpCsFixer\\Fixer\\DeprecatedFixerInterface' => __DIR__ . '/../..' . '/src/Fixer/DeprecatedFixerInterface.php',
'PhpCsFixer\\Fixer\\DoctrineAnnotation\\DoctrineAnnotationArrayAssignmentFixer' => __DIR__ . '/../..' . '/src/Fixer/DoctrineAnnotation/DoctrineAnnotationArrayAssignmentFixer.php',
'PhpCsFixer\\Fixer\\DoctrineAnnotation\\DoctrineAnnotationBracesFixer' => __DIR__ . '/../..' . '/src/Fixer/DoctrineAnnotation/DoctrineAnnotationBracesFixer.php',
'PhpCsFixer\\Fixer\\DoctrineAnnotation\\DoctrineAnnotationIndentationFixer' => __DIR__ . '/../..' . '/src/Fixer/DoctrineAnnotation/DoctrineAnnotationIndentationFixer.php',
'PhpCsFixer\\Fixer\\DoctrineAnnotation\\DoctrineAnnotationSpacesFixer' => __DIR__ . '/../..' . '/src/Fixer/DoctrineAnnotation/DoctrineAnnotationSpacesFixer.php',
'PhpCsFixer\\Fixer\\FixerInterface' => __DIR__ . '/../..' . '/src/Fixer/FixerInterface.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\CombineNestedDirnameFixer' => __DIR__ . '/../..' . '/src/Fixer/FunctionNotation/CombineNestedDirnameFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\DateTimeCreateFromFormatCallFixer' => __DIR__ . '/../..' . '/src/Fixer/FunctionNotation/DateTimeCreateFromFormatCallFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\FopenFlagOrderFixer' => __DIR__ . '/../..' . '/src/Fixer/FunctionNotation/FopenFlagOrderFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\FopenFlagsFixer' => __DIR__ . '/../..' . '/src/Fixer/FunctionNotation/FopenFlagsFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\FunctionDeclarationFixer' => __DIR__ . '/../..' . '/src/Fixer/FunctionNotation/FunctionDeclarationFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\FunctionTypehintSpaceFixer' => __DIR__ . '/../..' . '/src/Fixer/FunctionNotation/FunctionTypehintSpaceFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\ImplodeCallFixer' => __DIR__ . '/../..' . '/src/Fixer/FunctionNotation/ImplodeCallFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\LambdaNotUsedImportFixer' => __DIR__ . '/../..' . '/src/Fixer/FunctionNotation/LambdaNotUsedImportFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\MethodArgumentSpaceFixer' => __DIR__ . '/../..' . '/src/Fixer/FunctionNotation/MethodArgumentSpaceFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\NativeFunctionInvocationFixer' => __DIR__ . '/../..' . '/src/Fixer/FunctionNotation/NativeFunctionInvocationFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\NoSpacesAfterFunctionNameFixer' => __DIR__ . '/../..' . '/src/Fixer/FunctionNotation/NoSpacesAfterFunctionNameFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\NoTrailingCommaInSinglelineFunctionCallFixer' => __DIR__ . '/../..' . '/src/Fixer/FunctionNotation/NoTrailingCommaInSinglelineFunctionCallFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\NoUnreachableDefaultArgumentValueFixer' => __DIR__ . '/../..' . '/src/Fixer/FunctionNotation/NoUnreachableDefaultArgumentValueFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\NoUselessSprintfFixer' => __DIR__ . '/../..' . '/src/Fixer/FunctionNotation/NoUselessSprintfFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\NullableTypeDeclarationForDefaultNullValueFixer' => __DIR__ . '/../..' . '/src/Fixer/FunctionNotation/NullableTypeDeclarationForDefaultNullValueFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\PhpdocToParamTypeFixer' => __DIR__ . '/../..' . '/src/Fixer/FunctionNotation/PhpdocToParamTypeFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\PhpdocToPropertyTypeFixer' => __DIR__ . '/../..' . '/src/Fixer/FunctionNotation/PhpdocToPropertyTypeFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\PhpdocToReturnTypeFixer' => __DIR__ . '/../..' . '/src/Fixer/FunctionNotation/PhpdocToReturnTypeFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\RegularCallableCallFixer' => __DIR__ . '/../..' . '/src/Fixer/FunctionNotation/RegularCallableCallFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\ReturnTypeDeclarationFixer' => __DIR__ . '/../..' . '/src/Fixer/FunctionNotation/ReturnTypeDeclarationFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\SingleLineThrowFixer' => __DIR__ . '/../..' . '/src/Fixer/FunctionNotation/SingleLineThrowFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\StaticLambdaFixer' => __DIR__ . '/../..' . '/src/Fixer/FunctionNotation/StaticLambdaFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\UseArrowFunctionsFixer' => __DIR__ . '/../..' . '/src/Fixer/FunctionNotation/UseArrowFunctionsFixer.php',
'PhpCsFixer\\Fixer\\FunctionNotation\\VoidReturnFixer' => __DIR__ . '/../..' . '/src/Fixer/FunctionNotation/VoidReturnFixer.php',
'PhpCsFixer\\Fixer\\Import\\FullyQualifiedStrictTypesFixer' => __DIR__ . '/../..' . '/src/Fixer/Import/FullyQualifiedStrictTypesFixer.php',
'PhpCsFixer\\Fixer\\Import\\GlobalNamespaceImportFixer' => __DIR__ . '/../..' . '/src/Fixer/Import/GlobalNamespaceImportFixer.php',
'PhpCsFixer\\Fixer\\Import\\GroupImportFixer' => __DIR__ . '/../..' . '/src/Fixer/Import/GroupImportFixer.php',
'PhpCsFixer\\Fixer\\Import\\NoLeadingImportSlashFixer' => __DIR__ . '/../..' . '/src/Fixer/Import/NoLeadingImportSlashFixer.php',
'PhpCsFixer\\Fixer\\Import\\NoUnneededImportAliasFixer' => __DIR__ . '/../..' . '/src/Fixer/Import/NoUnneededImportAliasFixer.php',
'PhpCsFixer\\Fixer\\Import\\NoUnusedImportsFixer' => __DIR__ . '/../..' . '/src/Fixer/Import/NoUnusedImportsFixer.php',
'PhpCsFixer\\Fixer\\Import\\OrderedImportsFixer' => __DIR__ . '/../..' . '/src/Fixer/Import/OrderedImportsFixer.php',
'PhpCsFixer\\Fixer\\Import\\SingleImportPerStatementFixer' => __DIR__ . '/../..' . '/src/Fixer/Import/SingleImportPerStatementFixer.php',
'PhpCsFixer\\Fixer\\Import\\SingleLineAfterImportsFixer' => __DIR__ . '/../..' . '/src/Fixer/Import/SingleLineAfterImportsFixer.php',
'PhpCsFixer\\Fixer\\Indentation' => __DIR__ . '/../..' . '/src/Fixer/Indentation.php',
'PhpCsFixer\\Fixer\\LanguageConstruct\\ClassKeywordRemoveFixer' => __DIR__ . '/../..' . '/src/Fixer/LanguageConstruct/ClassKeywordRemoveFixer.php',
'PhpCsFixer\\Fixer\\LanguageConstruct\\CombineConsecutiveIssetsFixer' => __DIR__ . '/../..' . '/src/Fixer/LanguageConstruct/CombineConsecutiveIssetsFixer.php',
'PhpCsFixer\\Fixer\\LanguageConstruct\\CombineConsecutiveUnsetsFixer' => __DIR__ . '/../..' . '/src/Fixer/LanguageConstruct/CombineConsecutiveUnsetsFixer.php',
'PhpCsFixer\\Fixer\\LanguageConstruct\\DeclareEqualNormalizeFixer' => __DIR__ . '/../..' . '/src/Fixer/LanguageConstruct/DeclareEqualNormalizeFixer.php',
'PhpCsFixer\\Fixer\\LanguageConstruct\\DeclareParenthesesFixer' => __DIR__ . '/../..' . '/src/Fixer/LanguageConstruct/DeclareParenthesesFixer.php',
'PhpCsFixer\\Fixer\\LanguageConstruct\\DirConstantFixer' => __DIR__ . '/../..' . '/src/Fixer/LanguageConstruct/DirConstantFixer.php',
'PhpCsFixer\\Fixer\\LanguageConstruct\\ErrorSuppressionFixer' => __DIR__ . '/../..' . '/src/Fixer/LanguageConstruct/ErrorSuppressionFixer.php',
'PhpCsFixer\\Fixer\\LanguageConstruct\\ExplicitIndirectVariableFixer' => __DIR__ . '/../..' . '/src/Fixer/LanguageConstruct/ExplicitIndirectVariableFixer.php',
'PhpCsFixer\\Fixer\\LanguageConstruct\\FunctionToConstantFixer' => __DIR__ . '/../..' . '/src/Fixer/LanguageConstruct/FunctionToConstantFixer.php',
'PhpCsFixer\\Fixer\\LanguageConstruct\\GetClassToClassKeywordFixer' => __DIR__ . '/../..' . '/src/Fixer/LanguageConstruct/GetClassToClassKeywordFixer.php',
'PhpCsFixer\\Fixer\\LanguageConstruct\\IsNullFixer' => __DIR__ . '/../..' . '/src/Fixer/LanguageConstruct/IsNullFixer.php',
'PhpCsFixer\\Fixer\\LanguageConstruct\\NoUnsetOnPropertyFixer' => __DIR__ . '/../..' . '/src/Fixer/LanguageConstruct/NoUnsetOnPropertyFixer.php',
'PhpCsFixer\\Fixer\\LanguageConstruct\\SingleSpaceAfterConstructFixer' => __DIR__ . '/../..' . '/src/Fixer/LanguageConstruct/SingleSpaceAfterConstructFixer.php',
'PhpCsFixer\\Fixer\\ListNotation\\ListSyntaxFixer' => __DIR__ . '/../..' . '/src/Fixer/ListNotation/ListSyntaxFixer.php',
'PhpCsFixer\\Fixer\\NamespaceNotation\\BlankLineAfterNamespaceFixer' => __DIR__ . '/../..' . '/src/Fixer/NamespaceNotation/BlankLineAfterNamespaceFixer.php',
'PhpCsFixer\\Fixer\\NamespaceNotation\\CleanNamespaceFixer' => __DIR__ . '/../..' . '/src/Fixer/NamespaceNotation/CleanNamespaceFixer.php',
'PhpCsFixer\\Fixer\\NamespaceNotation\\NoBlankLinesBeforeNamespaceFixer' => __DIR__ . '/../..' . '/src/Fixer/NamespaceNotation/NoBlankLinesBeforeNamespaceFixer.php',
'PhpCsFixer\\Fixer\\NamespaceNotation\\NoLeadingNamespaceWhitespaceFixer' => __DIR__ . '/../..' . '/src/Fixer/NamespaceNotation/NoLeadingNamespaceWhitespaceFixer.php',
'PhpCsFixer\\Fixer\\NamespaceNotation\\SingleBlankLineBeforeNamespaceFixer' => __DIR__ . '/../..' . '/src/Fixer/NamespaceNotation/SingleBlankLineBeforeNamespaceFixer.php',
'PhpCsFixer\\Fixer\\Naming\\NoHomoglyphNamesFixer' => __DIR__ . '/../..' . '/src/Fixer/Naming/NoHomoglyphNamesFixer.php',
'PhpCsFixer\\Fixer\\Operator\\AssignNullCoalescingToCoalesceEqualFixer' => __DIR__ . '/../..' . '/src/Fixer/Operator/AssignNullCoalescingToCoalesceEqualFixer.php',
'PhpCsFixer\\Fixer\\Operator\\BinaryOperatorSpacesFixer' => __DIR__ . '/../..' . '/src/Fixer/Operator/BinaryOperatorSpacesFixer.php',
'PhpCsFixer\\Fixer\\Operator\\ConcatSpaceFixer' => __DIR__ . '/../..' . '/src/Fixer/Operator/ConcatSpaceFixer.php',
'PhpCsFixer\\Fixer\\Operator\\IncrementStyleFixer' => __DIR__ . '/../..' . '/src/Fixer/Operator/IncrementStyleFixer.php',
'PhpCsFixer\\Fixer\\Operator\\LogicalOperatorsFixer' => __DIR__ . '/../..' . '/src/Fixer/Operator/LogicalOperatorsFixer.php',
'PhpCsFixer\\Fixer\\Operator\\NewWithBracesFixer' => __DIR__ . '/../..' . '/src/Fixer/Operator/NewWithBracesFixer.php',
'PhpCsFixer\\Fixer\\Operator\\NoSpaceAroundDoubleColonFixer' => __DIR__ . '/../..' . '/src/Fixer/Operator/NoSpaceAroundDoubleColonFixer.php',
'PhpCsFixer\\Fixer\\Operator\\NoUselessConcatOperatorFixer' => __DIR__ . '/../..' . '/src/Fixer/Operator/NoUselessConcatOperatorFixer.php',
'PhpCsFixer\\Fixer\\Operator\\NoUselessNullsafeOperatorFixer' => __DIR__ . '/../..' . '/src/Fixer/Operator/NoUselessNullsafeOperatorFixer.php',
'PhpCsFixer\\Fixer\\Operator\\NotOperatorWithSpaceFixer' => __DIR__ . '/../..' . '/src/Fixer/Operator/NotOperatorWithSpaceFixer.php',
'PhpCsFixer\\Fixer\\Operator\\NotOperatorWithSuccessorSpaceFixer' => __DIR__ . '/../..' . '/src/Fixer/Operator/NotOperatorWithSuccessorSpaceFixer.php',
'PhpCsFixer\\Fixer\\Operator\\ObjectOperatorWithoutWhitespaceFixer' => __DIR__ . '/../..' . '/src/Fixer/Operator/ObjectOperatorWithoutWhitespaceFixer.php',
'PhpCsFixer\\Fixer\\Operator\\OperatorLinebreakFixer' => __DIR__ . '/../..' . '/src/Fixer/Operator/OperatorLinebreakFixer.php',
'PhpCsFixer\\Fixer\\Operator\\StandardizeIncrementFixer' => __DIR__ . '/../..' . '/src/Fixer/Operator/StandardizeIncrementFixer.php',
'PhpCsFixer\\Fixer\\Operator\\StandardizeNotEqualsFixer' => __DIR__ . '/../..' . '/src/Fixer/Operator/StandardizeNotEqualsFixer.php',
'PhpCsFixer\\Fixer\\Operator\\TernaryOperatorSpacesFixer' => __DIR__ . '/../..' . '/src/Fixer/Operator/TernaryOperatorSpacesFixer.php',
'PhpCsFixer\\Fixer\\Operator\\TernaryToElvisOperatorFixer' => __DIR__ . '/../..' . '/src/Fixer/Operator/TernaryToElvisOperatorFixer.php',
'PhpCsFixer\\Fixer\\Operator\\TernaryToNullCoalescingFixer' => __DIR__ . '/../..' . '/src/Fixer/Operator/TernaryToNullCoalescingFixer.php',
'PhpCsFixer\\Fixer\\Operator\\UnaryOperatorSpacesFixer' => __DIR__ . '/../..' . '/src/Fixer/Operator/UnaryOperatorSpacesFixer.php',
'PhpCsFixer\\Fixer\\PhpTag\\BlankLineAfterOpeningTagFixer' => __DIR__ . '/../..' . '/src/Fixer/PhpTag/BlankLineAfterOpeningTagFixer.php',
'PhpCsFixer\\Fixer\\PhpTag\\EchoTagSyntaxFixer' => __DIR__ . '/../..' . '/src/Fixer/PhpTag/EchoTagSyntaxFixer.php',
'PhpCsFixer\\Fixer\\PhpTag\\FullOpeningTagFixer' => __DIR__ . '/../..' . '/src/Fixer/PhpTag/FullOpeningTagFixer.php',
'PhpCsFixer\\Fixer\\PhpTag\\LinebreakAfterOpeningTagFixer' => __DIR__ . '/../..' . '/src/Fixer/PhpTag/LinebreakAfterOpeningTagFixer.php',
'PhpCsFixer\\Fixer\\PhpTag\\NoClosingTagFixer' => __DIR__ . '/../..' . '/src/Fixer/PhpTag/NoClosingTagFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitConstructFixer' => __DIR__ . '/../..' . '/src/Fixer/PhpUnit/PhpUnitConstructFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitDedicateAssertFixer' => __DIR__ . '/../..' . '/src/Fixer/PhpUnit/PhpUnitDedicateAssertFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitDedicateAssertInternalTypeFixer' => __DIR__ . '/../..' . '/src/Fixer/PhpUnit/PhpUnitDedicateAssertInternalTypeFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitExpectationFixer' => __DIR__ . '/../..' . '/src/Fixer/PhpUnit/PhpUnitExpectationFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitFqcnAnnotationFixer' => __DIR__ . '/../..' . '/src/Fixer/PhpUnit/PhpUnitFqcnAnnotationFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitInternalClassFixer' => __DIR__ . '/../..' . '/src/Fixer/PhpUnit/PhpUnitInternalClassFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitMethodCasingFixer' => __DIR__ . '/../..' . '/src/Fixer/PhpUnit/PhpUnitMethodCasingFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitMockFixer' => __DIR__ . '/../..' . '/src/Fixer/PhpUnit/PhpUnitMockFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitMockShortWillReturnFixer' => __DIR__ . '/../..' . '/src/Fixer/PhpUnit/PhpUnitMockShortWillReturnFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitNamespacedFixer' => __DIR__ . '/../..' . '/src/Fixer/PhpUnit/PhpUnitNamespacedFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitNoExpectationAnnotationFixer' => __DIR__ . '/../..' . '/src/Fixer/PhpUnit/PhpUnitNoExpectationAnnotationFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitSetUpTearDownVisibilityFixer' => __DIR__ . '/../..' . '/src/Fixer/PhpUnit/PhpUnitSetUpTearDownVisibilityFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitSizeClassFixer' => __DIR__ . '/../..' . '/src/Fixer/PhpUnit/PhpUnitSizeClassFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitStrictFixer' => __DIR__ . '/../..' . '/src/Fixer/PhpUnit/PhpUnitStrictFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitTargetVersion' => __DIR__ . '/../..' . '/src/Fixer/PhpUnit/PhpUnitTargetVersion.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitTestAnnotationFixer' => __DIR__ . '/../..' . '/src/Fixer/PhpUnit/PhpUnitTestAnnotationFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitTestCaseStaticMethodCallsFixer' => __DIR__ . '/../..' . '/src/Fixer/PhpUnit/PhpUnitTestCaseStaticMethodCallsFixer.php',
'PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitTestClassRequiresCoversFixer' => __DIR__ . '/../..' . '/src/Fixer/PhpUnit/PhpUnitTestClassRequiresCoversFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\AlignMultilineCommentFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/AlignMultilineCommentFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\GeneralPhpdocAnnotationRemoveFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/GeneralPhpdocAnnotationRemoveFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\GeneralPhpdocTagRenameFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/GeneralPhpdocTagRenameFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\NoBlankLinesAfterPhpdocFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/NoBlankLinesAfterPhpdocFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\NoEmptyPhpdocFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/NoEmptyPhpdocFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\NoSuperfluousPhpdocTagsFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/NoSuperfluousPhpdocTagsFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocAddMissingParamAnnotationFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/PhpdocAddMissingParamAnnotationFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocAlignFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/PhpdocAlignFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocAnnotationWithoutDotFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/PhpdocAnnotationWithoutDotFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocIndentFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/PhpdocIndentFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocInlineTagNormalizerFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/PhpdocInlineTagNormalizerFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocLineSpanFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/PhpdocLineSpanFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocNoAccessFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/PhpdocNoAccessFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocNoAliasTagFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/PhpdocNoAliasTagFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocNoEmptyReturnFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/PhpdocNoEmptyReturnFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocNoPackageFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/PhpdocNoPackageFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocNoUselessInheritdocFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/PhpdocNoUselessInheritdocFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocOrderByValueFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/PhpdocOrderByValueFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocOrderFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/PhpdocOrderFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocReturnSelfReferenceFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/PhpdocReturnSelfReferenceFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocScalarFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/PhpdocScalarFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocSeparationFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/PhpdocSeparationFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocSingleLineVarSpacingFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/PhpdocSingleLineVarSpacingFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocSummaryFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/PhpdocSummaryFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocTagCasingFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/PhpdocTagCasingFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocTagTypeFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/PhpdocTagTypeFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocToCommentFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/PhpdocToCommentFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocTrimConsecutiveBlankLineSeparationFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/PhpdocTrimConsecutiveBlankLineSeparationFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocTrimFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/PhpdocTrimFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocTypesFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/PhpdocTypesFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocTypesOrderFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/PhpdocTypesOrderFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocVarAnnotationCorrectOrderFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/PhpdocVarAnnotationCorrectOrderFixer.php',
'PhpCsFixer\\Fixer\\Phpdoc\\PhpdocVarWithoutNameFixer' => __DIR__ . '/../..' . '/src/Fixer/Phpdoc/PhpdocVarWithoutNameFixer.php',
'PhpCsFixer\\Fixer\\ReturnNotation\\NoUselessReturnFixer' => __DIR__ . '/../..' . '/src/Fixer/ReturnNotation/NoUselessReturnFixer.php',
'PhpCsFixer\\Fixer\\ReturnNotation\\ReturnAssignmentFixer' => __DIR__ . '/../..' . '/src/Fixer/ReturnNotation/ReturnAssignmentFixer.php',
'PhpCsFixer\\Fixer\\ReturnNotation\\SimplifiedNullReturnFixer' => __DIR__ . '/../..' . '/src/Fixer/ReturnNotation/SimplifiedNullReturnFixer.php',
'PhpCsFixer\\Fixer\\Semicolon\\MultilineWhitespaceBeforeSemicolonsFixer' => __DIR__ . '/../..' . '/src/Fixer/Semicolon/MultilineWhitespaceBeforeSemicolonsFixer.php',
'PhpCsFixer\\Fixer\\Semicolon\\NoEmptyStatementFixer' => __DIR__ . '/../..' . '/src/Fixer/Semicolon/NoEmptyStatementFixer.php',
'PhpCsFixer\\Fixer\\Semicolon\\NoSinglelineWhitespaceBeforeSemicolonsFixer' => __DIR__ . '/../..' . '/src/Fixer/Semicolon/NoSinglelineWhitespaceBeforeSemicolonsFixer.php',
'PhpCsFixer\\Fixer\\Semicolon\\SemicolonAfterInstructionFixer' => __DIR__ . '/../..' . '/src/Fixer/Semicolon/SemicolonAfterInstructionFixer.php',
'PhpCsFixer\\Fixer\\Semicolon\\SpaceAfterSemicolonFixer' => __DIR__ . '/../..' . '/src/Fixer/Semicolon/SpaceAfterSemicolonFixer.php',
'PhpCsFixer\\Fixer\\Strict\\DeclareStrictTypesFixer' => __DIR__ . '/../..' . '/src/Fixer/Strict/DeclareStrictTypesFixer.php',
'PhpCsFixer\\Fixer\\Strict\\StrictComparisonFixer' => __DIR__ . '/../..' . '/src/Fixer/Strict/StrictComparisonFixer.php',
'PhpCsFixer\\Fixer\\Strict\\StrictParamFixer' => __DIR__ . '/../..' . '/src/Fixer/Strict/StrictParamFixer.php',
'PhpCsFixer\\Fixer\\StringNotation\\EscapeImplicitBackslashesFixer' => __DIR__ . '/../..' . '/src/Fixer/StringNotation/EscapeImplicitBackslashesFixer.php',
'PhpCsFixer\\Fixer\\StringNotation\\ExplicitStringVariableFixer' => __DIR__ . '/../..' . '/src/Fixer/StringNotation/ExplicitStringVariableFixer.php',
'PhpCsFixer\\Fixer\\StringNotation\\HeredocToNowdocFixer' => __DIR__ . '/../..' . '/src/Fixer/StringNotation/HeredocToNowdocFixer.php',
'PhpCsFixer\\Fixer\\StringNotation\\NoBinaryStringFixer' => __DIR__ . '/../..' . '/src/Fixer/StringNotation/NoBinaryStringFixer.php',
'PhpCsFixer\\Fixer\\StringNotation\\NoTrailingWhitespaceInStringFixer' => __DIR__ . '/../..' . '/src/Fixer/StringNotation/NoTrailingWhitespaceInStringFixer.php',
'PhpCsFixer\\Fixer\\StringNotation\\SimpleToComplexStringVariableFixer' => __DIR__ . '/../..' . '/src/Fixer/StringNotation/SimpleToComplexStringVariableFixer.php',
'PhpCsFixer\\Fixer\\StringNotation\\SingleQuoteFixer' => __DIR__ . '/../..' . '/src/Fixer/StringNotation/SingleQuoteFixer.php',
'PhpCsFixer\\Fixer\\StringNotation\\StringLengthToEmptyFixer' => __DIR__ . '/../..' . '/src/Fixer/StringNotation/StringLengthToEmptyFixer.php',
'PhpCsFixer\\Fixer\\StringNotation\\StringLineEndingFixer' => __DIR__ . '/../..' . '/src/Fixer/StringNotation/StringLineEndingFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\ArrayIndentationFixer' => __DIR__ . '/../..' . '/src/Fixer/Whitespace/ArrayIndentationFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\BlankLineBeforeStatementFixer' => __DIR__ . '/../..' . '/src/Fixer/Whitespace/BlankLineBeforeStatementFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\BlankLineBetweenImportGroupsFixer' => __DIR__ . '/../..' . '/src/Fixer/Whitespace/BlankLineBetweenImportGroupsFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\CompactNullableTypehintFixer' => __DIR__ . '/../..' . '/src/Fixer/Whitespace/CompactNullableTypehintFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\HeredocIndentationFixer' => __DIR__ . '/../..' . '/src/Fixer/Whitespace/HeredocIndentationFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\IndentationTypeFixer' => __DIR__ . '/../..' . '/src/Fixer/Whitespace/IndentationTypeFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\LineEndingFixer' => __DIR__ . '/../..' . '/src/Fixer/Whitespace/LineEndingFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\MethodChainingIndentationFixer' => __DIR__ . '/../..' . '/src/Fixer/Whitespace/MethodChainingIndentationFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\NoExtraBlankLinesFixer' => __DIR__ . '/../..' . '/src/Fixer/Whitespace/NoExtraBlankLinesFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\NoSpacesAroundOffsetFixer' => __DIR__ . '/../..' . '/src/Fixer/Whitespace/NoSpacesAroundOffsetFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\NoSpacesInsideParenthesisFixer' => __DIR__ . '/../..' . '/src/Fixer/Whitespace/NoSpacesInsideParenthesisFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\NoTrailingWhitespaceFixer' => __DIR__ . '/../..' . '/src/Fixer/Whitespace/NoTrailingWhitespaceFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\NoWhitespaceInBlankLineFixer' => __DIR__ . '/../..' . '/src/Fixer/Whitespace/NoWhitespaceInBlankLineFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\SingleBlankLineAtEofFixer' => __DIR__ . '/../..' . '/src/Fixer/Whitespace/SingleBlankLineAtEofFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\StatementIndentationFixer' => __DIR__ . '/../..' . '/src/Fixer/Whitespace/StatementIndentationFixer.php',
'PhpCsFixer\\Fixer\\Whitespace\\TypesSpacesFixer' => __DIR__ . '/../..' . '/src/Fixer/Whitespace/TypesSpacesFixer.php',
'PhpCsFixer\\Fixer\\WhitespacesAwareFixerInterface' => __DIR__ . '/../..' . '/src/Fixer/WhitespacesAwareFixerInterface.php',
'PhpCsFixer\\Indicator\\PhpUnitTestCaseIndicator' => __DIR__ . '/../..' . '/src/Indicator/PhpUnitTestCaseIndicator.php',
'PhpCsFixer\\Linter\\CachingLinter' => __DIR__ . '/../..' . '/src/Linter/CachingLinter.php',
'PhpCsFixer\\Linter\\Linter' => __DIR__ . '/../..' . '/src/Linter/Linter.php',
'PhpCsFixer\\Linter\\LinterInterface' => __DIR__ . '/../..' . '/src/Linter/LinterInterface.php',
'PhpCsFixer\\Linter\\LintingException' => __DIR__ . '/../..' . '/src/Linter/LintingException.php',
'PhpCsFixer\\Linter\\LintingResultInterface' => __DIR__ . '/../..' . '/src/Linter/LintingResultInterface.php',
'PhpCsFixer\\Linter\\ProcessLinter' => __DIR__ . '/../..' . '/src/Linter/ProcessLinter.php',
'PhpCsFixer\\Linter\\ProcessLinterProcessBuilder' => __DIR__ . '/../..' . '/src/Linter/ProcessLinterProcessBuilder.php',
'PhpCsFixer\\Linter\\ProcessLintingResult' => __DIR__ . '/../..' . '/src/Linter/ProcessLintingResult.php',
'PhpCsFixer\\Linter\\TokenizerLinter' => __DIR__ . '/../..' . '/src/Linter/TokenizerLinter.php',
'PhpCsFixer\\Linter\\TokenizerLintingResult' => __DIR__ . '/../..' . '/src/Linter/TokenizerLintingResult.php',
'PhpCsFixer\\Linter\\UnavailableLinterException' => __DIR__ . '/../..' . '/src/Linter/UnavailableLinterException.php',
'PhpCsFixer\\PharChecker' => __DIR__ . '/../..' . '/src/PharChecker.php',
'PhpCsFixer\\PharCheckerInterface' => __DIR__ . '/../..' . '/src/PharCheckerInterface.php',
'PhpCsFixer\\Preg' => __DIR__ . '/../..' . '/src/Preg.php',
'PhpCsFixer\\PregException' => __DIR__ . '/../..' . '/src/PregException.php',
'PhpCsFixer\\RuleSet\\AbstractMigrationSetDescription' => __DIR__ . '/../..' . '/src/RuleSet/AbstractMigrationSetDescription.php',
'PhpCsFixer\\RuleSet\\AbstractRuleSetDescription' => __DIR__ . '/../..' . '/src/RuleSet/AbstractRuleSetDescription.php',
'PhpCsFixer\\RuleSet\\RuleSet' => __DIR__ . '/../..' . '/src/RuleSet/RuleSet.php',
'PhpCsFixer\\RuleSet\\RuleSetDescriptionInterface' => __DIR__ . '/../..' . '/src/RuleSet/RuleSetDescriptionInterface.php',
'PhpCsFixer\\RuleSet\\RuleSetInterface' => __DIR__ . '/../..' . '/src/RuleSet/RuleSetInterface.php',
'PhpCsFixer\\RuleSet\\RuleSets' => __DIR__ . '/../..' . '/src/RuleSet/RuleSets.php',
'PhpCsFixer\\RuleSet\\Sets\\DoctrineAnnotationSet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/DoctrineAnnotationSet.php',
'PhpCsFixer\\RuleSet\\Sets\\PERRiskySet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PERRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PERSet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PERSet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHP54MigrationSet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PHP54MigrationSet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHP56MigrationRiskySet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PHP56MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHP70MigrationRiskySet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PHP70MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHP70MigrationSet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PHP70MigrationSet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHP71MigrationRiskySet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PHP71MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHP71MigrationSet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PHP71MigrationSet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHP73MigrationSet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PHP73MigrationSet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHP74MigrationRiskySet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PHP74MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHP74MigrationSet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PHP74MigrationSet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHP80MigrationRiskySet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PHP80MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHP80MigrationSet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PHP80MigrationSet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHP81MigrationSet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PHP81MigrationSet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHP82MigrationSet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PHP82MigrationSet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHPUnit30MigrationRiskySet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PHPUnit30MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHPUnit32MigrationRiskySet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PHPUnit32MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHPUnit35MigrationRiskySet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PHPUnit35MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHPUnit43MigrationRiskySet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PHPUnit43MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHPUnit48MigrationRiskySet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PHPUnit48MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHPUnit50MigrationRiskySet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PHPUnit50MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHPUnit52MigrationRiskySet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PHPUnit52MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHPUnit54MigrationRiskySet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PHPUnit54MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHPUnit55MigrationRiskySet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PHPUnit55MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHPUnit56MigrationRiskySet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PHPUnit56MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHPUnit57MigrationRiskySet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PHPUnit57MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHPUnit60MigrationRiskySet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PHPUnit60MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHPUnit75MigrationRiskySet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PHPUnit75MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PHPUnit84MigrationRiskySet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PHPUnit84MigrationRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PSR12RiskySet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PSR12RiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PSR12Set' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PSR12Set.php',
'PhpCsFixer\\RuleSet\\Sets\\PSR1Set' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PSR1Set.php',
'PhpCsFixer\\RuleSet\\Sets\\PSR2Set' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PSR2Set.php',
'PhpCsFixer\\RuleSet\\Sets\\PhpCsFixerRiskySet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PhpCsFixerRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\PhpCsFixerSet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/PhpCsFixerSet.php',
'PhpCsFixer\\RuleSet\\Sets\\SymfonyRiskySet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/SymfonyRiskySet.php',
'PhpCsFixer\\RuleSet\\Sets\\SymfonySet' => __DIR__ . '/../..' . '/src/RuleSet/Sets/SymfonySet.php',
'PhpCsFixer\\Runner\\FileCachingLintingIterator' => __DIR__ . '/../..' . '/src/Runner/FileCachingLintingIterator.php',
'PhpCsFixer\\Runner\\FileFilterIterator' => __DIR__ . '/../..' . '/src/Runner/FileFilterIterator.php',
'PhpCsFixer\\Runner\\FileLintingIterator' => __DIR__ . '/../..' . '/src/Runner/FileLintingIterator.php',
'PhpCsFixer\\Runner\\Runner' => __DIR__ . '/../..' . '/src/Runner/Runner.php',
'PhpCsFixer\\StdinFileInfo' => __DIR__ . '/../..' . '/src/StdinFileInfo.php',
'PhpCsFixer\\Tokenizer\\AbstractTransformer' => __DIR__ . '/../..' . '/src/Tokenizer/AbstractTransformer.php',
'PhpCsFixer\\Tokenizer\\AbstractTypeTransformer' => __DIR__ . '/../..' . '/src/Tokenizer/AbstractTypeTransformer.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\AlternativeSyntaxAnalyzer' => __DIR__ . '/../..' . '/src/Tokenizer/Analyzer/AlternativeSyntaxAnalyzer.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\Analysis\\AbstractControlCaseStructuresAnalysis' => __DIR__ . '/../..' . '/src/Tokenizer/Analyzer/Analysis/AbstractControlCaseStructuresAnalysis.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\Analysis\\ArgumentAnalysis' => __DIR__ . '/../..' . '/src/Tokenizer/Analyzer/Analysis/ArgumentAnalysis.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\Analysis\\CaseAnalysis' => __DIR__ . '/../..' . '/src/Tokenizer/Analyzer/Analysis/CaseAnalysis.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\Analysis\\DefaultAnalysis' => __DIR__ . '/../..' . '/src/Tokenizer/Analyzer/Analysis/DefaultAnalysis.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\Analysis\\EnumAnalysis' => __DIR__ . '/../..' . '/src/Tokenizer/Analyzer/Analysis/EnumAnalysis.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\Analysis\\MatchAnalysis' => __DIR__ . '/../..' . '/src/Tokenizer/Analyzer/Analysis/MatchAnalysis.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\Analysis\\NamespaceAnalysis' => __DIR__ . '/../..' . '/src/Tokenizer/Analyzer/Analysis/NamespaceAnalysis.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\Analysis\\NamespaceUseAnalysis' => __DIR__ . '/../..' . '/src/Tokenizer/Analyzer/Analysis/NamespaceUseAnalysis.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\Analysis\\StartEndTokenAwareAnalysis' => __DIR__ . '/../..' . '/src/Tokenizer/Analyzer/Analysis/StartEndTokenAwareAnalysis.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\Analysis\\SwitchAnalysis' => __DIR__ . '/../..' . '/src/Tokenizer/Analyzer/Analysis/SwitchAnalysis.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\Analysis\\TypeAnalysis' => __DIR__ . '/../..' . '/src/Tokenizer/Analyzer/Analysis/TypeAnalysis.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\ArgumentsAnalyzer' => __DIR__ . '/../..' . '/src/Tokenizer/Analyzer/ArgumentsAnalyzer.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\AttributeAnalyzer' => __DIR__ . '/../..' . '/src/Tokenizer/Analyzer/AttributeAnalyzer.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\BlocksAnalyzer' => __DIR__ . '/../..' . '/src/Tokenizer/Analyzer/BlocksAnalyzer.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\ClassyAnalyzer' => __DIR__ . '/../..' . '/src/Tokenizer/Analyzer/ClassyAnalyzer.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\CommentsAnalyzer' => __DIR__ . '/../..' . '/src/Tokenizer/Analyzer/CommentsAnalyzer.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\ControlCaseStructuresAnalyzer' => __DIR__ . '/../..' . '/src/Tokenizer/Analyzer/ControlCaseStructuresAnalyzer.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\FunctionsAnalyzer' => __DIR__ . '/../..' . '/src/Tokenizer/Analyzer/FunctionsAnalyzer.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\GotoLabelAnalyzer' => __DIR__ . '/../..' . '/src/Tokenizer/Analyzer/GotoLabelAnalyzer.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\NamespaceUsesAnalyzer' => __DIR__ . '/../..' . '/src/Tokenizer/Analyzer/NamespaceUsesAnalyzer.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\NamespacesAnalyzer' => __DIR__ . '/../..' . '/src/Tokenizer/Analyzer/NamespacesAnalyzer.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\RangeAnalyzer' => __DIR__ . '/../..' . '/src/Tokenizer/Analyzer/RangeAnalyzer.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\ReferenceAnalyzer' => __DIR__ . '/../..' . '/src/Tokenizer/Analyzer/ReferenceAnalyzer.php',
'PhpCsFixer\\Tokenizer\\Analyzer\\WhitespacesAnalyzer' => __DIR__ . '/../..' . '/src/Tokenizer/Analyzer/WhitespacesAnalyzer.php',
'PhpCsFixer\\Tokenizer\\CT' => __DIR__ . '/../..' . '/src/Tokenizer/CT.php',
'PhpCsFixer\\Tokenizer\\CodeHasher' => __DIR__ . '/../..' . '/src/Tokenizer/CodeHasher.php',
'PhpCsFixer\\Tokenizer\\Token' => __DIR__ . '/../..' . '/src/Tokenizer/Token.php',
'PhpCsFixer\\Tokenizer\\Tokens' => __DIR__ . '/../..' . '/src/Tokenizer/Tokens.php',
'PhpCsFixer\\Tokenizer\\TokensAnalyzer' => __DIR__ . '/../..' . '/src/Tokenizer/TokensAnalyzer.php',
'PhpCsFixer\\Tokenizer\\TransformerInterface' => __DIR__ . '/../..' . '/src/Tokenizer/TransformerInterface.php',
'PhpCsFixer\\Tokenizer\\Transformer\\ArrayTypehintTransformer' => __DIR__ . '/../..' . '/src/Tokenizer/Transformer/ArrayTypehintTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\AttributeTransformer' => __DIR__ . '/../..' . '/src/Tokenizer/Transformer/AttributeTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\BraceClassInstantiationTransformer' => __DIR__ . '/../..' . '/src/Tokenizer/Transformer/BraceClassInstantiationTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\ClassConstantTransformer' => __DIR__ . '/../..' . '/src/Tokenizer/Transformer/ClassConstantTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\ConstructorPromotionTransformer' => __DIR__ . '/../..' . '/src/Tokenizer/Transformer/ConstructorPromotionTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\CurlyBraceTransformer' => __DIR__ . '/../..' . '/src/Tokenizer/Transformer/CurlyBraceTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\FirstClassCallableTransformer' => __DIR__ . '/../..' . '/src/Tokenizer/Transformer/FirstClassCallableTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\ImportTransformer' => __DIR__ . '/../..' . '/src/Tokenizer/Transformer/ImportTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\NameQualifiedTransformer' => __DIR__ . '/../..' . '/src/Tokenizer/Transformer/NameQualifiedTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\NamedArgumentTransformer' => __DIR__ . '/../..' . '/src/Tokenizer/Transformer/NamedArgumentTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\NamespaceOperatorTransformer' => __DIR__ . '/../..' . '/src/Tokenizer/Transformer/NamespaceOperatorTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\NullableTypeTransformer' => __DIR__ . '/../..' . '/src/Tokenizer/Transformer/NullableTypeTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\ReturnRefTransformer' => __DIR__ . '/../..' . '/src/Tokenizer/Transformer/ReturnRefTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\SquareBraceTransformer' => __DIR__ . '/../..' . '/src/Tokenizer/Transformer/SquareBraceTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\TypeAlternationTransformer' => __DIR__ . '/../..' . '/src/Tokenizer/Transformer/TypeAlternationTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\TypeColonTransformer' => __DIR__ . '/../..' . '/src/Tokenizer/Transformer/TypeColonTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\TypeIntersectionTransformer' => __DIR__ . '/../..' . '/src/Tokenizer/Transformer/TypeIntersectionTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\UseTransformer' => __DIR__ . '/../..' . '/src/Tokenizer/Transformer/UseTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformer\\WhitespacyCommentTransformer' => __DIR__ . '/../..' . '/src/Tokenizer/Transformer/WhitespacyCommentTransformer.php',
'PhpCsFixer\\Tokenizer\\Transformers' => __DIR__ . '/../..' . '/src/Tokenizer/Transformers.php',
'PhpCsFixer\\ToolInfo' => __DIR__ . '/../..' . '/src/ToolInfo.php',
'PhpCsFixer\\ToolInfoInterface' => __DIR__ . '/../..' . '/src/ToolInfoInterface.php',
'PhpCsFixer\\Utils' => __DIR__ . '/../..' . '/src/Utils.php',
'PhpCsFixer\\WhitespacesFixerConfig' => __DIR__ . '/../..' . '/src/WhitespacesFixerConfig.php',
'PhpCsFixer\\WordMatcher' => __DIR__ . '/../..' . '/src/WordMatcher.php',
'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
'Psr\\Cache\\CacheException' => __DIR__ . '/..' . '/psr/cache/src/CacheException.php',
'Psr\\Cache\\CacheItemInterface' => __DIR__ . '/..' . '/psr/cache/src/CacheItemInterface.php',
'Psr\\Cache\\CacheItemPoolInterface' => __DIR__ . '/..' . '/psr/cache/src/CacheItemPoolInterface.php',
'Psr\\Cache\\InvalidArgumentException' => __DIR__ . '/..' . '/psr/cache/src/InvalidArgumentException.php',
'Psr\\Container\\ContainerExceptionInterface' => __DIR__ . '/..' . '/psr/container/src/ContainerExceptionInterface.php',
'Psr\\Container\\ContainerInterface' => __DIR__ . '/..' . '/psr/container/src/ContainerInterface.php',
'Psr\\Container\\NotFoundExceptionInterface' => __DIR__ . '/..' . '/psr/container/src/NotFoundExceptionInterface.php',
'Psr\\EventDispatcher\\EventDispatcherInterface' => __DIR__ . '/..' . '/psr/event-dispatcher/src/EventDispatcherInterface.php',
'Psr\\EventDispatcher\\ListenerProviderInterface' => __DIR__ . '/..' . '/psr/event-dispatcher/src/ListenerProviderInterface.php',
'Psr\\EventDispatcher\\StoppableEventInterface' => __DIR__ . '/..' . '/psr/event-dispatcher/src/StoppableEventInterface.php',
'Psr\\Log\\AbstractLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/AbstractLogger.php',
'Psr\\Log\\InvalidArgumentException' => __DIR__ . '/..' . '/psr/log/Psr/Log/InvalidArgumentException.php',
'Psr\\Log\\LogLevel' => __DIR__ . '/..' . '/psr/log/Psr/Log/LogLevel.php',
'Psr\\Log\\LoggerAwareInterface' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerAwareInterface.php',
'Psr\\Log\\LoggerAwareTrait' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerAwareTrait.php',
'Psr\\Log\\LoggerInterface' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerInterface.php',
'Psr\\Log\\LoggerTrait' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerTrait.php',
'Psr\\Log\\NullLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/NullLogger.php',
'Psr\\Log\\Test\\DummyTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/DummyTest.php',
'Psr\\Log\\Test\\LoggerInterfaceTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php',
'Psr\\Log\\Test\\TestLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/TestLogger.php',
'ReturnTypeWillChange' => __DIR__ . '/..' . '/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php',
'SebastianBergmann\\Diff\\Chunk' => __DIR__ . '/..' . '/sebastian/diff/src/Chunk.php',
'SebastianBergmann\\Diff\\ConfigurationException' => __DIR__ . '/..' . '/sebastian/diff/src/Exception/ConfigurationException.php',
'SebastianBergmann\\Diff\\Diff' => __DIR__ . '/..' . '/sebastian/diff/src/Diff.php',
'SebastianBergmann\\Diff\\Differ' => __DIR__ . '/..' . '/sebastian/diff/src/Differ.php',
'SebastianBergmann\\Diff\\Exception' => __DIR__ . '/..' . '/sebastian/diff/src/Exception/Exception.php',
'SebastianBergmann\\Diff\\InvalidArgumentException' => __DIR__ . '/..' . '/sebastian/diff/src/Exception/InvalidArgumentException.php',
'SebastianBergmann\\Diff\\Line' => __DIR__ . '/..' . '/sebastian/diff/src/Line.php',
'SebastianBergmann\\Diff\\LongestCommonSubsequenceCalculator' => __DIR__ . '/..' . '/sebastian/diff/src/LongestCommonSubsequenceCalculator.php',
'SebastianBergmann\\Diff\\MemoryEfficientLongestCommonSubsequenceCalculator' => __DIR__ . '/..' . '/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php',
'SebastianBergmann\\Diff\\Output\\AbstractChunkOutputBuilder' => __DIR__ . '/..' . '/sebastian/diff/src/Output/AbstractChunkOutputBuilder.php',
'SebastianBergmann\\Diff\\Output\\DiffOnlyOutputBuilder' => __DIR__ . '/..' . '/sebastian/diff/src/Output/DiffOnlyOutputBuilder.php',
'SebastianBergmann\\Diff\\Output\\DiffOutputBuilderInterface' => __DIR__ . '/..' . '/sebastian/diff/src/Output/DiffOutputBuilderInterface.php',
'SebastianBergmann\\Diff\\Output\\StrictUnifiedDiffOutputBuilder' => __DIR__ . '/..' . '/sebastian/diff/src/Output/StrictUnifiedDiffOutputBuilder.php',
'SebastianBergmann\\Diff\\Output\\UnifiedDiffOutputBuilder' => __DIR__ . '/..' . '/sebastian/diff/src/Output/UnifiedDiffOutputBuilder.php',
'SebastianBergmann\\Diff\\Parser' => __DIR__ . '/..' . '/sebastian/diff/src/Parser.php',
'SebastianBergmann\\Diff\\TimeEfficientLongestCommonSubsequenceCalculator' => __DIR__ . '/..' . '/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php',
'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
'Symfony\\Component\\Console\\Application' => __DIR__ . '/..' . '/symfony/console/Application.php',
'Symfony\\Component\\Console\\Attribute\\AsCommand' => __DIR__ . '/..' . '/symfony/console/Attribute/AsCommand.php',
'Symfony\\Component\\Console\\CI\\GithubActionReporter' => __DIR__ . '/..' . '/symfony/console/CI/GithubActionReporter.php',
'Symfony\\Component\\Console\\Color' => __DIR__ . '/..' . '/symfony/console/Color.php',
'Symfony\\Component\\Console\\CommandLoader\\CommandLoaderInterface' => __DIR__ . '/..' . '/symfony/console/CommandLoader/CommandLoaderInterface.php',
'Symfony\\Component\\Console\\CommandLoader\\ContainerCommandLoader' => __DIR__ . '/..' . '/symfony/console/CommandLoader/ContainerCommandLoader.php',
'Symfony\\Component\\Console\\CommandLoader\\FactoryCommandLoader' => __DIR__ . '/..' . '/symfony/console/CommandLoader/FactoryCommandLoader.php',
'Symfony\\Component\\Console\\Command\\Command' => __DIR__ . '/..' . '/symfony/console/Command/Command.php',
'Symfony\\Component\\Console\\Command\\CompleteCommand' => __DIR__ . '/..' . '/symfony/console/Command/CompleteCommand.php',
'Symfony\\Component\\Console\\Command\\DumpCompletionCommand' => __DIR__ . '/..' . '/symfony/console/Command/DumpCompletionCommand.php',
'Symfony\\Component\\Console\\Command\\HelpCommand' => __DIR__ . '/..' . '/symfony/console/Command/HelpCommand.php',
'Symfony\\Component\\Console\\Command\\LazyCommand' => __DIR__ . '/..' . '/symfony/console/Command/LazyCommand.php',
'Symfony\\Component\\Console\\Command\\ListCommand' => __DIR__ . '/..' . '/symfony/console/Command/ListCommand.php',
'Symfony\\Component\\Console\\Command\\LockableTrait' => __DIR__ . '/..' . '/symfony/console/Command/LockableTrait.php',
'Symfony\\Component\\Console\\Command\\SignalableCommandInterface' => __DIR__ . '/..' . '/symfony/console/Command/SignalableCommandInterface.php',
'Symfony\\Component\\Console\\Completion\\CompletionInput' => __DIR__ . '/..' . '/symfony/console/Completion/CompletionInput.php',
'Symfony\\Component\\Console\\Completion\\CompletionSuggestions' => __DIR__ . '/..' . '/symfony/console/Completion/CompletionSuggestions.php',
'Symfony\\Component\\Console\\Completion\\Output\\BashCompletionOutput' => __DIR__ . '/..' . '/symfony/console/Completion/Output/BashCompletionOutput.php',
'Symfony\\Component\\Console\\Completion\\Output\\CompletionOutputInterface' => __DIR__ . '/..' . '/symfony/console/Completion/Output/CompletionOutputInterface.php',
'Symfony\\Component\\Console\\Completion\\Suggestion' => __DIR__ . '/..' . '/symfony/console/Completion/Suggestion.php',
'Symfony\\Component\\Console\\ConsoleEvents' => __DIR__ . '/..' . '/symfony/console/ConsoleEvents.php',
'Symfony\\Component\\Console\\Cursor' => __DIR__ . '/..' . '/symfony/console/Cursor.php',
'Symfony\\Component\\Console\\DependencyInjection\\AddConsoleCommandPass' => __DIR__ . '/..' . '/symfony/console/DependencyInjection/AddConsoleCommandPass.php',
'Symfony\\Component\\Console\\Descriptor\\ApplicationDescription' => __DIR__ . '/..' . '/symfony/console/Descriptor/ApplicationDescription.php',
'Symfony\\Component\\Console\\Descriptor\\Descriptor' => __DIR__ . '/..' . '/symfony/console/Descriptor/Descriptor.php',
'Symfony\\Component\\Console\\Descriptor\\DescriptorInterface' => __DIR__ . '/..' . '/symfony/console/Descriptor/DescriptorInterface.php',
'Symfony\\Component\\Console\\Descriptor\\JsonDescriptor' => __DIR__ . '/..' . '/symfony/console/Descriptor/JsonDescriptor.php',
'Symfony\\Component\\Console\\Descriptor\\MarkdownDescriptor' => __DIR__ . '/..' . '/symfony/console/Descriptor/MarkdownDescriptor.php',
'Symfony\\Component\\Console\\Descriptor\\TextDescriptor' => __DIR__ . '/..' . '/symfony/console/Descriptor/TextDescriptor.php',
'Symfony\\Component\\Console\\Descriptor\\XmlDescriptor' => __DIR__ . '/..' . '/symfony/console/Descriptor/XmlDescriptor.php',
'Symfony\\Component\\Console\\EventListener\\ErrorListener' => __DIR__ . '/..' . '/symfony/console/EventListener/ErrorListener.php',
'Symfony\\Component\\Console\\Event\\ConsoleCommandEvent' => __DIR__ . '/..' . '/symfony/console/Event/ConsoleCommandEvent.php',
'Symfony\\Component\\Console\\Event\\ConsoleErrorEvent' => __DIR__ . '/..' . '/symfony/console/Event/ConsoleErrorEvent.php',
'Symfony\\Component\\Console\\Event\\ConsoleEvent' => __DIR__ . '/..' . '/symfony/console/Event/ConsoleEvent.php',
'Symfony\\Component\\Console\\Event\\ConsoleSignalEvent' => __DIR__ . '/..' . '/symfony/console/Event/ConsoleSignalEvent.php',
'Symfony\\Component\\Console\\Event\\ConsoleTerminateEvent' => __DIR__ . '/..' . '/symfony/console/Event/ConsoleTerminateEvent.php',
'Symfony\\Component\\Console\\Exception\\CommandNotFoundException' => __DIR__ . '/..' . '/symfony/console/Exception/CommandNotFoundException.php',
'Symfony\\Component\\Console\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/console/Exception/ExceptionInterface.php',
'Symfony\\Component\\Console\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/symfony/console/Exception/InvalidArgumentException.php',
'Symfony\\Component\\Console\\Exception\\InvalidOptionException' => __DIR__ . '/..' . '/symfony/console/Exception/InvalidOptionException.php',
'Symfony\\Component\\Console\\Exception\\LogicException' => __DIR__ . '/..' . '/symfony/console/Exception/LogicException.php',
'Symfony\\Component\\Console\\Exception\\MissingInputException' => __DIR__ . '/..' . '/symfony/console/Exception/MissingInputException.php',
'Symfony\\Component\\Console\\Exception\\NamespaceNotFoundException' => __DIR__ . '/..' . '/symfony/console/Exception/NamespaceNotFoundException.php',
'Symfony\\Component\\Console\\Exception\\RuntimeException' => __DIR__ . '/..' . '/symfony/console/Exception/RuntimeException.php',
'Symfony\\Component\\Console\\Formatter\\NullOutputFormatter' => __DIR__ . '/..' . '/symfony/console/Formatter/NullOutputFormatter.php',
'Symfony\\Component\\Console\\Formatter\\NullOutputFormatterStyle' => __DIR__ . '/..' . '/symfony/console/Formatter/NullOutputFormatterStyle.php',
'Symfony\\Component\\Console\\Formatter\\OutputFormatter' => __DIR__ . '/..' . '/symfony/console/Formatter/OutputFormatter.php',
'Symfony\\Component\\Console\\Formatter\\OutputFormatterInterface' => __DIR__ . '/..' . '/symfony/console/Formatter/OutputFormatterInterface.php',
'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyle' => __DIR__ . '/..' . '/symfony/console/Formatter/OutputFormatterStyle.php',
'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyleInterface' => __DIR__ . '/..' . '/symfony/console/Formatter/OutputFormatterStyleInterface.php',
'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyleStack' => __DIR__ . '/..' . '/symfony/console/Formatter/OutputFormatterStyleStack.php',
'Symfony\\Component\\Console\\Formatter\\WrappableOutputFormatterInterface' => __DIR__ . '/..' . '/symfony/console/Formatter/WrappableOutputFormatterInterface.php',
'Symfony\\Component\\Console\\Helper\\DebugFormatterHelper' => __DIR__ . '/..' . '/symfony/console/Helper/DebugFormatterHelper.php',
'Symfony\\Component\\Console\\Helper\\DescriptorHelper' => __DIR__ . '/..' . '/symfony/console/Helper/DescriptorHelper.php',
'Symfony\\Component\\Console\\Helper\\Dumper' => __DIR__ . '/..' . '/symfony/console/Helper/Dumper.php',
'Symfony\\Component\\Console\\Helper\\FormatterHelper' => __DIR__ . '/..' . '/symfony/console/Helper/FormatterHelper.php',
'Symfony\\Component\\Console\\Helper\\Helper' => __DIR__ . '/..' . '/symfony/console/Helper/Helper.php',
'Symfony\\Component\\Console\\Helper\\HelperInterface' => __DIR__ . '/..' . '/symfony/console/Helper/HelperInterface.php',
'Symfony\\Component\\Console\\Helper\\HelperSet' => __DIR__ . '/..' . '/symfony/console/Helper/HelperSet.php',
'Symfony\\Component\\Console\\Helper\\InputAwareHelper' => __DIR__ . '/..' . '/symfony/console/Helper/InputAwareHelper.php',
'Symfony\\Component\\Console\\Helper\\ProcessHelper' => __DIR__ . '/..' . '/symfony/console/Helper/ProcessHelper.php',
'Symfony\\Component\\Console\\Helper\\ProgressBar' => __DIR__ . '/..' . '/symfony/console/Helper/ProgressBar.php',
'Symfony\\Component\\Console\\Helper\\ProgressIndicator' => __DIR__ . '/..' . '/symfony/console/Helper/ProgressIndicator.php',
'Symfony\\Component\\Console\\Helper\\QuestionHelper' => __DIR__ . '/..' . '/symfony/console/Helper/QuestionHelper.php',
'Symfony\\Component\\Console\\Helper\\SymfonyQuestionHelper' => __DIR__ . '/..' . '/symfony/console/Helper/SymfonyQuestionHelper.php',
'Symfony\\Component\\Console\\Helper\\Table' => __DIR__ . '/..' . '/symfony/console/Helper/Table.php',
'Symfony\\Component\\Console\\Helper\\TableCell' => __DIR__ . '/..' . '/symfony/console/Helper/TableCell.php',
'Symfony\\Component\\Console\\Helper\\TableCellStyle' => __DIR__ . '/..' . '/symfony/console/Helper/TableCellStyle.php',
'Symfony\\Component\\Console\\Helper\\TableRows' => __DIR__ . '/..' . '/symfony/console/Helper/TableRows.php',
'Symfony\\Component\\Console\\Helper\\TableSeparator' => __DIR__ . '/..' . '/symfony/console/Helper/TableSeparator.php',
'Symfony\\Component\\Console\\Helper\\TableStyle' => __DIR__ . '/..' . '/symfony/console/Helper/TableStyle.php',
'Symfony\\Component\\Console\\Input\\ArgvInput' => __DIR__ . '/..' . '/symfony/console/Input/ArgvInput.php',
'Symfony\\Component\\Console\\Input\\ArrayInput' => __DIR__ . '/..' . '/symfony/console/Input/ArrayInput.php',
'Symfony\\Component\\Console\\Input\\Input' => __DIR__ . '/..' . '/symfony/console/Input/Input.php',
'Symfony\\Component\\Console\\Input\\InputArgument' => __DIR__ . '/..' . '/symfony/console/Input/InputArgument.php',
'Symfony\\Component\\Console\\Input\\InputAwareInterface' => __DIR__ . '/..' . '/symfony/console/Input/InputAwareInterface.php',
'Symfony\\Component\\Console\\Input\\InputDefinition' => __DIR__ . '/..' . '/symfony/console/Input/InputDefinition.php',
'Symfony\\Component\\Console\\Input\\InputInterface' => __DIR__ . '/..' . '/symfony/console/Input/InputInterface.php',
'Symfony\\Component\\Console\\Input\\InputOption' => __DIR__ . '/..' . '/symfony/console/Input/InputOption.php',
'Symfony\\Component\\Console\\Input\\StreamableInputInterface' => __DIR__ . '/..' . '/symfony/console/Input/StreamableInputInterface.php',
'Symfony\\Component\\Console\\Input\\StringInput' => __DIR__ . '/..' . '/symfony/console/Input/StringInput.php',
'Symfony\\Component\\Console\\Logger\\ConsoleLogger' => __DIR__ . '/..' . '/symfony/console/Logger/ConsoleLogger.php',
'Symfony\\Component\\Console\\Output\\BufferedOutput' => __DIR__ . '/..' . '/symfony/console/Output/BufferedOutput.php',
'Symfony\\Component\\Console\\Output\\ConsoleOutput' => __DIR__ . '/..' . '/symfony/console/Output/ConsoleOutput.php',
'Symfony\\Component\\Console\\Output\\ConsoleOutputInterface' => __DIR__ . '/..' . '/symfony/console/Output/ConsoleOutputInterface.php',
'Symfony\\Component\\Console\\Output\\ConsoleSectionOutput' => __DIR__ . '/..' . '/symfony/console/Output/ConsoleSectionOutput.php',
'Symfony\\Component\\Console\\Output\\NullOutput' => __DIR__ . '/..' . '/symfony/console/Output/NullOutput.php',
'Symfony\\Component\\Console\\Output\\Output' => __DIR__ . '/..' . '/symfony/console/Output/Output.php',
'Symfony\\Component\\Console\\Output\\OutputInterface' => __DIR__ . '/..' . '/symfony/console/Output/OutputInterface.php',
'Symfony\\Component\\Console\\Output\\StreamOutput' => __DIR__ . '/..' . '/symfony/console/Output/StreamOutput.php',
'Symfony\\Component\\Console\\Output\\TrimmedBufferOutput' => __DIR__ . '/..' . '/symfony/console/Output/TrimmedBufferOutput.php',
'Symfony\\Component\\Console\\Question\\ChoiceQuestion' => __DIR__ . '/..' . '/symfony/console/Question/ChoiceQuestion.php',
'Symfony\\Component\\Console\\Question\\ConfirmationQuestion' => __DIR__ . '/..' . '/symfony/console/Question/ConfirmationQuestion.php',
'Symfony\\Component\\Console\\Question\\Question' => __DIR__ . '/..' . '/symfony/console/Question/Question.php',
'Symfony\\Component\\Console\\SignalRegistry\\SignalRegistry' => __DIR__ . '/..' . '/symfony/console/SignalRegistry/SignalRegistry.php',
'Symfony\\Component\\Console\\SingleCommandApplication' => __DIR__ . '/..' . '/symfony/console/SingleCommandApplication.php',
'Symfony\\Component\\Console\\Style\\OutputStyle' => __DIR__ . '/..' . '/symfony/console/Style/OutputStyle.php',
'Symfony\\Component\\Console\\Style\\StyleInterface' => __DIR__ . '/..' . '/symfony/console/Style/StyleInterface.php',
'Symfony\\Component\\Console\\Style\\SymfonyStyle' => __DIR__ . '/..' . '/symfony/console/Style/SymfonyStyle.php',
'Symfony\\Component\\Console\\Terminal' => __DIR__ . '/..' . '/symfony/console/Terminal.php',
'Symfony\\Component\\Console\\Tester\\ApplicationTester' => __DIR__ . '/..' . '/symfony/console/Tester/ApplicationTester.php',
'Symfony\\Component\\Console\\Tester\\CommandCompletionTester' => __DIR__ . '/..' . '/symfony/console/Tester/CommandCompletionTester.php',
'Symfony\\Component\\Console\\Tester\\CommandTester' => __DIR__ . '/..' . '/symfony/console/Tester/CommandTester.php',
'Symfony\\Component\\Console\\Tester\\Constraint\\CommandIsSuccessful' => __DIR__ . '/..' . '/symfony/console/Tester/Constraint/CommandIsSuccessful.php',
'Symfony\\Component\\Console\\Tester\\TesterTrait' => __DIR__ . '/..' . '/symfony/console/Tester/TesterTrait.php',
'Symfony\\Component\\EventDispatcher\\Attribute\\AsEventListener' => __DIR__ . '/..' . '/symfony/event-dispatcher/Attribute/AsEventListener.php',
'Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher' => __DIR__ . '/..' . '/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php',
'Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener' => __DIR__ . '/..' . '/symfony/event-dispatcher/Debug/WrappedListener.php',
'Symfony\\Component\\EventDispatcher\\DependencyInjection\\AddEventAliasesPass' => __DIR__ . '/..' . '/symfony/event-dispatcher/DependencyInjection/AddEventAliasesPass.php',
'Symfony\\Component\\EventDispatcher\\DependencyInjection\\RegisterListenersPass' => __DIR__ . '/..' . '/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php',
'Symfony\\Component\\EventDispatcher\\EventDispatcher' => __DIR__ . '/..' . '/symfony/event-dispatcher/EventDispatcher.php',
'Symfony\\Component\\EventDispatcher\\EventDispatcherInterface' => __DIR__ . '/..' . '/symfony/event-dispatcher/EventDispatcherInterface.php',
'Symfony\\Component\\EventDispatcher\\EventSubscriberInterface' => __DIR__ . '/..' . '/symfony/event-dispatcher/EventSubscriberInterface.php',
'Symfony\\Component\\EventDispatcher\\GenericEvent' => __DIR__ . '/..' . '/symfony/event-dispatcher/GenericEvent.php',
'Symfony\\Component\\EventDispatcher\\ImmutableEventDispatcher' => __DIR__ . '/..' . '/symfony/event-dispatcher/ImmutableEventDispatcher.php',
'Symfony\\Component\\EventDispatcher\\LegacyEventDispatcherProxy' => __DIR__ . '/..' . '/symfony/event-dispatcher/LegacyEventDispatcherProxy.php',
'Symfony\\Component\\Filesystem\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/filesystem/Exception/ExceptionInterface.php',
'Symfony\\Component\\Filesystem\\Exception\\FileNotFoundException' => __DIR__ . '/..' . '/symfony/filesystem/Exception/FileNotFoundException.php',
'Symfony\\Component\\Filesystem\\Exception\\IOException' => __DIR__ . '/..' . '/symfony/filesystem/Exception/IOException.php',
'Symfony\\Component\\Filesystem\\Exception\\IOExceptionInterface' => __DIR__ . '/..' . '/symfony/filesystem/Exception/IOExceptionInterface.php',
'Symfony\\Component\\Filesystem\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/symfony/filesystem/Exception/InvalidArgumentException.php',
'Symfony\\Component\\Filesystem\\Exception\\RuntimeException' => __DIR__ . '/..' . '/symfony/filesystem/Exception/RuntimeException.php',
'Symfony\\Component\\Filesystem\\Filesystem' => __DIR__ . '/..' . '/symfony/filesystem/Filesystem.php',
'Symfony\\Component\\Filesystem\\Path' => __DIR__ . '/..' . '/symfony/filesystem/Path.php',
'Symfony\\Component\\Finder\\Comparator\\Comparator' => __DIR__ . '/..' . '/symfony/finder/Comparator/Comparator.php',
'Symfony\\Component\\Finder\\Comparator\\DateComparator' => __DIR__ . '/..' . '/symfony/finder/Comparator/DateComparator.php',
'Symfony\\Component\\Finder\\Comparator\\NumberComparator' => __DIR__ . '/..' . '/symfony/finder/Comparator/NumberComparator.php',
'Symfony\\Component\\Finder\\Exception\\AccessDeniedException' => __DIR__ . '/..' . '/symfony/finder/Exception/AccessDeniedException.php',
'Symfony\\Component\\Finder\\Exception\\DirectoryNotFoundException' => __DIR__ . '/..' . '/symfony/finder/Exception/DirectoryNotFoundException.php',
'Symfony\\Component\\Finder\\Finder' => __DIR__ . '/..' . '/symfony/finder/Finder.php',
'Symfony\\Component\\Finder\\Gitignore' => __DIR__ . '/..' . '/symfony/finder/Gitignore.php',
'Symfony\\Component\\Finder\\Glob' => __DIR__ . '/..' . '/symfony/finder/Glob.php',
'Symfony\\Component\\Finder\\Iterator\\CustomFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/CustomFilterIterator.php',
'Symfony\\Component\\Finder\\Iterator\\DateRangeFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/DateRangeFilterIterator.php',
'Symfony\\Component\\Finder\\Iterator\\DepthRangeFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/DepthRangeFilterIterator.php',
'Symfony\\Component\\Finder\\Iterator\\ExcludeDirectoryFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php',
'Symfony\\Component\\Finder\\Iterator\\FileTypeFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/FileTypeFilterIterator.php',
'Symfony\\Component\\Finder\\Iterator\\FilecontentFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/FilecontentFilterIterator.php',
'Symfony\\Component\\Finder\\Iterator\\FilenameFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/FilenameFilterIterator.php',
'Symfony\\Component\\Finder\\Iterator\\LazyIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/LazyIterator.php',
'Symfony\\Component\\Finder\\Iterator\\MultiplePcreFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/MultiplePcreFilterIterator.php',
'Symfony\\Component\\Finder\\Iterator\\PathFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/PathFilterIterator.php',
'Symfony\\Component\\Finder\\Iterator\\RecursiveDirectoryIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/RecursiveDirectoryIterator.php',
'Symfony\\Component\\Finder\\Iterator\\SizeRangeFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/SizeRangeFilterIterator.php',
'Symfony\\Component\\Finder\\Iterator\\SortableIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/SortableIterator.php',
'Symfony\\Component\\Finder\\Iterator\\VcsIgnoredFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/VcsIgnoredFilterIterator.php',
'Symfony\\Component\\Finder\\SplFileInfo' => __DIR__ . '/..' . '/symfony/finder/SplFileInfo.php',
'Symfony\\Component\\OptionsResolver\\Debug\\OptionsResolverIntrospector' => __DIR__ . '/..' . '/symfony/options-resolver/Debug/OptionsResolverIntrospector.php',
'Symfony\\Component\\OptionsResolver\\Exception\\AccessException' => __DIR__ . '/..' . '/symfony/options-resolver/Exception/AccessException.php',
'Symfony\\Component\\OptionsResolver\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/options-resolver/Exception/ExceptionInterface.php',
'Symfony\\Component\\OptionsResolver\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/symfony/options-resolver/Exception/InvalidArgumentException.php',
'Symfony\\Component\\OptionsResolver\\Exception\\InvalidOptionsException' => __DIR__ . '/..' . '/symfony/options-resolver/Exception/InvalidOptionsException.php',
'Symfony\\Component\\OptionsResolver\\Exception\\MissingOptionsException' => __DIR__ . '/..' . '/symfony/options-resolver/Exception/MissingOptionsException.php',
'Symfony\\Component\\OptionsResolver\\Exception\\NoConfigurationException' => __DIR__ . '/..' . '/symfony/options-resolver/Exception/NoConfigurationException.php',
'Symfony\\Component\\OptionsResolver\\Exception\\NoSuchOptionException' => __DIR__ . '/..' . '/symfony/options-resolver/Exception/NoSuchOptionException.php',
'Symfony\\Component\\OptionsResolver\\Exception\\OptionDefinitionException' => __DIR__ . '/..' . '/symfony/options-resolver/Exception/OptionDefinitionException.php',
'Symfony\\Component\\OptionsResolver\\Exception\\UndefinedOptionsException' => __DIR__ . '/..' . '/symfony/options-resolver/Exception/UndefinedOptionsException.php',
'Symfony\\Component\\OptionsResolver\\OptionConfigurator' => __DIR__ . '/..' . '/symfony/options-resolver/OptionConfigurator.php',
'Symfony\\Component\\OptionsResolver\\Options' => __DIR__ . '/..' . '/symfony/options-resolver/Options.php',
'Symfony\\Component\\OptionsResolver\\OptionsResolver' => __DIR__ . '/..' . '/symfony/options-resolver/OptionsResolver.php',
'Symfony\\Component\\Process\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/process/Exception/ExceptionInterface.php',
'Symfony\\Component\\Process\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/symfony/process/Exception/InvalidArgumentException.php',
'Symfony\\Component\\Process\\Exception\\LogicException' => __DIR__ . '/..' . '/symfony/process/Exception/LogicException.php',
'Symfony\\Component\\Process\\Exception\\ProcessFailedException' => __DIR__ . '/..' . '/symfony/process/Exception/ProcessFailedException.php',
'Symfony\\Component\\Process\\Exception\\ProcessSignaledException' => __DIR__ . '/..' . '/symfony/process/Exception/ProcessSignaledException.php',
'Symfony\\Component\\Process\\Exception\\ProcessTimedOutException' => __DIR__ . '/..' . '/symfony/process/Exception/ProcessTimedOutException.php',
'Symfony\\Component\\Process\\Exception\\RuntimeException' => __DIR__ . '/..' . '/symfony/process/Exception/RuntimeException.php',
'Symfony\\Component\\Process\\ExecutableFinder' => __DIR__ . '/..' . '/symfony/process/ExecutableFinder.php',
'Symfony\\Component\\Process\\InputStream' => __DIR__ . '/..' . '/symfony/process/InputStream.php',
'Symfony\\Component\\Process\\PhpExecutableFinder' => __DIR__ . '/..' . '/symfony/process/PhpExecutableFinder.php',
'Symfony\\Component\\Process\\PhpProcess' => __DIR__ . '/..' . '/symfony/process/PhpProcess.php',
'Symfony\\Component\\Process\\Pipes\\AbstractPipes' => __DIR__ . '/..' . '/symfony/process/Pipes/AbstractPipes.php',
'Symfony\\Component\\Process\\Pipes\\PipesInterface' => __DIR__ . '/..' . '/symfony/process/Pipes/PipesInterface.php',
'Symfony\\Component\\Process\\Pipes\\UnixPipes' => __DIR__ . '/..' . '/symfony/process/Pipes/UnixPipes.php',
'Symfony\\Component\\Process\\Pipes\\WindowsPipes' => __DIR__ . '/..' . '/symfony/process/Pipes/WindowsPipes.php',
'Symfony\\Component\\Process\\Process' => __DIR__ . '/..' . '/symfony/process/Process.php',
'Symfony\\Component\\Process\\ProcessUtils' => __DIR__ . '/..' . '/symfony/process/ProcessUtils.php',
'Symfony\\Component\\Stopwatch\\Section' => __DIR__ . '/..' . '/symfony/stopwatch/Section.php',
'Symfony\\Component\\Stopwatch\\Stopwatch' => __DIR__ . '/..' . '/symfony/stopwatch/Stopwatch.php',
'Symfony\\Component\\Stopwatch\\StopwatchEvent' => __DIR__ . '/..' . '/symfony/stopwatch/StopwatchEvent.php',
'Symfony\\Component\\Stopwatch\\StopwatchPeriod' => __DIR__ . '/..' . '/symfony/stopwatch/StopwatchPeriod.php',
'Symfony\\Component\\String\\AbstractString' => __DIR__ . '/..' . '/symfony/string/AbstractString.php',
'Symfony\\Component\\String\\AbstractUnicodeString' => __DIR__ . '/..' . '/symfony/string/AbstractUnicodeString.php',
'Symfony\\Component\\String\\ByteString' => __DIR__ . '/..' . '/symfony/string/ByteString.php',
'Symfony\\Component\\String\\CodePointString' => __DIR__ . '/..' . '/symfony/string/CodePointString.php',
'Symfony\\Component\\String\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/string/Exception/ExceptionInterface.php',
'Symfony\\Component\\String\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/symfony/string/Exception/InvalidArgumentException.php',
'Symfony\\Component\\String\\Exception\\RuntimeException' => __DIR__ . '/..' . '/symfony/string/Exception/RuntimeException.php',
'Symfony\\Component\\String\\Inflector\\EnglishInflector' => __DIR__ . '/..' . '/symfony/string/Inflector/EnglishInflector.php',
'Symfony\\Component\\String\\Inflector\\FrenchInflector' => __DIR__ . '/..' . '/symfony/string/Inflector/FrenchInflector.php',
'Symfony\\Component\\String\\Inflector\\InflectorInterface' => __DIR__ . '/..' . '/symfony/string/Inflector/InflectorInterface.php',
'Symfony\\Component\\String\\LazyString' => __DIR__ . '/..' . '/symfony/string/LazyString.php',
'Symfony\\Component\\String\\Slugger\\AsciiSlugger' => __DIR__ . '/..' . '/symfony/string/Slugger/AsciiSlugger.php',
'Symfony\\Component\\String\\Slugger\\SluggerInterface' => __DIR__ . '/..' . '/symfony/string/Slugger/SluggerInterface.php',
'Symfony\\Component\\String\\UnicodeString' => __DIR__ . '/..' . '/symfony/string/UnicodeString.php',
'Symfony\\Contracts\\EventDispatcher\\Event' => __DIR__ . '/..' . '/symfony/event-dispatcher-contracts/Event.php',
'Symfony\\Contracts\\EventDispatcher\\EventDispatcherInterface' => __DIR__ . '/..' . '/symfony/event-dispatcher-contracts/EventDispatcherInterface.php',
'Symfony\\Contracts\\Service\\Attribute\\Required' => __DIR__ . '/..' . '/symfony/service-contracts/Attribute/Required.php',
'Symfony\\Contracts\\Service\\Attribute\\SubscribedService' => __DIR__ . '/..' . '/symfony/service-contracts/Attribute/SubscribedService.php',
'Symfony\\Contracts\\Service\\ResetInterface' => __DIR__ . '/..' . '/symfony/service-contracts/ResetInterface.php',
'Symfony\\Contracts\\Service\\ServiceLocatorTrait' => __DIR__ . '/..' . '/symfony/service-contracts/ServiceLocatorTrait.php',
'Symfony\\Contracts\\Service\\ServiceProviderInterface' => __DIR__ . '/..' . '/symfony/service-contracts/ServiceProviderInterface.php',
'Symfony\\Contracts\\Service\\ServiceSubscriberInterface' => __DIR__ . '/..' . '/symfony/service-contracts/ServiceSubscriberInterface.php',
'Symfony\\Contracts\\Service\\ServiceSubscriberTrait' => __DIR__ . '/..' . '/symfony/service-contracts/ServiceSubscriberTrait.php',
'Symfony\\Contracts\\Service\\Test\\ServiceLocatorTest' => __DIR__ . '/..' . '/symfony/service-contracts/Test/ServiceLocatorTest.php',
'Symfony\\Polyfill\\Ctype\\Ctype' => __DIR__ . '/..' . '/symfony/polyfill-ctype/Ctype.php',
'Symfony\\Polyfill\\Intl\\Grapheme\\Grapheme' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/Grapheme.php',
'Symfony\\Polyfill\\Intl\\Normalizer\\Normalizer' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/Normalizer.php',
'Symfony\\Polyfill\\Mbstring\\Mbstring' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/Mbstring.php',
'Symfony\\Polyfill\\Php73\\Php73' => __DIR__ . '/..' . '/symfony/polyfill-php73/Php73.php',
'Symfony\\Polyfill\\Php80\\Php80' => __DIR__ . '/..' . '/symfony/polyfill-php80/Php80.php',
'Symfony\\Polyfill\\Php80\\PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/PhpToken.php',
'Symfony\\Polyfill\\Php81\\Php81' => __DIR__ . '/..' . '/symfony/polyfill-php81/Php81.php',
'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit43f33e2f7f39ed6f3341269681ebd52e::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit43f33e2f7f39ed6f3341269681ebd52e::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit43f33e2f7f39ed6f3341269681ebd52e::$classMap;
}, null, ClassLoader::class);
}
}
<?php
class ComposerAutoloaderInit43f33e2f7f39ed6f3341269681ebd52e
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInit43f33e2f7f39ed6f3341269681ebd52e', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInit43f33e2f7f39ed6f3341269681ebd52e', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit43f33e2f7f39ed6f3341269681ebd52e::getInitializer($loader));
$loader->register(true);
$includeFiles = \Composer\Autoload\ComposerStaticInit43f33e2f7f39ed6f3341269681ebd52e::$files;
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire43f33e2f7f39ed6f3341269681ebd52e($fileIdentifier, $file);
}
return $loader;
}
}
function composerRequire43f33e2f7f39ed6f3341269681ebd52e($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
require $file;
}
}
<?php
namespace Composer\Semver;
use Composer\Semver\Constraint\Constraint;
use Composer\Semver\Constraint\ConstraintInterface;
class CompilingMatcher
{
/**
@phpstan-var
*/
private static $compiledCheckerCache = array();
/**
@phpstan-var
*/
private static $resultCache = array();
private static $enabled;
/**
@phpstan-var
*/
private static $transOpInt = array(
Constraint::OP_EQ => Constraint::STR_OP_EQ,
Constraint::OP_LT => Constraint::STR_OP_LT,
Constraint::OP_LE => Constraint::STR_OP_LE,
Constraint::OP_GT => Constraint::STR_OP_GT,
Constraint::OP_GE => Constraint::STR_OP_GE,
Constraint::OP_NE => Constraint::STR_OP_NE,
);
public static function clear()
{
self::$resultCache = array();
self::$compiledCheckerCache = array();
}
/**
@phpstan-param
*/
public static function match(ConstraintInterface $constraint, $operator, $version)
{
$resultCacheKey = $operator.$constraint.';'.$version;
if (isset(self::$resultCache[$resultCacheKey])) {
return self::$resultCache[$resultCacheKey];
}
if (self::$enabled === null) {
self::$enabled = !\in_array('eval', explode(',', (string) ini_get('disable_functions')), true);
}
if (!self::$enabled) {
return self::$resultCache[$resultCacheKey] = $constraint->matches(new Constraint(self::$transOpInt[$operator], $version));
}
$cacheKey = $operator.$constraint;
if (!isset(self::$compiledCheckerCache[$cacheKey])) {
$code = $constraint->compile($operator);
self::$compiledCheckerCache[$cacheKey] = $function = eval('return function($v, $b){return '.$code.';};');
} else {
$function = self::$compiledCheckerCache[$cacheKey];
}
return self::$resultCache[$resultCacheKey] = $function($version, strpos($version, 'dev-') === 0);
}
}
<?php
namespace Composer\Semver;
use Composer\Semver\Constraint\Constraint;
class Interval
{
private $start;
private $end;
public function __construct(Constraint $start, Constraint $end)
{
$this->start = $start;
$this->end = $end;
}
public function getStart()
{
return $this->start;
}
public function getEnd()
{
return $this->end;
}
public static function fromZero()
{
static $zero;
if (null === $zero) {
$zero = new Constraint('>=', '0.0.0.0-dev');
}
return $zero;
}
public static function untilPositiveInfinity()
{
static $positiveInfinity;
if (null === $positiveInfinity) {
$positiveInfinity = new Constraint('<', PHP_INT_MAX.'.0.0.0');
}
return $positiveInfinity;
}
public static function any()
{
return new self(self::fromZero(), self::untilPositiveInfinity());
}
public static function anyDev()
{
return array('names' => array(), 'exclude' => true);
}
public static function noDev()
{
return array('names' => array(), 'exclude' => false);
}
}
<?php
namespace Composer\Semver;
use Composer\Semver\Constraint\Constraint;
class Comparator
{
public static function greaterThan($version1, $version2)
{
return self::compare($version1, '>', $version2);
}
public static function greaterThanOrEqualTo($version1, $version2)
{
return self::compare($version1, '>=', $version2);
}
public static function lessThan($version1, $version2)
{
return self::compare($version1, '<', $version2);
}
public static function lessThanOrEqualTo($version1, $version2)
{
return self::compare($version1, '<=', $version2);
}
public static function equalTo($version1, $version2)
{
return self::compare($version1, '==', $version2);
}
public static function notEqualTo($version1, $version2)
{
return self::compare($version1, '!=', $version2);
}
/**
@phpstan-param
*/
public static function compare($version1, $operator, $version2)
{
$constraint = new Constraint($operator, $version2);
return $constraint->matchSpecific(new Constraint('==', $version1), true);
}
}
<?php
namespace Composer\Semver;
use Composer\Semver\Constraint\Constraint;
use Composer\Semver\Constraint\ConstraintInterface;
use Composer\Semver\Constraint\MatchAllConstraint;
use Composer\Semver\Constraint\MatchNoneConstraint;
use Composer\Semver\Constraint\MultiConstraint;
class Intervals
{
/**
@phpstan-var
*/
private static $intervalsCache = array();
/**
@phpstan-var
*/
private static $opSortOrder = array(
'>=' => -3,
'<' => -2,
'>' => 2,
'<=' => 3,
);
public static function clear()
{
self::$intervalsCache = array();
}
public static function isSubsetOf(ConstraintInterface $candidate, ConstraintInterface $constraint)
{
if ($constraint instanceof MatchAllConstraint) {
return true;
}
if ($candidate instanceof MatchNoneConstraint || $constraint instanceof MatchNoneConstraint) {
return false;
}
$intersectionIntervals = self::get(new MultiConstraint(array($candidate, $constraint), true));
$candidateIntervals = self::get($candidate);
if (\count($intersectionIntervals['numeric']) !== \count($candidateIntervals['numeric'])) {
return false;
}
foreach ($intersectionIntervals['numeric'] as $index => $interval) {
if (!isset($candidateIntervals['numeric'][$index])) {
return false;
}
if ((string) $candidateIntervals['numeric'][$index]->getStart() !== (string) $interval->getStart()) {
return false;
}
if ((string) $candidateIntervals['numeric'][$index]->getEnd() !== (string) $interval->getEnd()) {
return false;
}
}
if ($intersectionIntervals['branches']['exclude'] !== $candidateIntervals['branches']['exclude']) {
return false;
}
if (\count($intersectionIntervals['branches']['names']) !== \count($candidateIntervals['branches']['names'])) {
return false;
}
foreach ($intersectionIntervals['branches']['names'] as $index => $name) {
if ($name !== $candidateIntervals['branches']['names'][$index]) {
return false;
}
}
return true;
}
public static function haveIntersections(ConstraintInterface $a, ConstraintInterface $b)
{
if ($a instanceof MatchAllConstraint || $b instanceof MatchAllConstraint) {
return true;
}
if ($a instanceof MatchNoneConstraint || $b instanceof MatchNoneConstraint) {
return false;
}
$intersectionIntervals = self::generateIntervals(new MultiConstraint(array($a, $b), true), true);
return \count($intersectionIntervals['numeric']) > 0 || $intersectionIntervals['branches']['exclude'] || \count($intersectionIntervals['branches']['names']) > 0;
}
public static function compactConstraint(ConstraintInterface $constraint)
{
if (!$constraint instanceof MultiConstraint) {
return $constraint;
}
$intervals = self::generateIntervals($constraint);
$constraints = array();
$hasNumericMatchAll = false;
if (\count($intervals['numeric']) === 1 && (string) $intervals['numeric'][0]->getStart() === (string) Interval::fromZero() && (string) $intervals['numeric'][0]->getEnd() === (string) Interval::untilPositiveInfinity()) {
$constraints[] = $intervals['numeric'][0]->getStart();
$hasNumericMatchAll = true;
} else {
$unEqualConstraints = array();
for ($i = 0, $count = \count($intervals['numeric']); $i < $count; $i++) {
$interval = $intervals['numeric'][$i];
if ($interval->getEnd()->getOperator() === '<' && $i+1 < $count) {
$nextInterval = $intervals['numeric'][$i+1];
if ($interval->getEnd()->getVersion() === $nextInterval->getStart()->getVersion() && $nextInterval->getStart()->getOperator() === '>') {
if (\count($unEqualConstraints) === 0 && (string) $interval->getStart() !== (string) Interval::fromZero()) {
$unEqualConstraints[] = $interval->getStart();
}
$unEqualConstraints[] = new Constraint('!=', $interval->getEnd()->getVersion());
continue;
}
}
if (\count($unEqualConstraints) > 0) {
if ((string) $interval->getEnd() !== (string) Interval::untilPositiveInfinity()) {
$unEqualConstraints[] = $interval->getEnd();
}
if (\count($unEqualConstraints) > 1) {
$constraints[] = new MultiConstraint($unEqualConstraints, true);
} else {
$constraints[] = $unEqualConstraints[0];
}
$unEqualConstraints = array();
continue;
}
if ($interval->getStart()->getVersion() === $interval->getEnd()->getVersion() && $interval->getStart()->getOperator() === '>=' && $interval->getEnd()->getOperator() === '<=') {
$constraints[] = new Constraint('==', $interval->getStart()->getVersion());
continue;
}
if ((string) $interval->getStart() === (string) Interval::fromZero()) {
$constraints[] = $interval->getEnd();
} elseif ((string) $interval->getEnd() === (string) Interval::untilPositiveInfinity()) {
$constraints[] = $interval->getStart();
} else {
$constraints[] = new MultiConstraint(array($interval->getStart(), $interval->getEnd()), true);
}
}
}
$devConstraints = array();
if (0 === \count($intervals['branches']['names'])) {
if ($intervals['branches']['exclude']) {
if ($hasNumericMatchAll) {
return new MatchAllConstraint;
}
}
} else {
foreach ($intervals['branches']['names'] as $branchName) {
if ($intervals['branches']['exclude']) {
$devConstraints[] = new Constraint('!=', $branchName);
} else {
$devConstraints[] = new Constraint('==', $branchName);
}
}
if ($intervals['branches']['exclude']) {
if (\count($constraints) > 1) {
return new MultiConstraint(array_merge(
array(new MultiConstraint($constraints, false)),
$devConstraints
), true);
}
if (\count($constraints) === 1 && (string)$constraints[0] === (string)Interval::fromZero()) {
if (\count($devConstraints) > 1) {
return new MultiConstraint($devConstraints, true);
}
return $devConstraints[0];
}
return new MultiConstraint(array_merge($constraints, $devConstraints), true);
}
$constraints = array_merge($constraints, $devConstraints);
}
if (\count($constraints) > 1) {
return new MultiConstraint($constraints, false);
}
if (\count($constraints) === 1) {
return $constraints[0];
}
return new MatchNoneConstraint;
}
/**
@phpstan-return
*/
public static function get(ConstraintInterface $constraint)
{
$key = (string) $constraint;
if (!isset(self::$intervalsCache[$key])) {
self::$intervalsCache[$key] = self::generateIntervals($constraint);
}
return self::$intervalsCache[$key];
}
/**
@phpstan-return
*/
private static function generateIntervals(ConstraintInterface $constraint, $stopOnFirstValidInterval = false)
{
if ($constraint instanceof MatchAllConstraint) {
return array('numeric' => array(new Interval(Interval::fromZero(), Interval::untilPositiveInfinity())), 'branches' => Interval::anyDev());
}
if ($constraint instanceof MatchNoneConstraint) {
return array('numeric' => array(), 'branches' => array('names' => array(), 'exclude' => false));
}
if ($constraint instanceof Constraint) {
return self::generateSingleConstraintIntervals($constraint);
}
if (!$constraint instanceof MultiConstraint) {
throw new \UnexpectedValueException('The constraint passed in should be an MatchAllConstraint, Constraint or MultiConstraint instance, got '.\get_class($constraint).'.');
}
$constraints = $constraint->getConstraints();
$numericGroups = array();
$constraintBranches = array();
foreach ($constraints as $c) {
$res = self::get($c);
$numericGroups[] = $res['numeric'];
$constraintBranches[] = $res['branches'];
}
if ($constraint->isDisjunctive()) {
$branches = Interval::noDev();
foreach ($constraintBranches as $b) {
if ($b['exclude']) {
if ($branches['exclude']) {
$branches['names'] = array_intersect($branches['names'], $b['names']);
} else {
$branches['exclude'] = true;
$branches['names'] = array_diff($b['names'], $branches['names']);
}
} else {
if ($branches['exclude']) {
$branches['names'] = array_diff($branches['names'], $b['names']);
} else {
$branches['names'] = array_merge($branches['names'], $b['names']);
}
}
}
} else {
$branches = Interval::anyDev();
foreach ($constraintBranches as $b) {
if ($b['exclude']) {
if ($branches['exclude']) {
$branches['names'] = array_merge($branches['names'], $b['names']);
} else {
$branches['names'] = array_diff($branches['names'], $b['names']);
}
} else {
if ($branches['exclude']) {
$branches['names'] = array_diff($b['names'], $branches['names']);
$branches['exclude'] = false;
} else {
$branches['names'] = array_intersect($branches['names'], $b['names']);
}
}
}
}
$branches['names'] = array_unique($branches['names']);
if (\count($numericGroups) === 1) {
return array('numeric' => $numericGroups[0], 'branches' => $branches);
}
$borders = array();
foreach ($numericGroups as $group) {
foreach ($group as $interval) {
$borders[] = array('version' => $interval->getStart()->getVersion(), 'operator' => $interval->getStart()->getOperator(), 'side' => 'start');
$borders[] = array('version' => $interval->getEnd()->getVersion(), 'operator' => $interval->getEnd()->getOperator(), 'side' => 'end');
}
}
$opSortOrder = self::$opSortOrder;
usort($borders, function ($a, $b) use ($opSortOrder) {
$order = version_compare($a['version'], $b['version']);
if ($order === 0) {
return $opSortOrder[$a['operator']] - $opSortOrder[$b['operator']];
}
return $order;
});
$activeIntervals = 0;
$intervals = array();
$index = 0;
$activationThreshold = $constraint->isConjunctive() ? \count($numericGroups) : 1;
$start = null;
foreach ($borders as $border) {
if ($border['side'] === 'start') {
$activeIntervals++;
} else {
$activeIntervals--;
}
if (!$start && $activeIntervals >= $activationThreshold) {
$start = new Constraint($border['operator'], $border['version']);
} elseif ($start && $activeIntervals < $activationThreshold) {
if (
version_compare($start->getVersion(), $border['version'], '=')
&& (
($start->getOperator() === '>' && $border['operator'] === '<=')
|| ($start->getOperator() === '>=' && $border['operator'] === '<')
)
) {
unset($intervals[$index]);
} else {
$intervals[$index] = new Interval($start, new Constraint($border['operator'], $border['version']));
$index++;
if ($stopOnFirstValidInterval) {
break;
}
}
$start = null;
}
}
return array('numeric' => $intervals, 'branches' => $branches);
}
/**
@phpstan-return
*/
private static function generateSingleConstraintIntervals(Constraint $constraint)
{
$op = $constraint->getOperator();
if (strpos($constraint->getVersion(), 'dev-') === 0) {
$intervals = array();
$branches = array('names' => array(), 'exclude' => false);
if ($op === '!=') {
$intervals[] = new Interval(Interval::fromZero(), Interval::untilPositiveInfinity());
$branches = array('names' => array($constraint->getVersion()), 'exclude' => true);
} elseif ($op === '==') {
$branches['names'][] = $constraint->getVersion();
}
return array(
'numeric' => $intervals,
'branches' => $branches,
);
}
if ($op[0] === '>') {
return array('numeric' => array(new Interval($constraint, Interval::untilPositiveInfinity())), 'branches' => Interval::noDev());
}
if ($op[0] === '<') {
return array('numeric' => array(new Interval(Interval::fromZero(), $constraint)), 'branches' => Interval::noDev());
}
if ($op === '!=') {
return array('numeric' => array(
new Interval(Interval::fromZero(), new Constraint('<', $constraint->getVersion())),
new Interval(new Constraint('>', $constraint->getVersion()), Interval::untilPositiveInfinity()),
), 'branches' => Interval::anyDev());
}
return array('numeric' => array(
new Interval(new Constraint('>=', $constraint->getVersion()), new Constraint('<=', $constraint->getVersion())),
), 'branches' => Interval::noDev());
}
}
<?php
namespace Composer\Semver;
use Composer\Semver\Constraint\Constraint;
class Semver
{
const SORT_ASC = 1;
const SORT_DESC = -1;
private static $versionParser;
public static function satisfies($version, $constraints)
{
if (null === self::$versionParser) {
self::$versionParser = new VersionParser();
}
$versionParser = self::$versionParser;
$provider = new Constraint('==', $versionParser->normalize($version));
$parsedConstraints = $versionParser->parseConstraints($constraints);
return $parsedConstraints->matches($provider);
}
public static function satisfiedBy(array $versions, $constraints)
{
$versions = array_filter($versions, function ($version) use ($constraints) {
return Semver::satisfies($version, $constraints);
});
return array_values($versions);
}
public static function sort(array $versions)
{
return self::usort($versions, self::SORT_ASC);
}
public static function rsort(array $versions)
{
return self::usort($versions, self::SORT_DESC);
}
private static function usort(array $versions, $direction)
{
if (null === self::$versionParser) {
self::$versionParser = new VersionParser();
}
$versionParser = self::$versionParser;
$normalized = array();
foreach ($versions as $key => $version) {
$normalizedVersion = $versionParser->normalize($version);
$normalizedVersion = $versionParser->normalizeDefaultBranch($normalizedVersion);
$normalized[] = array($normalizedVersion, $key);
}
usort($normalized, function (array $left, array $right) use ($direction) {
if ($left[0] === $right[0]) {
return 0;
}
if (Comparator::lessThan($left[0], $right[0])) {
return -$direction;
}
return $direction;
});
$sorted = array();
foreach ($normalized as $item) {
$sorted[] = $versions[$item[1]];
}
return $sorted;
}
}
<?php
namespace Composer\Semver;
use Composer\Semver\Constraint\ConstraintInterface;
use Composer\Semver\Constraint\MatchAllConstraint;
use Composer\Semver\Constraint\MultiConstraint;
use Composer\Semver\Constraint\Constraint;
class VersionParser
{
private static $modifierRegex = '[._-]?(?:(stable|beta|b|RC|alpha|a|patch|pl|p)((?:[.-]?\d+)*+)?)?([.-]?dev)?';
private static $stabilitiesRegex = 'stable|RC|beta|alpha|dev';
/**
@phpstan-return
*/
public static function parseStability($version)
{
$version = (string) preg_replace('{#.+$}', '', (string) $version);
if (strpos($version, 'dev-') === 0 || '-dev' === substr($version, -4)) {
return 'dev';
}
preg_match('{' . self::$modifierRegex . '(?:\+.*)?$}i', strtolower($version), $match);
if (!empty($match[3])) {
return 'dev';
}
if (!empty($match[1])) {
if ('beta' === $match[1] || 'b' === $match[1]) {
return 'beta';
}
if ('alpha' === $match[1] || 'a' === $match[1]) {
return 'alpha';
}
if ('rc' === $match[1]) {
return 'RC';
}
}
return 'stable';
}
public static function normalizeStability($stability)
{
$stability = strtolower((string) $stability);
return $stability === 'rc' ? 'RC' : $stability;
}
public function normalize($version, $fullVersion = null)
{
$version = trim((string) $version);
$origVersion = $version;
if (null === $fullVersion) {
$fullVersion = $version;
}
if (preg_match('{^([^,\s]++) ++as ++([^,\s]++)$}', $version, $match)) {
$version = $match[1];
}
if (preg_match('{@(?:' . self::$stabilitiesRegex . ')$}i', $version, $match)) {
$version = substr($version, 0, strlen($version) - strlen($match[0]));
}
if (\in_array($version, array('master', 'trunk', 'default'), true)) {
$version = 'dev-' . $version;
}
if (stripos($version, 'dev-') === 0) {
return 'dev-' . substr($version, 4);
}
if (preg_match('{^([^,\s+]++)\+[^\s]++$}', $version, $match)) {
$version = $match[1];
}
if (preg_match('{^v?(\d{1,5})(\.\d++)?(\.\d++)?(\.\d++)?' . self::$modifierRegex . '$}i', $version, $matches)) {
$version = $matches[1]
. (!empty($matches[2]) ? $matches[2] : '.0')
. (!empty($matches[3]) ? $matches[3] : '.0')
. (!empty($matches[4]) ? $matches[4] : '.0');
$index = 5;
} elseif (preg_match('{^v?(\d{4}(?:[.:-]?\d{2}){1,6}(?:[.:-]?\d{1,3})?)' . self::$modifierRegex . '$}i', $version, $matches)) {
$version = preg_replace('{\D}', '.', $matches[1]);
$index = 2;
}
if (isset($index)) {
if (!empty($matches[$index])) {
if ('stable' === $matches[$index]) {
return $version;
}
$version .= '-' . $this->expandStability($matches[$index]) . (isset($matches[$index + 1]) && '' !== $matches[$index + 1] ? ltrim($matches[$index + 1], '.-') : '');
}
if (!empty($matches[$index + 2])) {
$version .= '-dev';
}
return $version;
}
if (preg_match('{(.*?)[.-]?dev$}i', $version, $match)) {
try {
$normalized = $this->normalizeBranch($match[1]);
if (strpos($normalized, 'dev-') === false) {
return $normalized;
}
} catch (\Exception $e) {
}
}
$extraMessage = '';
if (preg_match('{ +as +' . preg_quote($version) . '(?:@(?:'.self::$stabilitiesRegex.'))?$}', $fullVersion)) {
$extraMessage = ' in "' . $fullVersion . '", the alias must be an exact version';
} elseif (preg_match('{^' . preg_quote($version) . '(?:@(?:'.self::$stabilitiesRegex.'))? +as +}', $fullVersion)) {
$extraMessage = ' in "' . $fullVersion . '", the alias source must be an exact version, if it is a branch name you should prefix it with dev-';
}
throw new \UnexpectedValueException('Invalid version string "' . $origVersion . '"' . $extraMessage);
}
public function parseNumericAliasPrefix($branch)
{
if (preg_match('{^(?P<version>(\d++\\.)*\d++)(?:\.x)?-dev$}i', (string) $branch, $matches)) {
return $matches['version'] . '.';
}
return false;
}
public function normalizeBranch($name)
{
$name = trim((string) $name);
if (preg_match('{^v?(\d++)(\.(?:\d++|[xX*]))?(\.(?:\d++|[xX*]))?(\.(?:\d++|[xX*]))?$}i', $name, $matches)) {
$version = '';
for ($i = 1; $i < 5; ++$i) {
$version .= isset($matches[$i]) ? str_replace(array('*', 'X'), 'x', $matches[$i]) : '.x';
}
return str_replace('x', '9999999', $version) . '-dev';
}
return 'dev-' . $name;
}
public function normalizeDefaultBranch($name)
{
if ($name === 'dev-master' || $name === 'dev-default' || $name === 'dev-trunk') {
return '9999999-dev';
}
return (string) $name;
}
public function parseConstraints($constraints)
{
$prettyConstraint = (string) $constraints;
$orConstraints = preg_split('{\s*\|\|?\s*}', trim((string) $constraints));
if (false === $orConstraints) {
throw new \RuntimeException('Failed to preg_split string: '.$constraints);
}
$orGroups = array();
foreach ($orConstraints as $constraints) {
$andConstraints = preg_split('{(?<!^|as|[=>< ,]) *(?<!-)[, ](?!-) *(?!,|as|$)}', $constraints);
if (false === $andConstraints) {
throw new \RuntimeException('Failed to preg_split string: '.$constraints);
}
if (\count($andConstraints) > 1) {
$constraintObjects = array();
foreach ($andConstraints as $constraint) {
foreach ($this->parseConstraint($constraint) as $parsedConstraint) {
$constraintObjects[] = $parsedConstraint;
}
}
} else {
$constraintObjects = $this->parseConstraint($andConstraints[0]);
}
if (1 === \count($constraintObjects)) {
$constraint = $constraintObjects[0];
} else {
$constraint = new MultiConstraint($constraintObjects);
}
$orGroups[] = $constraint;
}
$constraint = MultiConstraint::create($orGroups, false);
$constraint->setPrettyString($prettyConstraint);
return $constraint;
}
/**
@phpstan-return
*/
private function parseConstraint($constraint)
{
if (preg_match('{^([^,\s]++) ++as ++([^,\s]++)$}', $constraint, $match)) {
$constraint = $match[1];
}
if (preg_match('{^([^,\s]*?)@(' . self::$stabilitiesRegex . ')$}i', $constraint, $match)) {
$constraint = '' !== $match[1] ? $match[1] : '*';
if ($match[2] !== 'stable') {
$stabilityModifier = $match[2];
}
}
if (preg_match('{^(dev-[^,\s@]+?|[^,\s@]+?\.x-dev)#.+$}i', $constraint, $match)) {
$constraint = $match[1];
}
if (preg_match('{^(v)?[xX*](\.[xX*])*$}i', $constraint, $match)) {
if (!empty($match[1]) || !empty($match[2])) {
return array(new Constraint('>=', '0.0.0.0-dev'));
}
return array(new MatchAllConstraint());
}
$versionRegex = 'v?(\d++)(?:\.(\d++))?(?:\.(\d++))?(?:\.(\d++))?(?:' . self::$modifierRegex . '|\.([xX*][.-]?dev))(?:\+[^\s]+)?';
if (preg_match('{^~>?' . $versionRegex . '$}i', $constraint, $matches)) {
if (strpos($constraint, '~>') === 0) {
throw new \UnexpectedValueException(
'Could not parse version constraint ' . $constraint . ': ' .
'Invalid operator "~>", you probably meant to use the "~" operator'
);
}
if (isset($matches[4]) && '' !== $matches[4] && null !== $matches[4]) {
$position = 4;
} elseif (isset($matches[3]) && '' !== $matches[3] && null !== $matches[3]) {
$position = 3;
} elseif (isset($matches[2]) && '' !== $matches[2] && null !== $matches[2]) {
$position = 2;
} else {
$position = 1;
}
if (!empty($matches[8])) {
$position++;
}
$stabilitySuffix = '';
if (empty($matches[5]) && empty($matches[7]) && empty($matches[8])) {
$stabilitySuffix .= '-dev';
}
$lowVersion = $this->normalize(substr($constraint . $stabilitySuffix, 1));
$lowerBound = new Constraint('>=', $lowVersion);
$highPosition = max(1, $position - 1);
$highVersion = $this->manipulateVersionString($matches, $highPosition, 1) . '-dev';
$upperBound = new Constraint('<', $highVersion);
return array(
$lowerBound,
$upperBound,
);
}
if (preg_match('{^\^' . $versionRegex . '($)}i', $constraint, $matches)) {
if ('0' !== $matches[1] || '' === $matches[2] || null === $matches[2]) {
$position = 1;
} elseif ('0' !== $matches[2] || '' === $matches[3] || null === $matches[3]) {
$position = 2;
} else {
$position = 3;
}
$stabilitySuffix = '';
if (empty($matches[5]) && empty($matches[7]) && empty($matches[8])) {
$stabilitySuffix .= '-dev';
}
$lowVersion = $this->normalize(substr($constraint . $stabilitySuffix, 1));
$lowerBound = new Constraint('>=', $lowVersion);
$highVersion = $this->manipulateVersionString($matches, $position, 1) . '-dev';
$upperBound = new Constraint('<', $highVersion);
return array(
$lowerBound,
$upperBound,
);
}
if (preg_match('{^v?(\d++)(?:\.(\d++))?(?:\.(\d++))?(?:\.[xX*])++$}', $constraint, $matches)) {
if (isset($matches[3]) && '' !== $matches[3] && null !== $matches[3]) {
$position = 3;
} elseif (isset($matches[2]) && '' !== $matches[2] && null !== $matches[2]) {
$position = 2;
} else {
$position = 1;
}
$lowVersion = $this->manipulateVersionString($matches, $position) . '-dev';
$highVersion = $this->manipulateVersionString($matches, $position, 1) . '-dev';
if ($lowVersion === '0.0.0.0-dev') {
return array(new Constraint('<', $highVersion));
}
return array(
new Constraint('>=', $lowVersion),
new Constraint('<', $highVersion),
);
}
if (preg_match('{^(?P<from>' . $versionRegex . ') +- +(?P<to>' . $versionRegex . ')($)}i', $constraint, $matches)) {
$lowStabilitySuffix = '';
if (empty($matches[6]) && empty($matches[8]) && empty($matches[9])) {
$lowStabilitySuffix = '-dev';
}
$lowVersion = $this->normalize($matches['from']);
$lowerBound = new Constraint('>=', $lowVersion . $lowStabilitySuffix);
$empty = function ($x) {
return ($x === 0 || $x === '0') ? false : empty($x);
};
if ((!$empty($matches[12]) && !$empty($matches[13])) || !empty($matches[15]) || !empty($matches[17]) || !empty($matches[18])) {
$highVersion = $this->normalize($matches['to']);
$upperBound = new Constraint('<=', $highVersion);
} else {
$highMatch = array('', $matches[11], $matches[12], $matches[13], $matches[14]);
$this->normalize($matches['to']);
$highVersion = $this->manipulateVersionString($highMatch, $empty($matches[12]) ? 1 : 2, 1) . '-dev';
$upperBound = new Constraint('<', $highVersion);
}
return array(
$lowerBound,
$upperBound,
);
}
if (preg_match('{^(<>|!=|>=?|<=?|==?)?\s*(.*)}', $constraint, $matches)) {
try {
try {
$version = $this->normalize($matches[2]);
} catch (\UnexpectedValueException $e) {
if (substr($matches[2], -4) === '-dev' && preg_match('{^[0-9a-zA-Z-./]+$}', $matches[2])) {
$version = $this->normalize('dev-'.substr($matches[2], 0, -4));
} else {
throw $e;
}
}
$op = $matches[1] ?: '=';
if ($op !== '==' && $op !== '=' && !empty($stabilityModifier) && self::parseStability($version) === 'stable') {
$version .= '-' . $stabilityModifier;
} elseif ('<' === $op || '>=' === $op) {
if (!preg_match('/-' . self::$modifierRegex . '$/', strtolower($matches[2]))) {
if (strpos($matches[2], 'dev-') !== 0) {
$version .= '-dev';
}
}
}
return array(new Constraint($matches[1] ?: '=', $version));
} catch (\Exception $e) {
}
}
$message = 'Could not parse version constraint ' . $constraint;
if (isset($e)) {
$message .= ': ' . $e->getMessage();
}
throw new \UnexpectedValueException($message);
}
/**
@phpstan-param
*/
private function manipulateVersionString(array $matches, $position, $increment = 0, $pad = '0')
{
for ($i = 4; $i > 0; --$i) {
if ($i > $position) {
$matches[$i] = $pad;
} elseif ($i === $position && $increment) {
$matches[$i] += $increment;
if ($matches[$i] < 0) {
$matches[$i] = $pad;
--$position;
if ($i === 1) {
return null;
}
}
}
}
return $matches[1] . '.' . $matches[2] . '.' . $matches[3] . '.' . $matches[4];
}
private function expandStability($stability)
{
$stability = strtolower($stability);
switch ($stability) {
case 'a':
return 'alpha';
case 'b':
return 'beta';
case 'p':
case 'pl':
return 'patch';
case 'rc':
return 'RC';
default:
return $stability;
}
}
}
<?php
namespace Composer\Semver\Constraint;
interface ConstraintInterface
{
public function matches(ConstraintInterface $provider);
/**
@phpstan-param
*/
public function compile($otherOperator);
public function getUpperBound();
public function getLowerBound();
public function getPrettyString();
public function setPrettyString($prettyString);
public function __toString();
}
<?php
namespace Composer\Semver\Constraint;
class MultiConstraint implements ConstraintInterface
{
/**
@phpstan-var
*/
protected $constraints;
protected $prettyString;
protected $string;
protected $conjunctive;
protected $lowerBound;
protected $upperBound;
public function __construct(array $constraints, $conjunctive = true)
{
if (\count($constraints) < 2) {
throw new \InvalidArgumentException(
'Must provide at least two constraints for a MultiConstraint. Use '.
'the regular Constraint class for one constraint only or MatchAllConstraint for none. You may use '.
'MultiConstraint::create() which optimizes and handles those cases automatically.'
);
}
$this->constraints = $constraints;
$this->conjunctive = $conjunctive;
}
public function getConstraints()
{
return $this->constraints;
}
public function isConjunctive()
{
return $this->conjunctive;
}
public function isDisjunctive()
{
return !$this->conjunctive;
}
public function compile($otherOperator)
{
$parts = array();
foreach ($this->constraints as $constraint) {
$code = $constraint->compile($otherOperator);
if ($code === 'true') {
if (!$this->conjunctive) {
return 'true';
}
} elseif ($code === 'false') {
if ($this->conjunctive) {
return 'false';
}
} else {
$parts[] = '('.$code.')';
}
}
if (!$parts) {
return $this->conjunctive ? 'true' : 'false';
}
return $this->conjunctive ? implode('&&', $parts) : implode('||', $parts);
}
public function matches(ConstraintInterface $provider)
{
if (false === $this->conjunctive) {
foreach ($this->constraints as $constraint) {
if ($provider->matches($constraint)) {
return true;
}
}
return false;
}
if ($provider instanceof MultiConstraint && $provider->isDisjunctive()) {
return $provider->matches($this);
}
foreach ($this->constraints as $constraint) {
if (!$provider->matches($constraint)) {
return false;
}
}
return true;
}
public function setPrettyString($prettyString)
{
$this->prettyString = $prettyString;
}
public function getPrettyString()
{
if ($this->prettyString) {
return $this->prettyString;
}
return (string) $this;
}
public function __toString()
{
if ($this->string !== null) {
return $this->string;
}
$constraints = array();
foreach ($this->constraints as $constraint) {
$constraints[] = (string) $constraint;
}
return $this->string = '[' . implode($this->conjunctive ? ' ' : ' || ', $constraints) . ']';
}
public function getLowerBound()
{
$this->extractBounds();
if (null === $this->lowerBound) {
throw new \LogicException('extractBounds should have populated the lowerBound property');
}
return $this->lowerBound;
}
public function getUpperBound()
{
$this->extractBounds();
if (null === $this->upperBound) {
throw new \LogicException('extractBounds should have populated the upperBound property');
}
return $this->upperBound;
}
public static function create(array $constraints, $conjunctive = true)
{
if (0 === \count($constraints)) {
return new MatchAllConstraint();
}
if (1 === \count($constraints)) {
return $constraints[0];
}
$optimized = self::optimizeConstraints($constraints, $conjunctive);
if ($optimized !== null) {
list($constraints, $conjunctive) = $optimized;
if (\count($constraints) === 1) {
return $constraints[0];
}
}
return new self($constraints, $conjunctive);
}
/**
@phpstan-return
*/
private static function optimizeConstraints(array $constraints, $conjunctive)
{
if (!$conjunctive) {
$left = $constraints[0];
$mergedConstraints = array();
$optimized = false;
for ($i = 1, $l = \count($constraints); $i < $l; $i++) {
$right = $constraints[$i];
if (
$left instanceof self
&& $left->conjunctive
&& $right instanceof self
&& $right->conjunctive
&& \count($left->constraints) === 2
&& \count($right->constraints) === 2
&& ($left0 = (string) $left->constraints[0])
&& $left0[0] === '>' && $left0[1] === '='
&& ($left1 = (string) $left->constraints[1])
&& $left1[0] === '<'
&& ($right0 = (string) $right->constraints[0])
&& $right0[0] === '>' && $right0[1] === '='
&& ($right1 = (string) $right->constraints[1])
&& $right1[0] === '<'
&& substr($left1, 2) === substr($right0, 3)
) {
$optimized = true;
$left = new MultiConstraint(
array(
$left->constraints[0],
$right->constraints[1],
),
true);
} else {
$mergedConstraints[] = $left;
$left = $right;
}
}
if ($optimized) {
$mergedConstraints[] = $left;
return array($mergedConstraints, false);
}
}
return null;
}
private function extractBounds()
{
if (null !== $this->lowerBound) {
return;
}
foreach ($this->constraints as $constraint) {
if (null === $this->lowerBound || null === $this->upperBound) {
$this->lowerBound = $constraint->getLowerBound();
$this->upperBound = $constraint->getUpperBound();
continue;
}
if ($constraint->getLowerBound()->compareTo($this->lowerBound, $this->isConjunctive() ? '>' : '<')) {
$this->lowerBound = $constraint->getLowerBound();
}
if ($constraint->getUpperBound()->compareTo($this->upperBound, $this->isConjunctive() ? '<' : '>')) {
$this->upperBound = $constraint->getUpperBound();
}
}
}
}
<?php
namespace Composer\Semver\Constraint;
class Constraint implements ConstraintInterface
{
const OP_EQ = 0;
const OP_LT = 1;
const OP_LE = 2;
const OP_GT = 3;
const OP_GE = 4;
const OP_NE = 5;
const STR_OP_EQ = '==';
const STR_OP_EQ_ALT = '=';
const STR_OP_LT = '<';
const STR_OP_LE = '<=';
const STR_OP_GT = '>';
const STR_OP_GE = '>=';
const STR_OP_NE = '!=';
const STR_OP_NE_ALT = '<>';
/**
@phpstan-var
*/
private static $transOpStr = array(
'=' => self::OP_EQ,
'==' => self::OP_EQ,
'<' => self::OP_LT,
'<=' => self::OP_LE,
'>' => self::OP_GT,
'>=' => self::OP_GE,
'<>' => self::OP_NE,
'!=' => self::OP_NE,
);
/**
@phpstan-var
*/
private static $transOpInt = array(
self::OP_EQ => '==',
self::OP_LT => '<',
self::OP_LE => '<=',
self::OP_GT => '>',
self::OP_GE => '>=',
self::OP_NE => '!=',
);
/**
@phpstan-var
*/
protected $operator;
protected $version;
protected $prettyString;
protected $lowerBound;
protected $upperBound;
/**
@phpstan-param
*/
public function __construct($operator, $version)
{
if (!isset(self::$transOpStr[$operator])) {
throw new \InvalidArgumentException(sprintf(
'Invalid operator "%s" given, expected one of: %s',
$operator,
implode(', ', self::getSupportedOperators())
));
}
$this->operator = self::$transOpStr[$operator];
$this->version = $version;
}
public function getVersion()
{
return $this->version;
}
/**
@phpstan-return
*/
public function getOperator()
{
return self::$transOpInt[$this->operator];
}
public function matches(ConstraintInterface $provider)
{
if ($provider instanceof self) {
return $this->matchSpecific($provider);
}
return $provider->matches($this);
}
public function setPrettyString($prettyString)
{
$this->prettyString = $prettyString;
}
public function getPrettyString()
{
if ($this->prettyString) {
return $this->prettyString;
}
return $this->__toString();
}
/**
@phpstan-return
*/
public static function getSupportedOperators()
{
return array_keys(self::$transOpStr);
}
/**
@phpstan-param
@phpstan-return
*/
public static function getOperatorConstant($operator)
{
return self::$transOpStr[$operator];
}
/**
@phpstan-param
*/
public function versionCompare($a, $b, $operator, $compareBranches = false)
{
if (!isset(self::$transOpStr[$operator])) {
throw new \InvalidArgumentException(sprintf(
'Invalid operator "%s" given, expected one of: %s',
$operator,
implode(', ', self::getSupportedOperators())
));
}
$aIsBranch = strpos($a, 'dev-') === 0;
$bIsBranch = strpos($b, 'dev-') === 0;
if ($operator === '!=' && ($aIsBranch || $bIsBranch)) {
return $a !== $b;
}
if ($aIsBranch && $bIsBranch) {
return $operator === '==' && $a === $b;
}
if (!$compareBranches && ($aIsBranch || $bIsBranch)) {
return false;
}
return \version_compare($a, $b, $operator);
}
public function compile($otherOperator)
{
if (strpos($this->version, 'dev-') === 0) {
if (self::OP_EQ === $this->operator) {
if (self::OP_EQ === $otherOperator) {
return sprintf('$b && $v === %s', \var_export($this->version, true));
}
if (self::OP_NE === $otherOperator) {
return sprintf('!$b || $v !== %s', \var_export($this->version, true));
}
return 'false';
}
if (self::OP_NE === $this->operator) {
if (self::OP_EQ === $otherOperator) {
return sprintf('!$b || $v !== %s', \var_export($this->version, true));
}
if (self::OP_NE === $otherOperator) {
return 'true';
}
return '!$b';
}
return 'false';
}
if (self::OP_EQ === $this->operator) {
if (self::OP_EQ === $otherOperator) {
return sprintf('\version_compare($v, %s, \'==\')', \var_export($this->version, true));
}
if (self::OP_NE === $otherOperator) {
return sprintf('$b || \version_compare($v, %s, \'!=\')', \var_export($this->version, true));
}
return sprintf('!$b && \version_compare(%s, $v, \'%s\')', \var_export($this->version, true), self::$transOpInt[$otherOperator]);
}
if (self::OP_NE === $this->operator) {
if (self::OP_EQ === $otherOperator) {
return sprintf('$b || (!$b && \version_compare($v, %s, \'!=\'))', \var_export($this->version, true));
}
if (self::OP_NE === $otherOperator) {
return 'true';
}
return '!$b';
}
if (self::OP_LT === $this->operator || self::OP_LE === $this->operator) {
if (self::OP_LT === $otherOperator || self::OP_LE === $otherOperator) {
return '!$b';
}
} else {
if (self::OP_GT === $otherOperator || self::OP_GE === $otherOperator) {
return '!$b';
}
}
if (self::OP_NE === $otherOperator) {
return 'true';
}
$codeComparison = sprintf('\version_compare($v, %s, \'%s\')', \var_export($this->version, true), self::$transOpInt[$this->operator]);
if ($this->operator === self::OP_LE) {
if ($otherOperator === self::OP_GT) {
return sprintf('!$b && \version_compare($v, %s, \'!=\') && ', \var_export($this->version, true)) . $codeComparison;
}
} elseif ($this->operator === self::OP_GE) {
if ($otherOperator === self::OP_LT) {
return sprintf('!$b && \version_compare($v, %s, \'!=\') && ', \var_export($this->version, true)) . $codeComparison;
}
}
return sprintf('!$b && %s', $codeComparison);
}
public function matchSpecific(Constraint $provider, $compareBranches = false)
{
$noEqualOp = str_replace('=', '', self::$transOpInt[$this->operator]);
$providerNoEqualOp = str_replace('=', '', self::$transOpInt[$provider->operator]);
$isEqualOp = self::OP_EQ === $this->operator;
$isNonEqualOp = self::OP_NE === $this->operator;
$isProviderEqualOp = self::OP_EQ === $provider->operator;
$isProviderNonEqualOp = self::OP_NE === $provider->operator;
if ($isNonEqualOp || $isProviderNonEqualOp) {
if ($isNonEqualOp && !$isProviderNonEqualOp && !$isProviderEqualOp && strpos($provider->version, 'dev-') === 0) {
return false;
}
if ($isProviderNonEqualOp && !$isNonEqualOp && !$isEqualOp && strpos($this->version, 'dev-') === 0) {
return false;
}
if (!$isEqualOp && !$isProviderEqualOp) {
return true;
}
return $this->versionCompare($provider->version, $this->version, '!=', $compareBranches);
}
if ($this->operator !== self::OP_EQ && $noEqualOp === $providerNoEqualOp) {
return !(strpos($this->version, 'dev-') === 0 || strpos($provider->version, 'dev-') === 0);
}
$version1 = $isEqualOp ? $this->version : $provider->version;
$version2 = $isEqualOp ? $provider->version : $this->version;
$operator = $isEqualOp ? $provider->operator : $this->operator;
if ($this->versionCompare($version1, $version2, self::$transOpInt[$operator], $compareBranches)) {
return !(self::$transOpInt[$provider->operator] === $providerNoEqualOp
&& self::$transOpInt[$this->operator] !== $noEqualOp
&& \version_compare($provider->version, $this->version, '=='));
}
return false;
}
public function __toString()
{
return self::$transOpInt[$this->operator] . ' ' . $this->version;
}
public function getLowerBound()
{
$this->extractBounds();
return $this->lowerBound;
}
public function getUpperBound()
{
$this->extractBounds();
return $this->upperBound;
}
private function extractBounds()
{
if (null !== $this->lowerBound) {
return;
}
if (strpos($this->version, 'dev-') === 0) {
$this->lowerBound = Bound::zero();
$this->upperBound = Bound::positiveInfinity();
return;
}
switch ($this->operator) {
case self::OP_EQ:
$this->lowerBound = new Bound($this->version, true);
$this->upperBound = new Bound($this->version, true);
break;
case self::OP_LT:
$this->lowerBound = Bound::zero();
$this->upperBound = new Bound($this->version, false);
break;
case self::OP_LE:
$this->lowerBound = Bound::zero();
$this->upperBound = new Bound($this->version, true);
break;
case self::OP_GT:
$this->lowerBound = new Bound($this->version, false);
$this->upperBound = Bound::positiveInfinity();
break;
case self::OP_GE:
$this->lowerBound = new Bound($this->version, true);
$this->upperBound = Bound::positiveInfinity();
break;
case self::OP_NE:
$this->lowerBound = Bound::zero();
$this->upperBound = Bound::positiveInfinity();
break;
}
}
}
<?php
namespace Composer\Semver\Constraint;
class MatchAllConstraint implements ConstraintInterface
{
protected $prettyString;
public function matches(ConstraintInterface $provider)
{
return true;
}
public function compile($otherOperator)
{
return 'true';
}
public function setPrettyString($prettyString)
{
$this->prettyString = $prettyString;
}
public function getPrettyString()
{
if ($this->prettyString) {
return $this->prettyString;
}
return (string) $this;
}
public function __toString()
{
return '*';
}
public function getUpperBound()
{
return Bound::positiveInfinity();
}
public function getLowerBound()
{
return Bound::zero();
}
}
<?php
namespace Composer\Semver\Constraint;
class MatchNoneConstraint implements ConstraintInterface
{
protected $prettyString;
public function matches(ConstraintInterface $provider)
{
return false;
}
public function compile($otherOperator)
{
return 'false';
}
public function setPrettyString($prettyString)
{
$this->prettyString = $prettyString;
}
public function getPrettyString()
{
if ($this->prettyString) {
return $this->prettyString;
}
return (string) $this;
}
public function __toString()
{
return '[]';
}
public function getUpperBound()
{
return new Bound('0.0.0.0-dev', false);
}
public function getLowerBound()
{
return new Bound('0.0.0.0-dev', false);
}
}
<?php
namespace Composer\Semver\Constraint;
class Bound
{
private $version;
private $isInclusive;
public function __construct($version, $isInclusive)
{
$this->version = $version;
$this->isInclusive = $isInclusive;
}
public function getVersion()
{
return $this->version;
}
public function isInclusive()
{
return $this->isInclusive;
}
public function isZero()
{
return $this->getVersion() === '0.0.0.0-dev' && $this->isInclusive();
}
public function isPositiveInfinity()
{
return $this->getVersion() === PHP_INT_MAX.'.0.0.0' && !$this->isInclusive();
}
public function compareTo(Bound $other, $operator)
{
if (!\in_array($operator, array('<', '>'), true)) {
throw new \InvalidArgumentException('Does not support any other operator other than > or <.');
}
if ($this == $other) {
return false;
}
$compareResult = version_compare($this->getVersion(), $other->getVersion());
if (0 !== $compareResult) {
return (('>' === $operator) ? 1 : -1) === $compareResult;
}
return '>' === $operator ? $other->isInclusive() : !$other->isInclusive();
}
public function __toString()
{
return sprintf(
'%s [%s]',
$this->getVersion(),
$this->isInclusive() ? 'inclusive' : 'exclusive'
);
}
public static function zero()
{
return new Bound('0.0.0.0-dev', true);
}
public static function positiveInfinity()
{
return new Bound(PHP_INT_MAX.'.0.0.0', false);
}
}
<?php
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
'8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php',
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'0d59ee240a4cd96ddbb4ff164fccea4d' => $vendorDir . '/symfony/polyfill-php73/bootstrap.php',
'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php',
'23c18046f52bef3eea034657bafda50f' => $vendorDir . '/symfony/polyfill-php81/bootstrap.php',
);
<?php
namespace Composer;
use Composer\Autoload\ClassLoader;
use Composer\Semver\VersionParser;
class InstalledVersions
{
/**
@psalm-var
*/
private static $installed;
private static $canGetVendors;
/**
@psalm-var
*/
private static $installedByVendor = array();
/**
@psalm-return
*/
public static function getInstalledPackages()
{
$packages = array();
foreach (self::getInstalled() as $installed) {
$packages[] = array_keys($installed['versions']);
}
if (1 === \count($packages)) {
return $packages[0];
}
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
}
/**
@psalm-return
*/
public static function getInstalledPackagesByType($type)
{
$packagesByType = array();
foreach (self::getInstalled() as $installed) {
foreach ($installed['versions'] as $name => $package) {
if (isset($package['type']) && $package['type'] === $type) {
$packagesByType[] = $name;
}
}
}
return $packagesByType;
}
public static function isInstalled($packageName, $includeDevRequirements = true)
{
foreach (self::getInstalled() as $installed) {
if (isset($installed['versions'][$packageName])) {
return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
}
}
return false;
}
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints($constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
}
public static function getVersionRanges($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
$ranges = array();
if (isset($installed['versions'][$packageName]['pretty_version'])) {
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
}
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
}
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
}
if (array_key_exists('provided', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
}
return implode(' || ', $ranges);
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
public static function getVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['version'])) {
return null;
}
return $installed['versions'][$packageName]['version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
public static function getPrettyVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
return null;
}
return $installed['versions'][$packageName]['pretty_version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
public static function getReference($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['reference'])) {
return null;
}
return $installed['versions'][$packageName]['reference'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
public static function getInstallPath($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
@psalm-return
*/
public static function getRootPackage()
{
$installed = self::getInstalled();
return $installed[0]['root'];
}
/**
@psalm-return
*/
public static function getRawData()
{
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
if (null === self::$installed) {
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = include __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
return self::$installed;
}
/**
@psalm-return
*/
public static function getAllRawData()
{
return self::getInstalled();
}
/**
@psalm-param
*/
public static function reload($data)
{
self::$installed = $data;
self::$installedByVendor = array();
}
/**
@psalm-return
*/
private static function getInstalled()
{
if (null === self::$canGetVendors) {
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
}
$installed = array();
if (self::$canGetVendors) {
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($vendorDir.'/composer/installed.php')) {
$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
self::$installed = $installed[count($installed) - 1];
}
}
}
}
if (null === self::$installed) {
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = require __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
$installed[] = self::$installed;
return $installed;
}
}
<?php return array(
'root' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'type' => 'application',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => 'a6232229a8309e8811dc751c28b91cb34b2943e1',
'name' => 'friendsofphp/php-cs-fixer',
'dev' => false,
),
'versions' => array(
'composer/pcre' => array(
'pretty_version' => '3.0.0',
'version' => '3.0.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/./pcre',
'aliases' => array(),
'reference' => 'e300eb6c535192decd27a85bc72a9290f0d6b3bd',
'dev_requirement' => false,
),
'composer/semver' => array(
'pretty_version' => '3.3.2',
'version' => '3.3.2.0',
'type' => 'library',
'install_path' => __DIR__ . '/./semver',
'aliases' => array(),
'reference' => '3953f23262f2bff1919fc82183ad9acb13ff62c9',
'dev_requirement' => false,
),
'composer/xdebug-handler' => array(
'pretty_version' => '3.0.3',
'version' => '3.0.3.0',
'type' => 'library',
'install_path' => __DIR__ . '/./xdebug-handler',
'aliases' => array(),
'reference' => 'ced299686f41dce890debac69273b47ffe98a40c',
'dev_requirement' => false,
),
'doctrine/annotations' => array(
'pretty_version' => '1.13.3',
'version' => '1.13.3.0',
'type' => 'library',
'install_path' => __DIR__ . '/../doctrine/annotations',
'aliases' => array(),
'reference' => '648b0343343565c4a056bfc8392201385e8d89f0',
'dev_requirement' => false,
),
'doctrine/lexer' => array(
'pretty_version' => '1.2.3',
'version' => '1.2.3.0',
'type' => 'library',
'install_path' => __DIR__ . '/../doctrine/lexer',
'aliases' => array(),
'reference' => 'c268e882d4dbdd85e36e4ad69e02dc284f89d229',
'dev_requirement' => false,
),
'friendsofphp/php-cs-fixer' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'type' => 'application',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => 'a6232229a8309e8811dc751c28b91cb34b2943e1',
'dev_requirement' => false,
),
'psr/cache' => array(
'pretty_version' => '1.0.1',
'version' => '1.0.1.0',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/cache',
'aliases' => array(),
'reference' => 'd11b50ad223250cf17b86e38383413f5a6764bf8',
'dev_requirement' => false,
),
'psr/container' => array(
'pretty_version' => '1.1.2',
'version' => '1.1.2.0',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/container',
'aliases' => array(),
'reference' => '513e0666f7216c7459170d56df27dfcefe1689ea',
'dev_requirement' => false,
),
'psr/event-dispatcher' => array(
'pretty_version' => '1.0.0',
'version' => '1.0.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/event-dispatcher',
'aliases' => array(),
'reference' => 'dbefd12671e8a14ec7f180cab83036ed26714bb0',
'dev_requirement' => false,
),
'psr/event-dispatcher-implementation' => array(
'dev_requirement' => false,
'provided' => array(
0 => '1.0',
),
),
'psr/log' => array(
'pretty_version' => '1.1.4',
'version' => '1.1.4.0',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/log',
'aliases' => array(),
'reference' => 'd49695b909c3b7628b6289db5479a1c204601f11',
'dev_requirement' => false,
),
'psr/log-implementation' => array(
'dev_requirement' => false,
'provided' => array(
0 => '1.0|2.0',
),
),
'sebastian/diff' => array(
'pretty_version' => '4.0.4',
'version' => '4.0.4.0',
'type' => 'library',
'install_path' => __DIR__ . '/../sebastian/diff',
'aliases' => array(),
'reference' => '3461e3fccc7cfdfc2720be910d3bd73c69be590d',
'dev_requirement' => false,
),
'symfony/console' => array(
'pretty_version' => 'v5.4.15',
'version' => '5.4.15.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/console',
'aliases' => array(),
'reference' => 'ea59bb0edfaf9f28d18d8791410ee0355f317669',
'dev_requirement' => false,
),
'symfony/deprecation-contracts' => array(
'pretty_version' => 'v2.5.2',
'version' => '2.5.2.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
'aliases' => array(),
'reference' => 'e8b495ea28c1d97b5e0c121748d6f9b53d075c66',
'dev_requirement' => false,
),
'symfony/event-dispatcher' => array(
'pretty_version' => 'v5.4.9',
'version' => '5.4.9.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/event-dispatcher',
'aliases' => array(),
'reference' => '8e6ce1cc0279e3ff3c8ff0f43813bc88d21ca1bc',
'dev_requirement' => false,
),
'symfony/event-dispatcher-contracts' => array(
'pretty_version' => 'v2.5.2',
'version' => '2.5.2.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/event-dispatcher-contracts',
'aliases' => array(),
'reference' => 'f98b54df6ad059855739db6fcbc2d36995283fe1',
'dev_requirement' => false,
),
'symfony/event-dispatcher-implementation' => array(
'dev_requirement' => false,
'provided' => array(
0 => '2.0',
),
),
'symfony/filesystem' => array(
'pretty_version' => 'v5.4.13',
'version' => '5.4.13.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/filesystem',
'aliases' => array(),
'reference' => 'ac09569844a9109a5966b9438fc29113ce77cf51',
'dev_requirement' => false,
),
'symfony/finder' => array(
'pretty_version' => 'v5.4.11',
'version' => '5.4.11.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/finder',
'aliases' => array(),
'reference' => '7872a66f57caffa2916a584db1aa7f12adc76f8c',
'dev_requirement' => false,
),
'symfony/options-resolver' => array(
'pretty_version' => 'v5.4.11',
'version' => '5.4.11.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/options-resolver',
'aliases' => array(),
'reference' => '54f14e36aa73cb8f7261d7686691fd4d75ea2690',
'dev_requirement' => false,
),
'symfony/polyfill-ctype' => array(
'pretty_version' => 'v1.26.0',
'version' => '1.26.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-ctype',
'aliases' => array(),
'reference' => '6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4',
'dev_requirement' => false,
),
'symfony/polyfill-intl-grapheme' => array(
'pretty_version' => 'v1.26.0',
'version' => '1.26.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-intl-grapheme',
'aliases' => array(),
'reference' => '433d05519ce6990bf3530fba6957499d327395c2',
'dev_requirement' => false,
),
'symfony/polyfill-intl-normalizer' => array(
'pretty_version' => 'v1.26.0',
'version' => '1.26.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer',
'aliases' => array(),
'reference' => '219aa369ceff116e673852dce47c3a41794c14bd',
'dev_requirement' => false,
),
'symfony/polyfill-mbstring' => array(
'pretty_version' => 'v1.26.0',
'version' => '1.26.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
'aliases' => array(),
'reference' => '9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e',
'dev_requirement' => false,
),
'symfony/polyfill-php73' => array(
'pretty_version' => 'v1.26.0',
'version' => '1.26.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php73',
'aliases' => array(),
'reference' => 'e440d35fa0286f77fb45b79a03fedbeda9307e85',
'dev_requirement' => false,
),
'symfony/polyfill-php80' => array(
'pretty_version' => 'v1.26.0',
'version' => '1.26.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
'aliases' => array(),
'reference' => 'cfa0ae98841b9e461207c13ab093d76b0fa7bace',
'dev_requirement' => false,
),
'symfony/polyfill-php81' => array(
'pretty_version' => 'v1.26.0',
'version' => '1.26.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php81',
'aliases' => array(),
'reference' => '13f6d1271c663dc5ae9fb843a8f16521db7687a1',
'dev_requirement' => false,
),
'symfony/process' => array(
'pretty_version' => 'v5.4.11',
'version' => '5.4.11.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/process',
'aliases' => array(),
'reference' => '6e75fe6874cbc7e4773d049616ab450eff537bf1',
'dev_requirement' => false,
),
'symfony/service-contracts' => array(
'pretty_version' => 'v2.5.2',
'version' => '2.5.2.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/service-contracts',
'aliases' => array(),
'reference' => '4b426aac47d6427cc1a1d0f7e2ac724627f5966c',
'dev_requirement' => false,
),
'symfony/stopwatch' => array(
'pretty_version' => 'v5.4.13',
'version' => '5.4.13.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/stopwatch',
'aliases' => array(),
'reference' => '6df7a3effde34d81717bbef4591e5ffe32226d69',
'dev_requirement' => false,
),
'symfony/string' => array(
'pretty_version' => 'v5.4.15',
'version' => '5.4.15.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/string',
'aliases' => array(),
'reference' => '571334ce9f687e3e6af72db4d3b2a9431e4fd9ed',
'dev_requirement' => false,
),
),
);
<?php
namespace Composer\Pcre;
final class MatchResult
{
/**
@readonly
*/
public $matches;
/**
@readonly
*/
public $matched;
public function __construct($count, array $matches)
{
$this->matches = $matches;
$this->matched = (bool) $count;
}
}
<?php
namespace Composer\Pcre;
class Regex
{
public static function isMatch(string $pattern, string $subject, int $offset = 0): bool
{
return (bool) Preg::match($pattern, $subject, $matches, 0, $offset);
}
public static function match(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchResult
{
if (($flags & PREG_OFFSET_CAPTURE) !== 0) {
throw new \InvalidArgumentException('PREG_OFFSET_CAPTURE is not supported as it changes the return type, use matchWithOffsets() instead');
}
$count = Preg::match($pattern, $subject, $matches, $flags, $offset);
return new MatchResult($count, $matches);
}
public static function matchWithOffsets(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchWithOffsetsResult
{
$count = Preg::matchWithOffsets($pattern, $subject, $matches, $flags, $offset);
return new MatchWithOffsetsResult($count, $matches);
}
public static function matchAll(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchAllResult
{
if (($flags & PREG_OFFSET_CAPTURE) !== 0) {
throw new \InvalidArgumentException('PREG_OFFSET_CAPTURE is not supported as it changes the return type, use matchAllWithOffsets() instead');
}
if (($flags & PREG_SET_ORDER) !== 0) {
throw new \InvalidArgumentException('PREG_SET_ORDER is not supported as it changes the return type');
}
$count = Preg::matchAll($pattern, $subject, $matches, $flags, $offset);
return new MatchAllResult($count, $matches);
}
public static function matchAllWithOffsets(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchAllWithOffsetsResult
{
$count = Preg::matchAllWithOffsets($pattern, $subject, $matches, $flags, $offset);
return new MatchAllWithOffsetsResult($count, $matches);
}
public static function replace($pattern, $replacement, $subject, int $limit = -1): ReplaceResult
{
$result = Preg::replace($pattern, $replacement, $subject, $limit, $count);
return new ReplaceResult($count, $result);
}
public static function replaceCallback($pattern, callable $replacement, $subject, int $limit = -1, int $flags = 0): ReplaceResult
{
$result = Preg::replaceCallback($pattern, $replacement, $subject, $limit, $count, $flags);
return new ReplaceResult($count, $result);
}
public static function replaceCallbackArray(array $pattern, $subject, int $limit = -1, int $flags = 0): ReplaceResult
{
$result = Preg::replaceCallbackArray($pattern, $subject, $limit, $count, $flags);
return new ReplaceResult($count, $result);
}
}
<?php
namespace Composer\Pcre;
final class MatchAllWithOffsetsResult
{
/**
@readonly
@phpstan-var
*/
public $matches;
/**
@readonly
*/
public $count;
/**
@readonly
*/
public $matched;
/**
@phpstan-param
*/
public function __construct($count, array $matches)
{
$this->matches = $matches;
$this->matched = (bool) $count;
$this->count = $count;
}
}
<?php
namespace Composer\Pcre;
final class MatchWithOffsetsResult
{
/**
@readonly
@phpstan-var
*/
public $matches;
/**
@readonly
*/
public $matched;
/**
@phpstan-param
*/
public function __construct($count, array $matches)
{
$this->matches = $matches;
$this->matched = (bool) $count;
}
}
<?php
namespace Composer\Pcre;
final class MatchAllResult
{
/**
@readonly
*/
public $matches;
/**
@readonly
*/
public $count;
/**
@readonly
*/
public $matched;
public function __construct($count, array $matches)
{
$this->matches = $matches;
$this->matched = (bool) $count;
$this->count = $count;
}
}
<?php
namespace Composer\Pcre;
final class ReplaceResult
{
/**
@readonly
*/
public $result;
/**
@readonly
*/
public $count;
/**
@readonly
*/
public $matched;
public function __construct($count, $result)
{
$this->count = $count;
$this->matched = (bool) $count;
$this->result = $result;
}
}
<?php
namespace Composer\Pcre;
class Preg
{
public const ARRAY_MSG = '$subject as an array is not supported. You can use \'foreach\' instead.';
public const INVALID_TYPE_MSG = '$subject must be a string, %s given.';
public static function match(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): int
{
if (($flags & PREG_OFFSET_CAPTURE) !== 0) {
throw new \InvalidArgumentException('PREG_OFFSET_CAPTURE is not supported as it changes the type of $matches, use matchWithOffsets() instead');
}
$result = preg_match($pattern, $subject, $matches, $flags | PREG_UNMATCHED_AS_NULL, $offset);
if ($result === false) {
throw PcreException::fromFunction('preg_match', $pattern);
}
return $result;
}
/**
@phpstan-param
*/
public static function matchWithOffsets(string $pattern, string $subject, ?array &$matches, int $flags = 0, int $offset = 0): int
{
$result = preg_match($pattern, $subject, $matches, $flags | PREG_UNMATCHED_AS_NULL | PREG_OFFSET_CAPTURE, $offset);
if ($result === false) {
throw PcreException::fromFunction('preg_match', $pattern);
}
return $result;
}
public static function matchAll(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): int
{
if (($flags & PREG_OFFSET_CAPTURE) !== 0) {
throw new \InvalidArgumentException('PREG_OFFSET_CAPTURE is not supported as it changes the type of $matches, use matchAllWithOffsets() instead');
}
if (($flags & PREG_SET_ORDER) !== 0) {
throw new \InvalidArgumentException('PREG_SET_ORDER is not supported as it changes the type of $matches');
}
$result = preg_match_all($pattern, $subject, $matches, $flags | PREG_UNMATCHED_AS_NULL, $offset);
if ($result === false || $result === null) {
throw PcreException::fromFunction('preg_match_all', $pattern);
}
return $result;
}
/**
@phpstan-param
*/
public static function matchAllWithOffsets(string $pattern, string $subject, ?array &$matches, int $flags = 0, int $offset = 0): int
{
$result = preg_match_all($pattern, $subject, $matches, $flags | PREG_UNMATCHED_AS_NULL | PREG_OFFSET_CAPTURE, $offset);
if ($result === false || $result === null) {
throw PcreException::fromFunction('preg_match_all', $pattern);
}
return $result;
}
public static function replace($pattern, $replacement, $subject, int $limit = -1, int &$count = null): string
{
if (!is_scalar($subject)) {
if (is_array($subject)) {
throw new \InvalidArgumentException(static::ARRAY_MSG);
}
throw new \TypeError(sprintf(static::INVALID_TYPE_MSG, gettype($subject)));
}
$result = preg_replace($pattern, $replacement, $subject, $limit, $count);
if ($result === null) {
throw PcreException::fromFunction('preg_replace', $pattern);
}
return $result;
}
public static function replaceCallback($pattern, callable $replacement, $subject, int $limit = -1, int &$count = null, int $flags = 0): string
{
if (!is_scalar($subject)) {
if (is_array($subject)) {
throw new \InvalidArgumentException(static::ARRAY_MSG);
}
throw new \TypeError(sprintf(static::INVALID_TYPE_MSG, gettype($subject)));
}
$result = preg_replace_callback($pattern, $replacement, $subject, $limit, $count, $flags | PREG_UNMATCHED_AS_NULL);
if ($result === null) {
throw PcreException::fromFunction('preg_replace_callback', $pattern);
}
return $result;
}
public static function replaceCallbackArray(array $pattern, $subject, int $limit = -1, int &$count = null, int $flags = 0): string
{
if (!is_scalar($subject)) {
if (is_array($subject)) {
throw new \InvalidArgumentException(static::ARRAY_MSG);
}
throw new \TypeError(sprintf(static::INVALID_TYPE_MSG, gettype($subject)));
}
$result = preg_replace_callback_array($pattern, $subject, $limit, $count, $flags | PREG_UNMATCHED_AS_NULL);
if ($result === null) {
$pattern = array_keys($pattern);
throw PcreException::fromFunction('preg_replace_callback_array', $pattern);
}
return $result;
}
public static function split(string $pattern, string $subject, int $limit = -1, int $flags = 0): array
{
if (($flags & PREG_SPLIT_OFFSET_CAPTURE) !== 0) {
throw new \InvalidArgumentException('PREG_SPLIT_OFFSET_CAPTURE is not supported as it changes the type of $matches, use splitWithOffsets() instead');
}
$result = preg_split($pattern, $subject, $limit, $flags);
if ($result === false) {
throw PcreException::fromFunction('preg_split', $pattern);
}
return $result;
}
/**
@phpstan-return
*/
public static function splitWithOffsets(string $pattern, string $subject, int $limit = -1, int $flags = 0): array
{
$result = preg_split($pattern, $subject, $limit, $flags | PREG_SPLIT_OFFSET_CAPTURE);
if ($result === false) {
throw PcreException::fromFunction('preg_split', $pattern);
}
return $result;
}
/**
@template
*/
public static function grep(string $pattern, array $array, int $flags = 0): array
{
$result = preg_grep($pattern, $array, $flags);
if ($result === false) {
throw PcreException::fromFunction('preg_grep', $pattern);
}
return $result;
}
public static function isMatch(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): bool
{
return (bool) static::match($pattern, $subject, $matches, $flags, $offset);
}
public static function isMatchAll(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): bool
{
return (bool) static::matchAll($pattern, $subject, $matches, $flags, $offset);
}
/**
@phpstan-param
*/
public static function isMatchWithOffsets(string $pattern, string $subject, ?array &$matches, int $flags = 0, int $offset = 0): bool
{
return (bool) static::matchWithOffsets($pattern, $subject, $matches, $flags, $offset);
}
/**
@phpstan-param
*/
public static function isMatchAllWithOffsets(string $pattern, string $subject, ?array &$matches, int $flags = 0, int $offset = 0): bool
{
return (bool) static::matchAllWithOffsets($pattern, $subject, $matches, $flags, $offset);
}
}
<?php
namespace Composer\Pcre;
class PcreException extends \RuntimeException
{
public static function fromFunction($function, $pattern)
{
$code = preg_last_error();
if (is_array($pattern)) {
$pattern = implode(', ', $pattern);
}
return new PcreException($function.'(): failed executing "'.$pattern.'": '.self::pcreLastErrorMessage($code), $code);
}
private static function pcreLastErrorMessage($code)
{
if (function_exists('preg_last_error_msg')) {
return preg_last_error_msg();
}
if (PHP_VERSION_ID < 70201 && $code === 0) {
return 'UNDEFINED_ERROR';
}
$constants = get_defined_constants(true);
if (!isset($constants['pcre'])) {
return 'UNDEFINED_ERROR';
}
foreach ($constants['pcre'] as $const => $val) {
if ($val === $code && substr($const, -6) === '_ERROR') {
return $const;
}
}
return 'UNDEFINED_ERROR';
}
}
<?php
declare(strict_types=1);
namespace Doctrine\Common\Lexer;
use ReflectionClass;
use function implode;
use function in_array;
use function preg_split;
use function sprintf;
use function substr;
use const PREG_SPLIT_DELIM_CAPTURE;
use const PREG_SPLIT_NO_EMPTY;
use const PREG_SPLIT_OFFSET_CAPTURE;
/**
@psalm-type
*/
abstract class AbstractLexer
{
private $input;
/**
@psalm-var
*/
private $tokens = [];
private $position = 0;
private $peek = 0;
/**
@psalm-var
*/
public $lookahead;
/**
@psalm-var
*/
public $token;
private $regex;
public function setInput($input)
{
$this->input = $input;
$this->tokens = [];
$this->reset();
$this->scan($input);
}
public function reset()
{
$this->lookahead = null;
$this->token = null;
$this->peek = 0;
$this->position = 0;
}
public function resetPeek()
{
$this->peek = 0;
}
public function resetPosition($position = 0)
{
$this->position = $position;
}
public function getInputUntilPosition($position)
{
return substr($this->input, 0, $position);
}
public function isNextToken($type)
{
return $this->lookahead !== null && $this->lookahead['type'] === $type;
}
public function isNextTokenAny(array $types)
{
return $this->lookahead !== null && in_array($this->lookahead['type'], $types, true);
}
public function moveNext()
{
$this->peek = 0;
$this->token = $this->lookahead;
$this->lookahead = isset($this->tokens[$this->position])
? $this->tokens[$this->position++] : null;
return $this->lookahead !== null;
}
public function skipUntil($type)
{
while ($this->lookahead !== null && $this->lookahead['type'] !== $type) {
$this->moveNext();
}
}
public function isA($value, $token)
{
return $this->getType($value) === $token;
}
/**
@psalm-return
*/
public function peek()
{
if (isset($this->tokens[$this->position + $this->peek])) {
return $this->tokens[$this->position + $this->peek++];
}
return null;
}
/**
@psalm-return
*/
public function glimpse()
{
$peek = $this->peek();
$this->peek = 0;
return $peek;
}
protected function scan($input)
{
if (! isset($this->regex)) {
$this->regex = sprintf(
'/(%s)|%s/%s',
implode(')|(', $this->getCatchablePatterns()),
implode('|', $this->getNonCatchablePatterns()),
$this->getModifiers()
);
}
$flags = PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE;
$matches = preg_split($this->regex, $input, -1, $flags);
if ($matches === false) {
$matches = [[$input, 0]];
}
foreach ($matches as $match) {
$type = $this->getType($match[0]);
$this->tokens[] = [
'value' => $match[0],
'type' => $type,
'position' => $match[1],
];
}
}
public function getLiteral($token)
{
$className = static::class;
$reflClass = new ReflectionClass($className);
$constants = $reflClass->getConstants();
foreach ($constants as $name => $value) {
if ($value === $token) {
return $className . '::' . $name;
}
}
return $token;
}
protected function getModifiers()
{
return 'iu';
}
abstract protected function getCatchablePatterns();
abstract protected function getNonCatchablePatterns();
abstract protected function getType(&$value);
}
<?php
namespace Doctrine\Common\Annotations;
use ReflectionClass;
use ReflectionFunction;
use SplFileObject;
use function is_file;
use function method_exists;
use function preg_quote;
use function preg_replace;
final class PhpParser
{
public function parseClass(ReflectionClass $class)
{
return $this->parseUseStatements($class);
}
/**
@psalm-return
*/
public function parseUseStatements($reflection): array
{
if (method_exists($reflection, 'getUseStatements')) {
return $reflection->getUseStatements();
}
$filename = $reflection->getFileName();
if ($filename === false) {
return [];
}
$content = $this->getFileContent($filename, $reflection->getStartLine());
if ($content === null) {
return [];
}
$namespace = preg_quote($reflection->getNamespaceName());
$content = preg_replace('/^.*?(\bnamespace\s+' . $namespace . '\s*[;{].*)$/s', '\\1', $content);
$tokenizer = new TokenParser('<?php ' . $content);
return $tokenizer->parseUseStatements($reflection->getNamespaceName());
}
private function getFileContent($filename, $lineNumber)
{
if (! is_file($filename)) {
return null;
}
$content = '';
$lineCnt = 0;
$file = new SplFileObject($filename);
while (! $file->eof()) {
if ($lineCnt++ === $lineNumber) {
break;
}
$content .= $file->fgets();
}
return $content;
}
}
<?php
namespace Doctrine\Common\Annotations;
use Exception;
use Throwable;
use function get_class;
use function gettype;
use function implode;
use function is_object;
use function sprintf;
class AnnotationException extends Exception
{
public static function syntaxError($message)
{
return new self('[Syntax Error] ' . $message);
}
public static function semanticalError($message)
{
return new self('[Semantical Error] ' . $message);
}
public static function creationError($message, ?Throwable $previous = null)
{
return new self('[Creation Error] ' . $message, 0, $previous);
}
public static function typeError($message)
{
return new self('[Type Error] ' . $message);
}
public static function semanticalErrorConstants($identifier, $context = null)
{
return self::semanticalError(sprintf(
"Couldn't find constant %s%s.",
$identifier,
$context ? ', ' . $context : ''
));
}
public static function attributeTypeError($attributeName, $annotationName, $context, $expected, $actual)
{
return self::typeError(sprintf(
'Attribute "%s" of @%s declared on %s expects %s, but got %s.',
$attributeName,
$annotationName,
$context,
$expected,
is_object($actual) ? 'an instance of ' . get_class($actual) : gettype($actual)
));
}
public static function requiredError($attributeName, $annotationName, $context, $expected)
{
return self::typeError(sprintf(
'Attribute "%s" of @%s declared on %s expects %s. This value should not be null.',
$attributeName,
$annotationName,
$context,
$expected
));
}
/**
@phpstan-param
*/
public static function enumeratorError($attributeName, $annotationName, $context, $available, $given)
{
return new self(sprintf(
'[Enum Error] Attribute "%s" of @%s declared on %s accepts only [%s], but got %s.',
$attributeName,
$annotationName,
$context,
implode(', ', $available),
is_object($given) ? get_class($given) : $given
));
}
public static function optimizerPlusSaveComments()
{
return new self(
'You have to enable opcache.save_comments=1 or zend_optimizerplus.save_comments=1.'
);
}
public static function optimizerPlusLoadComments()
{
return new self(
'You have to enable opcache.load_comments=1 or zend_optimizerplus.load_comments=1.'
);
}
}
<?php
namespace Doctrine\Common\Annotations;
use InvalidArgumentException;
use ReflectionClass;
use ReflectionMethod;
use ReflectionProperty;
use RuntimeException;
use function chmod;
use function file_put_contents;
use function filemtime;
use function gettype;
use function is_dir;
use function is_file;
use function is_int;
use function is_writable;
use function mkdir;
use function rename;
use function rtrim;
use function serialize;
use function sha1;
use function sprintf;
use function strtr;
use function tempnam;
use function uniqid;
use function unlink;
use function var_export;
class FileCacheReader implements Reader
{
private $reader;
private $dir;
private $debug;
/**
@phpstan-var */
private $loadedAnnotations = [];
private $classNameHashes = [];
private $umask;
public function __construct(Reader $reader, $cacheDir, $debug = false, $umask = 0002)
{
if (! is_int($umask)) {
throw new InvalidArgumentException(sprintf(
'The parameter umask must be an integer, was: %s',
gettype($umask)
));
}
$this->reader = $reader;
$this->umask = $umask;
if (! is_dir($cacheDir) && ! @mkdir($cacheDir, 0777 & (~$this->umask), true)) {
throw new InvalidArgumentException(sprintf(
'The directory "%s" does not exist and could not be created.',
$cacheDir
));
}
$this->dir = rtrim($cacheDir, '\\/');
$this->debug = $debug;
}
public function getClassAnnotations(ReflectionClass $class)
{
if (! isset($this->classNameHashes[$class->name])) {
$this->classNameHashes[$class->name] = sha1($class->name);
}
$key = $this->classNameHashes[$class->name];
if (isset($this->loadedAnnotations[$key])) {
return $this->loadedAnnotations[$key];
}
$path = $this->dir . '/' . strtr($key, '\\', '-') . '.cache.php';
if (! is_file($path)) {
$annot = $this->reader->getClassAnnotations($class);
$this->saveCacheFile($path, $annot);
return $this->loadedAnnotations[$key] = $annot;
}
$filename = $class->getFilename();
if (
$this->debug
&& $filename !== false
&& filemtime($path) < filemtime($filename)
) {
@unlink($path);
$annot = $this->reader->getClassAnnotations($class);
$this->saveCacheFile($path, $annot);
return $this->loadedAnnotations[$key] = $annot;
}
return $this->loadedAnnotations[$key] = include $path;
}
public function getPropertyAnnotations(ReflectionProperty $property)
{
$class = $property->getDeclaringClass();
if (! isset($this->classNameHashes[$class->name])) {
$this->classNameHashes[$class->name] = sha1($class->name);
}
$key = $this->classNameHashes[$class->name] . '$' . $property->getName();
if (isset($this->loadedAnnotations[$key])) {
return $this->loadedAnnotations[$key];
}
$path = $this->dir . '/' . strtr($key, '\\', '-') . '.cache.php';
if (! is_file($path)) {
$annot = $this->reader->getPropertyAnnotations($property);
$this->saveCacheFile($path, $annot);
return $this->loadedAnnotations[$key] = $annot;
}
$filename = $class->getFilename();
if (
$this->debug
&& $filename !== false
&& filemtime($path) < filemtime($filename)
) {
@unlink($path);
$annot = $this->reader->getPropertyAnnotations($property);
$this->saveCacheFile($path, $annot);
return $this->loadedAnnotations[$key] = $annot;
}
return $this->loadedAnnotations[$key] = include $path;
}
public function getMethodAnnotations(ReflectionMethod $method)
{
$class = $method->getDeclaringClass();
if (! isset($this->classNameHashes[$class->name])) {
$this->classNameHashes[$class->name] = sha1($class->name);
}
$key = $this->classNameHashes[$class->name] . '#' . $method->getName();
if (isset($this->loadedAnnotations[$key])) {
return $this->loadedAnnotations[$key];
}
$path = $this->dir . '/' . strtr($key, '\\', '-') . '.cache.php';
if (! is_file($path)) {
$annot = $this->reader->getMethodAnnotations($method);
$this->saveCacheFile($path, $annot);
return $this->loadedAnnotations[$key] = $annot;
}
$filename = $class->getFilename();
if (
$this->debug
&& $filename !== false
&& filemtime($path) < filemtime($filename)
) {
@unlink($path);
$annot = $this->reader->getMethodAnnotations($method);
$this->saveCacheFile($path, $annot);
return $this->loadedAnnotations[$key] = $annot;
}
return $this->loadedAnnotations[$key] = include $path;
}
private function saveCacheFile($path, $data)
{
if (! is_writable($this->dir)) {
throw new InvalidArgumentException(sprintf(
<<<'EXCEPTION'
The directory "%s" is not writable. Both the webserver and the console user need access.
You can manage access rights for multiple users with "chmod +a".
If your system does not support this, check out the acl package.,
EXCEPTION
,
$this->dir
));
}
$tempfile = tempnam($this->dir, uniqid('', true));
if ($tempfile === false) {
throw new RuntimeException(sprintf('Unable to create tempfile in directory: %s', $this->dir));
}
@chmod($tempfile, 0666 & (~$this->umask));
$written = file_put_contents(
$tempfile,
'<?php return unserialize(' . var_export(serialize($data), true) . ');'
);
if ($written === false) {
throw new RuntimeException(sprintf('Unable to write cached file to: %s', $tempfile));
}
@chmod($tempfile, 0666 & (~$this->umask));
if (rename($tempfile, $path) === false) {
@unlink($tempfile);
throw new RuntimeException(sprintf('Unable to rename %s to %s', $tempfile, $path));
}
}
public function getClassAnnotation(ReflectionClass $class, $annotationName)
{
$annotations = $this->getClassAnnotations($class);
foreach ($annotations as $annotation) {
if ($annotation instanceof $annotationName) {
return $annotation;
}
}
return null;
}
public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
{
$annotations = $this->getMethodAnnotations($method);
foreach ($annotations as $annotation) {
if ($annotation instanceof $annotationName) {
return $annotation;
}
}
return null;
}
public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
{
$annotations = $this->getPropertyAnnotations($property);
foreach ($annotations as $annotation) {
if ($annotation instanceof $annotationName) {
return $annotation;
}
}
return null;
}
public function clearLoadedAnnotations()
{
$this->loadedAnnotations = [];
}
}
<?php
namespace Doctrine\Common\Annotations\Annotation;
final class NamedArgumentConstructor
{
}
<?php
namespace Doctrine\Common\Annotations\Annotation;
use RuntimeException;
use function is_array;
use function is_string;
use function json_encode;
use function sprintf;
final class IgnoreAnnotation
{
/**
@phpstan-var */
public $names;
/**
@phpstan-param
*/
public function __construct(array $values)
{
if (is_string($values['value'])) {
$values['value'] = [$values['value']];
}
if (! is_array($values['value'])) {
throw new RuntimeException(sprintf(
'@IgnoreAnnotation expects either a string name, or an array of strings, but got %s.',
json_encode($values['value'])
));
}
$this->names = $values['value'];
}
}
<?php
namespace Doctrine\Common\Annotations\Annotation;
final class Attributes
{
public $value;
}
<?php
namespace Doctrine\Common\Annotations\Annotation;
use InvalidArgumentException;
use function get_class;
use function gettype;
use function in_array;
use function is_object;
use function is_scalar;
use function sprintf;
final class Enum
{
/**
@phpstan-var */
public $value;
public $literal;
/**
@phpstan-param
*/
public function __construct(array $values)
{
if (! isset($values['literal'])) {
$values['literal'] = [];
}
foreach ($values['value'] as $var) {
if (! is_scalar($var)) {
throw new InvalidArgumentException(sprintf(
'@Enum supports only scalar values "%s" given.',
is_object($var) ? get_class($var) : gettype($var)
));
}
}
foreach ($values['literal'] as $key => $var) {
if (! in_array($key, $values['value'])) {
throw new InvalidArgumentException(sprintf(
'Undefined enumerator value "%s" for literal "%s".',
$key,
$var
));
}
}
$this->value = $values['value'];
$this->literal = $values['literal'];
}
}
<?php
namespace Doctrine\Common\Annotations\Annotation;
final class Attribute
{
public $name;
public $type;
public $required = false;
}
<?php
namespace Doctrine\Common\Annotations\Annotation;
use InvalidArgumentException;
use function array_keys;
use function get_class;
use function gettype;
use function implode;
use function is_array;
use function is_object;
use function is_string;
use function sprintf;
final class Target
{
public const TARGET_CLASS = 1;
public const TARGET_METHOD = 2;
public const TARGET_PROPERTY = 4;
public const TARGET_ANNOTATION = 8;
public const TARGET_FUNCTION = 16;
public const TARGET_ALL = 31;
private static $map = [
'ALL' => self::TARGET_ALL,
'CLASS' => self::TARGET_CLASS,
'METHOD' => self::TARGET_METHOD,
'PROPERTY' => self::TARGET_PROPERTY,
'FUNCTION' => self::TARGET_FUNCTION,
'ANNOTATION' => self::TARGET_ANNOTATION,
];
/**
@phpstan-var */
public $value;
public $targets;
public $literal;
/**
@phpstan-param
*/
public function __construct(array $values)
{
if (! isset($values['value'])) {
$values['value'] = null;
}
if (is_string($values['value'])) {
$values['value'] = [$values['value']];
}
if (! is_array($values['value'])) {
throw new InvalidArgumentException(
sprintf(
'@Target expects either a string value, or an array of strings, "%s" given.',
is_object($values['value']) ? get_class($values['value']) : gettype($values['value'])
)
);
}
$bitmask = 0;
foreach ($values['value'] as $literal) {
if (! isset(self::$map[$literal])) {
throw new InvalidArgumentException(
sprintf(
'Invalid Target "%s". Available targets: [%s]',
$literal,
implode(', ', array_keys(self::$map))
)
);
}
$bitmask |= self::$map[$literal];
}
$this->targets = $bitmask;
$this->value = $values['value'];
$this->literal = implode(', ', $this->value);
}
}
<?php
namespace Doctrine\Common\Annotations\Annotation;
final class Required
{
}
<?php
namespace Doctrine\Common\Annotations;
use ReflectionClass;
use ReflectionMethod;
use ReflectionProperty;
use function call_user_func_array;
use function get_class;
class IndexedReader implements Reader
{
private $delegate;
public function __construct(Reader $reader)
{
$this->delegate = $reader;
}
public function getClassAnnotations(ReflectionClass $class)
{
$annotations = [];
foreach ($this->delegate->getClassAnnotations($class) as $annot) {
$annotations[get_class($annot)] = $annot;
}
return $annotations;
}
public function getClassAnnotation(ReflectionClass $class, $annotationName)
{
return $this->delegate->getClassAnnotation($class, $annotationName);
}
public function getMethodAnnotations(ReflectionMethod $method)
{
$annotations = [];
foreach ($this->delegate->getMethodAnnotations($method) as $annot) {
$annotations[get_class($annot)] = $annot;
}
return $annotations;
}
public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
{
return $this->delegate->getMethodAnnotation($method, $annotationName);
}
public function getPropertyAnnotations(ReflectionProperty $property)
{
$annotations = [];
foreach ($this->delegate->getPropertyAnnotations($property) as $annot) {
$annotations[get_class($annot)] = $annot;
}
return $annotations;
}
public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
{
return $this->delegate->getPropertyAnnotation($property, $annotationName);
}
public function __call($method, $args)
{
return call_user_func_array([$this->delegate, $method], $args);
}
}
<?php
namespace Doctrine\Common\Annotations;
use ReflectionClass;
use ReflectionMethod;
use ReflectionProperty;
interface Reader
{
public function getClassAnnotations(ReflectionClass $class);
/**
@template
*/
public function getClassAnnotation(ReflectionClass $class, $annotationName);
public function getMethodAnnotations(ReflectionMethod $method);
/**
@template
*/
public function getMethodAnnotation(ReflectionMethod $method, $annotationName);
public function getPropertyAnnotations(ReflectionProperty $property);
/**
@template
*/
public function getPropertyAnnotation(ReflectionProperty $property, $annotationName);
}
<?php
namespace Doctrine\Common\Annotations;
use BadMethodCallException;
use function sprintf;
class Annotation
{
public $value;
final public function __construct(array $data)
{
foreach ($data as $key => $value) {
$this->$key = $value;
}
}
public function __get($name)
{
throw new BadMethodCallException(
sprintf("Unknown property '%s' on annotation '%s'.", $name, static::class)
);
}
public function __set($name, $value)
{
throw new BadMethodCallException(
sprintf("Unknown property '%s' on annotation '%s'.", $name, static::class)
);
}
}
<?php
namespace Doctrine\Common\Annotations;
use ReflectionClass;
use ReflectionMethod;
use ReflectionProperty;
class SimpleAnnotationReader implements Reader
{
private $parser;
public function __construct()
{
$this->parser = new DocParser();
$this->parser->setIgnoreNotImportedAnnotations(true);
}
public function addNamespace($namespace)
{
$this->parser->addNamespace($namespace);
}
public function getClassAnnotations(ReflectionClass $class)
{
return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName());
}
public function getMethodAnnotations(ReflectionMethod $method)
{
return $this->parser->parse(
$method->getDocComment(),
'method ' . $method->getDeclaringClass()->name . '::' . $method->getName() . '()'
);
}
public function getPropertyAnnotations(ReflectionProperty $property)
{
return $this->parser->parse(
$property->getDocComment(),
'property ' . $property->getDeclaringClass()->name . '::$' . $property->getName()
);
}
public function getClassAnnotation(ReflectionClass $class, $annotationName)
{
foreach ($this->getClassAnnotations($class) as $annot) {
if ($annot instanceof $annotationName) {
return $annot;
}
}
return null;
}
public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
{
foreach ($this->getMethodAnnotations($method) as $annot) {
if ($annot instanceof $annotationName) {
return $annot;
}
}
return null;
}
public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
{
foreach ($this->getPropertyAnnotations($property) as $annot) {
if ($annot instanceof $annotationName) {
return $annot;
}
}
return null;
}
}
<?php
namespace Doctrine\Common\Annotations;
use Psr\Cache\CacheItemPoolInterface;
use ReflectionClass;
use ReflectionMethod;
use ReflectionProperty;
use Reflector;
use function array_map;
use function array_merge;
use function assert;
use function filemtime;
use function max;
use function rawurlencode;
use function time;
final class PsrCachedReader implements Reader
{
private $delegate;
private $cache;
private $debug;
private $loadedAnnotations = [];
private $loadedFilemtimes = [];
public function __construct(Reader $reader, CacheItemPoolInterface $cache, bool $debug = false)
{
$this->delegate = $reader;
$this->cache = $cache;
$this->debug = (bool) $debug;
}
public function getClassAnnotations(ReflectionClass $class)
{
$cacheKey = $class->getName();
if (isset($this->loadedAnnotations[$cacheKey])) {
return $this->loadedAnnotations[$cacheKey];
}
$annots = $this->fetchFromCache($cacheKey, $class, 'getClassAnnotations', $class);
return $this->loadedAnnotations[$cacheKey] = $annots;
}
public function getClassAnnotation(ReflectionClass $class, $annotationName)
{
foreach ($this->getClassAnnotations($class) as $annot) {
if ($annot instanceof $annotationName) {
return $annot;
}
}
return null;
}
public function getPropertyAnnotations(ReflectionProperty $property)
{
$class = $property->getDeclaringClass();
$cacheKey = $class->getName() . '$' . $property->getName();
if (isset($this->loadedAnnotations[$cacheKey])) {
return $this->loadedAnnotations[$cacheKey];
}
$annots = $this->fetchFromCache($cacheKey, $class, 'getPropertyAnnotations', $property);
return $this->loadedAnnotations[$cacheKey] = $annots;
}
public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
{
foreach ($this->getPropertyAnnotations($property) as $annot) {
if ($annot instanceof $annotationName) {
return $annot;
}
}
return null;
}
public function getMethodAnnotations(ReflectionMethod $method)
{
$class = $method->getDeclaringClass();
$cacheKey = $class->getName() . '#' . $method->getName();
if (isset($this->loadedAnnotations[$cacheKey])) {
return $this->loadedAnnotations[$cacheKey];
}
$annots = $this->fetchFromCache($cacheKey, $class, 'getMethodAnnotations', $method);
return $this->loadedAnnotations[$cacheKey] = $annots;
}
public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
{
foreach ($this->getMethodAnnotations($method) as $annot) {
if ($annot instanceof $annotationName) {
return $annot;
}
}
return null;
}
public function clearLoadedAnnotations(): void
{
$this->loadedAnnotations = [];
$this->loadedFilemtimes = [];
}
private function fetchFromCache(
string $cacheKey,
ReflectionClass $class,
string $method,
Reflector $reflector
): array {
$cacheKey = rawurlencode($cacheKey);
$item = $this->cache->getItem($cacheKey);
if (($this->debug && ! $this->refresh($cacheKey, $class)) || ! $item->isHit()) {
$this->cache->save($item->set($this->delegate->{$method}($reflector)));
}
return $item->get();
}
private function refresh(string $cacheKey, ReflectionClass $class): bool
{
$lastModification = $this->getLastModification($class);
if ($lastModification === 0) {
return true;
}
$item = $this->cache->getItem('[C]' . $cacheKey);
if ($item->isHit() && $item->get() >= $lastModification) {
return true;
}
$this->cache->save($item->set(time()));
return false;
}
private function getLastModification(ReflectionClass $class): int
{
$filename = $class->getFileName();
if (isset($this->loadedFilemtimes[$filename])) {
return $this->loadedFilemtimes[$filename];
}
$parent = $class->getParentClass();
$lastModification = max(array_merge(
[$filename ? filemtime($filename) : 0],
array_map(function (ReflectionClass $reflectionTrait): int {
return $this->getTraitLastModificationTime($reflectionTrait);
}, $class->getTraits()),
array_map(function (ReflectionClass $class): int {
return $this->getLastModification($class);
}, $class->getInterfaces()),
$parent ? [$this->getLastModification($parent)] : []
));
assert($lastModification !== false);
return $this->loadedFilemtimes[$filename] = $lastModification;
}
private function getTraitLastModificationTime(ReflectionClass $reflectionTrait): int
{
$fileName = $reflectionTrait->getFileName();
if (isset($this->loadedFilemtimes[$fileName])) {
return $this->loadedFilemtimes[$fileName];
}
$lastModificationTime = max(array_merge(
[$fileName ? filemtime($fileName) : 0],
array_map(function (ReflectionClass $reflectionTrait): int {
return $this->getTraitLastModificationTime($reflectionTrait);
}, $reflectionTrait->getTraits())
));
assert($lastModificationTime !== false);
return $this->loadedFilemtimes[$fileName] = $lastModificationTime;
}
}
<?php
namespace Doctrine\Common\Annotations;
use Doctrine\Common\Annotations\Annotation\Attribute;
use Doctrine\Common\Annotations\Annotation\Attributes;
use Doctrine\Common\Annotations\Annotation\Enum;
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
use Doctrine\Common\Annotations\Annotation\Target;
use ReflectionClass;
use ReflectionException;
use ReflectionProperty;
use RuntimeException;
use stdClass;
use Throwable;
use function array_keys;
use function array_map;
use function array_pop;
use function array_values;
use function class_exists;
use function constant;
use function count;
use function defined;
use function explode;
use function gettype;
use function implode;
use function in_array;
use function interface_exists;
use function is_array;
use function is_object;
use function json_encode;
use function ltrim;
use function preg_match;
use function reset;
use function rtrim;
use function sprintf;
use function stripos;
use function strlen;
use function strpos;
use function strrpos;
use function strtolower;
use function substr;
use function trim;
use const PHP_VERSION_ID;
final class DocParser
{
/**
@phpstan-var
*/
private static $classIdentifiers = [
DocLexer::T_IDENTIFIER,
DocLexer::T_TRUE,
DocLexer::T_FALSE,
DocLexer::T_NULL,
];
private $lexer;
private $target;
private static $metadataParser;
private $isNestedAnnotation = false;
private $imports = [];
private $classExists = [];
private $ignoreNotImportedAnnotations = false;
private $namespaces = [];
private $ignoredAnnotationNames = [];
private $ignoredAnnotationNamespaces = [];
private $context = '';
private static $annotationMetadata = [
Annotation\Target::class => [
'is_annotation' => true,
'has_constructor' => true,
'has_named_argument_constructor' => false,
'properties' => [],
'targets_literal' => 'ANNOTATION_CLASS',
'targets' => Target::TARGET_CLASS,
'default_property' => 'value',
'attribute_types' => [
'value' => [
'required' => false,
'type' => 'array',
'array_type' => 'string',
'value' => 'array<string>',
],
],
],
Annotation\Attribute::class => [
'is_annotation' => true,
'has_constructor' => false,
'has_named_argument_constructor' => false,
'targets_literal' => 'ANNOTATION_ANNOTATION',
'targets' => Target::TARGET_ANNOTATION,
'default_property' => 'name',
'properties' => [
'name' => 'name',
'type' => 'type',
'required' => 'required',
],
'attribute_types' => [
'value' => [
'required' => true,
'type' => 'string',
'value' => 'string',
],
'type' => [
'required' => true,
'type' => 'string',
'value' => 'string',
],
'required' => [
'required' => false,
'type' => 'boolean',
'value' => 'boolean',
],
],
],
Annotation\Attributes::class => [
'is_annotation' => true,
'has_constructor' => false,
'has_named_argument_constructor' => false,
'targets_literal' => 'ANNOTATION_CLASS',
'targets' => Target::TARGET_CLASS,
'default_property' => 'value',
'properties' => ['value' => 'value'],
'attribute_types' => [
'value' => [
'type' => 'array',
'required' => true,
'array_type' => Annotation\Attribute::class,
'value' => 'array<' . Annotation\Attribute::class . '>',
],
],
],
Annotation\Enum::class => [
'is_annotation' => true,
'has_constructor' => true,
'has_named_argument_constructor' => false,
'targets_literal' => 'ANNOTATION_PROPERTY',
'targets' => Target::TARGET_PROPERTY,
'default_property' => 'value',
'properties' => ['value' => 'value'],
'attribute_types' => [
'value' => [
'type' => 'array',
'required' => true,
],
'literal' => [
'type' => 'array',
'required' => false,
],
],
],
Annotation\NamedArgumentConstructor::class => [
'is_annotation' => true,
'has_constructor' => false,
'has_named_argument_constructor' => false,
'targets_literal' => 'ANNOTATION_CLASS',
'targets' => Target::TARGET_CLASS,
'default_property' => null,
'properties' => [],
'attribute_types' => [],
],
];
private static $typeMap = [
'float' => 'double',
'bool' => 'boolean',
'Boolean' => 'boolean',
'int' => 'integer',
];
public function __construct()
{
$this->lexer = new DocLexer();
}
public function setIgnoredAnnotationNames(array $names)
{
$this->ignoredAnnotationNames = $names;
}
public function setIgnoredAnnotationNamespaces($ignoredAnnotationNamespaces)
{
$this->ignoredAnnotationNamespaces = $ignoredAnnotationNamespaces;
}
public function setIgnoreNotImportedAnnotations($bool)
{
$this->ignoreNotImportedAnnotations = (bool) $bool;
}
public function addNamespace($namespace)
{
if ($this->imports) {
throw new RuntimeException('You must either use addNamespace(), or setImports(), but not both.');
}
$this->namespaces[] = $namespace;
}
public function setImports(array $imports)
{
if ($this->namespaces) {
throw new RuntimeException('You must either use addNamespace(), or setImports(), but not both.');
}
$this->imports = $imports;
}
public function setTarget($target)
{
$this->target = $target;
}
/**
@phpstan-return
*/
public function parse($input, $context = '')
{
$pos = $this->findInitialTokenPosition($input);
if ($pos === null) {
return [];
}
$this->context = $context;
$this->lexer->setInput(trim(substr($input, $pos), '* /'));
$this->lexer->moveNext();
return $this->Annotations();
}
private function findInitialTokenPosition($input): ?int
{
$pos = 0;
while (($pos = strpos($input, '@', $pos)) !== false) {
$preceding = substr($input, $pos - 1, 1);
if ($pos === 0 || $preceding === ' ' || $preceding === '*' || $preceding === "\t") {
return $pos;
}
$pos++;
}
return null;
}
private function match(int $token): bool
{
if (! $this->lexer->isNextToken($token)) {
throw $this->syntaxError($this->lexer->getLiteral($token));
}
return $this->lexer->moveNext();
}
/**
@phpstan-param
*/
private function matchAny(array $tokens): bool
{
if (! $this->lexer->isNextTokenAny($tokens)) {
throw $this->syntaxError(implode(' or ', array_map([$this->lexer, 'getLiteral'], $tokens)));
}
return $this->lexer->moveNext();
}
private function syntaxError(string $expected, ?array $token = null): AnnotationException
{
if ($token === null) {
$token = $this->lexer->lookahead;
}
$message = sprintf('Expected %s, got ', $expected);
$message .= $this->lexer->lookahead === null
? 'end of string'
: sprintf("'%s' at position %s", $token['value'], $token['position']);
if (strlen($this->context)) {
$message .= ' in ' . $this->context;
}
$message .= '.';
return AnnotationException::syntaxError($message);
}
private function classExists(string $fqcn): bool
{
if (isset($this->classExists[$fqcn])) {
return $this->classExists[$fqcn];
}
if (class_exists($fqcn, false)) {
return $this->classExists[$fqcn] = true;
}
return $this->classExists[$fqcn] = AnnotationRegistry::loadAnnotationClass($fqcn);
}
private function collectAnnotationMetadata(string $name): void
{
if (self::$metadataParser === null) {
self::$metadataParser = new self();
self::$metadataParser->setIgnoreNotImportedAnnotations(true);
self::$metadataParser->setIgnoredAnnotationNames($this->ignoredAnnotationNames);
self::$metadataParser->setImports([
'enum' => Enum::class,
'target' => Target::class,
'attribute' => Attribute::class,
'attributes' => Attributes::class,
'namedargumentconstructor' => NamedArgumentConstructor::class,
]);
class_exists(Enum::class);
class_exists(Target::class);
class_exists(Attribute::class);
class_exists(Attributes::class);
class_exists(NamedArgumentConstructor::class);
}
$class = new ReflectionClass($name);
$docComment = $class->getDocComment();
$constructor = $class->getConstructor();
$metadata = [
'default_property' => null,
'has_constructor' => $constructor !== null && $constructor->getNumberOfParameters() > 0,
'constructor_args' => [],
'properties' => [],
'property_types' => [],
'attribute_types' => [],
'targets_literal' => null,
'targets' => Target::TARGET_ALL,
'is_annotation' => strpos($docComment, '@Annotation') !== false,
];
$metadata['has_named_argument_constructor'] = $metadata['has_constructor']
&& $class->implementsInterface(NamedArgumentConstructorAnnotation::class);
if ($metadata['is_annotation']) {
self::$metadataParser->setTarget(Target::TARGET_CLASS);
foreach (self::$metadataParser->parse($docComment, 'class @' . $name) as $annotation) {
if ($annotation instanceof Target) {
$metadata['targets'] = $annotation->targets;
$metadata['targets_literal'] = $annotation->literal;
continue;
}
if ($annotation instanceof NamedArgumentConstructor) {
$metadata['has_named_argument_constructor'] = $metadata['has_constructor'];
if ($metadata['has_named_argument_constructor']) {
$metadata['default_property'] = $constructor->getParameters()[0]->getName();
}
}
if (! ($annotation instanceof Attributes)) {
continue;
}
foreach ($annotation->value as $attribute) {
$this->collectAttributeTypeMetadata($metadata, $attribute);
}
}
if ($metadata['has_constructor'] === false) {
foreach ($class->getProperties(ReflectionProperty::IS_PUBLIC) as $property) {
$metadata['properties'][$property->name] = $property->name;
$propertyComment = $property->getDocComment();
if ($propertyComment === false) {
continue;
}
$attribute = new Attribute();
$attribute->required = (strpos($propertyComment, '@Required') !== false);
$attribute->name = $property->name;
$attribute->type = (strpos($propertyComment, '@var') !== false &&
preg_match('/@var\s+([^\s]+)/', $propertyComment, $matches))
? $matches[1]
: 'mixed';
$this->collectAttributeTypeMetadata($metadata, $attribute);
if (strpos($propertyComment, '@Enum') === false) {
continue;
}
$context = 'property ' . $class->name . '::$' . $property->name;
self::$metadataParser->setTarget(Target::TARGET_PROPERTY);
foreach (self::$metadataParser->parse($propertyComment, $context) as $annotation) {
if (! $annotation instanceof Enum) {
continue;
}
$metadata['enum'][$property->name]['value'] = $annotation->value;
$metadata['enum'][$property->name]['literal'] = (! empty($annotation->literal))
? $annotation->literal
: $annotation->value;
}
}
$metadata['default_property'] = reset($metadata['properties']);
} elseif ($metadata['has_named_argument_constructor']) {
foreach ($constructor->getParameters() as $parameter) {
$metadata['constructor_args'][$parameter->getName()] = [
'position' => $parameter->getPosition(),
'default' => $parameter->isOptional() ? $parameter->getDefaultValue() : null,
];
}
}
}
self::$annotationMetadata[$name] = $metadata;
}
private function collectAttributeTypeMetadata(array &$metadata, Attribute $attribute): void
{
$type = self::$typeMap[$attribute->type] ?? $attribute->type;
if ($type === 'mixed') {
return;
}
$pos = strpos($type, '<');
if ($pos !== false) {
$arrayType = substr($type, $pos + 1, -1);
$type = 'array';
if (isset(self::$typeMap[$arrayType])) {
$arrayType = self::$typeMap[$arrayType];
}
$metadata['attribute_types'][$attribute->name]['array_type'] = $arrayType;
} else {
$pos = strrpos($type, '[');
if ($pos !== false) {
$arrayType = substr($type, 0, $pos);
$type = 'array';
if (isset(self::$typeMap[$arrayType])) {
$arrayType = self::$typeMap[$arrayType];
}
$metadata['attribute_types'][$attribute->name]['array_type'] = $arrayType;
}
}
$metadata['attribute_types'][$attribute->name]['type'] = $type;
$metadata['attribute_types'][$attribute->name]['value'] = $attribute->type;
$metadata['attribute_types'][$attribute->name]['required'] = $attribute->required;
}
/**
@phpstan-return
*/
private function Annotations(): array
{
$annotations = [];
while ($this->lexer->lookahead !== null) {
if ($this->lexer->lookahead['type'] !== DocLexer::T_AT) {
$this->lexer->moveNext();
continue;
}
if (
$this->lexer->token !== null &&
$this->lexer->lookahead['position'] === $this->lexer->token['position'] + strlen(
$this->lexer->token['value']
)
) {
$this->lexer->moveNext();
continue;
}
$peek = $this->lexer->glimpse();
if (
($peek === null)
|| ($peek['type'] !== DocLexer::T_NAMESPACE_SEPARATOR && ! in_array(
$peek['type'],
self::$classIdentifiers,
true
))
|| $peek['position'] !== $this->lexer->lookahead['position'] + 1
) {
$this->lexer->moveNext();
continue;
}
$this->isNestedAnnotation = false;
$annot = $this->Annotation();
if ($annot === false) {
continue;
}
$annotations[] = $annot;
}
return $annotations;
}
private function Annotation()
{
$this->match(DocLexer::T_AT);
$name = $this->Identifier();
if (
$this->lexer->isNextToken(DocLexer::T_MINUS)
&& $this->lexer->nextTokenIsAdjacent()
) {
return false;
}
$originalName = $name;
if ($name[0] !== '\\') {
$pos = strpos($name, '\\');
$alias = ($pos === false) ? $name : substr($name, 0, $pos);
$found = false;
$loweredAlias = strtolower($alias);
if ($this->namespaces) {
foreach ($this->namespaces as $namespace) {
if ($this->classExists($namespace . '\\' . $name)) {
$name = $namespace . '\\' . $name;
$found = true;
break;
}
}
} elseif (isset($this->imports[$loweredAlias])) {
$namespace = ltrim($this->imports[$loweredAlias], '\\');
$name = ($pos !== false)
? $namespace . substr($name, $pos)
: $namespace;
$found = $this->classExists($name);
} elseif (
! isset($this->ignoredAnnotationNames[$name])
&& isset($this->imports['__NAMESPACE__'])
&& $this->classExists($this->imports['__NAMESPACE__'] . '\\' . $name)
) {
$name = $this->imports['__NAMESPACE__'] . '\\' . $name;
$found = true;
} elseif (! isset($this->ignoredAnnotationNames[$name]) && $this->classExists($name)) {
$found = true;
}
if (! $found) {
if ($this->isIgnoredAnnotation($name)) {
return false;
}
throw AnnotationException::semanticalError(sprintf(
<<<'EXCEPTION'
The annotation "@%s" in %s was never imported. Did you maybe forget to add a "use" statement for this annotation?
EXCEPTION
,
$name,
$this->context
));
}
}
$name = ltrim($name, '\\');
if (! $this->classExists($name)) {
throw AnnotationException::semanticalError(sprintf(
'The annotation "@%s" in %s does not exist, or could not be auto-loaded.',
$name,
$this->context
));
}
if (! isset(self::$annotationMetadata[$name])) {
$this->collectAnnotationMetadata($name);
}
if (self::$annotationMetadata[$name]['is_annotation'] === false) {
if ($this->isIgnoredAnnotation($originalName) || $this->isIgnoredAnnotation($name)) {
return false;
}
throw AnnotationException::semanticalError(sprintf(
<<<'EXCEPTION'
The class "%s" is not annotated with @Annotation.
Are you sure this class can be used as annotation?
If so, then you need to add @Annotation to the _class_ doc comment of "%s".
If it is indeed no annotation, then you need to add @IgnoreAnnotation("%s") to the _class_ doc comment of %s.
EXCEPTION
,
$name,
$name,
$originalName,
$this->context
));
}
$target = $this->isNestedAnnotation ? Target::TARGET_ANNOTATION : $this->target;
$this->isNestedAnnotation = true;
if ((self::$annotationMetadata[$name]['targets'] & $target) === 0 && $target) {
throw AnnotationException::semanticalError(
sprintf(
<<<'EXCEPTION'
Annotation @%s is not allowed to be declared on %s. You may only use this annotation on these code elements: %s.
EXCEPTION
,
$originalName,
$this->context,
self::$annotationMetadata[$name]['targets_literal']
)
);
}
$arguments = $this->MethodCall();
$values = $this->resolvePositionalValues($arguments, $name);
if (isset(self::$annotationMetadata[$name]['enum'])) {
foreach (self::$annotationMetadata[$name]['enum'] as $property => $enum) {
if (isset($values[$property]) && ! in_array($values[$property], $enum['value'])) {
throw AnnotationException::enumeratorError(
$property,
$name,
$this->context,
$enum['literal'],
$values[$property]
);
}
}
}
foreach (self::$annotationMetadata[$name]['attribute_types'] as $property => $type) {
if (
$property === self::$annotationMetadata[$name]['default_property']
&& ! isset($values[$property]) && isset($values['value'])
) {
$property = 'value';
}
if (! isset($values[$property])) {
if ($type['required']) {
throw AnnotationException::requiredError(
$property,
$originalName,
$this->context,
'a(n) ' . $type['value']
);
}
continue;
}
if ($type['type'] === 'array') {
if (! is_array($values[$property])) {
$values[$property] = [$values[$property]];
}
if (isset($type['array_type'])) {
foreach ($values[$property] as $item) {
if (gettype($item) !== $type['array_type'] && ! $item instanceof $type['array_type']) {
throw AnnotationException::attributeTypeError(
$property,
$originalName,
$this->context,
'either a(n) ' . $type['array_type'] . ', or an array of ' . $type['array_type'] . 's',
$item
);
}
}
}
} elseif (gettype($values[$property]) !== $type['type'] && ! $values[$property] instanceof $type['type']) {
throw AnnotationException::attributeTypeError(
$property,
$originalName,
$this->context,
'a(n) ' . $type['value'],
$values[$property]
);
}
}
if (self::$annotationMetadata[$name]['has_named_argument_constructor']) {
if (PHP_VERSION_ID >= 80000) {
return $this->instantiateAnnotiation($originalName, $this->context, $name, $values);
}
$positionalValues = [];
foreach (self::$annotationMetadata[$name]['constructor_args'] as $property => $parameter) {
$positionalValues[$parameter['position']] = $parameter['default'];
}
foreach ($values as $property => $value) {
if (! isset(self::$annotationMetadata[$name]['constructor_args'][$property])) {
throw AnnotationException::creationError(sprintf(
<<<'EXCEPTION'
The annotation @%s declared on %s does not have a property named "%s"
that can be set through its named arguments constructor.
Available named arguments: %s
EXCEPTION
,
$originalName,
$this->context,
$property,
implode(', ', array_keys(self::$annotationMetadata[$name]['constructor_args']))
));
}
$positionalValues[self::$annotationMetadata[$name]['constructor_args'][$property]['position']] = $value;
}
return $this->instantiateAnnotiation($originalName, $this->context, $name, $positionalValues);
}
if (self::$annotationMetadata[$name]['has_constructor'] === true) {
return $this->instantiateAnnotiation($originalName, $this->context, $name, [$values]);
}
$instance = $this->instantiateAnnotiation($originalName, $this->context, $name, []);
foreach ($values as $property => $value) {
if (! isset(self::$annotationMetadata[$name]['properties'][$property])) {
if ($property !== 'value') {
throw AnnotationException::creationError(sprintf(
<<<'EXCEPTION'
The annotation @%s declared on %s does not have a property named "%s".
Available properties: %s
EXCEPTION
,
$originalName,
$this->context,
$property,
implode(', ', self::$annotationMetadata[$name]['properties'])
));
}
$property = self::$annotationMetadata[$name]['default_property'];
if (! $property) {
throw AnnotationException::creationError(sprintf(
'The annotation @%s declared on %s does not accept any values, but got %s.',
$originalName,
$this->context,
json_encode($values)
));
}
}
$instance->{$property} = $value;
}
return $instance;
}
private function MethodCall(): array
{
$values = [];
if (! $this->lexer->isNextToken(DocLexer::T_OPEN_PARENTHESIS)) {
return $values;
}
$this->match(DocLexer::T_OPEN_PARENTHESIS);
if (! $this->lexer->isNextToken(DocLexer::T_CLOSE_PARENTHESIS)) {
$values = $this->Values();
}
$this->match(DocLexer::T_CLOSE_PARENTHESIS);
return $values;
}
private function Values(): array
{
$values = [$this->Value()];
while ($this->lexer->isNextToken(DocLexer::T_COMMA)) {
$this->match(DocLexer::T_COMMA);
if ($this->lexer->isNextToken(DocLexer::T_CLOSE_PARENTHESIS)) {
break;
}
$token = $this->lexer->lookahead;
$value = $this->Value();
$values[] = $value;
}
$namedArguments = [];
$positionalArguments = [];
foreach ($values as $k => $value) {
if (is_object($value) && $value instanceof stdClass) {
$namedArguments[$value->name] = $value->value;
} else {
$positionalArguments[$k] = $value;
}
}
return ['named_arguments' => $namedArguments, 'positional_arguments' => $positionalArguments];
}
private function Constant()
{
$identifier = $this->Identifier();
if (! defined($identifier) && strpos($identifier, '::') !== false && $identifier[0] !== '\\') {
[$className, $const] = explode('::', $identifier);
$pos = strpos($className, '\\');
$alias = ($pos === false) ? $className : substr($className, 0, $pos);
$found = false;
$loweredAlias = strtolower($alias);
switch (true) {
case ! empty($this->namespaces):
foreach ($this->namespaces as $ns) {
if (class_exists($ns . '\\' . $className) || interface_exists($ns . '\\' . $className)) {
$className = $ns . '\\' . $className;
$found = true;
break;
}
}
break;
case isset($this->imports[$loweredAlias]):
$found = true;
$className = ($pos !== false)
? $this->imports[$loweredAlias] . substr($className, $pos)
: $this->imports[$loweredAlias];
break;
default:
if (isset($this->imports['__NAMESPACE__'])) {
$ns = $this->imports['__NAMESPACE__'];
if (class_exists($ns . '\\' . $className) || interface_exists($ns . '\\' . $className)) {
$className = $ns . '\\' . $className;
$found = true;
}
}
break;
}
if ($found) {
$identifier = $className . '::' . $const;
}
}
if (
$this->identifierEndsWithClassConstant($identifier) &&
! $this->identifierStartsWithBackslash($identifier)
) {
return substr($identifier, 0, $this->getClassConstantPositionInIdentifier($identifier));
}
if ($this->identifierEndsWithClassConstant($identifier) && $this->identifierStartsWithBackslash($identifier)) {
return substr($identifier, 1, $this->getClassConstantPositionInIdentifier($identifier) - 1);
}
if (! defined($identifier)) {
throw AnnotationException::semanticalErrorConstants($identifier, $this->context);
}
return constant($identifier);
}
private function identifierStartsWithBackslash(string $identifier): bool
{
return $identifier[0] === '\\';
}
private function identifierEndsWithClassConstant(string $identifier): bool
{
return $this->getClassConstantPositionInIdentifier($identifier) === strlen($identifier) - strlen('::class');
}
private function getClassConstantPositionInIdentifier(string $identifier)
{
return stripos($identifier, '::class');
}
private function Identifier(): string
{
if (! $this->lexer->isNextTokenAny(self::$classIdentifiers)) {
throw $this->syntaxError('namespace separator or identifier');
}
$this->lexer->moveNext();
$className = $this->lexer->token['value'];
while (
$this->lexer->lookahead !== null &&
$this->lexer->lookahead['position'] === ($this->lexer->token['position'] +
strlen($this->lexer->token['value'])) &&
$this->lexer->isNextToken(DocLexer::T_NAMESPACE_SEPARATOR)
) {
$this->match(DocLexer::T_NAMESPACE_SEPARATOR);
$this->matchAny(self::$classIdentifiers);
$className .= '\\' . $this->lexer->token['value'];
}
return $className;
}
private function Value()
{
$peek = $this->lexer->glimpse();
if ($peek['type'] === DocLexer::T_EQUALS) {
return $this->FieldAssignment();
}
return $this->PlainValue();
}
private function PlainValue()
{
if ($this->lexer->isNextToken(DocLexer::T_OPEN_CURLY_BRACES)) {
return $this->Arrayx();
}
if ($this->lexer->isNextToken(DocLexer::T_AT)) {
return $this->Annotation();
}
if ($this->lexer->isNextToken(DocLexer::T_IDENTIFIER)) {
return $this->Constant();
}
switch ($this->lexer->lookahead['type']) {
case DocLexer::T_STRING:
$this->match(DocLexer::T_STRING);
return $this->lexer->token['value'];
case DocLexer::T_INTEGER:
$this->match(DocLexer::T_INTEGER);
return (int) $this->lexer->token['value'];
case DocLexer::T_FLOAT:
$this->match(DocLexer::T_FLOAT);
return (float) $this->lexer->token['value'];
case DocLexer::T_TRUE:
$this->match(DocLexer::T_TRUE);
return true;
case DocLexer::T_FALSE:
$this->match(DocLexer::T_FALSE);
return false;
case DocLexer::T_NULL:
$this->match(DocLexer::T_NULL);
return null;
default:
throw $this->syntaxError('PlainValue');
}
}
private function FieldAssignment(): stdClass
{
$this->match(DocLexer::T_IDENTIFIER);
$fieldName = $this->lexer->token['value'];
$this->match(DocLexer::T_EQUALS);
$item = new stdClass();
$item->name = $fieldName;
$item->value = $this->PlainValue();
return $item;
}
private function Arrayx(): array
{
$array = $values = [];
$this->match(DocLexer::T_OPEN_CURLY_BRACES);
if ($this->lexer->isNextToken(DocLexer::T_CLOSE_CURLY_BRACES)) {
$this->match(DocLexer::T_CLOSE_CURLY_BRACES);
return $array;
}
$values[] = $this->ArrayEntry();
while ($this->lexer->isNextToken(DocLexer::T_COMMA)) {
$this->match(DocLexer::T_COMMA);
if ($this->lexer->isNextToken(DocLexer::T_CLOSE_CURLY_BRACES)) {
break;
}
$values[] = $this->ArrayEntry();
}
$this->match(DocLexer::T_CLOSE_CURLY_BRACES);
foreach ($values as $value) {
[$key, $val] = $value;
if ($key !== null) {
$array[$key] = $val;
} else {
$array[] = $val;
}
}
return $array;
}
/**
@phpstan-return
*/
private function ArrayEntry(): array
{
$peek = $this->lexer->glimpse();
if (
$peek['type'] === DocLexer::T_EQUALS
|| $peek['type'] === DocLexer::T_COLON
) {
if ($this->lexer->isNextToken(DocLexer::T_IDENTIFIER)) {
$key = $this->Constant();
} else {
$this->matchAny([DocLexer::T_INTEGER, DocLexer::T_STRING]);
$key = $this->lexer->token['value'];
}
$this->matchAny([DocLexer::T_EQUALS, DocLexer::T_COLON]);
return [$key, $this->PlainValue()];
}
return [null, $this->Value()];
}
private function isIgnoredAnnotation(string $name): bool
{
if ($this->ignoreNotImportedAnnotations || isset($this->ignoredAnnotationNames[$name])) {
return true;
}
foreach (array_keys($this->ignoredAnnotationNamespaces) as $ignoredAnnotationNamespace) {
$ignoredAnnotationNamespace = rtrim($ignoredAnnotationNamespace, '\\') . '\\';
if (stripos(rtrim($name, '\\') . '\\', $ignoredAnnotationNamespace) === 0) {
return true;
}
}
return false;
}
private function resolvePositionalValues(array $arguments, string $name): array
{
$positionalArguments = $arguments['positional_arguments'] ?? [];
$values = $arguments['named_arguments'] ?? [];
if (
self::$annotationMetadata[$name]['has_named_argument_constructor']
&& self::$annotationMetadata[$name]['default_property'] !== null
) {
$positions = array_keys($positionalArguments);
$lastPosition = null;
foreach ($positions as $position) {
if (
($lastPosition === null && $position !== 0) ||
($lastPosition !== null && $position !== $lastPosition + 1)
) {
throw $this->syntaxError('Positional arguments after named arguments is not allowed');
}
$lastPosition = $position;
}
foreach (self::$annotationMetadata[$name]['constructor_args'] as $property => $parameter) {
$position = $parameter['position'];
if (isset($values[$property]) || ! isset($positionalArguments[$position])) {
continue;
}
$values[$property] = $positionalArguments[$position];
}
} else {
if (count($positionalArguments) > 0 && ! isset($values['value'])) {
if (count($positionalArguments) === 1) {
$value = array_pop($positionalArguments);
} else {
$value = array_values($positionalArguments);
}
$values['value'] = $value;
}
}
return $values;
}
private function instantiateAnnotiation(string $originalName, string $context, string $name, array $arguments)
{
try {
return new $name(...$arguments);
} catch (Throwable $exception) {
throw AnnotationException::creationError(
sprintf(
'An error occurred while instantiating the annotation @%s declared on %s: "%s".',
$originalName,
$context,
$exception->getMessage()
),
$exception
);
}
}
}
<?php
namespace Doctrine\Common\Annotations;
use Doctrine\Common\Cache\Cache;
use ReflectionClass;
use ReflectionMethod;
use ReflectionProperty;
use function array_map;
use function array_merge;
use function assert;
use function filemtime;
use function max;
use function time;
final class CachedReader implements Reader
{
private $delegate;
private $cache;
private $debug;
private $loadedAnnotations = [];
private $loadedFilemtimes = [];
public function __construct(Reader $reader, Cache $cache, $debug = false)
{
$this->delegate = $reader;
$this->cache = $cache;
$this->debug = (bool) $debug;
}
public function getClassAnnotations(ReflectionClass $class)
{
$cacheKey = $class->getName();
if (isset($this->loadedAnnotations[$cacheKey])) {
return $this->loadedAnnotations[$cacheKey];
}
$annots = $this->fetchFromCache($cacheKey, $class);
if ($annots === false) {
$annots = $this->delegate->getClassAnnotations($class);
$this->saveToCache($cacheKey, $annots);
}
return $this->loadedAnnotations[$cacheKey] = $annots;
}
public function getClassAnnotation(ReflectionClass $class, $annotationName)
{
foreach ($this->getClassAnnotations($class) as $annot) {
if ($annot instanceof $annotationName) {
return $annot;
}
}
return null;
}
public function getPropertyAnnotations(ReflectionProperty $property)
{
$class = $property->getDeclaringClass();
$cacheKey = $class->getName() . '$' . $property->getName();
if (isset($this->loadedAnnotations[$cacheKey])) {
return $this->loadedAnnotations[$cacheKey];
}
$annots = $this->fetchFromCache($cacheKey, $class);
if ($annots === false) {
$annots = $this->delegate->getPropertyAnnotations($property);
$this->saveToCache($cacheKey, $annots);
}
return $this->loadedAnnotations[$cacheKey] = $annots;
}
public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
{
foreach ($this->getPropertyAnnotations($property) as $annot) {
if ($annot instanceof $annotationName) {
return $annot;
}
}
return null;
}
public function getMethodAnnotations(ReflectionMethod $method)
{
$class = $method->getDeclaringClass();
$cacheKey = $class->getName() . '#' . $method->getName();
if (isset($this->loadedAnnotations[$cacheKey])) {
return $this->loadedAnnotations[$cacheKey];
}
$annots = $this->fetchFromCache($cacheKey, $class);
if ($annots === false) {
$annots = $this->delegate->getMethodAnnotations($method);
$this->saveToCache($cacheKey, $annots);
}
return $this->loadedAnnotations[$cacheKey] = $annots;
}
public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
{
foreach ($this->getMethodAnnotations($method) as $annot) {
if ($annot instanceof $annotationName) {
return $annot;
}
}
return null;
}
public function clearLoadedAnnotations()
{
$this->loadedAnnotations = [];
$this->loadedFilemtimes = [];
}
private function fetchFromCache($cacheKey, ReflectionClass $class)
{
$data = $this->cache->fetch($cacheKey);
if ($data !== false) {
if (! $this->debug || $this->isCacheFresh($cacheKey, $class)) {
return $data;
}
}
return false;
}
private function saveToCache($cacheKey, $value)
{
$this->cache->save($cacheKey, $value);
if (! $this->debug) {
return;
}
$this->cache->save('[C]' . $cacheKey, time());
}
private function isCacheFresh($cacheKey, ReflectionClass $class)
{
$lastModification = $this->getLastModification($class);
if ($lastModification === 0) {
return true;
}
return $this->cache->fetch('[C]' . $cacheKey) >= $lastModification;
}
private function getLastModification(ReflectionClass $class): int
{
$filename = $class->getFileName();
if (isset($this->loadedFilemtimes[$filename])) {
return $this->loadedFilemtimes[$filename];
}
$parent = $class->getParentClass();
$lastModification = max(array_merge(
[$filename ? filemtime($filename) : 0],
array_map(function (ReflectionClass $reflectionTrait): int {
return $this->getTraitLastModificationTime($reflectionTrait);
}, $class->getTraits()),
array_map(function (ReflectionClass $class): int {
return $this->getLastModification($class);
}, $class->getInterfaces()),
$parent ? [$this->getLastModification($parent)] : []
));
assert($lastModification !== false);
return $this->loadedFilemtimes[$filename] = $lastModification;
}
private function getTraitLastModificationTime(ReflectionClass $reflectionTrait): int
{
$fileName = $reflectionTrait->getFileName();
if (isset($this->loadedFilemtimes[$fileName])) {
return $this->loadedFilemtimes[$fileName];
}
$lastModificationTime = max(array_merge(
[$fileName ? filemtime($fileName) : 0],
array_map(function (ReflectionClass $reflectionTrait): int {
return $this->getTraitLastModificationTime($reflectionTrait);
}, $reflectionTrait->getTraits())
));
assert($lastModificationTime !== false);
return $this->loadedFilemtimes[$fileName] = $lastModificationTime;
}
}
<?php
namespace Doctrine\Common\Annotations;
use function array_merge;
use function count;
use function explode;
use function strtolower;
use function token_get_all;
use const PHP_VERSION_ID;
use const T_AS;
use const T_COMMENT;
use const T_DOC_COMMENT;
use const T_NAME_FULLY_QUALIFIED;
use const T_NAME_QUALIFIED;
use const T_NAMESPACE;
use const T_NS_SEPARATOR;
use const T_STRING;
use const T_USE;
use const T_WHITESPACE;
class TokenParser
{
/**
@phpstan-var
*/
private $tokens;
private $numTokens;
private $pointer = 0;
public function __construct($contents)
{
$this->tokens = token_get_all($contents);
token_get_all("<?php\n/**\n *\n */");
$this->numTokens = count($this->tokens);
}
public function next($docCommentIsComment = true)
{
for ($i = $this->pointer; $i < $this->numTokens; $i++) {
$this->pointer++;
if (
$this->tokens[$i][0] === T_WHITESPACE ||
$this->tokens[$i][0] === T_COMMENT ||
($docCommentIsComment && $this->tokens[$i][0] === T_DOC_COMMENT)
) {
continue;
}
return $this->tokens[$i];
}
return null;
}
public function parseUseStatement()
{
$groupRoot = '';
$class = '';
$alias = '';
$statements = [];
$explicitAlias = false;
while (($token = $this->next())) {
if (! $explicitAlias && $token[0] === T_STRING) {
$class .= $token[1];
$alias = $token[1];
} elseif ($explicitAlias && $token[0] === T_STRING) {
$alias = $token[1];
} elseif (
PHP_VERSION_ID >= 80000 &&
($token[0] === T_NAME_QUALIFIED || $token[0] === T_NAME_FULLY_QUALIFIED)
) {
$class .= $token[1];
$classSplit = explode('\\', $token[1]);
$alias = $classSplit[count($classSplit) - 1];
} elseif ($token[0] === T_NS_SEPARATOR) {
$class .= '\\';
$alias = '';
} elseif ($token[0] === T_AS) {
$explicitAlias = true;
$alias = '';
} elseif ($token === ',') {
$statements[strtolower($alias)] = $groupRoot . $class;
$class = '';
$alias = '';
$explicitAlias = false;
} elseif ($token === ';') {
$statements[strtolower($alias)] = $groupRoot . $class;
break;
} elseif ($token === '{') {
$groupRoot = $class;
$class = '';
} elseif ($token === '}') {
continue;
} else {
break;
}
}
return $statements;
}
public function parseUseStatements($namespaceName)
{
$statements = [];
while (($token = $this->next())) {
if ($token[0] === T_USE) {
$statements = array_merge($statements, $this->parseUseStatement());
continue;
}
if ($token[0] !== T_NAMESPACE || $this->parseNamespace() !== $namespaceName) {
continue;
}
$statements = [];
}
return $statements;
}
public function parseNamespace()
{
$name = '';
while (
($token = $this->next()) && ($token[0] === T_STRING || $token[0] === T_NS_SEPARATOR || (
PHP_VERSION_ID >= 80000 &&
($token[0] === T_NAME_QUALIFIED || $token[0] === T_NAME_FULLY_QUALIFIED)
))
) {
$name .= $token[1];
}
return $name;
}
public function parseClass()
{
return $this->parseNamespace();
}
}
<?php
declare(strict_types=1);
namespace Doctrine\Common\Annotations;
final class ImplicitlyIgnoredAnnotationNames
{
private const Reserved = [
'Annotation' => true,
'Attribute' => true,
'Attributes' => true,
'Required' => true,
'Target' => true,
'NamedArgumentConstructor' => true,
];
private const WidelyUsedNonStandard = [
'fix' => true,
'fixme' => true,
'override' => true,
];
private const PhpDocumentor1 = [
'abstract' => true,
'access' => true,
'code' => true,
'deprec' => true,
'endcode' => true,
'exception' => true,
'final' => true,
'ingroup' => true,
'inheritdoc' => true,
'inheritDoc' => true,
'magic' => true,
'name' => true,
'private' => true,
'static' => true,
'staticvar' => true,
'staticVar' => true,
'toc' => true,
'tutorial' => true,
'throw' => true,
];
private const PhpDocumentor2 = [
'api' => true,
'author' => true,
'category' => true,
'copyright' => true,
'deprecated' => true,
'example' => true,
'filesource' => true,
'global' => true,
'ignore' => true,
'internal' => true,
'license' => true,
'link' => true,
'method' => true,
'package' => true,
'param' => true,
'property' => true,
'property-read' => true,
'property-write' => true,
'return' => true,
'see' => true,
'since' => true,
'source' => true,
'subpackage' => true,
'throws' => true,
'todo' => true,
'TODO' => true,
'usedby' => true,
'uses' => true,
'var' => true,
'version' => true,
];
private const PHPUnit = [
'author' => true,
'after' => true,
'afterClass' => true,
'backupGlobals' => true,
'backupStaticAttributes' => true,
'before' => true,
'beforeClass' => true,
'codeCoverageIgnore' => true,
'codeCoverageIgnoreStart' => true,
'codeCoverageIgnoreEnd' => true,
'covers' => true,
'coversDefaultClass' => true,
'coversNothing' => true,
'dataProvider' => true,
'depends' => true,
'doesNotPerformAssertions' => true,
'expectedException' => true,
'expectedExceptionCode' => true,
'expectedExceptionMessage' => true,
'expectedExceptionMessageRegExp' => true,
'group' => true,
'large' => true,
'medium' => true,
'preserveGlobalState' => true,
'requires' => true,
'runTestsInSeparateProcesses' => true,
'runInSeparateProcess' => true,
'small' => true,
'test' => true,
'testdox' => true,
'testWith' => true,
'ticket' => true,
'uses' => true,
];
private const PhpCheckStyle = ['SuppressWarnings' => true];
private const PhpStorm = ['noinspection' => true];
private const PEAR = ['package_version' => true];
private const PlainUML = [
'startuml' => true,
'enduml' => true,
];
private const Symfony = ['experimental' => true];
private const PhpCodeSniffer = [
'codingStandardsIgnoreStart' => true,
'codingStandardsIgnoreEnd' => true,
];
private const SlevomatCodingStandard = ['phpcsSuppress' => true];
private const Phan = ['suppress' => true];
private const Rector = ['noRector' => true];
private const StaticAnalysis = [
'extends' => true,
'implements' => true,
'readonly' => true,
'template' => true,
'use' => true,
'pure' => true,
'immutable' => true,
];
public const LIST = self::Reserved
+ self::WidelyUsedNonStandard
+ self::PhpDocumentor1
+ self::PhpDocumentor2
+ self::PHPUnit
+ self::PhpCheckStyle
+ self::PhpStorm
+ self::PEAR
+ self::PlainUML
+ self::Symfony
+ self::SlevomatCodingStandard
+ self::PhpCodeSniffer
+ self::Phan
+ self::Rector
+ self::StaticAnalysis;
private function __construct()
{
}
}
<?php
namespace Doctrine\Common\Annotations;
use Doctrine\Common\Annotations\Annotation\IgnoreAnnotation;
use Doctrine\Common\Annotations\Annotation\Target;
use ReflectionClass;
use ReflectionFunction;
use ReflectionMethod;
use ReflectionProperty;
use function array_merge;
use function class_exists;
use function extension_loaded;
use function ini_get;
class AnnotationReader implements Reader
{
private static $globalImports = [
'ignoreannotation' => Annotation\IgnoreAnnotation::class,
];
private static $globalIgnoredNames = ImplicitlyIgnoredAnnotationNames::LIST;
private static $globalIgnoredNamespaces = [];
public static function addGlobalIgnoredName($name)
{
self::$globalIgnoredNames[$name] = true;
}
public static function addGlobalIgnoredNamespace($namespace)
{
self::$globalIgnoredNamespaces[$namespace] = true;
}
private $parser;
private $preParser;
private $phpParser;
/**
@psalm-var
*/
private $imports = [];
/**
@psalm-var
*/
private $ignoredAnnotationNames = [];
public function __construct(?DocParser $parser = null)
{
if (
extension_loaded('Zend Optimizer+') && (ini_get('zend_optimizerplus.save_comments') === '0' ||
ini_get('opcache.save_comments') === '0')
) {
throw AnnotationException::optimizerPlusSaveComments();
}
if (extension_loaded('Zend OPcache') && ini_get('opcache.save_comments') === 0) {
throw AnnotationException::optimizerPlusSaveComments();
}
class_exists(IgnoreAnnotation::class);
$this->parser = $parser ?: new DocParser();
$this->preParser = new DocParser();
$this->preParser->setImports(self::$globalImports);
$this->preParser->setIgnoreNotImportedAnnotations(true);
$this->preParser->setIgnoredAnnotationNames(self::$globalIgnoredNames);
$this->phpParser = new PhpParser();
}
public function getClassAnnotations(ReflectionClass $class)
{
$this->parser->setTarget(Target::TARGET_CLASS);
$this->parser->setImports($this->getImports($class));
$this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
$this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName());
}
public function getClassAnnotation(ReflectionClass $class, $annotationName)
{
$annotations = $this->getClassAnnotations($class);
foreach ($annotations as $annotation) {
if ($annotation instanceof $annotationName) {
return $annotation;
}
}
return null;
}
public function getPropertyAnnotations(ReflectionProperty $property)
{
$class = $property->getDeclaringClass();
$context = 'property ' . $class->getName() . '::$' . $property->getName();
$this->parser->setTarget(Target::TARGET_PROPERTY);
$this->parser->setImports($this->getPropertyImports($property));
$this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
$this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
return $this->parser->parse($property->getDocComment(), $context);
}
public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
{
$annotations = $this->getPropertyAnnotations($property);
foreach ($annotations as $annotation) {
if ($annotation instanceof $annotationName) {
return $annotation;
}
}
return null;
}
public function getMethodAnnotations(ReflectionMethod $method)
{
$class = $method->getDeclaringClass();
$context = 'method ' . $class->getName() . '::' . $method->getName() . '()';
$this->parser->setTarget(Target::TARGET_METHOD);
$this->parser->setImports($this->getMethodImports($method));
$this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
$this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
return $this->parser->parse($method->getDocComment(), $context);
}
public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
{
$annotations = $this->getMethodAnnotations($method);
foreach ($annotations as $annotation) {
if ($annotation instanceof $annotationName) {
return $annotation;
}
}
return null;
}
/**
@phpstan-return
*/
public function getFunctionAnnotations(ReflectionFunction $function): array
{
$context = 'function ' . $function->getName();
$this->parser->setTarget(Target::TARGET_FUNCTION);
$this->parser->setImports($this->getImports($function));
$this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($function));
$this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
return $this->parser->parse($function->getDocComment(), $context);
}
public function getFunctionAnnotation(ReflectionFunction $function, string $annotationName)
{
$annotations = $this->getFunctionAnnotations($function);
foreach ($annotations as $annotation) {
if ($annotation instanceof $annotationName) {
return $annotation;
}
}
return null;
}
private function getIgnoredAnnotationNames($reflection): array
{
$type = $reflection instanceof ReflectionClass ? 'class' : 'function';
$name = $reflection->getName();
if (isset($this->ignoredAnnotationNames[$type][$name])) {
return $this->ignoredAnnotationNames[$type][$name];
}
$this->collectParsingMetadata($reflection);
return $this->ignoredAnnotationNames[$type][$name];
}
private function getImports($reflection): array
{
$type = $reflection instanceof ReflectionClass ? 'class' : 'function';
$name = $reflection->getName();
if (isset($this->imports[$type][$name])) {
return $this->imports[$type][$name];
}
$this->collectParsingMetadata($reflection);
return $this->imports[$type][$name];
}
private function getMethodImports(ReflectionMethod $method)
{
$class = $method->getDeclaringClass();
$classImports = $this->getImports($class);
$traitImports = [];
foreach ($class->getTraits() as $trait) {
if (
! $trait->hasMethod($method->getName())
|| $trait->getFileName() !== $method->getFileName()
) {
continue;
}
$traitImports = array_merge($traitImports, $this->phpParser->parseUseStatements($trait));
}
return array_merge($classImports, $traitImports);
}
private function getPropertyImports(ReflectionProperty $property)
{
$class = $property->getDeclaringClass();
$classImports = $this->getImports($class);
$traitImports = [];
foreach ($class->getTraits() as $trait) {
if (! $trait->hasProperty($property->getName())) {
continue;
}
$traitImports = array_merge($traitImports, $this->phpParser->parseUseStatements($trait));
}
return array_merge($classImports, $traitImports);
}
private function collectParsingMetadata($reflection): void
{
$type = $reflection instanceof ReflectionClass ? 'class' : 'function';
$name = $reflection->getName();
$ignoredAnnotationNames = self::$globalIgnoredNames;
$annotations = $this->preParser->parse($reflection->getDocComment(), $type . ' ' . $name);
foreach ($annotations as $annotation) {
if (! ($annotation instanceof IgnoreAnnotation)) {
continue;
}
foreach ($annotation->names as $annot) {
$ignoredAnnotationNames[$annot] = true;
}
}
$this->imports[$type][$name] = array_merge(
self::$globalImports,
$this->phpParser->parseUseStatements($reflection),
[
'__NAMESPACE__' => $reflection->getNamespaceName(),
'self' => $name,
]
);
$this->ignoredAnnotationNames[$type][$name] = $ignoredAnnotationNames;
}
}
<?php
namespace Doctrine\Common\Annotations;
use function array_key_exists;
use function array_merge;
use function class_exists;
use function in_array;
use function is_file;
use function str_replace;
use function stream_resolve_include_path;
use function strpos;
use const DIRECTORY_SEPARATOR;
final class AnnotationRegistry
{
private static $autoloadNamespaces = [];
private static $loaders = [];
private static $failedToAutoload = [];
private static $registerFileUsed = false;
public static function reset(): void
{
self::$autoloadNamespaces = [];
self::$loaders = [];
self::$failedToAutoload = [];
self::$registerFileUsed = false;
}
public static function registerFile(string $file): void
{
self::$registerFileUsed = true;
require_once $file;
}
/**
@phpstan-param
*/
public static function registerAutoloadNamespace(string $namespace, $dirs = null): void
{
self::$autoloadNamespaces[$namespace] = $dirs;
}
public static function registerAutoloadNamespaces(array $namespaces): void
{
self::$autoloadNamespaces = array_merge(self::$autoloadNamespaces, $namespaces);
}
public static function registerLoader(callable $callable): void
{
self::$failedToAutoload = [];
self::$loaders[] = $callable;
}
public static function registerUniqueLoader(callable $callable): void
{
if (in_array($callable, self::$loaders, true)) {
return;
}
self::registerLoader($callable);
}
public static function loadAnnotationClass(string $class): bool
{
if (class_exists($class, false)) {
return true;
}
if (array_key_exists($class, self::$failedToAutoload)) {
return false;
}
foreach (self::$autoloadNamespaces as $namespace => $dirs) {
if (strpos($class, $namespace) !== 0) {
continue;
}
$file = str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
if ($dirs === null) {
$path = stream_resolve_include_path($file);
if ($path) {
require $path;
return true;
}
} else {
foreach ((array) $dirs as $dir) {
if (is_file($dir . DIRECTORY_SEPARATOR . $file)) {
require $dir . DIRECTORY_SEPARATOR . $file;
return true;
}
}
}
}
foreach (self::$loaders as $loader) {
if ($loader($class) === true) {
return true;
}
}
if (
self::$loaders === [] &&
self::$autoloadNamespaces === [] &&
self::$registerFileUsed === false &&
class_exists($class)
) {
return true;
}
self::$failedToAutoload[$class] = null;
return false;
}
}
<?php
namespace Doctrine\Common\Annotations;
use Doctrine\Common\Lexer\AbstractLexer;
use function ctype_alpha;
use function is_numeric;
use function str_replace;
use function stripos;
use function strlen;
use function strpos;
use function strtolower;
use function substr;
final class DocLexer extends AbstractLexer
{
public const T_NONE = 1;
public const T_INTEGER = 2;
public const T_STRING = 3;
public const T_FLOAT = 4;
public const T_IDENTIFIER = 100;
public const T_AT = 101;
public const T_CLOSE_CURLY_BRACES = 102;
public const T_CLOSE_PARENTHESIS = 103;
public const T_COMMA = 104;
public const T_EQUALS = 105;
public const T_FALSE = 106;
public const T_NAMESPACE_SEPARATOR = 107;
public const T_OPEN_CURLY_BRACES = 108;
public const T_OPEN_PARENTHESIS = 109;
public const T_TRUE = 110;
public const T_NULL = 111;
public const T_COLON = 112;
public const T_MINUS = 113;
protected $noCase = [
'@' => self::T_AT,
',' => self::T_COMMA,
'(' => self::T_OPEN_PARENTHESIS,
')' => self::T_CLOSE_PARENTHESIS,
'{' => self::T_OPEN_CURLY_BRACES,
'}' => self::T_CLOSE_CURLY_BRACES,
'=' => self::T_EQUALS,
':' => self::T_COLON,
'-' => self::T_MINUS,
'\\' => self::T_NAMESPACE_SEPARATOR,
];
protected $withCase = [
'true' => self::T_TRUE,
'false' => self::T_FALSE,
'null' => self::T_NULL,
];
public function nextTokenIsAdjacent(): bool
{
return $this->token === null
|| ($this->lookahead !== null
&& ($this->lookahead['position'] - $this->token['position']) === strlen($this->token['value']));
}
protected function getCatchablePatterns()
{
return [
'[a-z_\\\][a-z0-9_\:\\\]*[a-z_][a-z0-9_]*',
'(?:[+-]?[0-9]+(?:[\.][0-9]+)*)(?:[eE][+-]?[0-9]+)?',
'"(?:""|[^"])*+"',
];
}
protected function getNonCatchablePatterns()
{
return ['\s+', '\*+', '(.)'];
}
protected function getType(&$value)
{
$type = self::T_NONE;
if ($value[0] === '"') {
$value = str_replace('""', '"', substr($value, 1, strlen($value) - 2));
return self::T_STRING;
}
if (isset($this->noCase[$value])) {
return $this->noCase[$value];
}
if ($value[0] === '_' || $value[0] === '\\' || ctype_alpha($value[0])) {
return self::T_IDENTIFIER;
}
$lowerValue = strtolower($value);
if (isset($this->withCase[$lowerValue])) {
return $this->withCase[$lowerValue];
}
if (is_numeric($value)) {
return strpos($value, '.') !== false || stripos($value, 'e') !== false
? self::T_FLOAT : self::T_INTEGER;
}
return $type;
}
}
<?php
namespace Doctrine\Common\Annotations;
interface NamedArgumentConstructorAnnotation
{
}
<?php
namespace Psr\Cache;
interface CacheItemPoolInterface
{
public function getItem($key);
public function getItems(array $keys = array());
public function hasItem($key);
public function clear();
public function deleteItem($key);
public function deleteItems(array $keys);
public function save(CacheItemInterface $item);
public function saveDeferred(CacheItemInterface $item);
public function commit();
}
<?php
namespace Psr\Cache;
interface CacheItemInterface
{
public function getKey();
public function get();
public function isHit();
public function set($value);
public function expiresAt($expiration);
public function expiresAfter($time);
}
<?php
namespace Psr\Cache;
interface InvalidArgumentException extends CacheException
{
}
<?php
namespace Psr\Cache;
interface CacheException
{
}
<?php
declare(strict_types=1);
namespace Psr\EventDispatcher;
interface ListenerProviderInterface
{
public function getListenersForEvent(object $event) : iterable;
}
<?php
declare(strict_types=1);
namespace Psr\EventDispatcher;
interface EventDispatcherInterface
{
public function dispatch(object $event);
}
<?php
declare(strict_types=1);
namespace Psr\EventDispatcher;
interface StoppableEventInterface
{
public function isPropagationStopped() : bool;
}
<?php
namespace Psr\Container;
interface NotFoundExceptionInterface extends ContainerExceptionInterface
{
}
<?php
declare(strict_types=1);
namespace Psr\Container;
interface ContainerInterface
{
public function get(string $id);
public function has(string $id);
}
<?php
namespace Psr\Container;
use Throwable;
interface ContainerExceptionInterface extends Throwable
{
}
<?php
namespace Psr\Log;
interface LoggerAwareInterface
{
public function setLogger(LoggerInterface $logger);
}
<?php
namespace Psr\Log;
class LogLevel
{
const EMERGENCY = 'emergency';
const ALERT = 'alert';
const CRITICAL = 'critical';
const ERROR = 'error';
const WARNING = 'warning';
const NOTICE = 'notice';
const INFO = 'info';
const DEBUG = 'debug';
}
<?php
namespace Psr\Log;
trait LoggerAwareTrait
{
protected $logger;
public function setLogger(LoggerInterface $logger)
{
$this->logger = $logger;
}
}
<?php
namespace Psr\Log;
class InvalidArgumentException extends \InvalidArgumentException
{
}
<?php
namespace Psr\Log;
class NullLogger extends AbstractLogger
{
public function log($level, $message, array $context = array())
{
}
}
<?php
namespace Psr\Log;
interface LoggerInterface
{
public function emergency($message, array $context = array());
public function alert($message, array $context = array());
public function critical($message, array $context = array());
public function error($message, array $context = array());
public function warning($message, array $context = array());
public function notice($message, array $context = array());
public function info($message, array $context = array());
public function debug($message, array $context = array());
public function log($level, $message, array $context = array());
}
<?php
namespace Psr\Log;
trait LoggerTrait
{
public function emergency($message, array $context = array())
{
$this->log(LogLevel::EMERGENCY, $message, $context);
}
public function alert($message, array $context = array())
{
$this->log(LogLevel::ALERT, $message, $context);
}
public function critical($message, array $context = array())
{
$this->log(LogLevel::CRITICAL, $message, $context);
}
public function error($message, array $context = array())
{
$this->log(LogLevel::ERROR, $message, $context);
}
public function warning($message, array $context = array())
{
$this->log(LogLevel::WARNING, $message, $context);
}
public function notice($message, array $context = array())
{
$this->log(LogLevel::NOTICE, $message, $context);
}
public function info($message, array $context = array())
{
$this->log(LogLevel::INFO, $message, $context);
}
public function debug($message, array $context = array())
{
$this->log(LogLevel::DEBUG, $message, $context);
}
abstract public function log($level, $message, array $context = array());
}
<?php
namespace Psr\Log;
abstract class AbstractLogger implements LoggerInterface
{
public function emergency($message, array $context = array())
{
$this->log(LogLevel::EMERGENCY, $message, $context);
}
public function alert($message, array $context = array())
{
$this->log(LogLevel::ALERT, $message, $context);
}
public function critical($message, array $context = array())
{
$this->log(LogLevel::CRITICAL, $message, $context);
}
public function error($message, array $context = array())
{
$this->log(LogLevel::ERROR, $message, $context);
}
public function warning($message, array $context = array())
{
$this->log(LogLevel::WARNING, $message, $context);
}
public function notice($message, array $context = array())
{
$this->log(LogLevel::NOTICE, $message, $context);
}
public function info($message, array $context = array())
{
$this->log(LogLevel::INFO, $message, $context);
}
public function debug($message, array $context = array())
{
$this->log(LogLevel::DEBUG, $message, $context);
}
}
<?php
namespace Symfony\Polyfill\Php73;
final class Php73
{
public static $startAt = 1533462603;
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];
}
}
<?php
if (\PHP_VERSION_ID < 70300) {
class JsonException extends Exception
{
}
}
<?php
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)); }
}
<?php
namespace Symfony\Polyfill\Php80;
class PhpToken implements \Stringable
{
public $id;
public $text;
public $line;
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;
}
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;
}
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;
}
}
<?php
if (\PHP_VERSION_ID < 80000 && \extension_loaded('tokenizer')) {
class PhpToken extends Symfony\Polyfill\Php80\PhpToken
{
}
}
<?php
if (\PHP_VERSION_ID < 80000) {
class ValueError extends Error
{
}
}
<?php
#[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;
public $flags;
public function __construct(int $flags = self::TARGET_ALL)
{
$this->flags = $flags;
}
}
<?php
if (\PHP_VERSION_ID < 80000) {
class UnhandledMatchError extends Error
{
}
}
<?php
if (\PHP_VERSION_ID < 80000) {
interface Stringable
{
public function __toString();
}
}
<?php
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); }
}
<?php
namespace Symfony\Polyfill\Php80;
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);
}
}
<?php
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); }
}
<?php
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); }
}
<?php
namespace Symfony\Polyfill\Ctype;
final class Ctype
{
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);
}
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);
}
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);
}
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);
}
public static function ctype_graph($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^!-~]/', $text);
}
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);
}
public static function ctype_print($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^ -~]/', $text);
}
public static function ctype_punct($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^!-\/\:-@\[-`\{-~]/', $text);
}
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);
}
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);
}
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);
}
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);
}
}
<?php
if (\PHP_VERSION_ID < 80100) {
#[Attribute(Attribute::TARGET_METHOD)]
final class ReturnTypeWillChange
{
public function __construct()
{
}
}
}
<?php
use Symfony\Polyfill\Php81 as p;
if (\PHP_VERSION_ID >= 80100) {
return;
}
if (defined('MYSQLI_REFRESH_SLAVE') && !defined('MYSQLI_REFRESH_REPLICA')) {
define('MYSQLI_REFRESH_REPLICA', 64);
}
if (!function_exists('array_is_list')) {
function array_is_list(array $array): bool { return p\Php81::array_is_list($array); }
}
if (!function_exists('enum_exists')) {
function enum_exists(string $enum, bool $autoload = true): bool { return $autoload && class_exists($enum) && false; }
}
<?php
namespace Symfony\Polyfill\Php81;
final class Php81
{
public static function array_is_list(array $array): bool
{
if ([] === $array || $array === array_values($array)) {
return true;
}
$nextKey = -1;
foreach ($array as $k => $v) {
if ($k !== ++$nextKey) {
return false;
}
}
return true;
}
}
<?php
namespace Symfony\Component\EventDispatcher;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface as ContractsEventDispatcherInterface;
interface EventDispatcherInterface extends ContractsEventDispatcherInterface
{
public function addListener(string $eventName, callable $listener, int $priority = 0);
public function addSubscriber(EventSubscriberInterface $subscriber);
public function removeListener(string $eventName, callable $listener);
public function removeSubscriber(EventSubscriberInterface $subscriber);
public function getListeners(string $eventName = null);
public function getListenerPriority(string $eventName, callable $listener);
public function hasListeners(string $eventName = null);
}
<?php
namespace Symfony\Component\EventDispatcher\Attribute;
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class AsEventListener
{
public function __construct(
public ?string $event = null,
public ?string $method = null,
public int $priority = 0,
public ?string $dispatcher = null,
) {
}
}
<?php
namespace Symfony\Component\EventDispatcher;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
trigger_deprecation('symfony/event-dispatcher', '5.1', '%s is deprecated, use the event dispatcher without the proxy.', LegacyEventDispatcherProxy::class);
final class LegacyEventDispatcherProxy
{
public static function decorate(?EventDispatcherInterface $dispatcher): ?EventDispatcherInterface
{
return $dispatcher;
}
}
<?php
namespace Symfony\Component\EventDispatcher;
use Psr\EventDispatcher\StoppableEventInterface;
use Symfony\Component\EventDispatcher\Debug\WrappedListener;
class EventDispatcher implements EventDispatcherInterface
{
private $listeners = [];
private $sorted = [];
private $optimized;
public function __construct()
{
if (__CLASS__ === static::class) {
$this->optimized = [];
}
}
public function dispatch(object $event, string $eventName = null): object
{
$eventName = $eventName ?? \get_class($event);
if (null !== $this->optimized) {
$listeners = $this->optimized[$eventName] ?? (empty($this->listeners[$eventName]) ? [] : $this->optimizeListeners($eventName));
} else {
$listeners = $this->getListeners($eventName);
}
if ($listeners) {
$this->callListeners($listeners, $eventName, $event);
}
return $event;
}
public function getListeners(string $eventName = null)
{
if (null !== $eventName) {
if (empty($this->listeners[$eventName])) {
return [];
}
if (!isset($this->sorted[$eventName])) {
$this->sortListeners($eventName);
}
return $this->sorted[$eventName];
}
foreach ($this->listeners as $eventName => $eventListeners) {
if (!isset($this->sorted[$eventName])) {
$this->sortListeners($eventName);
}
}
return array_filter($this->sorted);
}
public function getListenerPriority(string $eventName, $listener)
{
if (empty($this->listeners[$eventName])) {
return null;
}
if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure && 2 >= \count($listener)) {
$listener[0] = $listener[0]();
$listener[1] = $listener[1] ?? '__invoke';
}
foreach ($this->listeners[$eventName] as $priority => &$listeners) {
foreach ($listeners as &$v) {
if ($v !== $listener && \is_array($v) && isset($v[0]) && $v[0] instanceof \Closure && 2 >= \count($v)) {
$v[0] = $v[0]();
$v[1] = $v[1] ?? '__invoke';
}
if ($v === $listener || ($listener instanceof \Closure && $v == $listener)) {
return $priority;
}
}
}
return null;
}
public function hasListeners(string $eventName = null)
{
if (null !== $eventName) {
return !empty($this->listeners[$eventName]);
}
foreach ($this->listeners as $eventListeners) {
if ($eventListeners) {
return true;
}
}
return false;
}
public function addListener(string $eventName, $listener, int $priority = 0)
{
$this->listeners[$eventName][$priority][] = $listener;
unset($this->sorted[$eventName], $this->optimized[$eventName]);
}
public function removeListener(string $eventName, $listener)
{
if (empty($this->listeners[$eventName])) {
return;
}
if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure && 2 >= \count($listener)) {
$listener[0] = $listener[0]();
$listener[1] = $listener[1] ?? '__invoke';
}
foreach ($this->listeners[$eventName] as $priority => &$listeners) {
foreach ($listeners as $k => &$v) {
if ($v !== $listener && \is_array($v) && isset($v[0]) && $v[0] instanceof \Closure && 2 >= \count($v)) {
$v[0] = $v[0]();
$v[1] = $v[1] ?? '__invoke';
}
if ($v === $listener || ($listener instanceof \Closure && $v == $listener)) {
unset($listeners[$k], $this->sorted[$eventName], $this->optimized[$eventName]);
}
}
if (!$listeners) {
unset($this->listeners[$eventName][$priority]);
}
}
}
public function addSubscriber(EventSubscriberInterface $subscriber)
{
foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
if (\is_string($params)) {
$this->addListener($eventName, [$subscriber, $params]);
} elseif (\is_string($params[0])) {
$this->addListener($eventName, [$subscriber, $params[0]], $params[1] ?? 0);
} else {
foreach ($params as $listener) {
$this->addListener($eventName, [$subscriber, $listener[0]], $listener[1] ?? 0);
}
}
}
}
public function removeSubscriber(EventSubscriberInterface $subscriber)
{
foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
if (\is_array($params) && \is_array($params[0])) {
foreach ($params as $listener) {
$this->removeListener($eventName, [$subscriber, $listener[0]]);
}
} else {
$this->removeListener($eventName, [$subscriber, \is_string($params) ? $params : $params[0]]);
}
}
}
protected function callListeners(iterable $listeners, string $eventName, object $event)
{
$stoppable = $event instanceof StoppableEventInterface;
foreach ($listeners as $listener) {
if ($stoppable && $event->isPropagationStopped()) {
break;
}
$listener($event, $eventName, $this);
}
}
private function sortListeners(string $eventName)
{
krsort($this->listeners[$eventName]);
$this->sorted[$eventName] = [];
foreach ($this->listeners[$eventName] as &$listeners) {
foreach ($listeners as $k => &$listener) {
if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure && 2 >= \count($listener)) {
$listener[0] = $listener[0]();
$listener[1] = $listener[1] ?? '__invoke';
}
$this->sorted[$eventName][] = $listener;
}
}
}
private function optimizeListeners(string $eventName): array
{
krsort($this->listeners[$eventName]);
$this->optimized[$eventName] = [];
foreach ($this->listeners[$eventName] as &$listeners) {
foreach ($listeners as &$listener) {
$closure = &$this->optimized[$eventName][];
if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure && 2 >= \count($listener)) {
$closure = static function (...$args) use (&$listener, &$closure) {
if ($listener[0] instanceof \Closure) {
$listener[0] = $listener[0]();
$listener[1] = $listener[1] ?? '__invoke';
}
($closure = \Closure::fromCallable($listener))(...$args);
};
} else {
$closure = $listener instanceof \Closure || $listener instanceof WrappedListener ? $listener : \Closure::fromCallable($listener);
}
}
}
return $this->optimized[$eventName];
}
}
<?php
namespace Symfony\Component\EventDispatcher;
interface EventSubscriberInterface
{
public static function getSubscribedEvents();
}
<?php
namespace Symfony\Component\EventDispatcher;
class ImmutableEventDispatcher implements EventDispatcherInterface
{
private $dispatcher;
public function __construct(EventDispatcherInterface $dispatcher)
{
$this->dispatcher = $dispatcher;
}
public function dispatch(object $event, string $eventName = null): object
{
return $this->dispatcher->dispatch($event, $eventName);
}
public function addListener(string $eventName, $listener, int $priority = 0)
{
throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
}
public function addSubscriber(EventSubscriberInterface $subscriber)
{
throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
}
public function removeListener(string $eventName, $listener)
{
throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
}
public function removeSubscriber(EventSubscriberInterface $subscriber)
{
throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
}
public function getListeners(string $eventName = null)
{
return $this->dispatcher->getListeners($eventName);
}
public function getListenerPriority(string $eventName, $listener)
{
return $this->dispatcher->getListenerPriority($eventName, $listener);
}
public function hasListeners(string $eventName = null)
{
return $this->dispatcher->hasListeners($eventName);
}
}
<?php
namespace Symfony\Component\EventDispatcher;
use Symfony\Contracts\EventDispatcher\Event;
/**
@implements
@implements
*/
class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate
{
protected $subject;
protected $arguments;
public function __construct($subject = null, array $arguments = [])
{
$this->subject = $subject;
$this->arguments = $arguments;
}
public function getSubject()
{
return $this->subject;
}
public function getArgument(string $key)
{
if ($this->hasArgument($key)) {
return $this->arguments[$key];
}
throw new \InvalidArgumentException(sprintf('Argument "%s" not found.', $key));
}
public function setArgument(string $key, $value)
{
$this->arguments[$key] = $value;
return $this;
}
public function getArguments()
{
return $this->arguments;
}
public function setArguments(array $args = [])
{
$this->arguments = $args;
return $this;
}
public function hasArgument(string $key)
{
return \array_key_exists($key, $this->arguments);
}
#[\ReturnTypeWillChange]
public function offsetGet($key)
{
return $this->getArgument($key);
}
#[\ReturnTypeWillChange]
public function offsetSet($key, $value)
{
$this->setArgument($key, $value);
}
#[\ReturnTypeWillChange]
public function offsetUnset($key)
{
if ($this->hasArgument($key)) {
unset($this->arguments[$key]);
}
}
#[\ReturnTypeWillChange]
public function offsetExists($key)
{
return $this->hasArgument($key);
}
#[\ReturnTypeWillChange]
public function getIterator()
{
return new \ArrayIterator($this->arguments);
}
}
<?php
namespace Symfony\Component\EventDispatcher\DependencyInjection;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Contracts\EventDispatcher\Event;
class RegisterListenersPass implements CompilerPassInterface
{
protected $dispatcherService;
protected $listenerTag;
protected $subscriberTag;
protected $eventAliasesParameter;
private $hotPathEvents = [];
private $hotPathTagName = 'container.hot_path';
private $noPreloadEvents = [];
private $noPreloadTagName = 'container.no_preload';
public function __construct(string $dispatcherService = 'event_dispatcher', string $listenerTag = 'kernel.event_listener', string $subscriberTag = 'kernel.event_subscriber', string $eventAliasesParameter = 'event_dispatcher.event_aliases')
{
if (0 < \func_num_args()) {
trigger_deprecation('symfony/event-dispatcher', '5.3', 'Configuring "%s" is deprecated.', __CLASS__);
}
$this->dispatcherService = $dispatcherService;
$this->listenerTag = $listenerTag;
$this->subscriberTag = $subscriberTag;
$this->eventAliasesParameter = $eventAliasesParameter;
}
public function setHotPathEvents(array $hotPathEvents)
{
$this->hotPathEvents = array_flip($hotPathEvents);
if (1 < \func_num_args()) {
trigger_deprecation('symfony/event-dispatcher', '5.4', 'Configuring "$tagName" in "%s" is deprecated.', __METHOD__);
$this->hotPathTagName = func_get_arg(1);
}
return $this;
}
public function setNoPreloadEvents(array $noPreloadEvents): self
{
$this->noPreloadEvents = array_flip($noPreloadEvents);
if (1 < \func_num_args()) {
trigger_deprecation('symfony/event-dispatcher', '5.4', 'Configuring "$tagName" in "%s" is deprecated.', __METHOD__);
$this->noPreloadTagName = func_get_arg(1);
}
return $this;
}
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition($this->dispatcherService) && !$container->hasAlias($this->dispatcherService)) {
return;
}
$aliases = [];
if ($container->hasParameter($this->eventAliasesParameter)) {
$aliases = $container->getParameter($this->eventAliasesParameter);
}
$globalDispatcherDefinition = $container->findDefinition($this->dispatcherService);
foreach ($container->findTaggedServiceIds($this->listenerTag, true) as $id => $events) {
$noPreload = 0;
foreach ($events as $event) {
$priority = $event['priority'] ?? 0;
if (!isset($event['event'])) {
if ($container->getDefinition($id)->hasTag($this->subscriberTag)) {
continue;
}
$event['method'] = $event['method'] ?? '__invoke';
$event['event'] = $this->getEventFromTypeDeclaration($container, $id, $event['method']);
}
$event['event'] = $aliases[$event['event']] ?? $event['event'];
if (!isset($event['method'])) {
$event['method'] = 'on'.preg_replace_callback([
'/(?<=\b|_)[a-z]/i',
'/[^a-z0-9]/i',
], function ($matches) { return strtoupper($matches[0]); }, $event['event']);
$event['method'] = preg_replace('/[^a-z0-9]/i', '', $event['method']);
if (null !== ($class = $container->getDefinition($id)->getClass()) && ($r = $container->getReflectionClass($class, false)) && !$r->hasMethod($event['method']) && $r->hasMethod('__invoke')) {
$event['method'] = '__invoke';
}
}
$dispatcherDefinition = $globalDispatcherDefinition;
if (isset($event['dispatcher'])) {
$dispatcherDefinition = $container->getDefinition($event['dispatcher']);
}
$dispatcherDefinition->addMethodCall('addListener', [$event['event'], [new ServiceClosureArgument(new Reference($id)), $event['method']], $priority]);
if (isset($this->hotPathEvents[$event['event']])) {
$container->getDefinition($id)->addTag($this->hotPathTagName);
} elseif (isset($this->noPreloadEvents[$event['event']])) {
++$noPreload;
}
}
if ($noPreload && \count($events) === $noPreload) {
$container->getDefinition($id)->addTag($this->noPreloadTagName);
}
}
$extractingDispatcher = new ExtractingEventDispatcher();
foreach ($container->findTaggedServiceIds($this->subscriberTag, true) as $id => $tags) {
$def = $container->getDefinition($id);
$class = $def->getClass();
if (!$r = $container->getReflectionClass($class)) {
throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
}
if (!$r->isSubclassOf(EventSubscriberInterface::class)) {
throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, EventSubscriberInterface::class));
}
$class = $r->name;
$dispatcherDefinitions = [];
foreach ($tags as $attributes) {
if (!isset($attributes['dispatcher']) || isset($dispatcherDefinitions[$attributes['dispatcher']])) {
continue;
}
$dispatcherDefinitions[$attributes['dispatcher']] = $container->getDefinition($attributes['dispatcher']);
}
if (!$dispatcherDefinitions) {
$dispatcherDefinitions = [$globalDispatcherDefinition];
}
$noPreload = 0;
ExtractingEventDispatcher::$aliases = $aliases;
ExtractingEventDispatcher::$subscriber = $class;
$extractingDispatcher->addSubscriber($extractingDispatcher);
foreach ($extractingDispatcher->listeners as $args) {
$args[1] = [new ServiceClosureArgument(new Reference($id)), $args[1]];
foreach ($dispatcherDefinitions as $dispatcherDefinition) {
$dispatcherDefinition->addMethodCall('addListener', $args);
}
if (isset($this->hotPathEvents[$args[0]])) {
$container->getDefinition($id)->addTag($this->hotPathTagName);
} elseif (isset($this->noPreloadEvents[$args[0]])) {
++$noPreload;
}
}
if ($noPreload && \count($extractingDispatcher->listeners) === $noPreload) {
$container->getDefinition($id)->addTag($this->noPreloadTagName);
}
$extractingDispatcher->listeners = [];
ExtractingEventDispatcher::$aliases = [];
}
}
private function getEventFromTypeDeclaration(ContainerBuilder $container, string $id, string $method): string
{
if (
null === ($class = $container->getDefinition($id)->getClass())
|| !($r = $container->getReflectionClass($class, false))
|| !$r->hasMethod($method)
|| 1 > ($m = $r->getMethod($method))->getNumberOfParameters()
|| !($type = $m->getParameters()[0]->getType()) instanceof \ReflectionNamedType
|| $type->isBuiltin()
|| Event::class === ($name = $type->getName())
) {
throw new InvalidArgumentException(sprintf('Service "%s" must define the "event" attribute on "%s" tags.', $id, $this->listenerTag));
}
return $name;
}
}
class ExtractingEventDispatcher extends EventDispatcher implements EventSubscriberInterface
{
public $listeners = [];
public static $aliases = [];
public static $subscriber;
public function addListener(string $eventName, $listener, int $priority = 0)
{
$this->listeners[] = [$eventName, $listener[1], $priority];
}
public static function getSubscribedEvents(): array
{
$events = [];
foreach ([self::$subscriber, 'getSubscribedEvents']() as $eventName => $params) {
$events[self::$aliases[$eventName] ?? $eventName] = $params;
}
return $events;
}
}
<?php
namespace Symfony\Component\EventDispatcher\DependencyInjection;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class AddEventAliasesPass implements CompilerPassInterface
{
private $eventAliases;
private $eventAliasesParameter;
public function __construct(array $eventAliases, string $eventAliasesParameter = 'event_dispatcher.event_aliases')
{
if (1 < \func_num_args()) {
trigger_deprecation('symfony/event-dispatcher', '5.3', 'Configuring "%s" is deprecated.', __CLASS__);
}
$this->eventAliases = $eventAliases;
$this->eventAliasesParameter = $eventAliasesParameter;
}
public function process(ContainerBuilder $container): void
{
$eventAliases = $container->hasParameter($this->eventAliasesParameter) ? $container->getParameter($this->eventAliasesParameter) : [];
$container->setParameter(
$this->eventAliasesParameter,
array_merge($eventAliases, $this->eventAliases)
);
}
}
<?php
namespace Symfony\Component\EventDispatcher\Debug;
use Psr\EventDispatcher\StoppableEventInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Contracts\Service\ResetInterface;
class TraceableEventDispatcher implements EventDispatcherInterface, ResetInterface
{
protected $logger;
protected $stopwatch;
private $callStack;
private $dispatcher;
private $wrappedListeners;
private $orphanedEvents;
private $requestStack;
private $currentRequestHash = '';
public function __construct(EventDispatcherInterface $dispatcher, Stopwatch $stopwatch, LoggerInterface $logger = null, RequestStack $requestStack = null)
{
$this->dispatcher = $dispatcher;
$this->stopwatch = $stopwatch;
$this->logger = $logger;
$this->wrappedListeners = [];
$this->orphanedEvents = [];
$this->requestStack = $requestStack;
}
public function addListener(string $eventName, $listener, int $priority = 0)
{
$this->dispatcher->addListener($eventName, $listener, $priority);
}
public function addSubscriber(EventSubscriberInterface $subscriber)
{
$this->dispatcher->addSubscriber($subscriber);
}
public function removeListener(string $eventName, $listener)
{
if (isset($this->wrappedListeners[$eventName])) {
foreach ($this->wrappedListeners[$eventName] as $index => $wrappedListener) {
if ($wrappedListener->getWrappedListener() === $listener || ($listener instanceof \Closure && $wrappedListener->getWrappedListener() == $listener)) {
$listener = $wrappedListener;
unset($this->wrappedListeners[$eventName][$index]);
break;
}
}
}
return $this->dispatcher->removeListener($eventName, $listener);
}
public function removeSubscriber(EventSubscriberInterface $subscriber)
{
return $this->dispatcher->removeSubscriber($subscriber);
}
public function getListeners(string $eventName = null)
{
return $this->dispatcher->getListeners($eventName);
}
public function getListenerPriority(string $eventName, $listener)
{
if (isset($this->wrappedListeners[$eventName])) {
foreach ($this->wrappedListeners[$eventName] as $wrappedListener) {
if ($wrappedListener->getWrappedListener() === $listener || ($listener instanceof \Closure && $wrappedListener->getWrappedListener() == $listener)) {
return $this->dispatcher->getListenerPriority($eventName, $wrappedListener);
}
}
}
return $this->dispatcher->getListenerPriority($eventName, $listener);
}
public function hasListeners(string $eventName = null)
{
return $this->dispatcher->hasListeners($eventName);
}
public function dispatch(object $event, string $eventName = null): object
{
$eventName = $eventName ?? \get_class($event);
if (null === $this->callStack) {
$this->callStack = new \SplObjectStorage();
}
$currentRequestHash = $this->currentRequestHash = $this->requestStack && ($request = $this->requestStack->getCurrentRequest()) ? spl_object_hash($request) : '';
if (null !== $this->logger && $event instanceof StoppableEventInterface && $event->isPropagationStopped()) {
$this->logger->debug(sprintf('The "%s" event is already stopped. No listeners have been called.', $eventName));
}
$this->preProcess($eventName);
try {
$this->beforeDispatch($eventName, $event);
try {
$e = $this->stopwatch->start($eventName, 'section');
try {
$this->dispatcher->dispatch($event, $eventName);
} finally {
if ($e->isStarted()) {
$e->stop();
}
}
} finally {
$this->afterDispatch($eventName, $event);
}
} finally {
$this->currentRequestHash = $currentRequestHash;
$this->postProcess($eventName);
}
return $event;
}
public function getCalledListeners(Request $request = null)
{
if (null === $this->callStack) {
return [];
}
$hash = $request ? spl_object_hash($request) : null;
$called = [];
foreach ($this->callStack as $listener) {
[$eventName, $requestHash] = $this->callStack->getInfo();
if (null === $hash || $hash === $requestHash) {
$called[] = $listener->getInfo($eventName);
}
}
return $called;
}
public function getNotCalledListeners(Request $request = null)
{
try {
$allListeners = $this->getListeners();
} catch (\Exception $e) {
if (null !== $this->logger) {
$this->logger->info('An exception was thrown while getting the uncalled listeners.', ['exception' => $e]);
}
return [];
}
$hash = $request ? spl_object_hash($request) : null;
$calledListeners = [];
if (null !== $this->callStack) {
foreach ($this->callStack as $calledListener) {
[, $requestHash] = $this->callStack->getInfo();
if (null === $hash || $hash === $requestHash) {
$calledListeners[] = $calledListener->getWrappedListener();
}
}
}
$notCalled = [];
foreach ($allListeners as $eventName => $listeners) {
foreach ($listeners as $listener) {
if (!\in_array($listener, $calledListeners, true)) {
if (!$listener instanceof WrappedListener) {
$listener = new WrappedListener($listener, null, $this->stopwatch, $this);
}
$notCalled[] = $listener->getInfo($eventName);
}
}
}
uasort($notCalled, [$this, 'sortNotCalledListeners']);
return $notCalled;
}
public function getOrphanedEvents(Request $request = null): array
{
if ($request) {
return $this->orphanedEvents[spl_object_hash($request)] ?? [];
}
if (!$this->orphanedEvents) {
return [];
}
return array_merge(...array_values($this->orphanedEvents));
}
public function reset()
{
$this->callStack = null;
$this->orphanedEvents = [];
$this->currentRequestHash = '';
}
public function __call(string $method, array $arguments)
{
return $this->dispatcher->{$method}(...$arguments);
}
protected function beforeDispatch(string $eventName, object $event)
{
}
protected function afterDispatch(string $eventName, object $event)
{
}
private function preProcess(string $eventName): void
{
if (!$this->dispatcher->hasListeners($eventName)) {
$this->orphanedEvents[$this->currentRequestHash][] = $eventName;
return;
}
foreach ($this->dispatcher->getListeners($eventName) as $listener) {
$priority = $this->getListenerPriority($eventName, $listener);
$wrappedListener = new WrappedListener($listener instanceof WrappedListener ? $listener->getWrappedListener() : $listener, null, $this->stopwatch, $this);
$this->wrappedListeners[$eventName][] = $wrappedListener;
$this->dispatcher->removeListener($eventName, $listener);
$this->dispatcher->addListener($eventName, $wrappedListener, $priority);
$this->callStack->attach($wrappedListener, [$eventName, $this->currentRequestHash]);
}
}
private function postProcess(string $eventName): void
{
unset($this->wrappedListeners[$eventName]);
$skipped = false;
foreach ($this->dispatcher->getListeners($eventName) as $listener) {
if (!$listener instanceof WrappedListener) {
continue;
}
$priority = $this->getListenerPriority($eventName, $listener);
$this->dispatcher->removeListener($eventName, $listener);
$this->dispatcher->addListener($eventName, $listener->getWrappedListener(), $priority);
if (null !== $this->logger) {
$context = ['event' => $eventName, 'listener' => $listener->getPretty()];
}
if ($listener->wasCalled()) {
if (null !== $this->logger) {
$this->logger->debug('Notified event "{event}" to listener "{listener}".', $context);
}
} else {
$this->callStack->detach($listener);
}
if (null !== $this->logger && $skipped) {
$this->logger->debug('Listener "{listener}" was not called for event "{event}".', $context);
}
if ($listener->stoppedPropagation()) {
if (null !== $this->logger) {
$this->logger->debug('Listener "{listener}" stopped propagation of the event "{event}".', $context);
}
$skipped = true;
}
}
}
private function sortNotCalledListeners(array $a, array $b)
{
if (0 !== $cmp = strcmp($a['event'], $b['event'])) {
return $cmp;
}
if (\is_int($a['priority']) && !\is_int($b['priority'])) {
return 1;
}
if (!\is_int($a['priority']) && \is_int($b['priority'])) {
return -1;
}
if ($a['priority'] === $b['priority']) {
return 0;
}
if ($a['priority'] > $b['priority']) {
return -1;
}
return 1;
}
}
<?php
namespace Symfony\Component\EventDispatcher\Debug;
use Psr\EventDispatcher\StoppableEventInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Component\VarDumper\Caster\ClassStub;
final class WrappedListener
{
private $listener;
private $optimizedListener;
private $name;
private $called;
private $stoppedPropagation;
private $stopwatch;
private $dispatcher;
private $pretty;
private $stub;
private $priority;
private static $hasClassStub;
public function __construct($listener, ?string $name, Stopwatch $stopwatch, EventDispatcherInterface $dispatcher = null)
{
$this->listener = $listener;
$this->optimizedListener = $listener instanceof \Closure ? $listener : (\is_callable($listener) ? \Closure::fromCallable($listener) : null);
$this->stopwatch = $stopwatch;
$this->dispatcher = $dispatcher;
$this->called = false;
$this->stoppedPropagation = false;
if (\is_array($listener)) {
$this->name = \is_object($listener[0]) ? get_debug_type($listener[0]) : $listener[0];
$this->pretty = $this->name.'::'.$listener[1];
} elseif ($listener instanceof \Closure) {
$r = new \ReflectionFunction($listener);
if (str_contains($r->name, '{closure}')) {
$this->pretty = $this->name = 'closure';
} elseif ($class = $r->getClosureScopeClass()) {
$this->name = $class->name;
$this->pretty = $this->name.'::'.$r->name;
} else {
$this->pretty = $this->name = $r->name;
}
} elseif (\is_string($listener)) {
$this->pretty = $this->name = $listener;
} else {
$this->name = get_debug_type($listener);
$this->pretty = $this->name.'::__invoke';
}
if (null !== $name) {
$this->name = $name;
}
if (null === self::$hasClassStub) {
self::$hasClassStub = class_exists(ClassStub::class);
}
}
public function getWrappedListener()
{
return $this->listener;
}
public function wasCalled(): bool
{
return $this->called;
}
public function stoppedPropagation(): bool
{
return $this->stoppedPropagation;
}
public function getPretty(): string
{
return $this->pretty;
}
public function getInfo(string $eventName): array
{
if (null === $this->stub) {
$this->stub = self::$hasClassStub ? new ClassStub($this->pretty.'()', $this->listener) : $this->pretty.'()';
}
return [
'event' => $eventName,
'priority' => null !== $this->priority ? $this->priority : (null !== $this->dispatcher ? $this->dispatcher->getListenerPriority($eventName, $this->listener) : null),
'pretty' => $this->pretty,
'stub' => $this->stub,
];
}
public function __invoke(object $event, string $eventName, EventDispatcherInterface $dispatcher): void
{
$dispatcher = $this->dispatcher ?: $dispatcher;
$this->called = true;
$this->priority = $dispatcher->getListenerPriority($eventName, $this->listener);
$e = $this->stopwatch->start($this->name, 'event_listener');
($this->optimizedListener ?? $this->listener)($event, $eventName, $dispatcher);
if ($e->isStarted()) {
$e->stop();
}
if ($event instanceof StoppableEventInterface && $event->isPropagationStopped()) {
$this->stoppedPropagation = true;
}
}
}
<?php
namespace Symfony\Component\Filesystem;
use Symfony\Component\Filesystem\Exception\FileNotFoundException;
use Symfony\Component\Filesystem\Exception\InvalidArgumentException;
use Symfony\Component\Filesystem\Exception\IOException;
class Filesystem
{
private static $lastError;
public function copy(string $originFile, string $targetFile, bool $overwriteNewerFiles = false)
{
$originIsLocal = stream_is_local($originFile) || 0 === stripos($originFile, 'file://');
if ($originIsLocal && !is_file($originFile)) {
throw new FileNotFoundException(sprintf('Failed to copy "%s" because file does not exist.', $originFile), 0, null, $originFile);
}
$this->mkdir(\dirname($targetFile));
$doCopy = true;
if (!$overwriteNewerFiles && null === parse_url($originFile, \PHP_URL_HOST) && is_file($targetFile)) {
$doCopy = filemtime($originFile) > filemtime($targetFile);
}
if ($doCopy) {
if (!$source = self::box('fopen', $originFile, 'r')) {
throw new IOException(sprintf('Failed to copy "%s" to "%s" because source file could not be opened for reading: ', $originFile, $targetFile).self::$lastError, 0, null, $originFile);
}
if (!$target = self::box('fopen', $targetFile, 'w', false, stream_context_create(['ftp' => ['overwrite' => true]]))) {
throw new IOException(sprintf('Failed to copy "%s" to "%s" because target file could not be opened for writing: ', $originFile, $targetFile).self::$lastError, 0, null, $originFile);
}
$bytesCopied = stream_copy_to_stream($source, $target);
fclose($source);
fclose($target);
unset($source, $target);
if (!is_file($targetFile)) {
throw new IOException(sprintf('Failed to copy "%s" to "%s".', $originFile, $targetFile), 0, null, $originFile);
}
if ($originIsLocal) {
self::box('chmod', $targetFile, fileperms($targetFile) | (fileperms($originFile) & 0111));
if ($bytesCopied !== $bytesOrigin = filesize($originFile)) {
throw new IOException(sprintf('Failed to copy the whole content of "%s" to "%s" (%g of %g bytes copied).', $originFile, $targetFile, $bytesCopied, $bytesOrigin), 0, null, $originFile);
}
}
}
}
public function mkdir($dirs, int $mode = 0777)
{
foreach ($this->toIterable($dirs) as $dir) {
if (is_dir($dir)) {
continue;
}
if (!self::box('mkdir', $dir, $mode, true) && !is_dir($dir)) {
throw new IOException(sprintf('Failed to create "%s": ', $dir).self::$lastError, 0, null, $dir);
}
}
}
public function exists($files)
{
$maxPathLength = \PHP_MAXPATHLEN - 2;
foreach ($this->toIterable($files) as $file) {
if (\strlen($file) > $maxPathLength) {
throw new IOException(sprintf('Could not check if file exist because path length exceeds %d characters.', $maxPathLength), 0, null, $file);
}
if (!file_exists($file)) {
return false;
}
}
return true;
}
public function touch($files, int $time = null, int $atime = null)
{
foreach ($this->toIterable($files) as $file) {
if (!($time ? self::box('touch', $file, $time, $atime) : self::box('touch', $file))) {
throw new IOException(sprintf('Failed to touch "%s": ', $file).self::$lastError, 0, null, $file);
}
}
}
public function remove($files)
{
if ($files instanceof \Traversable) {
$files = iterator_to_array($files, false);
} elseif (!\is_array($files)) {
$files = [$files];
}
self::doRemove($files, false);
}
private static function doRemove(array $files, bool $isRecursive): void
{
$files = array_reverse($files);
foreach ($files as $file) {
if (is_link($file)) {
if (!(self::box('unlink', $file) || '\\' !== \DIRECTORY_SEPARATOR || self::box('rmdir', $file)) && file_exists($file)) {
throw new IOException(sprintf('Failed to remove symlink "%s": ', $file).self::$lastError);
}
} elseif (is_dir($file)) {
if (!$isRecursive) {
$tmpName = \dirname(realpath($file)).'/.'.strrev(strtr(base64_encode(random_bytes(2)), '/=', '-.'));
if (file_exists($tmpName)) {
try {
self::doRemove([$tmpName], true);
} catch (IOException $e) {
}
}
if (!file_exists($tmpName) && self::box('rename', $file, $tmpName)) {
$origFile = $file;
$file = $tmpName;
} else {
$origFile = null;
}
}
$files = new \FilesystemIterator($file, \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS);
self::doRemove(iterator_to_array($files, true), true);
if (!self::box('rmdir', $file) && file_exists($file) && !$isRecursive) {
$lastError = self::$lastError;
if (null !== $origFile && self::box('rename', $file, $origFile)) {
$file = $origFile;
}
throw new IOException(sprintf('Failed to remove directory "%s": ', $file).$lastError);
}
} elseif (!self::box('unlink', $file) && (str_contains(self::$lastError, 'Permission denied') || file_exists($file))) {
throw new IOException(sprintf('Failed to remove file "%s": ', $file).self::$lastError);
}
}
}
public function chmod($files, int $mode, int $umask = 0000, bool $recursive = false)
{
foreach ($this->toIterable($files) as $file) {
if ((\PHP_VERSION_ID < 80000 || \is_int($mode)) && !self::box('chmod', $file, $mode & ~$umask)) {
throw new IOException(sprintf('Failed to chmod file "%s": ', $file).self::$lastError, 0, null, $file);
}
if ($recursive && is_dir($file) && !is_link($file)) {
$this->chmod(new \FilesystemIterator($file), $mode, $umask, true);
}
}
}
public function chown($files, $user, bool $recursive = false)
{
foreach ($this->toIterable($files) as $file) {
if ($recursive && is_dir($file) && !is_link($file)) {
$this->chown(new \FilesystemIterator($file), $user, true);
}
if (is_link($file) && \function_exists('lchown')) {
if (!self::box('lchown', $file, $user)) {
throw new IOException(sprintf('Failed to chown file "%s": ', $file).self::$lastError, 0, null, $file);
}
} else {
if (!self::box('chown', $file, $user)) {
throw new IOException(sprintf('Failed to chown file "%s": ', $file).self::$lastError, 0, null, $file);
}
}
}
}
public function chgrp($files, $group, bool $recursive = false)
{
foreach ($this->toIterable($files) as $file) {
if ($recursive && is_dir($file) && !is_link($file)) {
$this->chgrp(new \FilesystemIterator($file), $group, true);
}
if (is_link($file) && \function_exists('lchgrp')) {
if (!self::box('lchgrp', $file, $group)) {
throw new IOException(sprintf('Failed to chgrp file "%s": ', $file).self::$lastError, 0, null, $file);
}
} else {
if (!self::box('chgrp', $file, $group)) {
throw new IOException(sprintf('Failed to chgrp file "%s": ', $file).self::$lastError, 0, null, $file);
}
}
}
}
public function rename(string $origin, string $target, bool $overwrite = false)
{
if (!$overwrite && $this->isReadable($target)) {
throw new IOException(sprintf('Cannot rename because the target "%s" already exists.', $target), 0, null, $target);
}
if (!self::box('rename', $origin, $target)) {
if (is_dir($origin)) {
$this->mirror($origin, $target, null, ['override' => $overwrite, 'delete' => $overwrite]);
$this->remove($origin);
return;
}
throw new IOException(sprintf('Cannot rename "%s" to "%s": ', $origin, $target).self::$lastError, 0, null, $target);
}
}
private function isReadable(string $filename): bool
{
$maxPathLength = \PHP_MAXPATHLEN - 2;
if (\strlen($filename) > $maxPathLength) {
throw new IOException(sprintf('Could not check if file is readable because path length exceeds %d characters.', $maxPathLength), 0, null, $filename);
}
return is_readable($filename);
}
public function symlink(string $originDir, string $targetDir, bool $copyOnWindows = false)
{
self::assertFunctionExists('symlink');
if ('\\' === \DIRECTORY_SEPARATOR) {
$originDir = strtr($originDir, '/', '\\');
$targetDir = strtr($targetDir, '/', '\\');
if ($copyOnWindows) {
$this->mirror($originDir, $targetDir);
return;
}
}
$this->mkdir(\dirname($targetDir));
if (is_link($targetDir)) {
if (readlink($targetDir) === $originDir) {
return;
}
$this->remove($targetDir);
}
if (!self::box('symlink', $originDir, $targetDir)) {
$this->linkException($originDir, $targetDir, 'symbolic');
}
}
public function hardlink(string $originFile, $targetFiles)
{
self::assertFunctionExists('link');
if (!$this->exists($originFile)) {
throw new FileNotFoundException(null, 0, null, $originFile);
}
if (!is_file($originFile)) {
throw new FileNotFoundException(sprintf('Origin file "%s" is not a file.', $originFile));
}
foreach ($this->toIterable($targetFiles) as $targetFile) {
if (is_file($targetFile)) {
if (fileinode($originFile) === fileinode($targetFile)) {
continue;
}
$this->remove($targetFile);
}
if (!self::box('link', $originFile, $targetFile)) {
$this->linkException($originFile, $targetFile, 'hard');
}
}
}
private function linkException(string $origin, string $target, string $linkType)
{
if (self::$lastError) {
if ('\\' === \DIRECTORY_SEPARATOR && str_contains(self::$lastError, 'error code(1314)')) {
throw new IOException(sprintf('Unable to create "%s" link due to error code 1314: \'A required privilege is not held by the client\'. Do you have the required Administrator-rights?', $linkType), 0, null, $target);
}
}
throw new IOException(sprintf('Failed to create "%s" link from "%s" to "%s": ', $linkType, $origin, $target).self::$lastError, 0, null, $target);
}
public function readlink(string $path, bool $canonicalize = false)
{
if (!$canonicalize && !is_link($path)) {
return null;
}
if ($canonicalize) {
if (!$this->exists($path)) {
return null;
}
if ('\\' === \DIRECTORY_SEPARATOR && \PHP_VERSION_ID < 70410) {
$path = readlink($path);
}
return realpath($path);
}
if ('\\' === \DIRECTORY_SEPARATOR && \PHP_VERSION_ID < 70400) {
return realpath($path);
}
return readlink($path);
}
public function makePathRelative(string $endPath, string $startPath)
{
if (!$this->isAbsolutePath($startPath)) {
throw new InvalidArgumentException(sprintf('The start path "%s" is not absolute.', $startPath));
}
if (!$this->isAbsolutePath($endPath)) {
throw new InvalidArgumentException(sprintf('The end path "%s" is not absolute.', $endPath));
}
if ('\\' === \DIRECTORY_SEPARATOR) {
$endPath = str_replace('\\', '/', $endPath);
$startPath = str_replace('\\', '/', $startPath);
}
$splitDriveLetter = function ($path) {
return (\strlen($path) > 2 && ':' === $path[1] && '/' === $path[2] && ctype_alpha($path[0]))
? [substr($path, 2), strtoupper($path[0])]
: [$path, null];
};
$splitPath = function ($path) {
$result = [];
foreach (explode('/', trim($path, '/')) as $segment) {
if ('..' === $segment) {
array_pop($result);
} elseif ('.' !== $segment && '' !== $segment) {
$result[] = $segment;
}
}
return $result;
};
[$endPath, $endDriveLetter] = $splitDriveLetter($endPath);
[$startPath, $startDriveLetter] = $splitDriveLetter($startPath);
$startPathArr = $splitPath($startPath);
$endPathArr = $splitPath($endPath);
if ($endDriveLetter && $startDriveLetter && $endDriveLetter != $startDriveLetter) {
return $endDriveLetter.':/'.($endPathArr ? implode('/', $endPathArr).'/' : '');
}
$index = 0;
while (isset($startPathArr[$index]) && isset($endPathArr[$index]) && $startPathArr[$index] === $endPathArr[$index]) {
++$index;
}
if (1 === \count($startPathArr) && '' === $startPathArr[0]) {
$depth = 0;
} else {
$depth = \count($startPathArr) - $index;
}
$traverser = str_repeat('../', $depth);
$endPathRemainder = implode('/', \array_slice($endPathArr, $index));
$relativePath = $traverser.('' !== $endPathRemainder ? $endPathRemainder.'/' : '');
return '' === $relativePath ? './' : $relativePath;
}
public function mirror(string $originDir, string $targetDir, \Traversable $iterator = null, array $options = [])
{
$targetDir = rtrim($targetDir, '/\\');
$originDir = rtrim($originDir, '/\\');
$originDirLen = \strlen($originDir);
if (!$this->exists($originDir)) {
throw new IOException(sprintf('The origin directory specified "%s" was not found.', $originDir), 0, null, $originDir);
}
if ($this->exists($targetDir) && isset($options['delete']) && $options['delete']) {
$deleteIterator = $iterator;
if (null === $deleteIterator) {
$flags = \FilesystemIterator::SKIP_DOTS;
$deleteIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($targetDir, $flags), \RecursiveIteratorIterator::CHILD_FIRST);
}
$targetDirLen = \strlen($targetDir);
foreach ($deleteIterator as $file) {
$origin = $originDir.substr($file->getPathname(), $targetDirLen);
if (!$this->exists($origin)) {
$this->remove($file);
}
}
}
$copyOnWindows = $options['copy_on_windows'] ?? false;
if (null === $iterator) {
$flags = $copyOnWindows ? \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS : \FilesystemIterator::SKIP_DOTS;
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($originDir, $flags), \RecursiveIteratorIterator::SELF_FIRST);
}
$this->mkdir($targetDir);
$filesCreatedWhileMirroring = [];
foreach ($iterator as $file) {
if ($file->getPathname() === $targetDir || $file->getRealPath() === $targetDir || isset($filesCreatedWhileMirroring[$file->getRealPath()])) {
continue;
}
$target = $targetDir.substr($file->getPathname(), $originDirLen);
$filesCreatedWhileMirroring[$target] = true;
if (!$copyOnWindows && is_link($file)) {
$this->symlink($file->getLinkTarget(), $target);
} elseif (is_dir($file)) {
$this->mkdir($target);
} elseif (is_file($file)) {
$this->copy($file, $target, $options['override'] ?? false);
} else {
throw new IOException(sprintf('Unable to guess "%s" file type.', $file), 0, null, $file);
}
}
}
public function isAbsolutePath(string $file)
{
return '' !== $file && (strspn($file, '/\\', 0, 1)
|| (\strlen($file) > 3 && ctype_alpha($file[0])
&& ':' === $file[1]
&& strspn($file, '/\\', 2, 1)
)
|| null !== parse_url($file, \PHP_URL_SCHEME)
);
}
public function tempnam(string $dir, string $prefix)
{
$suffix = \func_num_args() > 2 ? func_get_arg(2) : '';
[$scheme, $hierarchy] = $this->getSchemeAndHierarchy($dir);
if ((null === $scheme || 'file' === $scheme || 'gs' === $scheme) && '' === $suffix) {
if ($tmpFile = self::box('tempnam', $hierarchy, $prefix)) {
if (null !== $scheme && 'gs' !== $scheme) {
return $scheme.'://'.$tmpFile;
}
return $tmpFile;
}
throw new IOException('A temporary file could not be created: '.self::$lastError);
}
for ($i = 0; $i < 10; ++$i) {
$tmpFile = $dir.'/'.$prefix.uniqid(mt_rand(), true).$suffix;
if (!$handle = self::box('fopen', $tmpFile, 'x+')) {
continue;
}
self::box('fclose', $handle);
return $tmpFile;
}
throw new IOException('A temporary file could not be created: '.self::$lastError);
}
public function dumpFile(string $filename, $content)
{
if (\is_array($content)) {
throw new \TypeError(sprintf('Argument 2 passed to "%s()" must be string or resource, array given.', __METHOD__));
}
$dir = \dirname($filename);
if (!is_dir($dir)) {
$this->mkdir($dir);
}
$tmpFile = $this->tempnam($dir, basename($filename));
try {
if (false === self::box('file_put_contents', $tmpFile, $content)) {
throw new IOException(sprintf('Failed to write file "%s": ', $filename).self::$lastError, 0, null, $filename);
}
self::box('chmod', $tmpFile, file_exists($filename) ? fileperms($filename) : 0666 & ~umask());
$this->rename($tmpFile, $filename, true);
} finally {
if (file_exists($tmpFile)) {
self::box('unlink', $tmpFile);
}
}
}
public function appendToFile(string $filename, $content)
{
if (\is_array($content)) {
throw new \TypeError(sprintf('Argument 2 passed to "%s()" must be string or resource, array given.', __METHOD__));
}
$dir = \dirname($filename);
if (!is_dir($dir)) {
$this->mkdir($dir);
}
$lock = \func_num_args() > 2 && func_get_arg(2);
if (false === self::box('file_put_contents', $filename, $content, \FILE_APPEND | ($lock ? \LOCK_EX : 0))) {
throw new IOException(sprintf('Failed to write file "%s": ', $filename).self::$lastError, 0, null, $filename);
}
}
private function toIterable($files): iterable
{
return is_iterable($files) ? $files : [$files];
}
private function getSchemeAndHierarchy(string $filename): array
{
$components = explode('://', $filename, 2);
return 2 === \count($components) ? [$components[0], $components[1]] : [null, $components[0]];
}
private static function assertFunctionExists(string $func): void
{
if (!\function_exists($func)) {
throw new IOException(sprintf('Unable to perform filesystem operation because the "%s()" function has been disabled.', $func));
}
}
private static function box(string $func, ...$args)
{
self::assertFunctionExists($func);
self::$lastError = null;
set_error_handler(__CLASS__.'::handleError');
try {
return $func(...$args);
} finally {
restore_error_handler();
}
}
public static function handleError(int $type, string $msg)
{
self::$lastError = $msg;
}
}
<?php
namespace Symfony\Component\Filesystem;
use Symfony\Component\Filesystem\Exception\InvalidArgumentException;
use Symfony\Component\Filesystem\Exception\RuntimeException;
final class Path
{
private const CLEANUP_THRESHOLD = 1250;
private const CLEANUP_SIZE = 1000;
private static $buffer = [];
private static $bufferSize = 0;
public static function canonicalize(string $path): string
{
if ('' === $path) {
return '';
}
if (isset(self::$buffer[$path])) {
return self::$buffer[$path];
}
if ('~' === $path[0]) {
$path = self::getHomeDirectory().substr($path, 1);
}
$path = self::normalize($path);
[$root, $pathWithoutRoot] = self::split($path);
$canonicalParts = self::findCanonicalParts($root, $pathWithoutRoot);
self::$buffer[$path] = $canonicalPath = $root.implode('/', $canonicalParts);
++self::$bufferSize;
if (self::$bufferSize > self::CLEANUP_THRESHOLD) {
self::$buffer = \array_slice(self::$buffer, -self::CLEANUP_SIZE, null, true);
self::$bufferSize = self::CLEANUP_SIZE;
}
return $canonicalPath;
}
public static function normalize(string $path): string
{
return str_replace('\\', '/', $path);
}
public static function getDirectory(string $path): string
{
if ('' === $path) {
return '';
}
$path = self::canonicalize($path);
if (false !== $schemeSeparatorPosition = strpos($path, '://')) {
$scheme = substr($path, 0, $schemeSeparatorPosition + 3);
$path = substr($path, $schemeSeparatorPosition + 3);
} else {
$scheme = '';
}
if (false === $dirSeparatorPosition = strrpos($path, '/')) {
return '';
}
if (0 === $dirSeparatorPosition) {
return $scheme.'/';
}
if (2 === $dirSeparatorPosition && ctype_alpha($path[0]) && ':' === $path[1]) {
return $scheme.substr($path, 0, 3);
}
return $scheme.substr($path, 0, $dirSeparatorPosition);
}
public static function getHomeDirectory(): string
{
if (getenv('HOME')) {
return self::canonicalize(getenv('HOME'));
}
if (getenv('HOMEDRIVE') && getenv('HOMEPATH')) {
return self::canonicalize(getenv('HOMEDRIVE').getenv('HOMEPATH'));
}
throw new RuntimeException("Cannot find the home directory path: Your environment or operating system isn't supported.");
}
public static function getRoot(string $path): string
{
if ('' === $path) {
return '';
}
if (false !== $schemeSeparatorPosition = strpos($path, '://')) {
$scheme = substr($path, 0, $schemeSeparatorPosition + 3);
$path = substr($path, $schemeSeparatorPosition + 3);
} else {
$scheme = '';
}
$firstCharacter = $path[0];
if ('/' === $firstCharacter || '\\' === $firstCharacter) {
return $scheme.'/';
}
$length = \strlen($path);
if ($length > 1 && ':' === $path[1] && ctype_alpha($firstCharacter)) {
if (2 === $length) {
return $scheme.$path.'/';
}
if ('/' === $path[2] || '\\' === $path[2]) {
return $scheme.$firstCharacter.$path[1].'/';
}
}
return '';
}
public static function getFilenameWithoutExtension(string $path, string $extension = null): string
{
if ('' === $path) {
return '';
}
if (null !== $extension) {
return rtrim(basename($path, $extension), '.');
}
return pathinfo($path, \PATHINFO_FILENAME);
}
public static function getExtension(string $path, bool $forceLowerCase = false): string
{
if ('' === $path) {
return '';
}
$extension = pathinfo($path, \PATHINFO_EXTENSION);
if ($forceLowerCase) {
$extension = self::toLower($extension);
}
return $extension;
}
public static function hasExtension(string $path, $extensions = null, bool $ignoreCase = false): bool
{
if ('' === $path) {
return false;
}
$actualExtension = self::getExtension($path, $ignoreCase);
if ([] === $extensions || null === $extensions) {
return '' !== $actualExtension;
}
if (\is_string($extensions)) {
$extensions = [$extensions];
}
foreach ($extensions as $key => $extension) {
if ($ignoreCase) {
$extension = self::toLower($extension);
}
$extensions[$key] = ltrim($extension, '.');
}
return \in_array($actualExtension, $extensions, true);
}
public static function changeExtension(string $path, string $extension): string
{
if ('' === $path) {
return '';
}
$actualExtension = self::getExtension($path);
$extension = ltrim($extension, '.');
if ('/' === substr($path, -1)) {
return $path;
}
if (empty($actualExtension)) {
return $path.('.' === substr($path, -1) ? '' : '.').$extension;
}
return substr($path, 0, -\strlen($actualExtension)).$extension;
}
public static function isAbsolute(string $path): bool
{
if ('' === $path) {
return false;
}
if (false !== $schemeSeparatorPosition = strpos($path, '://')) {
$path = substr($path, $schemeSeparatorPosition + 3);
}
$firstCharacter = $path[0];
if ('/' === $firstCharacter || '\\' === $firstCharacter) {
return true;
}
if (\strlen($path) > 1 && ctype_alpha($firstCharacter) && ':' === $path[1]) {
if (2 === \strlen($path)) {
return true;
}
if ('/' === $path[2] || '\\' === $path[2]) {
return true;
}
}
return false;
}
public static function isRelative(string $path): bool
{
return !self::isAbsolute($path);
}
public static function makeAbsolute(string $path, string $basePath): string
{
if ('' === $basePath) {
throw new InvalidArgumentException(sprintf('The base path must be a non-empty string. Got: "%s".', $basePath));
}
if (!self::isAbsolute($basePath)) {
throw new InvalidArgumentException(sprintf('The base path "%s" is not an absolute path.', $basePath));
}
if (self::isAbsolute($path)) {
return self::canonicalize($path);
}
if (false !== $schemeSeparatorPosition = strpos($basePath, '://')) {
$scheme = substr($basePath, 0, $schemeSeparatorPosition + 3);
$basePath = substr($basePath, $schemeSeparatorPosition + 3);
} else {
$scheme = '';
}
return $scheme.self::canonicalize(rtrim($basePath, '/\\').'/'.$path);
}
public static function makeRelative(string $path, string $basePath): string
{
$path = self::canonicalize($path);
$basePath = self::canonicalize($basePath);
[$root, $relativePath] = self::split($path);
[$baseRoot, $relativeBasePath] = self::split($basePath);
if ('' === $root && '' !== $baseRoot) {
if ('' === $relativeBasePath) {
$relativePath = ltrim($relativePath, './\\');
}
return $relativePath;
}
if ('' !== $root && '' === $baseRoot) {
throw new InvalidArgumentException(sprintf('The absolute path "%s" cannot be made relative to the relative path "%s". You should provide an absolute base path instead.', $path, $basePath));
}
if ($baseRoot && $root !== $baseRoot) {
throw new InvalidArgumentException(sprintf('The path "%s" cannot be made relative to "%s", because they have different roots ("%s" and "%s").', $path, $basePath, $root, $baseRoot));
}
if ('' === $relativeBasePath) {
return $relativePath;
}
$parts = explode('/', $relativePath);
$baseParts = explode('/', $relativeBasePath);
$dotDotPrefix = '';
$match = true;
foreach ($baseParts as $index => $basePart) {
if ($match && isset($parts[$index]) && $basePart === $parts[$index]) {
unset($parts[$index]);
continue;
}
$match = false;
$dotDotPrefix .= '../';
}
return rtrim($dotDotPrefix.implode('/', $parts), '/');
}
public static function isLocal(string $path): bool
{
return '' !== $path && false === strpos($path, '://');
}
public static function getLongestCommonBasePath(string ...$paths): ?string
{
[$bpRoot, $basePath] = self::split(self::canonicalize(reset($paths)));
for (next($paths); null !== key($paths) && '' !== $basePath; next($paths)) {
[$root, $path] = self::split(self::canonicalize(current($paths)));
if ($root !== $bpRoot) {
return null;
}
while (true) {
if ('.' === $basePath) {
$basePath = '';
continue 2;
}
if (0 === strpos($path.'/', $basePath.'/')) {
continue 2;
}
$basePath = \dirname($basePath);
}
}
return $bpRoot.$basePath;
}
public static function join(string ...$paths): string
{
$finalPath = null;
$wasScheme = false;
foreach ($paths as $path) {
if ('' === $path) {
continue;
}
if (null === $finalPath) {
$finalPath = $path;
$wasScheme = (false !== strpos($path, '://'));
continue;
}
if (!\in_array(substr($finalPath, -1), ['/', '\\'])) {
$finalPath .= '/';
}
$finalPath .= $wasScheme ? $path : ltrim($path, '/');
$wasScheme = false;
}
if (null === $finalPath) {
return '';
}
return self::canonicalize($finalPath);
}
public static function isBasePath(string $basePath, string $ofPath): bool
{
$basePath = self::canonicalize($basePath);
$ofPath = self::canonicalize($ofPath);
return 0 === strpos($ofPath.'/', rtrim($basePath, '/').'/');
}
private static function findCanonicalParts(string $root, string $pathWithoutRoot): array
{
$parts = explode('/', $pathWithoutRoot);
$canonicalParts = [];
foreach ($parts as $part) {
if ('.' === $part || '' === $part) {
continue;
}
if ('..' === $part && \count($canonicalParts) > 0 && '..' !== $canonicalParts[\count($canonicalParts) - 1]) {
array_pop($canonicalParts);
continue;
}
if ('..' !== $part || '' === $root) {
$canonicalParts[] = $part;
}
}
return $canonicalParts;
}
private static function split(string $path): array
{
if ('' === $path) {
return ['', ''];
}
if (false !== $schemeSeparatorPosition = strpos($path, '://')) {
$root = substr($path, 0, $schemeSeparatorPosition + 3);
$path = substr($path, $schemeSeparatorPosition + 3);
} else {
$root = '';
}
$length = \strlen($path);
if (0 === strpos($path, '/')) {
$root .= '/';
$path = $length > 1 ? substr($path, 1) : '';
} elseif ($length > 1 && ctype_alpha($path[0]) && ':' === $path[1]) {
if (2 === $length) {
$root .= $path.'/';
$path = '';
} elseif ('/' === $path[2]) {
$root .= substr($path, 0, 3);
$path = $length > 3 ? substr($path, 3) : '';
}
}
return [$root, $path];
}
private static function toLower(string $string): string
{
if (false !== $encoding = mb_detect_encoding($string, null, true)) {
return mb_strtolower($string, $encoding);
}
return strtolower($string);
}
private function __construct()
{
}
}
<?php
namespace Symfony\Component\Filesystem\Exception;
interface IOExceptionInterface extends ExceptionInterface
{
public function getPath();
}
<?php
namespace Symfony\Component\Filesystem\Exception;
interface ExceptionInterface extends \Throwable
{
}
<?php
namespace Symfony\Component\Filesystem\Exception;
class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}
<?php
namespace Symfony\Component\Filesystem\Exception;
class IOException extends \RuntimeException implements IOExceptionInterface
{
private $path;
public function __construct(string $message, int $code = 0, \Throwable $previous = null, string $path = null)
{
$this->path = $path;
parent::__construct($message, $code, $previous);
}
public function getPath()
{
return $this->path;
}
}
<?php
namespace Symfony\Component\Filesystem\Exception;
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
{
}
<?php
namespace Symfony\Component\Filesystem\Exception;
class FileNotFoundException extends IOException
{
public function __construct(string $message = null, int $code = 0, \Throwable $previous = null, string $path = null)
{
if (null === $message) {
if (null === $path) {
$message = 'File could not be found.';
} else {
$message = sprintf('File "%s" could not be found.', $path);
}
}
parent::__construct($message, $code, $previous, $path);
}
}
<?php
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);
}
public function allowedTypes(string ...$types): self
{
$this->resolver->setAllowedTypes($this->name, $types);
return $this;
}
public function allowedValues(...$values): self
{
$this->resolver->setAllowedValues($this->name, $values);
return $this;
}
public function default($value): self
{
$this->resolver->setDefault($this->name, $value);
return $this;
}
public function define(string $option): self
{
return $this->resolver->define($option);
}
public function deprecated(string $package, string $version, $message = 'The option "%name%" is deprecated.'): self
{
$this->resolver->setDeprecated($this->name, $package, $version, $message);
return $this;
}
public function normalize(\Closure $normalizer): self
{
$this->resolver->setNormalizer($this->name, $normalizer);
return $this;
}
public function required(): self
{
$this->resolver->setRequired($this->name);
return $this;
}
public function info(string $info): self
{
$this->resolver->setInfo($this->name, $info);
return $this;
}
}
<?php
namespace Symfony\Component\OptionsResolver;
use Symfony\Component\OptionsResolver\Exception\AccessException;
use Symfony\Component\OptionsResolver\Exception\InvalidArgumentException;
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
use Symfony\Component\OptionsResolver\Exception\MissingOptionsException;
use Symfony\Component\OptionsResolver\Exception\NoSuchOptionException;
use Symfony\Component\OptionsResolver\Exception\OptionDefinitionException;
use Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException;
class OptionsResolver implements Options
{
private const VALIDATION_FUNCTIONS = [
'bool' => 'is_bool',
'boolean' => 'is_bool',
'int' => 'is_int',
'integer' => 'is_int',
'long' => 'is_int',
'float' => 'is_float',
'double' => 'is_float',
'real' => 'is_float',
'numeric' => 'is_numeric',
'string' => 'is_string',
'scalar' => 'is_scalar',
'array' => 'is_array',
'iterable' => 'is_iterable',
'countable' => 'is_countable',
'callable' => 'is_callable',
'object' => 'is_object',
'resource' => 'is_resource',
];
private $defined = [];
private $defaults = [];
private $nested = [];
private $required = [];
private $resolved = [];
private $normalizers = [];
private $allowedValues = [];
private $allowedTypes = [];
private $info = [];
private $lazy = [];
private $calling = [];
private $deprecated = [];
private $given = [];
private $locked = false;
private $parentsOptions = [];
private $prototype;
private $prototypeIndex;
public function setDefault(string $option, $value)
{
if ($this->locked) {
throw new AccessException('Default values cannot be set from a lazy option or normalizer.');
}
if ($value instanceof \Closure) {
$reflClosure = new \ReflectionFunction($value);
$params = $reflClosure->getParameters();
if (isset($params[0]) && Options::class === $this->getParameterClassName($params[0])) {
if (!isset($this->defaults[$option])) {
$this->defaults[$option] = null;
}
if (!isset($this->lazy[$option]) || !isset($params[1])) {
$this->lazy[$option] = [];
}
$this->lazy[$option][] = $value;
$this->defined[$option] = true;
unset($this->resolved[$option], $this->nested[$option]);
return $this;
}
if (isset($params[0]) && null !== ($type = $params[0]->getType()) && self::class === $type->getName() && (!isset($params[1]) || (($type = $params[1]->getType()) instanceof \ReflectionNamedType && Options::class === $type->getName()))) {
$this->nested[$option][] = $value;
$this->defaults[$option] = [];
$this->defined[$option] = true;
unset($this->resolved[$option], $this->lazy[$option]);
return $this;
}
}
unset($this->lazy[$option], $this->nested[$option]);
if (!isset($this->defined[$option]) || \array_key_exists($option, $this->resolved)) {
$this->resolved[$option] = $value;
}
$this->defaults[$option] = $value;
$this->defined[$option] = true;
return $this;
}
public function setDefaults(array $defaults)
{
foreach ($defaults as $option => $value) {
$this->setDefault($option, $value);
}
return $this;
}
public function hasDefault(string $option)
{
return \array_key_exists($option, $this->defaults);
}
public function setRequired($optionNames)
{
if ($this->locked) {
throw new AccessException('Options cannot be made required from a lazy option or normalizer.');
}
foreach ((array) $optionNames as $option) {
$this->defined[$option] = true;
$this->required[$option] = true;
}
return $this;
}
public function isRequired(string $option)
{
return isset($this->required[$option]);
}
public function getRequiredOptions()
{
return array_keys($this->required);
}
public function isMissing(string $option)
{
return isset($this->required[$option]) && !\array_key_exists($option, $this->defaults);
}
public function getMissingOptions()
{
return array_keys(array_diff_key($this->required, $this->defaults));
}
public function setDefined($optionNames)
{
if ($this->locked) {
throw new AccessException('Options cannot be defined from a lazy option or normalizer.');
}
foreach ((array) $optionNames as $option) {
$this->defined[$option] = true;
}
return $this;
}
public function isDefined(string $option)
{
return isset($this->defined[$option]);
}
public function getDefinedOptions()
{
return array_keys($this->defined);
}
public function isNested(string $option): bool
{
return isset($this->nested[$option]);
}
public function setDeprecated(string $option): self
{
if ($this->locked) {
throw new AccessException('Options cannot be deprecated from a lazy option or normalizer.');
}
if (!isset($this->defined[$option])) {
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist, defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined))));
}
$args = \func_get_args();
if (\func_num_args() < 3) {
trigger_deprecation('symfony/options-resolver', '5.1', 'The signature of method "%s()" requires 2 new arguments: "string $package, string $version", not defining them is deprecated.', __METHOD__);
$message = $args[1] ?? 'The option "%name%" is deprecated.';
$package = $version = '';
} else {
$package = $args[1];
$version = $args[2];
$message = $args[3] ?? 'The option "%name%" is deprecated.';
}
if (!\is_string($message) && !$message instanceof \Closure) {
throw new InvalidArgumentException(sprintf('Invalid type for deprecation message argument, expected string or \Closure, but got "%s".', get_debug_type($message)));
}
if ('' === $message) {
return $this;
}
$this->deprecated[$option] = [
'package' => $package,
'version' => $version,
'message' => $message,
];
unset($this->resolved[$option]);
return $this;
}
public function isDeprecated(string $option): bool
{
return isset($this->deprecated[$option]);
}
public function setNormalizer(string $option, \Closure $normalizer)
{
if ($this->locked) {
throw new AccessException('Normalizers cannot be set from a lazy option or normalizer.');
}
if (!isset($this->defined[$option])) {
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined))));
}
$this->normalizers[$option] = [$normalizer];
unset($this->resolved[$option]);
return $this;
}
public function addNormalizer(string $option, \Closure $normalizer, bool $forcePrepend = false): self
{
if ($this->locked) {
throw new AccessException('Normalizers cannot be set from a lazy option or normalizer.');
}
if (!isset($this->defined[$option])) {
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined))));
}
if ($forcePrepend) {
$this->normalizers[$option] = $this->normalizers[$option] ?? [];
array_unshift($this->normalizers[$option], $normalizer);
} else {
$this->normalizers[$option][] = $normalizer;
}
unset($this->resolved[$option]);
return $this;
}
public function setAllowedValues(string $option, $allowedValues)
{
if ($this->locked) {
throw new AccessException('Allowed values cannot be set from a lazy option or normalizer.');
}
if (!isset($this->defined[$option])) {
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined))));
}
$this->allowedValues[$option] = \is_array($allowedValues) ? $allowedValues : [$allowedValues];
unset($this->resolved[$option]);
return $this;
}
public function addAllowedValues(string $option, $allowedValues)
{
if ($this->locked) {
throw new AccessException('Allowed values cannot be added from a lazy option or normalizer.');
}
if (!isset($this->defined[$option])) {
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined))));
}
if (!\is_array($allowedValues)) {
$allowedValues = [$allowedValues];
}
if (!isset($this->allowedValues[$option])) {
$this->allowedValues[$option] = $allowedValues;
} else {
$this->allowedValues[$option] = array_merge($this->allowedValues[$option], $allowedValues);
}
unset($this->resolved[$option]);
return $this;
}
public function setAllowedTypes(string $option, $allowedTypes)
{
if ($this->locked) {
throw new AccessException('Allowed types cannot be set from a lazy option or normalizer.');
}
if (!isset($this->defined[$option])) {
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined))));
}
$this->allowedTypes[$option] = (array) $allowedTypes;
unset($this->resolved[$option]);
return $this;
}
public function addAllowedTypes(string $option, $allowedTypes)
{
if ($this->locked) {
throw new AccessException('Allowed types cannot be added from a lazy option or normalizer.');
}
if (!isset($this->defined[$option])) {
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined))));
}
if (!isset($this->allowedTypes[$option])) {
$this->allowedTypes[$option] = (array) $allowedTypes;
} else {
$this->allowedTypes[$option] = array_merge($this->allowedTypes[$option], (array) $allowedTypes);
}
unset($this->resolved[$option]);
return $this;
}
public function define(string $option): OptionConfigurator
{
if (isset($this->defined[$option])) {
throw new OptionDefinitionException(sprintf('The option "%s" is already defined.', $option));
}
return new OptionConfigurator($option, $this);
}
public function setInfo(string $option, string $info): self
{
if ($this->locked) {
throw new AccessException('The Info message cannot be set from a lazy option or normalizer.');
}
if (!isset($this->defined[$option])) {
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined))));
}
$this->info[$option] = $info;
return $this;
}
public function getInfo(string $option): ?string
{
if (!isset($this->defined[$option])) {
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined))));
}
return $this->info[$option] ?? null;
}
public function setPrototype(bool $prototype): self
{
if ($this->locked) {
throw new AccessException('The prototype property cannot be set from a lazy option or normalizer.');
}
if (null === $this->prototype && $prototype) {
throw new AccessException('The prototype property cannot be set from a root definition.');
}
$this->prototype = $prototype;
return $this;
}
public function isPrototype(): bool
{
return $this->prototype ?? false;
}
public function remove($optionNames)
{
if ($this->locked) {
throw new AccessException('Options cannot be removed from a lazy option or normalizer.');
}
foreach ((array) $optionNames as $option) {
unset($this->defined[$option], $this->defaults[$option], $this->required[$option], $this->resolved[$option]);
unset($this->lazy[$option], $this->normalizers[$option], $this->allowedTypes[$option], $this->allowedValues[$option], $this->info[$option]);
}
return $this;
}
public function clear()
{
if ($this->locked) {
throw new AccessException('Options cannot be cleared from a lazy option or normalizer.');
}
$this->defined = [];
$this->defaults = [];
$this->nested = [];
$this->required = [];
$this->resolved = [];
$this->lazy = [];
$this->normalizers = [];
$this->allowedTypes = [];
$this->allowedValues = [];
$this->deprecated = [];
$this->info = [];
return $this;
}
public function resolve(array $options = [])
{
if ($this->locked) {
throw new AccessException('Options cannot be resolved from a lazy option or normalizer.');
}
$clone = clone $this;
$diff = array_diff_key($options, $clone->defined);
if (\count($diff) > 0) {
ksort($clone->defined);
ksort($diff);
throw new UndefinedOptionsException(sprintf((\count($diff) > 1 ? 'The options "%s" do not exist.' : 'The option "%s" does not exist.').' Defined options are: "%s".', $this->formatOptions(array_keys($diff)), implode('", "', array_keys($clone->defined))));
}
foreach ($options as $option => $value) {
$clone->given[$option] = true;
$clone->defaults[$option] = $value;
unset($clone->resolved[$option], $clone->lazy[$option]);
}
$diff = array_diff_key($clone->required, $clone->defaults);
if (\count($diff) > 0) {
ksort($diff);
throw new MissingOptionsException(sprintf(\count($diff) > 1 ? 'The required options "%s" are missing.' : 'The required option "%s" is missing.', $this->formatOptions(array_keys($diff))));
}
$clone->locked = true;
foreach ($clone->defaults as $option => $_) {
$clone->offsetGet($option);
}
return $clone->resolved;
}
#[\ReturnTypeWillChange]
public function offsetGet($option, bool $triggerDeprecation = true)
{
if (!$this->locked) {
throw new AccessException('Array access is only supported within closures of lazy options and normalizers.');
}
if (isset($this->resolved[$option]) || \array_key_exists($option, $this->resolved)) {
if ($triggerDeprecation && isset($this->deprecated[$option]) && (isset($this->given[$option]) || $this->calling) && \is_string($this->deprecated[$option]['message'])) {
trigger_deprecation($this->deprecated[$option]['package'], $this->deprecated[$option]['version'], strtr($this->deprecated[$option]['message'], ['%name%' => $option]));
}
return $this->resolved[$option];
}
if (!isset($this->defaults[$option]) && !\array_key_exists($option, $this->defaults)) {
if (!isset($this->defined[$option])) {
throw new NoSuchOptionException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined))));
}
throw new NoSuchOptionException(sprintf('The optional option "%s" has no value set. You should make sure it is set with "isset" before reading it.', $this->formatOptions([$option])));
}
$value = $this->defaults[$option];
if (isset($this->nested[$option])) {
if (isset($this->calling[$option])) {
throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', $this->formatOptions(array_keys($this->calling))));
}
if (!\is_array($value)) {
throw new InvalidOptionsException(sprintf('The nested option "%s" with value %s is expected to be of type array, but is of type "%s".', $this->formatOptions([$option]), $this->formatValue($value), get_debug_type($value)));
}
$this->calling[$option] = true;
try {
$resolver = new self();
$resolver->prototype = false;
$resolver->parentsOptions = $this->parentsOptions;
$resolver->parentsOptions[] = $option;
foreach ($this->nested[$option] as $closure) {
$closure($resolver, $this);
}
if ($resolver->prototype) {
$values = [];
foreach ($value as $index => $prototypeValue) {
if (!\is_array($prototypeValue)) {
throw new InvalidOptionsException(sprintf('The value of the option "%s" is expected to be of type array of array, but is of type array of "%s".', $this->formatOptions([$option]), get_debug_type($prototypeValue)));
}
$resolver->prototypeIndex = $index;
$values[$index] = $resolver->resolve($prototypeValue);
}
$value = $values;
} else {
$value = $resolver->resolve($value);
}
} finally {
$resolver->prototypeIndex = null;
unset($this->calling[$option]);
}
}
if (isset($this->lazy[$option])) {
if (isset($this->calling[$option])) {
throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', $this->formatOptions(array_keys($this->calling))));
}
$this->calling[$option] = true;
try {
foreach ($this->lazy[$option] as $closure) {
$value = $closure($this, $value);
}
} finally {
unset($this->calling[$option]);
}
}
if (isset($this->allowedTypes[$option])) {
$valid = true;
$invalidTypes = [];
foreach ($this->allowedTypes[$option] as $type) {
if ($valid = $this->verifyTypes($type, $value, $invalidTypes)) {
break;
}
}
if (!$valid) {
$fmtActualValue = $this->formatValue($value);
$fmtAllowedTypes = implode('" or "', $this->allowedTypes[$option]);
$fmtProvidedTypes = implode('|', array_keys($invalidTypes));
$allowedContainsArrayType = \count(array_filter($this->allowedTypes[$option], static function ($item) {
return str_ends_with($item, '[]');
})) > 0;
if (\is_array($value) && $allowedContainsArrayType) {
throw new InvalidOptionsException(sprintf('The option "%s" with value %s is expected to be of type "%s", but one of the elements is of type "%s".', $this->formatOptions([$option]), $fmtActualValue, $fmtAllowedTypes, $fmtProvidedTypes));
}
throw new InvalidOptionsException(sprintf('The option "%s" with value %s is expected to be of type "%s", but is of type "%s".', $this->formatOptions([$option]), $fmtActualValue, $fmtAllowedTypes, $fmtProvidedTypes));
}
}
if (isset($this->allowedValues[$option])) {
$success = false;
$printableAllowedValues = [];
foreach ($this->allowedValues[$option] as $allowedValue) {
if ($allowedValue instanceof \Closure) {
if ($allowedValue($value)) {
$success = true;
break;
}
continue;
}
if ($value === $allowedValue) {
$success = true;
break;
}
$printableAllowedValues[] = $allowedValue;
}
if (!$success) {
$message = sprintf(
'The option "%s" with value %s is invalid.',
$option,
$this->formatValue($value)
);
if (\count($printableAllowedValues) > 0) {
$message .= sprintf(
' Accepted values are: %s.',
$this->formatValues($printableAllowedValues)
);
}
if (isset($this->info[$option])) {
$message .= sprintf(' Info: %s.', $this->info[$option]);
}
throw new InvalidOptionsException($message);
}
}
if ($triggerDeprecation && isset($this->deprecated[$option]) && (isset($this->given[$option]) || ($this->calling && \is_string($this->deprecated[$option]['message'])))) {
$deprecation = $this->deprecated[$option];
$message = $this->deprecated[$option]['message'];
if ($message instanceof \Closure) {
if (isset($this->calling[$option])) {
throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', $this->formatOptions(array_keys($this->calling))));
}
$this->calling[$option] = true;
try {
if (!\is_string($message = $message($this, $value))) {
throw new InvalidOptionsException(sprintf('Invalid type for deprecation message, expected string but got "%s", return an empty string to ignore.', get_debug_type($message)));
}
} finally {
unset($this->calling[$option]);
}
}
if ('' !== $message) {
trigger_deprecation($deprecation['package'], $deprecation['version'], strtr($message, ['%name%' => $option]));
}
}
if (isset($this->normalizers[$option])) {
if (isset($this->calling[$option])) {
throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', $this->formatOptions(array_keys($this->calling))));
}
$this->calling[$option] = true;
try {
foreach ($this->normalizers[$option] as $normalizer) {
$value = $normalizer($this, $value);
}
} finally {
unset($this->calling[$option]);
}
}
$this->resolved[$option] = $value;
return $value;
}
private function verifyTypes(string $type, $value, array &$invalidTypes, int $level = 0): bool
{
if (\is_array($value) && '[]' === substr($type, -2)) {
$type = substr($type, 0, -2);
$valid = true;
foreach ($value as $val) {
if (!$this->verifyTypes($type, $val, $invalidTypes, $level + 1)) {
$valid = false;
}
}
return $valid;
}
if (('null' === $type && null === $value) || (isset(self::VALIDATION_FUNCTIONS[$type]) ? self::VALIDATION_FUNCTIONS[$type]($value) : $value instanceof $type)) {
return true;
}
if (!$invalidTypes || $level > 0) {
$invalidTypes[get_debug_type($value)] = true;
}
return false;
}
#[\ReturnTypeWillChange]
public function offsetExists($option)
{
if (!$this->locked) {
throw new AccessException('Array access is only supported within closures of lazy options and normalizers.');
}
return \array_key_exists($option, $this->defaults);
}
#[\ReturnTypeWillChange]
public function offsetSet($option, $value)
{
throw new AccessException('Setting options via array access is not supported. Use setDefault() instead.');
}
#[\ReturnTypeWillChange]
public function offsetUnset($option)
{
throw new AccessException('Removing options via array access is not supported. Use remove() instead.');
}
#[\ReturnTypeWillChange]
public function count()
{
if (!$this->locked) {
throw new AccessException('Counting is only supported within closures of lazy options and normalizers.');
}
return \count($this->defaults);
}
private function formatValue($value): string
{
if (\is_object($value)) {
return \get_class($value);
}
if (\is_array($value)) {
return 'array';
}
if (\is_string($value)) {
return '"'.$value.'"';
}
if (\is_resource($value)) {
return 'resource';
}
if (null === $value) {
return 'null';
}
if (false === $value) {
return 'false';
}
if (true === $value) {
return 'true';
}
return (string) $value;
}
private function formatValues(array $values): string
{
foreach ($values as $key => $value) {
$values[$key] = $this->formatValue($value);
}
return implode(', ', $values);
}
private function formatOptions(array $options): string
{
if ($this->parentsOptions) {
$prefix = array_shift($this->parentsOptions);
if ($this->parentsOptions) {
$prefix .= sprintf('[%s]', implode('][', $this->parentsOptions));
}
if ($this->prototype && null !== $this->prototypeIndex) {
$prefix .= sprintf('[%s]', $this->prototypeIndex);
}
$options = array_map(static function (string $option) use ($prefix): string {
return sprintf('%s[%s]', $prefix, $option);
}, $options);
}
return implode('", "', $options);
}
private function getParameterClassName(\ReflectionParameter $parameter): ?string
{
if (!($type = $parameter->getType()) instanceof \ReflectionNamedType || $type->isBuiltin()) {
return null;
}
return $type->getName();
}
}
<?php
namespace Symfony\Component\OptionsResolver;
interface Options extends \ArrayAccess, \Countable
{
}
<?php
namespace Symfony\Component\OptionsResolver\Exception;
class InvalidOptionsException extends InvalidArgumentException
{
}
<?php
namespace Symfony\Component\OptionsResolver\Exception;
class MissingOptionsException extends InvalidArgumentException
{
}
<?php
namespace Symfony\Component\OptionsResolver\Exception;
class AccessException extends \LogicException implements ExceptionInterface
{
}
<?php
namespace Symfony\Component\OptionsResolver\Exception;
class OptionDefinitionException extends \LogicException implements ExceptionInterface
{
}
<?php
namespace Symfony\Component\OptionsResolver\Exception;
interface ExceptionInterface extends \Throwable
{
}
<?php
namespace Symfony\Component\OptionsResolver\Exception;
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
{
}
<?php
namespace Symfony\Component\OptionsResolver\Exception;
class NoSuchOptionException extends \OutOfBoundsException implements ExceptionInterface
{
}
<?php
namespace Symfony\Component\OptionsResolver\Exception;
use Symfony\Component\OptionsResolver\Debug\OptionsResolverIntrospector;
class NoConfigurationException extends \RuntimeException implements ExceptionInterface
{
}
<?php
namespace Symfony\Component\OptionsResolver\Exception;
class UndefinedOptionsException extends InvalidArgumentException
{
}
<?php
namespace Symfony\Component\OptionsResolver\Debug;
use Symfony\Component\OptionsResolver\Exception\NoConfigurationException;
use Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException;
use Symfony\Component\OptionsResolver\OptionsResolver;
class OptionsResolverIntrospector
{
private $get;
public function __construct(OptionsResolver $optionsResolver)
{
$this->get = \Closure::bind(function ($property, $option, $message) {
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);
}
public function getDefault(string $option)
{
return ($this->get)('defaults', $option, sprintf('No default value was set for the "%s" option.', $option));
}
public function getLazyClosures(string $option): array
{
return ($this->get)('lazy', $option, sprintf('No lazy closures were set for the "%s" option.', $option));
}
public function getAllowedTypes(string $option): array
{
return ($this->get)('allowedTypes', $option, sprintf('No allowed types were set for the "%s" option.', $option));
}
public function getAllowedValues(string $option): array
{
return ($this->get)('allowedValues', $option, sprintf('No allowed values were set for the "%s" option.', $option));
}
public function getNormalizer(string $option): \Closure
{
return current($this->getNormalizers($option));
}
public function getNormalizers(string $option): array
{
return ($this->get)('normalizers', $option, sprintf('No normalizer was set for the "%s" option.', $option));
}
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'];
}
public function getDeprecation(string $option): array
{
return ($this->get)('deprecated', $option, sprintf('No deprecation was set for the "%s" option.', $option));
}
}
<?php
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); }
}
<?php
namespace Symfony\Polyfill\Intl\Normalizer;
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 & (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") {
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) {
$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 {
$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") {
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) {
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) {
$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])) {
if (!isset($c[$combClass[$uchr]])) {
$c[$combClass[$uchr]] = '';
}
$c[$combClass[$uchr]] .= $uchr;
continue;
}
} else {
$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;
}
}
<?php
class Normalizer extends Symfony\Polyfill\Intl\Normalizer\Normalizer
{
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;
}
<?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,
);
<?php
return array (
'À' => 'À',
'Á' => 'Á',
'Â' => 'Â',
'Ã' => 'Ã',
'Ä' => 'Ä',
'Å' => 'Å',
'Ç' => 'Ç',
'È' => 'È',
'É' => 'É',
'Ê' => 'Ê',
'Ë' => 'Ë',
'Ì' => 'Ì',
'Í' => 'Í',
'Î' => 'Î',
'Ï' => 'Ï',
'Ñ' => 'Ñ',
'Ò' => 'Ò',
'Ó' => 'Ó',
'Ô' => 'Ô',
'Õ' => 'Õ',
'Ö' => 'Ö',
'Ù' => 'Ù',
'Ú' => 'Ú',
'Û' => 'Û',
'Ü' => 'Ü',
'Ý' => 'Ý',
'à' => 'à',
'á' => 'á',
'â' => 'â',
'ã' => 'ã',
'ä' => 'ä',
'å' => 'å',
'ç' => 'ç',
'è' => 'è',
'é' => 'é',
'ê' => 'ê',
'ë' => 'ë',
'ì' => 'ì',
'í' => 'í',
'î' => 'î',
'ï' => 'ï',
'ñ' => 'ñ',
'ò' => 'ò',
'ó' => 'ó',
'ô' => 'ô',
'õ' => 'õ',
'ö' => 'ö',
'ù' => 'ù',
'ú' => 'ú',
'û' => 'û',
'ü' => 'ü',
'ý' => 'ý',
'ÿ' => 'ÿ',
'Ā' => 'Ā',
'ā' => 'ā',
'Ă' => 'Ă',
'ă' => 'ă',
'Ą' => 'Ą',
'ą' => 'ą',
'Ć' => 'Ć',
'ć' => 'ć',
'Ĉ' => 'Ĉ',
'ĉ' => 'ĉ',
'Ċ' => 'Ċ',
'ċ' => 'ċ',
'Č' => 'Č',
'č' => 'č',
'Ď' => 'Ď',
'ď' => 'ď',
'Ē' => 'Ē',
'ē' => 'ē',
'Ĕ' => 'Ĕ',
'ĕ' => 'ĕ',
'Ė' => 'Ė',
'ė' => 'ė',
'Ę' => 'Ę',
'ę' => 'ę',
'Ě' => 'Ě',
'ě' => 'ě',
'Ĝ' => 'Ĝ',
'ĝ' => 'ĝ',
'Ğ' => 'Ğ',
'ğ' => 'ğ',
'Ġ' => 'Ġ',
'ġ' => 'ġ',
'Ģ' => 'Ģ',
'ģ' => 'ģ',
'Ĥ' => 'Ĥ',
'ĥ' => 'ĥ',
'Ĩ' => 'Ĩ',
'ĩ' => 'ĩ',
'Ī' => 'Ī',
'ī' => 'ī',
'Ĭ' => 'Ĭ',
'ĭ' => 'ĭ',
'Į' => 'Į',
'į' => 'į',
'İ' => 'İ',
'Ĵ' => 'Ĵ',
'ĵ' => 'ĵ',
'Ķ' => 'Ķ',
'ķ' => 'ķ',
'Ĺ' => 'Ĺ',
'ĺ' => 'ĺ',
'Ļ' => 'Ļ',
'ļ' => 'ļ',
'Ľ' => 'Ľ',
'ľ' => 'ľ',
'Ń' => 'Ń',
'ń' => 'ń',
'Ņ' => 'Ņ',
'ņ' => 'ņ',
'Ň' => 'Ň',
'ň' => 'ň',
'Ō' => 'Ō',
'ō' => 'ō',
'Ŏ' => 'Ŏ',
'ŏ' => 'ŏ',
'Ő' => 'Ő',
'ő' => 'ő',
'Ŕ' => 'Ŕ',
'ŕ' => 'ŕ',
'Ŗ' => 'Ŗ',
'ŗ' => 'ŗ',
'Ř' => 'Ř',
'ř' => 'ř',
'Ś' => 'Ś',
'ś' => 'ś',
'Ŝ' => 'Ŝ',
'ŝ' => 'ŝ',
'Ş' => 'Ş',
'ş' => 'ş',
'Š' => 'Š',
'š' => 'š',
'Ţ' => 'Ţ',
'ţ' => 'ţ',
'Ť' => 'Ť',
'ť' => 'ť',
'Ũ' => 'Ũ',
'ũ' => 'ũ',
'Ū' => 'Ū',
'ū' => 'ū',
'Ŭ' => 'Ŭ',
'ŭ' => 'ŭ',
'Ů' => 'Ů',
'ů' => 'ů',
'Ű' => 'Ű',
'ű' => 'ű',
'Ų' => 'Ų',
'ų' => 'ų',
'Ŵ' => 'Ŵ',
'ŵ' => 'ŵ',
'Ŷ' => 'Ŷ',
'ŷ' => 'ŷ',
'Ÿ' => 'Ÿ',
'Ź' => 'Ź',
'ź' => 'ź',
'Ż' => 'Ż',
'ż' => 'ż',
'Ž' => 'Ž',
'ž' => 'ž',
'Ơ' => 'Ơ',
'ơ' => 'ơ',
'Ư' => 'Ư',
'ư' => 'ư',
'Ǎ' => 'Ǎ',
'ǎ' => 'ǎ',
'Ǐ' => 'Ǐ',
'ǐ' => 'ǐ',
'Ǒ' => 'Ǒ',
'ǒ' => 'ǒ',
'Ǔ' => 'Ǔ',
'ǔ' => 'ǔ',
'Ǖ' => 'Ǖ',
'ǖ' => 'ǖ',
'Ǘ' => 'Ǘ',
'ǘ' => 'ǘ',
'Ǚ' => 'Ǚ',
'ǚ' => 'ǚ',
'Ǜ' => 'Ǜ',
'ǜ' => 'ǜ',
'Ǟ' => 'Ǟ',
'ǟ' => 'ǟ',
'Ǡ' => 'Ǡ',
'ǡ' => 'ǡ',
'Ǣ' => 'Ǣ',
'ǣ' => 'ǣ',
'Ǧ' => 'Ǧ',
'ǧ' => 'ǧ',
'Ǩ' => 'Ǩ',
'ǩ' => 'ǩ',
'Ǫ' => 'Ǫ',
'ǫ' => 'ǫ',
'Ǭ' => 'Ǭ',
'ǭ' => 'ǭ',
'Ǯ' => 'Ǯ',
'ǯ' => 'ǯ',
'ǰ' => 'ǰ',
'Ǵ' => 'Ǵ',
'ǵ' => 'ǵ',
'Ǹ' => 'Ǹ',
'ǹ' => 'ǹ',
'Ǻ' => 'Ǻ',
'ǻ' => 'ǻ',
'Ǽ' => 'Ǽ',
'ǽ' => 'ǽ',
'Ǿ' => 'Ǿ',
'ǿ' => 'ǿ',
'Ȁ' => 'Ȁ',
'ȁ' => 'ȁ',
'Ȃ' => 'Ȃ',
'ȃ' => 'ȃ',
'Ȅ' => 'Ȅ',
'ȅ' => 'ȅ',
'Ȇ' => 'Ȇ',
'ȇ' => 'ȇ',
'Ȉ' => 'Ȉ',
'ȉ' => 'ȉ',
'Ȋ' => 'Ȋ',
'ȋ' => 'ȋ',
'Ȍ' => 'Ȍ',
'ȍ' => 'ȍ',
'Ȏ' => 'Ȏ',
'ȏ' => 'ȏ',
'Ȑ' => 'Ȑ',
'ȑ' => 'ȑ',
'Ȓ' => 'Ȓ',
'ȓ' => 'ȓ',
'Ȕ' => 'Ȕ',
'ȕ' => 'ȕ',
'Ȗ' => 'Ȗ',
'ȗ' => 'ȗ',
'Ș' => 'Ș',
'ș' => 'ș',
'Ț' => 'Ț',
'ț' => 'ț',
'Ȟ' => 'Ȟ',
'ȟ' => 'ȟ',
'Ȧ' => 'Ȧ',
'ȧ' => 'ȧ',
'Ȩ' => 'Ȩ',
'ȩ' => 'ȩ',
'Ȫ' => 'Ȫ',
'ȫ' => 'ȫ',
'Ȭ' => 'Ȭ',
'ȭ' => 'ȭ',
'Ȯ' => 'Ȯ',
'ȯ' => 'ȯ',
'Ȱ' => 'Ȱ',
'ȱ' => 'ȱ',
'Ȳ' => 'Ȳ',
'ȳ' => 'ȳ',
'̀' => '̀',
'́' => '́',
'̓' => '̓',
'̈́' => '̈́',
'ʹ' => 'ʹ',
';' => ';',
'΅' => '΅',
'Ά' => 'Ά',
'·' => '·',
'Έ' => 'Έ',
'Ή' => 'Ή',
'Ί' => 'Ί',
'Ό' => 'Ό',
'Ύ' => 'Ύ',
'Ώ' => 'Ώ',
'ΐ' => 'ΐ',
'Ϊ' => 'Ϊ',
'Ϋ' => 'Ϋ',
'ά' => 'ά',
'έ' => 'έ',
'ή' => 'ή',
'ί' => 'ί',
'ΰ' => 'ΰ',
'ϊ' => 'ϊ',
'ϋ' => 'ϋ',
'ό' => 'ό',
'ύ' => 'ύ',
'ώ' => 'ώ',
'ϓ' => 'ϓ',
'ϔ' => 'ϔ',
'Ѐ' => 'Ѐ',
'Ё' => 'Ё',
'Ѓ' => 'Ѓ',
'Ї' => 'Ї',
'Ќ' => 'Ќ',
'Ѝ' => 'Ѝ',
'Ў' => 'Ў',
'Й' => 'Й',
'й' => 'й',
'ѐ' => 'ѐ',
'ё' => 'ё',
'ѓ' => 'ѓ',
'ї' => 'ї',
'ќ' => 'ќ',
'ѝ' => 'ѝ',
'ў' => 'ў',
'Ѷ' => 'Ѷ',
'ѷ' => 'ѷ',
'Ӂ' => 'Ӂ',
'ӂ' => 'ӂ',
'Ӑ' => 'Ӑ',
'ӑ' => 'ӑ',
'Ӓ' => 'Ӓ',
'ӓ' => 'ӓ',
'Ӗ' => 'Ӗ',
'ӗ' => 'ӗ',
'Ӛ' => 'Ӛ',
'ӛ' => 'ӛ',
'Ӝ' => 'Ӝ',
'ӝ' => 'ӝ',
'Ӟ' => 'Ӟ',
'ӟ' => 'ӟ',
'Ӣ' => 'Ӣ',
'ӣ' => 'ӣ',
'Ӥ' => 'Ӥ',
'ӥ' => 'ӥ',
'Ӧ' => 'Ӧ',
'ӧ' => 'ӧ',
'Ӫ' => 'Ӫ',
'ӫ' => 'ӫ',
'Ӭ' => 'Ӭ',
'ӭ' => 'ӭ',
'Ӯ' => 'Ӯ',
'ӯ' => 'ӯ',
'Ӱ' => 'Ӱ',
'ӱ' => 'ӱ',
'Ӳ' => 'Ӳ',
'ӳ' => 'ӳ',
'Ӵ' => 'Ӵ',
'ӵ' => 'ӵ',
'Ӹ' => 'Ӹ',
'ӹ' => 'ӹ',
'آ' => 'آ',
'أ' => 'أ',
'ؤ' => 'ؤ',
'إ' => 'إ',
'ئ' => 'ئ',
'ۀ' => 'ۀ',
'ۂ' => 'ۂ',
'ۓ' => 'ۓ',
'ऩ' => 'ऩ',
'ऱ' => 'ऱ',
'ऴ' => 'ऴ',
'क़' => 'क़',
'ख़' => 'ख़',
'ग़' => 'ग़',
'ज़' => 'ज़',
'ड़' => 'ड़',
'ढ़' => 'ढ़',
'फ़' => 'फ़',
'य़' => 'य़',
'ো' => 'ো',
'ৌ' => 'ৌ',
'ড়' => 'ড়',
'ঢ়' => 'ঢ়',
'য়' => 'য়',
'ਲ਼' => 'ਲ਼',
'ਸ਼' => 'ਸ਼',
'ਖ਼' => 'ਖ਼',
'ਗ਼' => 'ਗ਼',
'ਜ਼' => 'ਜ਼',
'ਫ਼' => 'ਫ਼',
'ୈ' => 'ୈ',
'ୋ' => 'ୋ',
'ୌ' => 'ୌ',
'ଡ଼' => 'ଡ଼',
'ଢ଼' => 'ଢ଼',
'ஔ' => 'ஔ',
'ொ' => 'ொ',
'ோ' => 'ோ',
'ௌ' => 'ௌ',
'ై' => 'ై',
'ೀ' => 'ೀ',
'ೇ' => 'ೇ',
'ೈ' => 'ೈ',
'ೊ' => 'ೊ',
'ೋ' => 'ೋ',
'ൊ' => 'ൊ',
'ോ' => 'ോ',
'ൌ' => 'ൌ',
'ේ' => 'ේ',
'ො' => 'ො',
'ෝ' => 'ෝ',
'ෞ' => 'ෞ',
'གྷ' => 'གྷ',
'ཌྷ' => 'ཌྷ',
'དྷ' => 'དྷ',
'བྷ' => 'བྷ',
'ཛྷ' => 'ཛྷ',
'ཀྵ' => 'ཀྵ',
'ཱི' => 'ཱི',
'ཱུ' => 'ཱུ',
'ྲྀ' => 'ྲྀ',
'ླྀ' => 'ླྀ',
'ཱྀ' => 'ཱྀ',
'ྒྷ' => 'ྒྷ',
'ྜྷ' => 'ྜྷ',
'ྡྷ' => 'ྡྷ',
'ྦྷ' => 'ྦྷ',
'ྫྷ' => 'ྫྷ',
'ྐྵ' => 'ྐྵ',
'ဦ' => 'ဦ',
'ᬆ' => 'ᬆ',
'ᬈ' => 'ᬈ',
'ᬊ' => 'ᬊ',
'ᬌ' => 'ᬌ',
'ᬎ' => 'ᬎ',
'ᬒ' => 'ᬒ',
'ᬻ' => 'ᬻ',
'ᬽ' => 'ᬽ',
'ᭀ' => 'ᭀ',
'ᭁ' => 'ᭁ',
'ᭃ' => 'ᭃ',
'Ḁ' => 'Ḁ',
'ḁ' => 'ḁ',
'Ḃ' => 'Ḃ',
'ḃ' => 'ḃ',
'Ḅ' => 'Ḅ',
'ḅ' => 'ḅ',
'Ḇ' => 'Ḇ',
'ḇ' => 'ḇ',
'Ḉ' => 'Ḉ',
'ḉ' => 'ḉ',
'Ḋ' => 'Ḋ',
'ḋ' => 'ḋ',
'Ḍ' => 'Ḍ',
'ḍ' => 'ḍ',
'Ḏ' => 'Ḏ',
'ḏ' => 'ḏ',
'Ḑ' => 'Ḑ',
'ḑ' => 'ḑ',
'Ḓ' => 'Ḓ',
'ḓ' => 'ḓ',
'Ḕ' => 'Ḕ',
'ḕ' => 'ḕ',
'Ḗ' => 'Ḗ',
'ḗ' => 'ḗ',
'Ḙ' => 'Ḙ',
'ḙ' => 'ḙ',
'Ḛ' => 'Ḛ',
'ḛ' => 'ḛ',
'Ḝ' => 'Ḝ',
'ḝ' => 'ḝ',
'Ḟ' => 'Ḟ',
'ḟ' => 'ḟ',
'Ḡ' => 'Ḡ',
'ḡ' => 'ḡ',
'Ḣ' => 'Ḣ',
'ḣ' => 'ḣ',
'Ḥ' => 'Ḥ',
'ḥ' => 'ḥ',
'Ḧ' => 'Ḧ',
'ḧ' => 'ḧ',
'Ḩ' => 'Ḩ',
'ḩ' => 'ḩ',
'Ḫ' => 'Ḫ',
'ḫ' => 'ḫ',
'Ḭ' => 'Ḭ',
'ḭ' => 'ḭ',
'Ḯ' => 'Ḯ',
'ḯ' => 'ḯ',
'Ḱ' => 'Ḱ',
'ḱ' => 'ḱ',
'Ḳ' => 'Ḳ',
'ḳ' => 'ḳ',
'Ḵ' => 'Ḵ',
'ḵ' => 'ḵ',
'Ḷ' => 'Ḷ',
'ḷ' => 'ḷ',
'Ḹ' => 'Ḹ',
'ḹ' => 'ḹ',
'Ḻ' => 'Ḻ',
'ḻ' => 'ḻ',
'Ḽ' => 'Ḽ',
'ḽ' => 'ḽ',
'Ḿ' => 'Ḿ',
'ḿ' => 'ḿ',
'Ṁ' => 'Ṁ',
'ṁ' => 'ṁ',
'Ṃ' => 'Ṃ',
'ṃ' => 'ṃ',
'Ṅ' => 'Ṅ',
'ṅ' => 'ṅ',
'Ṇ' => 'Ṇ',
'ṇ' => 'ṇ',
'Ṉ' => 'Ṉ',
'ṉ' => 'ṉ',
'Ṋ' => 'Ṋ',
'ṋ' => 'ṋ',
'Ṍ' => 'Ṍ',
'ṍ' => 'ṍ',
'Ṏ' => 'Ṏ',
'ṏ' => 'ṏ',
'Ṑ' => 'Ṑ',
'ṑ' => 'ṑ',
'Ṓ' => 'Ṓ',
'ṓ' => 'ṓ',
'Ṕ' => 'Ṕ',
'ṕ' => 'ṕ',
'Ṗ' => 'Ṗ',
'ṗ' => 'ṗ',
'Ṙ' => 'Ṙ',
'ṙ' => 'ṙ',
'Ṛ' => 'Ṛ',
'ṛ' => 'ṛ',
'Ṝ' => 'Ṝ',
'ṝ' => 'ṝ',
'Ṟ' => 'Ṟ',
'ṟ' => 'ṟ',
'Ṡ' => 'Ṡ',
'ṡ' => 'ṡ',
'Ṣ' => 'Ṣ',
'ṣ' => 'ṣ',
'Ṥ' => 'Ṥ',
'ṥ' => 'ṥ',
'Ṧ' => 'Ṧ',
'ṧ' => 'ṧ',
'Ṩ' => 'Ṩ',
'ṩ' => 'ṩ',
'Ṫ' => 'Ṫ',
'ṫ' => 'ṫ',
'Ṭ' => 'Ṭ',
'ṭ' => 'ṭ',
'Ṯ' => 'Ṯ',
'ṯ' => 'ṯ',
'Ṱ' => 'Ṱ',
'ṱ' => 'ṱ',
'Ṳ' => 'Ṳ',
'ṳ' => 'ṳ',
'Ṵ' => 'Ṵ',
'ṵ' => 'ṵ',
'Ṷ' => 'Ṷ',
'ṷ' => 'ṷ',
'Ṹ' => 'Ṹ',
'ṹ' => 'ṹ',
'Ṻ' => 'Ṻ',
'ṻ' => 'ṻ',
'Ṽ' => 'Ṽ',
'ṽ' => 'ṽ',
'Ṿ' => 'Ṿ',
'ṿ' => 'ṿ',
'Ẁ' => 'Ẁ',
'ẁ' => 'ẁ',
'Ẃ' => 'Ẃ',
'ẃ' => 'ẃ',
'Ẅ' => 'Ẅ',
'ẅ' => 'ẅ',
'Ẇ' => 'Ẇ',
'ẇ' => 'ẇ',
'Ẉ' => 'Ẉ',
'ẉ' => 'ẉ',
'Ẋ' => 'Ẋ',
'ẋ' => 'ẋ',
'Ẍ' => 'Ẍ',
'ẍ' => 'ẍ',
'Ẏ' => 'Ẏ',
'ẏ' => 'ẏ',
'Ẑ' => 'Ẑ',
'ẑ' => 'ẑ',
'Ẓ' => 'Ẓ',
'ẓ' => 'ẓ',
'Ẕ' => 'Ẕ',
'ẕ' => 'ẕ',
'ẖ' => 'ẖ',
'ẗ' => 'ẗ',
'ẘ' => 'ẘ',
'ẙ' => 'ẙ',
'ẛ' => 'ẛ',
'Ạ' => 'Ạ',
'ạ' => 'ạ',
'Ả' => 'Ả',
'ả' => 'ả',
'Ấ' => 'Ấ',
'ấ' => 'ấ',
'Ầ' => 'Ầ',
'ầ' => 'ầ',
'Ẩ' => 'Ẩ',
'ẩ' => 'ẩ',
'Ẫ' => 'Ẫ',
'ẫ' => 'ẫ',
'Ậ' => 'Ậ',
'ậ' => 'ậ',
'Ắ' => 'Ắ',
'ắ' => 'ắ',
'Ằ' => 'Ằ',
'ằ' => 'ằ',
'Ẳ' => 'Ẳ',
'ẳ' => 'ẳ',
'Ẵ' => 'Ẵ',
'ẵ' => 'ẵ',
'Ặ' => 'Ặ',
'ặ' => 'ặ',
'Ẹ' => 'Ẹ',
'ẹ' => 'ẹ',
'Ẻ' => 'Ẻ',
'ẻ' => 'ẻ',
'Ẽ' => 'Ẽ',
'ẽ' => 'ẽ',
'Ế' => 'Ế',
'ế' => 'ế',
'Ề' => 'Ề',
'ề' => 'ề',
'Ể' => 'Ể',
'ể' => 'ể',
'Ễ' => 'Ễ',
'ễ' => 'ễ',
'Ệ' => 'Ệ',
'ệ' => 'ệ',
'Ỉ' => 'Ỉ',
'ỉ' => 'ỉ',
'Ị' => 'Ị',
'ị' => 'ị',
'Ọ' => 'Ọ',
'ọ' => 'ọ',
'Ỏ' => 'Ỏ',
'ỏ' => 'ỏ',
'Ố' => 'Ố',
'ố' => 'ố',
'Ồ' => 'Ồ',
'ồ' => 'ồ',
'Ổ' => 'Ổ',
'ổ' => 'ổ',
'Ỗ' => 'Ỗ',
'ỗ' => 'ỗ',
'Ộ' => 'Ộ',
'ộ' => 'ộ',
'Ớ' => 'Ớ',
'ớ' => 'ớ',
'Ờ' => 'Ờ',
'ờ' => 'ờ',
'Ở' => 'Ở',
'ở' => 'ở',
'Ỡ' => 'Ỡ',
'ỡ' => 'ỡ',
'Ợ' => 'Ợ',
'ợ' => 'ợ',
'Ụ' => 'Ụ',
'ụ' => 'ụ',
'Ủ' => 'Ủ',
'ủ' => 'ủ',
'Ứ' => 'Ứ',
'ứ' => 'ứ',
'Ừ' => 'Ừ',
'ừ' => 'ừ',
'Ử' => 'Ử',
'ử' => 'ử',
'Ữ' => 'Ữ',
'ữ' => 'ữ',
'Ự' => 'Ự',
'ự' => 'ự',
'Ỳ' => 'Ỳ',
'ỳ' => 'ỳ',
'Ỵ' => 'Ỵ',
'ỵ' => 'ỵ',
'Ỷ' => 'Ỷ',
'ỷ' => 'ỷ',
'Ỹ' => 'Ỹ',
'ỹ' => 'ỹ',
'ἀ' => 'ἀ',
'ἁ' => 'ἁ',
'ἂ' => 'ἂ',
'ἃ' => 'ἃ',
'ἄ' => 'ἄ',
'ἅ' => 'ἅ',
'ἆ' => 'ἆ',
'ἇ' => 'ἇ',
'Ἀ' => 'Ἀ',
'Ἁ' => 'Ἁ',
'Ἂ' => 'Ἂ',
'Ἃ' => 'Ἃ',
'Ἄ' => 'Ἄ',
'Ἅ' => 'Ἅ',
'Ἆ' => 'Ἆ',
'Ἇ' => 'Ἇ',
'ἐ' => 'ἐ',
'ἑ' => 'ἑ',
'ἒ' => 'ἒ',
'ἓ' => 'ἓ',
'ἔ' => 'ἔ',
'ἕ' => 'ἕ',
'Ἐ' => 'Ἐ',
'Ἑ' => 'Ἑ',
'Ἒ' => 'Ἒ',
'Ἓ' => 'Ἓ',
'Ἔ' => 'Ἔ',
'Ἕ' => 'Ἕ',
'ἠ' => 'ἠ',
'ἡ' => 'ἡ',
'ἢ' => 'ἢ',
'ἣ' => 'ἣ',
'ἤ' => 'ἤ',
'ἥ' => 'ἥ',
'ἦ' => 'ἦ',
'ἧ' => 'ἧ',
'Ἠ' => 'Ἠ',
'Ἡ' => 'Ἡ',
'Ἢ' => 'Ἢ',
'Ἣ' => 'Ἣ',
'Ἤ' => 'Ἤ',
'Ἥ' => 'Ἥ',
'Ἦ' => 'Ἦ',
'Ἧ' => 'Ἧ',
'ἰ' => 'ἰ',
'ἱ' => 'ἱ',
'ἲ' => 'ἲ',
'ἳ' => 'ἳ',
'ἴ' => 'ἴ',
'ἵ' => 'ἵ',
'ἶ' => 'ἶ',
'ἷ' => 'ἷ',
'Ἰ' => 'Ἰ',
'Ἱ' => 'Ἱ',
'Ἲ' => 'Ἲ',
'Ἳ' => 'Ἳ',
'Ἴ' => 'Ἴ',
'Ἵ' => 'Ἵ',
'Ἶ' => 'Ἶ',
'Ἷ' => 'Ἷ',
'ὀ' => 'ὀ',
'ὁ' => 'ὁ',
'ὂ' => 'ὂ',
'ὃ' => 'ὃ',
'ὄ' => 'ὄ',
'ὅ' => 'ὅ',
'Ὀ' => 'Ὀ',
'Ὁ' => 'Ὁ',
'Ὂ' => 'Ὂ',
'Ὃ' => 'Ὃ',
'Ὄ' => 'Ὄ',
'Ὅ' => 'Ὅ',
'ὐ' => 'ὐ',
'ὑ' => 'ὑ',
'ὒ' => 'ὒ',
'ὓ' => 'ὓ',
'ὔ' => 'ὔ',
'ὕ' => 'ὕ',
'ὖ' => 'ὖ',
'ὗ' => 'ὗ',
'Ὑ' => 'Ὑ',
'Ὓ' => 'Ὓ',
'Ὕ' => 'Ὕ',
'Ὗ' => 'Ὗ',
'ὠ' => 'ὠ',
'ὡ' => 'ὡ',
'ὢ' => 'ὢ',
'ὣ' => 'ὣ',
'ὤ' => 'ὤ',
'ὥ' => 'ὥ',
'ὦ' => 'ὦ',
'ὧ' => 'ὧ',
'Ὠ' => 'Ὠ',
'Ὡ' => 'Ὡ',
'Ὢ' => 'Ὢ',
'Ὣ' => 'Ὣ',
'Ὤ' => 'Ὤ',
'Ὥ' => 'Ὥ',
'Ὦ' => 'Ὦ',
'Ὧ' => 'Ὧ',
'ὰ' => 'ὰ',
'ά' => 'ά',
'ὲ' => 'ὲ',
'έ' => 'έ',
'ὴ' => 'ὴ',
'ή' => 'ή',
'ὶ' => 'ὶ',
'ί' => 'ί',
'ὸ' => 'ὸ',
'ό' => 'ό',
'ὺ' => 'ὺ',
'ύ' => 'ύ',
'ὼ' => 'ὼ',
'ώ' => 'ώ',
'ᾀ' => 'ᾀ',
'ᾁ' => 'ᾁ',
'ᾂ' => 'ᾂ',
'ᾃ' => 'ᾃ',
'ᾄ' => 'ᾄ',
'ᾅ' => 'ᾅ',
'ᾆ' => 'ᾆ',
'ᾇ' => 'ᾇ',
'ᾈ' => 'ᾈ',
'ᾉ' => 'ᾉ',
'ᾊ' => 'ᾊ',
'ᾋ' => 'ᾋ',
'ᾌ' => 'ᾌ',
'ᾍ' => 'ᾍ',
'ᾎ' => 'ᾎ',
'ᾏ' => 'ᾏ',
'ᾐ' => 'ᾐ',
'ᾑ' => 'ᾑ',
'ᾒ' => 'ᾒ',
'ᾓ' => 'ᾓ',
'ᾔ' => 'ᾔ',
'ᾕ' => 'ᾕ',
'ᾖ' => 'ᾖ',
'ᾗ' => 'ᾗ',
'ᾘ' => 'ᾘ',
'ᾙ' => 'ᾙ',
'ᾚ' => 'ᾚ',
'ᾛ' => 'ᾛ',
'ᾜ' => 'ᾜ',
'ᾝ' => 'ᾝ',
'ᾞ' => 'ᾞ',
'ᾟ' => 'ᾟ',
'ᾠ' => 'ᾠ',
'ᾡ' => 'ᾡ',
'ᾢ' => 'ᾢ',
'ᾣ' => 'ᾣ',
'ᾤ' => 'ᾤ',
'ᾥ' => 'ᾥ',
'ᾦ' => 'ᾦ',
'ᾧ' => 'ᾧ',
'ᾨ' => 'ᾨ',
'ᾩ' => 'ᾩ',
'ᾪ' => 'ᾪ',
'ᾫ' => 'ᾫ',
'ᾬ' => 'ᾬ',
'ᾭ' => 'ᾭ',
'ᾮ' => 'ᾮ',
'ᾯ' => 'ᾯ',
'ᾰ' => 'ᾰ',
'ᾱ' => 'ᾱ',
'ᾲ' => 'ᾲ',
'ᾳ' => 'ᾳ',
'ᾴ' => 'ᾴ',
'ᾶ' => 'ᾶ',
'ᾷ' => 'ᾷ',
'Ᾰ' => 'Ᾰ',
'Ᾱ' => 'Ᾱ',
'Ὰ' => 'Ὰ',
'Ά' => 'Ά',
'ᾼ' => 'ᾼ',
'' => 'ι',
'῁' => '῁',
'ῂ' => 'ῂ',
'ῃ' => 'ῃ',
'ῄ' => 'ῄ',
'ῆ' => 'ῆ',
'ῇ' => 'ῇ',
'Ὲ' => 'Ὲ',
'Έ' => 'Έ',
'Ὴ' => 'Ὴ',
'Ή' => 'Ή',
'ῌ' => 'ῌ',
'῍' => '῍',
'῎' => '῎',
'῏' => '῏',
'ῐ' => 'ῐ',
'ῑ' => 'ῑ',
'ῒ' => 'ῒ',
'ΐ' => 'ΐ',
'ῖ' => 'ῖ',
'ῗ' => 'ῗ',
'Ῐ' => 'Ῐ',
'Ῑ' => 'Ῑ',
'Ὶ' => 'Ὶ',
'Ί' => 'Ί',
'῝' => '῝',
'῞' => '῞',
'῟' => '῟',
'ῠ' => 'ῠ',
'ῡ' => 'ῡ',
'ῢ' => 'ῢ',
'ΰ' => 'ΰ',
'ῤ' => 'ῤ',
'ῥ' => 'ῥ',
'ῦ' => 'ῦ',
'ῧ' => 'ῧ',
'Ῠ' => 'Ῠ',
'Ῡ' => 'Ῡ',
'Ὺ' => 'Ὺ',
'Ύ' => 'Ύ',
'Ῥ' => 'Ῥ',
'῭' => '῭',
'΅' => '΅',
'' => '`',
'ῲ' => 'ῲ',
'ῳ' => 'ῳ',
'ῴ' => 'ῴ',
'ῶ' => 'ῶ',
'ῷ' => 'ῷ',
'Ὸ' => 'Ὸ',
'Ό' => 'Ό',
'Ὼ' => 'Ὼ',
'Ώ' => 'Ώ',
'ῼ' => 'ῼ',
'' => '´',
' ' => '',
'' => '',
'Ω' => 'Ω',
'' => 'K',
'Å' => 'Å',
'↚' => '↚',
'↛' => '↛',
'↮' => '↮',
'⇍' => '⇍',
'⇎' => '⇎',
'⇏' => '⇏',
'∄' => '∄',
'∉' => '∉',
'∌' => '∌',
'∤' => '∤',
'∦' => '∦',
'≁' => '≁',
'≄' => '≄',
'≇' => '≇',
'≉' => '≉',
'≠' => '≠',
'≢' => '≢',
'≭' => '≭',
'≮' => '≮',
'≯' => '≯',
'≰' => '≰',
'≱' => '≱',
'≴' => '≴',
'≵' => '≵',
'≸' => '≸',
'≹' => '≹',
'⊀' => '⊀',
'⊁' => '⊁',
'⊄' => '⊄',
'⊅' => '⊅',
'⊈' => '⊈',
'⊉' => '⊉',
'⊬' => '⊬',
'⊭' => '⊭',
'⊮' => '⊮',
'⊯' => '⊯',
'⋠' => '⋠',
'⋡' => '⋡',
'⋢' => '⋢',
'⋣' => '⋣',
'⋪' => '⋪',
'⋫' => '⋫',
'⋬' => '⋬',
'⋭' => '⋭',
'〈' => '〈',
'〉' => '〉',
'⫝̸' => '⫝̸',
'が' => 'が',
'ぎ' => 'ぎ',
'ぐ' => 'ぐ',
'げ' => 'げ',
'ご' => 'ご',
'ざ' => 'ざ',
'じ' => 'じ',
'ず' => 'ず',
'ぜ' => 'ぜ',
'ぞ' => 'ぞ',
'だ' => 'だ',
'ぢ' => 'ぢ',
'づ' => 'づ',
'で' => 'で',
'ど' => 'ど',
'ば' => 'ば',
'ぱ' => 'ぱ',
'び' => 'び',
'ぴ' => 'ぴ',
'ぶ' => 'ぶ',
'ぷ' => 'ぷ',
'べ' => 'べ',
'ぺ' => 'ぺ',
'ぼ' => 'ぼ',
'ぽ' => 'ぽ',
'ゔ' => 'ゔ',
'ゞ' => 'ゞ',
'ガ' => 'ガ',
'ギ' => 'ギ',
'グ' => 'グ',
'ゲ' => 'ゲ',
'ゴ' => 'ゴ',
'ザ' => 'ザ',
'ジ' => 'ジ',
'ズ' => 'ズ',
'ゼ' => 'ゼ',
'ゾ' => 'ゾ',
'ダ' => 'ダ',
'ヂ' => 'ヂ',
'ヅ' => 'ヅ',
'デ' => 'デ',
'ド' => 'ド',
'バ' => 'バ',
'パ' => 'パ',
'ビ' => 'ビ',
'ピ' => 'ピ',
'ブ' => 'ブ',
'プ' => 'プ',
'ベ' => 'ベ',
'ペ' => 'ペ',
'ボ' => 'ボ',
'ポ' => 'ポ',
'ヴ' => 'ヴ',
'ヷ' => 'ヷ',
'ヸ' => 'ヸ',
'ヹ' => 'ヹ',
'ヺ' => 'ヺ',
'ヾ' => 'ヾ',
'豈' => '豈',
'更' => '更',
'車' => '車',
'賈' => '賈',
'滑' => '滑',
'串' => '串',
'句' => '句',
'龜' => '龜',
'龜' => '龜',
'契' => '契',
'金' => '金',
'喇' => '喇',
'奈' => '奈',
'懶' => '懶',
'癩' => '癩',
'羅' => '羅',
'蘿' => '蘿',
'螺' => '螺',
'裸' => '裸',
'邏' => '邏',
'樂' => '樂',
'洛' => '洛',
'烙' => '烙',
'珞' => '珞',
'落' => '落',
'酪' => '酪',
'駱' => '駱',
'亂' => '亂',
'卵' => '卵',
'欄' => '欄',
'爛' => '爛',
'蘭' => '蘭',
'鸞' => '鸞',
'嵐' => '嵐',
'濫' => '濫',
'藍' => '藍',
'襤' => '襤',
'拉' => '拉',
'臘' => '臘',
'蠟' => '蠟',
'廊' => '廊',
'朗' => '朗',
'浪' => '浪',
'狼' => '狼',
'郎' => '郎',
'來' => '來',
'冷' => '冷',
'勞' => '勞',
'擄' => '擄',
'櫓' => '櫓',
'爐' => '爐',
'盧' => '盧',
'老' => '老',
'蘆' => '蘆',
'虜' => '虜',
'路' => '路',
'露' => '露',
'魯' => '魯',
'鷺' => '鷺',
'碌' => '碌',
'祿' => '祿',
'綠' => '綠',
'菉' => '菉',
'錄' => '錄',
'鹿' => '鹿',
'論' => '論',
'壟' => '壟',
'弄' => '弄',
'籠' => '籠',
'聾' => '聾',
'牢' => '牢',
'磊' => '磊',
'賂' => '賂',
'雷' => '雷',
'壘' => '壘',
'屢' => '屢',
'樓' => '樓',
'淚' => '淚',
'漏' => '漏',
'累' => '累',
'縷' => '縷',
'陋' => '陋',
'勒' => '勒',
'肋' => '肋',
'凜' => '凜',
'凌' => '凌',
'稜' => '稜',
'綾' => '綾',
'菱' => '菱',
'陵' => '陵',
'讀' => '讀',
'拏' => '拏',
'樂' => '樂',
'諾' => '諾',
'丹' => '丹',
'寧' => '寧',
'怒' => '怒',
'率' => '率',
'異' => '異',
'北' => '北',
'磻' => '磻',
'便' => '便',
'復' => '復',
'不' => '不',
'泌' => '泌',
'數' => '數',
'索' => '索',
'參' => '參',
'塞' => '塞',
'省' => '省',
'葉' => '葉',
'說' => '說',
'殺' => '殺',
'辰' => '辰',
'沈' => '沈',
'拾' => '拾',
'若' => '若',
'掠' => '掠',
'略' => '略',
'亮' => '亮',
'兩' => '兩',
'凉' => '凉',
'梁' => '梁',
'糧' => '糧',
'良' => '良',
'諒' => '諒',
'量' => '量',
'勵' => '勵',
'呂' => '呂',
'女' => '女',
'廬' => '廬',
'旅' => '旅',
'濾' => '濾',
'礪' => '礪',
'閭' => '閭',
'驪' => '驪',
'麗' => '麗',
'黎' => '黎',
'力' => '力',
'曆' => '曆',
'歷' => '歷',
'轢' => '轢',
'年' => '年',
'憐' => '憐',
'戀' => '戀',
'撚' => '撚',
'漣' => '漣',
'煉' => '煉',
'璉' => '璉',
'秊' => '秊',
'練' => '練',
'聯' => '聯',
'輦' => '輦',
'蓮' => '蓮',
'連' => '連',
'鍊' => '鍊',
'列' => '列',
'劣' => '劣',
'咽' => '咽',
'烈' => '烈',
'裂' => '裂',
'說' => '說',
'廉' => '廉',
'念' => '念',
'捻' => '捻',
'殮' => '殮',
'簾' => '簾',
'獵' => '獵',
'令' => '令',
'囹' => '囹',
'寧' => '寧',
'嶺' => '嶺',
'怜' => '怜',
'玲' => '玲',
'瑩' => '瑩',
'羚' => '羚',
'聆' => '聆',
'鈴' => '鈴',
'零' => '零',
'靈' => '靈',
'領' => '領',
'例' => '例',
'禮' => '禮',
'醴' => '醴',
'隸' => '隸',
'惡' => '惡',
'了' => '了',
'僚' => '僚',
'寮' => '寮',
'尿' => '尿',
'料' => '料',
'樂' => '樂',
'燎' => '燎',
'療' => '療',
'蓼' => '蓼',
'遼' => '遼',
'龍' => '龍',
'暈' => '暈',
'阮' => '阮',
'劉' => '劉',
'杻' => '杻',
'柳' => '柳',
'流' => '流',
'溜' => '溜',
'琉' => '琉',
'留' => '留',
'硫' => '硫',
'紐' => '紐',
'類' => '類',
'六' => '六',
'戮' => '戮',
'陸' => '陸',
'倫' => '倫',
'崙' => '崙',
'淪' => '淪',
'輪' => '輪',
'律' => '律',
'慄' => '慄',
'栗' => '栗',
'率' => '率',
'隆' => '隆',
'利' => '利',
'吏' => '吏',
'履' => '履',
'易' => '易',
'李' => '李',
'梨' => '梨',
'泥' => '泥',
'理' => '理',
'痢' => '痢',
'罹' => '罹',
'裏' => '裏',
'裡' => '裡',
'里' => '里',
'離' => '離',
'匿' => '匿',
'溺' => '溺',
'吝' => '吝',
'燐' => '燐',
'璘' => '璘',
'藺' => '藺',
'隣' => '隣',
'鱗' => '鱗',
'麟' => '麟',
'林' => '林',
'淋' => '淋',
'臨' => '臨',
'立' => '立',
'笠' => '笠',
'粒' => '粒',
'狀' => '狀',
'炙' => '炙',
'識' => '識',
'什' => '什',
'茶' => '茶',
'刺' => '刺',
'切' => '切',
'度' => '度',
'拓' => '拓',
'糖' => '糖',
'宅' => '宅',
'洞' => '洞',
'暴' => '暴',
'輻' => '輻',
'行' => '行',
'降' => '降',
'見' => '見',
'廓' => '廓',
'兀' => '兀',
'嗀' => '嗀',
'塚' => '塚',
'晴' => '晴',
'凞' => '凞',
'猪' => '猪',
'益' => '益',
'礼' => '礼',
'神' => '神',
'祥' => '祥',
'福' => '福',
'靖' => '靖',
'精' => '精',
'羽' => '羽',
'蘒' => '蘒',
'諸' => '諸',
'逸' => '逸',
'都' => '都',
'飯' => '飯',
'飼' => '飼',
'館' => '館',
'鶴' => '鶴',
'郞' => '郞',
'隷' => '隷',
'侮' => '侮',
'僧' => '僧',
'免' => '免',
'勉' => '勉',
'勤' => '勤',
'卑' => '卑',
'喝' => '喝',
'嘆' => '嘆',
'器' => '器',
'塀' => '塀',
'墨' => '墨',
'層' => '層',
'屮' => '屮',
'悔' => '悔',
'慨' => '慨',
'憎' => '憎',
'懲' => '懲',
'敏' => '敏',
'既' => '既',
'暑' => '暑',
'梅' => '梅',
'海' => '海',
'渚' => '渚',
'漢' => '漢',
'煮' => '煮',
'爫' => '爫',
'琢' => '琢',
'碑' => '碑',
'社' => '社',
'祉' => '祉',
'祈' => '祈',
'祐' => '祐',
'祖' => '祖',
'祝' => '祝',
'禍' => '禍',
'禎' => '禎',
'穀' => '穀',
'突' => '突',
'節' => '節',
'練' => '練',
'縉' => '縉',
'繁' => '繁',
'署' => '署',
'者' => '者',
'臭' => '臭',
'艹' => '艹',
'艹' => '艹',
'著' => '著',
'褐' => '褐',
'視' => '視',
'謁' => '謁',
'謹' => '謹',
'賓' => '賓',
'贈' => '贈',
'辶' => '辶',
'逸' => '逸',
'難' => '難',
'響' => '響',
'頻' => '頻',
'恵' => '恵',
'𤋮' => '𤋮',
'舘' => '舘',
'並' => '並',
'况' => '况',
'全' => '全',
'侀' => '侀',
'充' => '充',
'冀' => '冀',
'勇' => '勇',
'勺' => '勺',
'喝' => '喝',
'啕' => '啕',
'喙' => '喙',
'嗢' => '嗢',
'塚' => '塚',
'墳' => '墳',
'奄' => '奄',
'奔' => '奔',
'婢' => '婢',
'嬨' => '嬨',
'廒' => '廒',
'廙' => '廙',
'彩' => '彩',
'徭' => '徭',
'惘' => '惘',
'慎' => '慎',
'愈' => '愈',
'憎' => '憎',
'慠' => '慠',
'懲' => '懲',
'戴' => '戴',
'揄' => '揄',
'搜' => '搜',
'摒' => '摒',
'敖' => '敖',
'晴' => '晴',
'朗' => '朗',
'望' => '望',
'杖' => '杖',
'歹' => '歹',
'殺' => '殺',
'流' => '流',
'滛' => '滛',
'滋' => '滋',
'漢' => '漢',
'瀞' => '瀞',
'煮' => '煮',
'瞧' => '瞧',
'爵' => '爵',
'犯' => '犯',
'猪' => '猪',
'瑱' => '瑱',
'甆' => '甆',
'画' => '画',
'瘝' => '瘝',
'瘟' => '瘟',
'益' => '益',
'盛' => '盛',
'直' => '直',
'睊' => '睊',
'着' => '着',
'磌' => '磌',
'窱' => '窱',
'節' => '節',
'类' => '类',
'絛' => '絛',
'練' => '練',
'缾' => '缾',
'者' => '者',
'荒' => '荒',
'華' => '華',
'蝹' => '蝹',
'襁' => '襁',
'覆' => '覆',
'視' => '視',
'調' => '調',
'諸' => '諸',
'請' => '請',
'謁' => '謁',
'諾' => '諾',
'諭' => '諭',
'謹' => '謹',
'變' => '變',
'贈' => '贈',
'輸' => '輸',
'遲' => '遲',
'醙' => '醙',
'鉶' => '鉶',
'陼' => '陼',
'難' => '難',
'靖' => '靖',
'韛' => '韛',
'響' => '響',
'頋' => '頋',
'頻' => '頻',
'鬒' => '鬒',
'龜' => '龜',
'𢡊' => '𢡊',
'𢡄' => '𢡄',
'𣏕' => '𣏕',
'㮝' => '㮝',
'䀘' => '䀘',
'䀹' => '䀹',
'𥉉' => '𥉉',
'𥳐' => '𥳐',
'𧻓' => '𧻓',
'齃' => '齃',
'龎' => '龎',
'יִ' => 'יִ',
'ײַ' => 'ײַ',
'שׁ' => 'שׁ',
'שׂ' => 'שׂ',
'שּׁ' => 'שּׁ',
'שּׂ' => 'שּׂ',
'אַ' => 'אַ',
'אָ' => 'אָ',
'אּ' => 'אּ',
'בּ' => 'בּ',
'גּ' => 'גּ',
'דּ' => 'דּ',
'הּ' => 'הּ',
'וּ' => 'וּ',
'זּ' => 'זּ',
'טּ' => 'טּ',
'יּ' => 'יּ',
'ךּ' => 'ךּ',
'כּ' => 'כּ',
'לּ' => 'לּ',
'מּ' => 'מּ',
'נּ' => 'נּ',
'סּ' => 'סּ',
'ףּ' => 'ףּ',
'פּ' => 'פּ',
'צּ' => 'צּ',
'קּ' => 'קּ',
'רּ' => 'רּ',
'שּ' => 'שּ',
'תּ' => 'תּ',
'וֹ' => 'וֹ',
'בֿ' => 'בֿ',
'כֿ' => 'כֿ',
'פֿ' => 'פֿ',
'𑂚' => '𑂚',
'𑂜' => '𑂜',
'𑂫' => '𑂫',
'𑄮' => '𑄮',
'𑄯' => '𑄯',
'𑍋' => '𑍋',
'𑍌' => '𑍌',
'𑒻' => '𑒻',
'𑒼' => '𑒼',
'𑒾' => '𑒾',
'𑖺' => '𑖺',
'𑖻' => '𑖻',
'𑤸' => '𑤸',
'𝅗𝅥' => '𝅗𝅥',
'𝅘𝅥' => '𝅘𝅥',
'𝅘𝅥𝅮' => '𝅘𝅥𝅮',
'𝅘𝅥𝅯' => '𝅘𝅥𝅯',
'𝅘𝅥𝅰' => '𝅘𝅥𝅰',
'𝅘𝅥𝅱' => '𝅘𝅥𝅱',
'𝅘𝅥𝅲' => '𝅘𝅥𝅲',
'𝆹𝅥' => '𝆹𝅥',
'𝆺𝅥' => '𝆺𝅥',
'𝆹𝅥𝅮' => '𝆹𝅥𝅮',
'𝆺𝅥𝅮' => '𝆺𝅥𝅮',
'𝆹𝅥𝅯' => '𝆹𝅥𝅯',
'𝆺𝅥𝅯' => '𝆺𝅥𝅯',
'丽' => '丽',
'丸' => '丸',
'乁' => '乁',
'𠄢' => '𠄢',
'你' => '你',
'侮' => '侮',
'侻' => '侻',
'倂' => '倂',
'偺' => '偺',
'備' => '備',
'僧' => '僧',
'像' => '像',
'㒞' => '㒞',
'𠘺' => '𠘺',
'免' => '免',
'兔' => '兔',
'兤' => '兤',
'具' => '具',
'𠔜' => '𠔜',
'㒹' => '㒹',
'內' => '內',
'再' => '再',
'𠕋' => '𠕋',
'冗' => '冗',
'冤' => '冤',
'仌' => '仌',
'冬' => '冬',
'况' => '况',
'𩇟' => '𩇟',
'凵' => '凵',
'刃' => '刃',
'㓟' => '㓟',
'刻' => '刻',
'剆' => '剆',
'割' => '割',
'剷' => '剷',
'㔕' => '㔕',
'勇' => '勇',
'勉' => '勉',
'勤' => '勤',
'勺' => '勺',
'包' => '包',
'匆' => '匆',
'北' => '北',
'卉' => '卉',
'卑' => '卑',
'博' => '博',
'即' => '即',
'卽' => '卽',
'卿' => '卿',
'卿' => '卿',
'卿' => '卿',
'𠨬' => '𠨬',
'灰' => '灰',
'及' => '及',
'叟' => '叟',
'𠭣' => '𠭣',
'叫' => '叫',
'叱' => '叱',
'吆' => '吆',
'咞' => '咞',
'吸' => '吸',
'呈' => '呈',
'周' => '周',
'咢' => '咢',
'哶' => '哶',
'唐' => '唐',
'啓' => '啓',
'啣' => '啣',
'善' => '善',
'善' => '善',
'喙' => '喙',
'喫' => '喫',
'喳' => '喳',
'嗂' => '嗂',
'圖' => '圖',
'嘆' => '嘆',
'圗' => '圗',
'噑' => '噑',
'噴' => '噴',
'切' => '切',
'壮' => '壮',
'城' => '城',
'埴' => '埴',
'堍' => '堍',
'型' => '型',
'堲' => '堲',
'報' => '報',
'墬' => '墬',
'𡓤' => '𡓤',
'売' => '売',
'壷' => '壷',
'夆' => '夆',
'多' => '多',
'夢' => '夢',
'奢' => '奢',
'𡚨' => '𡚨',
'𡛪' => '𡛪',
'姬' => '姬',
'娛' => '娛',
'娧' => '娧',
'姘' => '姘',
'婦' => '婦',
'㛮' => '㛮',
'㛼' => '㛼',
'嬈' => '嬈',
'嬾' => '嬾',
'嬾' => '嬾',
'𡧈' => '𡧈',
'寃' => '寃',
'寘' => '寘',
'寧' => '寧',
'寳' => '寳',
'𡬘' => '𡬘',
'寿' => '寿',
'将' => '将',
'当' => '当',
'尢' => '尢',
'㞁' => '㞁',
'屠' => '屠',
'屮' => '屮',
'峀' => '峀',
'岍' => '岍',
'𡷤' => '𡷤',
'嵃' => '嵃',
'𡷦' => '𡷦',
'嵮' => '嵮',
'嵫' => '嵫',
'嵼' => '嵼',
'巡' => '巡',
'巢' => '巢',
'㠯' => '㠯',
'巽' => '巽',
'帨' => '帨',
'帽' => '帽',
'幩' => '幩',
'㡢' => '㡢',
'𢆃' => '𢆃',
'㡼' => '㡼',
'庰' => '庰',
'庳' => '庳',
'庶' => '庶',
'廊' => '廊',
'𪎒' => '𪎒',
'廾' => '廾',
'𢌱' => '𢌱',
'𢌱' => '𢌱',
'舁' => '舁',
'弢' => '弢',
'弢' => '弢',
'㣇' => '㣇',
'𣊸' => '𣊸',
'𦇚' => '𦇚',
'形' => '形',
'彫' => '彫',
'㣣' => '㣣',
'徚' => '徚',
'忍' => '忍',
'志' => '志',
'忹' => '忹',
'悁' => '悁',
'㤺' => '㤺',
'㤜' => '㤜',
'悔' => '悔',
'𢛔' => '𢛔',
'惇' => '惇',
'慈' => '慈',
'慌' => '慌',
'慎' => '慎',
'慌' => '慌',
'慺' => '慺',
'憎' => '憎',
'憲' => '憲',
'憤' => '憤',
'憯' => '憯',
'懞' => '懞',
'懲' => '懲',
'懶' => '懶',
'成' => '成',
'戛' => '戛',
'扝' => '扝',
'抱' => '抱',
'拔' => '拔',
'捐' => '捐',
'𢬌' => '𢬌',
'挽' => '挽',
'拼' => '拼',
'捨' => '捨',
'掃' => '掃',
'揤' => '揤',
'𢯱' => '𢯱',
'搢' => '搢',
'揅' => '揅',
'掩' => '掩',
'㨮' => '㨮',
'摩' => '摩',
'摾' => '摾',
'撝' => '撝',
'摷' => '摷',
'㩬' => '㩬',
'敏' => '敏',
'敬' => '敬',
'𣀊' => '𣀊',
'旣' => '旣',
'書' => '書',
'晉' => '晉',
'㬙' => '㬙',
'暑' => '暑',
'㬈' => '㬈',
'㫤' => '㫤',
'冒' => '冒',
'冕' => '冕',
'最' => '最',
'暜' => '暜',
'肭' => '肭',
'䏙' => '䏙',
'朗' => '朗',
'望' => '望',
'朡' => '朡',
'杞' => '杞',
'杓' => '杓',
'𣏃' => '𣏃',
'㭉' => '㭉',
'柺' => '柺',
'枅' => '枅',
'桒' => '桒',
'梅' => '梅',
'𣑭' => '𣑭',
'梎' => '梎',
'栟' => '栟',
'椔' => '椔',
'㮝' => '㮝',
'楂' => '楂',
'榣' => '榣',
'槪' => '槪',
'檨' => '檨',
'𣚣' => '𣚣',
'櫛' => '櫛',
'㰘' => '㰘',
'次' => '次',
'𣢧' => '𣢧',
'歔' => '歔',
'㱎' => '㱎',
'歲' => '歲',
'殟' => '殟',
'殺' => '殺',
'殻' => '殻',
'𣪍' => '𣪍',
'𡴋' => '𡴋',
'𣫺' => '𣫺',
'汎' => '汎',
'𣲼' => '𣲼',
'沿' => '沿',
'泍' => '泍',
'汧' => '汧',
'洖' => '洖',
'派' => '派',
'海' => '海',
'流' => '流',
'浩' => '浩',
'浸' => '浸',
'涅' => '涅',
'𣴞' => '𣴞',
'洴' => '洴',
'港' => '港',
'湮' => '湮',
'㴳' => '㴳',
'滋' => '滋',
'滇' => '滇',
'𣻑' => '𣻑',
'淹' => '淹',
'潮' => '潮',
'𣽞' => '𣽞',
'𣾎' => '𣾎',
'濆' => '濆',
'瀹' => '瀹',
'瀞' => '瀞',
'瀛' => '瀛',
'㶖' => '㶖',
'灊' => '灊',
'災' => '災',
'灷' => '灷',
'炭' => '炭',
'𠔥' => '𠔥',
'煅' => '煅',
'𤉣' => '𤉣',
'熜' => '熜',
'𤎫' => '𤎫',
'爨' => '爨',
'爵' => '爵',
'牐' => '牐',
'𤘈' => '𤘈',
'犀' => '犀',
'犕' => '犕',
'𤜵' => '𤜵',
'𤠔' => '𤠔',
'獺' => '獺',
'王' => '王',
'㺬' => '㺬',
'玥' => '玥',
'㺸' => '㺸',
'㺸' => '㺸',
'瑇' => '瑇',
'瑜' => '瑜',
'瑱' => '瑱',
'璅' => '璅',
'瓊' => '瓊',
'㼛' => '㼛',
'甤' => '甤',
'𤰶' => '𤰶',
'甾' => '甾',
'𤲒' => '𤲒',
'異' => '異',
'𢆟' => '𢆟',
'瘐' => '瘐',
'𤾡' => '𤾡',
'𤾸' => '𤾸',
'𥁄' => '𥁄',
'㿼' => '㿼',
'䀈' => '䀈',
'直' => '直',
'𥃳' => '𥃳',
'𥃲' => '𥃲',
'𥄙' => '𥄙',
'𥄳' => '𥄳',
'眞' => '眞',
'真' => '真',
'真' => '真',
'睊' => '睊',
'䀹' => '䀹',
'瞋' => '瞋',
'䁆' => '䁆',
'䂖' => '䂖',
'𥐝' => '𥐝',
'硎' => '硎',
'碌' => '碌',
'磌' => '磌',
'䃣' => '䃣',
'𥘦' => '𥘦',
'祖' => '祖',
'𥚚' => '𥚚',
'𥛅' => '𥛅',
'福' => '福',
'秫' => '秫',
'䄯' => '䄯',
'穀' => '穀',
'穊' => '穊',
'穏' => '穏',
'𥥼' => '𥥼',
'𥪧' => '𥪧',
'𥪧' => '𥪧',
'竮' => '竮',
'䈂' => '䈂',
'𥮫' => '𥮫',
'篆' => '篆',
'築' => '築',
'䈧' => '䈧',
'𥲀' => '𥲀',
'糒' => '糒',
'䊠' => '䊠',
'糨' => '糨',
'糣' => '糣',
'紀' => '紀',
'𥾆' => '𥾆',
'絣' => '絣',
'䌁' => '䌁',
'緇' => '緇',
'縂' => '縂',
'繅' => '繅',
'䌴' => '䌴',
'𦈨' => '𦈨',
'𦉇' => '𦉇',
'䍙' => '䍙',
'𦋙' => '𦋙',
'罺' => '罺',
'𦌾' => '𦌾',
'羕' => '羕',
'翺' => '翺',
'者' => '者',
'𦓚' => '𦓚',
'𦔣' => '𦔣',
'聠' => '聠',
'𦖨' => '𦖨',
'聰' => '聰',
'𣍟' => '𣍟',
'䏕' => '䏕',
'育' => '育',
'脃' => '脃',
'䐋' => '䐋',
'脾' => '脾',
'媵' => '媵',
'𦞧' => '𦞧',
'𦞵' => '𦞵',
'𣎓' => '𣎓',
'𣎜' => '𣎜',
'舁' => '舁',
'舄' => '舄',
'辞' => '辞',
'䑫' => '䑫',
'芑' => '芑',
'芋' => '芋',
'芝' => '芝',
'劳' => '劳',
'花' => '花',
'芳' => '芳',
'芽' => '芽',
'苦' => '苦',
'𦬼' => '𦬼',
'若' => '若',
'茝' => '茝',
'荣' => '荣',
'莭' => '莭',
'茣' => '茣',
'莽' => '莽',
'菧' => '菧',
'著' => '著',
'荓' => '荓',
'菊' => '菊',
'菌' => '菌',
'菜' => '菜',
'𦰶' => '𦰶',
'𦵫' => '𦵫',
'𦳕' => '𦳕',
'䔫' => '䔫',
'蓱' => '蓱',
'蓳' => '蓳',
'蔖' => '蔖',
'𧏊' => '𧏊',
'蕤' => '蕤',
'𦼬' => '𦼬',
'䕝' => '䕝',
'䕡' => '䕡',
'𦾱' => '𦾱',
'𧃒' => '𧃒',
'䕫' => '䕫',
'虐' => '虐',
'虜' => '虜',
'虧' => '虧',
'虩' => '虩',
'蚩' => '蚩',
'蚈' => '蚈',
'蜎' => '蜎',
'蛢' => '蛢',
'蝹' => '蝹',
'蜨' => '蜨',
'蝫' => '蝫',
'螆' => '螆',
'䗗' => '䗗',
'蟡' => '蟡',
'蠁' => '蠁',
'䗹' => '䗹',
'衠' => '衠',
'衣' => '衣',
'𧙧' => '𧙧',
'裗' => '裗',
'裞' => '裞',
'䘵' => '䘵',
'裺' => '裺',
'㒻' => '㒻',
'𧢮' => '𧢮',
'𧥦' => '𧥦',
'䚾' => '䚾',
'䛇' => '䛇',
'誠' => '誠',
'諭' => '諭',
'變' => '變',
'豕' => '豕',
'𧲨' => '𧲨',
'貫' => '貫',
'賁' => '賁',
'贛' => '贛',
'起' => '起',
'𧼯' => '𧼯',
'𠠄' => '𠠄',
'跋' => '跋',
'趼' => '趼',
'跰' => '跰',
'𠣞' => '𠣞',
'軔' => '軔',
'輸' => '輸',
'𨗒' => '𨗒',
'𨗭' => '𨗭',
'邔' => '邔',
'郱' => '郱',
'鄑' => '鄑',
'𨜮' => '𨜮',
'鄛' => '鄛',
'鈸' => '鈸',
'鋗' => '鋗',
'鋘' => '鋘',
'鉼' => '鉼',
'鏹' => '鏹',
'鐕' => '鐕',
'𨯺' => '𨯺',
'開' => '開',
'䦕' => '䦕',
'閷' => '閷',
'𨵷' => '𨵷',
'䧦' => '䧦',
'雃' => '雃',
'嶲' => '嶲',
'霣' => '霣',
'𩅅' => '𩅅',
'𩈚' => '𩈚',
'䩮' => '䩮',
'䩶' => '䩶',
'韠' => '韠',
'𩐊' => '𩐊',
'䪲' => '䪲',
'𩒖' => '𩒖',
'頋' => '頋',
'頋' => '頋',
'頩' => '頩',
'𩖶' => '𩖶',
'飢' => '飢',
'䬳' => '䬳',
'餩' => '餩',
'馧' => '馧',
'駂' => '駂',
'駾' => '駾',
'䯎' => '䯎',
'𩬰' => '𩬰',
'鬒' => '鬒',
'鱀' => '鱀',
'鳽' => '鳽',
'䳎' => '䳎',
'䳭' => '䳭',
'鵧' => '鵧',
'𪃎' => '𪃎',
'䳸' => '䳸',
'𪄅' => '𪄅',
'𪈎' => '𪈎',
'𪊑' => '𪊑',
'麻' => '麻',
'䵖' => '䵖',
'黹' => '黹',
'黾' => '黾',
'鼅' => '鼅',
'鼏' => '鼏',
'鼖' => '鼖',
'鼻' => '鼻',
'𪘀' => '𪘀',
);
<?php
return array (
'À' => 'À',
'Á' => 'Á',
'Â' => 'Â',
'Ã' => 'Ã',
'Ä' => 'Ä',
'Å' => 'Å',
'Ç' => 'Ç',
'È' => 'È',
'É' => 'É',
'Ê' => 'Ê',
'Ë' => 'Ë',
'Ì' => 'Ì',
'Í' => 'Í',
'Î' => 'Î',
'Ï' => 'Ï',
'Ñ' => 'Ñ',
'Ò' => 'Ò',
'Ó' => 'Ó',
'Ô' => 'Ô',
'Õ' => 'Õ',
'Ö' => 'Ö',
'Ù' => 'Ù',
'Ú' => 'Ú',
'Û' => 'Û',
'Ü' => 'Ü',
'Ý' => 'Ý',
'à' => 'à',
'á' => 'á',
'â' => 'â',
'ã' => 'ã',
'ä' => 'ä',
'å' => 'å',
'ç' => 'ç',
'è' => 'è',
'é' => 'é',
'ê' => 'ê',
'ë' => 'ë',
'ì' => 'ì',
'í' => 'í',
'î' => 'î',
'ï' => 'ï',
'ñ' => 'ñ',
'ò' => 'ò',
'ó' => 'ó',
'ô' => 'ô',
'õ' => 'õ',
'ö' => 'ö',
'ù' => 'ù',
'ú' => 'ú',
'û' => 'û',
'ü' => 'ü',
'ý' => 'ý',
'ÿ' => 'ÿ',
'Ā' => 'Ā',
'ā' => 'ā',
'Ă' => 'Ă',
'ă' => 'ă',
'Ą' => 'Ą',
'ą' => 'ą',
'Ć' => 'Ć',
'ć' => 'ć',
'Ĉ' => 'Ĉ',
'ĉ' => 'ĉ',
'Ċ' => 'Ċ',
'ċ' => 'ċ',
'Č' => 'Č',
'č' => 'č',
'Ď' => 'Ď',
'ď' => 'ď',
'Ē' => 'Ē',
'ē' => 'ē',
'Ĕ' => 'Ĕ',
'ĕ' => 'ĕ',
'Ė' => 'Ė',
'ė' => 'ė',
'Ę' => 'Ę',
'ę' => 'ę',
'Ě' => 'Ě',
'ě' => 'ě',
'Ĝ' => 'Ĝ',
'ĝ' => 'ĝ',
'Ğ' => 'Ğ',
'ğ' => 'ğ',
'Ġ' => 'Ġ',
'ġ' => 'ġ',
'Ģ' => 'Ģ',
'ģ' => 'ģ',
'Ĥ' => 'Ĥ',
'ĥ' => 'ĥ',
'Ĩ' => 'Ĩ',
'ĩ' => 'ĩ',
'Ī' => 'Ī',
'ī' => 'ī',
'Ĭ' => 'Ĭ',
'ĭ' => 'ĭ',
'Į' => 'Į',
'į' => 'į',
'İ' => 'İ',
'Ĵ' => 'Ĵ',
'ĵ' => 'ĵ',
'Ķ' => 'Ķ',
'ķ' => 'ķ',
'Ĺ' => 'Ĺ',
'ĺ' => 'ĺ',
'Ļ' => 'Ļ',
'ļ' => 'ļ',
'Ľ' => 'Ľ',
'ľ' => 'ľ',
'Ń' => 'Ń',
'ń' => 'ń',
'Ņ' => 'Ņ',
'ņ' => 'ņ',
'Ň' => 'Ň',
'ň' => 'ň',
'Ō' => 'Ō',
'ō' => 'ō',
'Ŏ' => 'Ŏ',
'ŏ' => 'ŏ',
'Ő' => 'Ő',
'ő' => 'ő',
'Ŕ' => 'Ŕ',
'ŕ' => 'ŕ',
'Ŗ' => 'Ŗ',
'ŗ' => 'ŗ',
'Ř' => 'Ř',
'ř' => 'ř',
'Ś' => 'Ś',
'ś' => 'ś',
'Ŝ' => 'Ŝ',
'ŝ' => 'ŝ',
'Ş' => 'Ş',
'ş' => 'ş',
'Š' => 'Š',
'š' => 'š',
'Ţ' => 'Ţ',
'ţ' => 'ţ',
'Ť' => 'Ť',
'ť' => 'ť',
'Ũ' => 'Ũ',
'ũ' => 'ũ',
'Ū' => 'Ū',
'ū' => 'ū',
'Ŭ' => 'Ŭ',
'ŭ' => 'ŭ',
'Ů' => 'Ů',
'ů' => 'ů',
'Ű' => 'Ű',
'ű' => 'ű',
'Ų' => 'Ų',
'ų' => 'ų',
'Ŵ' => 'Ŵ',
'ŵ' => 'ŵ',
'Ŷ' => 'Ŷ',
'ŷ' => 'ŷ',
'Ÿ' => 'Ÿ',
'Ź' => 'Ź',
'ź' => 'ź',
'Ż' => 'Ż',
'ż' => 'ż',
'Ž' => 'Ž',
'ž' => 'ž',
'Ơ' => 'Ơ',
'ơ' => 'ơ',
'Ư' => 'Ư',
'ư' => 'ư',
'Ǎ' => 'Ǎ',
'ǎ' => 'ǎ',
'Ǐ' => 'Ǐ',
'ǐ' => 'ǐ',
'Ǒ' => 'Ǒ',
'ǒ' => 'ǒ',
'Ǔ' => 'Ǔ',
'ǔ' => 'ǔ',
'Ǖ' => 'Ǖ',
'ǖ' => 'ǖ',
'Ǘ' => 'Ǘ',
'ǘ' => 'ǘ',
'Ǚ' => 'Ǚ',
'ǚ' => 'ǚ',
'Ǜ' => 'Ǜ',
'ǜ' => 'ǜ',
'Ǟ' => 'Ǟ',
'ǟ' => 'ǟ',
'Ǡ' => 'Ǡ',
'ǡ' => 'ǡ',
'Ǣ' => 'Ǣ',
'ǣ' => 'ǣ',
'Ǧ' => 'Ǧ',
'ǧ' => 'ǧ',
'Ǩ' => 'Ǩ',
'ǩ' => 'ǩ',
'Ǫ' => 'Ǫ',
'ǫ' => 'ǫ',
'Ǭ' => 'Ǭ',
'ǭ' => 'ǭ',
'Ǯ' => 'Ǯ',
'ǯ' => 'ǯ',
'ǰ' => 'ǰ',
'Ǵ' => 'Ǵ',
'ǵ' => 'ǵ',
'Ǹ' => 'Ǹ',
'ǹ' => 'ǹ',
'Ǻ' => 'Ǻ',
'ǻ' => 'ǻ',
'Ǽ' => 'Ǽ',
'ǽ' => 'ǽ',
'Ǿ' => 'Ǿ',
'ǿ' => 'ǿ',
'Ȁ' => 'Ȁ',
'ȁ' => 'ȁ',
'Ȃ' => 'Ȃ',
'ȃ' => 'ȃ',
'Ȅ' => 'Ȅ',
'ȅ' => 'ȅ',
'Ȇ' => 'Ȇ',
'ȇ' => 'ȇ',
'Ȉ' => 'Ȉ',
'ȉ' => 'ȉ',
'Ȋ' => 'Ȋ',
'ȋ' => 'ȋ',
'Ȍ' => 'Ȍ',
'ȍ' => 'ȍ',
'Ȏ' => 'Ȏ',
'ȏ' => 'ȏ',
'Ȑ' => 'Ȑ',
'ȑ' => 'ȑ',
'Ȓ' => 'Ȓ',
'ȓ' => 'ȓ',
'Ȕ' => 'Ȕ',
'ȕ' => 'ȕ',
'Ȗ' => 'Ȗ',
'ȗ' => 'ȗ',
'Ș' => 'Ș',
'ș' => 'ș',
'Ț' => 'Ț',
'ț' => 'ț',
'Ȟ' => 'Ȟ',
'ȟ' => 'ȟ',
'Ȧ' => 'Ȧ',
'ȧ' => 'ȧ',
'Ȩ' => 'Ȩ',
'ȩ' => 'ȩ',
'Ȫ' => 'Ȫ',
'ȫ' => 'ȫ',
'Ȭ' => 'Ȭ',
'ȭ' => 'ȭ',
'Ȯ' => 'Ȯ',
'ȯ' => 'ȯ',
'Ȱ' => 'Ȱ',
'ȱ' => 'ȱ',
'Ȳ' => 'Ȳ',
'ȳ' => 'ȳ',
'΅' => '΅',
'Ά' => 'Ά',
'Έ' => 'Έ',
'Ή' => 'Ή',
'Ί' => 'Ί',
'Ό' => 'Ό',
'Ύ' => 'Ύ',
'Ώ' => 'Ώ',
'ΐ' => 'ΐ',
'Ϊ' => 'Ϊ',
'Ϋ' => 'Ϋ',
'ά' => 'ά',
'έ' => 'έ',
'ή' => 'ή',
'ί' => 'ί',
'ΰ' => 'ΰ',
'ϊ' => 'ϊ',
'ϋ' => 'ϋ',
'ό' => 'ό',
'ύ' => 'ύ',
'ώ' => 'ώ',
'ϓ' => 'ϓ',
'ϔ' => 'ϔ',
'Ѐ' => 'Ѐ',
'Ё' => 'Ё',
'Ѓ' => 'Ѓ',
'Ї' => 'Ї',
'Ќ' => 'Ќ',
'Ѝ' => 'Ѝ',
'Ў' => 'Ў',
'Й' => 'Й',
'й' => 'й',
'ѐ' => 'ѐ',
'ё' => 'ё',
'ѓ' => 'ѓ',
'ї' => 'ї',
'ќ' => 'ќ',
'ѝ' => 'ѝ',
'ў' => 'ў',
'Ѷ' => 'Ѷ',
'ѷ' => 'ѷ',
'Ӂ' => 'Ӂ',
'ӂ' => 'ӂ',
'Ӑ' => 'Ӑ',
'ӑ' => 'ӑ',
'Ӓ' => 'Ӓ',
'ӓ' => 'ӓ',
'Ӗ' => 'Ӗ',
'ӗ' => 'ӗ',
'Ӛ' => 'Ӛ',
'ӛ' => 'ӛ',
'Ӝ' => 'Ӝ',
'ӝ' => 'ӝ',
'Ӟ' => 'Ӟ',
'ӟ' => 'ӟ',
'Ӣ' => 'Ӣ',
'ӣ' => 'ӣ',
'Ӥ' => 'Ӥ',
'ӥ' => 'ӥ',
'Ӧ' => 'Ӧ',
'ӧ' => 'ӧ',
'Ӫ' => 'Ӫ',
'ӫ' => 'ӫ',
'Ӭ' => 'Ӭ',
'ӭ' => 'ӭ',
'Ӯ' => 'Ӯ',
'ӯ' => 'ӯ',
'Ӱ' => 'Ӱ',
'ӱ' => 'ӱ',
'Ӳ' => 'Ӳ',
'ӳ' => 'ӳ',
'Ӵ' => 'Ӵ',
'ӵ' => 'ӵ',
'Ӹ' => 'Ӹ',
'ӹ' => 'ӹ',
'آ' => 'آ',
'أ' => 'أ',
'ؤ' => 'ؤ',
'إ' => 'إ',
'ئ' => 'ئ',
'ۀ' => 'ۀ',
'ۂ' => 'ۂ',
'ۓ' => 'ۓ',
'ऩ' => 'ऩ',
'ऱ' => 'ऱ',
'ऴ' => 'ऴ',
'ো' => 'ো',
'ৌ' => 'ৌ',
'ୈ' => 'ୈ',
'ୋ' => 'ୋ',
'ୌ' => 'ୌ',
'ஔ' => 'ஔ',
'ொ' => 'ொ',
'ோ' => 'ோ',
'ௌ' => 'ௌ',
'ై' => 'ై',
'ೀ' => 'ೀ',
'ೇ' => 'ೇ',
'ೈ' => 'ೈ',
'ೊ' => 'ೊ',
'ೋ' => 'ೋ',
'ൊ' => 'ൊ',
'ോ' => 'ോ',
'ൌ' => 'ൌ',
'ේ' => 'ේ',
'ො' => 'ො',
'ෝ' => 'ෝ',
'ෞ' => 'ෞ',
'ဦ' => 'ဦ',
'ᬆ' => 'ᬆ',
'ᬈ' => 'ᬈ',
'ᬊ' => 'ᬊ',
'ᬌ' => 'ᬌ',
'ᬎ' => 'ᬎ',
'ᬒ' => 'ᬒ',
'ᬻ' => 'ᬻ',
'ᬽ' => 'ᬽ',
'ᭀ' => 'ᭀ',
'ᭁ' => 'ᭁ',
'ᭃ' => 'ᭃ',
'Ḁ' => 'Ḁ',
'ḁ' => 'ḁ',
'Ḃ' => 'Ḃ',
'ḃ' => 'ḃ',
'Ḅ' => 'Ḅ',
'ḅ' => 'ḅ',
'Ḇ' => 'Ḇ',
'ḇ' => 'ḇ',
'Ḉ' => 'Ḉ',
'ḉ' => 'ḉ',
'Ḋ' => 'Ḋ',
'ḋ' => 'ḋ',
'Ḍ' => 'Ḍ',
'ḍ' => 'ḍ',
'Ḏ' => 'Ḏ',
'ḏ' => 'ḏ',
'Ḑ' => 'Ḑ',
'ḑ' => 'ḑ',
'Ḓ' => 'Ḓ',
'ḓ' => 'ḓ',
'Ḕ' => 'Ḕ',
'ḕ' => 'ḕ',
'Ḗ' => 'Ḗ',
'ḗ' => 'ḗ',
'Ḙ' => 'Ḙ',
'ḙ' => 'ḙ',
'Ḛ' => 'Ḛ',
'ḛ' => 'ḛ',
'Ḝ' => 'Ḝ',
'ḝ' => 'ḝ',
'Ḟ' => 'Ḟ',
'ḟ' => 'ḟ',
'Ḡ' => 'Ḡ',
'ḡ' => 'ḡ',
'Ḣ' => 'Ḣ',
'ḣ' => 'ḣ',
'Ḥ' => 'Ḥ',
'ḥ' => 'ḥ',
'Ḧ' => 'Ḧ',
'ḧ' => 'ḧ',
'Ḩ' => 'Ḩ',
'ḩ' => 'ḩ',
'Ḫ' => 'Ḫ',
'ḫ' => 'ḫ',
'Ḭ' => 'Ḭ',
'ḭ' => 'ḭ',
'Ḯ' => 'Ḯ',
'ḯ' => 'ḯ',
'Ḱ' => 'Ḱ',
'ḱ' => 'ḱ',
'Ḳ' => 'Ḳ',
'ḳ' => 'ḳ',
'Ḵ' => 'Ḵ',
'ḵ' => 'ḵ',
'Ḷ' => 'Ḷ',
'ḷ' => 'ḷ',
'Ḹ' => 'Ḹ',
'ḹ' => 'ḹ',
'Ḻ' => 'Ḻ',
'ḻ' => 'ḻ',
'Ḽ' => 'Ḽ',
'ḽ' => 'ḽ',
'Ḿ' => 'Ḿ',
'ḿ' => 'ḿ',
'Ṁ' => 'Ṁ',
'ṁ' => 'ṁ',
'Ṃ' => 'Ṃ',
'ṃ' => 'ṃ',
'Ṅ' => 'Ṅ',
'ṅ' => 'ṅ',
'Ṇ' => 'Ṇ',
'ṇ' => 'ṇ',
'Ṉ' => 'Ṉ',
'ṉ' => 'ṉ',
'Ṋ' => 'Ṋ',
'ṋ' => 'ṋ',
'Ṍ' => 'Ṍ',
'ṍ' => 'ṍ',
'Ṏ' => 'Ṏ',
'ṏ' => 'ṏ',
'Ṑ' => 'Ṑ',
'ṑ' => 'ṑ',
'Ṓ' => 'Ṓ',
'ṓ' => 'ṓ',
'Ṕ' => 'Ṕ',
'ṕ' => 'ṕ',
'Ṗ' => 'Ṗ',
'ṗ' => 'ṗ',
'Ṙ' => 'Ṙ',
'ṙ' => 'ṙ',
'Ṛ' => 'Ṛ',
'ṛ' => 'ṛ',
'Ṝ' => 'Ṝ',
'ṝ' => 'ṝ',
'Ṟ' => 'Ṟ',
'ṟ' => 'ṟ',
'Ṡ' => 'Ṡ',
'ṡ' => 'ṡ',
'Ṣ' => 'Ṣ',
'ṣ' => 'ṣ',
'Ṥ' => 'Ṥ',
'ṥ' => 'ṥ',
'Ṧ' => 'Ṧ',
'ṧ' => 'ṧ',
'Ṩ' => 'Ṩ',
'ṩ' => 'ṩ',
'Ṫ' => 'Ṫ',
'ṫ' => 'ṫ',
'Ṭ' => 'Ṭ',
'ṭ' => 'ṭ',
'Ṯ' => 'Ṯ',
'ṯ' => 'ṯ',
'Ṱ' => 'Ṱ',
'ṱ' => 'ṱ',
'Ṳ' => 'Ṳ',
'ṳ' => 'ṳ',
'Ṵ' => 'Ṵ',
'ṵ' => 'ṵ',
'Ṷ' => 'Ṷ',
'ṷ' => 'ṷ',
'Ṹ' => 'Ṹ',
'ṹ' => 'ṹ',
'Ṻ' => 'Ṻ',
'ṻ' => 'ṻ',
'Ṽ' => 'Ṽ',
'ṽ' => 'ṽ',
'Ṿ' => 'Ṿ',
'ṿ' => 'ṿ',
'Ẁ' => 'Ẁ',
'ẁ' => 'ẁ',
'Ẃ' => 'Ẃ',
'ẃ' => 'ẃ',
'Ẅ' => 'Ẅ',
'ẅ' => 'ẅ',
'Ẇ' => 'Ẇ',
'ẇ' => 'ẇ',
'Ẉ' => 'Ẉ',
'ẉ' => 'ẉ',
'Ẋ' => 'Ẋ',
'ẋ' => 'ẋ',
'Ẍ' => 'Ẍ',
'ẍ' => 'ẍ',
'Ẏ' => 'Ẏ',
'ẏ' => 'ẏ',
'Ẑ' => 'Ẑ',
'ẑ' => 'ẑ',
'Ẓ' => 'Ẓ',
'ẓ' => 'ẓ',
'Ẕ' => 'Ẕ',
'ẕ' => 'ẕ',
'ẖ' => 'ẖ',
'ẗ' => 'ẗ',
'ẘ' => 'ẘ',
'ẙ' => 'ẙ',
'ẛ' => 'ẛ',
'Ạ' => 'Ạ',
'ạ' => 'ạ',
'Ả' => 'Ả',
'ả' => 'ả',
'Ấ' => 'Ấ',
'ấ' => 'ấ',
'Ầ' => 'Ầ',
'ầ' => 'ầ',
'Ẩ' => 'Ẩ',
'ẩ' => 'ẩ',
'Ẫ' => 'Ẫ',
'ẫ' => 'ẫ',
'Ậ' => 'Ậ',
'ậ' => 'ậ',
'Ắ' => 'Ắ',
'ắ' => 'ắ',
'Ằ' => 'Ằ',
'ằ' => 'ằ',
'Ẳ' => 'Ẳ',
'ẳ' => 'ẳ',
'Ẵ' => 'Ẵ',
'ẵ' => 'ẵ',
'Ặ' => 'Ặ',
'ặ' => 'ặ',
'Ẹ' => 'Ẹ',
'ẹ' => 'ẹ',
'Ẻ' => 'Ẻ',
'ẻ' => 'ẻ',
'Ẽ' => 'Ẽ',
'ẽ' => 'ẽ',
'Ế' => 'Ế',
'ế' => 'ế',
'Ề' => 'Ề',
'ề' => 'ề',
'Ể' => 'Ể',
'ể' => 'ể',
'Ễ' => 'Ễ',
'ễ' => 'ễ',
'Ệ' => 'Ệ',
'ệ' => 'ệ',
'Ỉ' => 'Ỉ',
'ỉ' => 'ỉ',
'Ị' => 'Ị',
'ị' => 'ị',
'Ọ' => 'Ọ',
'ọ' => 'ọ',
'Ỏ' => 'Ỏ',
'ỏ' => 'ỏ',
'Ố' => 'Ố',
'ố' => 'ố',
'Ồ' => 'Ồ',
'ồ' => 'ồ',
'Ổ' => 'Ổ',
'ổ' => 'ổ',
'Ỗ' => 'Ỗ',
'ỗ' => 'ỗ',
'Ộ' => 'Ộ',
'ộ' => 'ộ',
'Ớ' => 'Ớ',
'ớ' => 'ớ',
'Ờ' => 'Ờ',
'ờ' => 'ờ',
'Ở' => 'Ở',
'ở' => 'ở',
'Ỡ' => 'Ỡ',
'ỡ' => 'ỡ',
'Ợ' => 'Ợ',
'ợ' => 'ợ',
'Ụ' => 'Ụ',
'ụ' => 'ụ',
'Ủ' => 'Ủ',
'ủ' => 'ủ',
'Ứ' => 'Ứ',
'ứ' => 'ứ',
'Ừ' => 'Ừ',
'ừ' => 'ừ',
'Ử' => 'Ử',
'ử' => 'ử',
'Ữ' => 'Ữ',
'ữ' => 'ữ',
'Ự' => 'Ự',
'ự' => 'ự',
'Ỳ' => 'Ỳ',
'ỳ' => 'ỳ',
'Ỵ' => 'Ỵ',
'ỵ' => 'ỵ',
'Ỷ' => 'Ỷ',
'ỷ' => 'ỷ',
'Ỹ' => 'Ỹ',
'ỹ' => 'ỹ',
'ἀ' => 'ἀ',
'ἁ' => 'ἁ',
'ἂ' => 'ἂ',
'ἃ' => 'ἃ',
'ἄ' => 'ἄ',
'ἅ' => 'ἅ',
'ἆ' => 'ἆ',
'ἇ' => 'ἇ',
'Ἀ' => 'Ἀ',
'Ἁ' => 'Ἁ',
'Ἂ' => 'Ἂ',
'Ἃ' => 'Ἃ',
'Ἄ' => 'Ἄ',
'Ἅ' => 'Ἅ',
'Ἆ' => 'Ἆ',
'Ἇ' => 'Ἇ',
'ἐ' => 'ἐ',
'ἑ' => 'ἑ',
'ἒ' => 'ἒ',
'ἓ' => 'ἓ',
'ἔ' => 'ἔ',
'ἕ' => 'ἕ',
'Ἐ' => 'Ἐ',
'Ἑ' => 'Ἑ',
'Ἒ' => 'Ἒ',
'Ἓ' => 'Ἓ',
'Ἔ' => 'Ἔ',
'Ἕ' => 'Ἕ',
'ἠ' => 'ἠ',
'ἡ' => 'ἡ',
'ἢ' => 'ἢ',
'ἣ' => 'ἣ',
'ἤ' => 'ἤ',
'ἥ' => 'ἥ',
'ἦ' => 'ἦ',
'ἧ' => 'ἧ',
'Ἠ' => 'Ἠ',
'Ἡ' => 'Ἡ',
'Ἢ' => 'Ἢ',
'Ἣ' => 'Ἣ',
'Ἤ' => 'Ἤ',
'Ἥ' => 'Ἥ',
'Ἦ' => 'Ἦ',
'Ἧ' => 'Ἧ',
'ἰ' => 'ἰ',
'ἱ' => 'ἱ',
'ἲ' => 'ἲ',
'ἳ' => 'ἳ',
'ἴ' => 'ἴ',
'ἵ' => 'ἵ',
'ἶ' => 'ἶ',
'ἷ' => 'ἷ',
'Ἰ' => 'Ἰ',
'Ἱ' => 'Ἱ',
'Ἲ' => 'Ἲ',
'Ἳ' => 'Ἳ',
'Ἴ' => 'Ἴ',
'Ἵ' => 'Ἵ',
'Ἶ' => 'Ἶ',
'Ἷ' => 'Ἷ',
'ὀ' => 'ὀ',
'ὁ' => 'ὁ',
'ὂ' => 'ὂ',
'ὃ' => 'ὃ',
'ὄ' => 'ὄ',
'ὅ' => 'ὅ',
'Ὀ' => 'Ὀ',
'Ὁ' => 'Ὁ',
'Ὂ' => 'Ὂ',
'Ὃ' => 'Ὃ',
'Ὄ' => 'Ὄ',
'Ὅ' => 'Ὅ',
'ὐ' => 'ὐ',
'ὑ' => 'ὑ',
'ὒ' => 'ὒ',
'ὓ' => 'ὓ',
'ὔ' => 'ὔ',
'ὕ' => 'ὕ',
'ὖ' => 'ὖ',
'ὗ' => 'ὗ',
'Ὑ' => 'Ὑ',
'Ὓ' => 'Ὓ',
'Ὕ' => 'Ὕ',
'Ὗ' => 'Ὗ',
'ὠ' => 'ὠ',
'ὡ' => 'ὡ',
'ὢ' => 'ὢ',
'ὣ' => 'ὣ',
'ὤ' => 'ὤ',
'ὥ' => 'ὥ',
'ὦ' => 'ὦ',
'ὧ' => 'ὧ',
'Ὠ' => 'Ὠ',
'Ὡ' => 'Ὡ',
'Ὢ' => 'Ὢ',
'Ὣ' => 'Ὣ',
'Ὤ' => 'Ὤ',
'Ὥ' => 'Ὥ',
'Ὦ' => 'Ὦ',
'Ὧ' => 'Ὧ',
'ὰ' => 'ὰ',
'ὲ' => 'ὲ',
'ὴ' => 'ὴ',
'ὶ' => 'ὶ',
'ὸ' => 'ὸ',
'ὺ' => 'ὺ',
'ὼ' => 'ὼ',
'ᾀ' => 'ᾀ',
'ᾁ' => 'ᾁ',
'ᾂ' => 'ᾂ',
'ᾃ' => 'ᾃ',
'ᾄ' => 'ᾄ',
'ᾅ' => 'ᾅ',
'ᾆ' => 'ᾆ',
'ᾇ' => 'ᾇ',
'ᾈ' => 'ᾈ',
'ᾉ' => 'ᾉ',
'ᾊ' => 'ᾊ',
'ᾋ' => 'ᾋ',
'ᾌ' => 'ᾌ',
'ᾍ' => 'ᾍ',
'ᾎ' => 'ᾎ',
'ᾏ' => 'ᾏ',
'ᾐ' => 'ᾐ',
'ᾑ' => 'ᾑ',
'ᾒ' => 'ᾒ',
'ᾓ' => 'ᾓ',
'ᾔ' => 'ᾔ',
'ᾕ' => 'ᾕ',
'ᾖ' => 'ᾖ',
'ᾗ' => 'ᾗ',
'ᾘ' => 'ᾘ',
'ᾙ' => 'ᾙ',
'ᾚ' => 'ᾚ',
'ᾛ' => 'ᾛ',
'ᾜ' => 'ᾜ',
'ᾝ' => 'ᾝ',
'ᾞ' => 'ᾞ',
'ᾟ' => 'ᾟ',
'ᾠ' => 'ᾠ',
'ᾡ' => 'ᾡ',
'ᾢ' => 'ᾢ',
'ᾣ' => 'ᾣ',
'ᾤ' => 'ᾤ',
'ᾥ' => 'ᾥ',
'ᾦ' => 'ᾦ',
'ᾧ' => 'ᾧ',
'ᾨ' => 'ᾨ',
'ᾩ' => 'ᾩ',
'ᾪ' => 'ᾪ',
'ᾫ' => 'ᾫ',
'ᾬ' => 'ᾬ',
'ᾭ' => 'ᾭ',
'ᾮ' => 'ᾮ',
'ᾯ' => 'ᾯ',
'ᾰ' => 'ᾰ',
'ᾱ' => 'ᾱ',
'ᾲ' => 'ᾲ',
'ᾳ' => 'ᾳ',
'ᾴ' => 'ᾴ',
'ᾶ' => 'ᾶ',
'ᾷ' => 'ᾷ',
'Ᾰ' => 'Ᾰ',
'Ᾱ' => 'Ᾱ',
'Ὰ' => 'Ὰ',
'ᾼ' => 'ᾼ',
'῁' => '῁',
'ῂ' => 'ῂ',
'ῃ' => 'ῃ',
'ῄ' => 'ῄ',
'ῆ' => 'ῆ',
'ῇ' => 'ῇ',
'Ὲ' => 'Ὲ',
'Ὴ' => 'Ὴ',
'ῌ' => 'ῌ',
'῍' => '῍',
'῎' => '῎',
'῏' => '῏',
'ῐ' => 'ῐ',
'ῑ' => 'ῑ',
'ῒ' => 'ῒ',
'ῖ' => 'ῖ',
'ῗ' => 'ῗ',
'Ῐ' => 'Ῐ',
'Ῑ' => 'Ῑ',
'Ὶ' => 'Ὶ',
'῝' => '῝',
'῞' => '῞',
'῟' => '῟',
'ῠ' => 'ῠ',
'ῡ' => 'ῡ',
'ῢ' => 'ῢ',
'ῤ' => 'ῤ',
'ῥ' => 'ῥ',
'ῦ' => 'ῦ',
'ῧ' => 'ῧ',
'Ῠ' => 'Ῠ',
'Ῡ' => 'Ῡ',
'Ὺ' => 'Ὺ',
'Ῥ' => 'Ῥ',
'῭' => '῭',
'ῲ' => 'ῲ',
'ῳ' => 'ῳ',
'ῴ' => 'ῴ',
'ῶ' => 'ῶ',
'ῷ' => 'ῷ',
'Ὸ' => 'Ὸ',
'Ὼ' => 'Ὼ',
'ῼ' => 'ῼ',
'↚' => '↚',
'↛' => '↛',
'↮' => '↮',
'⇍' => '⇍',
'⇎' => '⇎',
'⇏' => '⇏',
'∄' => '∄',
'∉' => '∉',
'∌' => '∌',
'∤' => '∤',
'∦' => '∦',
'≁' => '≁',
'≄' => '≄',
'≇' => '≇',
'≉' => '≉',
'≠' => '≠',
'≢' => '≢',
'≭' => '≭',
'≮' => '≮',
'≯' => '≯',
'≰' => '≰',
'≱' => '≱',
'≴' => '≴',
'≵' => '≵',
'≸' => '≸',
'≹' => '≹',
'⊀' => '⊀',
'⊁' => '⊁',
'⊄' => '⊄',
'⊅' => '⊅',
'⊈' => '⊈',
'⊉' => '⊉',
'⊬' => '⊬',
'⊭' => '⊭',
'⊮' => '⊮',
'⊯' => '⊯',
'⋠' => '⋠',
'⋡' => '⋡',
'⋢' => '⋢',
'⋣' => '⋣',
'⋪' => '⋪',
'⋫' => '⋫',
'⋬' => '⋬',
'⋭' => '⋭',
'が' => 'が',
'ぎ' => 'ぎ',
'ぐ' => 'ぐ',
'げ' => 'げ',
'ご' => 'ご',
'ざ' => 'ざ',
'じ' => 'じ',
'ず' => 'ず',
'ぜ' => 'ぜ',
'ぞ' => 'ぞ',
'だ' => 'だ',
'ぢ' => 'ぢ',
'づ' => 'づ',
'で' => 'で',
'ど' => 'ど',
'ば' => 'ば',
'ぱ' => 'ぱ',
'び' => 'び',
'ぴ' => 'ぴ',
'ぶ' => 'ぶ',
'ぷ' => 'ぷ',
'べ' => 'べ',
'ぺ' => 'ぺ',
'ぼ' => 'ぼ',
'ぽ' => 'ぽ',
'ゔ' => 'ゔ',
'ゞ' => 'ゞ',
'ガ' => 'ガ',
'ギ' => 'ギ',
'グ' => 'グ',
'ゲ' => 'ゲ',
'ゴ' => 'ゴ',
'ザ' => 'ザ',
'ジ' => 'ジ',
'ズ' => 'ズ',
'ゼ' => 'ゼ',
'ゾ' => 'ゾ',
'ダ' => 'ダ',
'ヂ' => 'ヂ',
'ヅ' => 'ヅ',
'デ' => 'デ',
'ド' => 'ド',
'バ' => 'バ',
'パ' => 'パ',
'ビ' => 'ビ',
'ピ' => 'ピ',
'ブ' => 'ブ',
'プ' => 'プ',
'ベ' => 'ベ',
'ペ' => 'ペ',
'ボ' => 'ボ',
'ポ' => 'ポ',
'ヴ' => 'ヴ',
'ヷ' => 'ヷ',
'ヸ' => 'ヸ',
'ヹ' => 'ヹ',
'ヺ' => 'ヺ',
'ヾ' => 'ヾ',
'𑂚' => '𑂚',
'𑂜' => '𑂜',
'𑂫' => '𑂫',
'𑄮' => '𑄮',
'𑄯' => '𑄯',
'𑍋' => '𑍋',
'𑍌' => '𑍌',
'𑒻' => '𑒻',
'𑒼' => '𑒼',
'𑒾' => '𑒾',
'𑖺' => '𑖺',
'𑖻' => '𑖻',
'𑤸' => '𑤸',
);
<?php
return array (
' ' => ' ',
'¨' => ' ̈',
'ª' => 'a',
'¯' => ' ̄',
'²' => '2',
'³' => '3',
'´' => ' ́',
'µ' => 'μ',
'¸' => ' ̧',
'¹' => '1',
'º' => 'o',
'¼' => '14',
'½' => '12',
'¾' => '34',
'IJ' => 'IJ',
'ij' => 'ij',
'Ŀ' => 'L·',
'ŀ' => 'l·',
'ʼn' => 'ʼn',
'ſ' => 's',
'DŽ' => 'DŽ',
'Dž' => 'Dž',
'dž' => 'dž',
'LJ' => 'LJ',
'Lj' => 'Lj',
'lj' => 'lj',
'NJ' => 'NJ',
'Nj' => 'Nj',
'nj' => 'nj',
'DZ' => 'DZ',
'Dz' => 'Dz',
'dz' => 'dz',
'ʰ' => 'h',
'ʱ' => 'ɦ',
'ʲ' => 'j',
'ʳ' => 'r',
'ʴ' => 'ɹ',
'ʵ' => 'ɻ',
'ʶ' => 'ʁ',
'ʷ' => 'w',
'ʸ' => 'y',
'˘' => ' ̆',
'˙' => ' ̇',
'˚' => ' ̊',
'˛' => ' ̨',
'˜' => ' ̃',
'˝' => ' ̋',
'ˠ' => 'ɣ',
'ˡ' => 'l',
'ˢ' => 's',
'ˣ' => 'x',
'ˤ' => 'ʕ',
'ͺ' => ' ͅ',
'΄' => ' ́',
'΅' => ' ̈́',
'ϐ' => 'β',
'ϑ' => 'θ',
'ϒ' => 'Υ',
'ϓ' => 'Ύ',
'ϔ' => 'Ϋ',
'ϕ' => 'φ',
'ϖ' => 'π',
'ϰ' => 'κ',
'ϱ' => 'ρ',
'ϲ' => 'ς',
'ϴ' => 'Θ',
'ϵ' => 'ε',
'Ϲ' => 'Σ',
'և' => 'եւ',
'ٵ' => 'اٴ',
'ٶ' => 'وٴ',
'ٷ' => 'ۇٴ',
'ٸ' => 'يٴ',
'ำ' => 'ํา',
'ຳ' => 'ໍາ',
'ໜ' => 'ຫນ',
'ໝ' => 'ຫມ',
'༌' => '་',
'ཷ' => 'ྲཱྀ',
'ཹ' => 'ླཱྀ',
'ჼ' => 'ნ',
'ᴬ' => 'A',
'ᴭ' => 'Æ',
'ᴮ' => 'B',
'ᴰ' => 'D',
'ᴱ' => 'E',
'ᴲ' => 'Ǝ',
'ᴳ' => 'G',
'ᴴ' => 'H',
'ᴵ' => 'I',
'ᴶ' => 'J',
'ᴷ' => 'K',
'ᴸ' => 'L',
'ᴹ' => 'M',
'ᴺ' => 'N',
'ᴼ' => 'O',
'ᴽ' => 'Ȣ',
'ᴾ' => 'P',
'ᴿ' => 'R',
'ᵀ' => 'T',
'ᵁ' => 'U',
'ᵂ' => 'W',
'ᵃ' => 'a',
'ᵄ' => 'ɐ',
'ᵅ' => 'ɑ',
'ᵆ' => 'ᴂ',
'ᵇ' => 'b',
'ᵈ' => 'd',
'ᵉ' => 'e',
'ᵊ' => 'ə',
'ᵋ' => 'ɛ',
'ᵌ' => 'ɜ',
'ᵍ' => 'g',
'ᵏ' => 'k',
'ᵐ' => 'm',
'ᵑ' => 'ŋ',
'ᵒ' => 'o',
'ᵓ' => 'ɔ',
'ᵔ' => 'ᴖ',
'ᵕ' => 'ᴗ',
'ᵖ' => 'p',
'ᵗ' => 't',
'ᵘ' => 'u',
'ᵙ' => 'ᴝ',
'ᵚ' => 'ɯ',
'ᵛ' => 'v',
'ᵜ' => 'ᴥ',
'ᵝ' => 'β',
'ᵞ' => 'γ',
'ᵟ' => 'δ',
'ᵠ' => 'φ',
'ᵡ' => 'χ',
'ᵢ' => 'i',
'ᵣ' => 'r',
'ᵤ' => 'u',
'ᵥ' => 'v',
'ᵦ' => 'β',
'ᵧ' => 'γ',
'ᵨ' => 'ρ',
'ᵩ' => 'φ',
'ᵪ' => 'χ',
'ᵸ' => 'н',
'ᶛ' => 'ɒ',
'ᶜ' => 'c',
'ᶝ' => 'ɕ',
'ᶞ' => 'ð',
'ᶟ' => 'ɜ',
'ᶠ' => 'f',
'ᶡ' => 'ɟ',
'ᶢ' => 'ɡ',
'ᶣ' => 'ɥ',
'ᶤ' => 'ɨ',
'ᶥ' => 'ɩ',
'ᶦ' => 'ɪ',
'ᶧ' => 'ᵻ',
'ᶨ' => 'ʝ',
'ᶩ' => 'ɭ',
'ᶪ' => 'ᶅ',
'ᶫ' => 'ʟ',
'ᶬ' => 'ɱ',
'ᶭ' => 'ɰ',
'ᶮ' => 'ɲ',
'ᶯ' => 'ɳ',
'ᶰ' => 'ɴ',
'ᶱ' => 'ɵ',
'ᶲ' => 'ɸ',
'ᶳ' => 'ʂ',
'ᶴ' => 'ʃ',
'ᶵ' => 'ƫ',
'ᶶ' => 'ʉ',
'ᶷ' => 'ʊ',
'ᶸ' => '',
'ᶹ' => 'ʋ',
'ᶺ' => 'ʌ',
'ᶻ' => 'z',
'ᶼ' => 'ʐ',
'ᶽ' => 'ʑ',
'ᶾ' => 'ʒ',
'ᶿ' => 'θ',
'ẚ' => 'aʾ',
'ẛ' => 'ṡ',
'' => ' ̓',
'᾿' => ' ̓',
'' => ' ͂',
'῁' => ' ̈͂',
'῍' => ' ̓̀',
'῎' => ' ̓́',
'῏' => ' ̓͂',
'῝' => ' ̔̀',
'῞' => ' ̔́',
'῟' => ' ̔͂',
'῭' => ' ̈̀',
'΅' => ' ̈́',
'' => ' ́',
'' => ' ̔',
' ' => ' ',
'' => ' ',
'' => ' ',
'' => ' ',
'' => ' ',
'' => ' ',
'' => ' ',
'' => ' ',
'' => ' ',
'' => ' ',
'' => ' ',
'' => '',
'‗' => ' ̳',
'' => '.',
'‥' => '..',
'…' => '...',
'' => ' ',
'″' => '',
'‴' => '',
'‶' => '',
'‷' => '',
'‼' => '!!',
'‾' => ' ̅',
'⁇' => '??',
'⁈' => '?!',
'⁉' => '!?',
'⁗' => '',
'' => ' ',
'⁰' => '0',
'ⁱ' => 'i',
'⁴' => '4',
'⁵' => '5',
'⁶' => '6',
'⁷' => '7',
'⁸' => '8',
'⁹' => '9',
'⁺' => '+',
'⁻' => '',
'⁼' => '=',
'⁽' => '(',
'⁾' => ')',
'ⁿ' => 'n',
'₀' => '0',
'₁' => '1',
'₂' => '2',
'₃' => '3',
'₄' => '4',
'₅' => '5',
'₆' => '6',
'₇' => '7',
'₈' => '8',
'₉' => '9',
'₊' => '+',
'₋' => '',
'₌' => '=',
'₍' => '(',
'₎' => ')',
'ₐ' => 'a',
'ₑ' => 'e',
'ₒ' => 'o',
'ₓ' => 'x',
'ₔ' => 'ə',
'ₕ' => 'h',
'ₖ' => 'k',
'ₗ' => 'l',
'ₘ' => 'm',
'ₙ' => 'n',
'ₚ' => 'p',
'ₛ' => 's',
'ₜ' => 't',
'₨' => 'Rs',
'℀' => 'a/c',
'℁' => 'a/s',
'' => 'C',
'℃' => '°C',
'℅' => 'c/o',
'℆' => 'c/u',
'ℇ' => 'Ɛ',
'℉' => '°F',
'' => 'g',
'' => 'H',
'' => 'H',
'' => 'H',
'' => 'h',
'ℏ' => 'ħ',
'' => 'I',
'' => 'I',
'' => 'L',
'' => 'l',
'' => 'N',
'№' => 'No',
'' => 'P',
'' => 'Q',
'' => 'R',
'' => 'R',
'' => 'R',
'℠' => 'SM',
'℡' => 'TEL',
'™' => 'TM',
'' => 'Z',
'' => 'Z',
'' => 'B',
'' => 'C',
'' => 'e',
'' => 'E',
'' => 'F',
'' => 'M',
'' => 'o',
'ℵ' => 'א',
'ℶ' => 'ב',
'ℷ' => 'ג',
'ℸ' => 'ד',
'' => 'i',
'℻' => 'FAX',
'ℼ' => 'π',
'' => 'γ',
'ℾ' => 'Γ',
'ℿ' => 'Π',
'⅀' => '∑',
'' => 'D',
'' => 'd',
'' => 'e',
'' => 'i',
'' => 'j',
'⅐' => '17',
'⅑' => '19',
'⅒' => '110',
'⅓' => '13',
'⅔' => '23',
'⅕' => '15',
'⅖' => '25',
'⅗' => '35',
'⅘' => '45',
'⅙' => '16',
'⅚' => '56',
'⅛' => '18',
'⅜' => '38',
'⅝' => '58',
'⅞' => '78',
'⅟' => '1',
'' => 'I',
'Ⅱ' => 'II',
'Ⅲ' => 'III',
'Ⅳ' => 'IV',
'' => 'V',
'Ⅵ' => 'VI',
'Ⅶ' => 'VII',
'Ⅷ' => 'VIII',
'Ⅸ' => 'IX',
'' => 'X',
'Ⅺ' => 'XI',
'Ⅻ' => 'XII',
'' => 'L',
'' => 'C',
'' => 'D',
'' => 'M',
'' => 'i',
'ⅱ' => 'ii',
'ⅲ' => 'iii',
'ⅳ' => 'iv',
'' => 'v',
'ⅵ' => 'vi',
'ⅶ' => 'vii',
'ⅷ' => 'viii',
'ⅸ' => 'ix',
'' => 'x',
'ⅺ' => 'xi',
'ⅻ' => 'xii',
'' => 'l',
'' => 'c',
'' => 'd',
'ⅿ' => 'm',
'↉' => '03',
'∬' => '∫∫',
'∭' => '∫∫∫',
'∯' => '∮∮',
'∰' => '∮∮∮',
'①' => '1',
'②' => '2',
'③' => '3',
'④' => '4',
'⑤' => '5',
'⑥' => '6',
'⑦' => '7',
'⑧' => '8',
'⑨' => '9',
'⑩' => '10',
'⑪' => '11',
'⑫' => '12',
'⑬' => '13',
'⑭' => '14',
'⑮' => '15',
'⑯' => '16',
'⑰' => '17',
'⑱' => '18',
'⑲' => '19',
'⑳' => '20',
'⑴' => '(1)',
'⑵' => '(2)',
'⑶' => '(3)',
'⑷' => '(4)',
'⑸' => '(5)',
'⑹' => '(6)',
'⑺' => '(7)',
'⑻' => '(8)',
'⑼' => '(9)',
'⑽' => '(10)',
'⑾' => '(11)',
'⑿' => '(12)',
'⒀' => '(13)',
'⒁' => '(14)',
'⒂' => '(15)',
'⒃' => '(16)',
'⒄' => '(17)',
'⒅' => '(18)',
'⒆' => '(19)',
'⒇' => '(20)',
'⒈' => '1.',
'⒉' => '2.',
'⒊' => '3.',
'⒋' => '4.',
'⒌' => '5.',
'⒍' => '6.',
'⒎' => '7.',
'⒏' => '8.',
'⒐' => '9.',
'⒑' => '10.',
'⒒' => '11.',
'⒓' => '12.',
'⒔' => '13.',
'⒕' => '14.',
'⒖' => '15.',
'⒗' => '16.',
'⒘' => '17.',
'⒙' => '18.',
'⒚' => '19.',
'⒛' => '20.',
'⒜' => '(a)',
'⒝' => '(b)',
'⒞' => '(c)',
'⒟' => '(d)',
'⒠' => '(e)',
'⒡' => '(f)',
'⒢' => '(g)',
'⒣' => '(h)',
'⒤' => '(i)',
'⒥' => '(j)',
'⒦' => '(k)',
'⒧' => '(l)',
'⒨' => '(m)',
'⒩' => '(n)',
'⒪' => '(o)',
'⒫' => '(p)',
'⒬' => '(q)',
'⒭' => '(r)',
'⒮' => '(s)',
'⒯' => '(t)',
'⒰' => '(u)',
'⒱' => '(v)',
'⒲' => '(w)',
'⒳' => '(x)',
'⒴' => '(y)',
'⒵' => '(z)',
'Ⓐ' => 'A',
'Ⓑ' => 'B',
'Ⓒ' => 'C',
'Ⓓ' => 'D',
'Ⓔ' => 'E',
'Ⓕ' => 'F',
'Ⓖ' => 'G',
'Ⓗ' => 'H',
'Ⓘ' => 'I',
'Ⓙ' => 'J',
'Ⓚ' => 'K',
'Ⓛ' => 'L',
'Ⓜ' => 'M',
'Ⓝ' => 'N',
'Ⓞ' => 'O',
'Ⓟ' => 'P',
'Ⓠ' => 'Q',
'Ⓡ' => 'R',
'Ⓢ' => 'S',
'Ⓣ' => 'T',
'Ⓤ' => 'U',
'Ⓥ' => 'V',
'Ⓦ' => 'W',
'Ⓧ' => 'X',
'Ⓨ' => 'Y',
'Ⓩ' => 'Z',
'ⓐ' => 'a',
'ⓑ' => 'b',
'ⓒ' => 'c',
'ⓓ' => 'd',
'ⓔ' => 'e',
'ⓕ' => 'f',
'ⓖ' => 'g',
'ⓗ' => 'h',
'ⓘ' => 'i',
'ⓙ' => 'j',
'ⓚ' => 'k',
'ⓛ' => 'l',
'ⓜ' => 'm',
'ⓝ' => 'n',
'ⓞ' => 'o',
'ⓟ' => 'p',
'ⓠ' => 'q',
'ⓡ' => 'r',
'ⓢ' => 's',
'ⓣ' => 't',
'ⓤ' => 'u',
'ⓥ' => 'v',
'ⓦ' => 'w',
'ⓧ' => 'x',
'ⓨ' => 'y',
'ⓩ' => 'z',
'⓪' => '0',
'⨌' => '∫∫∫∫',
'⩴' => '::=',
'⩵' => '==',
'⩶' => '===',
'ⱼ' => 'j',
'ⱽ' => 'V',
'ⵯ' => 'ⵡ',
'⺟' => '母',
'⻳' => '龟',
'⼀' => '一',
'⼁' => '丨',
'' => '',
'' => '丿',
'⼄' => '乙',
'⼅' => '亅',
'⼆' => '二',
'⼇' => '亠',
'⼈' => '人',
'⼉' => '儿',
'⼊' => '入',
'⼋' => '八',
'⼌' => '冂',
'⼍' => '冖',
'⼎' => '冫',
'⼏' => '几',
'⼐' => '凵',
'⼑' => '刀',
'⼒' => '力',
'⼓' => '勹',
'⼔' => '匕',
'⼕' => '匚',
'⼖' => '匸',
'⼗' => '十',
'⼘' => '卜',
'⼙' => '卩',
'⼚' => '厂',
'⼛' => '厶',
'⼜' => '又',
'⼝' => '口',
'⼞' => '囗',
'⼟' => '土',
'⼠' => '士',
'⼡' => '夂',
'⼢' => '夊',
'⼣' => '夕',
'⼤' => '大',
'⼥' => '女',
'⼦' => '子',
'⼧' => '宀',
'⼨' => '寸',
'⼩' => '小',
'⼪' => '尢',
'⼫' => '尸',
'⼬' => '屮',
'⼭' => '山',
'⼮' => '巛',
'⼯' => '工',
'⼰' => '己',
'⼱' => '巾',
'⼲' => '干',
'⼳' => '幺',
'⼴' => '广',
'⼵' => '廴',
'⼶' => '廾',
'⼷' => '弋',
'⼸' => '弓',
'⼹' => '彐',
'⼺' => '彡',
'⼻' => '彳',
'⼼' => '心',
'⼽' => '戈',
'⼾' => '戶',
'⼿' => '手',
'⽀' => '支',
'⽁' => '攴',
'⽂' => '文',
'⽃' => '斗',
'⽄' => '斤',
'⽅' => '方',
'⽆' => '无',
'⽇' => '日',
'⽈' => '曰',
'⽉' => '月',
'⽊' => '木',
'⽋' => '欠',
'⽌' => '止',
'⽍' => '歹',
'⽎' => '殳',
'⽏' => '毋',
'⽐' => '比',
'⽑' => '毛',
'⽒' => '氏',
'⽓' => '气',
'⽔' => '水',
'⽕' => '火',
'⽖' => '爪',
'⽗' => '父',
'⽘' => '爻',
'⽙' => '爿',
'⽚' => '片',
'⽛' => '牙',
'⽜' => '牛',
'⽝' => '犬',
'⽞' => '玄',
'⽟' => '玉',
'⽠' => '瓜',
'⽡' => '瓦',
'⽢' => '甘',
'⽣' => '生',
'⽤' => '用',
'⽥' => '田',
'⽦' => '疋',
'⽧' => '疒',
'⽨' => '癶',
'⽩' => '白',
'⽪' => '皮',
'⽫' => '皿',
'⽬' => '目',
'⽭' => '矛',
'⽮' => '矢',
'⽯' => '石',
'⽰' => '示',
'⽱' => '禸',
'⽲' => '禾',
'⽳' => '穴',
'⽴' => '立',
'⽵' => '竹',
'⽶' => '米',
'⽷' => '糸',
'⽸' => '缶',
'⽹' => '网',
'⽺' => '羊',
'⽻' => '羽',
'⽼' => '老',
'⽽' => '而',
'⽾' => '耒',
'⽿' => '耳',
'⾀' => '聿',
'⾁' => '肉',
'⾂' => '臣',
'⾃' => '自',
'⾄' => '至',
'⾅' => '臼',
'⾆' => '舌',
'⾇' => '舛',
'⾈' => '舟',
'⾉' => '艮',
'⾊' => '色',
'⾋' => '艸',
'⾌' => '虍',
'⾍' => '虫',
'⾎' => '血',
'⾏' => '行',
'⾐' => '衣',
'⾑' => '襾',
'⾒' => '見',
'⾓' => '角',
'⾔' => '言',
'⾕' => '谷',
'⾖' => '豆',
'⾗' => '豕',
'⾘' => '豸',
'⾙' => '貝',
'⾚' => '赤',
'⾛' => '走',
'⾜' => '足',
'⾝' => '身',
'⾞' => '車',
'⾟' => '辛',
'⾠' => '辰',
'⾡' => '辵',
'⾢' => '邑',
'⾣' => '酉',
'⾤' => '釆',
'⾥' => '里',
'⾦' => '金',
'⾧' => '長',
'⾨' => '門',
'⾩' => '阜',
'⾪' => '隶',
'⾫' => '隹',
'⾬' => '雨',
'⾭' => '靑',
'⾮' => '非',
'⾯' => '面',
'⾰' => '革',
'⾱' => '韋',
'⾲' => '韭',
'⾳' => '音',
'⾴' => '頁',
'⾵' => '風',
'⾶' => '飛',
'⾷' => '食',
'⾸' => '首',
'⾹' => '香',
'⾺' => '馬',
'⾻' => '骨',
'⾼' => '高',
'⾽' => '髟',
'⾾' => '鬥',
'⾿' => '鬯',
'⿀' => '鬲',
'⿁' => '鬼',
'⿂' => '魚',
'⿃' => '鳥',
'⿄' => '鹵',
'⿅' => '鹿',
'⿆' => '麥',
'⿇' => '麻',
'⿈' => '黃',
'⿉' => '黍',
'⿊' => '黑',
'⿋' => '黹',
'⿌' => '黽',
'⿍' => '鼎',
'⿎' => '鼓',
'⿏' => '鼠',
'⿐' => '鼻',
'⿑' => '齊',
'⿒' => '齒',
'⿓' => '龍',
'⿔' => '龜',
'⿕' => '龠',
' ' => ' ',
'〶' => '〒',
'〸' => '十',
'〹' => '卄',
'〺' => '卅',
'゛' => ' ゙',
'゜' => ' ゚',
'ゟ' => 'より',
'ヿ' => 'コト',
'ㄱ' => 'ᄀ',
'ㄲ' => 'ᄁ',
'ㄳ' => 'ᆪ',
'ㄴ' => 'ᄂ',
'ㄵ' => 'ᆬ',
'ㄶ' => 'ᆭ',
'ㄷ' => 'ᄃ',
'ㄸ' => 'ᄄ',
'ㄹ' => 'ᄅ',
'ㄺ' => 'ᆰ',
'ㄻ' => 'ᆱ',
'ㄼ' => 'ᆲ',
'ㄽ' => 'ᆳ',
'ㄾ' => 'ᆴ',
'ㄿ' => 'ᆵ',
'ㅀ' => 'ᄚ',
'ㅁ' => 'ᄆ',
'ㅂ' => 'ᄇ',
'ㅃ' => 'ᄈ',
'ㅄ' => 'ᄡ',
'ㅅ' => 'ᄉ',
'ㅆ' => 'ᄊ',
'ㅇ' => 'ᄋ',
'ㅈ' => 'ᄌ',
'ㅉ' => 'ᄍ',
'ㅊ' => 'ᄎ',
'ㅋ' => 'ᄏ',
'ㅌ' => 'ᄐ',
'ㅍ' => 'ᄑ',
'ㅎ' => 'ᄒ',
'ㅏ' => 'ᅡ',
'ㅐ' => 'ᅢ',
'ㅑ' => 'ᅣ',
'ㅒ' => 'ᅤ',
'ㅓ' => 'ᅥ',
'ㅔ' => 'ᅦ',
'ㅕ' => 'ᅧ',
'ㅖ' => 'ᅨ',
'ㅗ' => 'ᅩ',
'ㅘ' => 'ᅪ',
'ㅙ' => 'ᅫ',
'ㅚ' => 'ᅬ',
'ㅛ' => 'ᅭ',
'ㅜ' => 'ᅮ',
'ㅝ' => 'ᅯ',
'ㅞ' => 'ᅰ',
'ㅟ' => 'ᅱ',
'ㅠ' => 'ᅲ',
'ㅡ' => 'ᅳ',
'ㅢ' => 'ᅴ',
'ㅣ' => 'ᅵ',
'' => '',
'ㅥ' => 'ᄔ',
'ㅦ' => 'ᄕ',
'ㅧ' => 'ᇇ',
'ㅨ' => 'ᇈ',
'ㅩ' => 'ᇌ',
'ㅪ' => 'ᇎ',
'ㅫ' => 'ᇓ',
'ㅬ' => 'ᇗ',
'ㅭ' => 'ᇙ',
'ㅮ' => 'ᄜ',
'ㅯ' => 'ᇝ',
'ㅰ' => 'ᇟ',
'ㅱ' => 'ᄝ',
'ㅲ' => 'ᄞ',
'ㅳ' => 'ᄠ',
'ㅴ' => 'ᄢ',
'ㅵ' => 'ᄣ',
'ㅶ' => 'ᄧ',
'ㅷ' => 'ᄩ',
'ㅸ' => 'ᄫ',
'ㅹ' => 'ᄬ',
'ㅺ' => 'ᄭ',
'ㅻ' => 'ᄮ',
'ㅼ' => 'ᄯ',
'ㅽ' => 'ᄲ',
'ㅾ' => 'ᄶ',
'ㅿ' => 'ᅀ',
'ㆀ' => 'ᅇ',
'ㆁ' => 'ᅌ',
'ㆂ' => 'ᇱ',
'ㆃ' => 'ᇲ',
'ㆄ' => 'ᅗ',
'ㆅ' => 'ᅘ',
'ㆆ' => 'ᅙ',
'ㆇ' => 'ᆄ',
'ㆈ' => 'ᆅ',
'ㆉ' => 'ᆈ',
'ㆊ' => 'ᆑ',
'ㆋ' => 'ᆒ',
'ㆌ' => 'ᆔ',
'ㆍ' => 'ᆞ',
'ㆎ' => 'ᆡ',
'㆒' => '一',
'㆓' => '二',
'㆔' => '三',
'㆕' => '四',
'㆖' => '上',
'㆗' => '中',
'㆘' => '下',
'㆙' => '甲',
'㆚' => '乙',
'㆛' => '丙',
'㆜' => '丁',
'㆝' => '天',
'㆞' => '地',
'㆟' => '人',
'㈀' => '(ᄀ)',
'㈁' => '(ᄂ)',
'㈂' => '(ᄃ)',
'㈃' => '(ᄅ)',
'㈄' => '(ᄆ)',
'㈅' => '(ᄇ)',
'㈆' => '(ᄉ)',
'㈇' => '(ᄋ)',
'㈈' => '(ᄌ)',
'㈉' => '(ᄎ)',
'㈊' => '(ᄏ)',
'㈋' => '(ᄐ)',
'㈌' => '(ᄑ)',
'㈍' => '(ᄒ)',
'㈎' => '(가)',
'㈏' => '(나)',
'㈐' => '(다)',
'㈑' => '(라)',
'㈒' => '(마)',
'㈓' => '(바)',
'㈔' => '(사)',
'㈕' => '(아)',
'㈖' => '(자)',
'㈗' => '(차)',
'㈘' => '(카)',
'㈙' => '(타)',
'㈚' => '(파)',
'㈛' => '(하)',
'㈜' => '(주)',
'㈝' => '(오전)',
'㈞' => '(오후)',
'㈠' => '(一)',
'㈡' => '(二)',
'㈢' => '(三)',
'㈣' => '(四)',
'㈤' => '(五)',
'㈥' => '(六)',
'㈦' => '(七)',
'㈧' => '(八)',
'㈨' => '(九)',
'㈩' => '(十)',
'㈪' => '(月)',
'㈫' => '(火)',
'㈬' => '(水)',
'㈭' => '(木)',
'㈮' => '(金)',
'㈯' => '(土)',
'㈰' => '(日)',
'㈱' => '(株)',
'㈲' => '(有)',
'㈳' => '(社)',
'㈴' => '(名)',
'㈵' => '(特)',
'㈶' => '(財)',
'㈷' => '(祝)',
'㈸' => '(労)',
'㈹' => '(代)',
'㈺' => '(呼)',
'㈻' => '(学)',
'㈼' => '(監)',
'㈽' => '(企)',
'㈾' => '(資)',
'㈿' => '(協)',
'㉀' => '(祭)',
'㉁' => '(休)',
'㉂' => '(自)',
'㉃' => '(至)',
'㉄' => '問',
'㉅' => '幼',
'㉆' => '文',
'㉇' => '箏',
'㉐' => 'PTE',
'㉑' => '21',
'㉒' => '22',
'㉓' => '23',
'㉔' => '24',
'㉕' => '25',
'㉖' => '26',
'㉗' => '27',
'㉘' => '28',
'㉙' => '29',
'㉚' => '30',
'㉛' => '31',
'㉜' => '32',
'㉝' => '33',
'㉞' => '34',
'㉟' => '35',
'㉠' => 'ᄀ',
'㉡' => 'ᄂ',
'㉢' => 'ᄃ',
'㉣' => 'ᄅ',
'㉤' => 'ᄆ',
'㉥' => 'ᄇ',
'㉦' => 'ᄉ',
'㉧' => 'ᄋ',
'㉨' => 'ᄌ',
'㉩' => 'ᄎ',
'㉪' => 'ᄏ',
'㉫' => 'ᄐ',
'㉬' => 'ᄑ',
'㉭' => 'ᄒ',
'㉮' => '가',
'㉯' => '나',
'㉰' => '다',
'㉱' => '라',
'㉲' => '마',
'㉳' => '바',
'㉴' => '사',
'㉵' => '아',
'㉶' => '자',
'㉷' => '차',
'㉸' => '카',
'㉹' => '타',
'㉺' => '파',
'㉻' => '하',
'㉼' => '참고',
'㉽' => '주의',
'㉾' => '우',
'㊀' => '一',
'㊁' => '二',
'㊂' => '三',
'㊃' => '四',
'㊄' => '五',
'㊅' => '六',
'㊆' => '七',
'㊇' => '八',
'㊈' => '九',
'㊉' => '十',
'㊊' => '月',
'㊋' => '火',
'㊌' => '水',
'㊍' => '木',
'㊎' => '金',
'㊏' => '土',
'㊐' => '日',
'㊑' => '株',
'㊒' => '有',
'㊓' => '社',
'㊔' => '名',
'㊕' => '特',
'㊖' => '財',
'㊗' => '祝',
'㊘' => '労',
'㊙' => '秘',
'㊚' => '男',
'㊛' => '女',
'㊜' => '適',
'㊝' => '優',
'㊞' => '印',
'㊟' => '注',
'㊠' => '項',
'㊡' => '休',
'㊢' => '写',
'㊣' => '正',
'㊤' => '上',
'㊥' => '中',
'㊦' => '下',
'㊧' => '左',
'㊨' => '右',
'㊩' => '医',
'㊪' => '宗',
'㊫' => '学',
'㊬' => '監',
'㊭' => '企',
'㊮' => '資',
'㊯' => '協',
'㊰' => '夜',
'㊱' => '36',
'㊲' => '37',
'㊳' => '38',
'㊴' => '39',
'㊵' => '40',
'㊶' => '41',
'㊷' => '42',
'㊸' => '43',
'㊹' => '44',
'㊺' => '45',
'㊻' => '46',
'㊼' => '47',
'㊽' => '48',
'㊾' => '49',
'㊿' => '50',
'㋀' => '1月',
'㋁' => '2月',
'㋂' => '3月',
'㋃' => '4月',
'㋄' => '5月',
'㋅' => '6月',
'㋆' => '7月',
'㋇' => '8月',
'㋈' => '9月',
'㋉' => '10月',
'㋊' => '11月',
'㋋' => '12月',
'㋌' => 'Hg',
'㋍' => 'erg',
'㋎' => 'eV',
'㋏' => 'LTD',
'㋐' => 'ア',
'㋑' => 'イ',
'㋒' => 'ウ',
'㋓' => 'エ',
'㋔' => 'オ',
'㋕' => 'カ',
'㋖' => 'キ',
'㋗' => 'ク',
'㋘' => 'ケ',
'㋙' => 'コ',
'㋚' => 'サ',
'㋛' => 'シ',
'㋜' => 'ス',
'㋝' => 'セ',
'㋞' => 'ソ',
'㋟' => 'タ',
'㋠' => 'チ',
'㋡' => 'ツ',
'㋢' => 'テ',
'㋣' => 'ト',
'㋤' => 'ナ',
'㋥' => 'ニ',
'㋦' => 'ヌ',
'㋧' => 'ネ',
'㋨' => '',
'㋩' => 'ハ',
'㋪' => 'ヒ',
'㋫' => 'フ',
'㋬' => 'ヘ',
'㋭' => 'ホ',
'㋮' => 'マ',
'㋯' => 'ミ',
'㋰' => 'ム',
'㋱' => 'メ',
'㋲' => 'モ',
'㋳' => 'ヤ',
'㋴' => 'ユ',
'㋵' => 'ヨ',
'㋶' => 'ラ',
'㋷' => 'リ',
'㋸' => 'ル',
'㋹' => 'レ',
'㋺' => 'ロ',
'㋻' => 'ワ',
'㋼' => 'ヰ',
'㋽' => 'ヱ',
'㋾' => 'ヲ',
'㋿' => '令和',
'㌀' => 'アパート',
'㌁' => 'アルファ',
'㌂' => 'アンペア',
'㌃' => 'アール',
'㌄' => 'イニング',
'㌅' => 'インチ',
'㌆' => 'ウォン',
'㌇' => 'エスクード',
'㌈' => 'エーカー',
'㌉' => 'オンス',
'㌊' => 'オーム',
'㌋' => 'カイリ',
'㌌' => 'カラット',
'㌍' => 'カロリー',
'㌎' => 'ガロン',
'㌏' => 'ガンマ',
'㌐' => 'ギガ',
'㌑' => 'ギニー',
'㌒' => 'キュリー',
'㌓' => 'ギルダー',
'㌔' => 'キロ',
'㌕' => 'キログラム',
'㌖' => 'キロメートル',
'㌗' => 'キロワット',
'㌘' => 'グラム',
'㌙' => 'グラムトン',
'㌚' => 'クルゼイロ',
'㌛' => 'クローネ',
'㌜' => 'ケース',
'㌝' => 'コルナ',
'㌞' => 'コーポ',
'㌟' => 'サイクル',
'㌠' => 'サンチーム',
'㌡' => 'シリング',
'㌢' => 'センチ',
'㌣' => 'セント',
'㌤' => 'ダース',
'㌥' => 'デシ',
'㌦' => 'ドル',
'㌧' => 'トン',
'㌨' => 'ナノ',
'㌩' => 'ノット',
'㌪' => 'ハイツ',
'㌫' => 'パーセント',
'㌬' => 'パーツ',
'㌭' => 'バーレル',
'㌮' => 'ピアストル',
'㌯' => 'ピクル',
'㌰' => 'ピコ',
'㌱' => 'ビル',
'㌲' => 'ファラッド',
'㌳' => 'フィート',
'㌴' => 'ブッシェル',
'㌵' => 'フラン',
'㌶' => 'ヘクタール',
'㌷' => 'ペソ',
'㌸' => 'ペニヒ',
'㌹' => 'ヘルツ',
'㌺' => 'ペンス',
'㌻' => 'ページ',
'㌼' => 'ベータ',
'㌽' => 'ポイント',
'㌾' => 'ボルト',
'㌿' => 'ホン',
'㍀' => 'ポンド',
'㍁' => 'ホール',
'㍂' => 'ホーン',
'㍃' => 'マイクロ',
'㍄' => 'マイル',
'㍅' => 'マッハ',
'㍆' => 'マルク',
'㍇' => 'マンション',
'㍈' => 'ミクロン',
'㍉' => 'ミリ',
'㍊' => 'ミリバール',
'㍋' => 'メガ',
'㍌' => 'メガトン',
'㍍' => 'メートル',
'㍎' => 'ヤード',
'㍏' => 'ヤール',
'㍐' => 'ユアン',
'㍑' => 'リットル',
'㍒' => 'リラ',
'㍓' => 'ルピー',
'㍔' => 'ルーブル',
'㍕' => 'レム',
'㍖' => 'レントゲン',
'㍗' => 'ワット',
'㍘' => '0点',
'㍙' => '1点',
'㍚' => '2点',
'㍛' => '3点',
'㍜' => '4点',
'㍝' => '5点',
'㍞' => '6点',
'㍟' => '7点',
'㍠' => '8点',
'㍡' => '9点',
'㍢' => '10点',
'㍣' => '11点',
'㍤' => '12点',
'㍥' => '13点',
'㍦' => '14点',
'㍧' => '15点',
'㍨' => '16点',
'㍩' => '17点',
'㍪' => '18点',
'㍫' => '19点',
'㍬' => '20点',
'㍭' => '21点',
'㍮' => '22点',
'㍯' => '23点',
'㍰' => '24点',
'㍱' => 'hPa',
'㍲' => 'da',
'㍳' => 'AU',
'㍴' => 'bar',
'㍵' => 'oV',
'㍶' => 'pc',
'㍷' => 'dm',
'㍸' => 'dm2',
'㍹' => 'dm3',
'㍺' => 'IU',
'㍻' => '平成',
'㍼' => '昭和',
'㍽' => '大正',
'㍾' => '明治',
'㍿' => '株式会社',
'㎀' => 'pA',
'㎁' => 'nA',
'㎂' => 'μA',
'㎃' => 'mA',
'㎄' => 'kA',
'㎅' => 'KB',
'㎆' => 'MB',
'㎇' => 'GB',
'㎈' => 'cal',
'㎉' => 'kcal',
'㎊' => 'pF',
'㎋' => 'nF',
'㎌' => 'μF',
'㎍' => 'μg',
'㎎' => 'mg',
'㎏' => 'kg',
'㎐' => 'Hz',
'㎑' => 'kHz',
'㎒' => 'MHz',
'㎓' => 'GHz',
'㎔' => 'THz',
'㎕' => 'μl',
'㎖' => 'ml',
'㎗' => 'dl',
'㎘' => 'kl',
'㎙' => 'fm',
'㎚' => 'nm',
'㎛' => 'μm',
'㎜' => 'mm',
'㎝' => 'cm',
'㎞' => 'km',
'㎟' => 'mm2',
'㎠' => 'cm2',
'㎡' => 'm2',
'㎢' => 'km2',
'㎣' => 'mm3',
'㎤' => 'cm3',
'㎥' => 'm3',
'㎦' => 'km3',
'㎧' => 'ms',
'㎨' => 'ms2',
'㎩' => 'Pa',
'㎪' => 'kPa',
'㎫' => 'MPa',
'㎬' => 'GPa',
'㎭' => 'rad',
'㎮' => 'rads',
'㎯' => 'rads2',
'㎰' => 'ps',
'㎱' => 'ns',
'㎲' => 'μs',
'㎳' => 'ms',
'㎴' => 'pV',
'㎵' => 'nV',
'㎶' => 'μV',
'㎷' => 'mV',
'㎸' => 'kV',
'㎹' => 'MV',
'㎺' => 'pW',
'㎻' => 'nW',
'㎼' => 'μW',
'㎽' => 'mW',
'㎾' => 'kW',
'㎿' => 'MW',
'㏀' => 'kΩ',
'㏁' => 'MΩ',
'㏂' => 'a.m.',
'㏃' => 'Bq',
'㏄' => 'cc',
'㏅' => 'cd',
'㏆' => 'Ckg',
'㏇' => 'Co.',
'㏈' => 'dB',
'㏉' => 'Gy',
'㏊' => 'ha',
'㏋' => 'HP',
'㏌' => 'in',
'㏍' => 'KK',
'㏎' => 'KM',
'㏏' => 'kt',
'㏐' => 'lm',
'㏑' => 'ln',
'㏒' => 'log',
'㏓' => 'lx',
'㏔' => 'mb',
'㏕' => 'mil',
'㏖' => 'mol',
'㏗' => 'PH',
'㏘' => 'p.m.',
'㏙' => 'PPM',
'㏚' => 'PR',
'㏛' => 'sr',
'㏜' => 'Sv',
'㏝' => 'Wb',
'㏞' => 'Vm',
'㏟' => 'Am',
'㏠' => '1日',
'㏡' => '2日',
'㏢' => '3日',
'㏣' => '4日',
'㏤' => '5日',
'㏥' => '6日',
'㏦' => '7日',
'㏧' => '8日',
'㏨' => '9日',
'㏩' => '10日',
'㏪' => '11日',
'㏫' => '12日',
'㏬' => '13日',
'㏭' => '14日',
'㏮' => '15日',
'㏯' => '16日',
'㏰' => '17日',
'㏱' => '18日',
'㏲' => '19日',
'㏳' => '20日',
'㏴' => '21日',
'㏵' => '22日',
'㏶' => '23日',
'㏷' => '24日',
'㏸' => '25日',
'㏹' => '26日',
'㏺' => '27日',
'㏻' => '28日',
'㏼' => '29日',
'㏽' => '30日',
'㏾' => '31日',
'㏿' => 'gal',
'ꚜ' => 'ъ',
'ꚝ' => 'ь',
'ꝰ' => 'ꝯ',
'ꟸ' => 'Ħ',
'ꟹ' => 'œ',
'ꭜ' => 'ꜧ',
'ꭝ' => 'ꬷ',
'ꭞ' => 'ɫ',
'ꭟ' => '',
'ꭩ' => 'ʍ',
'ff' => 'ff',
'fi' => 'fi',
'fl' => 'fl',
'ffi' => 'ffi',
'ffl' => 'ffl',
'ſt' => 'st',
'st' => 'st',
'ﬓ' => 'մն',
'ﬔ' => 'մե',
'ﬕ' => 'մի',
'ﬖ' => 'վն',
'ﬗ' => 'մխ',
'ﬠ' => 'ע',
'ﬡ' => 'א',
'ﬢ' => 'ד',
'ﬣ' => 'ה',
'ﬤ' => 'כ',
'ﬥ' => 'ל',
'ﬦ' => 'ם',
'ﬧ' => 'ר',
'ﬨ' => 'ת',
'﬩' => '+',
'ﭏ' => 'אל',
'ﭐ' => 'ٱ',
'ﭑ' => 'ٱ',
'ﭒ' => 'ٻ',
'ﭓ' => 'ٻ',
'ﭔ' => 'ٻ',
'ﭕ' => 'ٻ',
'ﭖ' => 'پ',
'ﭗ' => 'پ',
'ﭘ' => 'پ',
'ﭙ' => 'پ',
'ﭚ' => 'ڀ',
'ﭛ' => 'ڀ',
'ﭜ' => 'ڀ',
'ﭝ' => 'ڀ',
'ﭞ' => 'ٺ',
'ﭟ' => 'ٺ',
'ﭠ' => 'ٺ',
'ﭡ' => 'ٺ',
'ﭢ' => 'ٿ',
'ﭣ' => 'ٿ',
'ﭤ' => 'ٿ',
'ﭥ' => 'ٿ',
'ﭦ' => 'ٹ',
'ﭧ' => 'ٹ',
'ﭨ' => 'ٹ',
'ﭩ' => 'ٹ',
'ﭪ' => 'ڤ',
'ﭫ' => 'ڤ',
'ﭬ' => 'ڤ',
'ﭭ' => 'ڤ',
'ﭮ' => 'ڦ',
'ﭯ' => 'ڦ',
'ﭰ' => 'ڦ',
'ﭱ' => 'ڦ',
'ﭲ' => 'ڄ',
'ﭳ' => 'ڄ',
'ﭴ' => 'ڄ',
'ﭵ' => 'ڄ',
'ﭶ' => 'ڃ',
'ﭷ' => 'ڃ',
'ﭸ' => 'ڃ',
'ﭹ' => 'ڃ',
'ﭺ' => 'چ',
'ﭻ' => 'چ',
'ﭼ' => 'چ',
'ﭽ' => 'چ',
'ﭾ' => 'ڇ',
'ﭿ' => 'ڇ',
'ﮀ' => 'ڇ',
'ﮁ' => 'ڇ',
'ﮂ' => 'ڍ',
'ﮃ' => 'ڍ',
'ﮄ' => 'ڌ',
'ﮅ' => 'ڌ',
'ﮆ' => 'ڎ',
'ﮇ' => 'ڎ',
'ﮈ' => 'ڈ',
'ﮉ' => 'ڈ',
'ﮊ' => 'ژ',
'ﮋ' => 'ژ',
'ﮌ' => 'ڑ',
'ﮍ' => 'ڑ',
'ﮎ' => 'ک',
'ﮏ' => 'ک',
'ﮐ' => 'ک',
'ﮑ' => 'ک',
'ﮒ' => 'گ',
'ﮓ' => 'گ',
'ﮔ' => 'گ',
'ﮕ' => 'گ',
'ﮖ' => 'ڳ',
'ﮗ' => 'ڳ',
'ﮘ' => 'ڳ',
'ﮙ' => 'ڳ',
'ﮚ' => 'ڱ',
'ﮛ' => 'ڱ',
'ﮜ' => 'ڱ',
'ﮝ' => 'ڱ',
'ﮞ' => 'ں',
'ﮟ' => 'ں',
'ﮠ' => 'ڻ',
'ﮡ' => 'ڻ',
'ﮢ' => 'ڻ',
'ﮣ' => 'ڻ',
'ﮤ' => 'ۀ',
'ﮥ' => 'ۀ',
'' => 'ہ',
'' => 'ہ',
'' => 'ہ',
'' => 'ہ',
'' => 'ھ',
'' => 'ھ',
'' => 'ھ',
'' => 'ھ',
'ﮮ' => 'ے',
'ﮯ' => 'ے',
'ﮰ' => 'ۓ',
'ﮱ' => 'ۓ',
'ﯓ' => 'ڭ',
'ﯔ' => 'ڭ',
'ﯕ' => 'ڭ',
'ﯖ' => 'ڭ',
'ﯗ' => 'ۇ',
'ﯘ' => 'ۇ',
'ﯙ' => 'ۆ',
'ﯚ' => 'ۆ',
'ﯛ' => 'ۈ',
'ﯜ' => 'ۈ',
'ﯝ' => 'ۇٴ',
'ﯞ' => 'ۋ',
'ﯟ' => 'ۋ',
'ﯠ' => 'ۅ',
'ﯡ' => 'ۅ',
'ﯢ' => 'ۉ',
'ﯣ' => 'ۉ',
'ﯤ' => 'ې',
'ﯥ' => 'ې',
'ﯦ' => 'ې',
'ﯧ' => 'ې',
'ﯨ' => 'ى',
'ﯩ' => 'ى',
'ﯪ' => 'ئا',
'ﯫ' => 'ئا',
'ﯬ' => 'ئە',
'ﯭ' => 'ئە',
'ﯮ' => 'ئو',
'ﯯ' => 'ئو',
'ﯰ' => 'ئۇ',
'ﯱ' => 'ئۇ',
'ﯲ' => 'ئۆ',
'ﯳ' => 'ئۆ',
'ﯴ' => 'ئۈ',
'ﯵ' => 'ئۈ',
'ﯶ' => 'ئې',
'ﯷ' => 'ئې',
'ﯸ' => 'ئې',
'ﯹ' => 'ئى',
'ﯺ' => 'ئى',
'ﯻ' => 'ئى',
'ﯼ' => 'ی',
'ﯽ' => 'ی',
'ﯾ' => 'ی',
'ﯿ' => 'ی',
'ﰀ' => 'ئج',
'ﰁ' => 'ئح',
'ﰂ' => 'ئم',
'ﰃ' => 'ئى',
'ﰄ' => 'ئي',
'ﰅ' => 'بج',
'ﰆ' => 'بح',
'ﰇ' => 'بخ',
'ﰈ' => 'بم',
'ﰉ' => 'بى',
'ﰊ' => 'بي',
'ﰋ' => 'تج',
'ﰌ' => 'تح',
'ﰍ' => 'تخ',
'ﰎ' => 'تم',
'ﰏ' => 'تى',
'ﰐ' => 'تي',
'ﰑ' => 'ثج',
'ﰒ' => 'ثم',
'ﰓ' => 'ثى',
'ﰔ' => 'ثي',
'ﰕ' => 'جح',
'ﰖ' => 'جم',
'ﰗ' => 'حج',
'ﰘ' => 'حم',
'ﰙ' => 'خج',
'ﰚ' => 'خح',
'ﰛ' => 'خم',
'ﰜ' => 'سج',
'ﰝ' => 'سح',
'ﰞ' => 'سخ',
'ﰟ' => 'سم',
'ﰠ' => 'صح',
'ﰡ' => 'صم',
'ﰢ' => 'ضج',
'ﰣ' => 'ضح',
'ﰤ' => 'ضخ',
'ﰥ' => 'ضم',
'ﰦ' => 'طح',
'ﰧ' => 'طم',
'ﰨ' => 'ظم',
'ﰩ' => 'عج',
'ﰪ' => 'عم',
'ﰫ' => 'غج',
'ﰬ' => 'غم',
'ﰭ' => 'فج',
'ﰮ' => 'فح',
'ﰯ' => 'فخ',
'ﰰ' => 'فم',
'ﰱ' => 'فى',
'ﰲ' => 'في',
'ﰳ' => 'قح',
'ﰴ' => 'قم',
'ﰵ' => 'قى',
'ﰶ' => 'قي',
'ﰷ' => 'كا',
'ﰸ' => 'كج',
'ﰹ' => 'كح',
'ﰺ' => 'كخ',
'ﰻ' => 'كل',
'ﰼ' => 'كم',
'ﰽ' => 'كى',
'ﰾ' => 'كي',
'ﰿ' => 'لج',
'ﱀ' => 'لح',
'ﱁ' => 'لخ',
'ﱂ' => 'لم',
'ﱃ' => 'لى',
'ﱄ' => 'لي',
'ﱅ' => 'مج',
'ﱆ' => 'مح',
'ﱇ' => 'مخ',
'ﱈ' => 'مم',
'ﱉ' => 'مى',
'ﱊ' => 'مي',
'ﱋ' => 'نج',
'ﱌ' => 'نح',
'ﱍ' => 'نخ',
'ﱎ' => 'نم',
'ﱏ' => 'نى',
'ﱐ' => 'ني',
'ﱑ' => 'هج',
'ﱒ' => 'هم',
'ﱓ' => 'هى',
'ﱔ' => 'هي',
'ﱕ' => 'يج',
'ﱖ' => 'يح',
'ﱗ' => 'يخ',
'ﱘ' => 'يم',
'ﱙ' => 'يى',
'ﱚ' => 'يي',
'ﱛ' => 'ذٰ',
'ﱜ' => 'رٰ',
'ﱝ' => 'ىٰ',
'ﱞ' => ' ٌّ',
'ﱟ' => ' ٍّ',
'ﱠ' => ' َّ',
'ﱡ' => ' ُّ',
'ﱢ' => ' ِّ',
'ﱣ' => ' ّٰ',
'ﱤ' => 'ئر',
'ﱥ' => 'ئز',
'ﱦ' => 'ئم',
'ﱧ' => 'ئن',
'ﱨ' => 'ئى',
'ﱩ' => 'ئي',
'ﱪ' => 'بر',
'ﱫ' => 'بز',
'ﱬ' => 'بم',
'ﱭ' => 'بن',
'ﱮ' => 'بى',
'ﱯ' => 'بي',
'ﱰ' => 'تر',
'ﱱ' => 'تز',
'ﱲ' => 'تم',
'ﱳ' => 'تن',
'ﱴ' => 'تى',
'ﱵ' => 'تي',
'ﱶ' => 'ثر',
'ﱷ' => 'ثز',
'ﱸ' => 'ثم',
'ﱹ' => 'ثن',
'ﱺ' => 'ثى',
'ﱻ' => 'ثي',
'ﱼ' => 'فى',
'ﱽ' => 'في',
'ﱾ' => 'قى',
'ﱿ' => 'قي',
'ﲀ' => 'كا',
'ﲁ' => 'كل',
'ﲂ' => 'كم',
'ﲃ' => 'كى',
'ﲄ' => 'كي',
'ﲅ' => 'لم',
'ﲆ' => 'لى',
'ﲇ' => 'لي',
'ﲈ' => 'ما',
'ﲉ' => 'مم',
'ﲊ' => 'نر',
'ﲋ' => 'نز',
'ﲌ' => 'نم',
'ﲍ' => 'نن',
'ﲎ' => 'نى',
'ﲏ' => 'ني',
'ﲐ' => 'ىٰ',
'ﲑ' => 'ير',
'ﲒ' => 'يز',
'ﲓ' => 'يم',
'ﲔ' => 'ين',
'ﲕ' => 'يى',
'ﲖ' => 'يي',
'ﲗ' => 'ئج',
'ﲘ' => 'ئح',
'ﲙ' => 'ئخ',
'ﲚ' => 'ئم',
'ﲛ' => 'ئه',
'ﲜ' => 'بج',
'ﲝ' => 'بح',
'ﲞ' => 'بخ',
'ﲟ' => 'بم',
'ﲠ' => 'به',
'ﲡ' => 'تج',
'ﲢ' => 'تح',
'ﲣ' => 'تخ',
'ﲤ' => 'تم',
'ﲥ' => 'ته',
'ﲦ' => 'ثم',
'ﲧ' => 'جح',
'ﲨ' => 'جم',
'ﲩ' => 'حج',
'ﲪ' => 'حم',
'ﲫ' => 'خج',
'ﲬ' => 'خم',
'ﲭ' => 'سج',
'ﲮ' => 'سح',
'ﲯ' => 'سخ',
'ﲰ' => 'سم',
'ﲱ' => 'صح',
'ﲲ' => 'صخ',
'ﲳ' => 'صم',
'ﲴ' => 'ضج',
'ﲵ' => 'ضح',
'ﲶ' => 'ضخ',
'ﲷ' => 'ضم',
'ﲸ' => 'طح',
'ﲹ' => 'ظم',
'ﲺ' => 'عج',
'ﲻ' => 'عم',
'ﲼ' => 'غج',
'ﲽ' => 'غم',
'ﲾ' => 'فج',
'ﲿ' => 'فح',
'ﳀ' => 'فخ',
'ﳁ' => 'فم',
'ﳂ' => 'قح',
'ﳃ' => 'قم',
'ﳄ' => 'كج',
'ﳅ' => 'كح',
'ﳆ' => 'كخ',
'ﳇ' => 'كل',
'ﳈ' => 'كم',
'ﳉ' => 'لج',
'ﳊ' => 'لح',
'ﳋ' => 'لخ',
'ﳌ' => 'لم',
'ﳍ' => 'له',
'ﳎ' => 'مج',
'ﳏ' => 'مح',
'ﳐ' => 'مخ',
'ﳑ' => 'مم',
'ﳒ' => 'نج',
'ﳓ' => 'نح',
'ﳔ' => 'نخ',
'ﳕ' => 'نم',
'ﳖ' => 'نه',
'ﳗ' => 'هج',
'ﳘ' => 'هم',
'ﳙ' => 'هٰ',
'ﳚ' => 'يج',
'ﳛ' => 'يح',
'ﳜ' => 'يخ',
'ﳝ' => 'يم',
'ﳞ' => 'يه',
'ﳟ' => 'ئم',
'ﳠ' => 'ئه',
'ﳡ' => 'بم',
'ﳢ' => 'به',
'ﳣ' => 'تم',
'ﳤ' => 'ته',
'ﳥ' => 'ثم',
'ﳦ' => 'ثه',
'ﳧ' => 'سم',
'ﳨ' => 'سه',
'ﳩ' => 'شم',
'ﳪ' => 'شه',
'ﳫ' => 'كل',
'ﳬ' => 'كم',
'ﳭ' => 'لم',
'ﳮ' => 'نم',
'ﳯ' => 'نه',
'ﳰ' => 'يم',
'ﳱ' => 'يه',
'ﳲ' => 'ـَّ',
'ﳳ' => 'ـُّ',
'ﳴ' => 'ـِّ',
'ﳵ' => 'طى',
'ﳶ' => 'طي',
'ﳷ' => 'عى',
'ﳸ' => 'عي',
'ﳹ' => 'غى',
'ﳺ' => 'غي',
'ﳻ' => 'سى',
'ﳼ' => 'سي',
'ﳽ' => 'شى',
'ﳾ' => 'شي',
'ﳿ' => 'حى',
'ﴀ' => 'حي',
'ﴁ' => 'جى',
'ﴂ' => 'جي',
'ﴃ' => 'خى',
'ﴄ' => 'خي',
'ﴅ' => 'صى',
'ﴆ' => 'صي',
'ﴇ' => 'ضى',
'ﴈ' => 'ضي',
'ﴉ' => 'شج',
'ﴊ' => 'شح',
'ﴋ' => 'شخ',
'ﴌ' => 'شم',
'ﴍ' => 'شر',
'ﴎ' => 'سر',
'ﴏ' => 'صر',
'ﴐ' => 'ضر',
'ﴑ' => 'طى',
'ﴒ' => 'طي',
'ﴓ' => 'عى',
'ﴔ' => 'عي',
'ﴕ' => 'غى',
'ﴖ' => 'غي',
'ﴗ' => 'سى',
'ﴘ' => 'سي',
'ﴙ' => 'شى',
'ﴚ' => 'شي',
'ﴛ' => 'حى',
'ﴜ' => 'حي',
'ﴝ' => 'جى',
'ﴞ' => 'جي',
'ﴟ' => 'خى',
'ﴠ' => 'خي',
'ﴡ' => 'صى',
'ﴢ' => 'صي',
'ﴣ' => 'ضى',
'ﴤ' => 'ضي',
'ﴥ' => 'شج',
'ﴦ' => 'شح',
'ﴧ' => 'شخ',
'ﴨ' => 'شم',
'ﴩ' => 'شر',
'ﴪ' => 'سر',
'ﴫ' => 'صر',
'ﴬ' => 'ضر',
'ﴭ' => 'شج',
'ﴮ' => 'شح',
'ﴯ' => 'شخ',
'ﴰ' => 'شم',
'ﴱ' => 'سه',
'ﴲ' => 'شه',
'ﴳ' => 'طم',
'ﴴ' => 'سج',
'ﴵ' => 'سح',
'ﴶ' => 'سخ',
'ﴷ' => 'شج',
'ﴸ' => 'شح',
'ﴹ' => 'شخ',
'ﴺ' => 'طم',
'ﴻ' => 'ظم',
'ﴼ' => 'اً',
'ﴽ' => 'اً',
'ﵐ' => 'تجم',
'ﵑ' => 'تحج',
'ﵒ' => 'تحج',
'ﵓ' => 'تحم',
'ﵔ' => 'تخم',
'ﵕ' => 'تمج',
'ﵖ' => 'تمح',
'ﵗ' => 'تمخ',
'ﵘ' => 'جمح',
'ﵙ' => 'جمح',
'ﵚ' => 'حمي',
'ﵛ' => 'حمى',
'ﵜ' => 'سحج',
'ﵝ' => 'سجح',
'ﵞ' => 'سجى',
'ﵟ' => 'سمح',
'ﵠ' => 'سمح',
'ﵡ' => 'سمج',
'ﵢ' => 'سمم',
'ﵣ' => 'سمم',
'ﵤ' => 'صحح',
'ﵥ' => 'صحح',
'ﵦ' => 'صمم',
'ﵧ' => 'شحم',
'ﵨ' => 'شحم',
'ﵩ' => 'شجي',
'ﵪ' => 'شمخ',
'ﵫ' => 'شمخ',
'ﵬ' => 'شمم',
'ﵭ' => 'شمم',
'ﵮ' => 'ضحى',
'ﵯ' => 'ضخم',
'ﵰ' => 'ضخم',
'ﵱ' => 'طمح',
'ﵲ' => 'طمح',
'ﵳ' => 'طمم',
'ﵴ' => 'طمي',
'ﵵ' => 'عجم',
'ﵶ' => 'عمم',
'ﵷ' => 'عمم',
'ﵸ' => 'عمى',
'ﵹ' => 'غمم',
'ﵺ' => 'غمي',
'ﵻ' => 'غمى',
'ﵼ' => 'فخم',
'ﵽ' => 'فخم',
'ﵾ' => 'قمح',
'ﵿ' => 'قمم',
'ﶀ' => 'لحم',
'ﶁ' => 'لحي',
'ﶂ' => 'لحى',
'ﶃ' => 'لجج',
'ﶄ' => 'لجج',
'ﶅ' => 'لخم',
'ﶆ' => 'لخم',
'ﶇ' => 'لمح',
'ﶈ' => 'لمح',
'ﶉ' => 'محج',
'ﶊ' => 'محم',
'ﶋ' => 'محي',
'ﶌ' => 'مجح',
'ﶍ' => 'مجم',
'ﶎ' => 'مخج',
'ﶏ' => 'مخم',
'ﶒ' => 'مجخ',
'ﶓ' => 'همج',
'ﶔ' => 'همم',
'ﶕ' => 'نحم',
'ﶖ' => 'نحى',
'ﶗ' => 'نجم',
'ﶘ' => 'نجم',
'ﶙ' => 'نجى',
'ﶚ' => 'نمي',
'ﶛ' => 'نمى',
'ﶜ' => 'يمم',
'ﶝ' => 'يمم',
'ﶞ' => 'بخي',
'ﶟ' => 'تجي',
'ﶠ' => 'تجى',
'ﶡ' => 'تخي',
'ﶢ' => 'تخى',
'ﶣ' => 'تمي',
'ﶤ' => 'تمى',
'ﶥ' => 'جمي',
'ﶦ' => 'جحى',
'ﶧ' => 'جمى',
'ﶨ' => 'سخى',
'ﶩ' => 'صحي',
'ﶪ' => 'شحي',
'ﶫ' => 'ضحي',
'ﶬ' => 'لجي',
'ﶭ' => 'لمي',
'ﶮ' => 'يحي',
'ﶯ' => 'يجي',
'ﶰ' => 'يمي',
'ﶱ' => 'ممي',
'ﶲ' => 'قمي',
'ﶳ' => 'نحي',
'ﶴ' => 'قمح',
'ﶵ' => 'لحم',
'ﶶ' => 'عمي',
'ﶷ' => 'كمي',
'ﶸ' => 'نجح',
'ﶹ' => 'مخي',
'ﶺ' => 'لجم',
'ﶻ' => 'كمم',
'ﶼ' => 'لجم',
'ﶽ' => 'نجح',
'ﶾ' => 'جحي',
'ﶿ' => 'حجي',
'ﷀ' => 'مجي',
'ﷁ' => 'فمي',
'ﷂ' => 'بحي',
'ﷃ' => 'كمم',
'ﷄ' => 'عجم',
'ﷅ' => 'صمم',
'ﷆ' => 'سخي',
'ﷇ' => 'نجي',
'ﷰ' => 'صلے',
'ﷱ' => 'قلے',
'ﷲ' => 'الله',
'ﷳ' => 'اكبر',
'ﷴ' => 'محمد',
'ﷵ' => 'صلعم',
'ﷶ' => 'رسول',
'ﷷ' => 'عليه',
'ﷸ' => 'وسلم',
'ﷹ' => 'صلى',
'ﷺ' => 'صلى الله عليه وسلم',
'ﷻ' => 'جل جلاله',
'﷼' => 'ریال',
'︐' => ',',
'︑' => '、',
'︒' => '。',
'︓' => ':',
'︔' => ';',
'︕' => '!',
'︖' => '?',
'︗' => '〖',
'︘' => '〗',
'︙' => '...',
'' => '..',
'︱' => '—',
'︲' => '',
'︳' => '_',
'︴' => '_',
'︵' => '(',
'︶' => ')',
'︷' => '{',
'︸' => '}',
'︹' => '',
'︺' => '',
'︻' => '【',
'︼' => '】',
'︽' => '《',
'︾' => '》',
'︿' => '〈',
'﹀' => '〉',
'﹁' => '「',
'﹂' => '」',
'﹃' => '『',
'﹄' => '』',
'﹇' => '[',
'﹈' => ']',
'﹉' => ' ̅',
'﹊' => ' ̅',
'﹋' => ' ̅',
'﹌' => ' ̅',
'' => '_',
'' => '_',
'' => '_',
'﹐' => ',',
'﹑' => '、',
'﹒' => '.',
'﹔' => ';',
'﹕' => ':',
'﹖' => '?',
'﹗' => '!',
'' => '—',
'﹙' => '(',
'﹚' => ')',
'﹛' => '{',
'﹜' => '}',
'﹝' => '',
'﹞' => '',
'﹟' => '#',
'﹠' => '&',
'﹡' => '*',
'﹢' => '+',
'﹣' => '-',
'﹤' => '<',
'﹥' => '>',
'﹦' => '=',
'' => '\\',
'﹩' => '$',
'﹪' => '%',
'﹫' => '@',
'ﹰ' => ' ً',
'ﹱ' => 'ـً',
'ﹲ' => ' ٌ',
'ﹴ' => ' ٍ',
'ﹶ' => ' َ',
'ﹷ' => 'ـَ',
'ﹸ' => ' ُ',
'ﹹ' => 'ـُ',
'ﹺ' => ' ِ',
'ﹻ' => 'ـِ',
'ﹼ' => ' ّ',
'ﹽ' => 'ـّ',
'ﹾ' => ' ْ',
'ﹿ' => 'ـْ',
'ﺀ' => 'ء',
'ﺁ' => 'آ',
'ﺂ' => 'آ',
'ﺃ' => 'أ',
'ﺄ' => 'أ',
'ﺅ' => 'ؤ',
'ﺆ' => 'ؤ',
'ﺇ' => 'إ',
'ﺈ' => 'إ',
'ﺉ' => 'ئ',
'ﺊ' => 'ئ',
'ﺋ' => 'ئ',
'ﺌ' => 'ئ',
'' => 'ا',
'' => 'ا',
'ﺏ' => 'ب',
'ﺐ' => 'ب',
'ﺑ' => 'ب',
'ﺒ' => 'ب',
'ﺓ' => 'ة',
'ﺔ' => 'ة',
'ﺕ' => 'ت',
'ﺖ' => 'ت',
'ﺗ' => 'ت',
'ﺘ' => 'ت',
'ﺙ' => 'ث',
'ﺚ' => 'ث',
'ﺛ' => 'ث',
'ﺜ' => 'ث',
'ﺝ' => 'ج',
'ﺞ' => 'ج',
'ﺟ' => 'ج',
'ﺠ' => 'ج',
'ﺡ' => 'ح',
'ﺢ' => 'ح',
'ﺣ' => 'ح',
'ﺤ' => 'ح',
'ﺥ' => 'خ',
'ﺦ' => 'خ',
'ﺧ' => 'خ',
'ﺨ' => 'خ',
'ﺩ' => 'د',
'ﺪ' => 'د',
'ﺫ' => 'ذ',
'ﺬ' => 'ذ',
'ﺭ' => 'ر',
'ﺮ' => 'ر',
'ﺯ' => 'ز',
'ﺰ' => 'ز',
'ﺱ' => 'س',
'ﺲ' => 'س',
'ﺳ' => 'س',
'ﺴ' => 'س',
'ﺵ' => 'ش',
'ﺶ' => 'ش',
'ﺷ' => 'ش',
'ﺸ' => 'ش',
'ﺹ' => 'ص',
'ﺺ' => 'ص',
'ﺻ' => 'ص',
'ﺼ' => 'ص',
'ﺽ' => 'ض',
'ﺾ' => 'ض',
'ﺿ' => 'ض',
'ﻀ' => 'ض',
'ﻁ' => 'ط',
'ﻂ' => 'ط',
'ﻃ' => 'ط',
'ﻄ' => 'ط',
'ﻅ' => 'ظ',
'ﻆ' => 'ظ',
'ﻇ' => 'ظ',
'ﻈ' => 'ظ',
'ﻉ' => 'ع',
'ﻊ' => 'ع',
'ﻋ' => 'ع',
'ﻌ' => 'ع',
'ﻍ' => 'غ',
'ﻎ' => 'غ',
'ﻏ' => 'غ',
'ﻐ' => 'غ',
'ﻑ' => 'ف',
'ﻒ' => 'ف',
'ﻓ' => 'ف',
'ﻔ' => 'ف',
'ﻕ' => 'ق',
'ﻖ' => 'ق',
'ﻗ' => 'ق',
'ﻘ' => 'ق',
'ﻙ' => 'ك',
'ﻚ' => 'ك',
'ﻛ' => 'ك',
'ﻜ' => 'ك',
'ﻝ' => 'ل',
'ﻞ' => 'ل',
'ﻟ' => 'ل',
'ﻠ' => 'ل',
'ﻡ' => 'م',
'ﻢ' => 'م',
'ﻣ' => 'م',
'ﻤ' => 'م',
'ﻥ' => 'ن',
'ﻦ' => 'ن',
'ﻧ' => 'ن',
'ﻨ' => 'ن',
'' => 'ه',
'' => 'ه',
'' => 'ه',
'' => 'ه',
'ﻭ' => 'و',
'ﻮ' => 'و',
'ﻯ' => 'ى',
'ﻰ' => 'ى',
'ﻱ' => 'ي',
'ﻲ' => 'ي',
'ﻳ' => 'ي',
'ﻴ' => 'ي',
'ﻵ' => 'لآ',
'ﻶ' => 'لآ',
'ﻷ' => 'لأ',
'ﻸ' => 'لأ',
'ﻹ' => 'لإ',
'ﻺ' => 'لإ',
'ﻻ' => 'لا',
'ﻼ' => 'لا',
'' => '!',
'' => '"',
'' => '#',
'' => '$',
'' => '%',
'' => '&',
'' => '\'',
'' => '(',
'' => ')',
'' => '*',
'' => '+',
'' => ',',
'' => '-',
'' => '.',
'' => '/',
'' => '0',
'' => '1',
'' => '2',
'' => '3',
'' => '4',
'' => '5',
'' => '6',
'' => '7',
'' => '8',
'' => '9',
'' => ':',
'' => ';',
'' => '<',
'' => '=',
'' => '>',
'' => '?',
'' => '@',
'' => 'A',
'' => 'B',
'' => 'C',
'' => 'D',
'' => 'E',
'' => 'F',
'' => 'G',
'' => 'H',
'' => 'I',
'' => 'J',
'' => 'K',
'' => 'L',
'' => 'M',
'' => 'N',
'' => 'O',
'' => 'P',
'' => 'Q',
'' => 'R',
'' => 'S',
'' => 'T',
'' => 'U',
'' => 'V',
'' => 'W',
'' => 'X',
'' => 'Y',
'' => 'Z',
'' => '[',
'' => '\\',
'' => ']',
'' => '^',
'_' => '_',
'' => '`',
'' => 'a',
'' => 'b',
'' => 'c',
'' => 'd',
'' => 'e',
'' => 'f',
'' => 'g',
'' => 'h',
'' => 'i',
'' => 'j',
'' => 'k',
'' => 'l',
'' => 'm',
'' => 'n',
'' => 'o',
'' => 'p',
'' => 'q',
'' => 'r',
'' => 's',
'' => 't',
'' => 'u',
'' => 'v',
'' => 'w',
'' => 'x',
'' => 'y',
'' => 'z',
'' => '{',
'' => '|',
'' => '}',
'' => '~',
'⦅' => '⦅',
'⦆' => '⦆',
'。' => '。',
'「' => '「',
'」' => '」',
'、' => '、',
'・' => '・',
'ヲ' => 'ヲ',
'ァ' => 'ァ',
'ィ' => 'ィ',
'ゥ' => 'ゥ',
'ェ' => 'ェ',
'ォ' => 'ォ',
'ャ' => 'ャ',
'ュ' => 'ュ',
'ョ' => 'ョ',
'ッ' => 'ッ',
'ー' => 'ー',
'ア' => 'ア',
'イ' => 'イ',
'ウ' => 'ウ',
'エ' => 'エ',
'オ' => 'オ',
'カ' => 'カ',
'キ' => 'キ',
'ク' => 'ク',
'ケ' => 'ケ',
'コ' => 'コ',
'サ' => 'サ',
'シ' => 'シ',
'ス' => 'ス',
'セ' => 'セ',
'ソ' => 'ソ',
'タ' => 'タ',
'チ' => 'チ',
'ツ' => 'ツ',
'テ' => 'テ',
'ト' => 'ト',
'ナ' => 'ナ',
'ニ' => 'ニ',
'ヌ' => 'ヌ',
'ネ' => 'ネ',
'ノ' => '',
'ハ' => 'ハ',
'ヒ' => 'ヒ',
'フ' => 'フ',
'ヘ' => 'ヘ',
'ホ' => 'ホ',
'マ' => 'マ',
'ミ' => 'ミ',
'ム' => 'ム',
'メ' => 'メ',
'モ' => 'モ',
'ヤ' => 'ヤ',
'ユ' => 'ユ',
'ヨ' => 'ヨ',
'ラ' => 'ラ',
'リ' => 'リ',
'ル' => 'ル',
'レ' => 'レ',
'ロ' => 'ロ',
'ワ' => 'ワ',
'ン' => 'ン',
'゙' => '゙',
'゚' => '゚',
'' => '',
'ᄀ' => 'ᄀ',
'ᄁ' => 'ᄁ',
'ᆪ' => 'ᆪ',
'ᄂ' => 'ᄂ',
'ᆬ' => 'ᆬ',
'ᆭ' => 'ᆭ',
'ᄃ' => 'ᄃ',
'ᄄ' => 'ᄄ',
'ᄅ' => 'ᄅ',
'ᆰ' => 'ᆰ',
'ᆱ' => 'ᆱ',
'ᆲ' => 'ᆲ',
'ᆳ' => 'ᆳ',
'ᆴ' => 'ᆴ',
'ᆵ' => 'ᆵ',
'ᄚ' => 'ᄚ',
'ᄆ' => 'ᄆ',
'ᄇ' => 'ᄇ',
'ᄈ' => 'ᄈ',
'ᄡ' => 'ᄡ',
'ᄉ' => 'ᄉ',
'ᄊ' => 'ᄊ',
'ᄋ' => 'ᄋ',
'ᄌ' => 'ᄌ',
'ᄍ' => 'ᄍ',
'ᄎ' => 'ᄎ',
'ᄏ' => 'ᄏ',
'ᄐ' => 'ᄐ',
'ᄑ' => 'ᄑ',
'ᄒ' => 'ᄒ',
'ᅡ' => 'ᅡ',
'ᅢ' => 'ᅢ',
'ᅣ' => 'ᅣ',
'ᅤ' => 'ᅤ',
'ᅥ' => 'ᅥ',
'ᅦ' => 'ᅦ',
'ᅧ' => 'ᅧ',
'ᅨ' => 'ᅨ',
'ᅩ' => 'ᅩ',
'ᅪ' => 'ᅪ',
'ᅫ' => 'ᅫ',
'ᅬ' => 'ᅬ',
'ᅭ' => 'ᅭ',
'ᅮ' => 'ᅮ',
'ᅯ' => 'ᅯ',
'ᅰ' => 'ᅰ',
'ᅱ' => 'ᅱ',
'ᅲ' => 'ᅲ',
'ᅳ' => 'ᅳ',
'ᅴ' => 'ᅴ',
'ᅵ' => 'ᅵ',
'¢' => '¢',
'£' => '£',
'¬' => '¬',
' ̄' => ' ̄',
'¦' => '¦',
'¥' => '¥',
'₩' => '₩',
'' => '│',
'←' => '←',
'↑' => '↑',
'→' => '→',
'↓' => '↓',
'■' => '■',
'○' => '○',
'𝐀' => 'A',
'𝐁' => 'B',
'𝐂' => 'C',
'𝐃' => 'D',
'𝐄' => 'E',
'𝐅' => 'F',
'𝐆' => 'G',
'𝐇' => 'H',
'𝐈' => 'I',
'𝐉' => 'J',
'𝐊' => 'K',
'𝐋' => 'L',
'𝐌' => 'M',
'𝐍' => 'N',
'𝐎' => 'O',
'𝐏' => 'P',
'𝐐' => 'Q',
'𝐑' => 'R',
'𝐒' => 'S',
'𝐓' => 'T',
'𝐔' => 'U',
'𝐕' => 'V',
'𝐖' => 'W',
'𝐗' => 'X',
'𝐘' => 'Y',
'𝐙' => 'Z',
'𝐚' => 'a',
'𝐛' => 'b',
'𝐜' => 'c',
'𝐝' => 'd',
'𝐞' => 'e',
'𝐟' => 'f',
'𝐠' => 'g',
'𝐡' => 'h',
'𝐢' => 'i',
'𝐣' => 'j',
'𝐤' => 'k',
'𝐥' => 'l',
'𝐦' => 'm',
'𝐧' => 'n',
'𝐨' => 'o',
'𝐩' => 'p',
'𝐪' => 'q',
'𝐫' => 'r',
'𝐬' => 's',
'𝐭' => 't',
'𝐮' => 'u',
'𝐯' => 'v',
'𝐰' => 'w',
'𝐱' => 'x',
'𝐲' => 'y',
'𝐳' => 'z',
'𝐴' => 'A',
'𝐵' => 'B',
'𝐶' => 'C',
'𝐷' => 'D',
'𝐸' => 'E',
'𝐹' => 'F',
'𝐺' => 'G',
'𝐻' => 'H',
'𝐼' => 'I',
'𝐽' => 'J',
'𝐾' => 'K',
'𝐿' => 'L',
'𝑀' => 'M',
'𝑁' => 'N',
'𝑂' => 'O',
'𝑃' => 'P',
'𝑄' => 'Q',
'𝑅' => 'R',
'𝑆' => 'S',
'𝑇' => 'T',
'𝑈' => 'U',
'𝑉' => 'V',
'𝑊' => 'W',
'𝑋' => 'X',
'𝑌' => 'Y',
'𝑍' => 'Z',
'𝑎' => 'a',
'𝑏' => 'b',
'𝑐' => 'c',
'𝑑' => 'd',
'𝑒' => 'e',
'𝑓' => 'f',
'𝑔' => 'g',
'𝑖' => 'i',
'𝑗' => 'j',
'𝑘' => 'k',
'𝑙' => 'l',
'𝑚' => 'm',
'𝑛' => 'n',
'𝑜' => 'o',
'𝑝' => 'p',
'𝑞' => 'q',
'𝑟' => 'r',
'𝑠' => 's',
'𝑡' => 't',
'𝑢' => 'u',
'𝑣' => 'v',
'𝑤' => 'w',
'𝑥' => 'x',
'𝑦' => 'y',
'𝑧' => 'z',
'𝑨' => 'A',
'𝑩' => 'B',
'𝑪' => 'C',
'𝑫' => 'D',
'𝑬' => 'E',
'𝑭' => 'F',
'𝑮' => 'G',
'𝑯' => 'H',
'𝑰' => 'I',
'𝑱' => 'J',
'𝑲' => 'K',
'𝑳' => 'L',
'𝑴' => 'M',
'𝑵' => 'N',
'𝑶' => 'O',
'𝑷' => 'P',
'𝑸' => 'Q',
'𝑹' => 'R',
'𝑺' => 'S',
'𝑻' => 'T',
'𝑼' => 'U',
'𝑽' => 'V',
'𝑾' => 'W',
'𝑿' => 'X',
'𝒀' => 'Y',
'𝒁' => 'Z',
'𝒂' => 'a',
'𝒃' => 'b',
'𝒄' => 'c',
'𝒅' => 'd',
'𝒆' => 'e',
'𝒇' => 'f',
'𝒈' => 'g',
'𝒉' => 'h',
'𝒊' => 'i',
'𝒋' => 'j',
'𝒌' => 'k',
'𝒍' => 'l',
'𝒎' => 'm',
'𝒏' => 'n',
'𝒐' => 'o',
'𝒑' => 'p',
'𝒒' => 'q',
'𝒓' => 'r',
'𝒔' => 's',
'𝒕' => 't',
'𝒖' => 'u',
'𝒗' => 'v',
'𝒘' => 'w',
'𝒙' => 'x',
'𝒚' => 'y',
'𝒛' => 'z',
'𝒜' => 'A',
'𝒞' => 'C',
'𝒟' => 'D',
'𝒢' => 'G',
'𝒥' => 'J',
'𝒦' => 'K',
'𝒩' => 'N',
'𝒪' => 'O',
'𝒫' => 'P',
'𝒬' => 'Q',
'𝒮' => 'S',
'𝒯' => 'T',
'𝒰' => 'U',
'𝒱' => 'V',
'𝒲' => 'W',
'𝒳' => 'X',
'𝒴' => 'Y',
'𝒵' => 'Z',
'𝒶' => 'a',
'𝒷' => 'b',
'𝒸' => 'c',
'𝒹' => 'd',
'𝒻' => 'f',
'𝒽' => 'h',
'𝒾' => 'i',
'𝒿' => 'j',
'𝓀' => 'k',
'𝓁' => 'l',
'𝓂' => 'm',
'𝓃' => 'n',
'𝓅' => 'p',
'𝓆' => 'q',
'𝓇' => 'r',
'𝓈' => 's',
'𝓉' => 't',
'𝓊' => 'u',
'𝓋' => 'v',
'𝓌' => 'w',
'𝓍' => 'x',
'𝓎' => 'y',
'𝓏' => 'z',
'𝓐' => 'A',
'𝓑' => 'B',
'𝓒' => 'C',
'𝓓' => 'D',
'𝓔' => 'E',
'𝓕' => 'F',
'𝓖' => 'G',
'𝓗' => 'H',
'𝓘' => 'I',
'𝓙' => 'J',
'𝓚' => 'K',
'𝓛' => 'L',
'𝓜' => 'M',
'𝓝' => 'N',
'𝓞' => 'O',
'𝓟' => 'P',
'𝓠' => 'Q',
'𝓡' => 'R',
'𝓢' => 'S',
'𝓣' => 'T',
'𝓤' => 'U',
'𝓥' => 'V',
'𝓦' => 'W',
'𝓧' => 'X',
'𝓨' => 'Y',
'𝓩' => 'Z',
'𝓪' => 'a',
'𝓫' => 'b',
'𝓬' => 'c',
'𝓭' => 'd',
'𝓮' => 'e',
'𝓯' => 'f',
'𝓰' => 'g',
'𝓱' => 'h',
'𝓲' => 'i',
'𝓳' => 'j',
'𝓴' => 'k',
'𝓵' => 'l',
'𝓶' => 'm',
'𝓷' => 'n',
'𝓸' => 'o',
'𝓹' => 'p',
'𝓺' => 'q',
'𝓻' => 'r',
'𝓼' => 's',
'𝓽' => 't',
'𝓾' => 'u',
'𝓿' => 'v',
'𝔀' => 'w',
'𝔁' => 'x',
'𝔂' => 'y',
'𝔃' => 'z',
'𝔄' => 'A',
'𝔅' => 'B',
'𝔇' => 'D',
'𝔈' => 'E',
'𝔉' => 'F',
'𝔊' => 'G',
'𝔍' => 'J',
'𝔎' => 'K',
'𝔏' => 'L',
'𝔐' => 'M',
'𝔑' => 'N',
'𝔒' => 'O',
'𝔓' => 'P',
'𝔔' => 'Q',
'𝔖' => 'S',
'𝔗' => 'T',
'𝔘' => 'U',
'𝔙' => 'V',
'𝔚' => 'W',
'𝔛' => 'X',
'𝔜' => 'Y',
'𝔞' => 'a',
'𝔟' => 'b',
'𝔠' => 'c',
'𝔡' => 'd',
'𝔢' => 'e',
'𝔣' => 'f',
'𝔤' => 'g',
'𝔥' => 'h',
'𝔦' => 'i',
'𝔧' => 'j',
'𝔨' => 'k',
'𝔩' => 'l',
'𝔪' => 'm',
'𝔫' => 'n',
'𝔬' => 'o',
'𝔭' => 'p',
'𝔮' => 'q',
'𝔯' => 'r',
'𝔰' => 's',
'𝔱' => 't',
'𝔲' => 'u',
'𝔳' => 'v',
'𝔴' => 'w',
'𝔵' => 'x',
'𝔶' => 'y',
'𝔷' => 'z',
'𝔸' => 'A',
'𝔹' => 'B',
'𝔻' => 'D',
'𝔼' => 'E',
'𝔽' => 'F',
'𝔾' => 'G',
'𝕀' => 'I',
'𝕁' => 'J',
'𝕂' => 'K',
'𝕃' => 'L',
'𝕄' => 'M',
'𝕆' => 'O',
'𝕊' => 'S',
'𝕋' => 'T',
'𝕌' => 'U',
'𝕍' => 'V',
'𝕎' => 'W',
'𝕏' => 'X',
'𝕐' => 'Y',
'𝕒' => 'a',
'𝕓' => 'b',
'𝕔' => 'c',
'𝕕' => 'd',
'𝕖' => 'e',
'𝕗' => 'f',
'𝕘' => 'g',
'𝕙' => 'h',
'𝕚' => 'i',
'𝕛' => 'j',
'𝕜' => 'k',
'𝕝' => 'l',
'𝕞' => 'm',
'𝕟' => 'n',
'𝕠' => 'o',
'𝕡' => 'p',
'𝕢' => 'q',
'𝕣' => 'r',
'𝕤' => 's',
'𝕥' => 't',
'𝕦' => 'u',
'𝕧' => 'v',
'𝕨' => 'w',
'𝕩' => 'x',
'𝕪' => 'y',
'𝕫' => 'z',
'𝕬' => 'A',
'𝕭' => 'B',
'𝕮' => 'C',
'𝕯' => 'D',
'𝕰' => 'E',
'𝕱' => 'F',
'𝕲' => 'G',
'𝕳' => 'H',
'𝕴' => 'I',
'𝕵' => 'J',
'𝕶' => 'K',
'𝕷' => 'L',
'𝕸' => 'M',
'𝕹' => 'N',
'𝕺' => 'O',
'𝕻' => 'P',
'𝕼' => 'Q',
'𝕽' => 'R',
'𝕾' => 'S',
'𝕿' => 'T',
'𝖀' => 'U',
'𝖁' => 'V',
'𝖂' => 'W',
'𝖃' => 'X',
'𝖄' => 'Y',
'𝖅' => 'Z',
'𝖆' => 'a',
'𝖇' => 'b',
'𝖈' => 'c',
'𝖉' => 'd',
'𝖊' => 'e',
'𝖋' => 'f',
'𝖌' => 'g',
'𝖍' => 'h',
'𝖎' => 'i',
'𝖏' => 'j',
'𝖐' => 'k',
'𝖑' => 'l',
'𝖒' => 'm',
'𝖓' => 'n',
'𝖔' => 'o',
'𝖕' => 'p',
'𝖖' => 'q',
'𝖗' => 'r',
'𝖘' => 's',
'𝖙' => 't',
'𝖚' => 'u',
'𝖛' => 'v',
'𝖜' => 'w',
'𝖝' => 'x',
'𝖞' => 'y',
'𝖟' => 'z',
'𝖠' => 'A',
'𝖡' => 'B',
'𝖢' => 'C',
'𝖣' => 'D',
'𝖤' => 'E',
'𝖥' => 'F',
'𝖦' => 'G',
'𝖧' => 'H',
'𝖨' => 'I',
'𝖩' => 'J',
'𝖪' => 'K',
'𝖫' => 'L',
'𝖬' => 'M',
'𝖭' => 'N',
'𝖮' => 'O',
'𝖯' => 'P',
'𝖰' => 'Q',
'𝖱' => 'R',
'𝖲' => 'S',
'𝖳' => 'T',
'𝖴' => 'U',
'𝖵' => 'V',
'𝖶' => 'W',
'𝖷' => 'X',
'𝖸' => 'Y',
'𝖹' => 'Z',
'𝖺' => 'a',
'𝖻' => 'b',
'𝖼' => 'c',
'𝖽' => 'd',
'𝖾' => 'e',
'𝖿' => 'f',
'𝗀' => 'g',
'𝗁' => 'h',
'𝗂' => 'i',
'𝗃' => 'j',
'𝗄' => 'k',
'𝗅' => 'l',
'𝗆' => 'm',
'𝗇' => 'n',
'𝗈' => 'o',
'𝗉' => 'p',
'𝗊' => 'q',
'𝗋' => 'r',
'𝗌' => 's',
'𝗍' => 't',
'𝗎' => 'u',
'𝗏' => 'v',
'𝗐' => 'w',
'𝗑' => 'x',
'𝗒' => 'y',
'𝗓' => 'z',
'𝗔' => 'A',
'𝗕' => 'B',
'𝗖' => 'C',
'𝗗' => 'D',
'𝗘' => 'E',
'𝗙' => 'F',
'𝗚' => 'G',
'𝗛' => 'H',
'𝗜' => 'I',
'𝗝' => 'J',
'𝗞' => 'K',
'𝗟' => 'L',
'𝗠' => 'M',
'𝗡' => 'N',
'𝗢' => 'O',
'𝗣' => 'P',
'𝗤' => 'Q',
'𝗥' => 'R',
'𝗦' => 'S',
'𝗧' => 'T',
'𝗨' => 'U',
'𝗩' => 'V',
'𝗪' => 'W',
'𝗫' => 'X',
'𝗬' => 'Y',
'𝗭' => 'Z',
'𝗮' => 'a',
'𝗯' => 'b',
'𝗰' => 'c',
'𝗱' => 'd',
'𝗲' => 'e',
'𝗳' => 'f',
'𝗴' => 'g',
'𝗵' => 'h',
'𝗶' => 'i',
'𝗷' => 'j',
'𝗸' => 'k',
'𝗹' => 'l',
'𝗺' => 'm',
'𝗻' => 'n',
'𝗼' => 'o',
'𝗽' => 'p',
'𝗾' => 'q',
'𝗿' => 'r',
'𝘀' => 's',
'𝘁' => 't',
'𝘂' => 'u',
'𝘃' => 'v',
'𝘄' => 'w',
'𝘅' => 'x',
'𝘆' => 'y',
'𝘇' => 'z',
'𝘈' => 'A',
'𝘉' => 'B',
'𝘊' => 'C',
'𝘋' => 'D',
'𝘌' => 'E',
'𝘍' => 'F',
'𝘎' => 'G',
'𝘏' => 'H',
'𝘐' => 'I',
'𝘑' => 'J',
'𝘒' => 'K',
'𝘓' => 'L',
'𝘔' => 'M',
'𝘕' => 'N',
'𝘖' => 'O',
'𝘗' => 'P',
'𝘘' => 'Q',
'𝘙' => 'R',
'𝘚' => 'S',
'𝘛' => 'T',
'𝘜' => 'U',
'𝘝' => 'V',
'𝘞' => 'W',
'𝘟' => 'X',
'𝘠' => 'Y',
'𝘡' => 'Z',
'𝘢' => 'a',
'𝘣' => 'b',
'𝘤' => 'c',
'𝘥' => 'd',
'𝘦' => 'e',
'𝘧' => 'f',
'𝘨' => 'g',
'𝘩' => 'h',
'𝘪' => 'i',
'𝘫' => 'j',
'𝘬' => 'k',
'𝘭' => 'l',
'𝘮' => 'm',
'𝘯' => 'n',
'𝘰' => 'o',
'𝘱' => 'p',
'𝘲' => 'q',
'𝘳' => 'r',
'𝘴' => 's',
'𝘵' => 't',
'𝘶' => 'u',
'𝘷' => 'v',
'𝘸' => 'w',
'𝘹' => 'x',
'𝘺' => 'y',
'𝘻' => 'z',
'𝘼' => 'A',
'𝘽' => 'B',
'𝘾' => 'C',
'𝘿' => 'D',
'𝙀' => 'E',
'𝙁' => 'F',
'𝙂' => 'G',
'𝙃' => 'H',
'𝙄' => 'I',
'𝙅' => 'J',
'𝙆' => 'K',
'𝙇' => 'L',
'𝙈' => 'M',
'𝙉' => 'N',
'𝙊' => 'O',
'𝙋' => 'P',
'𝙌' => 'Q',
'𝙍' => 'R',
'𝙎' => 'S',
'𝙏' => 'T',
'𝙐' => 'U',
'𝙑' => 'V',
'𝙒' => 'W',
'𝙓' => 'X',
'𝙔' => 'Y',
'𝙕' => 'Z',
'𝙖' => 'a',
'𝙗' => 'b',
'𝙘' => 'c',
'𝙙' => 'd',
'𝙚' => 'e',
'𝙛' => 'f',
'𝙜' => 'g',
'𝙝' => 'h',
'𝙞' => 'i',
'𝙟' => 'j',
'𝙠' => 'k',
'𝙡' => 'l',
'𝙢' => 'm',
'𝙣' => 'n',
'𝙤' => 'o',
'𝙥' => 'p',
'𝙦' => 'q',
'𝙧' => 'r',
'𝙨' => 's',
'𝙩' => 't',
'𝙪' => 'u',
'𝙫' => 'v',
'𝙬' => 'w',
'𝙭' => 'x',
'𝙮' => 'y',
'𝙯' => 'z',
'𝙰' => 'A',
'𝙱' => 'B',
'𝙲' => 'C',
'𝙳' => 'D',
'𝙴' => 'E',
'𝙵' => 'F',
'𝙶' => 'G',
'𝙷' => 'H',
'𝙸' => 'I',
'𝙹' => 'J',
'𝙺' => 'K',
'𝙻' => 'L',
'𝙼' => 'M',
'𝙽' => 'N',
'𝙾' => 'O',
'𝙿' => 'P',
'𝚀' => 'Q',
'𝚁' => 'R',
'𝚂' => 'S',
'𝚃' => 'T',
'𝚄' => 'U',
'𝚅' => 'V',
'𝚆' => 'W',
'𝚇' => 'X',
'𝚈' => 'Y',
'𝚉' => 'Z',
'𝚊' => 'a',
'𝚋' => 'b',
'𝚌' => 'c',
'𝚍' => 'd',
'𝚎' => 'e',
'𝚏' => 'f',
'𝚐' => 'g',
'𝚑' => 'h',
'𝚒' => 'i',
'𝚓' => 'j',
'𝚔' => 'k',
'𝚕' => 'l',
'𝚖' => 'm',
'𝚗' => 'n',
'𝚘' => 'o',
'𝚙' => 'p',
'𝚚' => 'q',
'𝚛' => 'r',
'𝚜' => 's',
'𝚝' => 't',
'𝚞' => 'u',
'𝚟' => 'v',
'𝚠' => 'w',
'𝚡' => 'x',
'𝚢' => 'y',
'𝚣' => 'z',
'𝚤' => 'ı',
'𝚥' => 'ȷ',
'𝚨' => 'Α',
'𝚩' => 'Β',
'𝚪' => 'Γ',
'𝚫' => 'Δ',
'𝚬' => 'Ε',
'𝚭' => 'Ζ',
'𝚮' => 'Η',
'𝚯' => 'Θ',
'𝚰' => 'Ι',
'𝚱' => 'Κ',
'𝚲' => 'Λ',
'𝚳' => 'Μ',
'𝚴' => 'Ν',
'𝚵' => 'Ξ',
'𝚶' => 'Ο',
'𝚷' => 'Π',
'𝚸' => 'Ρ',
'𝚹' => 'Θ',
'𝚺' => 'Σ',
'𝚻' => 'Τ',
'𝚼' => 'Υ',
'𝚽' => 'Φ',
'𝚾' => 'Χ',
'𝚿' => 'Ψ',
'𝛀' => 'Ω',
'𝛁' => '∇',
'𝛂' => 'α',
'𝛃' => 'β',
'𝛄' => 'γ',
'𝛅' => 'δ',
'𝛆' => 'ε',
'𝛇' => 'ζ',
'𝛈' => 'η',
'𝛉' => 'θ',
'𝛊' => 'ι',
'𝛋' => 'κ',
'𝛌' => 'λ',
'𝛍' => 'μ',
'𝛎' => 'ν',
'𝛏' => 'ξ',
'𝛐' => 'ο',
'𝛑' => 'π',
'𝛒' => 'ρ',
'𝛓' => 'ς',
'𝛔' => 'σ',
'𝛕' => 'τ',
'𝛖' => 'υ',
'𝛗' => 'φ',
'𝛘' => 'χ',
'𝛙' => 'ψ',
'𝛚' => 'ω',
'𝛛' => '∂',
'𝛜' => 'ε',
'𝛝' => 'θ',
'𝛞' => 'κ',
'𝛟' => 'φ',
'𝛠' => 'ρ',
'𝛡' => 'π',
'𝛢' => 'Α',
'𝛣' => 'Β',
'𝛤' => 'Γ',
'𝛥' => 'Δ',
'𝛦' => 'Ε',
'𝛧' => 'Ζ',
'𝛨' => 'Η',
'𝛩' => 'Θ',
'𝛪' => 'Ι',
'𝛫' => 'Κ',
'𝛬' => 'Λ',
'𝛭' => 'Μ',
'𝛮' => 'Ν',
'𝛯' => 'Ξ',
'𝛰' => 'Ο',
'𝛱' => 'Π',
'𝛲' => 'Ρ',
'𝛳' => 'Θ',
'𝛴' => 'Σ',
'𝛵' => 'Τ',
'𝛶' => 'Υ',
'𝛷' => 'Φ',
'𝛸' => 'Χ',
'𝛹' => 'Ψ',
'𝛺' => 'Ω',
'𝛻' => '∇',
'𝛼' => 'α',
'𝛽' => 'β',
'𝛾' => 'γ',
'𝛿' => 'δ',
'𝜀' => 'ε',
'𝜁' => 'ζ',
'𝜂' => 'η',
'𝜃' => 'θ',
'𝜄' => 'ι',
'𝜅' => 'κ',
'𝜆' => 'λ',
'𝜇' => 'μ',
'𝜈' => 'ν',
'𝜉' => 'ξ',
'𝜊' => 'ο',
'𝜋' => 'π',
'𝜌' => 'ρ',
'𝜍' => 'ς',
'𝜎' => 'σ',
'𝜏' => 'τ',
'𝜐' => 'υ',
'𝜑' => 'φ',
'𝜒' => 'χ',
'𝜓' => 'ψ',
'𝜔' => 'ω',
'𝜕' => '∂',
'𝜖' => 'ε',
'𝜗' => 'θ',
'𝜘' => 'κ',
'𝜙' => 'φ',
'𝜚' => 'ρ',
'𝜛' => 'π',
'𝜜' => 'Α',
'𝜝' => 'Β',
'𝜞' => 'Γ',
'𝜟' => 'Δ',
'𝜠' => 'Ε',
'𝜡' => 'Ζ',
'𝜢' => 'Η',
'𝜣' => 'Θ',
'𝜤' => 'Ι',
'𝜥' => 'Κ',
'𝜦' => 'Λ',
'𝜧' => 'Μ',
'𝜨' => 'Ν',
'𝜩' => 'Ξ',
'𝜪' => 'Ο',
'𝜫' => 'Π',
'𝜬' => 'Ρ',
'𝜭' => 'Θ',
'𝜮' => 'Σ',
'𝜯' => 'Τ',
'𝜰' => 'Υ',
'𝜱' => 'Φ',
'𝜲' => 'Χ',
'𝜳' => 'Ψ',
'𝜴' => 'Ω',
'𝜵' => '∇',
'𝜶' => 'α',
'𝜷' => 'β',
'𝜸' => 'γ',
'𝜹' => 'δ',
'𝜺' => 'ε',
'𝜻' => 'ζ',
'𝜼' => 'η',
'𝜽' => 'θ',
'𝜾' => 'ι',
'𝜿' => 'κ',
'𝝀' => 'λ',
'𝝁' => 'μ',
'𝝂' => 'ν',
'𝝃' => 'ξ',
'𝝄' => 'ο',
'𝝅' => 'π',
'𝝆' => 'ρ',
'𝝇' => 'ς',
'𝝈' => 'σ',
'𝝉' => 'τ',
'𝝊' => 'υ',
'𝝋' => 'φ',
'𝝌' => 'χ',
'𝝍' => 'ψ',
'𝝎' => 'ω',
'𝝏' => '∂',
'𝝐' => 'ε',
'𝝑' => 'θ',
'𝝒' => 'κ',
'𝝓' => 'φ',
'𝝔' => 'ρ',
'𝝕' => 'π',
'𝝖' => 'Α',
'𝝗' => 'Β',
'𝝘' => 'Γ',
'𝝙' => 'Δ',
'𝝚' => 'Ε',
'𝝛' => 'Ζ',
'𝝜' => 'Η',
'𝝝' => 'Θ',
'𝝞' => 'Ι',
'𝝟' => 'Κ',
'𝝠' => 'Λ',
'𝝡' => 'Μ',
'𝝢' => 'Ν',
'𝝣' => 'Ξ',
'𝝤' => 'Ο',
'𝝥' => 'Π',
'𝝦' => 'Ρ',
'𝝧' => 'Θ',
'𝝨' => 'Σ',
'𝝩' => 'Τ',
'𝝪' => 'Υ',
'𝝫' => 'Φ',
'𝝬' => 'Χ',
'𝝭' => 'Ψ',
'𝝮' => 'Ω',
'𝝯' => '∇',
'𝝰' => 'α',
'𝝱' => 'β',
'𝝲' => 'γ',
'𝝳' => 'δ',
'𝝴' => 'ε',
'𝝵' => 'ζ',
'𝝶' => 'η',
'𝝷' => 'θ',
'𝝸' => 'ι',
'𝝹' => 'κ',
'𝝺' => 'λ',
'𝝻' => 'μ',
'𝝼' => 'ν',
'𝝽' => 'ξ',
'𝝾' => 'ο',
'𝝿' => 'π',
'𝞀' => 'ρ',
'𝞁' => 'ς',
'𝞂' => 'σ',
'𝞃' => 'τ',
'𝞄' => 'υ',
'𝞅' => 'φ',
'𝞆' => 'χ',
'𝞇' => 'ψ',
'𝞈' => 'ω',
'𝞉' => '∂',
'𝞊' => 'ε',
'𝞋' => 'θ',
'𝞌' => 'κ',
'𝞍' => 'φ',
'𝞎' => 'ρ',
'𝞏' => 'π',
'𝞐' => 'Α',
'𝞑' => 'Β',
'𝞒' => 'Γ',
'𝞓' => 'Δ',
'𝞔' => 'Ε',
'𝞕' => 'Ζ',
'𝞖' => 'Η',
'𝞗' => 'Θ',
'𝞘' => 'Ι',
'𝞙' => 'Κ',
'𝞚' => 'Λ',
'𝞛' => 'Μ',
'𝞜' => 'Ν',
'𝞝' => 'Ξ',
'𝞞' => 'Ο',
'𝞟' => 'Π',
'𝞠' => 'Ρ',
'𝞡' => 'Θ',
'𝞢' => 'Σ',
'𝞣' => 'Τ',
'𝞤' => 'Υ',
'𝞥' => 'Φ',
'𝞦' => 'Χ',
'𝞧' => 'Ψ',
'𝞨' => 'Ω',
'𝞩' => '∇',
'𝞪' => 'α',
'𝞫' => 'β',
'𝞬' => 'γ',
'𝞭' => 'δ',
'𝞮' => 'ε',
'𝞯' => 'ζ',
'𝞰' => 'η',
'𝞱' => 'θ',
'𝞲' => 'ι',
'𝞳' => 'κ',
'𝞴' => 'λ',
'𝞵' => 'μ',
'𝞶' => 'ν',
'𝞷' => 'ξ',
'𝞸' => 'ο',
'𝞹' => 'π',
'𝞺' => 'ρ',
'𝞻' => 'ς',
'𝞼' => 'σ',
'𝞽' => 'τ',
'𝞾' => 'υ',
'𝞿' => 'φ',
'𝟀' => 'χ',
'𝟁' => 'ψ',
'𝟂' => 'ω',
'𝟃' => '∂',
'𝟄' => 'ε',
'𝟅' => 'θ',
'𝟆' => 'κ',
'𝟇' => 'φ',
'𝟈' => 'ρ',
'𝟉' => 'π',
'𝟊' => 'Ϝ',
'𝟋' => 'ϝ',
'𝟎' => '0',
'𝟏' => '1',
'𝟐' => '2',
'𝟑' => '3',
'𝟒' => '4',
'𝟓' => '5',
'𝟔' => '6',
'𝟕' => '7',
'𝟖' => '8',
'𝟗' => '9',
'𝟘' => '0',
'𝟙' => '1',
'𝟚' => '2',
'𝟛' => '3',
'𝟜' => '4',
'𝟝' => '5',
'𝟞' => '6',
'𝟟' => '7',
'𝟠' => '8',
'𝟡' => '9',
'𝟢' => '0',
'𝟣' => '1',
'𝟤' => '2',
'𝟥' => '3',
'𝟦' => '4',
'𝟧' => '5',
'𝟨' => '6',
'𝟩' => '7',
'𝟪' => '8',
'𝟫' => '9',
'𝟬' => '0',
'𝟭' => '1',
'𝟮' => '2',
'𝟯' => '3',
'𝟰' => '4',
'𝟱' => '5',
'𝟲' => '6',
'𝟳' => '7',
'𝟴' => '8',
'𝟵' => '9',
'𝟶' => '0',
'𝟷' => '1',
'𝟸' => '2',
'𝟹' => '3',
'𝟺' => '4',
'𝟻' => '5',
'𝟼' => '6',
'𝟽' => '7',
'𝟾' => '8',
'𝟿' => '9',
'𞸀' => 'ا',
'𞸁' => 'ب',
'𞸂' => 'ج',
'𞸃' => 'د',
'𞸅' => 'و',
'𞸆' => 'ز',
'𞸇' => 'ح',
'𞸈' => 'ط',
'𞸉' => 'ي',
'𞸊' => 'ك',
'𞸋' => 'ل',
'𞸌' => 'م',
'𞸍' => 'ن',
'𞸎' => 'س',
'𞸏' => 'ع',
'𞸐' => 'ف',
'𞸑' => 'ص',
'𞸒' => 'ق',
'𞸓' => 'ر',
'𞸔' => 'ش',
'𞸕' => 'ت',
'𞸖' => 'ث',
'𞸗' => 'خ',
'𞸘' => 'ذ',
'𞸙' => 'ض',
'𞸚' => 'ظ',
'𞸛' => 'غ',
'𞸜' => 'ٮ',
'𞸝' => 'ں',
'𞸞' => 'ڡ',
'𞸟' => 'ٯ',
'𞸡' => 'ب',
'𞸢' => 'ج',
'𞸤' => 'ه',
'𞸧' => 'ح',
'𞸩' => 'ي',
'𞸪' => 'ك',
'𞸫' => 'ل',
'𞸬' => 'م',
'𞸭' => 'ن',
'𞸮' => 'س',
'𞸯' => 'ع',
'𞸰' => 'ف',
'𞸱' => 'ص',
'𞸲' => 'ق',
'𞸴' => 'ش',
'𞸵' => 'ت',
'𞸶' => 'ث',
'𞸷' => 'خ',
'𞸹' => 'ض',
'𞸻' => 'غ',
'𞹂' => 'ج',
'𞹇' => 'ح',
'𞹉' => 'ي',
'𞹋' => 'ل',
'𞹍' => 'ن',
'𞹎' => 'س',
'𞹏' => 'ع',
'𞹑' => 'ص',
'𞹒' => 'ق',
'𞹔' => 'ش',
'𞹗' => 'خ',
'𞹙' => 'ض',
'𞹛' => 'غ',
'𞹝' => 'ں',
'𞹟' => 'ٯ',
'𞹡' => 'ب',
'𞹢' => 'ج',
'𞹤' => 'ه',
'𞹧' => 'ح',
'𞹨' => 'ط',
'𞹩' => 'ي',
'𞹪' => 'ك',
'𞹬' => 'م',
'𞹭' => 'ن',
'𞹮' => 'س',
'𞹯' => 'ع',
'𞹰' => 'ف',
'𞹱' => 'ص',
'𞹲' => 'ق',
'𞹴' => 'ش',
'𞹵' => 'ت',
'𞹶' => 'ث',
'𞹷' => 'خ',
'𞹹' => 'ض',
'𞹺' => 'ظ',
'𞹻' => 'غ',
'𞹼' => 'ٮ',
'𞹾' => 'ڡ',
'𞺀' => 'ا',
'𞺁' => 'ب',
'𞺂' => 'ج',
'𞺃' => 'د',
'𞺄' => 'ه',
'𞺅' => 'و',
'𞺆' => 'ز',
'𞺇' => 'ح',
'𞺈' => 'ط',
'𞺉' => 'ي',
'𞺋' => 'ل',
'𞺌' => 'م',
'𞺍' => 'ن',
'𞺎' => 'س',
'𞺏' => 'ع',
'𞺐' => 'ف',
'𞺑' => 'ص',
'𞺒' => 'ق',
'𞺓' => 'ر',
'𞺔' => 'ش',
'𞺕' => 'ت',
'𞺖' => 'ث',
'𞺗' => 'خ',
'𞺘' => 'ذ',
'𞺙' => 'ض',
'𞺚' => 'ظ',
'𞺛' => 'غ',
'𞺡' => 'ب',
'𞺢' => 'ج',
'𞺣' => 'د',
'𞺥' => 'و',
'𞺦' => 'ز',
'𞺧' => 'ح',
'𞺨' => 'ط',
'𞺩' => 'ي',
'𞺫' => 'ل',
'𞺬' => 'م',
'𞺭' => 'ن',
'𞺮' => 'س',
'𞺯' => 'ع',
'𞺰' => 'ف',
'𞺱' => 'ص',
'𞺲' => 'ق',
'𞺳' => 'ر',
'𞺴' => 'ش',
'𞺵' => 'ت',
'𞺶' => 'ث',
'𞺷' => 'خ',
'𞺸' => 'ذ',
'𞺹' => 'ض',
'𞺺' => 'ظ',
'𞺻' => 'غ',
'🄀' => '0.',
'🄁' => '0,',
'🄂' => '1,',
'🄃' => '2,',
'🄄' => '3,',
'🄅' => '4,',
'🄆' => '5,',
'🄇' => '6,',
'🄈' => '7,',
'🄉' => '8,',
'🄊' => '9,',
'🄐' => '(A)',
'🄑' => '(B)',
'🄒' => '(C)',
'🄓' => '(D)',
'🄔' => '(E)',
'🄕' => '(F)',
'🄖' => '(G)',
'🄗' => '(H)',
'🄘' => '(I)',
'🄙' => '(J)',
'🄚' => '(K)',
'🄛' => '(L)',
'🄜' => '(M)',
'🄝' => '(N)',
'🄞' => '(O)',
'🄟' => '(P)',
'🄠' => '(Q)',
'🄡' => '(R)',
'🄢' => '(S)',
'🄣' => '(T)',
'🄤' => '(U)',
'🄥' => '(V)',
'🄦' => '(W)',
'🄧' => '(X)',
'🄨' => '(Y)',
'🄩' => '(Z)',
'🄪' => 'S',
'🄫' => 'C',
'🄬' => 'R',
'🄭' => 'CD',
'🄮' => 'WZ',
'🄰' => 'A',
'🄱' => 'B',
'🄲' => 'C',
'🄳' => 'D',
'🄴' => 'E',
'🄵' => 'F',
'🄶' => 'G',
'🄷' => 'H',
'🄸' => 'I',
'🄹' => 'J',
'🄺' => 'K',
'🄻' => 'L',
'🄼' => 'M',
'🄽' => 'N',
'🄾' => 'O',
'🄿' => 'P',
'🅀' => 'Q',
'🅁' => 'R',
'🅂' => 'S',
'🅃' => 'T',
'🅄' => 'U',
'🅅' => 'V',
'🅆' => 'W',
'🅇' => 'X',
'🅈' => 'Y',
'🅉' => 'Z',
'🅊' => 'HV',
'🅋' => 'MV',
'🅌' => 'SD',
'🅍' => 'SS',
'🅎' => 'PPV',
'🅏' => 'WC',
'🅪' => 'MC',
'🅫' => 'MD',
'🅬' => 'MR',
'🆐' => 'DJ',
'🈀' => 'ほか',
'🈁' => 'ココ',
'🈂' => 'サ',
'🈐' => '手',
'🈑' => '字',
'🈒' => '双',
'🈓' => 'デ',
'🈔' => '二',
'🈕' => '多',
'🈖' => '解',
'🈗' => '天',
'🈘' => '交',
'🈙' => '映',
'🈚' => '無',
'🈛' => '料',
'🈜' => '前',
'🈝' => '後',
'🈞' => '再',
'🈟' => '新',
'🈠' => '初',
'🈡' => '終',
'🈢' => '生',
'🈣' => '販',
'🈤' => '声',
'🈥' => '吹',
'🈦' => '演',
'🈧' => '投',
'🈨' => '捕',
'🈩' => '一',
'🈪' => '三',
'🈫' => '遊',
'🈬' => '左',
'🈭' => '中',
'🈮' => '右',
'🈯' => '指',
'🈰' => '走',
'🈱' => '打',
'🈲' => '禁',
'🈳' => '空',
'🈴' => '合',
'🈵' => '満',
'🈶' => '有',
'🈷' => '月',
'🈸' => '申',
'🈹' => '割',
'🈺' => '営',
'🈻' => '配',
'🉀' => '〔本〕',
'🉁' => '〔三〕',
'🉂' => '〔二〕',
'🉃' => '〔安〕',
'🉄' => '〔点〕',
'🉅' => '〔打〕',
'🉆' => '〔盗〕',
'🉇' => '〔勝〕',
'🉈' => '〔敗〕',
'🉐' => '得',
'🉑' => '可',
'🯰' => '0',
'🯱' => '1',
'🯲' => '2',
'🯳' => '3',
'🯴' => '4',
'🯵' => '5',
'🯶' => '6',
'🯷' => '7',
'🯸' => '8',
'🯹' => '9',
);
<?php
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); }
}
<?php
namespace Symfony\Contracts\EventDispatcher;
use Psr\EventDispatcher\EventDispatcherInterface as PsrEventDispatcherInterface;
interface EventDispatcherInterface extends PsrEventDispatcherInterface
{
public function dispatch(object $event, string $eventName = null): object;
}
<?php
namespace Symfony\Contracts\EventDispatcher;
use Psr\EventDispatcher\StoppableEventInterface;
class Event implements StoppableEventInterface
{
private $propagationStopped = false;
public function isPropagationStopped(): bool
{
return $this->propagationStopped;
}
public function stopPropagation(): void
{
$this->propagationStopped = true;
}
}
<?php
namespace Symfony\Component\Finder;
class SplFileInfo extends \SplFileInfo
{
private $relativePath;
private $relativePathname;
public function __construct(string $file, string $relativePath, string $relativePathname)
{
parent::__construct($file);
$this->relativePath = $relativePath;
$this->relativePathname = $relativePathname;
}
public function getRelativePath()
{
return $this->relativePath;
}
public function getRelativePathname()
{
return $this->relativePathname;
}
public function getFilenameWithoutExtension(): string
{
$filename = $this->getFilename();
return pathinfo($filename, \PATHINFO_FILENAME);
}
public function getContents()
{
set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; });
try {
$content = file_get_contents($this->getPathname());
} finally {
restore_error_handler();
}
if (false === $content) {
throw new \RuntimeException($error);
}
return $content;
}
}
<?php
namespace Symfony\Component\Finder\Comparator;
class NumberComparator extends Comparator
{
public function __construct(?string $test)
{
if (null === $test || !preg_match('#^\s*(==|!=|[<>]=?)?\s*([0-9\.]+)\s*([kmg]i?)?\s*$#i', $test, $matches)) {
throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a number test.', $test ?? 'null'));
}
$target = $matches[2];
if (!is_numeric($target)) {
throw new \InvalidArgumentException(sprintf('Invalid number "%s".', $target));
}
if (isset($matches[3])) {
switch (strtolower($matches[3])) {
case 'k':
$target *= 1000;
break;
case 'ki':
$target *= 1024;
break;
case 'm':
$target *= 1000000;
break;
case 'mi':
$target *= 1024 * 1024;
break;
case 'g':
$target *= 1000000000;
break;
case 'gi':
$target *= 1024 * 1024 * 1024;
break;
}
}
parent::__construct($target, $matches[1] ?: '==');
}
}
<?php
namespace Symfony\Component\Finder\Comparator;
class Comparator
{
private $target;
private $operator = '==';
public function __construct(string $target = null, string $operator = '==')
{
if (null === $target) {
trigger_deprecation('symfony/finder', '5.4', 'Constructing a "%s" without setting "$target" is deprecated.', __CLASS__);
}
$this->target = $target;
$this->doSetOperator($operator);
}
public function getTarget()
{
if (null === $this->target) {
trigger_deprecation('symfony/finder', '5.4', 'Calling "%s" without initializing the target is deprecated.', __METHOD__);
}
return $this->target;
}
public function setTarget(string $target)
{
trigger_deprecation('symfony/finder', '5.4', '"%s" is deprecated. Set the target via the constructor instead.', __METHOD__);
$this->target = $target;
}
public function getOperator()
{
return $this->operator;
}
public function setOperator(string $operator)
{
trigger_deprecation('symfony/finder', '5.4', '"%s" is deprecated. Set the operator via the constructor instead.', __METHOD__);
$this->doSetOperator('' === $operator ? '==' : $operator);
}
public function test($test)
{
if (null === $this->target) {
trigger_deprecation('symfony/finder', '5.4', 'Calling "%s" without initializing the target is deprecated.', __METHOD__);
}
switch ($this->operator) {
case '>':
return $test > $this->target;
case '>=':
return $test >= $this->target;
case '<':
return $test < $this->target;
case '<=':
return $test <= $this->target;
case '!=':
return $test != $this->target;
}
return $test == $this->target;
}
private function doSetOperator(string $operator): void
{
if (!\in_array($operator, ['>', '<', '>=', '<=', '==', '!='])) {
throw new \InvalidArgumentException(sprintf('Invalid operator "%s".', $operator));
}
$this->operator = $operator;
}
}
<?php
namespace Symfony\Component\Finder\Comparator;
class DateComparator extends Comparator
{
public function __construct(string $test)
{
if (!preg_match('#^\s*(==|!=|[<>]=?|after|since|before|until)?\s*(.+?)\s*$#i', $test, $matches)) {
throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a date test.', $test));
}
try {
$date = new \DateTime($matches[2]);
$target = $date->format('U');
} catch (\Exception $e) {
throw new \InvalidArgumentException(sprintf('"%s" is not a valid date.', $matches[2]));
}
$operator = $matches[1] ?? '==';
if ('since' === $operator || 'after' === $operator) {
$operator = '>';
}
if ('until' === $operator || 'before' === $operator) {
$operator = '<';
}
parent::__construct($target, $operator);
}
}
<?php
namespace Symfony\Component\Finder;
use Symfony\Component\Finder\Comparator\DateComparator;
use Symfony\Component\Finder\Comparator\NumberComparator;
use Symfony\Component\Finder\Exception\DirectoryNotFoundException;
use Symfony\Component\Finder\Iterator\CustomFilterIterator;
use Symfony\Component\Finder\Iterator\DateRangeFilterIterator;
use Symfony\Component\Finder\Iterator\DepthRangeFilterIterator;
use Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator;
use Symfony\Component\Finder\Iterator\FilecontentFilterIterator;
use Symfony\Component\Finder\Iterator\FilenameFilterIterator;
use Symfony\Component\Finder\Iterator\LazyIterator;
use Symfony\Component\Finder\Iterator\SizeRangeFilterIterator;
use Symfony\Component\Finder\Iterator\SortableIterator;
/**
@implements
*/
class Finder implements \IteratorAggregate, \Countable
{
public const IGNORE_VCS_FILES = 1;
public const IGNORE_DOT_FILES = 2;
public const IGNORE_VCS_IGNORED_FILES = 4;
private $mode = 0;
private $names = [];
private $notNames = [];
private $exclude = [];
private $filters = [];
private $depths = [];
private $sizes = [];
private $followLinks = false;
private $reverseSorting = false;
private $sort = false;
private $ignore = 0;
private $dirs = [];
private $dates = [];
private $iterators = [];
private $contains = [];
private $notContains = [];
private $paths = [];
private $notPaths = [];
private $ignoreUnreadableDirs = false;
private static $vcsPatterns = ['.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg'];
public function __construct()
{
$this->ignore = static::IGNORE_VCS_FILES | static::IGNORE_DOT_FILES;
}
public static function create()
{
return new static();
}
public function directories()
{
$this->mode = Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES;
return $this;
}
public function files()
{
$this->mode = Iterator\FileTypeFilterIterator::ONLY_FILES;
return $this;
}
public function depth($levels)
{
foreach ((array) $levels as $level) {
$this->depths[] = new Comparator\NumberComparator($level);
}
return $this;
}
public function date($dates)
{
foreach ((array) $dates as $date) {
$this->dates[] = new Comparator\DateComparator($date);
}
return $this;
}
public function name($patterns)
{
$this->names = array_merge($this->names, (array) $patterns);
return $this;
}
public function notName($patterns)
{
$this->notNames = array_merge($this->notNames, (array) $patterns);
return $this;
}
public function contains($patterns)
{
$this->contains = array_merge($this->contains, (array) $patterns);
return $this;
}
public function notContains($patterns)
{
$this->notContains = array_merge($this->notContains, (array) $patterns);
return $this;
}
public function path($patterns)
{
$this->paths = array_merge($this->paths, (array) $patterns);
return $this;
}
public function notPath($patterns)
{
$this->notPaths = array_merge($this->notPaths, (array) $patterns);
return $this;
}
public function size($sizes)
{
foreach ((array) $sizes as $size) {
$this->sizes[] = new Comparator\NumberComparator($size);
}
return $this;
}
public function exclude($dirs)
{
$this->exclude = array_merge($this->exclude, (array) $dirs);
return $this;
}
public function ignoreDotFiles(bool $ignoreDotFiles)
{
if ($ignoreDotFiles) {
$this->ignore |= static::IGNORE_DOT_FILES;
} else {
$this->ignore &= ~static::IGNORE_DOT_FILES;
}
return $this;
}
public function ignoreVCS(bool $ignoreVCS)
{
if ($ignoreVCS) {
$this->ignore |= static::IGNORE_VCS_FILES;
} else {
$this->ignore &= ~static::IGNORE_VCS_FILES;
}
return $this;
}
public function ignoreVCSIgnored(bool $ignoreVCSIgnored)
{
if ($ignoreVCSIgnored) {
$this->ignore |= static::IGNORE_VCS_IGNORED_FILES;
} else {
$this->ignore &= ~static::IGNORE_VCS_IGNORED_FILES;
}
return $this;
}
public static function addVCSPattern($pattern)
{
foreach ((array) $pattern as $p) {
self::$vcsPatterns[] = $p;
}
self::$vcsPatterns = array_unique(self::$vcsPatterns);
}
public function sort(\Closure $closure)
{
$this->sort = $closure;
return $this;
}
public function sortByName(bool $useNaturalSort = false)
{
$this->sort = $useNaturalSort ? Iterator\SortableIterator::SORT_BY_NAME_NATURAL : Iterator\SortableIterator::SORT_BY_NAME;
return $this;
}
public function sortByType()
{
$this->sort = Iterator\SortableIterator::SORT_BY_TYPE;
return $this;
}
public function sortByAccessedTime()
{
$this->sort = Iterator\SortableIterator::SORT_BY_ACCESSED_TIME;
return $this;
}
public function reverseSorting()
{
$this->reverseSorting = true;
return $this;
}
public function sortByChangedTime()
{
$this->sort = Iterator\SortableIterator::SORT_BY_CHANGED_TIME;
return $this;
}
public function sortByModifiedTime()
{
$this->sort = Iterator\SortableIterator::SORT_BY_MODIFIED_TIME;
return $this;
}
public function filter(\Closure $closure)
{
$this->filters[] = $closure;
return $this;
}
public function followLinks()
{
$this->followLinks = true;
return $this;
}
public function ignoreUnreadableDirs(bool $ignore = true)
{
$this->ignoreUnreadableDirs = $ignore;
return $this;
}
public function in($dirs)
{
$resolvedDirs = [];
foreach ((array) $dirs as $dir) {
if (is_dir($dir)) {
$resolvedDirs[] = [$this->normalizeDir($dir)];
} elseif ($glob = glob($dir, (\defined('GLOB_BRACE') ? \GLOB_BRACE : 0) | \GLOB_ONLYDIR | \GLOB_NOSORT)) {
sort($glob);
$resolvedDirs[] = array_map([$this, 'normalizeDir'], $glob);
} else {
throw new DirectoryNotFoundException(sprintf('The "%s" directory does not exist.', $dir));
}
}
$this->dirs = array_merge($this->dirs, ...$resolvedDirs);
return $this;
}
#[\ReturnTypeWillChange]
public function getIterator()
{
if (0 === \count($this->dirs) && 0 === \count($this->iterators)) {
throw new \LogicException('You must call one of in() or append() methods before iterating over a Finder.');
}
if (1 === \count($this->dirs) && 0 === \count($this->iterators)) {
$iterator = $this->searchInDirectory($this->dirs[0]);
if ($this->sort || $this->reverseSorting) {
$iterator = (new Iterator\SortableIterator($iterator, $this->sort, $this->reverseSorting))->getIterator();
}
return $iterator;
}
$iterator = new \AppendIterator();
foreach ($this->dirs as $dir) {
$iterator->append(new \IteratorIterator(new LazyIterator(function () use ($dir) {
return $this->searchInDirectory($dir);
})));
}
foreach ($this->iterators as $it) {
$iterator->append($it);
}
if ($this->sort || $this->reverseSorting) {
$iterator = (new Iterator\SortableIterator($iterator, $this->sort, $this->reverseSorting))->getIterator();
}
return $iterator;
}
public function append(iterable $iterator)
{
if ($iterator instanceof \IteratorAggregate) {
$this->iterators[] = $iterator->getIterator();
} elseif ($iterator instanceof \Iterator) {
$this->iterators[] = $iterator;
} elseif (is_iterable($iterator)) {
$it = new \ArrayIterator();
foreach ($iterator as $file) {
$file = $file instanceof \SplFileInfo ? $file : new \SplFileInfo($file);
$it[$file->getPathname()] = $file;
}
$this->iterators[] = $it;
} else {
throw new \InvalidArgumentException('Finder::append() method wrong argument type.');
}
return $this;
}
public function hasResults()
{
foreach ($this->getIterator() as $_) {
return true;
}
return false;
}
#[\ReturnTypeWillChange]
public function count()
{
return iterator_count($this->getIterator());
}
private function searchInDirectory(string $dir): \Iterator
{
$exclude = $this->exclude;
$notPaths = $this->notPaths;
if (static::IGNORE_VCS_FILES === (static::IGNORE_VCS_FILES & $this->ignore)) {
$exclude = array_merge($exclude, self::$vcsPatterns);
}
if (static::IGNORE_DOT_FILES === (static::IGNORE_DOT_FILES & $this->ignore)) {
$notPaths[] = '#(^|/)\..+(/|$)#';
}
$minDepth = 0;
$maxDepth = \PHP_INT_MAX;
foreach ($this->depths as $comparator) {
switch ($comparator->getOperator()) {
case '>':
$minDepth = $comparator->getTarget() + 1;
break;
case '>=':
$minDepth = $comparator->getTarget();
break;
case '<':
$maxDepth = $comparator->getTarget() - 1;
break;
case '<=':
$maxDepth = $comparator->getTarget();
break;
default:
$minDepth = $maxDepth = $comparator->getTarget();
}
}
$flags = \RecursiveDirectoryIterator::SKIP_DOTS;
if ($this->followLinks) {
$flags |= \RecursiveDirectoryIterator::FOLLOW_SYMLINKS;
}
$iterator = new Iterator\RecursiveDirectoryIterator($dir, $flags, $this->ignoreUnreadableDirs);
if ($exclude) {
$iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $exclude);
}
$iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST);
if ($minDepth > 0 || $maxDepth < \PHP_INT_MAX) {
$iterator = new Iterator\DepthRangeFilterIterator($iterator, $minDepth, $maxDepth);
}
if ($this->mode) {
$iterator = new Iterator\FileTypeFilterIterator($iterator, $this->mode);
}
if ($this->names || $this->notNames) {
$iterator = new Iterator\FilenameFilterIterator($iterator, $this->names, $this->notNames);
}
if ($this->contains || $this->notContains) {
$iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains);
}
if ($this->sizes) {
$iterator = new Iterator\SizeRangeFilterIterator($iterator, $this->sizes);
}
if ($this->dates) {
$iterator = new Iterator\DateRangeFilterIterator($iterator, $this->dates);
}
if ($this->filters) {
$iterator = new Iterator\CustomFilterIterator($iterator, $this->filters);
}
if ($this->paths || $notPaths) {
$iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $notPaths);
}
if (static::IGNORE_VCS_IGNORED_FILES === (static::IGNORE_VCS_IGNORED_FILES & $this->ignore)) {
$iterator = new Iterator\VcsIgnoredFilterIterator($iterator, $dir);
}
return $iterator;
}
private function normalizeDir(string $dir): string
{
if ('/' === $dir) {
return $dir;
}
$dir = rtrim($dir, '/'.\DIRECTORY_SEPARATOR);
if (preg_match('#^(ssh2\.)?s?ftp://#', $dir)) {
$dir .= '/';
}
return $dir;
}
}
<?php
namespace Symfony\Component\Finder;
class Glob
{
public static function toRegex(string $glob, bool $strictLeadingDot = true, bool $strictWildcardSlash = true, string $delimiter = '#')
{
$firstByte = true;
$escaping = false;
$inCurlies = 0;
$regex = '';
$sizeGlob = \strlen($glob);
for ($i = 0; $i < $sizeGlob; ++$i) {
$car = $glob[$i];
if ($firstByte && $strictLeadingDot && '.' !== $car) {
$regex .= '(?=[^\.])';
}
$firstByte = '/' === $car;
if ($firstByte && $strictWildcardSlash && isset($glob[$i + 2]) && '**' === $glob[$i + 1].$glob[$i + 2] && (!isset($glob[$i + 3]) || '/' === $glob[$i + 3])) {
$car = '[^/]++/';
if (!isset($glob[$i + 3])) {
$car .= '?';
}
if ($strictLeadingDot) {
$car = '(?=[^\.])'.$car;
}
$car = '/(?:'.$car.')*';
$i += 2 + isset($glob[$i + 3]);
if ('/' === $delimiter) {
$car = str_replace('/', '\\/', $car);
}
}
if ($delimiter === $car || '.' === $car || '(' === $car || ')' === $car || '|' === $car || '+' === $car || '^' === $car || '$' === $car) {
$regex .= "\\$car";
} elseif ('*' === $car) {
$regex .= $escaping ? '\\*' : ($strictWildcardSlash ? '[^/]*' : '.*');
} elseif ('?' === $car) {
$regex .= $escaping ? '\\?' : ($strictWildcardSlash ? '[^/]' : '.');
} elseif ('{' === $car) {
$regex .= $escaping ? '\\{' : '(';
if (!$escaping) {
++$inCurlies;
}
} elseif ('}' === $car && $inCurlies) {
$regex .= $escaping ? '}' : ')';
if (!$escaping) {
--$inCurlies;
}
} elseif (',' === $car && $inCurlies) {
$regex .= $escaping ? ',' : '|';
} elseif ('\\' === $car) {
if ($escaping) {
$regex .= '\\\\';
$escaping = false;
} else {
$escaping = true;
}
continue;
} else {
$regex .= $car;
}
$escaping = false;
}
return $delimiter.'^'.$regex.'$'.$delimiter;
}
}
<?php
namespace Symfony\Component\Finder\Iterator;
class LazyIterator implements \IteratorAggregate
{
private $iteratorFactory;
public function __construct(callable $iteratorFactory)
{
$this->iteratorFactory = $iteratorFactory;
}
public function getIterator(): \Traversable
{
yield from ($this->iteratorFactory)();
}
}
<?php
namespace Symfony\Component\Finder\Iterator;
use Symfony\Component\Finder\Gitignore;
final class VcsIgnoredFilterIterator extends \FilterIterator
{
private $baseDir;
private $gitignoreFilesCache = [];
private $ignoredPathsCache = [];
public function __construct(\Iterator $iterator, string $baseDir)
{
$this->baseDir = $this->normalizePath($baseDir);
parent::__construct($iterator);
}
public function accept(): bool
{
$file = $this->current();
$fileRealPath = $this->normalizePath($file->getRealPath());
return !$this->isIgnored($fileRealPath);
}
private function isIgnored(string $fileRealPath): bool
{
if (is_dir($fileRealPath) && !str_ends_with($fileRealPath, '/')) {
$fileRealPath .= '/';
}
if (isset($this->ignoredPathsCache[$fileRealPath])) {
return $this->ignoredPathsCache[$fileRealPath];
}
$ignored = false;
foreach ($this->parentsDirectoryDownward($fileRealPath) as $parentDirectory) {
if ($this->isIgnored($parentDirectory)) {
break;
}
$fileRelativePath = substr($fileRealPath, \strlen($parentDirectory) + 1);
if (null === $regexps = $this->readGitignoreFile("{$parentDirectory}/.gitignore")) {
continue;
}
[$exclusionRegex, $inclusionRegex] = $regexps;
if (preg_match($exclusionRegex, $fileRelativePath)) {
$ignored = true;
continue;
}
if (preg_match($inclusionRegex, $fileRelativePath)) {
$ignored = false;
}
}
return $this->ignoredPathsCache[$fileRealPath] = $ignored;
}
private function parentsDirectoryDownward(string $fileRealPath): array
{
$parentDirectories = [];
$parentDirectory = $fileRealPath;
while (true) {
$newParentDirectory = \dirname($parentDirectory);
if ($newParentDirectory === $parentDirectory) {
break;
}
$parentDirectory = $newParentDirectory;
if (0 !== strpos($parentDirectory, $this->baseDir)) {
break;
}
$parentDirectories[] = $parentDirectory;
}
return array_reverse($parentDirectories);
}
private function readGitignoreFile(string $path): ?array
{
if (\array_key_exists($path, $this->gitignoreFilesCache)) {
return $this->gitignoreFilesCache[$path];
}
if (!file_exists($path)) {
return $this->gitignoreFilesCache[$path] = null;
}
if (!is_file($path) || !is_readable($path)) {
throw new \RuntimeException("The \"ignoreVCSIgnored\" option cannot be used by the Finder as the \"{$path}\" file is not readable.");
}
$gitignoreFileContent = file_get_contents($path);
return $this->gitignoreFilesCache[$path] = [
Gitignore::toRegex($gitignoreFileContent),
Gitignore::toRegexMatchingNegatedPatterns($gitignoreFileContent),
];
}
private function normalizePath(string $path): string
{
if ('\\' === \DIRECTORY_SEPARATOR) {
return str_replace('\\', '/', $path);
}
return $path;
}
}
<?php
namespace Symfony\Component\Finder\Iterator;
/**
@extends
*/
class FilecontentFilterIterator extends MultiplePcreFilterIterator
{
#[\ReturnTypeWillChange]
public function accept()
{
if (!$this->matchRegexps && !$this->noMatchRegexps) {
return true;
}
$fileinfo = $this->current();
if ($fileinfo->isDir() || !$fileinfo->isReadable()) {
return false;
}
$content = $fileinfo->getContents();
if (!$content) {
return false;
}
return $this->isAccepted($content);
}
protected function toRegex(string $str)
{
return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/';
}
}
<?php
namespace Symfony\Component\Finder\Iterator;
/**
@template-covariant
@template-covariant
@extends
*/
abstract class MultiplePcreFilterIterator extends \FilterIterator
{
protected $matchRegexps = [];
protected $noMatchRegexps = [];
public function __construct(\Iterator $iterator, array $matchPatterns, array $noMatchPatterns)
{
foreach ($matchPatterns as $pattern) {
$this->matchRegexps[] = $this->toRegex($pattern);
}
foreach ($noMatchPatterns as $pattern) {
$this->noMatchRegexps[] = $this->toRegex($pattern);
}
parent::__construct($iterator);
}
protected function isAccepted(string $string)
{
foreach ($this->noMatchRegexps as $regex) {
if (preg_match($regex, $string)) {
return false;
}
}
if ($this->matchRegexps) {
foreach ($this->matchRegexps as $regex) {
if (preg_match($regex, $string)) {
return true;
}
}
return false;
}
return true;
}
protected function isRegex(string $str)
{
$availableModifiers = 'imsxuADU';
if (\PHP_VERSION_ID >= 80200) {
$availableModifiers .= 'n';
}
if (preg_match('/^(.{3,}?)['.$availableModifiers.']*$/', $str, $m)) {
$start = substr($m[1], 0, 1);
$end = substr($m[1], -1);
if ($start === $end) {
return !preg_match('/[*?[:alnum:] \\\\]/', $start);
}
foreach ([['{', '}'], ['(', ')'], ['[', ']'], ['<', '>']] as $delimiters) {
if ($start === $delimiters[0] && $end === $delimiters[1]) {
return true;
}
}
}
return false;
}
abstract protected function toRegex(string $str);
}
<?php
namespace Symfony\Component\Finder\Iterator;
use Symfony\Component\Finder\Comparator\NumberComparator;
/**
@extends
*/
class SizeRangeFilterIterator extends \FilterIterator
{
private $comparators = [];
public function __construct(\Iterator $iterator, array $comparators)
{
$this->comparators = $comparators;
parent::__construct($iterator);
}
#[\ReturnTypeWillChange]
public function accept()
{
$fileinfo = $this->current();
if (!$fileinfo->isFile()) {
return true;
}
$filesize = $fileinfo->getSize();
foreach ($this->comparators as $compare) {
if (!$compare->test($filesize)) {
return false;
}
}
return true;
}
}
<?php
namespace Symfony\Component\Finder\Iterator;
use Symfony\Component\Finder\Glob;
/**
@extends
*/
class FilenameFilterIterator extends MultiplePcreFilterIterator
{
#[\ReturnTypeWillChange]
public function accept()
{
return $this->isAccepted($this->current()->getFilename());
}
protected function toRegex(string $str)
{
return $this->isRegex($str) ? $str : Glob::toRegex($str);
}
}
<?php
namespace Symfony\Component\Finder\Iterator;
use Symfony\Component\Finder\Comparator\DateComparator;
/**
@extends
*/
class DateRangeFilterIterator extends \FilterIterator
{
private $comparators = [];
public function __construct(\Iterator $iterator, array $comparators)
{
$this->comparators = $comparators;
parent::__construct($iterator);
}
#[\ReturnTypeWillChange]
public function accept()
{
$fileinfo = $this->current();
if (!file_exists($fileinfo->getPathname())) {
return false;
}
$filedate = $fileinfo->getMTime();
foreach ($this->comparators as $compare) {
if (!$compare->test($filedate)) {
return false;
}
}
return true;
}
}
<?php
namespace Symfony\Component\Finder\Iterator;
/**
@extends
*/
class CustomFilterIterator extends \FilterIterator
{
private $filters = [];
public function __construct(\Iterator $iterator, array $filters)
{
foreach ($filters as $filter) {
if (!\is_callable($filter)) {
throw new \InvalidArgumentException('Invalid PHP callback.');
}
}
$this->filters = $filters;
parent::__construct($iterator);
}
#[\ReturnTypeWillChange]
public function accept()
{
$fileinfo = $this->current();
foreach ($this->filters as $filter) {
if (false === $filter($fileinfo)) {
return false;
}
}
return true;
}
}
<?php
namespace Symfony\Component\Finder\Iterator;
use Symfony\Component\Finder\Exception\AccessDeniedException;
use Symfony\Component\Finder\SplFileInfo;
class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
{
private $ignoreUnreadableDirs;
private $rewindable;
private $rootPath;
private $subPath;
private $directorySeparator = '/';
public function __construct(string $path, int $flags, bool $ignoreUnreadableDirs = false)
{
if ($flags & (self::CURRENT_AS_PATHNAME | self::CURRENT_AS_SELF)) {
throw new \RuntimeException('This iterator only support returning current as fileinfo.');
}
parent::__construct($path, $flags);
$this->ignoreUnreadableDirs = $ignoreUnreadableDirs;
$this->rootPath = $path;
if ('/' !== \DIRECTORY_SEPARATOR && !($flags & self::UNIX_PATHS)) {
$this->directorySeparator = \DIRECTORY_SEPARATOR;
}
}
#[\ReturnTypeWillChange]
public function current()
{
if (null === $subPathname = $this->subPath) {
$subPathname = $this->subPath = $this->getSubPath();
}
if ('' !== $subPathname) {
$subPathname .= $this->directorySeparator;
}
$subPathname .= $this->getFilename();
if ('/' !== $basePath = $this->rootPath) {
$basePath .= $this->directorySeparator;
}
return new SplFileInfo($basePath.$subPathname, $this->subPath, $subPathname);
}
#[\ReturnTypeWillChange]
public function hasChildren($allowLinks = false)
{
$hasChildren = parent::hasChildren($allowLinks);
if (!$hasChildren || !$this->ignoreUnreadableDirs) {
return $hasChildren;
}
try {
parent::getChildren();
return true;
} catch (\UnexpectedValueException $e) {
return false;
}
}
#[\ReturnTypeWillChange]
public function getChildren()
{
try {
$children = parent::getChildren();
if ($children instanceof self) {
$children->ignoreUnreadableDirs = $this->ignoreUnreadableDirs;
$children->rewindable = &$this->rewindable;
$children->rootPath = $this->rootPath;
}
return $children;
} catch (\UnexpectedValueException $e) {
throw new AccessDeniedException($e->getMessage(), $e->getCode(), $e);
}
}
#[\ReturnTypeWillChange]
public function rewind()
{
if (false === $this->isRewindable()) {
return;
}
parent::rewind();
}
public function isRewindable()
{
if (null !== $this->rewindable) {
return $this->rewindable;
}
if (false !== $stream = @opendir($this->getPath())) {
$infos = stream_get_meta_data($stream);
closedir($stream);
if ($infos['seekable']) {
return $this->rewindable = true;
}
}
return $this->rewindable = false;
}
}
<?php
namespace Symfony\Component\Finder\Iterator;
/**
@extends
@implements
*/
class ExcludeDirectoryFilterIterator extends \FilterIterator implements \RecursiveIterator
{
private $iterator;
private $isRecursive;
private $excludedDirs = [];
private $excludedPattern;
public function __construct(\Iterator $iterator, array $directories)
{
$this->iterator = $iterator;
$this->isRecursive = $iterator instanceof \RecursiveIterator;
$patterns = [];
foreach ($directories as $directory) {
$directory = rtrim($directory, '/');
if (!$this->isRecursive || str_contains($directory, '/')) {
$patterns[] = preg_quote($directory, '#');
} else {
$this->excludedDirs[$directory] = true;
}
}
if ($patterns) {
$this->excludedPattern = '#(?:^|/)(?:'.implode('|', $patterns).')(?:/|$)#';
}
parent::__construct($iterator);
}
#[\ReturnTypeWillChange]
public function accept()
{
if ($this->isRecursive && isset($this->excludedDirs[$this->getFilename()]) && $this->isDir()) {
return false;
}
if ($this->excludedPattern) {
$path = $this->isDir() ? $this->current()->getRelativePathname() : $this->current()->getRelativePath();
$path = str_replace('\\', '/', $path);
return !preg_match($this->excludedPattern, $path);
}
return true;
}
#[\ReturnTypeWillChange]
public function hasChildren()
{
return $this->isRecursive && $this->iterator->hasChildren();
}
#[\ReturnTypeWillChange]
public function getChildren()
{
$children = new self($this->iterator->getChildren(), []);
$children->excludedDirs = $this->excludedDirs;
$children->excludedPattern = $this->excludedPattern;
return $children;
}
}
<?php
namespace Symfony\Component\Finder\Iterator;
/**
@implements
*/
class SortableIterator implements \IteratorAggregate
{
public const SORT_BY_NONE = 0;
public const SORT_BY_NAME = 1;
public const SORT_BY_TYPE = 2;
public const SORT_BY_ACCESSED_TIME = 3;
public const SORT_BY_CHANGED_TIME = 4;
public const SORT_BY_MODIFIED_TIME = 5;
public const SORT_BY_NAME_NATURAL = 6;
private $iterator;
private $sort;
public function __construct(\Traversable $iterator, $sort, bool $reverseOrder = false)
{
$this->iterator = $iterator;
$order = $reverseOrder ? -1 : 1;
if (self::SORT_BY_NAME === $sort) {
$this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) {
return $order * strcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname());
};
} elseif (self::SORT_BY_NAME_NATURAL === $sort) {
$this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) {
return $order * strnatcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname());
};
} elseif (self::SORT_BY_TYPE === $sort) {
$this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) {
if ($a->isDir() && $b->isFile()) {
return -$order;
} elseif ($a->isFile() && $b->isDir()) {
return $order;
}
return $order * strcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname());
};
} elseif (self::SORT_BY_ACCESSED_TIME === $sort) {
$this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) {
return $order * ($a->getATime() - $b->getATime());
};
} elseif (self::SORT_BY_CHANGED_TIME === $sort) {
$this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) {
return $order * ($a->getCTime() - $b->getCTime());
};
} elseif (self::SORT_BY_MODIFIED_TIME === $sort) {
$this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) {
return $order * ($a->getMTime() - $b->getMTime());
};
} elseif (self::SORT_BY_NONE === $sort) {
$this->sort = $order;
} elseif (\is_callable($sort)) {
$this->sort = $reverseOrder ? static function (\SplFileInfo $a, \SplFileInfo $b) use ($sort) { return -$sort($a, $b); } : $sort;
} else {
throw new \InvalidArgumentException('The SortableIterator takes a PHP callable or a valid built-in sort algorithm as an argument.');
}
}
#[\ReturnTypeWillChange]
public function getIterator()
{
if (1 === $this->sort) {
return $this->iterator;
}
$array = iterator_to_array($this->iterator, true);
if (-1 === $this->sort) {
$array = array_reverse($array);
} else {
uasort($array, $this->sort);
}
return new \ArrayIterator($array);
}
}
<?php
namespace Symfony\Component\Finder\Iterator;
/**
@template-covariant
@template-covariant
@extends
*/
class DepthRangeFilterIterator extends \FilterIterator
{
private $minDepth = 0;
public function __construct(\RecursiveIteratorIterator $iterator, int $minDepth = 0, int $maxDepth = \PHP_INT_MAX)
{
$this->minDepth = $minDepth;
$iterator->setMaxDepth(\PHP_INT_MAX === $maxDepth ? -1 : $maxDepth);
parent::__construct($iterator);
}
#[\ReturnTypeWillChange]
public function accept()
{
return $this->getInnerIterator()->getDepth() >= $this->minDepth;
}
}
<?php
namespace Symfony\Component\Finder\Iterator;
/**
@extends
*/
class FileTypeFilterIterator extends \FilterIterator
{
public const ONLY_FILES = 1;
public const ONLY_DIRECTORIES = 2;
private $mode;
public function __construct(\Iterator $iterator, int $mode)
{
$this->mode = $mode;
parent::__construct($iterator);
}
#[\ReturnTypeWillChange]
public function accept()
{
$fileinfo = $this->current();
if (self::ONLY_DIRECTORIES === (self::ONLY_DIRECTORIES & $this->mode) && $fileinfo->isFile()) {
return false;
} elseif (self::ONLY_FILES === (self::ONLY_FILES & $this->mode) && $fileinfo->isDir()) {
return false;
}
return true;
}
}
<?php
namespace Symfony\Component\Finder\Iterator;
/**
@extends
*/
class PathFilterIterator extends MultiplePcreFilterIterator
{
#[\ReturnTypeWillChange]
public function accept()
{
$filename = $this->current()->getRelativePathname();
if ('\\' === \DIRECTORY_SEPARATOR) {
$filename = str_replace('\\', '/', $filename);
}
return $this->isAccepted($filename);
}
protected function toRegex(string $str)
{
return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/';
}
}
<?php
namespace Symfony\Component\Finder;
class Gitignore
{
public static function toRegex(string $gitignoreFileContent): string
{
return self::buildRegex($gitignoreFileContent, false);
}
public static function toRegexMatchingNegatedPatterns(string $gitignoreFileContent): string
{
return self::buildRegex($gitignoreFileContent, true);
}
private static function buildRegex(string $gitignoreFileContent, bool $inverted): string
{
$gitignoreFileContent = preg_replace('~(?<!\\\\)#[^\n\r]*~', '', $gitignoreFileContent);
$gitignoreLines = preg_split('~\r\n?|\n~', $gitignoreFileContent);
$res = self::lineToRegex('');
foreach ($gitignoreLines as $line) {
$line = preg_replace('~(?<!\\\\)[ \t]+$~', '', $line);
if ('!' === substr($line, 0, 1)) {
$line = substr($line, 1);
$isNegative = true;
} else {
$isNegative = false;
}
if ('' !== $line) {
if ($isNegative xor $inverted) {
$res = '(?!'.self::lineToRegex($line).'$)'.$res;
} else {
$res = '(?:'.$res.'|'.self::lineToRegex($line).')';
}
}
}
return '~^(?:'.$res.')~s';
}
private static function lineToRegex(string $gitignoreLine): string
{
if ('' === $gitignoreLine) {
return '$f';
}
$slashPos = strpos($gitignoreLine, '/');
if (false !== $slashPos && \strlen($gitignoreLine) - 1 !== $slashPos) {
if (0 === $slashPos) {
$gitignoreLine = substr($gitignoreLine, 1);
}
$isAbsolute = true;
} else {
$isAbsolute = false;
}
$regex = preg_quote(str_replace('\\', '', $gitignoreLine), '~');
$regex = preg_replace_callback('~\\\\\[((?:\\\\!)?)([^\[\]]*)\\\\\]~', function (array $matches): string {
return '['.('' !== $matches[1] ? '^' : '').str_replace('\\-', '-', $matches[2]).']';
}, $regex);
$regex = preg_replace('~(?:(?:\\\\\*){2,}(/?))+~', '(?:(?:(?!//).(?<!//))+$1)?', $regex);
$regex = preg_replace('~\\\\\*~', '[^/]*', $regex);
$regex = preg_replace('~\\\\\?~', '[^/]', $regex);
return ($isAbsolute ? '' : '(?:[^/]+/)*')
.$regex
.(!str_ends_with($gitignoreLine, '/') ? '(?:$|/)' : '');
}
}
<?php
namespace Symfony\Component\Finder\Exception;
class AccessDeniedException extends \UnexpectedValueException
{
}
<?php
namespace Symfony\Component\Finder\Exception;
class DirectoryNotFoundException extends \InvalidArgumentException
{
}
<?php
namespace Symfony\Component\Stopwatch;
use Symfony\Contracts\Service\ResetInterface;
class_exists(Section::class);
class Stopwatch implements ResetInterface
{
private $morePrecision;
private $sections;
private $activeSections;
public function __construct(bool $morePrecision = false)
{
$this->morePrecision = $morePrecision;
$this->reset();
}
public function getSections()
{
return $this->sections;
}
public function openSection(string $id = null)
{
$current = end($this->activeSections);
if (null !== $id && null === $current->get($id)) {
throw new \LogicException(sprintf('The section "%s" has been started at an other level and cannot be opened.', $id));
}
$this->start('__section__.child', 'section');
$this->activeSections[] = $current->open($id);
$this->start('__section__');
}
public function stopSection(string $id)
{
$this->stop('__section__');
if (1 == \count($this->activeSections)) {
throw new \LogicException('There is no started section to stop.');
}
$this->sections[$id] = array_pop($this->activeSections)->setId($id);
$this->stop('__section__.child');
}
public function start(string $name, string $category = null)
{
return end($this->activeSections)->startEvent($name, $category);
}
public function isStarted(string $name)
{
return end($this->activeSections)->isEventStarted($name);
}
public function stop(string $name)
{
return end($this->activeSections)->stopEvent($name);
}
public function lap(string $name)
{
return end($this->activeSections)->stopEvent($name)->start();
}
public function getEvent(string $name)
{
return end($this->activeSections)->getEvent($name);
}
public function getSectionEvents(string $id)
{
return isset($this->sections[$id]) ? $this->sections[$id]->getEvents() : [];
}
public function reset()
{
$this->sections = $this->activeSections = ['__root__' => new Section(null, $this->morePrecision)];
}
}
<?php
namespace Symfony\Component\Stopwatch;
class Section
{
private $events = [];
private $origin;
private $morePrecision;
private $id;
private $children = [];
public function __construct(float $origin = null, bool $morePrecision = false)
{
$this->origin = $origin;
$this->morePrecision = $morePrecision;
}
public function get(string $id)
{
foreach ($this->children as $child) {
if ($id === $child->getId()) {
return $child;
}
}
return null;
}
public function open(?string $id)
{
if (null === $id || null === $session = $this->get($id)) {
$session = $this->children[] = new self(microtime(true) * 1000, $this->morePrecision);
}
return $session;
}
public function getId()
{
return $this->id;
}
public function setId(string $id)
{
$this->id = $id;
return $this;
}
public function startEvent(string $name, ?string $category)
{
if (!isset($this->events[$name])) {
$this->events[$name] = new StopwatchEvent($this->origin ?: microtime(true) * 1000, $category, $this->morePrecision, $name);
}
return $this->events[$name]->start();
}
public function isEventStarted(string $name)
{
return isset($this->events[$name]) && $this->events[$name]->isStarted();
}
public function stopEvent(string $name)
{
if (!isset($this->events[$name])) {
throw new \LogicException(sprintf('Event "%s" is not started.', $name));
}
return $this->events[$name]->stop();
}
public function lap(string $name)
{
return $this->stopEvent($name)->start();
}
public function getEvent(string $name)
{
if (!isset($this->events[$name])) {
throw new \LogicException(sprintf('Event "%s" is not known.', $name));
}
return $this->events[$name];
}
public function getEvents()
{
return $this->events;
}
}
<?php
namespace Symfony\Component\Stopwatch;
class StopwatchPeriod
{
private $start;
private $end;
private $memory;
public function __construct($start, $end, bool $morePrecision = false)
{
$this->start = $morePrecision ? (float) $start : (int) $start;
$this->end = $morePrecision ? (float) $end : (int) $end;
$this->memory = memory_get_usage(true);
}
public function getStartTime()
{
return $this->start;
}
public function getEndTime()
{
return $this->end;
}
public function getDuration()
{
return $this->end - $this->start;
}
public function getMemory()
{
return $this->memory;
}
public function __toString(): string
{
return sprintf('%.2F MiB - %d ms', $this->getMemory() / 1024 / 1024, $this->getDuration());
}
}
<?php
namespace Symfony\Component\Stopwatch;
class StopwatchEvent
{
private $periods = [];
private $origin;
private $category;
private $morePrecision;
private $started = [];
private $name;
public function __construct(float $origin, string $category = null, bool $morePrecision = false, string $name = null)
{
$this->origin = $this->formatTime($origin);
$this->category = \is_string($category) ? $category : 'default';
$this->morePrecision = $morePrecision;
$this->name = $name ?? 'default';
}
public function getCategory()
{
return $this->category;
}
public function getOrigin()
{
return $this->origin;
}
public function start()
{
$this->started[] = $this->getNow();
return $this;
}
public function stop()
{
if (!\count($this->started)) {
throw new \LogicException('stop() called but start() has not been called before.');
}
$this->periods[] = new StopwatchPeriod(array_pop($this->started), $this->getNow(), $this->morePrecision);
return $this;
}
public function isStarted()
{
return !empty($this->started);
}
public function lap()
{
return $this->stop()->start();
}
public function ensureStopped()
{
while (\count($this->started)) {
$this->stop();
}
}
public function getPeriods()
{
return $this->periods;
}
public function getStartTime()
{
if (isset($this->periods[0])) {
return $this->periods[0]->getStartTime();
}
if ($this->started) {
return $this->started[0];
}
return 0;
}
public function getEndTime()
{
$count = \count($this->periods);
return $count ? $this->periods[$count - 1]->getEndTime() : 0;
}
public function getDuration()
{
$periods = $this->periods;
$left = \count($this->started);
for ($i = $left - 1; $i >= 0; --$i) {
$periods[] = new StopwatchPeriod($this->started[$i], $this->getNow(), $this->morePrecision);
}
$total = 0;
foreach ($periods as $period) {
$total += $period->getDuration();
}
return $total;
}
public function getMemory()
{
$memory = 0;
foreach ($this->periods as $period) {
if ($period->getMemory() > $memory) {
$memory = $period->getMemory();
}
}
return $memory;
}
protected function getNow()
{
return $this->formatTime(microtime(true) * 1000 - $this->origin);
}
private function formatTime(float $time): float
{
return round($time, 1);
}
public function getName(): string
{
return $this->name;
}
public function __toString(): string
{
return sprintf('%s/%s: %.2F MiB - %d ms', $this->getCategory(), $this->getName(), $this->getMemory() / 1024 / 1024, $this->getDuration());
}
}
<?php
namespace Symfony\Component\String;
use Symfony\Component\String\Exception\ExceptionInterface;
use Symfony\Component\String\Exception\InvalidArgumentException;
class CodePointString extends AbstractUnicodeString
{
public function __construct(string $string = '')
{
if ('' !== $string && !preg_match('//u', $string)) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
}
$this->string = $string;
}
public function append(string ...$suffix): AbstractString
{
$str = clone $this;
$str->string .= 1 >= \count($suffix) ? ($suffix[0] ?? '') : implode('', $suffix);
if (!preg_match('//u', $str->string)) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
}
return $str;
}
public function chunk(int $length = 1): array
{
if (1 > $length) {
throw new InvalidArgumentException('The chunk length must be greater than zero.');
}
if ('' === $this->string) {
return [];
}
$rx = '/(';
while (65535 < $length) {
$rx .= '.{65535}';
$length -= 65535;
}
$rx .= '.{'.$length.'})/us';
$str = clone $this;
$chunks = [];
foreach (preg_split($rx, $this->string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY) as $chunk) {
$str->string = $chunk;
$chunks[] = clone $str;
}
return $chunks;
}
public function codePointsAt(int $offset): array
{
$str = $offset ? $this->slice($offset, 1) : $this;
return '' === $str->string ? [] : [mb_ord($str->string, 'UTF-8')];
}
public function endsWith($suffix): bool
{
if ($suffix instanceof AbstractString) {
$suffix = $suffix->string;
} elseif (\is_array($suffix) || $suffix instanceof \Traversable) {
return parent::endsWith($suffix);
} else {
$suffix = (string) $suffix;
}
if ('' === $suffix || !preg_match('//u', $suffix)) {
return false;
}
if ($this->ignoreCase) {
return preg_match('{'.preg_quote($suffix).'$}iuD', $this->string);
}
return \strlen($this->string) >= \strlen($suffix) && 0 === substr_compare($this->string, $suffix, -\strlen($suffix));
}
public function equalsTo($string): bool
{
if ($string instanceof AbstractString) {
$string = $string->string;
} elseif (\is_array($string) || $string instanceof \Traversable) {
return parent::equalsTo($string);
} else {
$string = (string) $string;
}
if ('' !== $string && $this->ignoreCase) {
return \strlen($string) === \strlen($this->string) && 0 === mb_stripos($this->string, $string, 0, 'UTF-8');
}
return $string === $this->string;
}
public function indexOf($needle, int $offset = 0): ?int
{
if ($needle instanceof AbstractString) {
$needle = $needle->string;
} elseif (\is_array($needle) || $needle instanceof \Traversable) {
return parent::indexOf($needle, $offset);
} else {
$needle = (string) $needle;
}
if ('' === $needle) {
return null;
}
$i = $this->ignoreCase ? mb_stripos($this->string, $needle, $offset, 'UTF-8') : mb_strpos($this->string, $needle, $offset, 'UTF-8');
return false === $i ? null : $i;
}
public function indexOfLast($needle, int $offset = 0): ?int
{
if ($needle instanceof AbstractString) {
$needle = $needle->string;
} elseif (\is_array($needle) || $needle instanceof \Traversable) {
return parent::indexOfLast($needle, $offset);
} else {
$needle = (string) $needle;
}
if ('' === $needle) {
return null;
}
$i = $this->ignoreCase ? mb_strripos($this->string, $needle, $offset, 'UTF-8') : mb_strrpos($this->string, $needle, $offset, 'UTF-8');
return false === $i ? null : $i;
}
public function length(): int
{
return mb_strlen($this->string, 'UTF-8');
}
public function prepend(string ...$prefix): AbstractString
{
$str = clone $this;
$str->string = (1 >= \count($prefix) ? ($prefix[0] ?? '') : implode('', $prefix)).$this->string;
if (!preg_match('//u', $str->string)) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
}
return $str;
}
public function replace(string $from, string $to): AbstractString
{
$str = clone $this;
if ('' === $from || !preg_match('//u', $from)) {
return $str;
}
if ('' !== $to && !preg_match('//u', $to)) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
}
if ($this->ignoreCase) {
$str->string = implode($to, preg_split('{'.preg_quote($from).'}iuD', $this->string));
} else {
$str->string = str_replace($from, $to, $this->string);
}
return $str;
}
public function slice(int $start = 0, int $length = null): AbstractString
{
$str = clone $this;
$str->string = mb_substr($this->string, $start, $length, 'UTF-8');
return $str;
}
public function splice(string $replacement, int $start = 0, int $length = null): AbstractString
{
if (!preg_match('//u', $replacement)) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
}
$str = clone $this;
$start = $start ? \strlen(mb_substr($this->string, 0, $start, 'UTF-8')) : 0;
$length = $length ? \strlen(mb_substr($this->string, $start, $length, 'UTF-8')) : $length;
$str->string = substr_replace($this->string, $replacement, $start, $length ?? \PHP_INT_MAX);
return $str;
}
public function split(string $delimiter, int $limit = null, int $flags = null): array
{
if (1 > $limit = $limit ?? \PHP_INT_MAX) {
throw new InvalidArgumentException('Split limit must be a positive integer.');
}
if ('' === $delimiter) {
throw new InvalidArgumentException('Split delimiter is empty.');
}
if (null !== $flags) {
return parent::split($delimiter.'u', $limit, $flags);
}
if (!preg_match('//u', $delimiter)) {
throw new InvalidArgumentException('Split delimiter is not a valid UTF-8 string.');
}
$str = clone $this;
$chunks = $this->ignoreCase
? preg_split('{'.preg_quote($delimiter).'}iuD', $this->string, $limit)
: explode($delimiter, $this->string, $limit);
foreach ($chunks as &$chunk) {
$str->string = $chunk;
$chunk = clone $str;
}
return $chunks;
}
public function startsWith($prefix): bool
{
if ($prefix instanceof AbstractString) {
$prefix = $prefix->string;
} elseif (\is_array($prefix) || $prefix instanceof \Traversable) {
return parent::startsWith($prefix);
} else {
$prefix = (string) $prefix;
}
if ('' === $prefix || !preg_match('//u', $prefix)) {
return false;
}
if ($this->ignoreCase) {
return 0 === mb_stripos($this->string, $prefix, 0, 'UTF-8');
}
return 0 === strncmp($this->string, $prefix, \strlen($prefix));
}
}
<?php
namespace Symfony\Component\String;
use Symfony\Component\String\Exception\ExceptionInterface;
use Symfony\Component\String\Exception\InvalidArgumentException;
use Symfony\Component\String\Exception\RuntimeException;
abstract class AbstractString implements \Stringable, \JsonSerializable
{
public const PREG_PATTERN_ORDER = \PREG_PATTERN_ORDER;
public const PREG_SET_ORDER = \PREG_SET_ORDER;
public const PREG_OFFSET_CAPTURE = \PREG_OFFSET_CAPTURE;
public const PREG_UNMATCHED_AS_NULL = \PREG_UNMATCHED_AS_NULL;
public const PREG_SPLIT = 0;
public const PREG_SPLIT_NO_EMPTY = \PREG_SPLIT_NO_EMPTY;
public const PREG_SPLIT_DELIM_CAPTURE = \PREG_SPLIT_DELIM_CAPTURE;
public const PREG_SPLIT_OFFSET_CAPTURE = \PREG_SPLIT_OFFSET_CAPTURE;
protected $string = '';
protected $ignoreCase = false;
abstract public function __construct(string $string = '');
public static function unwrap(array $values): array
{
foreach ($values as $k => $v) {
if ($v instanceof self) {
$values[$k] = $v->__toString();
} elseif (\is_array($v) && $values[$k] !== $v = static::unwrap($v)) {
$values[$k] = $v;
}
}
return $values;
}
public static function wrap(array $values): array
{
$i = 0;
$keys = null;
foreach ($values as $k => $v) {
if (\is_string($k) && '' !== $k && $k !== $j = (string) new static($k)) {
$keys = $keys ?? array_keys($values);
$keys[$i] = $j;
}
if (\is_string($v)) {
$values[$k] = new static($v);
} elseif (\is_array($v) && $values[$k] !== $v = static::wrap($v)) {
$values[$k] = $v;
}
++$i;
}
return null !== $keys ? array_combine($keys, $values) : $values;
}
public function after($needle, bool $includeNeedle = false, int $offset = 0): self
{
$str = clone $this;
$i = \PHP_INT_MAX;
foreach ((array) $needle as $n) {
$n = (string) $n;
$j = $this->indexOf($n, $offset);
if (null !== $j && $j < $i) {
$i = $j;
$str->string = $n;
}
}
if (\PHP_INT_MAX === $i) {
return $str;
}
if (!$includeNeedle) {
$i += $str->length();
}
return $this->slice($i);
}
public function afterLast($needle, bool $includeNeedle = false, int $offset = 0): self
{
$str = clone $this;
$i = null;
foreach ((array) $needle as $n) {
$n = (string) $n;
$j = $this->indexOfLast($n, $offset);
if (null !== $j && $j >= $i) {
$i = $offset = $j;
$str->string = $n;
}
}
if (null === $i) {
return $str;
}
if (!$includeNeedle) {
$i += $str->length();
}
return $this->slice($i);
}
abstract public function append(string ...$suffix): self;
public function before($needle, bool $includeNeedle = false, int $offset = 0): self
{
$str = clone $this;
$i = \PHP_INT_MAX;
foreach ((array) $needle as $n) {
$n = (string) $n;
$j = $this->indexOf($n, $offset);
if (null !== $j && $j < $i) {
$i = $j;
$str->string = $n;
}
}
if (\PHP_INT_MAX === $i) {
return $str;
}
if ($includeNeedle) {
$i += $str->length();
}
return $this->slice(0, $i);
}
public function beforeLast($needle, bool $includeNeedle = false, int $offset = 0): self
{
$str = clone $this;
$i = null;
foreach ((array) $needle as $n) {
$n = (string) $n;
$j = $this->indexOfLast($n, $offset);
if (null !== $j && $j >= $i) {
$i = $offset = $j;
$str->string = $n;
}
}
if (null === $i) {
return $str;
}
if ($includeNeedle) {
$i += $str->length();
}
return $this->slice(0, $i);
}
public function bytesAt(int $offset): array
{
$str = $this->slice($offset, 1);
return '' === $str->string ? [] : array_values(unpack('C*', $str->string));
}
abstract public function camel(): self;
abstract public function chunk(int $length = 1): array;
public function collapseWhitespace(): self
{
$str = clone $this;
$str->string = trim(preg_replace("/(?:[ \n\r\t\x0C]{2,}+|[\n\r\t\x0C])/", ' ', $str->string), " \n\r\t\x0C");
return $str;
}
public function containsAny($needle): bool
{
return null !== $this->indexOf($needle);
}
public function endsWith($suffix): bool
{
if (!\is_array($suffix) && !$suffix instanceof \Traversable) {
throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
}
foreach ($suffix as $s) {
if ($this->endsWith((string) $s)) {
return true;
}
}
return false;
}
public function ensureEnd(string $suffix): self
{
if (!$this->endsWith($suffix)) {
return $this->append($suffix);
}
$suffix = preg_quote($suffix);
$regex = '{('.$suffix.')(?:'.$suffix.')++$}D';
return $this->replaceMatches($regex.($this->ignoreCase ? 'i' : ''), '$1');
}
public function ensureStart(string $prefix): self
{
$prefix = new static($prefix);
if (!$this->startsWith($prefix)) {
return $this->prepend($prefix);
}
$str = clone $this;
$i = $prefixLen = $prefix->length();
while ($this->indexOf($prefix, $i) === $i) {
$str = $str->slice($prefixLen);
$i += $prefixLen;
}
return $str;
}
public function equalsTo($string): bool
{
if (!\is_array($string) && !$string instanceof \Traversable) {
throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
}
foreach ($string as $s) {
if ($this->equalsTo((string) $s)) {
return true;
}
}
return false;
}
abstract public function folded(): self;
public function ignoreCase(): self
{
$str = clone $this;
$str->ignoreCase = true;
return $str;
}
public function indexOf($needle, int $offset = 0): ?int
{
if (!\is_array($needle) && !$needle instanceof \Traversable) {
throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
}
$i = \PHP_INT_MAX;
foreach ($needle as $n) {
$j = $this->indexOf((string) $n, $offset);
if (null !== $j && $j < $i) {
$i = $j;
}
}
return \PHP_INT_MAX === $i ? null : $i;
}
public function indexOfLast($needle, int $offset = 0): ?int
{
if (!\is_array($needle) && !$needle instanceof \Traversable) {
throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
}
$i = null;
foreach ($needle as $n) {
$j = $this->indexOfLast((string) $n, $offset);
if (null !== $j && $j >= $i) {
$i = $offset = $j;
}
}
return $i;
}
public function isEmpty(): bool
{
return '' === $this->string;
}
abstract public function join(array $strings, string $lastGlue = null): self;
public function jsonSerialize(): string
{
return $this->string;
}
abstract public function length(): int;
abstract public function lower(): self;
abstract public function match(string $regexp, int $flags = 0, int $offset = 0): array;
abstract public function padBoth(int $length, string $padStr = ' '): self;
abstract public function padEnd(int $length, string $padStr = ' '): self;
abstract public function padStart(int $length, string $padStr = ' '): self;
abstract public function prepend(string ...$prefix): self;
public function repeat(int $multiplier): self
{
if (0 > $multiplier) {
throw new InvalidArgumentException(sprintf('Multiplier must be positive, %d given.', $multiplier));
}
$str = clone $this;
$str->string = str_repeat($str->string, $multiplier);
return $str;
}
abstract public function replace(string $from, string $to): self;
abstract public function replaceMatches(string $fromRegexp, $to): self;
abstract public function reverse(): self;
abstract public function slice(int $start = 0, int $length = null): self;
abstract public function snake(): self;
abstract public function splice(string $replacement, int $start = 0, int $length = null): self;
public function split(string $delimiter, int $limit = null, int $flags = null): array
{
if (null === $flags) {
throw new \TypeError('Split behavior when $flags is null must be implemented by child classes.');
}
if ($this->ignoreCase) {
$delimiter .= 'i';
}
set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); });
try {
if (false === $chunks = preg_split($delimiter, $this->string, $limit, $flags)) {
$lastError = preg_last_error();
foreach (get_defined_constants(true)['pcre'] as $k => $v) {
if ($lastError === $v && '_ERROR' === substr($k, -6)) {
throw new RuntimeException('Splitting failed with '.$k.'.');
}
}
throw new RuntimeException('Splitting failed with unknown error code.');
}
} finally {
restore_error_handler();
}
$str = clone $this;
if (self::PREG_SPLIT_OFFSET_CAPTURE & $flags) {
foreach ($chunks as &$chunk) {
$str->string = $chunk[0];
$chunk[0] = clone $str;
}
} else {
foreach ($chunks as &$chunk) {
$str->string = $chunk;
$chunk = clone $str;
}
}
return $chunks;
}
public function startsWith($prefix): bool
{
if (!\is_array($prefix) && !$prefix instanceof \Traversable) {
throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
}
foreach ($prefix as $prefix) {
if ($this->startsWith((string) $prefix)) {
return true;
}
}
return false;
}
abstract public function title(bool $allWords = false): self;
public function toByteString(string $toEncoding = null): ByteString
{
$b = new ByteString();
$toEncoding = \in_array($toEncoding, ['utf8', 'utf-8', 'UTF8'], true) ? 'UTF-8' : $toEncoding;
if (null === $toEncoding || $toEncoding === $fromEncoding = $this instanceof AbstractUnicodeString || preg_match('//u', $b->string) ? 'UTF-8' : 'Windows-1252') {
$b->string = $this->string;
return $b;
}
set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); });
try {
try {
$b->string = mb_convert_encoding($this->string, $toEncoding, 'UTF-8');
} catch (InvalidArgumentException $e) {
if (!\function_exists('iconv')) {
throw $e;
}
$b->string = iconv('UTF-8', $toEncoding, $this->string);
}
} finally {
restore_error_handler();
}
return $b;
}
public function toCodePointString(): CodePointString
{
return new CodePointString($this->string);
}
public function toString(): string
{
return $this->string;
}
public function toUnicodeString(): UnicodeString
{
return new UnicodeString($this->string);
}
abstract public function trim(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): self;
abstract public function trimEnd(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): self;
public function trimPrefix($prefix): self
{
if (\is_array($prefix) || $prefix instanceof \Traversable) {
foreach ($prefix as $s) {
$t = $this->trimPrefix($s);
if ($t->string !== $this->string) {
return $t;
}
}
return clone $this;
}
$str = clone $this;
if ($prefix instanceof self) {
$prefix = $prefix->string;
} else {
$prefix = (string) $prefix;
}
if ('' !== $prefix && \strlen($this->string) >= \strlen($prefix) && 0 === substr_compare($this->string, $prefix, 0, \strlen($prefix), $this->ignoreCase)) {
$str->string = substr($this->string, \strlen($prefix));
}
return $str;
}
abstract public function trimStart(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): self;
public function trimSuffix($suffix): self
{
if (\is_array($suffix) || $suffix instanceof \Traversable) {
foreach ($suffix as $s) {
$t = $this->trimSuffix($s);
if ($t->string !== $this->string) {
return $t;
}
}
return clone $this;
}
$str = clone $this;
if ($suffix instanceof self) {
$suffix = $suffix->string;
} else {
$suffix = (string) $suffix;
}
if ('' !== $suffix && \strlen($this->string) >= \strlen($suffix) && 0 === substr_compare($this->string, $suffix, -\strlen($suffix), null, $this->ignoreCase)) {
$str->string = substr($this->string, 0, -\strlen($suffix));
}
return $str;
}
public function truncate(int $length, string $ellipsis = '', bool $cut = true): self
{
$stringLength = $this->length();
if ($stringLength <= $length) {
return clone $this;
}
$ellipsisLength = '' !== $ellipsis ? (new static($ellipsis))->length() : 0;
if ($length < $ellipsisLength) {
$ellipsisLength = 0;
}
if (!$cut) {
if (null === $length = $this->indexOf([' ', "\r", "\n", "\t"], ($length ?: 1) - 1)) {
return clone $this;
}
$length += $ellipsisLength;
}
$str = $this->slice(0, $length - $ellipsisLength);
return $ellipsisLength ? $str->trimEnd()->append($ellipsis) : $str;
}
abstract public function upper(): self;
abstract public function width(bool $ignoreAnsiDecoration = true): int;
public function wordwrap(int $width = 75, string $break = "\n", bool $cut = false): self
{
$lines = '' !== $break ? $this->split($break) : [clone $this];
$chars = [];
$mask = '';
if (1 === \count($lines) && '' === $lines[0]->string) {
return $lines[0];
}
foreach ($lines as $i => $line) {
if ($i) {
$chars[] = $break;
$mask .= '#';
}
foreach ($line->chunk() as $char) {
$chars[] = $char->string;
$mask .= ' ' === $char->string ? ' ' : '?';
}
}
$string = '';
$j = 0;
$b = $i = -1;
$mask = wordwrap($mask, $width, '#', $cut);
while (false !== $b = strpos($mask, '#', $b + 1)) {
for (++$i; $i < $b; ++$i) {
$string .= $chars[$j];
unset($chars[$j++]);
}
if ($break === $chars[$j] || ' ' === $chars[$j]) {
unset($chars[$j++]);
}
$string .= $break;
}
$str = clone $this;
$str->string = $string.implode('', $chars);
return $str;
}
public function __sleep(): array
{
return ['string'];
}
public function __clone()
{
$this->ignoreCase = false;
}
public function __toString(): string
{
return $this->string;
}
}
<?php
namespace Symfony\Component\String;
if (!\function_exists(u::class)) {
function u(?string $string = ''): UnicodeString
{
return new UnicodeString($string ?? '');
}
}
if (!\function_exists(b::class)) {
function b(?string $string = ''): ByteString
{
return new ByteString($string ?? '');
}
}
if (!\function_exists(s::class)) {
function s(?string $string = ''): AbstractString
{
$string = $string ?? '';
return preg_match('//u', $string) ? new UnicodeString($string) : new ByteString($string);
}
}
<?php
return [
[
768,
879,
],
[
1155,
1159,
],
[
1160,
1161,
],
[
1425,
1469,
],
[
1471,
1471,
],
[
1473,
1474,
],
[
1476,
1477,
],
[
1479,
1479,
],
[
1552,
1562,
],
[
1611,
1631,
],
[
1648,
1648,
],
[
1750,
1756,
],
[
1759,
1764,
],
[
1767,
1768,
],
[
1770,
1773,
],
[
1809,
1809,
],
[
1840,
1866,
],
[
1958,
1968,
],
[
2027,
2035,
],
[
2045,
2045,
],
[
2070,
2073,
],
[
2075,
2083,
],
[
2085,
2087,
],
[
2089,
2093,
],
[
2137,
2139,
],
[
2200,
2207,
],
[
2250,
2273,
],
[
2275,
2306,
],
[
2362,
2362,
],
[
2364,
2364,
],
[
2369,
2376,
],
[
2381,
2381,
],
[
2385,
2391,
],
[
2402,
2403,
],
[
2433,
2433,
],
[
2492,
2492,
],
[
2497,
2500,
],
[
2509,
2509,
],
[
2530,
2531,
],
[
2558,
2558,
],
[
2561,
2562,
],
[
2620,
2620,
],
[
2625,
2626,
],
[
2631,
2632,
],
[
2635,
2637,
],
[
2641,
2641,
],
[
2672,
2673,
],
[
2677,
2677,
],
[
2689,
2690,
],
[
2748,
2748,
],
[
2753,
2757,
],
[
2759,
2760,
],
[
2765,
2765,
],
[
2786,
2787,
],
[
2810,
2815,
],
[
2817,
2817,
],
[
2876,
2876,
],
[
2879,
2879,
],
[
2881,
2884,
],
[
2893,
2893,
],
[
2901,
2902,
],
[
2914,
2915,
],
[
2946,
2946,
],
[
3008,
3008,
],
[
3021,
3021,
],
[
3072,
3072,
],
[
3076,
3076,
],
[
3132,
3132,
],
[
3134,
3136,
],
[
3142,
3144,
],
[
3146,
3149,
],
[
3157,
3158,
],
[
3170,
3171,
],
[
3201,
3201,
],
[
3260,
3260,
],
[
3263,
3263,
],
[
3270,
3270,
],
[
3276,
3277,
],
[
3298,
3299,
],
[
3328,
3329,
],
[
3387,
3388,
],
[
3393,
3396,
],
[
3405,
3405,
],
[
3426,
3427,
],
[
3457,
3457,
],
[
3530,
3530,
],
[
3538,
3540,
],
[
3542,
3542,
],
[
3633,
3633,
],
[
3636,
3642,
],
[
3655,
3662,
],
[
3761,
3761,
],
[
3764,
3772,
],
[
3784,
3790,
],
[
3864,
3865,
],
[
3893,
3893,
],
[
3895,
3895,
],
[
3897,
3897,
],
[
3953,
3966,
],
[
3968,
3972,
],
[
3974,
3975,
],
[
3981,
3991,
],
[
3993,
4028,
],
[
4038,
4038,
],
[
4141,
4144,
],
[
4146,
4151,
],
[
4153,
4154,
],
[
4157,
4158,
],
[
4184,
4185,
],
[
4190,
4192,
],
[
4209,
4212,
],
[
4226,
4226,
],
[
4229,
4230,
],
[
4237,
4237,
],
[
4253,
4253,
],
[
4957,
4959,
],
[
5906,
5908,
],
[
5938,
5939,
],
[
5970,
5971,
],
[
6002,
6003,
],
[
6068,
6069,
],
[
6071,
6077,
],
[
6086,
6086,
],
[
6089,
6099,
],
[
6109,
6109,
],
[
6155,
6157,
],
[
6159,
6159,
],
[
6277,
6278,
],
[
6313,
6313,
],
[
6432,
6434,
],
[
6439,
6440,
],
[
6450,
6450,
],
[
6457,
6459,
],
[
6679,
6680,
],
[
6683,
6683,
],
[
6742,
6742,
],
[
6744,
6750,
],
[
6752,
6752,
],
[
6754,
6754,
],
[
6757,
6764,
],
[
6771,
6780,
],
[
6783,
6783,
],
[
6832,
6845,
],
[
6846,
6846,
],
[
6847,
6862,
],
[
6912,
6915,
],
[
6964,
6964,
],
[
6966,
6970,
],
[
6972,
6972,
],
[
6978,
6978,
],
[
7019,
7027,
],
[
7040,
7041,
],
[
7074,
7077,
],
[
7080,
7081,
],
[
7083,
7085,
],
[
7142,
7142,
],
[
7144,
7145,
],
[
7149,
7149,
],
[
7151,
7153,
],
[
7212,
7219,
],
[
7222,
7223,
],
[
7376,
7378,
],
[
7380,
7392,
],
[
7394,
7400,
],
[
7405,
7405,
],
[
7412,
7412,
],
[
7416,
7417,
],
[
7616,
7679,
],
[
8400,
8412,
],
[
8413,
8416,
],
[
8417,
8417,
],
[
8418,
8420,
],
[
8421,
8432,
],
[
11503,
11505,
],
[
11647,
11647,
],
[
11744,
11775,
],
[
12330,
12333,
],
[
12441,
12442,
],
[
42607,
42607,
],
[
42608,
42610,
],
[
42612,
42621,
],
[
42654,
42655,
],
[
42736,
42737,
],
[
43010,
43010,
],
[
43014,
43014,
],
[
43019,
43019,
],
[
43045,
43046,
],
[
43052,
43052,
],
[
43204,
43205,
],
[
43232,
43249,
],
[
43263,
43263,
],
[
43302,
43309,
],
[
43335,
43345,
],
[
43392,
43394,
],
[
43443,
43443,
],
[
43446,
43449,
],
[
43452,
43453,
],
[
43493,
43493,
],
[
43561,
43566,
],
[
43569,
43570,
],
[
43573,
43574,
],
[
43587,
43587,
],
[
43596,
43596,
],
[
43644,
43644,
],
[
43696,
43696,
],
[
43698,
43700,
],
[
43703,
43704,
],
[
43710,
43711,
],
[
43713,
43713,
],
[
43756,
43757,
],
[
43766,
43766,
],
[
44005,
44005,
],
[
44008,
44008,
],
[
44013,
44013,
],
[
64286,
64286,
],
[
65024,
65039,
],
[
65056,
65071,
],
[
66045,
66045,
],
[
66272,
66272,
],
[
66422,
66426,
],
[
68097,
68099,
],
[
68101,
68102,
],
[
68108,
68111,
],
[
68152,
68154,
],
[
68159,
68159,
],
[
68325,
68326,
],
[
68900,
68903,
],
[
69291,
69292,
],
[
69373,
69375,
],
[
69446,
69456,
],
[
69506,
69509,
],
[
69633,
69633,
],
[
69688,
69702,
],
[
69744,
69744,
],
[
69747,
69748,
],
[
69759,
69761,
],
[
69811,
69814,
],
[
69817,
69818,
],
[
69826,
69826,
],
[
69888,
69890,
],
[
69927,
69931,
],
[
69933,
69940,
],
[
70003,
70003,
],
[
70016,
70017,
],
[
70070,
70078,
],
[
70089,
70092,
],
[
70095,
70095,
],
[
70191,
70193,
],
[
70196,
70196,
],
[
70198,
70199,
],
[
70206,
70206,
],
[
70209,
70209,
],
[
70367,
70367,
],
[
70371,
70378,
],
[
70400,
70401,
],
[
70459,
70460,
],
[
70464,
70464,
],
[
70502,
70508,
],
[
70512,
70516,
],
[
70712,
70719,
],
[
70722,
70724,
],
[
70726,
70726,
],
[
70750,
70750,
],
[
70835,
70840,
],
[
70842,
70842,
],
[
70847,
70848,
],
[
70850,
70851,
],
[
71090,
71093,
],
[
71100,
71101,
],
[
71103,
71104,
],
[
71132,
71133,
],
[
71219,
71226,
],
[
71229,
71229,
],
[
71231,
71232,
],
[
71339,
71339,
],
[
71341,
71341,
],
[
71344,
71349,
],
[
71351,
71351,
],
[
71453,
71455,
],
[
71458,
71461,
],
[
71463,
71467,
],
[
71727,
71735,
],
[
71737,
71738,
],
[
71995,
71996,
],
[
71998,
71998,
],
[
72003,
72003,
],
[
72148,
72151,
],
[
72154,
72155,
],
[
72160,
72160,
],
[
72193,
72202,
],
[
72243,
72248,
],
[
72251,
72254,
],
[
72263,
72263,
],
[
72273,
72278,
],
[
72281,
72283,
],
[
72330,
72342,
],
[
72344,
72345,
],
[
72752,
72758,
],
[
72760,
72765,
],
[
72767,
72767,
],
[
72850,
72871,
],
[
72874,
72880,
],
[
72882,
72883,
],
[
72885,
72886,
],
[
73009,
73014,
],
[
73018,
73018,
],
[
73020,
73021,
],
[
73023,
73029,
],
[
73031,
73031,
],
[
73104,
73105,
],
[
73109,
73109,
],
[
73111,
73111,
],
[
73459,
73460,
],
[
73472,
73473,
],
[
73526,
73530,
],
[
73536,
73536,
],
[
73538,
73538,
],
[
78912,
78912,
],
[
78919,
78933,
],
[
92912,
92916,
],
[
92976,
92982,
],
[
94031,
94031,
],
[
94095,
94098,
],
[
94180,
94180,
],
[
113821,
113822,
],
[
118528,
118573,
],
[
118576,
118598,
],
[
119143,
119145,
],
[
119163,
119170,
],
[
119173,
119179,
],
[
119210,
119213,
],
[
119362,
119364,
],
[
121344,
121398,
],
[
121403,
121452,
],
[
121461,
121461,
],
[
121476,
121476,
],
[
121499,
121503,
],
[
121505,
121519,
],
[
122880,
122886,
],
[
122888,
122904,
],
[
122907,
122913,
],
[
122915,
122916,
],
[
122918,
122922,
],
[
123023,
123023,
],
[
123184,
123190,
],
[
123566,
123566,
],
[
123628,
123631,
],
[
124140,
124143,
],
[
125136,
125142,
],
[
125252,
125258,
],
[
917760,
917999,
],
];
<?php
return [
[
4352,
4447,
],
[
8986,
8987,
],
[
9001,
9001,
],
[
9002,
9002,
],
[
9193,
9196,
],
[
9200,
9200,
],
[
9203,
9203,
],
[
9725,
9726,
],
[
9748,
9749,
],
[
9800,
9811,
],
[
9855,
9855,
],
[
9875,
9875,
],
[
9889,
9889,
],
[
9898,
9899,
],
[
9917,
9918,
],
[
9924,
9925,
],
[
9934,
9934,
],
[
9940,
9940,
],
[
9962,
9962,
],
[
9970,
9971,
],
[
9973,
9973,
],
[
9978,
9978,
],
[
9981,
9981,
],
[
9989,
9989,
],
[
9994,
9995,
],
[
10024,
10024,
],
[
10060,
10060,
],
[
10062,
10062,
],
[
10067,
10069,
],
[
10071,
10071,
],
[
10133,
10135,
],
[
10160,
10160,
],
[
10175,
10175,
],
[
11035,
11036,
],
[
11088,
11088,
],
[
11093,
11093,
],
[
11904,
11929,
],
[
11931,
12019,
],
[
12032,
12245,
],
[
12272,
12283,
],
[
12288,
12288,
],
[
12289,
12291,
],
[
12292,
12292,
],
[
12293,
12293,
],
[
12294,
12294,
],
[
12295,
12295,
],
[
12296,
12296,
],
[
12297,
12297,
],
[
12298,
12298,
],
[
12299,
12299,
],
[
12300,
12300,
],
[
12301,
12301,
],
[
12302,
12302,
],
[
12303,
12303,
],
[
12304,
12304,
],
[
12305,
12305,
],
[
12306,
12307,
],
[
12308,
12308,
],
[
12309,
12309,
],
[
12310,
12310,
],
[
12311,
12311,
],
[
12312,
12312,
],
[
12313,
12313,
],
[
12314,
12314,
],
[
12315,
12315,
],
[
12316,
12316,
],
[
12317,
12317,
],
[
12318,
12319,
],
[
12320,
12320,
],
[
12321,
12329,
],
[
12330,
12333,
],
[
12334,
12335,
],
[
12336,
12336,
],
[
12337,
12341,
],
[
12342,
12343,
],
[
12344,
12346,
],
[
12347,
12347,
],
[
12348,
12348,
],
[
12349,
12349,
],
[
12350,
12350,
],
[
12353,
12438,
],
[
12441,
12442,
],
[
12443,
12444,
],
[
12445,
12446,
],
[
12447,
12447,
],
[
12448,
12448,
],
[
12449,
12538,
],
[
12539,
12539,
],
[
12540,
12542,
],
[
12543,
12543,
],
[
12549,
12591,
],
[
12593,
12686,
],
[
12688,
12689,
],
[
12690,
12693,
],
[
12694,
12703,
],
[
12704,
12735,
],
[
12736,
12771,
],
[
12784,
12799,
],
[
12800,
12830,
],
[
12832,
12841,
],
[
12842,
12871,
],
[
12880,
12880,
],
[
12881,
12895,
],
[
12896,
12927,
],
[
12928,
12937,
],
[
12938,
12976,
],
[
12977,
12991,
],
[
12992,
13055,
],
[
13056,
13311,
],
[
13312,
19903,
],
[
19968,
40959,
],
[
40960,
40980,
],
[
40981,
40981,
],
[
40982,
42124,
],
[
42128,
42182,
],
[
43360,
43388,
],
[
44032,
55203,
],
[
63744,
64109,
],
[
64110,
64111,
],
[
64112,
64217,
],
[
64218,
64255,
],
[
65040,
65046,
],
[
65047,
65047,
],
[
65048,
65048,
],
[
65049,
65049,
],
[
65072,
65072,
],
[
65073,
65074,
],
[
65075,
65076,
],
[
65077,
65077,
],
[
65078,
65078,
],
[
65079,
65079,
],
[
65080,
65080,
],
[
65081,
65081,
],
[
65082,
65082,
],
[
65083,
65083,
],
[
65084,
65084,
],
[
65085,
65085,
],
[
65086,
65086,
],
[
65087,
65087,
],
[
65088,
65088,
],
[
65089,
65089,
],
[
65090,
65090,
],
[
65091,
65091,
],
[
65092,
65092,
],
[
65093,
65094,
],
[
65095,
65095,
],
[
65096,
65096,
],
[
65097,
65100,
],
[
65101,
65103,
],
[
65104,
65106,
],
[
65108,
65111,
],
[
65112,
65112,
],
[
65113,
65113,
],
[
65114,
65114,
],
[
65115,
65115,
],
[
65116,
65116,
],
[
65117,
65117,
],
[
65118,
65118,
],
[
65119,
65121,
],
[
65122,
65122,
],
[
65123,
65123,
],
[
65124,
65126,
],
[
65128,
65128,
],
[
65129,
65129,
],
[
65130,
65131,
],
[
65281,
65283,
],
[
65284,
65284,
],
[
65285,
65287,
],
[
65288,
65288,
],
[
65289,
65289,
],
[
65290,
65290,
],
[
65291,
65291,
],
[
65292,
65292,
],
[
65293,
65293,
],
[
65294,
65295,
],
[
65296,
65305,
],
[
65306,
65307,
],
[
65308,
65310,
],
[
65311,
65312,
],
[
65313,
65338,
],
[
65339,
65339,
],
[
65340,
65340,
],
[
65341,
65341,
],
[
65342,
65342,
],
[
65343,
65343,
],
[
65344,
65344,
],
[
65345,
65370,
],
[
65371,
65371,
],
[
65372,
65372,
],
[
65373,
65373,
],
[
65374,
65374,
],
[
65375,
65375,
],
[
65376,
65376,
],
[
65504,
65505,
],
[
65506,
65506,
],
[
65507,
65507,
],
[
65508,
65508,
],
[
65509,
65510,
],
[
94176,
94177,
],
[
94178,
94178,
],
[
94179,
94179,
],
[
94180,
94180,
],
[
94192,
94193,
],
[
94208,
100343,
],
[
100352,
101119,
],
[
101120,
101589,
],
[
101632,
101640,
],
[
110576,
110579,
],
[
110581,
110587,
],
[
110589,
110590,
],
[
110592,
110847,
],
[
110848,
110882,
],
[
110898,
110898,
],
[
110928,
110930,
],
[
110933,
110933,
],
[
110948,
110951,
],
[
110960,
111355,
],
[
126980,
126980,
],
[
127183,
127183,
],
[
127374,
127374,
],
[
127377,
127386,
],
[
127488,
127490,
],
[
127504,
127547,
],
[
127552,
127560,
],
[
127568,
127569,
],
[
127584,
127589,
],
[
127744,
127776,
],
[
127789,
127797,
],
[
127799,
127868,
],
[
127870,
127891,
],
[
127904,
127946,
],
[
127951,
127955,
],
[
127968,
127984,
],
[
127988,
127988,
],
[
127992,
127994,
],
[
127995,
127999,
],
[
128000,
128062,
],
[
128064,
128064,
],
[
128066,
128252,
],
[
128255,
128317,
],
[
128331,
128334,
],
[
128336,
128359,
],
[
128378,
128378,
],
[
128405,
128406,
],
[
128420,
128420,
],
[
128507,
128511,
],
[
128512,
128591,
],
[
128640,
128709,
],
[
128716,
128716,
],
[
128720,
128722,
],
[
128725,
128727,
],
[
128732,
128735,
],
[
128747,
128748,
],
[
128756,
128764,
],
[
128992,
129003,
],
[
129008,
129008,
],
[
129292,
129338,
],
[
129340,
129349,
],
[
129351,
129535,
],
[
129648,
129660,
],
[
129664,
129672,
],
[
129680,
129725,
],
[
129727,
129733,
],
[
129742,
129755,
],
[
129760,
129768,
],
[
129776,
129784,
],
[
131072,
173791,
],
[
173792,
173823,
],
[
173824,
177977,
],
[
177978,
177983,
],
[
177984,
178205,
],
[
178206,
178207,
],
[
178208,
183969,
],
[
183970,
183983,
],
[
183984,
191456,
],
[
191457,
194559,
],
[
194560,
195101,
],
[
195102,
195103,
],
[
195104,
196605,
],
[
196608,
201546,
],
[
201547,
201551,
],
[
201552,
205743,
],
[
205744,
262141,
],
];
<?php
namespace Symfony\Component\String\Inflector;
final class FrenchInflector implements InflectorInterface
{
private const PLURALIZE_REGEXP = [
['/(s|x|z)$/i', '\1'],
['/(eau)$/i', '\1x'],
['/^(landau)$/i', '\1s'],
['/(au)$/i', '\1x'],
['/^(pneu|bleu|émeu)$/i', '\1s'],
['/(eu)$/i', '\1x'],
['/^(bal|carnaval|caracal|chacal|choral|corral|étal|festival|récital|val)$/i', '\1s'],
['/al$/i', '\1aux'],
['/^(aspir|b|cor|ém|ferm|soupir|trav|vant|vitr)ail$/i', '\1aux'],
['/^(bij|caill|ch|gen|hib|jouj|p)ou$/i', '\1oux'],
['/^(cinquante|soixante|mille)$/i', '\1'],
['/^(mon|ma)(sieur|dame|demoiselle|seigneur)$/', 'mes\2s'],
['/^(Mon|Ma)(sieur|dame|demoiselle|seigneur)$/', 'Mes\2s'],
];
private const SINGULARIZE_REGEXP = [
['/((aspir|b|cor|ém|ferm|soupir|trav|vant|vitr))aux$/i', '\1ail'],
['/(eau)x$/i', '\1'],
['/(amir|anim|arsen|boc|can|capit|capor|chev|crist|génér|hopit|hôpit|idé|journ|littor|loc|m|mét|minér|princip|radic|termin)aux$/i', '\1al'],
['/(au)x$/i', '\1'],
['/(eu)x$/i', '\1'],
['/(bij|caill|ch|gen|hib|jouj|p)oux$/i', '\1ou'],
['/^mes(dame|demoiselle)s$/', 'ma\1'],
['/^Mes(dame|demoiselle)s$/', 'Ma\1'],
['/^mes(sieur|seigneur)s$/', 'mon\1'],
['/^Mes(sieur|seigneur)s$/', 'Mon\1'],
['/s$/i', ''],
];
private const UNINFLECTED = '/^(abcès|accès|abus|albatros|anchois|anglais|autobus|bois|brebis|carquois|cas|chas|colis|concours|corps|cours|cyprès|décès|devis|discours|dos|embarras|engrais|entrelacs|excès|fils|fois|gâchis|gars|glas|héros|intrus|jars|jus|kermès|lacis|legs|lilas|marais|mars|matelas|mépris|mets|mois|mors|obus|os|palais|paradis|parcours|pardessus|pays|plusieurs|poids|pois|pouls|printemps|processus|progrès|puits|pus|rabais|radis|recors|recours|refus|relais|remords|remous|rictus|rhinocéros|repas|rubis|sans|sas|secours|sens|souris|succès|talus|tapis|tas|taudis|temps|tiers|univers|velours|verglas|vernis|virus)$/i';
public function singularize(string $plural): array
{
if ($this->isInflectedWord($plural)) {
return [$plural];
}
foreach (self::SINGULARIZE_REGEXP as $rule) {
[$regexp, $replace] = $rule;
if (1 === preg_match($regexp, $plural)) {
return [preg_replace($regexp, $replace, $plural)];
}
}
return [$plural];
}
public function pluralize(string $singular): array
{
if ($this->isInflectedWord($singular)) {
return [$singular];
}
foreach (self::PLURALIZE_REGEXP as $rule) {
[$regexp, $replace] = $rule;
if (1 === preg_match($regexp, $singular)) {
return [preg_replace($regexp, $replace, $singular)];
}
}
return [$singular.'s'];
}
private function isInflectedWord(string $word): bool
{
return 1 === preg_match(self::UNINFLECTED, $word);
}
}
<?php
namespace Symfony\Component\String\Inflector;
interface InflectorInterface
{
public function singularize(string $plural): array;
public function pluralize(string $singular): array;
}
<?php
namespace Symfony\Component\String\Inflector;
final class EnglishInflector implements InflectorInterface
{
private const PLURAL_MAP = [
['a', 1, true, true, ['on', 'um']],
['ea', 2, true, true, 'a'],
['secivres', 8, true, true, 'service'],
['eci', 3, false, true, 'ouse'],
['esee', 4, false, true, 'oose'],
['i', 1, true, true, 'us'],
['nem', 3, true, true, 'man'],
['nerdlihc', 8, true, true, 'child'],
['nexo', 4, false, false, 'ox'],
['seci', 4, false, true, ['ex', 'ix', 'ice']],
['seifles', 7, true, true, 'selfie'],
['seibmoz', 7, true, true, 'zombie'],
['seivom', 6, true, true, 'movie'],
['sesutcep', 8, true, true, 'pectus'],
['teef', 4, true, true, 'foot'],
['eseeg', 5, true, true, 'goose'],
['hteet', 5, true, true, 'tooth'],
['swen', 4, true, true, 'news'],
['seires', 6, true, true, 'series'],
['sei', 3, false, true, 'y'],
['sess', 4, true, false, 'ss'],
['ses', 3, true, true, ['s', 'se', 'sis']],
['sevit', 5, true, true, 'tive'],
['sevird', 6, false, true, 'drive'],
['sevi', 4, false, true, 'ife'],
['sevom', 5, true, true, 'move'],
['sev', 3, true, true, ['f', 've', 'ff']],
['sexa', 4, false, false, ['ax', 'axe', 'axis']],
['sex', 3, true, false, 'x'],
['sezz', 4, true, false, 'z'],
['suae', 4, false, true, 'eau'],
['see', 3, true, true, 'ee'],
['segd', 4, true, true, 'dge'],
['se', 2, true, true, ['', 'e']],
['s', 1, true, true, ''],
['xuae', 4, false, true, 'eau'],
['elpoep', 6, true, true, 'person'],
];
private const SINGULAR_MAP = [
['airetirc', 8, false, false, 'criterion'],
['aluben', 6, false, false, 'nebulae'],
['dlihc', 5, true, true, 'children'],
['eci', 3, false, true, 'ices'],
['ecivres', 7, true, true, 'services'],
['efi', 3, false, true, 'ives'],
['eifles', 6, true, true, 'selfies'],
['eivom', 5, true, true, 'movies'],
['esuol', 5, false, true, 'lice'],
['esuom', 5, false, true, 'mice'],
['esoo', 4, false, true, 'eese'],
['es', 2, true, true, 'ses'],
['esoog', 5, true, true, 'geese'],
['ev', 2, true, true, 'ves'],
['evird', 5, false, true, 'drives'],
['evit', 4, true, true, 'tives'],
['evom', 4, true, true, 'moves'],
['ffats', 5, true, true, 'staves'],
['ff', 2, true, true, 'ffs'],
['f', 1, true, true, ['fs', 'ves']],
['hc', 2, true, true, 'ches'],
['hs', 2, true, true, 'shes'],
['htoot', 5, true, true, 'teeth'],
['mu', 2, true, true, 'a'],
['nam', 3, true, true, 'men'],
['nosrep', 6, true, true, ['persons', 'people']],
['noi', 3, true, true, 'ions'],
['nop', 3, true, true, 'pons'],
['nos', 3, true, true, 'sons'],
['no', 2, true, true, 'a'],
['ohce', 4, true, true, 'echoes'],
['oreh', 4, true, true, 'heroes'],
['salta', 5, true, true, 'atlases'],
['siri', 4, true, true, 'irises'],
['sis', 3, true, true, 'ses'],
['ss', 2, true, false, 'sses'],
['suballys', 8, true, true, 'syllabi'],
['sub', 3, true, true, 'buses'],
['suc', 3, true, true, 'cuses'],
['sutcep', 6, true, true, 'pectuses'],
['su', 2, true, true, 'i'],
['swen', 4, true, true, 'news'],
['toof', 4, true, true, 'feet'],
['uae', 3, false, true, ['eaus', 'eaux']],
['xo', 2, false, false, 'oxen'],
['xaoh', 4, true, false, 'hoaxes'],
['xedni', 5, false, true, ['indicies', 'indexes']],
['xo', 2, false, true, 'oxes'],
['x', 1, true, false, ['cies', 'xes']],
['xi', 2, false, true, 'ices'],
['y', 1, false, true, 'ies'],
['ziuq', 4, true, false, 'quizzes'],
['z', 1, true, true, 'zes'],
];
private const UNINFLECTED = [
'',
'atad',
'reed',
'kcabdeef',
'hsif',
'ofni',
'esoom',
'seires',
'peehs',
'seiceps',
];
public function singularize(string $plural): array
{
$pluralRev = strrev($plural);
$lowerPluralRev = strtolower($pluralRev);
$pluralLength = \strlen($lowerPluralRev);
if (\in_array($lowerPluralRev, self::UNINFLECTED, true)) {
return [$plural];
}
foreach (self::PLURAL_MAP as $map) {
$suffix = $map[0];
$suffixLength = $map[1];
$j = 0;
while ($suffix[$j] === $lowerPluralRev[$j]) {
++$j;
if ($j === $suffixLength) {
if ($j < $pluralLength) {
$nextIsVocal = false !== strpos('aeiou', $lowerPluralRev[$j]);
if (!$map[2] && $nextIsVocal) {
break;
}
if (!$map[3] && !$nextIsVocal) {
break;
}
}
$newBase = substr($plural, 0, $pluralLength - $suffixLength);
$newSuffix = $map[4];
$firstUpper = ctype_upper($pluralRev[$j - 1]);
if (\is_array($newSuffix)) {
$singulars = [];
foreach ($newSuffix as $newSuffixEntry) {
$singulars[] = $newBase.($firstUpper ? ucfirst($newSuffixEntry) : $newSuffixEntry);
}
return $singulars;
}
return [$newBase.($firstUpper ? ucfirst($newSuffix) : $newSuffix)];
}
if ($j === $pluralLength) {
break;
}
}
}
return [$plural];
}
public function pluralize(string $singular): array
{
$singularRev = strrev($singular);
$lowerSingularRev = strtolower($singularRev);
$singularLength = \strlen($lowerSingularRev);
if (\in_array($lowerSingularRev, self::UNINFLECTED, true)) {
return [$singular];
}
foreach (self::SINGULAR_MAP as $map) {
$suffix = $map[0];
$suffixLength = $map[1];
$j = 0;
while ($suffix[$j] === $lowerSingularRev[$j]) {
++$j;
if ($j === $suffixLength) {
if ($j < $singularLength) {
$nextIsVocal = false !== strpos('aeiou', $lowerSingularRev[$j]);
if (!$map[2] && $nextIsVocal) {
break;
}
if (!$map[3] && !$nextIsVocal) {
break;
}
}
$newBase = substr($singular, 0, $singularLength - $suffixLength);
$newSuffix = $map[4];
$firstUpper = ctype_upper($singularRev[$j - 1]);
if (\is_array($newSuffix)) {
$plurals = [];
foreach ($newSuffix as $newSuffixEntry) {
$plurals[] = $newBase.($firstUpper ? ucfirst($newSuffixEntry) : $newSuffixEntry);
}
return $plurals;
}
return [$newBase.($firstUpper ? ucfirst($newSuffix) : $newSuffix)];
}
if ($j === $singularLength) {
break;
}
}
}
return [$singular.'s'];
}
}
<?php
namespace Symfony\Component\String;
use Symfony\Component\String\Exception\ExceptionInterface;
use Symfony\Component\String\Exception\InvalidArgumentException;
use Symfony\Component\String\Exception\RuntimeException;
abstract class AbstractUnicodeString extends AbstractString
{
public const NFC = \Normalizer::NFC;
public const NFD = \Normalizer::NFD;
public const NFKC = \Normalizer::NFKC;
public const NFKD = \Normalizer::NFKD;
private const 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";
private const FOLD_FROM = ['İ', 'µ', 'ſ', "\xCD\x85", 'ς', 'ϐ', 'ϑ', 'ϕ', 'ϖ', 'ϰ', 'ϱ', 'ϵ', 'ẛ', "\xE1\xBE\xBE", 'ß', 'İ', 'ʼn', 'ǰ', 'ΐ', 'ΰ', 'և', 'ẖ', 'ẗ', 'ẘ', 'ẙ', 'ẚ', 'ẞ', 'ὐ', 'ὒ', 'ὔ', 'ὖ', 'ᾀ', 'ᾁ', 'ᾂ', 'ᾃ', 'ᾄ', 'ᾅ', 'ᾆ', 'ᾇ', 'ᾈ', 'ᾉ', 'ᾊ', 'ᾋ', 'ᾌ', 'ᾍ', 'ᾎ', 'ᾏ', 'ᾐ', 'ᾑ', 'ᾒ', 'ᾓ', 'ᾔ', 'ᾕ', 'ᾖ', 'ᾗ', 'ᾘ', 'ᾙ', 'ᾚ', 'ᾛ', 'ᾜ', 'ᾝ', 'ᾞ', 'ᾟ', 'ᾠ', 'ᾡ', 'ᾢ', 'ᾣ', 'ᾤ', 'ᾥ', 'ᾦ', 'ᾧ', 'ᾨ', 'ᾩ', 'ᾪ', 'ᾫ', 'ᾬ', 'ᾭ', 'ᾮ', 'ᾯ', 'ᾲ', 'ᾳ', 'ᾴ', 'ᾶ', 'ᾷ', 'ᾼ', 'ῂ', 'ῃ', 'ῄ', 'ῆ', 'ῇ', 'ῌ', 'ῒ', 'ΐ', 'ῖ', 'ῗ', 'ῢ', 'ΰ', 'ῤ', 'ῦ', 'ῧ', 'ῲ', 'ῳ', 'ῴ', 'ῶ', 'ῷ', 'ῼ', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'ſt', 'st', 'ﬓ', 'ﬔ', 'ﬕ', 'ﬖ', 'ﬗ'];
private const FOLD_TO = ['i̇', 'μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', 'ṡ', 'ι', 'ss', 'i̇', 'ʼn', 'ǰ', 'ΐ', 'ΰ', 'եւ', 'ẖ', 'ẗ', 'ẘ', 'ẙ', 'aʾ', 'ss', 'ὐ', 'ὒ', 'ὔ', 'ὖ', 'ἀι', 'ἁι', 'ἂι', 'ἃι', 'ἄι', 'ἅι', 'ἆι', 'ἇι', 'ἀι', 'ἁι', 'ἂι', 'ἃι', 'ἄι', 'ἅι', 'ἆι', 'ἇι', 'ἠι', 'ἡι', 'ἢι', 'ἣι', 'ἤι', 'ἥι', 'ἦι', 'ἧι', 'ἠι', 'ἡι', 'ἢι', 'ἣι', 'ἤι', 'ἥι', 'ἦι', 'ἧι', 'ὠι', 'ὡι', 'ὢι', 'ὣι', 'ὤι', 'ὥι', 'ὦι', 'ὧι', 'ὠι', 'ὡι', 'ὢι', 'ὣι', 'ὤι', 'ὥι', 'ὦι', 'ὧι', 'ὰι', 'αι', 'άι', 'ᾶ', 'ᾶι', 'αι', 'ὴι', 'ηι', 'ήι', 'ῆ', 'ῆι', 'ηι', 'ῒ', 'ΐ', 'ῖ', 'ῗ', 'ῢ', 'ΰ', 'ῤ', 'ῦ', 'ῧ', 'ὼι', 'ωι', 'ώι', 'ῶ', 'ῶι', 'ωι', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'st', 'st', 'մն', 'մե', 'մի', 'վն', 'մխ'];
private const UPPER_FROM = ['ß', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'ſt', 'st', 'և', 'ﬓ', 'ﬔ', 'ﬕ', 'ﬖ', 'ﬗ', 'ʼn', 'ΐ', 'ΰ', 'ǰ', 'ẖ', 'ẗ', 'ẘ', 'ẙ', 'ẚ', 'ὐ', 'ὒ', 'ὔ', 'ὖ', 'ᾶ', 'ῆ', 'ῒ', 'ΐ', 'ῖ', 'ῗ', 'ῢ', 'ΰ', 'ῤ', 'ῦ', 'ῧ', 'ῶ'];
private const UPPER_TO = ['SS', 'FF', 'FI', 'FL', 'FFI', 'FFL', 'ST', 'ST', 'ԵՒ', 'ՄՆ', 'ՄԵ', 'ՄԻ', 'ՎՆ', 'ՄԽ', 'ʼN', 'Ϊ́', 'Ϋ́', 'J̌', 'H̱', 'T̈', 'W̊', 'Y̊', 'Aʾ', 'Υ̓', 'Υ̓̀', 'Υ̓́', 'Υ̓͂', 'Α͂', 'Η͂', 'Ϊ̀', 'Ϊ́', 'Ι͂', 'Ϊ͂', 'Ϋ̀', 'Ϋ́', 'Ρ̓', 'Υ͂', 'Ϋ͂', 'Ω͂'];
private const TRANSLIT_FROM = ['Æ', 'Ð', 'Ø', 'Þ', 'ß', 'æ', 'ð', 'ø', 'þ', 'Đ', 'đ', 'Ħ', 'ħ', 'ı', 'ĸ', 'Ŀ', 'ŀ', 'Ł', 'ł', 'ʼn', 'Ŋ', 'ŋ', 'Œ', 'œ', 'Ŧ', 'ŧ', 'ƀ', 'Ɓ', 'Ƃ', 'ƃ', 'Ƈ', 'ƈ', 'Ɖ', 'Ɗ', 'Ƌ', 'ƌ', 'Ɛ', 'Ƒ', 'ƒ', 'Ɠ', 'ƕ', 'Ɩ', 'Ɨ', 'Ƙ', 'ƙ', 'ƚ', 'Ɲ', 'ƞ', 'Ƣ', 'ƣ', 'Ƥ', 'ƥ', 'ƫ', 'Ƭ', 'ƭ', 'Ʈ', 'Ʋ', 'Ƴ', 'ƴ', 'Ƶ', 'ƶ', 'DŽ', 'Dž', 'dž', 'Ǥ', 'ǥ', 'ȡ', 'Ȥ', 'ȥ', 'ȴ', 'ȵ', 'ȶ', 'ȷ', 'ȸ', 'ȹ', 'Ⱥ', 'Ȼ', 'ȼ', 'Ƚ', 'Ⱦ', 'ȿ', 'ɀ', 'Ƀ', 'Ʉ', 'Ɇ', 'ɇ', 'Ɉ', 'ɉ', 'Ɍ', 'ɍ', 'Ɏ', 'ɏ', 'ɓ', 'ɕ', 'ɖ', 'ɗ', 'ɛ', 'ɟ', 'ɠ', 'ɡ', 'ɢ', 'ɦ', 'ɧ', 'ɨ', 'ɪ', 'ɫ', 'ɬ', 'ɭ', 'ɱ', 'ɲ', 'ɳ', 'ɴ', 'ɶ', 'ɼ', 'ɽ', 'ɾ', 'ʀ', 'ʂ', 'ʈ', 'ʉ', 'ʋ', 'ʏ', 'ʐ', 'ʑ', 'ʙ', 'ʛ', 'ʜ', 'ʝ', 'ʟ', 'ʠ', 'ʣ', 'ʥ', 'ʦ', 'ʪ', 'ʫ', 'ᴀ', 'ᴁ', 'ᴃ', '', 'ᴅ', 'ᴆ', 'ᴇ', 'ᴊ', 'ᴋ', 'ᴌ', 'ᴍ', '', 'ᴘ', 'ᴛ', '', '', '', '', 'ᵫ', 'ᵬ', 'ᵭ', 'ᵮ', 'ᵯ', 'ᵰ', 'ᵱ', 'ᵲ', 'ᵳ', 'ᵴ', 'ᵵ', 'ᵶ', 'ᵺ', 'ᵻ', 'ᵽ', 'ᵾ', 'ᶀ', 'ᶁ', 'ᶂ', '', 'ᶄ', 'ᶅ', 'ᶆ', 'ᶇ', 'ᶈ', 'ᶉ', 'ᶊ', '', 'ᶍ', 'ᶎ', 'ᶏ', 'ᶑ', 'ᶒ', 'ᶓ', 'ᶖ', 'ᶙ', 'ẚ', 'ẜ', '', 'ẞ', 'Ỻ', 'ỻ', 'Ỽ', 'ỽ', 'Ỿ', 'ỿ', '©', '®', '₠', '₢', '₣', '₤', '₧', '₺', '₹', '', '℞', '㎧', '㎮', '㏆', '㏗', '㏞', '㏟', '¼', '½', '¾', '⅓', '⅔', '⅕', '⅖', '⅗', '⅘', '⅙', '⅚', '⅛', '⅜', '⅝', '⅞', '⅟', '', '', '', '', '', '“', '”', '„', '‟', '', '″', '〝', '〞', '«', '»', '', '', '', '', '', '', '—', '―', '︱', '︲', '', '‖', '', '⁅', '⁆', '', '、', '。', '〈', '〉', '《', '》', '', '', '〘', '〙', '〚', '〛', '︑', '︒', '︹', '︺', '︽', '︾', '︿', '﹀', '﹑', '﹝', '﹞', '⦅', '⦆', '。', '、', '×', '÷', '', '', '', '', '∥', '≪', '≫', '⦅', '⦆'];
private const TRANSLIT_TO = ['AE', 'D', 'O', 'TH', 'ss', 'ae', 'd', 'o', 'th', 'D', 'd', 'H', 'h', 'i', 'q', 'L', 'l', 'L', 'l', '\'n', 'N', 'n', 'OE', 'oe', 'T', 't', 'b', 'B', 'B', 'b', 'C', 'c', 'D', 'D', 'D', 'd', 'E', 'F', 'f', 'G', 'hv', 'I', 'I', 'K', 'k', 'l', 'N', 'n', 'OI', 'oi', 'P', 'p', 't', 'T', 't', 'T', 'V', 'Y', 'y', 'Z', 'z', 'DZ', 'Dz', 'dz', 'G', 'g', 'd', 'Z', 'z', 'l', 'n', 't', 'j', 'db', 'qp', 'A', 'C', 'c', 'L', 'T', 's', 'z', 'B', 'U', 'E', 'e', 'J', 'j', 'R', 'r', 'Y', 'y', 'b', 'c', 'd', 'd', 'e', 'j', 'g', 'g', 'G', 'h', 'h', 'i', 'I', 'l', 'l', 'l', 'm', 'n', 'n', 'N', 'OE', 'r', 'r', 'r', 'R', 's', 't', 'u', 'v', 'Y', 'z', 'z', 'B', 'G', 'H', 'j', 'L', 'q', 'dz', 'dz', 'ts', 'ls', 'lz', 'A', 'AE', 'B', 'C', 'D', 'D', 'E', 'J', 'K', 'L', 'M', 'O', 'P', 'T', 'U', 'V', 'W', 'Z', 'ue', 'b', 'd', 'f', 'm', 'n', 'p', 'r', 'r', 's', 't', 'z', 'th', 'I', 'p', 'U', 'b', 'd', 'f', 'g', 'k', 'l', 'm', 'n', 'p', 'r', 's', 'v', 'x', 'z', 'a', 'd', 'e', 'e', 'i', 'u', 'a', 's', 's', 'SS', 'LL', 'll', 'V', 'v', 'Y', 'y', '(C)', '(R)', 'CE', 'Cr', 'Fr.', 'L.', 'Pts', 'TL', 'Rs', 'x', 'Rx', 'm/s', 'rad/s', 'C/kg', 'pH', 'V/m', 'A/m', ' 1/4', ' 1/2', ' 3/4', ' 1/3', ' 2/3', ' 1/5', ' 2/5', ' 3/5', ' 4/5', ' 1/6', ' 5/6', ' 1/8', ' 3/8', ' 5/8', ' 7/8', ' 1/', '0', '\'', '\'', ',', '\'', '"', '"', ',,', '"', '\'', '"', '"', '"', '<<', '>>', '<', '>', '-', '-', '-', '-', '-', '-', '-', '-', '-', '||', '/', '[', ']', '*', ',', '.', '<', '>', '<<', '>>', '[', ']', '[', ']', '[', ']', ',', '.', '[', ']', '<<', '>>', '<', '>', ',', '[', ']', '((', '))', '.', ',', '*', '/', '-', '/', '\\', '|', '||', '<<', '>>', '((', '))'];
private static $transliterators = [];
private static $tableZero;
private static $tableWide;
public static function fromCodePoints(int ...$codes): self
{
$string = '';
foreach ($codes as $code) {
if (0x80 > $code %= 0x200000) {
$string .= \chr($code);
} elseif (0x800 > $code) {
$string .= \chr(0xC0 | $code >> 6).\chr(0x80 | $code & 0x3F);
} elseif (0x10000 > $code) {
$string .= \chr(0xE0 | $code >> 12).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F);
} else {
$string .= \chr(0xF0 | $code >> 18).\chr(0x80 | $code >> 12 & 0x3F).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F);
}
}
return new static($string);
}
public function ascii(array $rules = []): self
{
$str = clone $this;
$s = $str->string;
$str->string = '';
array_unshift($rules, 'nfd');
$rules[] = 'latin-ascii';
if (\function_exists('transliterator_transliterate')) {
$rules[] = 'any-latin/bgn';
}
$rules[] = 'nfkd';
$rules[] = '[:nonspacing mark:] remove';
while (\strlen($s) - 1 > $i = strspn($s, self::ASCII)) {
if (0 < --$i) {
$str->string .= substr($s, 0, $i);
$s = substr($s, $i);
}
if (!$rule = array_shift($rules)) {
$rules = [];
}
if ($rule instanceof \Transliterator) {
$s = $rule->transliterate($s);
} elseif ($rule instanceof \Closure) {
$s = $rule($s);
} elseif ($rule) {
if ('nfd' === $rule = strtolower($rule)) {
normalizer_is_normalized($s, self::NFD) ?: $s = normalizer_normalize($s, self::NFD);
} elseif ('nfkd' === $rule) {
normalizer_is_normalized($s, self::NFKD) ?: $s = normalizer_normalize($s, self::NFKD);
} elseif ('[:nonspacing mark:] remove' === $rule) {
$s = preg_replace('/\p{Mn}++/u', '', $s);
} elseif ('latin-ascii' === $rule) {
$s = str_replace(self::TRANSLIT_FROM, self::TRANSLIT_TO, $s);
} elseif ('de-ascii' === $rule) {
$s = preg_replace("/([AUO])\u{0308}(?=\p{Ll})/u", '$1e', $s);
$s = str_replace(["a\u{0308}", "o\u{0308}", "u\u{0308}", "A\u{0308}", "O\u{0308}", "U\u{0308}"], ['ae', 'oe', 'ue', 'AE', 'OE', 'UE'], $s);
} elseif (\function_exists('transliterator_transliterate')) {
if (null === $transliterator = self::$transliterators[$rule] ?? self::$transliterators[$rule] = \Transliterator::create($rule)) {
if ('any-latin/bgn' === $rule) {
$rule = 'any-latin';
$transliterator = self::$transliterators[$rule] ?? self::$transliterators[$rule] = \Transliterator::create($rule);
}
if (null === $transliterator) {
throw new InvalidArgumentException(sprintf('Unknown transliteration rule "%s".', $rule));
}
self::$transliterators['any-latin/bgn'] = $transliterator;
}
$s = $transliterator->transliterate($s);
}
} elseif (!\function_exists('iconv')) {
$s = preg_replace('/[^\x00-\x7F]/u', '?', $s);
} else {
$s = @preg_replace_callback('/[^\x00-\x7F]/u', static function ($c) {
$c = (string) iconv('UTF-8', 'ASCII//TRANSLIT', $c[0]);
if ('' === $c && '' === iconv('UTF-8', 'ASCII//TRANSLIT', '²')) {
throw new \LogicException(sprintf('"%s" requires a translit-able iconv implementation, try installing "gnu-libiconv" if you\'re using Alpine Linux.', static::class));
}
return 1 < \strlen($c) ? ltrim($c, '\'`"^~') : ('' !== $c ? $c : '?');
}, $s);
}
}
$str->string .= $s;
return $str;
}
public function camel(): parent
{
$str = clone $this;
$str->string = str_replace(' ', '', preg_replace_callback('/\b.(?![A-Z]{2,})/u', static function ($m) use (&$i) {
return 1 === ++$i ? ('İ' === $m[0] ? 'i̇' : mb_strtolower($m[0], 'UTF-8')) : mb_convert_case($m[0], \MB_CASE_TITLE, 'UTF-8');
}, preg_replace('/[^\pL0-9]++/u', ' ', $this->string)));
return $str;
}
public function codePointsAt(int $offset): array
{
$str = $this->slice($offset, 1);
if ('' === $str->string) {
return [];
}
$codePoints = [];
foreach (preg_split('//u', $str->string, -1, \PREG_SPLIT_NO_EMPTY) as $c) {
$codePoints[] = mb_ord($c, 'UTF-8');
}
return $codePoints;
}
public function folded(bool $compat = true): parent
{
$str = clone $this;
if (!$compat || \PHP_VERSION_ID < 70300 || !\defined('Normalizer::NFKC_CF')) {
$str->string = normalizer_normalize($str->string, $compat ? \Normalizer::NFKC : \Normalizer::NFC);
$str->string = mb_strtolower(str_replace(self::FOLD_FROM, self::FOLD_TO, $this->string), 'UTF-8');
} else {
$str->string = normalizer_normalize($str->string, \Normalizer::NFKC_CF);
}
return $str;
}
public function join(array $strings, string $lastGlue = null): parent
{
$str = clone $this;
$tail = null !== $lastGlue && 1 < \count($strings) ? $lastGlue.array_pop($strings) : '';
$str->string = implode($this->string, $strings).$tail;
if (!preg_match('//u', $str->string)) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
}
return $str;
}
public function lower(): parent
{
$str = clone $this;
$str->string = mb_strtolower(str_replace('İ', 'i̇', $str->string), 'UTF-8');
return $str;
}
public function match(string $regexp, int $flags = 0, int $offset = 0): array
{
$match = ((\PREG_PATTERN_ORDER | \PREG_SET_ORDER) & $flags) ? 'preg_match_all' : 'preg_match';
if ($this->ignoreCase) {
$regexp .= 'i';
}
set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); });
try {
if (false === $match($regexp.'u', $this->string, $matches, $flags | \PREG_UNMATCHED_AS_NULL, $offset)) {
$lastError = preg_last_error();
foreach (get_defined_constants(true)['pcre'] as $k => $v) {
if ($lastError === $v && '_ERROR' === substr($k, -6)) {
throw new RuntimeException('Matching failed with '.$k.'.');
}
}
throw new RuntimeException('Matching failed with unknown error code.');
}
} finally {
restore_error_handler();
}
return $matches;
}
public function normalize(int $form = self::NFC): self
{
if (!\in_array($form, [self::NFC, self::NFD, self::NFKC, self::NFKD])) {
throw new InvalidArgumentException('Unsupported normalization form.');
}
$str = clone $this;
normalizer_is_normalized($str->string, $form) ?: $str->string = normalizer_normalize($str->string, $form);
return $str;
}
public function padBoth(int $length, string $padStr = ' '): parent
{
if ('' === $padStr || !preg_match('//u', $padStr)) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
}
$pad = clone $this;
$pad->string = $padStr;
return $this->pad($length, $pad, \STR_PAD_BOTH);
}
public function padEnd(int $length, string $padStr = ' '): parent
{
if ('' === $padStr || !preg_match('//u', $padStr)) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
}
$pad = clone $this;
$pad->string = $padStr;
return $this->pad($length, $pad, \STR_PAD_RIGHT);
}
public function padStart(int $length, string $padStr = ' '): parent
{
if ('' === $padStr || !preg_match('//u', $padStr)) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
}
$pad = clone $this;
$pad->string = $padStr;
return $this->pad($length, $pad, \STR_PAD_LEFT);
}
public function replaceMatches(string $fromRegexp, $to): parent
{
if ($this->ignoreCase) {
$fromRegexp .= 'i';
}
if (\is_array($to) || $to instanceof \Closure) {
if (!\is_callable($to)) {
throw new \TypeError(sprintf('Argument 2 passed to "%s::replaceMatches()" must be callable, array given.', static::class));
}
$replace = 'preg_replace_callback';
$to = static function (array $m) use ($to): string {
$to = $to($m);
if ('' !== $to && (!\is_string($to) || !preg_match('//u', $to))) {
throw new InvalidArgumentException('Replace callback must return a valid UTF-8 string.');
}
return $to;
};
} elseif ('' !== $to && !preg_match('//u', $to)) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
} else {
$replace = 'preg_replace';
}
set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); });
try {
if (null === $string = $replace($fromRegexp.'u', $to, $this->string)) {
$lastError = preg_last_error();
foreach (get_defined_constants(true)['pcre'] as $k => $v) {
if ($lastError === $v && '_ERROR' === substr($k, -6)) {
throw new RuntimeException('Matching failed with '.$k.'.');
}
}
throw new RuntimeException('Matching failed with unknown error code.');
}
} finally {
restore_error_handler();
}
$str = clone $this;
$str->string = $string;
return $str;
}
public function reverse(): parent
{
$str = clone $this;
$str->string = implode('', array_reverse(preg_split('/(\X)/u', $str->string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY)));
return $str;
}
public function snake(): parent
{
$str = $this->camel();
$str->string = mb_strtolower(preg_replace(['/(\p{Lu}+)(\p{Lu}\p{Ll})/u', '/([\p{Ll}0-9])(\p{Lu})/u'], '\1_\2', $str->string), 'UTF-8');
return $str;
}
public function title(bool $allWords = false): parent
{
$str = clone $this;
$limit = $allWords ? -1 : 1;
$str->string = preg_replace_callback('/\b./u', static function (array $m): string {
return mb_convert_case($m[0], \MB_CASE_TITLE, 'UTF-8');
}, $str->string, $limit);
return $str;
}
public function trim(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): parent
{
if (" \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}" !== $chars && !preg_match('//u', $chars)) {
throw new InvalidArgumentException('Invalid UTF-8 chars.');
}
$chars = preg_quote($chars);
$str = clone $this;
$str->string = preg_replace("{^[$chars]++|[$chars]++$}uD", '', $str->string);
return $str;
}
public function trimEnd(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): parent
{
if (" \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}" !== $chars && !preg_match('//u', $chars)) {
throw new InvalidArgumentException('Invalid UTF-8 chars.');
}
$chars = preg_quote($chars);
$str = clone $this;
$str->string = preg_replace("{[$chars]++$}uD", '', $str->string);
return $str;
}
public function trimPrefix($prefix): parent
{
if (!$this->ignoreCase) {
return parent::trimPrefix($prefix);
}
$str = clone $this;
if ($prefix instanceof \Traversable) {
$prefix = iterator_to_array($prefix, false);
} elseif ($prefix instanceof parent) {
$prefix = $prefix->string;
}
$prefix = implode('|', array_map('preg_quote', (array) $prefix));
$str->string = preg_replace("{^(?:$prefix)}iuD", '', $this->string);
return $str;
}
public function trimStart(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): parent
{
if (" \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}" !== $chars && !preg_match('//u', $chars)) {
throw new InvalidArgumentException('Invalid UTF-8 chars.');
}
$chars = preg_quote($chars);
$str = clone $this;
$str->string = preg_replace("{^[$chars]++}uD", '', $str->string);
return $str;
}
public function trimSuffix($suffix): parent
{
if (!$this->ignoreCase) {
return parent::trimSuffix($suffix);
}
$str = clone $this;
if ($suffix instanceof \Traversable) {
$suffix = iterator_to_array($suffix, false);
} elseif ($suffix instanceof parent) {
$suffix = $suffix->string;
}
$suffix = implode('|', array_map('preg_quote', (array) $suffix));
$str->string = preg_replace("{(?:$suffix)$}iuD", '', $this->string);
return $str;
}
public function upper(): parent
{
$str = clone $this;
$str->string = mb_strtoupper($str->string, 'UTF-8');
if (\PHP_VERSION_ID < 70300) {
$str->string = str_replace(self::UPPER_FROM, self::UPPER_TO, $str->string);
}
return $str;
}
public function width(bool $ignoreAnsiDecoration = true): int
{
$width = 0;
$s = str_replace(["\x00", "\x05", "\x07"], '', $this->string);
if (false !== strpos($s, "\r")) {
$s = str_replace(["\r\n", "\r"], "\n", $s);
}
if (!$ignoreAnsiDecoration) {
$s = preg_replace('/[\p{Cc}\x7F]++/u', '', $s);
}
foreach (explode("\n", $s) as $s) {
if ($ignoreAnsiDecoration) {
$s = preg_replace('/(?:\x1B(?:
\[ [\x30-\x3F]*+ [\x20-\x2F]*+ [\x40-\x7E]
| [P\]X^_] .*? \x1B\\\\
| [\x41-\x7E]
)|[\p{Cc}\x7F]++)/xu', '', $s);
}
$lineWidth = $this->wcswidth($s);
if ($lineWidth > $width) {
$width = $lineWidth;
}
}
return $width;
}
private function pad(int $len, self $pad, int $type): parent
{
$sLen = $this->length();
if ($len <= $sLen) {
return clone $this;
}
$padLen = $pad->length();
$freeLen = $len - $sLen;
$len = $freeLen % $padLen;
switch ($type) {
case \STR_PAD_RIGHT:
return $this->append(str_repeat($pad->string, intdiv($freeLen, $padLen)).($len ? $pad->slice(0, $len) : ''));
case \STR_PAD_LEFT:
return $this->prepend(str_repeat($pad->string, intdiv($freeLen, $padLen)).($len ? $pad->slice(0, $len) : ''));
case \STR_PAD_BOTH:
$freeLen /= 2;
$rightLen = ceil($freeLen);
$len = $rightLen % $padLen;
$str = $this->append(str_repeat($pad->string, intdiv($rightLen, $padLen)).($len ? $pad->slice(0, $len) : ''));
$leftLen = floor($freeLen);
$len = $leftLen % $padLen;
return $str->prepend(str_repeat($pad->string, intdiv($leftLen, $padLen)).($len ? $pad->slice(0, $len) : ''));
default:
throw new InvalidArgumentException('Invalid padding type.');
}
}
private function wcswidth(string $string): int
{
$width = 0;
foreach (preg_split('//u', $string, -1, \PREG_SPLIT_NO_EMPTY) as $c) {
$codePoint = mb_ord($c, 'UTF-8');
if (0 === $codePoint
|| 0x034F === $codePoint
|| (0x200B <= $codePoint && 0x200F >= $codePoint)
|| 0x2028 === $codePoint
|| 0x2029 === $codePoint
|| (0x202A <= $codePoint && 0x202E >= $codePoint)
|| (0x2060 <= $codePoint && 0x2063 >= $codePoint)
) {
continue;
}
if (32 > $codePoint
|| (0x07F <= $codePoint && 0x0A0 > $codePoint)
) {
return -1;
}
if (null === self::$tableZero) {
self::$tableZero = require __DIR__.'/Resources/data/wcswidth_table_zero.php';
}
if ($codePoint >= self::$tableZero[0][0] && $codePoint <= self::$tableZero[$ubound = \count(self::$tableZero) - 1][1]) {
$lbound = 0;
while ($ubound >= $lbound) {
$mid = floor(($lbound + $ubound) / 2);
if ($codePoint > self::$tableZero[$mid][1]) {
$lbound = $mid + 1;
} elseif ($codePoint < self::$tableZero[$mid][0]) {
$ubound = $mid - 1;
} else {
continue 2;
}
}
}
if (null === self::$tableWide) {
self::$tableWide = require __DIR__.'/Resources/data/wcswidth_table_wide.php';
}
if ($codePoint >= self::$tableWide[0][0] && $codePoint <= self::$tableWide[$ubound = \count(self::$tableWide) - 1][1]) {
$lbound = 0;
while ($ubound >= $lbound) {
$mid = floor(($lbound + $ubound) / 2);
if ($codePoint > self::$tableWide[$mid][1]) {
$lbound = $mid + 1;
} elseif ($codePoint < self::$tableWide[$mid][0]) {
$ubound = $mid - 1;
} else {
$width += 2;
continue 2;
}
}
}
++$width;
}
return $width;
}
}
<?php
namespace Symfony\Component\String;
class LazyString implements \Stringable, \JsonSerializable
{
private $value;
public static function fromCallable($callback, ...$arguments): self
{
if (!\is_callable($callback) && !(\is_array($callback) && isset($callback[0]) && $callback[0] instanceof \Closure && 2 >= \count($callback))) {
throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be a callable or a [Closure, method] lazy-callable, "%s" given.', __METHOD__, get_debug_type($callback)));
}
$lazyString = new static();
$lazyString->value = static function () use (&$callback, &$arguments, &$value): string {
if (null !== $arguments) {
if (!\is_callable($callback)) {
$callback[0] = $callback[0]();
$callback[1] = $callback[1] ?? '__invoke';
}
$value = $callback(...$arguments);
$callback = self::getPrettyName($callback);
$arguments = null;
}
return $value ?? '';
};
return $lazyString;
}
public static function fromStringable($value): self
{
if (!self::isStringable($value)) {
throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be a scalar or a stringable object, "%s" given.', __METHOD__, get_debug_type($value)));
}
if (\is_object($value)) {
return static::fromCallable([$value, '__toString']);
}
$lazyString = new static();
$lazyString->value = (string) $value;
return $lazyString;
}
final public static function isStringable($value): bool
{
return \is_string($value) || $value instanceof self || (\is_object($value) ? method_exists($value, '__toString') : \is_scalar($value));
}
final public static function resolve($value): string
{
return $value;
}
public function __toString()
{
if (\is_string($this->value)) {
return $this->value;
}
try {
return $this->value = ($this->value)();
} catch (\Throwable $e) {
if (\TypeError::class === \get_class($e) && __FILE__ === $e->getFile()) {
$type = explode(', ', $e->getMessage());
$type = substr(array_pop($type), 0, -\strlen(' returned'));
$r = new \ReflectionFunction($this->value);
$callback = $r->getStaticVariables()['callback'];
$e = new \TypeError(sprintf('Return value of %s() passed to %s::fromCallable() must be of the type string, %s returned.', $callback, static::class, $type));
}
if (\PHP_VERSION_ID < 70400) {
return trigger_error($e, \E_USER_ERROR);
}
throw $e;
}
}
public function __sleep(): array
{
$this->__toString();
return ['value'];
}
public function jsonSerialize(): string
{
return $this->__toString();
}
private function __construct()
{
}
private static function getPrettyName(callable $callback): string
{
if (\is_string($callback)) {
return $callback;
}
if (\is_array($callback)) {
$class = \is_object($callback[0]) ? get_debug_type($callback[0]) : $callback[0];
$method = $callback[1];
} elseif ($callback instanceof \Closure) {
$r = new \ReflectionFunction($callback);
if (false !== strpos($r->name, '{closure}') || !$class = $r->getClosureScopeClass()) {
return $r->name;
}
$class = $class->name;
$method = $r->name;
} else {
$class = get_debug_type($callback);
$method = '__invoke';
}
return $class.'::'.$method;
}
}
<?php
namespace Symfony\Component\String;
use Symfony\Component\String\Exception\ExceptionInterface;
use Symfony\Component\String\Exception\InvalidArgumentException;
use Symfony\Component\String\Exception\RuntimeException;
class ByteString extends AbstractString
{
private const ALPHABET_ALPHANUMERIC = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
public function __construct(string $string = '')
{
$this->string = $string;
}
public static function fromRandom(int $length = 16, string $alphabet = null): self
{
if ($length <= 0) {
throw new InvalidArgumentException(sprintf('A strictly positive length is expected, "%d" given.', $length));
}
$alphabet = $alphabet ?? self::ALPHABET_ALPHANUMERIC;
$alphabetSize = \strlen($alphabet);
$bits = (int) ceil(log($alphabetSize, 2.0));
if ($bits <= 0 || $bits > 56) {
throw new InvalidArgumentException('The length of the alphabet must in the [2^1, 2^56] range.');
}
$ret = '';
while ($length > 0) {
$urandomLength = (int) ceil(2 * $length * $bits / 8.0);
$data = random_bytes($urandomLength);
$unpackedData = 0;
$unpackedBits = 0;
for ($i = 0; $i < $urandomLength && $length > 0; ++$i) {
$unpackedData = ($unpackedData << 8) | \ord($data[$i]);
$unpackedBits += 8;
for (; $unpackedBits >= $bits && $length > 0; $unpackedBits -= $bits) {
$index = ($unpackedData & ((1 << $bits) - 1));
$unpackedData >>= $bits;
if ($index < $alphabetSize) {
$ret .= $alphabet[$index];
--$length;
}
}
}
}
return new static($ret);
}
public function bytesAt(int $offset): array
{
$str = $this->string[$offset] ?? '';
return '' === $str ? [] : [\ord($str)];
}
public function append(string ...$suffix): parent
{
$str = clone $this;
$str->string .= 1 >= \count($suffix) ? ($suffix[0] ?? '') : implode('', $suffix);
return $str;
}
public function camel(): parent
{
$str = clone $this;
$parts = explode(' ', trim(ucwords(preg_replace('/[^a-zA-Z0-9\x7f-\xff]++/', ' ', $this->string))));
$parts[0] = 1 !== \strlen($parts[0]) && ctype_upper($parts[0]) ? $parts[0] : lcfirst($parts[0]);
$str->string = implode('', $parts);
return $str;
}
public function chunk(int $length = 1): array
{
if (1 > $length) {
throw new InvalidArgumentException('The chunk length must be greater than zero.');
}
if ('' === $this->string) {
return [];
}
$str = clone $this;
$chunks = [];
foreach (str_split($this->string, $length) as $chunk) {
$str->string = $chunk;
$chunks[] = clone $str;
}
return $chunks;
}
public function endsWith($suffix): bool
{
if ($suffix instanceof parent) {
$suffix = $suffix->string;
} elseif (\is_array($suffix) || $suffix instanceof \Traversable) {
return parent::endsWith($suffix);
} else {
$suffix = (string) $suffix;
}
return '' !== $suffix && \strlen($this->string) >= \strlen($suffix) && 0 === substr_compare($this->string, $suffix, -\strlen($suffix), null, $this->ignoreCase);
}
public function equalsTo($string): bool
{
if ($string instanceof parent) {
$string = $string->string;
} elseif (\is_array($string) || $string instanceof \Traversable) {
return parent::equalsTo($string);
} else {
$string = (string) $string;
}
if ('' !== $string && $this->ignoreCase) {
return 0 === strcasecmp($string, $this->string);
}
return $string === $this->string;
}
public function folded(): parent
{
$str = clone $this;
$str->string = strtolower($str->string);
return $str;
}
public function indexOf($needle, int $offset = 0): ?int
{
if ($needle instanceof parent) {
$needle = $needle->string;
} elseif (\is_array($needle) || $needle instanceof \Traversable) {
return parent::indexOf($needle, $offset);
} else {
$needle = (string) $needle;
}
if ('' === $needle) {
return null;
}
$i = $this->ignoreCase ? stripos($this->string, $needle, $offset) : strpos($this->string, $needle, $offset);
return false === $i ? null : $i;
}
public function indexOfLast($needle, int $offset = 0): ?int
{
if ($needle instanceof parent) {
$needle = $needle->string;
} elseif (\is_array($needle) || $needle instanceof \Traversable) {
return parent::indexOfLast($needle, $offset);
} else {
$needle = (string) $needle;
}
if ('' === $needle) {
return null;
}
$i = $this->ignoreCase ? strripos($this->string, $needle, $offset) : strrpos($this->string, $needle, $offset);
return false === $i ? null : $i;
}
public function isUtf8(): bool
{
return '' === $this->string || preg_match('//u', $this->string);
}
public function join(array $strings, string $lastGlue = null): parent
{
$str = clone $this;
$tail = null !== $lastGlue && 1 < \count($strings) ? $lastGlue.array_pop($strings) : '';
$str->string = implode($this->string, $strings).$tail;
return $str;
}
public function length(): int
{
return \strlen($this->string);
}
public function lower(): parent
{
$str = clone $this;
$str->string = strtolower($str->string);
return $str;
}
public function match(string $regexp, int $flags = 0, int $offset = 0): array
{
$match = ((\PREG_PATTERN_ORDER | \PREG_SET_ORDER) & $flags) ? 'preg_match_all' : 'preg_match';
if ($this->ignoreCase) {
$regexp .= 'i';
}
set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); });
try {
if (false === $match($regexp, $this->string, $matches, $flags | \PREG_UNMATCHED_AS_NULL, $offset)) {
$lastError = preg_last_error();
foreach (get_defined_constants(true)['pcre'] as $k => $v) {
if ($lastError === $v && '_ERROR' === substr($k, -6)) {
throw new RuntimeException('Matching failed with '.$k.'.');
}
}
throw new RuntimeException('Matching failed with unknown error code.');
}
} finally {
restore_error_handler();
}
return $matches;
}
public function padBoth(int $length, string $padStr = ' '): parent
{
$str = clone $this;
$str->string = str_pad($this->string, $length, $padStr, \STR_PAD_BOTH);
return $str;
}
public function padEnd(int $length, string $padStr = ' '): parent
{
$str = clone $this;
$str->string = str_pad($this->string, $length, $padStr, \STR_PAD_RIGHT);
return $str;
}
public function padStart(int $length, string $padStr = ' '): parent
{
$str = clone $this;
$str->string = str_pad($this->string, $length, $padStr, \STR_PAD_LEFT);
return $str;
}
public function prepend(string ...$prefix): parent
{
$str = clone $this;
$str->string = (1 >= \count($prefix) ? ($prefix[0] ?? '') : implode('', $prefix)).$str->string;
return $str;
}
public function replace(string $from, string $to): parent
{
$str = clone $this;
if ('' !== $from) {
$str->string = $this->ignoreCase ? str_ireplace($from, $to, $this->string) : str_replace($from, $to, $this->string);
}
return $str;
}
public function replaceMatches(string $fromRegexp, $to): parent
{
if ($this->ignoreCase) {
$fromRegexp .= 'i';
}
if (\is_array($to)) {
if (!\is_callable($to)) {
throw new \TypeError(sprintf('Argument 2 passed to "%s::replaceMatches()" must be callable, array given.', static::class));
}
$replace = 'preg_replace_callback';
} else {
$replace = $to instanceof \Closure ? 'preg_replace_callback' : 'preg_replace';
}
set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); });
try {
if (null === $string = $replace($fromRegexp, $to, $this->string)) {
$lastError = preg_last_error();
foreach (get_defined_constants(true)['pcre'] as $k => $v) {
if ($lastError === $v && '_ERROR' === substr($k, -6)) {
throw new RuntimeException('Matching failed with '.$k.'.');
}
}
throw new RuntimeException('Matching failed with unknown error code.');
}
} finally {
restore_error_handler();
}
$str = clone $this;
$str->string = $string;
return $str;
}
public function reverse(): parent
{
$str = clone $this;
$str->string = strrev($str->string);
return $str;
}
public function slice(int $start = 0, int $length = null): parent
{
$str = clone $this;
$str->string = (string) substr($this->string, $start, $length ?? \PHP_INT_MAX);
return $str;
}
public function snake(): parent
{
$str = $this->camel();
$str->string = strtolower(preg_replace(['/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'], '\1_\2', $str->string));
return $str;
}
public function splice(string $replacement, int $start = 0, int $length = null): parent
{
$str = clone $this;
$str->string = substr_replace($this->string, $replacement, $start, $length ?? \PHP_INT_MAX);
return $str;
}
public function split(string $delimiter, int $limit = null, int $flags = null): array
{
if (1 > $limit = $limit ?? \PHP_INT_MAX) {
throw new InvalidArgumentException('Split limit must be a positive integer.');
}
if ('' === $delimiter) {
throw new InvalidArgumentException('Split delimiter is empty.');
}
if (null !== $flags) {
return parent::split($delimiter, $limit, $flags);
}
$str = clone $this;
$chunks = $this->ignoreCase
? preg_split('{'.preg_quote($delimiter).'}iD', $this->string, $limit)
: explode($delimiter, $this->string, $limit);
foreach ($chunks as &$chunk) {
$str->string = $chunk;
$chunk = clone $str;
}
return $chunks;
}
public function startsWith($prefix): bool
{
if ($prefix instanceof parent) {
$prefix = $prefix->string;
} elseif (!\is_string($prefix)) {
return parent::startsWith($prefix);
}
return '' !== $prefix && 0 === ($this->ignoreCase ? strncasecmp($this->string, $prefix, \strlen($prefix)) : strncmp($this->string, $prefix, \strlen($prefix)));
}
public function title(bool $allWords = false): parent
{
$str = clone $this;
$str->string = $allWords ? ucwords($str->string) : ucfirst($str->string);
return $str;
}
public function toUnicodeString(string $fromEncoding = null): UnicodeString
{
return new UnicodeString($this->toCodePointString($fromEncoding)->string);
}
public function toCodePointString(string $fromEncoding = null): CodePointString
{
$u = new CodePointString();
if (\in_array($fromEncoding, [null, 'utf8', 'utf-8', 'UTF8', 'UTF-8'], true) && preg_match('//u', $this->string)) {
$u->string = $this->string;
return $u;
}
set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); });
try {
try {
$validEncoding = false !== mb_detect_encoding($this->string, $fromEncoding ?? 'Windows-1252', true);
} catch (InvalidArgumentException $e) {
if (!\function_exists('iconv')) {
throw $e;
}
$u->string = iconv($fromEncoding ?? 'Windows-1252', 'UTF-8', $this->string);
return $u;
}
} finally {
restore_error_handler();
}
if (!$validEncoding) {
throw new InvalidArgumentException(sprintf('Invalid "%s" string.', $fromEncoding ?? 'Windows-1252'));
}
$u->string = mb_convert_encoding($this->string, 'UTF-8', $fromEncoding ?? 'Windows-1252');
return $u;
}
public function trim(string $chars = " \t\n\r\0\x0B\x0C"): parent
{
$str = clone $this;
$str->string = trim($str->string, $chars);
return $str;
}
public function trimEnd(string $chars = " \t\n\r\0\x0B\x0C"): parent
{
$str = clone $this;
$str->string = rtrim($str->string, $chars);
return $str;
}
public function trimStart(string $chars = " \t\n\r\0\x0B\x0C"): parent
{
$str = clone $this;
$str->string = ltrim($str->string, $chars);
return $str;
}
public function upper(): parent
{
$str = clone $this;
$str->string = strtoupper($str->string);
return $str;
}
public function width(bool $ignoreAnsiDecoration = true): int
{
$string = preg_match('//u', $this->string) ? $this->string : preg_replace('/[\x80-\xFF]/', '?', $this->string);
return (new CodePointString($string))->width($ignoreAnsiDecoration);
}
}
<?php
namespace Symfony\Component\String;
use Symfony\Component\String\Exception\ExceptionInterface;
use Symfony\Component\String\Exception\InvalidArgumentException;
class UnicodeString extends AbstractUnicodeString
{
public function __construct(string $string = '')
{
$this->string = normalizer_is_normalized($string) ? $string : normalizer_normalize($string);
if (false === $this->string) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
}
}
public function append(string ...$suffix): AbstractString
{
$str = clone $this;
$str->string = $this->string.(1 >= \count($suffix) ? ($suffix[0] ?? '') : implode('', $suffix));
normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string);
if (false === $str->string) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
}
return $str;
}
public function chunk(int $length = 1): array
{
if (1 > $length) {
throw new InvalidArgumentException('The chunk length must be greater than zero.');
}
if ('' === $this->string) {
return [];
}
$rx = '/(';
while (65535 < $length) {
$rx .= '\X{65535}';
$length -= 65535;
}
$rx .= '\X{'.$length.'})/u';
$str = clone $this;
$chunks = [];
foreach (preg_split($rx, $this->string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY) as $chunk) {
$str->string = $chunk;
$chunks[] = clone $str;
}
return $chunks;
}
public function endsWith($suffix): bool
{
if ($suffix instanceof AbstractString) {
$suffix = $suffix->string;
} elseif (\is_array($suffix) || $suffix instanceof \Traversable) {
return parent::endsWith($suffix);
} else {
$suffix = (string) $suffix;
}
$form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC;
normalizer_is_normalized($suffix, $form) ?: $suffix = normalizer_normalize($suffix, $form);
if ('' === $suffix || false === $suffix) {
return false;
}
if ($this->ignoreCase) {
return 0 === mb_stripos(grapheme_extract($this->string, \strlen($suffix), \GRAPHEME_EXTR_MAXBYTES, \strlen($this->string) - \strlen($suffix)), $suffix, 0, 'UTF-8');
}
return $suffix === grapheme_extract($this->string, \strlen($suffix), \GRAPHEME_EXTR_MAXBYTES, \strlen($this->string) - \strlen($suffix));
}
public function equalsTo($string): bool
{
if ($string instanceof AbstractString) {
$string = $string->string;
} elseif (\is_array($string) || $string instanceof \Traversable) {
return parent::equalsTo($string);
} else {
$string = (string) $string;
}
$form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC;
normalizer_is_normalized($string, $form) ?: $string = normalizer_normalize($string, $form);
if ('' !== $string && false !== $string && $this->ignoreCase) {
return \strlen($string) === \strlen($this->string) && 0 === mb_stripos($this->string, $string, 0, 'UTF-8');
}
return $string === $this->string;
}
public function indexOf($needle, int $offset = 0): ?int
{
if ($needle instanceof AbstractString) {
$needle = $needle->string;
} elseif (\is_array($needle) || $needle instanceof \Traversable) {
return parent::indexOf($needle, $offset);
} else {
$needle = (string) $needle;
}
$form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC;
normalizer_is_normalized($needle, $form) ?: $needle = normalizer_normalize($needle, $form);
if ('' === $needle || false === $needle) {
return null;
}
try {
$i = $this->ignoreCase ? grapheme_stripos($this->string, $needle, $offset) : grapheme_strpos($this->string, $needle, $offset);
} catch (\ValueError $e) {
return null;
}
return false === $i ? null : $i;
}
public function indexOfLast($needle, int $offset = 0): ?int
{
if ($needle instanceof AbstractString) {
$needle = $needle->string;
} elseif (\is_array($needle) || $needle instanceof \Traversable) {
return parent::indexOfLast($needle, $offset);
} else {
$needle = (string) $needle;
}
$form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC;
normalizer_is_normalized($needle, $form) ?: $needle = normalizer_normalize($needle, $form);
if ('' === $needle || false === $needle) {
return null;
}
$string = $this->string;
if (0 > $offset) {
if (0 > $offset += grapheme_strlen($needle)) {
$string = grapheme_substr($string, 0, $offset);
}
$offset = 0;
}
$i = $this->ignoreCase ? grapheme_strripos($string, $needle, $offset) : grapheme_strrpos($string, $needle, $offset);
return false === $i ? null : $i;
}
public function join(array $strings, string $lastGlue = null): AbstractString
{
$str = parent::join($strings, $lastGlue);
normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string);
return $str;
}
public function length(): int
{
return grapheme_strlen($this->string);
}
public function normalize(int $form = self::NFC): parent
{
$str = clone $this;
if (\in_array($form, [self::NFC, self::NFKC], true)) {
normalizer_is_normalized($str->string, $form) ?: $str->string = normalizer_normalize($str->string, $form);
} elseif (!\in_array($form, [self::NFD, self::NFKD], true)) {
throw new InvalidArgumentException('Unsupported normalization form.');
} elseif (!normalizer_is_normalized($str->string, $form)) {
$str->string = normalizer_normalize($str->string, $form);
$str->ignoreCase = null;
}
return $str;
}
public function prepend(string ...$prefix): AbstractString
{
$str = clone $this;
$str->string = (1 >= \count($prefix) ? ($prefix[0] ?? '') : implode('', $prefix)).$this->string;
normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string);
if (false === $str->string) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
}
return $str;
}
public function replace(string $from, string $to): AbstractString
{
$str = clone $this;
normalizer_is_normalized($from) ?: $from = normalizer_normalize($from);
if ('' !== $from && false !== $from) {
$tail = $str->string;
$result = '';
$indexOf = $this->ignoreCase ? 'grapheme_stripos' : 'grapheme_strpos';
while ('' !== $tail && false !== $i = $indexOf($tail, $from)) {
$slice = grapheme_substr($tail, 0, $i);
$result .= $slice.$to;
$tail = substr($tail, \strlen($slice) + \strlen($from));
}
$str->string = $result.$tail;
normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string);
if (false === $str->string) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
}
}
return $str;
}
public function replaceMatches(string $fromRegexp, $to): AbstractString
{
$str = parent::replaceMatches($fromRegexp, $to);
normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string);
return $str;
}
public function slice(int $start = 0, int $length = null): AbstractString
{
$str = clone $this;
if (\PHP_VERSION_ID < 80000 && 0 > $start && grapheme_strlen($this->string) < -$start) {
$start = 0;
}
$str->string = (string) grapheme_substr($this->string, $start, $length ?? 2147483647);
return $str;
}
public function splice(string $replacement, int $start = 0, int $length = null): AbstractString
{
$str = clone $this;
if (\PHP_VERSION_ID < 80000 && 0 > $start && grapheme_strlen($this->string) < -$start) {
$start = 0;
}
$start = $start ? \strlen(grapheme_substr($this->string, 0, $start)) : 0;
$length = $length ? \strlen(grapheme_substr($this->string, $start, $length ?? 2147483647)) : $length;
$str->string = substr_replace($this->string, $replacement, $start, $length ?? 2147483647);
normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string);
if (false === $str->string) {
throw new InvalidArgumentException('Invalid UTF-8 string.');
}
return $str;
}
public function split(string $delimiter, int $limit = null, int $flags = null): array
{
if (1 > $limit = $limit ?? 2147483647) {
throw new InvalidArgumentException('Split limit must be a positive integer.');
}
if ('' === $delimiter) {
throw new InvalidArgumentException('Split delimiter is empty.');
}
if (null !== $flags) {
return parent::split($delimiter.'u', $limit, $flags);
}
normalizer_is_normalized($delimiter) ?: $delimiter = normalizer_normalize($delimiter);
if (false === $delimiter) {
throw new InvalidArgumentException('Split delimiter is not a valid UTF-8 string.');
}
$str = clone $this;
$tail = $this->string;
$chunks = [];
$indexOf = $this->ignoreCase ? 'grapheme_stripos' : 'grapheme_strpos';
while (1 < $limit && false !== $i = $indexOf($tail, $delimiter)) {
$str->string = grapheme_substr($tail, 0, $i);
$chunks[] = clone $str;
$tail = substr($tail, \strlen($str->string) + \strlen($delimiter));
--$limit;
}
$str->string = $tail;
$chunks[] = clone $str;
return $chunks;
}
public function startsWith($prefix): bool
{
if ($prefix instanceof AbstractString) {
$prefix = $prefix->string;
} elseif (\is_array($prefix) || $prefix instanceof \Traversable) {
return parent::startsWith($prefix);
} else {
$prefix = (string) $prefix;
}
$form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC;
normalizer_is_normalized($prefix, $form) ?: $prefix = normalizer_normalize($prefix, $form);
if ('' === $prefix || false === $prefix) {
return false;
}
if ($this->ignoreCase) {
return 0 === mb_stripos(grapheme_extract($this->string, \strlen($prefix), \GRAPHEME_EXTR_MAXBYTES), $prefix, 0, 'UTF-8');
}
return $prefix === grapheme_extract($this->string, \strlen($prefix), \GRAPHEME_EXTR_MAXBYTES);
}
public function __wakeup()
{
if (!\is_string($this->string)) {
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
normalizer_is_normalized($this->string) ?: $this->string = normalizer_normalize($this->string);
}
public function __clone()
{
if (null === $this->ignoreCase) {
normalizer_is_normalized($this->string) ?: $this->string = normalizer_normalize($this->string);
}
$this->ignoreCase = false;
}
}
<?php
namespace Symfony\Component\String\Exception;
interface ExceptionInterface extends \Throwable
{
}
<?php
namespace Symfony\Component\String\Exception;
class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}
<?php
namespace Symfony\Component\String\Exception;
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
{
}
<?php
namespace Symfony\Component\String\Slugger;
use Symfony\Component\String\AbstractUnicodeString;
interface SluggerInterface
{
public function slug(string $string, string $separator = '-', string $locale = null): AbstractUnicodeString;
}
<?php
namespace Symfony\Component\String\Slugger;
use Symfony\Component\String\AbstractUnicodeString;
use Symfony\Component\String\UnicodeString;
use Symfony\Contracts\Translation\LocaleAwareInterface;
if (!interface_exists(LocaleAwareInterface::class)) {
throw new \LogicException('You cannot use the "Symfony\Component\String\Slugger\AsciiSlugger" as the "symfony/translation-contracts" package is not installed. Try running "composer require symfony/translation-contracts".');
}
class AsciiSlugger implements SluggerInterface, LocaleAwareInterface
{
private const LOCALE_TO_TRANSLITERATOR_ID = [
'am' => 'Amharic-Latin',
'ar' => 'Arabic-Latin',
'az' => 'Azerbaijani-Latin',
'be' => 'Belarusian-Latin',
'bg' => 'Bulgarian-Latin',
'bn' => 'Bengali-Latin',
'de' => 'de-ASCII',
'el' => 'Greek-Latin',
'fa' => 'Persian-Latin',
'he' => 'Hebrew-Latin',
'hy' => 'Armenian-Latin',
'ka' => 'Georgian-Latin',
'kk' => 'Kazakh-Latin',
'ky' => 'Kirghiz-Latin',
'ko' => 'Korean-Latin',
'mk' => 'Macedonian-Latin',
'mn' => 'Mongolian-Latin',
'or' => 'Oriya-Latin',
'ps' => 'Pashto-Latin',
'ru' => 'Russian-Latin',
'sr' => 'Serbian-Latin',
'sr_Cyrl' => 'Serbian-Latin',
'th' => 'Thai-Latin',
'tk' => 'Turkmen-Latin',
'uk' => 'Ukrainian-Latin',
'uz' => 'Uzbek-Latin',
'zh' => 'Han-Latin',
];
private $defaultLocale;
private $symbolsMap = [
'en' => ['@' => 'at', '&' => 'and'],
];
private $transliterators = [];
public function __construct(string $defaultLocale = null, $symbolsMap = null)
{
if (null !== $symbolsMap && !\is_array($symbolsMap) && !$symbolsMap instanceof \Closure) {
throw new \TypeError(sprintf('Argument 2 passed to "%s()" must be array, Closure or null, "%s" given.', __METHOD__, \gettype($symbolsMap)));
}
$this->defaultLocale = $defaultLocale;
$this->symbolsMap = $symbolsMap ?? $this->symbolsMap;
}
public function setLocale($locale)
{
$this->defaultLocale = $locale;
}
public function getLocale()
{
return $this->defaultLocale;
}
public function slug(string $string, string $separator = '-', string $locale = null): AbstractUnicodeString
{
$locale = $locale ?? $this->defaultLocale;
$transliterator = [];
if ($locale && ('de' === $locale || 0 === strpos($locale, 'de_'))) {
$transliterator = ['de-ASCII'];
} elseif (\function_exists('transliterator_transliterate') && $locale) {
$transliterator = (array) $this->createTransliterator($locale);
}
if ($this->symbolsMap instanceof \Closure) {
$symbolsMap = $this->symbolsMap;
array_unshift($transliterator, static function ($s) use ($symbolsMap, $locale) {
return $symbolsMap($s, $locale);
});
}
$unicodeString = (new UnicodeString($string))->ascii($transliterator);
if (\is_array($this->symbolsMap)) {
$map = null;
if (isset($this->symbolsMap[$locale])) {
$map = $this->symbolsMap[$locale];
} else {
$parent = self::getParentLocale($locale);
if ($parent && isset($this->symbolsMap[$parent])) {
$map = $this->symbolsMap[$parent];
}
}
if ($map) {
foreach ($map as $char => $replace) {
$unicodeString = $unicodeString->replace($char, ' '.$replace.' ');
}
}
}
return $unicodeString
->replaceMatches('/[^A-Za-z0-9]++/', $separator)
->trim($separator)
;
}
private function createTransliterator(string $locale): ?\Transliterator
{
if (\array_key_exists($locale, $this->transliterators)) {
return $this->transliterators[$locale];
}
if ($id = self::LOCALE_TO_TRANSLITERATOR_ID[$locale] ?? null) {
return $this->transliterators[$locale] = \Transliterator::create($id.'/BGN') ?? \Transliterator::create($id);
}
if (!$parent = self::getParentLocale($locale)) {
return $this->transliterators[$locale] = null;
}
if ($id = self::LOCALE_TO_TRANSLITERATOR_ID[$parent] ?? null) {
$transliterator = \Transliterator::create($id.'/BGN') ?? \Transliterator::create($id);
}
return $this->transliterators[$locale] = $this->transliterators[$parent] = $transliterator ?? null;
}
private static function getParentLocale(?string $locale): ?string
{
if (!$locale) {
return null;
}
if (false === $str = strrchr($locale, '_')) {
return null;
}
return substr($locale, 0, -\strlen($str));
}
}
<?php
if (!function_exists('trigger_deprecation')) {
function trigger_deprecation(string $package, string $version, string $message, ...$args): void
{
@trigger_error(($package || $version ? "Since $package $version: " : '').($args ? vsprintf($message, $args) : $message), \E_USER_DEPRECATED);
}
}
<?php
namespace Symfony\Contracts\Service;
interface ServiceSubscriberInterface
{
public static function getSubscribedServices();
}
<?php
namespace Symfony\Contracts\Service\Attribute;
use Symfony\Contracts\Service\ServiceSubscriberTrait;
#[\Attribute(\Attribute::TARGET_METHOD)]
final class SubscribedService
{
public function __construct(
public ?string $key = null
) {
}
}
<?php
namespace Symfony\Contracts\Service\Attribute;
#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::TARGET_PROPERTY)]
final class Required
{
}
<?php
namespace Symfony\Contracts\Service;
interface ResetInterface
{
public function reset();
}
<?php
namespace Symfony\Contracts\Service;
use Psr\Container\ContainerInterface;
interface ServiceProviderInterface extends ContainerInterface
{
public function getProvidedServices(): array;
}
<?php
namespace Symfony\Contracts\Service;
use Psr\Container\ContainerInterface;
use Symfony\Contracts\Service\Attribute\SubscribedService;
trait ServiceSubscriberTrait
{
protected $container;
public static function getSubscribedServices(): array
{
$services = method_exists(get_parent_class(self::class) ?: '', __FUNCTION__) ? parent::getSubscribedServices() : [];
$attributeOptIn = false;
if (\PHP_VERSION_ID >= 80000) {
foreach ((new \ReflectionClass(self::class))->getMethods() as $method) {
if (self::class !== $method->getDeclaringClass()->name) {
continue;
}
if (!$attribute = $method->getAttributes(SubscribedService::class)[0] ?? null) {
continue;
}
if ($method->isStatic() || $method->isAbstract() || $method->isGenerator() || $method->isInternal() || $method->getNumberOfRequiredParameters()) {
throw new \LogicException(sprintf('Cannot use "%s" on method "%s::%s()" (can only be used on non-static, non-abstract methods with no parameters).', SubscribedService::class, self::class, $method->name));
}
if (!$returnType = $method->getReturnType()) {
throw new \LogicException(sprintf('Cannot use "%s" on methods without a return type in "%s::%s()".', SubscribedService::class, $method->name, self::class));
}
$serviceId = $returnType instanceof \ReflectionNamedType ? $returnType->getName() : (string) $returnType;
if ($returnType->allowsNull()) {
$serviceId = '?'.$serviceId;
}
$services[$attribute->newInstance()->key ?? self::class.'::'.$method->name] = $serviceId;
$attributeOptIn = true;
}
}
if (!$attributeOptIn) {
foreach ((new \ReflectionClass(self::class))->getMethods() as $method) {
if ($method->isStatic() || $method->isAbstract() || $method->isGenerator() || $method->isInternal() || $method->getNumberOfRequiredParameters()) {
continue;
}
if (self::class !== $method->getDeclaringClass()->name) {
continue;
}
if (!($returnType = $method->getReturnType()) instanceof \ReflectionNamedType) {
continue;
}
if ($returnType->isBuiltin()) {
continue;
}
if (\PHP_VERSION_ID >= 80000) {
trigger_deprecation('symfony/service-contracts', '2.5', 'Using "%s" in "%s" without using the "%s" attribute on any method is deprecated.', ServiceSubscriberTrait::class, self::class, SubscribedService::class);
}
$services[self::class.'::'.$method->name] = '?'.($returnType instanceof \ReflectionNamedType ? $returnType->getName() : $returnType);
}
}
return $services;
}
public function setContainer(ContainerInterface $container)
{
$this->container = $container;
if (method_exists(get_parent_class(self::class) ?: '', __FUNCTION__)) {
return parent::setContainer($container);
}
return null;
}
}
<?php
namespace Symfony\Contracts\Service;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class_exists(ContainerExceptionInterface::class);
class_exists(NotFoundExceptionInterface::class);
trait ServiceLocatorTrait
{
private $factories;
private $loading = [];
private $providedTypes;
public function __construct(array $factories)
{
$this->factories = $factories;
}
public function has(string $id)
{
return isset($this->factories[$id]);
}
public function get(string $id)
{
if (!isset($this->factories[$id])) {
throw $this->createNotFoundException($id);
}
if (isset($this->loading[$id])) {
$ids = array_values($this->loading);
$ids = \array_slice($this->loading, array_search($id, $ids));
$ids[] = $id;
throw $this->createCircularReferenceException($id, $ids);
}
$this->loading[$id] = $id;
try {
return $this->factories[$id]($this);
} finally {
unset($this->loading[$id]);
}
}
public function getProvidedServices(): array
{
if (null === $this->providedTypes) {
$this->providedTypes = [];
foreach ($this->factories as $name => $factory) {
if (!\is_callable($factory)) {
$this->providedTypes[$name] = '?';
} else {
$type = (new \ReflectionFunction($factory))->getReturnType();
$this->providedTypes[$name] = $type ? ($type->allowsNull() ? '?' : '').($type instanceof \ReflectionNamedType ? $type->getName() : $type) : '?';
}
}
}
return $this->providedTypes;
}
private function createNotFoundException(string $id): NotFoundExceptionInterface
{
if (!$alternatives = array_keys($this->factories)) {
$message = 'is empty...';
} else {
$last = array_pop($alternatives);
if ($alternatives) {
$message = sprintf('only knows about the "%s" and "%s" services.', implode('", "', $alternatives), $last);
} else {
$message = sprintf('only knows about the "%s" service.', $last);
}
}
if ($this->loading) {
$message = sprintf('The service "%s" has a dependency on a non-existent service "%s". This locator %s', end($this->loading), $id, $message);
} else {
$message = sprintf('Service "%s" not found: the current service locator %s', $id, $message);
}
return new class($message) extends \InvalidArgumentException implements NotFoundExceptionInterface {
};
}
private function createCircularReferenceException(string $id, array $path): ContainerExceptionInterface
{
return new class(sprintf('Circular reference detected for service "%s", path: "%s".', $id, implode(' -> ', $path))) extends \RuntimeException implements ContainerExceptionInterface {
};
}
}
<?php
namespace Symfony\Component\Process;
class ExecutableFinder
{
private $suffixes = ['.exe', '.bat', '.cmd', '.com'];
public function setSuffixes(array $suffixes)
{
$this->suffixes = $suffixes;
}
public function addSuffix(string $suffix)
{
$this->suffixes[] = $suffix;
}
public function find(string $name, string $default = null, array $extraDirs = [])
{
if (\ini_get('open_basedir')) {
$searchPath = array_merge(explode(\PATH_SEPARATOR, \ini_get('open_basedir')), $extraDirs);
$dirs = [];
foreach ($searchPath as $path) {
if (@is_dir($path)) {
$dirs[] = $path;
} else {
if (basename($path) == $name && @is_executable($path)) {
return $path;
}
}
}
} else {
$dirs = array_merge(
explode(\PATH_SEPARATOR, getenv('PATH') ?: getenv('Path')),
$extraDirs
);
}
$suffixes = [''];
if ('\\' === \DIRECTORY_SEPARATOR) {
$pathExt = getenv('PATHEXT');
$suffixes = array_merge($pathExt ? explode(\PATH_SEPARATOR, $pathExt) : $this->suffixes, $suffixes);
}
foreach ($suffixes as $suffix) {
foreach ($dirs as $dir) {
if (@is_file($file = $dir.\DIRECTORY_SEPARATOR.$name.$suffix) && ('\\' === \DIRECTORY_SEPARATOR || @is_executable($file))) {
return $file;
}
}
}
return $default;
}
}
<?php
namespace Symfony\Component\Process\Pipes;
use Symfony\Component\Process\Process;
class UnixPipes extends AbstractPipes
{
private $ttyMode;
private $ptyMode;
private $haveReadSupport;
public function __construct(?bool $ttyMode, bool $ptyMode, $input, bool $haveReadSupport)
{
$this->ttyMode = $ttyMode;
$this->ptyMode = $ptyMode;
$this->haveReadSupport = $haveReadSupport;
parent::__construct($input);
}
public function __sleep(): array
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
public function __destruct()
{
$this->close();
}
public function getDescriptors(): array
{
if (!$this->haveReadSupport) {
$nullstream = fopen('/dev/null', 'c');
return [
['pipe', 'r'],
$nullstream,
$nullstream,
];
}
if ($this->ttyMode) {
return [
['file', '/dev/tty', 'r'],
['file', '/dev/tty', 'w'],
['file', '/dev/tty', 'w'],
];
}
if ($this->ptyMode && Process::isPtySupported()) {
return [
['pty'],
['pty'],
['pty'],
];
}
return [
['pipe', 'r'],
['pipe', 'w'],
['pipe', 'w'],
];
}
public function getFiles(): array
{
return [];
}
public function readAndWrite(bool $blocking, bool $close = false): array
{
$this->unblock();
$w = $this->write();
$read = $e = [];
$r = $this->pipes;
unset($r[0]);
set_error_handler([$this, 'handleError']);
if (($r || $w) && false === stream_select($r, $w, $e, 0, $blocking ? Process::TIMEOUT_PRECISION * 1E6 : 0)) {
restore_error_handler();
if (!$this->hasSystemCallBeenInterrupted()) {
$this->pipes = [];
}
return $read;
}
restore_error_handler();
foreach ($r as $pipe) {
$read[$type = array_search($pipe, $this->pipes, true)] = '';
do {
$data = @fread($pipe, self::CHUNK_SIZE);
$read[$type] .= $data;
} while (isset($data[0]) && ($close || isset($data[self::CHUNK_SIZE - 1])));
if (!isset($read[$type][0])) {
unset($read[$type]);
}
if ($close && feof($pipe)) {
fclose($pipe);
unset($this->pipes[$type]);
}
}
return $read;
}
public function haveReadSupport(): bool
{
return $this->haveReadSupport;
}
public function areOpen(): bool
{
return (bool) $this->pipes;
}
}
<?php
namespace Symfony\Component\Process\Pipes;
use Symfony\Component\Process\Exception\RuntimeException;
use Symfony\Component\Process\Process;
class WindowsPipes extends AbstractPipes
{
private $files = [];
private $fileHandles = [];
private $lockHandles = [];
private $readBytes = [
Process::STDOUT => 0,
Process::STDERR => 0,
];
private $haveReadSupport;
public function __construct($input, bool $haveReadSupport)
{
$this->haveReadSupport = $haveReadSupport;
if ($this->haveReadSupport) {
$pipes = [
Process::STDOUT => Process::OUT,
Process::STDERR => Process::ERR,
];
$tmpDir = sys_get_temp_dir();
$lastError = 'unknown reason';
set_error_handler(function ($type, $msg) use (&$lastError) { $lastError = $msg; });
for ($i = 0;; ++$i) {
foreach ($pipes as $pipe => $name) {
$file = sprintf('%s\\sf_proc_%02X.%s', $tmpDir, $i, $name);
if (!$h = fopen($file.'.lock', 'w')) {
if (file_exists($file.'.lock')) {
continue 2;
}
restore_error_handler();
throw new RuntimeException('A temporary file could not be opened to write the process output: '.$lastError);
}
if (!flock($h, \LOCK_EX | \LOCK_NB)) {
continue 2;
}
if (isset($this->lockHandles[$pipe])) {
flock($this->lockHandles[$pipe], \LOCK_UN);
fclose($this->lockHandles[$pipe]);
}
$this->lockHandles[$pipe] = $h;
if (!($h = fopen($file, 'w')) || !fclose($h) || !$h = fopen($file, 'r')) {
flock($this->lockHandles[$pipe], \LOCK_UN);
fclose($this->lockHandles[$pipe]);
unset($this->lockHandles[$pipe]);
continue 2;
}
$this->fileHandles[$pipe] = $h;
$this->files[$pipe] = $file;
}
break;
}
restore_error_handler();
}
parent::__construct($input);
}
public function __sleep(): array
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
public function __destruct()
{
$this->close();
}
public function getDescriptors(): array
{
if (!$this->haveReadSupport) {
$nullstream = fopen('NUL', 'c');
return [
['pipe', 'r'],
$nullstream,
$nullstream,
];
}
return [
['pipe', 'r'],
['file', 'NUL', 'w'],
['file', 'NUL', 'w'],
];
}
public function getFiles(): array
{
return $this->files;
}
public function readAndWrite(bool $blocking, bool $close = false): array
{
$this->unblock();
$w = $this->write();
$read = $r = $e = [];
if ($blocking) {
if ($w) {
@stream_select($r, $w, $e, 0, Process::TIMEOUT_PRECISION * 1E6);
} elseif ($this->fileHandles) {
usleep(Process::TIMEOUT_PRECISION * 1E6);
}
}
foreach ($this->fileHandles as $type => $fileHandle) {
$data = stream_get_contents($fileHandle, -1, $this->readBytes[$type]);
if (isset($data[0])) {
$this->readBytes[$type] += \strlen($data);
$read[$type] = $data;
}
if ($close) {
ftruncate($fileHandle, 0);
fclose($fileHandle);
flock($this->lockHandles[$type], \LOCK_UN);
fclose($this->lockHandles[$type]);
unset($this->fileHandles[$type], $this->lockHandles[$type]);
}
}
return $read;
}
public function haveReadSupport(): bool
{
return $this->haveReadSupport;
}
public function areOpen(): bool
{
return $this->pipes && $this->fileHandles;
}
public function close()
{
parent::close();
foreach ($this->fileHandles as $type => $handle) {
ftruncate($handle, 0);
fclose($handle);
flock($this->lockHandles[$type], \LOCK_UN);
fclose($this->lockHandles[$type]);
}
$this->fileHandles = $this->lockHandles = [];
}
}
<?php
namespace Symfony\Component\Process\Pipes;
use Symfony\Component\Process\Exception\InvalidArgumentException;
abstract class AbstractPipes implements PipesInterface
{
public $pipes = [];
private $inputBuffer = '';
private $input;
private $blocked = true;
private $lastError;
public function __construct($input)
{
if (\is_resource($input) || $input instanceof \Iterator) {
$this->input = $input;
} elseif (\is_string($input)) {
$this->inputBuffer = $input;
} else {
$this->inputBuffer = (string) $input;
}
}
public function close()
{
foreach ($this->pipes as $pipe) {
if (\is_resource($pipe)) {
fclose($pipe);
}
}
$this->pipes = [];
}
protected function hasSystemCallBeenInterrupted(): bool
{
$lastError = $this->lastError;
$this->lastError = null;
return null !== $lastError && false !== stripos($lastError, 'interrupted system call');
}
protected function unblock()
{
if (!$this->blocked) {
return;
}
foreach ($this->pipes as $pipe) {
stream_set_blocking($pipe, 0);
}
if (\is_resource($this->input)) {
stream_set_blocking($this->input, 0);
}
$this->blocked = false;
}
protected function write(): ?array
{
if (!isset($this->pipes[0])) {
return null;
}
$input = $this->input;
if ($input instanceof \Iterator) {
if (!$input->valid()) {
$input = null;
} elseif (\is_resource($input = $input->current())) {
stream_set_blocking($input, 0);
} elseif (!isset($this->inputBuffer[0])) {
if (!\is_string($input)) {
if (!\is_scalar($input)) {
throw new InvalidArgumentException(sprintf('"%s" yielded a value of type "%s", but only scalars and stream resources are supported.', get_debug_type($this->input), get_debug_type($input)));
}
$input = (string) $input;
}
$this->inputBuffer = $input;
$this->input->next();
$input = null;
} else {
$input = null;
}
}
$r = $e = [];
$w = [$this->pipes[0]];
if (false === @stream_select($r, $w, $e, 0, 0)) {
return null;
}
foreach ($w as $stdin) {
if (isset($this->inputBuffer[0])) {
$written = fwrite($stdin, $this->inputBuffer);
$this->inputBuffer = substr($this->inputBuffer, $written);
if (isset($this->inputBuffer[0])) {
return [$this->pipes[0]];
}
}
if ($input) {
while (true) {
$data = fread($input, self::CHUNK_SIZE);
if (!isset($data[0])) {
break;
}
$written = fwrite($stdin, $data);
$data = substr($data, $written);
if (isset($data[0])) {
$this->inputBuffer = $data;
return [$this->pipes[0]];
}
}
if (feof($input)) {
if ($this->input instanceof \Iterator) {
$this->input->next();
} else {
$this->input = null;
}
}
}
}
if (!isset($this->inputBuffer[0]) && !($this->input instanceof \Iterator ? $this->input->valid() : $this->input)) {
$this->input = null;
fclose($this->pipes[0]);
unset($this->pipes[0]);
} elseif (!$w) {
return [$this->pipes[0]];
}
return null;
}
public function handleError(int $type, string $msg)
{
$this->lastError = $msg;
}
}
<?php
namespace Symfony\Component\Process\Pipes;
interface PipesInterface
{
public const CHUNK_SIZE = 16384;
public function getDescriptors(): array;
public function getFiles(): array;
public function readAndWrite(bool $blocking, bool $close = false): array;
public function areOpen(): bool;
public function haveReadSupport(): bool;
public function close();
}
<?php
namespace Symfony\Component\Process;
use Symfony\Component\Process\Exception\RuntimeException;
/**
@implements
*/
class InputStream implements \IteratorAggregate
{
private $onEmpty = null;
private $input = [];
private $open = true;
public function onEmpty(callable $onEmpty = null)
{
$this->onEmpty = $onEmpty;
}
public function write($input)
{
if (null === $input) {
return;
}
if ($this->isClosed()) {
throw new RuntimeException(sprintf('"%s" is closed.', static::class));
}
$this->input[] = ProcessUtils::validateInput(__METHOD__, $input);
}
public function close()
{
$this->open = false;
}
public function isClosed()
{
return !$this->open;
}
#[\ReturnTypeWillChange]
public function getIterator()
{
$this->open = true;
while ($this->open || $this->input) {
if (!$this->input) {
yield '';
continue;
}
$current = array_shift($this->input);
if ($current instanceof \Iterator) {
yield from $current;
} else {
yield $current;
}
if (!$this->input && $this->open && null !== $onEmpty = $this->onEmpty) {
$this->write($onEmpty($this));
}
}
}
}
<?php
namespace Symfony\Component\Process;
use Symfony\Component\Process\Exception\LogicException;
use Symfony\Component\Process\Exception\RuntimeException;
class PhpProcess extends Process
{
public function __construct(string $script, string $cwd = null, array $env = null, int $timeout = 60, array $php = null)
{
if (null === $php) {
$executableFinder = new PhpExecutableFinder();
$php = $executableFinder->find(false);
$php = false === $php ? null : array_merge([$php], $executableFinder->findArguments());
}
if ('phpdbg' === \PHP_SAPI) {
$file = tempnam(sys_get_temp_dir(), 'dbg');
file_put_contents($file, $script);
register_shutdown_function('unlink', $file);
$php[] = $file;
$script = null;
}
parent::__construct($php, $cwd, $env, $script, $timeout);
}
public static function fromShellCommandline(string $command, string $cwd = null, array $env = null, $input = null, ?float $timeout = 60)
{
throw new LogicException(sprintf('The "%s()" method cannot be called when using "%s".', __METHOD__, self::class));
}
public function start(callable $callback = null, array $env = [])
{
if (null === $this->getCommandLine()) {
throw new RuntimeException('Unable to find the PHP executable.');
}
parent::start($callback, $env);
}
}
<?php
namespace Symfony\Component\Process;
class PhpExecutableFinder
{
private $executableFinder;
public function __construct()
{
$this->executableFinder = new ExecutableFinder();
}
public function find(bool $includeArgs = true)
{
if ($php = getenv('PHP_BINARY')) {
if (!is_executable($php)) {
$command = '\\' === \DIRECTORY_SEPARATOR ? 'where' : 'command -v';
if ($php = strtok(exec($command.' '.escapeshellarg($php)), \PHP_EOL)) {
if (!is_executable($php)) {
return false;
}
} else {
return false;
}
}
if (@is_dir($php)) {
return false;
}
return $php;
}
$args = $this->findArguments();
$args = $includeArgs && $args ? ' '.implode(' ', $args) : '';
if (\PHP_BINARY && \in_array(\PHP_SAPI, ['cgi-fcgi', 'cli', 'cli-server', 'phpdbg'], true)) {
return \PHP_BINARY.$args;
}
if ($php = getenv('PHP_PATH')) {
if (!@is_executable($php) || @is_dir($php)) {
return false;
}
return $php;
}
if ($php = getenv('PHP_PEAR_PHP_BIN')) {
if (@is_executable($php) && !@is_dir($php)) {
return $php;
}
}
if (@is_executable($php = \PHP_BINDIR.('\\' === \DIRECTORY_SEPARATOR ? '\\php.exe' : '/php')) && !@is_dir($php)) {
return $php;
}
$dirs = [\PHP_BINDIR];
if ('\\' === \DIRECTORY_SEPARATOR) {
$dirs[] = 'C:\xampp\php\\';
}
return $this->executableFinder->find('php', false, $dirs);
}
public function findArguments()
{
$arguments = [];
if ('phpdbg' === \PHP_SAPI) {
$arguments[] = '-qrr';
}
return $arguments;
}
}
<?php
namespace Symfony\Component\Process\Exception;
use Symfony\Component\Process\Process;
final class ProcessSignaledException extends RuntimeException
{
private $process;
public function __construct(Process $process)
{
$this->process = $process;
parent::__construct(sprintf('The process has been signaled with signal "%s".', $process->getTermSignal()));
}
public function getProcess(): Process
{
return $this->process;
}
public function getSignal(): int
{
return $this->getProcess()->getTermSignal();
}
}
<?php
namespace Symfony\Component\Process\Exception;
class LogicException extends \LogicException implements ExceptionInterface
{
}
<?php
namespace Symfony\Component\Process\Exception;
interface ExceptionInterface extends \Throwable
{
}
<?php
namespace Symfony\Component\Process\Exception;
use Symfony\Component\Process\Process;
class ProcessFailedException extends RuntimeException
{
private $process;
public function __construct(Process $process)
{
if ($process->isSuccessful()) {
throw new InvalidArgumentException('Expected a failed process, but the given process was successful.');
}
$error = sprintf('The command "%s" failed.'."\n\nExit Code: %s(%s)\n\nWorking directory: %s",
$process->getCommandLine(),
$process->getExitCode(),
$process->getExitCodeText(),
$process->getWorkingDirectory()
);
if (!$process->isOutputDisabled()) {
$error .= sprintf("\n\nOutput:\n================\n%s\n\nError Output:\n================\n%s",
$process->getOutput(),
$process->getErrorOutput()
);
}
parent::__construct($error);
$this->process = $process;
}
public function getProcess()
{
return $this->process;
}
}
<?php
namespace Symfony\Component\Process\Exception;
class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}
<?php
namespace Symfony\Component\Process\Exception;
use Symfony\Component\Process\Process;
class ProcessTimedOutException extends RuntimeException
{
public const TYPE_GENERAL = 1;
public const TYPE_IDLE = 2;
private $process;
private $timeoutType;
public function __construct(Process $process, int $timeoutType)
{
$this->process = $process;
$this->timeoutType = $timeoutType;
parent::__construct(sprintf(
'The process "%s" exceeded the timeout of %s seconds.',
$process->getCommandLine(),
$this->getExceededTimeout()
));
}
public function getProcess()
{
return $this->process;
}
public function isGeneralTimeout()
{
return self::TYPE_GENERAL === $this->timeoutType;
}
public function isIdleTimeout()
{
return self::TYPE_IDLE === $this->timeoutType;
}
public function getExceededTimeout()
{
switch ($this->timeoutType) {
case self::TYPE_GENERAL:
return $this->process->getTimeout();
case self::TYPE_IDLE:
return $this->process->getIdleTimeout();
default:
throw new \LogicException(sprintf('Unknown timeout type "%d".', $this->timeoutType));
}
}
}
<?php
namespace Symfony\Component\Process\Exception;
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
{
}
<?php
namespace Symfony\Component\Process;
use Symfony\Component\Process\Exception\InvalidArgumentException;
class ProcessUtils
{
private function __construct()
{
}
public static function validateInput(string $caller, $input)
{
if (null !== $input) {
if (\is_resource($input)) {
return $input;
}
if (\is_string($input)) {
return $input;
}
if (\is_scalar($input)) {
return (string) $input;
}
if ($input instanceof Process) {
return $input->getIterator($input::ITER_SKIP_ERR);
}
if ($input instanceof \Iterator) {
return $input;
}
if ($input instanceof \Traversable) {
return new \IteratorIterator($input);
}
throw new InvalidArgumentException(sprintf('"%s" only accepts strings, Traversable objects or stream resources.', $caller));
}
return $input;
}
}
<?php
namespace Symfony\Component\Process;
use Symfony\Component\Process\Exception\InvalidArgumentException;
use Symfony\Component\Process\Exception\LogicException;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Exception\ProcessSignaledException;
use Symfony\Component\Process\Exception\ProcessTimedOutException;
use Symfony\Component\Process\Exception\RuntimeException;
use Symfony\Component\Process\Pipes\PipesInterface;
use Symfony\Component\Process\Pipes\UnixPipes;
use Symfony\Component\Process\Pipes\WindowsPipes;
/**
@implements
*/
class Process implements \IteratorAggregate
{
public const ERR = 'err';
public const OUT = 'out';
public const STATUS_READY = 'ready';
public const STATUS_STARTED = 'started';
public const STATUS_TERMINATED = 'terminated';
public const STDIN = 0;
public const STDOUT = 1;
public const STDERR = 2;
public const TIMEOUT_PRECISION = 0.2;
public const ITER_NON_BLOCKING = 1;
public const ITER_KEEP_OUTPUT = 2;
public const ITER_SKIP_OUT = 4;
public const ITER_SKIP_ERR = 8;
private $callback;
private $hasCallback = false;
private $commandline;
private $cwd;
private $env = [];
private $input;
private $starttime;
private $lastOutputTime;
private $timeout;
private $idleTimeout;
private $exitcode;
private $fallbackStatus = [];
private $processInformation;
private $outputDisabled = false;
private $stdout;
private $stderr;
private $process;
private $status = self::STATUS_READY;
private $incrementalOutputOffset = 0;
private $incrementalErrorOutputOffset = 0;
private $tty = false;
private $pty;
private $options = ['suppress_errors' => true, 'bypass_shell' => true];
private $useFileHandles = false;
private $processPipes;
private $latestSignal;
private static $sigchild;
public static $exitCodes = [
0 => 'OK',
1 => 'General error',
2 => 'Misuse of shell builtins',
126 => 'Invoked command cannot execute',
127 => 'Command not found',
128 => 'Invalid exit argument',
129 => 'Hangup',
130 => 'Interrupt',
131 => 'Quit and dump core',
132 => 'Illegal instruction',
133 => 'Trace/breakpoint trap',
134 => 'Process aborted',
135 => 'Bus error: "access to undefined portion of memory object"',
136 => 'Floating point exception: "erroneous arithmetic operation"',
137 => 'Kill (terminate immediately)',
138 => 'User-defined 1',
139 => 'Segmentation violation',
140 => 'User-defined 2',
141 => 'Write to pipe with no one reading',
142 => 'Signal raised by alarm',
143 => 'Termination (request to terminate)',
145 => 'Child process terminated, stopped (or continued*)',
146 => 'Continue if stopped',
147 => 'Stop executing temporarily',
148 => 'Terminal stop signal',
149 => 'Background process attempting to read from tty ("in")',
150 => 'Background process attempting to write to tty ("out")',
151 => 'Urgent data available on socket',
152 => 'CPU time limit exceeded',
153 => 'File size limit exceeded',
154 => 'Signal raised by timer counting virtual time: "virtual timer expired"',
155 => 'Profiling timer expired',
157 => 'Pollable event',
159 => 'Bad syscall',
];
public function __construct(array $command, string $cwd = null, array $env = null, $input = null, ?float $timeout = 60)
{
if (!\function_exists('proc_open')) {
throw new LogicException('The Process class relies on proc_open, which is not available on your PHP installation.');
}
$this->commandline = $command;
$this->cwd = $cwd;
if (null === $this->cwd && (\defined('ZEND_THREAD_SAFE') || '\\' === \DIRECTORY_SEPARATOR)) {
$this->cwd = getcwd();
}
if (null !== $env) {
$this->setEnv($env);
}
$this->setInput($input);
$this->setTimeout($timeout);
$this->useFileHandles = '\\' === \DIRECTORY_SEPARATOR;
$this->pty = false;
}
public static function fromShellCommandline(string $command, string $cwd = null, array $env = null, $input = null, ?float $timeout = 60)
{
$process = new static([], $cwd, $env, $input, $timeout);
$process->commandline = $command;
return $process;
}
public function __sleep()
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
public function __destruct()
{
if ($this->options['create_new_console'] ?? false) {
$this->processPipes->close();
} else {
$this->stop(0);
}
}
public function __clone()
{
$this->resetProcessData();
}
public function run(callable $callback = null, array $env = []): int
{
$this->start($callback, $env);
return $this->wait();
}
public function mustRun(callable $callback = null, array $env = []): self
{
if (0 !== $this->run($callback, $env)) {
throw new ProcessFailedException($this);
}
return $this;
}
public function start(callable $callback = null, array $env = [])
{
if ($this->isRunning()) {
throw new RuntimeException('Process is already running.');
}
$this->resetProcessData();
$this->starttime = $this->lastOutputTime = microtime(true);
$this->callback = $this->buildCallback($callback);
$this->hasCallback = null !== $callback;
$descriptors = $this->getDescriptors();
if ($this->env) {
$env += '\\' === \DIRECTORY_SEPARATOR ? array_diff_ukey($this->env, $env, 'strcasecmp') : $this->env;
}
$env += '\\' === \DIRECTORY_SEPARATOR ? array_diff_ukey($this->getDefaultEnv(), $env, 'strcasecmp') : $this->getDefaultEnv();
if (\is_array($commandline = $this->commandline)) {
$commandline = implode(' ', array_map([$this, 'escapeArgument'], $commandline));
if ('\\' !== \DIRECTORY_SEPARATOR) {
$commandline = 'exec '.$commandline;
}
} else {
$commandline = $this->replacePlaceholders($commandline, $env);
}
if ('\\' === \DIRECTORY_SEPARATOR) {
$commandline = $this->prepareWindowsCommandLine($commandline, $env);
} elseif (!$this->useFileHandles && $this->isSigchildEnabled()) {
$descriptors[3] = ['pipe', 'w'];
$commandline = '{ ('.$commandline.') <&3 3<&- 3>/dev/null & } 3<&0;';
$commandline .= 'pid=$!; echo $pid >&3; wait $pid; code=$?; echo $code >&3; exit $code';
$ptsWorkaround = fopen(__FILE__, 'r');
}
$envPairs = [];
foreach ($env as $k => $v) {
if (false !== $v && false === \in_array($k, ['argc', 'argv', 'ARGC', 'ARGV'], true)) {
$envPairs[] = $k.'='.$v;
}
}
if (!is_dir($this->cwd)) {
throw new RuntimeException(sprintf('The provided cwd "%s" does not exist.', $this->cwd));
}
$this->process = @proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $envPairs, $this->options);
if (!\is_resource($this->process)) {
throw new RuntimeException('Unable to launch a new process.');
}
$this->status = self::STATUS_STARTED;
if (isset($descriptors[3])) {
$this->fallbackStatus['pid'] = (int) fgets($this->processPipes->pipes[3]);
}
if ($this->tty) {
return;
}
$this->updateStatus(false);
$this->checkTimeout();
}
public function restart(callable $callback = null, array $env = []): self
{
if ($this->isRunning()) {
throw new RuntimeException('Process is already running.');
}
$process = clone $this;
$process->start($callback, $env);
return $process;
}
public function wait(callable $callback = null)
{
$this->requireProcessIsStarted(__FUNCTION__);
$this->updateStatus(false);
if (null !== $callback) {
if (!$this->processPipes->haveReadSupport()) {
$this->stop(0);
throw new LogicException('Pass the callback to the "Process::start" method or call enableOutput to use a callback with "Process::wait".');
}
$this->callback = $this->buildCallback($callback);
}
do {
$this->checkTimeout();
$running = '\\' === \DIRECTORY_SEPARATOR ? $this->isRunning() : $this->processPipes->areOpen();
$this->readPipes($running, '\\' !== \DIRECTORY_SEPARATOR || !$running);
} while ($running);
while ($this->isRunning()) {
$this->checkTimeout();
usleep(1000);
}
if ($this->processInformation['signaled'] && $this->processInformation['termsig'] !== $this->latestSignal) {
throw new ProcessSignaledException($this);
}
return $this->exitcode;
}
public function waitUntil(callable $callback): bool
{
$this->requireProcessIsStarted(__FUNCTION__);
$this->updateStatus(false);
if (!$this->processPipes->haveReadSupport()) {
$this->stop(0);
throw new LogicException('Pass the callback to the "Process::start" method or call enableOutput to use a callback with "Process::waitUntil".');
}
$callback = $this->buildCallback($callback);
$ready = false;
while (true) {
$this->checkTimeout();
$running = '\\' === \DIRECTORY_SEPARATOR ? $this->isRunning() : $this->processPipes->areOpen();
$output = $this->processPipes->readAndWrite($running, '\\' !== \DIRECTORY_SEPARATOR || !$running);
foreach ($output as $type => $data) {
if (3 !== $type) {
$ready = $callback(self::STDOUT === $type ? self::OUT : self::ERR, $data) || $ready;
} elseif (!isset($this->fallbackStatus['signaled'])) {
$this->fallbackStatus['exitcode'] = (int) $data;
}
}
if ($ready) {
return true;
}
if (!$running) {
return false;
}
usleep(1000);
}
}
public function getPid()
{
return $this->isRunning() ? $this->processInformation['pid'] : null;
}
public function signal(int $signal)
{
$this->doSignal($signal, true);
return $this;
}
public function disableOutput()
{
if ($this->isRunning()) {
throw new RuntimeException('Disabling output while the process is running is not possible.');
}
if (null !== $this->idleTimeout) {
throw new LogicException('Output cannot be disabled while an idle timeout is set.');
}
$this->outputDisabled = true;
return $this;
}
public function enableOutput()
{
if ($this->isRunning()) {
throw new RuntimeException('Enabling output while the process is running is not possible.');
}
$this->outputDisabled = false;
return $this;
}
public function isOutputDisabled()
{
return $this->outputDisabled;
}
public function getOutput()
{
$this->readPipesForOutput(__FUNCTION__);
if (false === $ret = stream_get_contents($this->stdout, -1, 0)) {
return '';
}
return $ret;
}
public function getIncrementalOutput()
{
$this->readPipesForOutput(__FUNCTION__);
$latest = stream_get_contents($this->stdout, -1, $this->incrementalOutputOffset);
$this->incrementalOutputOffset = ftell($this->stdout);
if (false === $latest) {
return '';
}
return $latest;
}
#[\ReturnTypeWillChange]
public function getIterator(int $flags = 0)
{
$this->readPipesForOutput(__FUNCTION__, false);
$clearOutput = !(self::ITER_KEEP_OUTPUT & $flags);
$blocking = !(self::ITER_NON_BLOCKING & $flags);
$yieldOut = !(self::ITER_SKIP_OUT & $flags);
$yieldErr = !(self::ITER_SKIP_ERR & $flags);
while (null !== $this->callback || ($yieldOut && !feof($this->stdout)) || ($yieldErr && !feof($this->stderr))) {
if ($yieldOut) {
$out = stream_get_contents($this->stdout, -1, $this->incrementalOutputOffset);
if (isset($out[0])) {
if ($clearOutput) {
$this->clearOutput();
} else {
$this->incrementalOutputOffset = ftell($this->stdout);
}
yield self::OUT => $out;
}
}
if ($yieldErr) {
$err = stream_get_contents($this->stderr, -1, $this->incrementalErrorOutputOffset);
if (isset($err[0])) {
if ($clearOutput) {
$this->clearErrorOutput();
} else {
$this->incrementalErrorOutputOffset = ftell($this->stderr);
}
yield self::ERR => $err;
}
}
if (!$blocking && !isset($out[0]) && !isset($err[0])) {
yield self::OUT => '';
}
$this->checkTimeout();
$this->readPipesForOutput(__FUNCTION__, $blocking);
}
}
public function clearOutput()
{
ftruncate($this->stdout, 0);
fseek($this->stdout, 0);
$this->incrementalOutputOffset = 0;
return $this;
}
public function getErrorOutput()
{
$this->readPipesForOutput(__FUNCTION__);
if (false === $ret = stream_get_contents($this->stderr, -1, 0)) {
return '';
}
return $ret;
}
public function getIncrementalErrorOutput()
{
$this->readPipesForOutput(__FUNCTION__);
$latest = stream_get_contents($this->stderr, -1, $this->incrementalErrorOutputOffset);
$this->incrementalErrorOutputOffset = ftell($this->stderr);
if (false === $latest) {
return '';
}
return $latest;
}
public function clearErrorOutput()
{
ftruncate($this->stderr, 0);
fseek($this->stderr, 0);
$this->incrementalErrorOutputOffset = 0;
return $this;
}
public function getExitCode()
{
$this->updateStatus(false);
return $this->exitcode;
}
public function getExitCodeText()
{
if (null === $exitcode = $this->getExitCode()) {
return null;
}
return self::$exitCodes[$exitcode] ?? 'Unknown error';
}
public function isSuccessful()
{
return 0 === $this->getExitCode();
}
public function hasBeenSignaled()
{
$this->requireProcessIsTerminated(__FUNCTION__);
return $this->processInformation['signaled'];
}
public function getTermSignal()
{
$this->requireProcessIsTerminated(__FUNCTION__);
if ($this->isSigchildEnabled() && -1 === $this->processInformation['termsig']) {
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal cannot be retrieved.');
}
return $this->processInformation['termsig'];
}
public function hasBeenStopped()
{
$this->requireProcessIsTerminated(__FUNCTION__);
return $this->processInformation['stopped'];
}
public function getStopSignal()
{
$this->requireProcessIsTerminated(__FUNCTION__);
return $this->processInformation['stopsig'];
}
public function isRunning()
{
if (self::STATUS_STARTED !== $this->status) {
return false;
}
$this->updateStatus(false);
return $this->processInformation['running'];
}
public function isStarted()
{
return self::STATUS_READY != $this->status;
}
public function isTerminated()
{
$this->updateStatus(false);
return self::STATUS_TERMINATED == $this->status;
}
public function getStatus()
{
$this->updateStatus(false);
return $this->status;
}
public function stop(float $timeout = 10, int $signal = null)
{
$timeoutMicro = microtime(true) + $timeout;
if ($this->isRunning()) {
$this->doSignal(15, false);
do {
usleep(1000);
} while ($this->isRunning() && microtime(true) < $timeoutMicro);
if ($this->isRunning()) {
$this->doSignal($signal ?: 9, false);
}
}
if ($this->isRunning()) {
if (isset($this->fallbackStatus['pid'])) {
unset($this->fallbackStatus['pid']);
return $this->stop(0, $signal);
}
$this->close();
}
return $this->exitcode;
}
public function addOutput(string $line)
{
$this->lastOutputTime = microtime(true);
fseek($this->stdout, 0, \SEEK_END);
fwrite($this->stdout, $line);
fseek($this->stdout, $this->incrementalOutputOffset);
}
public function addErrorOutput(string $line)
{
$this->lastOutputTime = microtime(true);
fseek($this->stderr, 0, \SEEK_END);
fwrite($this->stderr, $line);
fseek($this->stderr, $this->incrementalErrorOutputOffset);
}
public function getLastOutputTime(): ?float
{
return $this->lastOutputTime;
}
public function getCommandLine()
{
return \is_array($this->commandline) ? implode(' ', array_map([$this, 'escapeArgument'], $this->commandline)) : $this->commandline;
}
public function getTimeout()
{
return $this->timeout;
}
public function getIdleTimeout()
{
return $this->idleTimeout;
}
public function setTimeout(?float $timeout)
{
$this->timeout = $this->validateTimeout($timeout);
return $this;
}
public function setIdleTimeout(?float $timeout)
{
if (null !== $timeout && $this->outputDisabled) {
throw new LogicException('Idle timeout cannot be set while the output is disabled.');
}
$this->idleTimeout = $this->validateTimeout($timeout);
return $this;
}
public function setTty(bool $tty)
{
if ('\\' === \DIRECTORY_SEPARATOR && $tty) {
throw new RuntimeException('TTY mode is not supported on Windows platform.');
}
if ($tty && !self::isTtySupported()) {
throw new RuntimeException('TTY mode requires /dev/tty to be read/writable.');
}
$this->tty = $tty;
return $this;
}
public function isTty()
{
return $this->tty;
}
public function setPty(bool $bool)
{
$this->pty = $bool;
return $this;
}
public function isPty()
{
return $this->pty;
}
public function getWorkingDirectory()
{
if (null === $this->cwd) {
return getcwd() ?: null;
}
return $this->cwd;
}
public function setWorkingDirectory(string $cwd)
{
$this->cwd = $cwd;
return $this;
}
public function getEnv()
{
return $this->env;
}
public function setEnv(array $env)
{
$this->env = $env;
return $this;
}
public function getInput()
{
return $this->input;
}
public function setInput($input)
{
if ($this->isRunning()) {
throw new LogicException('Input cannot be set while the process is running.');
}
$this->input = ProcessUtils::validateInput(__METHOD__, $input);
return $this;
}
public function checkTimeout()
{
if (self::STATUS_STARTED !== $this->status) {
return;
}
if (null !== $this->timeout && $this->timeout < microtime(true) - $this->starttime) {
$this->stop(0);
throw new ProcessTimedOutException($this, ProcessTimedOutException::TYPE_GENERAL);
}
if (null !== $this->idleTimeout && $this->idleTimeout < microtime(true) - $this->lastOutputTime) {
$this->stop(0);
throw new ProcessTimedOutException($this, ProcessTimedOutException::TYPE_IDLE);
}
}
public function getStartTime(): float
{
if (!$this->isStarted()) {
throw new LogicException('Start time is only available after process start.');
}
return $this->starttime;
}
public function setOptions(array $options)
{
if ($this->isRunning()) {
throw new RuntimeException('Setting options while the process is running is not possible.');
}
$defaultOptions = $this->options;
$existingOptions = ['blocking_pipes', 'create_process_group', 'create_new_console'];
foreach ($options as $key => $value) {
if (!\in_array($key, $existingOptions)) {
$this->options = $defaultOptions;
throw new LogicException(sprintf('Invalid option "%s" passed to "%s()". Supported options are "%s".', $key, __METHOD__, implode('", "', $existingOptions)));
}
$this->options[$key] = $value;
}
}
public static function isTtySupported(): bool
{
static $isTtySupported;
if (null === $isTtySupported) {
$isTtySupported = (bool) @proc_open('echo 1 >/dev/null', [['file', '/dev/tty', 'r'], ['file', '/dev/tty', 'w'], ['file', '/dev/tty', 'w']], $pipes);
}
return $isTtySupported;
}
public static function isPtySupported()
{
static $result;
if (null !== $result) {
return $result;
}
if ('\\' === \DIRECTORY_SEPARATOR) {
return $result = false;
}
return $result = (bool) @proc_open('echo 1 >/dev/null', [['pty'], ['pty'], ['pty']], $pipes);
}
private function getDescriptors(): array
{
if ($this->input instanceof \Iterator) {
$this->input->rewind();
}
if ('\\' === \DIRECTORY_SEPARATOR) {
$this->processPipes = new WindowsPipes($this->input, !$this->outputDisabled || $this->hasCallback);
} else {
$this->processPipes = new UnixPipes($this->isTty(), $this->isPty(), $this->input, !$this->outputDisabled || $this->hasCallback);
}
return $this->processPipes->getDescriptors();
}
protected function buildCallback(callable $callback = null)
{
if ($this->outputDisabled) {
return function ($type, $data) use ($callback): bool {
return null !== $callback && $callback($type, $data);
};
}
$out = self::OUT;
return function ($type, $data) use ($callback, $out): bool {
if ($out == $type) {
$this->addOutput($data);
} else {
$this->addErrorOutput($data);
}
return null !== $callback && $callback($type, $data);
};
}
protected function updateStatus(bool $blocking)
{
if (self::STATUS_STARTED !== $this->status) {
return;
}
$this->processInformation = proc_get_status($this->process);
$running = $this->processInformation['running'];
$this->readPipes($running && $blocking, '\\' !== \DIRECTORY_SEPARATOR || !$running);
if ($this->fallbackStatus && $this->isSigchildEnabled()) {
$this->processInformation = $this->fallbackStatus + $this->processInformation;
}
if (!$running) {
$this->close();
}
}
protected function isSigchildEnabled()
{
if (null !== self::$sigchild) {
return self::$sigchild;
}
if (!\function_exists('phpinfo')) {
return self::$sigchild = false;
}
ob_start();
phpinfo(\INFO_GENERAL);
return self::$sigchild = str_contains(ob_get_clean(), '--enable-sigchild');
}
private function readPipesForOutput(string $caller, bool $blocking = false)
{
if ($this->outputDisabled) {
throw new LogicException('Output has been disabled.');
}
$this->requireProcessIsStarted($caller);
$this->updateStatus($blocking);
}
private function validateTimeout(?float $timeout): ?float
{
$timeout = (float) $timeout;
if (0.0 === $timeout) {
$timeout = null;
} elseif ($timeout < 0) {
throw new InvalidArgumentException('The timeout value must be a valid positive integer or float number.');
}
return $timeout;
}
private function readPipes(bool $blocking, bool $close)
{
$result = $this->processPipes->readAndWrite($blocking, $close);
$callback = $this->callback;
foreach ($result as $type => $data) {
if (3 !== $type) {
$callback(self::STDOUT === $type ? self::OUT : self::ERR, $data);
} elseif (!isset($this->fallbackStatus['signaled'])) {
$this->fallbackStatus['exitcode'] = (int) $data;
}
}
}
private function close(): int
{
$this->processPipes->close();
if (\is_resource($this->process)) {
proc_close($this->process);
}
$this->exitcode = $this->processInformation['exitcode'];
$this->status = self::STATUS_TERMINATED;
if (-1 === $this->exitcode) {
if ($this->processInformation['signaled'] && 0 < $this->processInformation['termsig']) {
$this->exitcode = 128 + $this->processInformation['termsig'];
} elseif ($this->isSigchildEnabled()) {
$this->processInformation['signaled'] = true;
$this->processInformation['termsig'] = -1;
}
}
$this->callback = null;
return $this->exitcode;
}
private function resetProcessData()
{
$this->starttime = null;
$this->callback = null;
$this->exitcode = null;
$this->fallbackStatus = [];
$this->processInformation = null;
$this->stdout = fopen('php://temp/maxmemory:'.(1024 * 1024), 'w+');
$this->stderr = fopen('php://temp/maxmemory:'.(1024 * 1024), 'w+');
$this->process = null;
$this->latestSignal = null;
$this->status = self::STATUS_READY;
$this->incrementalOutputOffset = 0;
$this->incrementalErrorOutputOffset = 0;
}
private function doSignal(int $signal, bool $throwException): bool
{
if (null === $pid = $this->getPid()) {
if ($throwException) {
throw new LogicException('Cannot send signal on a non running process.');
}
return false;
}
if ('\\' === \DIRECTORY_SEPARATOR) {
exec(sprintf('taskkill /F /T /PID %d 2>&1', $pid), $output, $exitCode);
if ($exitCode && $this->isRunning()) {
if ($throwException) {
throw new RuntimeException(sprintf('Unable to kill the process (%s).', implode(' ', $output)));
}
return false;
}
} else {
if (!$this->isSigchildEnabled()) {
$ok = @proc_terminate($this->process, $signal);
} elseif (\function_exists('posix_kill')) {
$ok = @posix_kill($pid, $signal);
} elseif ($ok = proc_open(sprintf('kill -%d %d', $signal, $pid), [2 => ['pipe', 'w']], $pipes)) {
$ok = false === fgets($pipes[2]);
}
if (!$ok) {
if ($throwException) {
throw new RuntimeException(sprintf('Error while sending signal "%s".', $signal));
}
return false;
}
}
$this->latestSignal = $signal;
$this->fallbackStatus['signaled'] = true;
$this->fallbackStatus['exitcode'] = -1;
$this->fallbackStatus['termsig'] = $this->latestSignal;
return true;
}
private function prepareWindowsCommandLine(string $cmd, array &$env): string
{
$uid = uniqid('', true);
$varCount = 0;
$varCache = [];
$cmd = preg_replace_callback(
'/"(?:(
[^"%!^]*+
(?:
(?: !LF! | "(?:\^[%!^])?+" )
[^"%!^]*+
)++
) | [^"]*+ )"/x',
function ($m) use (&$env, &$varCache, &$varCount, $uid) {
if (!isset($m[1])) {
return $m[0];
}
if (isset($varCache[$m[0]])) {
return $varCache[$m[0]];
}
if (str_contains($value = $m[1], "\0")) {
$value = str_replace("\0", '?', $value);
}
if (false === strpbrk($value, "\"%!\n")) {
return '"'.$value.'"';
}
$value = str_replace(['!LF!', '"^!"', '"^%"', '"^^"', '""'], ["\n", '!', '%', '^', '"'], $value);
$value = '"'.preg_replace('/(\\\\*)"/', '$1$1\\"', $value).'"';
$var = $uid.++$varCount;
$env[$var] = $value;
return $varCache[$m[0]] = '!'.$var.'!';
},
$cmd
);
$cmd = 'cmd /V:ON /E:ON /D /C ('.str_replace("\n", ' ', $cmd).')';
foreach ($this->processPipes->getFiles() as $offset => $filename) {
$cmd .= ' '.$offset.'>"'.$filename.'"';
}
return $cmd;
}
private function requireProcessIsStarted(string $functionName)
{
if (!$this->isStarted()) {
throw new LogicException(sprintf('Process must be started before calling "%s()".', $functionName));
}
}
private function requireProcessIsTerminated(string $functionName)
{
if (!$this->isTerminated()) {
throw new LogicException(sprintf('Process must be terminated before calling "%s()".', $functionName));
}
}
private function escapeArgument(?string $argument): string
{
if ('' === $argument || null === $argument) {
return '""';
}
if ('\\' !== \DIRECTORY_SEPARATOR) {
return "'".str_replace("'", "'\\''", $argument)."'";
}
if (str_contains($argument, "\0")) {
$argument = str_replace("\0", '?', $argument);
}
if (!preg_match('/[\/()%!^"<>&|\s]/', $argument)) {
return $argument;
}
$argument = preg_replace('/(\\\\+)$/', '$1$1', $argument);
return '"'.str_replace(['"', '^', '%', '!', "\n"], ['""', '"^^"', '"^%"', '"^!"', '!LF!'], $argument).'"';
}
private function replacePlaceholders(string $commandline, array $env)
{
return preg_replace_callback('/"\$\{:([_a-zA-Z]++[_a-zA-Z0-9]*+)\}"/', function ($matches) use ($commandline, $env) {
if (!isset($env[$matches[1]]) || false === $env[$matches[1]]) {
throw new InvalidArgumentException(sprintf('Command line is missing a value for parameter "%s": ', $matches[1]).$commandline);
}
return $this->escapeArgument($env[$matches[1]]);
}, $commandline);
}
private function getDefaultEnv(): array
{
$env = getenv();
$env = ('\\' === \DIRECTORY_SEPARATOR ? array_intersect_ukey($env, $_SERVER, 'strcasecmp') : array_intersect_key($env, $_SERVER)) ?: $env;
return $_ENV + ('\\' === \DIRECTORY_SEPARATOR ? array_diff_ukey($env, $_ENV, 'strcasecmp') : $env);
}
}
<?php
namespace Symfony\Component\Console;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\Console\Event\ConsoleErrorEvent;
use Symfony\Component\Console\Event\ConsoleSignalEvent;
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
final class ConsoleEvents
{
/**
@Event("Symfony\Component\Console\Event\ConsoleCommandEvent")
*/
public const COMMAND = 'console.command';
/**
@Event("Symfony\Component\Console\Event\ConsoleSignalEvent")
*/
public const SIGNAL = 'console.signal';
/**
@Event("Symfony\Component\Console\Event\ConsoleTerminateEvent")
*/
public const TERMINATE = 'console.terminate';
/**
@Event("Symfony\Component\Console\Event\ConsoleErrorEvent")
*/
public const ERROR = 'console.error';
public const ALIASES = [
ConsoleCommandEvent::class => self::COMMAND,
ConsoleErrorEvent::class => self::ERROR,
ConsoleSignalEvent::class => self::SIGNAL,
ConsoleTerminateEvent::class => self::TERMINATE,
];
}
<?php
namespace Symfony\Component\Console;
use Symfony\Component\Console\Output\OutputInterface;
final class Cursor
{
private $output;
private $input;
public function __construct(OutputInterface $output, $input = null)
{
$this->output = $output;
$this->input = $input ?? (\defined('STDIN') ? \STDIN : fopen('php://input', 'r+'));
}
public function moveUp(int $lines = 1): self
{
$this->output->write(sprintf("\x1b[%dA", $lines));
return $this;
}
public function moveDown(int $lines = 1): self
{
$this->output->write(sprintf("\x1b[%dB", $lines));
return $this;
}
public function moveRight(int $columns = 1): self
{
$this->output->write(sprintf("\x1b[%dC", $columns));
return $this;
}
public function moveLeft(int $columns = 1): self
{
$this->output->write(sprintf("\x1b[%dD", $columns));
return $this;
}
public function moveToColumn(int $column): self
{
$this->output->write(sprintf("\x1b[%dG", $column));
return $this;
}
public function moveToPosition(int $column, int $row): self
{
$this->output->write(sprintf("\x1b[%d;%dH", $row + 1, $column));
return $this;
}
public function savePosition(): self
{
$this->output->write("\x1b7");
return $this;
}
public function restorePosition(): self
{
$this->output->write("\x1b8");
return $this;
}
public function hide(): self
{
$this->output->write("\x1b[?25l");
return $this;
}
public function show(): self
{
$this->output->write("\x1b[?25h\x1b[?0c");
return $this;
}
public function clearLine(): self
{
$this->output->write("\x1b[2K");
return $this;
}
public function clearLineAfter(): self
{
$this->output->write("\x1b[K");
return $this;
}
public function clearOutput(): self
{
$this->output->write("\x1b[0J");
return $this;
}
public function clearScreen(): self
{
$this->output->write("\x1b[2J");
return $this;
}
public function getCurrentPosition(): array
{
static $isTtySupported;
if (null === $isTtySupported && \function_exists('proc_open')) {
$isTtySupported = (bool) @proc_open('echo 1 >/dev/null', [['file', '/dev/tty', 'r'], ['file', '/dev/tty', 'w'], ['file', '/dev/tty', 'w']], $pipes);
}
if (!$isTtySupported) {
return [1, 1];
}
$sttyMode = shell_exec('stty -g');
shell_exec('stty -icanon -echo');
@fwrite($this->input, "\033[6n");
$code = trim(fread($this->input, 1024));
shell_exec(sprintf('stty %s', $sttyMode));
sscanf($code, "\033[%d;%dR", $row, $col);
return [$col, $row];
}
}
<?php
namespace Symfony\Component\Console;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Command\CompleteCommand;
use Symfony\Component\Console\Command\DumpCompletionCommand;
use Symfony\Component\Console\Command\HelpCommand;
use Symfony\Component\Console\Command\LazyCommand;
use Symfony\Component\Console\Command\ListCommand;
use Symfony\Component\Console\Command\SignalableCommandInterface;
use Symfony\Component\Console\CommandLoader\CommandLoaderInterface;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\Console\Event\ConsoleErrorEvent;
use Symfony\Component\Console\Event\ConsoleSignalEvent;
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
use Symfony\Component\Console\Exception\CommandNotFoundException;
use Symfony\Component\Console\Exception\ExceptionInterface;
use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Console\Exception\NamespaceNotFoundException;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Helper\DebugFormatterHelper;
use Symfony\Component\Console\Helper\FormatterHelper;
use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Helper\ProcessHelper;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputAwareInterface;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\SignalRegistry\SignalRegistry;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\ErrorHandler\ErrorHandler;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Symfony\Contracts\Service\ResetInterface;
class Application implements ResetInterface
{
private $commands = [];
private $wantHelps = false;
private $runningCommand;
private $name;
private $version;
private $commandLoader;
private $catchExceptions = true;
private $autoExit = true;
private $definition;
private $helperSet;
private $dispatcher;
private $terminal;
private $defaultCommand;
private $singleCommand = false;
private $initialized;
private $signalRegistry;
private $signalsToDispatchEvent = [];
public function __construct(string $name = 'UNKNOWN', string $version = 'UNKNOWN')
{
$this->name = $name;
$this->version = $version;
$this->terminal = new Terminal();
$this->defaultCommand = 'list';
if (\defined('SIGINT') && SignalRegistry::isSupported()) {
$this->signalRegistry = new SignalRegistry();
$this->signalsToDispatchEvent = [\SIGINT, \SIGTERM, \SIGUSR1, \SIGUSR2];
}
}
public function setDispatcher(EventDispatcherInterface $dispatcher)
{
$this->dispatcher = $dispatcher;
}
public function setCommandLoader(CommandLoaderInterface $commandLoader)
{
$this->commandLoader = $commandLoader;
}
public function getSignalRegistry(): SignalRegistry
{
if (!$this->signalRegistry) {
throw new RuntimeException('Signals are not supported. Make sure that the `pcntl` extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.');
}
return $this->signalRegistry;
}
public function setSignalsToDispatchEvent(int ...$signalsToDispatchEvent)
{
$this->signalsToDispatchEvent = $signalsToDispatchEvent;
}
public function run(InputInterface $input = null, OutputInterface $output = null)
{
if (\function_exists('putenv')) {
@putenv('LINES='.$this->terminal->getHeight());
@putenv('COLUMNS='.$this->terminal->getWidth());
}
if (null === $input) {
$input = new ArgvInput();
}
if (null === $output) {
$output = new ConsoleOutput();
}
$renderException = function (\Throwable $e) use ($output) {
if ($output instanceof ConsoleOutputInterface) {
$this->renderThrowable($e, $output->getErrorOutput());
} else {
$this->renderThrowable($e, $output);
}
};
if ($phpHandler = set_exception_handler($renderException)) {
restore_exception_handler();
if (!\is_array($phpHandler) || !$phpHandler[0] instanceof ErrorHandler) {
$errorHandler = true;
} elseif ($errorHandler = $phpHandler[0]->setExceptionHandler($renderException)) {
$phpHandler[0]->setExceptionHandler($errorHandler);
}
}
$this->configureIO($input, $output);
try {
$exitCode = $this->doRun($input, $output);
} catch (\Exception $e) {
if (!$this->catchExceptions) {
throw $e;
}
$renderException($e);
$exitCode = $e->getCode();
if (is_numeric($exitCode)) {
$exitCode = (int) $exitCode;
if ($exitCode <= 0) {
$exitCode = 1;
}
} else {
$exitCode = 1;
}
} finally {
if (!$phpHandler) {
if (set_exception_handler($renderException) === $renderException) {
restore_exception_handler();
}
restore_exception_handler();
} elseif (!$errorHandler) {
$finalHandler = $phpHandler[0]->setExceptionHandler(null);
if ($finalHandler !== $renderException) {
$phpHandler[0]->setExceptionHandler($finalHandler);
}
}
}
if ($this->autoExit) {
if ($exitCode > 255) {
$exitCode = 255;
}
exit($exitCode);
}
return $exitCode;
}
public function doRun(InputInterface $input, OutputInterface $output)
{
if (true === $input->hasParameterOption(['--version', '-V'], true)) {
$output->writeln($this->getLongVersion());
return 0;
}
try {
$input->bind($this->getDefinition());
} catch (ExceptionInterface $e) {
}
$name = $this->getCommandName($input);
if (true === $input->hasParameterOption(['--help', '-h'], true)) {
if (!$name) {
$name = 'help';
$input = new ArrayInput(['command_name' => $this->defaultCommand]);
} else {
$this->wantHelps = true;
}
}
if (!$name) {
$name = $this->defaultCommand;
$definition = $this->getDefinition();
$definition->setArguments(array_merge(
$definition->getArguments(),
[
'command' => new InputArgument('command', InputArgument::OPTIONAL, $definition->getArgument('command')->getDescription(), $name),
]
));
}
try {
$this->runningCommand = null;
$command = $this->find($name);
} catch (\Throwable $e) {
if (!($e instanceof CommandNotFoundException && !$e instanceof NamespaceNotFoundException) || 1 !== \count($alternatives = $e->getAlternatives()) || !$input->isInteractive()) {
if (null !== $this->dispatcher) {
$event = new ConsoleErrorEvent($input, $output, $e);
$this->dispatcher->dispatch($event, ConsoleEvents::ERROR);
if (0 === $event->getExitCode()) {
return 0;
}
$e = $event->getError();
}
throw $e;
}
$alternative = $alternatives[0];
$style = new SymfonyStyle($input, $output);
$output->writeln('');
$formattedBlock = (new FormatterHelper())->formatBlock(sprintf('Command "%s" is not defined.', $name), 'error', true);
$output->writeln($formattedBlock);
if (!$style->confirm(sprintf('Do you want to run "%s" instead? ', $alternative), false)) {
if (null !== $this->dispatcher) {
$event = new ConsoleErrorEvent($input, $output, $e);
$this->dispatcher->dispatch($event, ConsoleEvents::ERROR);
return $event->getExitCode();
}
return 1;
}
$command = $this->find($alternative);
}
if ($command instanceof LazyCommand) {
$command = $command->getCommand();
}
$this->runningCommand = $command;
$exitCode = $this->doRunCommand($command, $input, $output);
$this->runningCommand = null;
return $exitCode;
}
public function reset()
{
}
public function setHelperSet(HelperSet $helperSet)
{
$this->helperSet = $helperSet;
}
public function getHelperSet()
{
if (!$this->helperSet) {
$this->helperSet = $this->getDefaultHelperSet();
}
return $this->helperSet;
}
public function setDefinition(InputDefinition $definition)
{
$this->definition = $definition;
}
public function getDefinition()
{
if (!$this->definition) {
$this->definition = $this->getDefaultInputDefinition();
}
if ($this->singleCommand) {
$inputDefinition = $this->definition;
$inputDefinition->setArguments();
return $inputDefinition;
}
return $this->definition;
}
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
if (
CompletionInput::TYPE_ARGUMENT_VALUE === $input->getCompletionType()
&& 'command' === $input->getCompletionName()
) {
$commandNames = [];
foreach ($this->all() as $name => $command) {
if ($command->isHidden() || $command->getName() !== $name) {
continue;
}
$commandNames[] = $command->getName();
foreach ($command->getAliases() as $name) {
$commandNames[] = $name;
}
}
$suggestions->suggestValues(array_filter($commandNames));
return;
}
if (CompletionInput::TYPE_OPTION_NAME === $input->getCompletionType()) {
$suggestions->suggestOptions($this->getDefinition()->getOptions());
return;
}
}
public function getHelp()
{
return $this->getLongVersion();
}
public function areExceptionsCaught()
{
return $this->catchExceptions;
}
public function setCatchExceptions(bool $boolean)
{
$this->catchExceptions = $boolean;
}
public function isAutoExitEnabled()
{
return $this->autoExit;
}
public function setAutoExit(bool $boolean)
{
$this->autoExit = $boolean;
}
public function getName()
{
return $this->name;
}
public function setName(string $name)
{
$this->name = $name;
}
public function getVersion()
{
return $this->version;
}
public function setVersion(string $version)
{
$this->version = $version;
}
public function getLongVersion()
{
if ('UNKNOWN' !== $this->getName()) {
if ('UNKNOWN' !== $this->getVersion()) {
return sprintf('%s <info>%s</info>', $this->getName(), $this->getVersion());
}
return $this->getName();
}
return 'Console Tool';
}
public function register(string $name)
{
return $this->add(new Command($name));
}
public function addCommands(array $commands)
{
foreach ($commands as $command) {
$this->add($command);
}
}
public function add(Command $command)
{
$this->init();
$command->setApplication($this);
if (!$command->isEnabled()) {
$command->setApplication(null);
return null;
}
if (!$command instanceof LazyCommand) {
$command->getDefinition();
}
if (!$command->getName()) {
throw new LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_debug_type($command)));
}
$this->commands[$command->getName()] = $command;
foreach ($command->getAliases() as $alias) {
$this->commands[$alias] = $command;
}
return $command;
}
public function get(string $name)
{
$this->init();
if (!$this->has($name)) {
throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name));
}
if (!isset($this->commands[$name])) {
throw new CommandNotFoundException(sprintf('The "%s" command cannot be found because it is registered under multiple names. Make sure you don\'t set a different name via constructor or "setName()".', $name));
}
$command = $this->commands[$name];
if ($this->wantHelps) {
$this->wantHelps = false;
$helpCommand = $this->get('help');
$helpCommand->setCommand($command);
return $helpCommand;
}
return $command;
}
public function has(string $name)
{
$this->init();
return isset($this->commands[$name]) || ($this->commandLoader && $this->commandLoader->has($name) && $this->add($this->commandLoader->get($name)));
}
public function getNamespaces()
{
$namespaces = [];
foreach ($this->all() as $command) {
if ($command->isHidden()) {
continue;
}
$namespaces[] = $this->extractAllNamespaces($command->getName());
foreach ($command->getAliases() as $alias) {
$namespaces[] = $this->extractAllNamespaces($alias);
}
}
return array_values(array_unique(array_filter(array_merge([], ...$namespaces))));
}
public function findNamespace(string $namespace)
{
$allNamespaces = $this->getNamespaces();
$expr = implode('[^:]*:', array_map('preg_quote', explode(':', $namespace))).'[^:]*';
$namespaces = preg_grep('{^'.$expr.'}', $allNamespaces);
if (empty($namespaces)) {
$message = sprintf('There are no commands defined in the "%s" namespace.', $namespace);
if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) {
if (1 == \count($alternatives)) {
$message .= "\n\nDid you mean this?\n ";
} else {
$message .= "\n\nDid you mean one of these?\n ";
}
$message .= implode("\n ", $alternatives);
}
throw new NamespaceNotFoundException($message, $alternatives);
}
$exact = \in_array($namespace, $namespaces, true);
if (\count($namespaces) > 1 && !$exact) {
throw new NamespaceNotFoundException(sprintf("The namespace \"%s\" is ambiguous.\nDid you mean one of these?\n%s.", $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))), array_values($namespaces));
}
return $exact ? $namespace : reset($namespaces);
}
public function find(string $name)
{
$this->init();
$aliases = [];
foreach ($this->commands as $command) {
foreach ($command->getAliases() as $alias) {
if (!$this->has($alias)) {
$this->commands[$alias] = $command;
}
}
}
if ($this->has($name)) {
return $this->get($name);
}
$allCommands = $this->commandLoader ? array_merge($this->commandLoader->getNames(), array_keys($this->commands)) : array_keys($this->commands);
$expr = implode('[^:]*:', array_map('preg_quote', explode(':', $name))).'[^:]*';
$commands = preg_grep('{^'.$expr.'}', $allCommands);
if (empty($commands)) {
$commands = preg_grep('{^'.$expr.'}i', $allCommands);
}
if (empty($commands) || \count(preg_grep('{^'.$expr.'$}i', $commands)) < 1) {
if (false !== $pos = strrpos($name, ':')) {
$this->findNamespace(substr($name, 0, $pos));
}
$message = sprintf('Command "%s" is not defined.', $name);
if ($alternatives = $this->findAlternatives($name, $allCommands)) {
$alternatives = array_filter($alternatives, function ($name) {
return !$this->get($name)->isHidden();
});
if (1 == \count($alternatives)) {
$message .= "\n\nDid you mean this?\n ";
} else {
$message .= "\n\nDid you mean one of these?\n ";
}
$message .= implode("\n ", $alternatives);
}
throw new CommandNotFoundException($message, array_values($alternatives));
}
if (\count($commands) > 1) {
$commandList = $this->commandLoader ? array_merge(array_flip($this->commandLoader->getNames()), $this->commands) : $this->commands;
$commands = array_unique(array_filter($commands, function ($nameOrAlias) use (&$commandList, $commands, &$aliases) {
if (!$commandList[$nameOrAlias] instanceof Command) {
$commandList[$nameOrAlias] = $this->commandLoader->get($nameOrAlias);
}
$commandName = $commandList[$nameOrAlias]->getName();
$aliases[$nameOrAlias] = $commandName;
return $commandName === $nameOrAlias || !\in_array($commandName, $commands);
}));
}
if (\count($commands) > 1) {
$usableWidth = $this->terminal->getWidth() - 10;
$abbrevs = array_values($commands);
$maxLen = 0;
foreach ($abbrevs as $abbrev) {
$maxLen = max(Helper::width($abbrev), $maxLen);
}
$abbrevs = array_map(function ($cmd) use ($commandList, $usableWidth, $maxLen, &$commands) {
if ($commandList[$cmd]->isHidden()) {
unset($commands[array_search($cmd, $commands)]);
return false;
}
$abbrev = str_pad($cmd, $maxLen, ' ').' '.$commandList[$cmd]->getDescription();
return Helper::width($abbrev) > $usableWidth ? Helper::substr($abbrev, 0, $usableWidth - 3).'...' : $abbrev;
}, array_values($commands));
if (\count($commands) > 1) {
$suggestions = $this->getAbbreviationSuggestions(array_filter($abbrevs));
throw new CommandNotFoundException(sprintf("Command \"%s\" is ambiguous.\nDid you mean one of these?\n%s.", $name, $suggestions), array_values($commands));
}
}
$command = $this->get(reset($commands));
if ($command->isHidden()) {
throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name));
}
return $command;
}
public function all(string $namespace = null)
{
$this->init();
if (null === $namespace) {
if (!$this->commandLoader) {
return $this->commands;
}
$commands = $this->commands;
foreach ($this->commandLoader->getNames() as $name) {
if (!isset($commands[$name]) && $this->has($name)) {
$commands[$name] = $this->get($name);
}
}
return $commands;
}
$commands = [];
foreach ($this->commands as $name => $command) {
if ($namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) {
$commands[$name] = $command;
}
}
if ($this->commandLoader) {
foreach ($this->commandLoader->getNames() as $name) {
if (!isset($commands[$name]) && $namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1) && $this->has($name)) {
$commands[$name] = $this->get($name);
}
}
}
return $commands;
}
public static function getAbbreviations(array $names)
{
$abbrevs = [];
foreach ($names as $name) {
for ($len = \strlen($name); $len > 0; --$len) {
$abbrev = substr($name, 0, $len);
$abbrevs[$abbrev][] = $name;
}
}
return $abbrevs;
}
public function renderThrowable(\Throwable $e, OutputInterface $output): void
{
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
$this->doRenderThrowable($e, $output);
if (null !== $this->runningCommand) {
$output->writeln(sprintf('<info>%s</info>', OutputFormatter::escape(sprintf($this->runningCommand->getSynopsis(), $this->getName()))), OutputInterface::VERBOSITY_QUIET);
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
}
}
protected function doRenderThrowable(\Throwable $e, OutputInterface $output): void
{
do {
$message = trim($e->getMessage());
if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
$class = get_debug_type($e);
$title = sprintf(' [%s%s] ', $class, 0 !== ($code = $e->getCode()) ? ' ('.$code.')' : '');
$len = Helper::width($title);
} else {
$len = 0;
}
if (str_contains($message, "@anonymous\0")) {
$message = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', function ($m) {
return class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0];
}, $message);
}
$width = $this->terminal->getWidth() ? $this->terminal->getWidth() - 1 : \PHP_INT_MAX;
$lines = [];
foreach ('' !== $message ? preg_split('/\r?\n/', $message) : [] as $line) {
foreach ($this->splitStringByWidth($line, $width - 4) as $line) {
$lineLength = Helper::width($line) + 4;
$lines[] = [$line, $lineLength];
$len = max($lineLength, $len);
}
}
$messages = [];
if (!$e instanceof ExceptionInterface || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
$messages[] = sprintf('<comment>%s</comment>', OutputFormatter::escape(sprintf('In %s line %s:', basename($e->getFile()) ?: 'n/a', $e->getLine() ?: 'n/a')));
}
$messages[] = $emptyLine = sprintf('<error>%s</error>', str_repeat(' ', $len));
if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
$messages[] = sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - Helper::width($title))));
}
foreach ($lines as $line) {
$messages[] = sprintf('<error> %s %s</error>', OutputFormatter::escape($line[0]), str_repeat(' ', $len - $line[1]));
}
$messages[] = $emptyLine;
$messages[] = '';
$output->writeln($messages, OutputInterface::VERBOSITY_QUIET);
if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
$output->writeln('<comment>Exception trace:</comment>', OutputInterface::VERBOSITY_QUIET);
$trace = $e->getTrace();
array_unshift($trace, [
'function' => '',
'file' => $e->getFile() ?: 'n/a',
'line' => $e->getLine() ?: 'n/a',
'args' => [],
]);
for ($i = 0, $count = \count($trace); $i < $count; ++$i) {
$class = $trace[$i]['class'] ?? '';
$type = $trace[$i]['type'] ?? '';
$function = $trace[$i]['function'] ?? '';
$file = $trace[$i]['file'] ?? 'n/a';
$line = $trace[$i]['line'] ?? 'n/a';
$output->writeln(sprintf(' %s%s at <info>%s:%s</info>', $class, $function ? $type.$function.'()' : '', $file, $line), OutputInterface::VERBOSITY_QUIET);
}
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
}
} while ($e = $e->getPrevious());
}
protected function configureIO(InputInterface $input, OutputInterface $output)
{
if (true === $input->hasParameterOption(['--ansi'], true)) {
$output->setDecorated(true);
} elseif (true === $input->hasParameterOption(['--no-ansi'], true)) {
$output->setDecorated(false);
}
if (true === $input->hasParameterOption(['--no-interaction', '-n'], true)) {
$input->setInteractive(false);
}
switch ($shellVerbosity = (int) getenv('SHELL_VERBOSITY')) {
case -1:
$output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
break;
case 1:
$output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
break;
case 2:
$output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE);
break;
case 3:
$output->setVerbosity(OutputInterface::VERBOSITY_DEBUG);
break;
default:
$shellVerbosity = 0;
break;
}
if (true === $input->hasParameterOption(['--quiet', '-q'], true)) {
$output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
$shellVerbosity = -1;
} else {
if ($input->hasParameterOption('-vvv', true) || $input->hasParameterOption('--verbose=3', true) || 3 === $input->getParameterOption('--verbose', false, true)) {
$output->setVerbosity(OutputInterface::VERBOSITY_DEBUG);
$shellVerbosity = 3;
} elseif ($input->hasParameterOption('-vv', true) || $input->hasParameterOption('--verbose=2', true) || 2 === $input->getParameterOption('--verbose', false, true)) {
$output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE);
$shellVerbosity = 2;
} elseif ($input->hasParameterOption('-v', true) || $input->hasParameterOption('--verbose=1', true) || $input->hasParameterOption('--verbose', true) || $input->getParameterOption('--verbose', false, true)) {
$output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
$shellVerbosity = 1;
}
}
if (-1 === $shellVerbosity) {
$input->setInteractive(false);
}
if (\function_exists('putenv')) {
@putenv('SHELL_VERBOSITY='.$shellVerbosity);
}
$_ENV['SHELL_VERBOSITY'] = $shellVerbosity;
$_SERVER['SHELL_VERBOSITY'] = $shellVerbosity;
}
protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
{
foreach ($command->getHelperSet() as $helper) {
if ($helper instanceof InputAwareInterface) {
$helper->setInput($input);
}
}
if ($this->signalsToDispatchEvent) {
$commandSignals = $command instanceof SignalableCommandInterface ? $command->getSubscribedSignals() : [];
if ($commandSignals || null !== $this->dispatcher) {
if (!$this->signalRegistry) {
throw new RuntimeException('Unable to subscribe to signal events. Make sure that the `pcntl` extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.');
}
if (Terminal::hasSttyAvailable()) {
$sttyMode = shell_exec('stty -g');
foreach ([\SIGINT, \SIGTERM] as $signal) {
$this->signalRegistry->register($signal, static function () use ($sttyMode) {
shell_exec('stty '.$sttyMode);
});
}
}
foreach ($commandSignals as $signal) {
$this->signalRegistry->register($signal, [$command, 'handleSignal']);
}
}
if (null !== $this->dispatcher) {
foreach ($this->signalsToDispatchEvent as $signal) {
$event = new ConsoleSignalEvent($command, $input, $output, $signal);
$this->signalRegistry->register($signal, function ($signal, $hasNext) use ($event) {
$this->dispatcher->dispatch($event, ConsoleEvents::SIGNAL);
if (!$hasNext) {
if (!\in_array($signal, [\SIGUSR1, \SIGUSR2], true)) {
exit(0);
}
}
});
}
}
}
if (null === $this->dispatcher) {
return $command->run($input, $output);
}
try {
$command->mergeApplicationDefinition();
$input->bind($command->getDefinition());
} catch (ExceptionInterface $e) {
}
$event = new ConsoleCommandEvent($command, $input, $output);
$e = null;
try {
$this->dispatcher->dispatch($event, ConsoleEvents::COMMAND);
if ($event->commandShouldRun()) {
$exitCode = $command->run($input, $output);
} else {
$exitCode = ConsoleCommandEvent::RETURN_CODE_DISABLED;
}
} catch (\Throwable $e) {
$event = new ConsoleErrorEvent($input, $output, $e, $command);
$this->dispatcher->dispatch($event, ConsoleEvents::ERROR);
$e = $event->getError();
if (0 === $exitCode = $event->getExitCode()) {
$e = null;
}
}
$event = new ConsoleTerminateEvent($command, $input, $output, $exitCode);
$this->dispatcher->dispatch($event, ConsoleEvents::TERMINATE);
if (null !== $e) {
throw $e;
}
return $event->getExitCode();
}
protected function getCommandName(InputInterface $input)
{
return $this->singleCommand ? $this->defaultCommand : $input->getFirstArgument();
}
protected function getDefaultInputDefinition()
{
return new InputDefinition([
new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display help for the given command. When no command is given display help for the <info>'.$this->defaultCommand.'</info> command'),
new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'),
new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'),
new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version'),
new InputOption('--ansi', '', InputOption::VALUE_NEGATABLE, 'Force (or disable --no-ansi) ANSI output', null),
new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'),
]);
}
protected function getDefaultCommands()
{
return [new HelpCommand(), new ListCommand(), new CompleteCommand(), new DumpCompletionCommand()];
}
protected function getDefaultHelperSet()
{
return new HelperSet([
new FormatterHelper(),
new DebugFormatterHelper(),
new ProcessHelper(),
new QuestionHelper(),
]);
}
private function getAbbreviationSuggestions(array $abbrevs): string
{
return ' '.implode("\n ", $abbrevs);
}
public function extractNamespace(string $name, int $limit = null)
{
$parts = explode(':', $name, -1);
return implode(':', null === $limit ? $parts : \array_slice($parts, 0, $limit));
}
private function findAlternatives(string $name, iterable $collection): array
{
$threshold = 1e3;
$alternatives = [];
$collectionParts = [];
foreach ($collection as $item) {
$collectionParts[$item] = explode(':', $item);
}
foreach (explode(':', $name) as $i => $subname) {
foreach ($collectionParts as $collectionName => $parts) {
$exists = isset($alternatives[$collectionName]);
if (!isset($parts[$i]) && $exists) {
$alternatives[$collectionName] += $threshold;
continue;
} elseif (!isset($parts[$i])) {
continue;
}
$lev = levenshtein($subname, $parts[$i]);
if ($lev <= \strlen($subname) / 3 || '' !== $subname && str_contains($parts[$i], $subname)) {
$alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev;
} elseif ($exists) {
$alternatives[$collectionName] += $threshold;
}
}
}
foreach ($collection as $item) {
$lev = levenshtein($name, $item);
if ($lev <= \strlen($name) / 3 || str_contains($item, $name)) {
$alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev;
}
}
$alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2 * $threshold; });
ksort($alternatives, \SORT_NATURAL | \SORT_FLAG_CASE);
return array_keys($alternatives);
}
public function setDefaultCommand(string $commandName, bool $isSingleCommand = false)
{
$this->defaultCommand = explode('|', ltrim($commandName, '|'))[0];
if ($isSingleCommand) {
$this->find($commandName);
$this->singleCommand = true;
}
return $this;
}
public function isSingleCommand(): bool
{
return $this->singleCommand;
}
private function splitStringByWidth(string $string, int $width): array
{
if (false === $encoding = mb_detect_encoding($string, null, true)) {
return str_split($string, $width);
}
$utf8String = mb_convert_encoding($string, 'utf8', $encoding);
$lines = [];
$line = '';
$offset = 0;
while (preg_match('/.{1,10000}/u', $utf8String, $m, 0, $offset)) {
$offset += \strlen($m[0]);
foreach (preg_split('//u', $m[0]) as $char) {
if (mb_strwidth($line.$char, 'utf8') <= $width) {
$line .= $char;
continue;
}
$lines[] = str_pad($line, $width);
$line = $char;
}
}
$lines[] = \count($lines) ? str_pad($line, $width) : $line;
mb_convert_variables($encoding, 'utf8', $lines);
return $lines;
}
private function extractAllNamespaces(string $name): array
{
$parts = explode(':', $name, -1);
$namespaces = [];
foreach ($parts as $part) {
if (\count($namespaces)) {
$namespaces[] = end($namespaces).':'.$part;
} else {
$namespaces[] = $part;
}
}
return $namespaces;
}
private function init()
{
if ($this->initialized) {
return;
}
$this->initialized = true;
foreach ($this->getDefaultCommands() as $command) {
$this->add($command);
}
}
}
<?php
namespace Symfony\Component\Console\Logger;
use Psr\Log\AbstractLogger;
use Psr\Log\InvalidArgumentException;
use Psr\Log\LogLevel;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class ConsoleLogger extends AbstractLogger
{
public const INFO = 'info';
public const ERROR = 'error';
private $output;
private $verbosityLevelMap = [
LogLevel::EMERGENCY => OutputInterface::VERBOSITY_NORMAL,
LogLevel::ALERT => OutputInterface::VERBOSITY_NORMAL,
LogLevel::CRITICAL => OutputInterface::VERBOSITY_NORMAL,
LogLevel::ERROR => OutputInterface::VERBOSITY_NORMAL,
LogLevel::WARNING => OutputInterface::VERBOSITY_NORMAL,
LogLevel::NOTICE => OutputInterface::VERBOSITY_VERBOSE,
LogLevel::INFO => OutputInterface::VERBOSITY_VERY_VERBOSE,
LogLevel::DEBUG => OutputInterface::VERBOSITY_DEBUG,
];
private $formatLevelMap = [
LogLevel::EMERGENCY => self::ERROR,
LogLevel::ALERT => self::ERROR,
LogLevel::CRITICAL => self::ERROR,
LogLevel::ERROR => self::ERROR,
LogLevel::WARNING => self::INFO,
LogLevel::NOTICE => self::INFO,
LogLevel::INFO => self::INFO,
LogLevel::DEBUG => self::INFO,
];
private $errored = false;
public function __construct(OutputInterface $output, array $verbosityLevelMap = [], array $formatLevelMap = [])
{
$this->output = $output;
$this->verbosityLevelMap = $verbosityLevelMap + $this->verbosityLevelMap;
$this->formatLevelMap = $formatLevelMap + $this->formatLevelMap;
}
public function log($level, $message, array $context = [])
{
if (!isset($this->verbosityLevelMap[$level])) {
throw new InvalidArgumentException(sprintf('The log level "%s" does not exist.', $level));
}
$output = $this->output;
if (self::ERROR === $this->formatLevelMap[$level]) {
if ($this->output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
}
$this->errored = true;
}
if ($output->getVerbosity() >= $this->verbosityLevelMap[$level]) {
$output->writeln(sprintf('<%1$s>[%2$s] %3$s</%1$s>', $this->formatLevelMap[$level], $level, $this->interpolate($message, $context)), $this->verbosityLevelMap[$level]);
}
}
public function hasErrored()
{
return $this->errored;
}
private function interpolate(string $message, array $context): string
{
if (!str_contains($message, '{')) {
return $message;
}
$replacements = [];
foreach ($context as $key => $val) {
if (null === $val || \is_scalar($val) || (\is_object($val) && method_exists($val, '__toString'))) {
$replacements["{{$key}}"] = $val;
} elseif ($val instanceof \DateTimeInterface) {
$replacements["{{$key}}"] = $val->format(\DateTime::RFC3339);
} elseif (\is_object($val)) {
$replacements["{{$key}}"] = '[object '.\get_class($val).']';
} else {
$replacements["{{$key}}"] = '['.\gettype($val).']';
}
}
return strtr($message, $replacements);
}
}
<?php
namespace Symfony\Component\Console\Attribute;
#[\Attribute(\Attribute::TARGET_CLASS)]
class AsCommand
{
public function __construct(
public string $name,
public ?string $description = null,
array $aliases = [],
bool $hidden = false,
) {
if (!$hidden && !$aliases) {
return;
}
$name = explode('|', $name);
$name = array_merge($name, $aliases);
if ($hidden && '' !== $name[0]) {
array_unshift($name, '');
}
$this->name = implode('|', $name);
}
}
<?php
namespace Symfony\Component\Console\Input;
interface StreamableInputInterface extends InputInterface
{
public function setStream($stream);
public function getStream();
}
<?php
namespace Symfony\Component\Console\Input;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\InvalidOptionException;
class ArrayInput extends Input
{
private $parameters;
public function __construct(array $parameters, InputDefinition $definition = null)
{
$this->parameters = $parameters;
parent::__construct($definition);
}
public function getFirstArgument()
{
foreach ($this->parameters as $param => $value) {
if ($param && \is_string($param) && '-' === $param[0]) {
continue;
}
return $value;
}
return null;
}
public function hasParameterOption($values, bool $onlyParams = false)
{
$values = (array) $values;
foreach ($this->parameters as $k => $v) {
if (!\is_int($k)) {
$v = $k;
}
if ($onlyParams && '--' === $v) {
return false;
}
if (\in_array($v, $values)) {
return true;
}
}
return false;
}
public function getParameterOption($values, $default = false, bool $onlyParams = false)
{
$values = (array) $values;
foreach ($this->parameters as $k => $v) {
if ($onlyParams && ('--' === $k || (\is_int($k) && '--' === $v))) {
return $default;
}
if (\is_int($k)) {
if (\in_array($v, $values)) {
return true;
}
} elseif (\in_array($k, $values)) {
return $v;
}
}
return $default;
}
public function __toString()
{
$params = [];
foreach ($this->parameters as $param => $val) {
if ($param && \is_string($param) && '-' === $param[0]) {
$glue = ('-' === $param[1]) ? '=' : ' ';
if (\is_array($val)) {
foreach ($val as $v) {
$params[] = $param.('' != $v ? $glue.$this->escapeToken($v) : '');
}
} else {
$params[] = $param.('' != $val ? $glue.$this->escapeToken($val) : '');
}
} else {
$params[] = \is_array($val) ? implode(' ', array_map([$this, 'escapeToken'], $val)) : $this->escapeToken($val);
}
}
return implode(' ', $params);
}
protected function parse()
{
foreach ($this->parameters as $key => $value) {
if ('--' === $key) {
return;
}
if (str_starts_with($key, '--')) {
$this->addLongOption(substr($key, 2), $value);
} elseif (str_starts_with($key, '-')) {
$this->addShortOption(substr($key, 1), $value);
} else {
$this->addArgument($key, $value);
}
}
}
private function addShortOption(string $shortcut, $value)
{
if (!$this->definition->hasShortcut($shortcut)) {
throw new InvalidOptionException(sprintf('The "-%s" option does not exist.', $shortcut));
}
$this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
}
private function addLongOption(string $name, $value)
{
if (!$this->definition->hasOption($name)) {
if (!$this->definition->hasNegation($name)) {
throw new InvalidOptionException(sprintf('The "--%s" option does not exist.', $name));
}
$optionName = $this->definition->negationToName($name);
$this->options[$optionName] = false;
return;
}
$option = $this->definition->getOption($name);
if (null === $value) {
if ($option->isValueRequired()) {
throw new InvalidOptionException(sprintf('The "--%s" option requires a value.', $name));
}
if (!$option->isValueOptional()) {
$value = true;
}
}
$this->options[$name] = $value;
}
private function addArgument($name, $value)
{
if (!$this->definition->hasArgument($name)) {
throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
}
$this->arguments[$name] = $value;
}
}
<?php
namespace Symfony\Component\Console\Input;
use Symfony\Component\Console\Exception\RuntimeException;
class ArgvInput extends Input
{
private $tokens;
private $parsed;
public function __construct(array $argv = null, InputDefinition $definition = null)
{
$argv = $argv ?? $_SERVER['argv'] ?? [];
array_shift($argv);
$this->tokens = $argv;
parent::__construct($definition);
}
protected function setTokens(array $tokens)
{
$this->tokens = $tokens;
}
protected function parse()
{
$parseOptions = true;
$this->parsed = $this->tokens;
while (null !== $token = array_shift($this->parsed)) {
$parseOptions = $this->parseToken($token, $parseOptions);
}
}
protected function parseToken(string $token, bool $parseOptions): bool
{
if ($parseOptions && '' == $token) {
$this->parseArgument($token);
} elseif ($parseOptions && '--' == $token) {
return false;
} elseif ($parseOptions && str_starts_with($token, '--')) {
$this->parseLongOption($token);
} elseif ($parseOptions && '-' === $token[0] && '-' !== $token) {
$this->parseShortOption($token);
} else {
$this->parseArgument($token);
}
return $parseOptions;
}
private function parseShortOption(string $token)
{
$name = substr($token, 1);
if (\strlen($name) > 1) {
if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) {
$this->addShortOption($name[0], substr($name, 1));
} else {
$this->parseShortOptionSet($name);
}
} else {
$this->addShortOption($name, null);
}
}
private function parseShortOptionSet(string $name)
{
$len = \strlen($name);
for ($i = 0; $i < $len; ++$i) {
if (!$this->definition->hasShortcut($name[$i])) {
$encoding = mb_detect_encoding($name, null, true);
throw new RuntimeException(sprintf('The "-%s" option does not exist.', false === $encoding ? $name[$i] : mb_substr($name, $i, 1, $encoding)));
}
$option = $this->definition->getOptionForShortcut($name[$i]);
if ($option->acceptValue()) {
$this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1));
break;
} else {
$this->addLongOption($option->getName(), null);
}
}
}
private function parseLongOption(string $token)
{
$name = substr($token, 2);
if (false !== $pos = strpos($name, '=')) {
if ('' === $value = substr($name, $pos + 1)) {
array_unshift($this->parsed, $value);
}
$this->addLongOption(substr($name, 0, $pos), $value);
} else {
$this->addLongOption($name, null);
}
}
private function parseArgument(string $token)
{
$c = \count($this->arguments);
if ($this->definition->hasArgument($c)) {
$arg = $this->definition->getArgument($c);
$this->arguments[$arg->getName()] = $arg->isArray() ? [$token] : $token;
} elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) {
$arg = $this->definition->getArgument($c - 1);
$this->arguments[$arg->getName()][] = $token;
} else {
$all = $this->definition->getArguments();
$symfonyCommandName = null;
if (($inputArgument = $all[$key = array_key_first($all)] ?? null) && 'command' === $inputArgument->getName()) {
$symfonyCommandName = $this->arguments['command'] ?? null;
unset($all[$key]);
}
if (\count($all)) {
if ($symfonyCommandName) {
$message = sprintf('Too many arguments to "%s" command, expected arguments "%s".', $symfonyCommandName, implode('" "', array_keys($all)));
} else {
$message = sprintf('Too many arguments, expected arguments "%s".', implode('" "', array_keys($all)));
}
} elseif ($symfonyCommandName) {
$message = sprintf('No arguments expected for "%s" command, got "%s".', $symfonyCommandName, $token);
} else {
$message = sprintf('No arguments expected, got "%s".', $token);
}
throw new RuntimeException($message);
}
}
private function addShortOption(string $shortcut, $value)
{
if (!$this->definition->hasShortcut($shortcut)) {
throw new RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut));
}
$this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
}
private function addLongOption(string $name, $value)
{
if (!$this->definition->hasOption($name)) {
if (!$this->definition->hasNegation($name)) {
throw new RuntimeException(sprintf('The "--%s" option does not exist.', $name));
}
$optionName = $this->definition->negationToName($name);
if (null !== $value) {
throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name));
}
$this->options[$optionName] = false;
return;
}
$option = $this->definition->getOption($name);
if (null !== $value && !$option->acceptValue()) {
throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name));
}
if (\in_array($value, ['', null], true) && $option->acceptValue() && \count($this->parsed)) {
$next = array_shift($this->parsed);
if ((isset($next[0]) && '-' !== $next[0]) || \in_array($next, ['', null], true)) {
$value = $next;
} else {
array_unshift($this->parsed, $next);
}
}
if (null === $value) {
if ($option->isValueRequired()) {
throw new RuntimeException(sprintf('The "--%s" option requires a value.', $name));
}
if (!$option->isArray() && !$option->isValueOptional()) {
$value = true;
}
}
if ($option->isArray()) {
$this->options[$name][] = $value;
} else {
$this->options[$name] = $value;
}
}
public function getFirstArgument()
{
$isOption = false;
foreach ($this->tokens as $i => $token) {
if ($token && '-' === $token[0]) {
if (str_contains($token, '=') || !isset($this->tokens[$i + 1])) {
continue;
}
$name = '-' === $token[1] ? substr($token, 2) : substr($token, -1);
if (!isset($this->options[$name]) && !$this->definition->hasShortcut($name)) {
} elseif ((isset($this->options[$name]) || isset($this->options[$name = $this->definition->shortcutToName($name)])) && $this->tokens[$i + 1] === $this->options[$name]) {
$isOption = true;
}
continue;
}
if ($isOption) {
$isOption = false;
continue;
}
return $token;
}
return null;
}
public function hasParameterOption($values, bool $onlyParams = false)
{
$values = (array) $values;
foreach ($this->tokens as $token) {
if ($onlyParams && '--' === $token) {
return false;
}
foreach ($values as $value) {
$leading = str_starts_with($value, '--') ? $value.'=' : $value;
if ($token === $value || '' !== $leading && str_starts_with($token, $leading)) {
return true;
}
}
}
return false;
}
public function getParameterOption($values, $default = false, bool $onlyParams = false)
{
$values = (array) $values;
$tokens = $this->tokens;
while (0 < \count($tokens)) {
$token = array_shift($tokens);
if ($onlyParams && '--' === $token) {
return $default;
}
foreach ($values as $value) {
if ($token === $value) {
return array_shift($tokens);
}
$leading = str_starts_with($value, '--') ? $value.'=' : $value;
if ('' !== $leading && str_starts_with($token, $leading)) {
return substr($token, \strlen($leading));
}
}
}
return $default;
}
public function __toString()
{
$tokens = array_map(function ($token) {
if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) {
return $match[1].$this->escapeToken($match[2]);
}
if ($token && '-' !== $token[0]) {
return $this->escapeToken($token);
}
return $token;
}, $this->tokens);
return implode(' ', $tokens);
}
}
<?php
namespace Symfony\Component\Console\Input;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;
class InputArgument
{
public const REQUIRED = 1;
public const OPTIONAL = 2;
public const IS_ARRAY = 4;
private $name;
private $mode;
private $default;
private $description;
public function __construct(string $name, int $mode = null, string $description = '', $default = null)
{
if (null === $mode) {
$mode = self::OPTIONAL;
} elseif ($mode > 7 || $mode < 1) {
throw new InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode));
}
$this->name = $name;
$this->mode = $mode;
$this->description = $description;
$this->setDefault($default);
}
public function getName()
{
return $this->name;
}
public function isRequired()
{
return self::REQUIRED === (self::REQUIRED & $this->mode);
}
public function isArray()
{
return self::IS_ARRAY === (self::IS_ARRAY & $this->mode);
}
public function setDefault($default = null)
{
if ($this->isRequired() && null !== $default) {
throw new LogicException('Cannot set a default value except for InputArgument::OPTIONAL mode.');
}
if ($this->isArray()) {
if (null === $default) {
$default = [];
} elseif (!\is_array($default)) {
throw new LogicException('A default value for an array argument must be an array.');
}
}
$this->default = $default;
}
public function getDefault()
{
return $this->default;
}
public function getDescription()
{
return $this->description;
}
}
<?php
namespace Symfony\Component\Console\Input;
interface InputAwareInterface
{
public function setInput(InputInterface $input);
}
<?php
namespace Symfony\Component\Console\Input;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;
class InputOption
{
public const VALUE_NONE = 1;
public const VALUE_REQUIRED = 2;
public const VALUE_OPTIONAL = 4;
public const VALUE_IS_ARRAY = 8;
public const VALUE_NEGATABLE = 16;
private $name;
private $shortcut;
private $mode;
private $default;
private $description;
public function __construct(string $name, $shortcut = null, int $mode = null, string $description = '', $default = null)
{
if (str_starts_with($name, '--')) {
$name = substr($name, 2);
}
if (empty($name)) {
throw new InvalidArgumentException('An option name cannot be empty.');
}
if (empty($shortcut)) {
$shortcut = null;
}
if (null !== $shortcut) {
if (\is_array($shortcut)) {
$shortcut = implode('|', $shortcut);
}
$shortcuts = preg_split('{(\|)-?}', ltrim($shortcut, '-'));
$shortcuts = array_filter($shortcuts);
$shortcut = implode('|', $shortcuts);
if (empty($shortcut)) {
throw new InvalidArgumentException('An option shortcut cannot be empty.');
}
}
if (null === $mode) {
$mode = self::VALUE_NONE;
} elseif ($mode >= (self::VALUE_NEGATABLE << 1) || $mode < 1) {
throw new InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode));
}
$this->name = $name;
$this->shortcut = $shortcut;
$this->mode = $mode;
$this->description = $description;
if ($this->isArray() && !$this->acceptValue()) {
throw new InvalidArgumentException('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.');
}
if ($this->isNegatable() && $this->acceptValue()) {
throw new InvalidArgumentException('Impossible to have an option mode VALUE_NEGATABLE if the option also accepts a value.');
}
$this->setDefault($default);
}
public function getShortcut()
{
return $this->shortcut;
}
public function getName()
{
return $this->name;
}
public function acceptValue()
{
return $this->isValueRequired() || $this->isValueOptional();
}
public function isValueRequired()
{
return self::VALUE_REQUIRED === (self::VALUE_REQUIRED & $this->mode);
}
public function isValueOptional()
{
return self::VALUE_OPTIONAL === (self::VALUE_OPTIONAL & $this->mode);
}
public function isArray()
{
return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode);
}
public function isNegatable(): bool
{
return self::VALUE_NEGATABLE === (self::VALUE_NEGATABLE & $this->mode);
}
public function setDefault($default = null)
{
if (self::VALUE_NONE === (self::VALUE_NONE & $this->mode) && null !== $default) {
throw new LogicException('Cannot set a default value when using InputOption::VALUE_NONE mode.');
}
if ($this->isArray()) {
if (null === $default) {
$default = [];
} elseif (!\is_array($default)) {
throw new LogicException('A default value for an array option must be an array.');
}
}
$this->default = $this->acceptValue() || $this->isNegatable() ? $default : false;
}
public function getDefault()
{
return $this->default;
}
public function getDescription()
{
return $this->description;
}
public function equals(self $option)
{
return $option->getName() === $this->getName()
&& $option->getShortcut() === $this->getShortcut()
&& $option->getDefault() === $this->getDefault()
&& $option->isNegatable() === $this->isNegatable()
&& $option->isArray() === $this->isArray()
&& $option->isValueRequired() === $this->isValueRequired()
&& $option->isValueOptional() === $this->isValueOptional()
;
}
}
<?php
namespace Symfony\Component\Console\Input;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;
class InputDefinition
{
private $arguments;
private $requiredCount;
private $lastArrayArgument;
private $lastOptionalArgument;
private $options;
private $negations;
private $shortcuts;
public function __construct(array $definition = [])
{
$this->setDefinition($definition);
}
public function setDefinition(array $definition)
{
$arguments = [];
$options = [];
foreach ($definition as $item) {
if ($item instanceof InputOption) {
$options[] = $item;
} else {
$arguments[] = $item;
}
}
$this->setArguments($arguments);
$this->setOptions($options);
}
public function setArguments(array $arguments = [])
{
$this->arguments = [];
$this->requiredCount = 0;
$this->lastOptionalArgument = null;
$this->lastArrayArgument = null;
$this->addArguments($arguments);
}
public function addArguments(?array $arguments = [])
{
if (null !== $arguments) {
foreach ($arguments as $argument) {
$this->addArgument($argument);
}
}
}
public function addArgument(InputArgument $argument)
{
if (isset($this->arguments[$argument->getName()])) {
throw new LogicException(sprintf('An argument with name "%s" already exists.', $argument->getName()));
}
if (null !== $this->lastArrayArgument) {
throw new LogicException(sprintf('Cannot add a required argument "%s" after an array argument "%s".', $argument->getName(), $this->lastArrayArgument->getName()));
}
if ($argument->isRequired() && null !== $this->lastOptionalArgument) {
throw new LogicException(sprintf('Cannot add a required argument "%s" after an optional one "%s".', $argument->getName(), $this->lastOptionalArgument->getName()));
}
if ($argument->isArray()) {
$this->lastArrayArgument = $argument;
}
if ($argument->isRequired()) {
++$this->requiredCount;
} else {
$this->lastOptionalArgument = $argument;
}
$this->arguments[$argument->getName()] = $argument;
}
public function getArgument($name)
{
if (!$this->hasArgument($name)) {
throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
}
$arguments = \is_int($name) ? array_values($this->arguments) : $this->arguments;
return $arguments[$name];
}
public function hasArgument($name)
{
$arguments = \is_int($name) ? array_values($this->arguments) : $this->arguments;
return isset($arguments[$name]);
}
public function getArguments()
{
return $this->arguments;
}
public function getArgumentCount()
{
return null !== $this->lastArrayArgument ? \PHP_INT_MAX : \count($this->arguments);
}
public function getArgumentRequiredCount()
{
return $this->requiredCount;
}
public function getArgumentDefaults()
{
$values = [];
foreach ($this->arguments as $argument) {
$values[$argument->getName()] = $argument->getDefault();
}
return $values;
}
public function setOptions(array $options = [])
{
$this->options = [];
$this->shortcuts = [];
$this->negations = [];
$this->addOptions($options);
}
public function addOptions(array $options = [])
{
foreach ($options as $option) {
$this->addOption($option);
}
}
public function addOption(InputOption $option)
{
if (isset($this->options[$option->getName()]) && !$option->equals($this->options[$option->getName()])) {
throw new LogicException(sprintf('An option named "%s" already exists.', $option->getName()));
}
if (isset($this->negations[$option->getName()])) {
throw new LogicException(sprintf('An option named "%s" already exists.', $option->getName()));
}
if ($option->getShortcut()) {
foreach (explode('|', $option->getShortcut()) as $shortcut) {
if (isset($this->shortcuts[$shortcut]) && !$option->equals($this->options[$this->shortcuts[$shortcut]])) {
throw new LogicException(sprintf('An option with shortcut "%s" already exists.', $shortcut));
}
}
}
$this->options[$option->getName()] = $option;
if ($option->getShortcut()) {
foreach (explode('|', $option->getShortcut()) as $shortcut) {
$this->shortcuts[$shortcut] = $option->getName();
}
}
if ($option->isNegatable()) {
$negatedName = 'no-'.$option->getName();
if (isset($this->options[$negatedName])) {
throw new LogicException(sprintf('An option named "%s" already exists.', $negatedName));
}
$this->negations[$negatedName] = $option->getName();
}
}
public function getOption(string $name)
{
if (!$this->hasOption($name)) {
throw new InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name));
}
return $this->options[$name];
}
public function hasOption(string $name)
{
return isset($this->options[$name]);
}
public function getOptions()
{
return $this->options;
}
public function hasShortcut(string $name)
{
return isset($this->shortcuts[$name]);
}
public function hasNegation(string $name): bool
{
return isset($this->negations[$name]);
}
public function getOptionForShortcut(string $shortcut)
{
return $this->getOption($this->shortcutToName($shortcut));
}
public function getOptionDefaults()
{
$values = [];
foreach ($this->options as $option) {
$values[$option->getName()] = $option->getDefault();
}
return $values;
}
public function shortcutToName(string $shortcut): string
{
if (!isset($this->shortcuts[$shortcut])) {
throw new InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut));
}
return $this->shortcuts[$shortcut];
}
public function negationToName(string $negation): string
{
if (!isset($this->negations[$negation])) {
throw new InvalidArgumentException(sprintf('The "--%s" option does not exist.', $negation));
}
return $this->negations[$negation];
}
public function getSynopsis(bool $short = false)
{
$elements = [];
if ($short && $this->getOptions()) {
$elements[] = '[options]';
} elseif (!$short) {
foreach ($this->getOptions() as $option) {
$value = '';
if ($option->acceptValue()) {
$value = sprintf(
' %s%s%s',
$option->isValueOptional() ? '[' : '',
strtoupper($option->getName()),
$option->isValueOptional() ? ']' : ''
);
}
$shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : '';
$negation = $option->isNegatable() ? sprintf('|--no-%s', $option->getName()) : '';
$elements[] = sprintf('[%s--%s%s%s]', $shortcut, $option->getName(), $value, $negation);
}
}
if (\count($elements) && $this->getArguments()) {
$elements[] = '[--]';
}
$tail = '';
foreach ($this->getArguments() as $argument) {
$element = '<'.$argument->getName().'>';
if ($argument->isArray()) {
$element .= '...';
}
if (!$argument->isRequired()) {
$element = '['.$element;
$tail .= ']';
}
$elements[] = $element;
}
return implode(' ', $elements).$tail;
}
}
<?php
namespace Symfony\Component\Console\Input;
use Symfony\Component\Console\Exception\InvalidArgumentException;
class StringInput extends ArgvInput
{
public const REGEX_STRING = '([^\s]+?)(?:\s|(?<!\\\\)"|(?<!\\\\)\'|$)';
public const REGEX_UNQUOTED_STRING = '([^\s\\\\]+?)';
public const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\')';
public function __construct(string $input)
{
parent::__construct([]);
$this->setTokens($this->tokenize($input));
}
private function tokenize(string $input): array
{
$tokens = [];
$length = \strlen($input);
$cursor = 0;
$token = null;
while ($cursor < $length) {
if ('\\' === $input[$cursor]) {
$token .= $input[++$cursor] ?? '';
++$cursor;
continue;
}
if (preg_match('/\s+/A', $input, $match, 0, $cursor)) {
if (null !== $token) {
$tokens[] = $token;
$token = null;
}
} elseif (preg_match('/([^="\'\s]+?)(=?)('.self::REGEX_QUOTED_STRING.'+)/A', $input, $match, 0, $cursor)) {
$token .= $match[1].$match[2].stripcslashes(str_replace(['"\'', '\'"', '\'\'', '""'], '', substr($match[3], 1, -1)));
} elseif (preg_match('/'.self::REGEX_QUOTED_STRING.'/A', $input, $match, 0, $cursor)) {
$token .= stripcslashes(substr($match[0], 1, -1));
} elseif (preg_match('/'.self::REGEX_UNQUOTED_STRING.'/A', $input, $match, 0, $cursor)) {
$token .= $match[1];
} else {
throw new InvalidArgumentException(sprintf('Unable to parse input near "... %s ...".', substr($input, $cursor, 10)));
}
$cursor += \strlen($match[0]);
}
if (null !== $token) {
$tokens[] = $token;
}
return $tokens;
}
}
<?php
namespace Symfony\Component\Console\Input;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\RuntimeException;
abstract class Input implements InputInterface, StreamableInputInterface
{
protected $definition;
protected $stream;
protected $options = [];
protected $arguments = [];
protected $interactive = true;
public function __construct(InputDefinition $definition = null)
{
if (null === $definition) {
$this->definition = new InputDefinition();
} else {
$this->bind($definition);
$this->validate();
}
}
public function bind(InputDefinition $definition)
{
$this->arguments = [];
$this->options = [];
$this->definition = $definition;
$this->parse();
}
abstract protected function parse();
public function validate()
{
$definition = $this->definition;
$givenArguments = $this->arguments;
$missingArguments = array_filter(array_keys($definition->getArguments()), function ($argument) use ($definition, $givenArguments) {
return !\array_key_exists($argument, $givenArguments) && $definition->getArgument($argument)->isRequired();
});
if (\count($missingArguments) > 0) {
throw new RuntimeException(sprintf('Not enough arguments (missing: "%s").', implode(', ', $missingArguments)));
}
}
public function isInteractive()
{
return $this->interactive;
}
public function setInteractive(bool $interactive)
{
$this->interactive = $interactive;
}
public function getArguments()
{
return array_merge($this->definition->getArgumentDefaults(), $this->arguments);
}
public function getArgument(string $name)
{
if (!$this->definition->hasArgument($name)) {
throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
}
return $this->arguments[$name] ?? $this->definition->getArgument($name)->getDefault();
}
public function setArgument(string $name, $value)
{
if (!$this->definition->hasArgument($name)) {
throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
}
$this->arguments[$name] = $value;
}
public function hasArgument(string $name)
{
return $this->definition->hasArgument($name);
}
public function getOptions()
{
return array_merge($this->definition->getOptionDefaults(), $this->options);
}
public function getOption(string $name)
{
if ($this->definition->hasNegation($name)) {
if (null === $value = $this->getOption($this->definition->negationToName($name))) {
return $value;
}
return !$value;
}
if (!$this->definition->hasOption($name)) {
throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
}
return \array_key_exists($name, $this->options) ? $this->options[$name] : $this->definition->getOption($name)->getDefault();
}
public function setOption(string $name, $value)
{
if ($this->definition->hasNegation($name)) {
$this->options[$this->definition->negationToName($name)] = !$value;
return;
} elseif (!$this->definition->hasOption($name)) {
throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
}
$this->options[$name] = $value;
}
public function hasOption(string $name)
{
return $this->definition->hasOption($name) || $this->definition->hasNegation($name);
}
public function escapeToken(string $token)
{
return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token);
}
public function setStream($stream)
{
$this->stream = $stream;
}
public function getStream()
{
return $this->stream;
}
}
<?php
namespace Symfony\Component\Console\Input;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\RuntimeException;
interface InputInterface
{
public function getFirstArgument();
public function hasParameterOption($values, bool $onlyParams = false);
public function getParameterOption($values, $default = false, bool $onlyParams = false);
public function bind(InputDefinition $definition);
public function validate();
public function getArguments();
public function getArgument(string $name);
public function setArgument(string $name, $value);
public function hasArgument(string $name);
public function getOptions();
public function getOption(string $name);
public function setOption(string $name, $value);
public function hasOption(string $name);
public function isInteractive();
public function setInteractive(bool $interactive);
}
<?php
namespace Symfony\Component\Console;
class Terminal
{
private static $width;
private static $height;
private static $stty;
public function getWidth()
{
$width = getenv('COLUMNS');
if (false !== $width) {
return (int) trim($width);
}
if (null === self::$width) {
self::initDimensions();
}
return self::$width ?: 80;
}
public function getHeight()
{
$height = getenv('LINES');
if (false !== $height) {
return (int) trim($height);
}
if (null === self::$height) {
self::initDimensions();
}
return self::$height ?: 50;
}
public static function hasSttyAvailable(): bool
{
if (null !== self::$stty) {
return self::$stty;
}
if (!\function_exists('exec')) {
return false;
}
exec('stty 2>&1', $output, $exitcode);
return self::$stty = 0 === $exitcode;
}
private static function initDimensions()
{
if ('\\' === \DIRECTORY_SEPARATOR) {
if (preg_match('/^(\d+)x(\d+)(?: \((\d+)x(\d+)\))?$/', trim(getenv('ANSICON')), $matches)) {
self::$width = (int) $matches[1];
self::$height = isset($matches[4]) ? (int) $matches[4] : (int) $matches[2];
} elseif (!self::hasVt100Support() && self::hasSttyAvailable()) {
self::initDimensionsUsingStty();
} elseif (null !== $dimensions = self::getConsoleMode()) {
self::$width = (int) $dimensions[0];
self::$height = (int) $dimensions[1];
}
} else {
self::initDimensionsUsingStty();
}
}
private static function hasVt100Support(): bool
{
return \function_exists('sapi_windows_vt100_support') && sapi_windows_vt100_support(fopen('php://stdout', 'w'));
}
private static function initDimensionsUsingStty()
{
if ($sttyString = self::getSttyColumns()) {
if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) {
self::$width = (int) $matches[2];
self::$height = (int) $matches[1];
} elseif (preg_match('/;.(\d+).rows;.(\d+).columns/i', $sttyString, $matches)) {
self::$width = (int) $matches[2];
self::$height = (int) $matches[1];
}
}
}
private static function getConsoleMode(): ?array
{
$info = self::readFromProcess('mode CON');
if (null === $info || !preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) {
return null;
}
return [(int) $matches[2], (int) $matches[1]];
}
private static function getSttyColumns(): ?string
{
return self::readFromProcess('stty -a | grep columns');
}
private static function readFromProcess(string $command): ?string
{
if (!\function_exists('proc_open')) {
return null;
}
$descriptorspec = [
1 => ['pipe', 'w'],
2 => ['pipe', 'w'],
];
$process = proc_open($command, $descriptorspec, $pipes, null, null, ['suppress_errors' => true]);
if (!\is_resource($process)) {
return null;
}
$info = stream_get_contents($pipes[1]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
return $info;
}
}
<?php
namespace Symfony\Component\Console\CI;
use Symfony\Component\Console\Output\OutputInterface;
class GithubActionReporter
{
private $output;
private const ESCAPED_DATA = [
'%' => '%25',
"\r" => '%0D',
"\n" => '%0A',
];
private const ESCAPED_PROPERTIES = [
'%' => '%25',
"\r" => '%0D',
"\n" => '%0A',
':' => '%3A',
',' => '%2C',
];
public function __construct(OutputInterface $output)
{
$this->output = $output;
}
public static function isGithubActionEnvironment(): bool
{
return false !== getenv('GITHUB_ACTIONS');
}
public function error(string $message, string $file = null, int $line = null, int $col = null): void
{
$this->log('error', $message, $file, $line, $col);
}
public function warning(string $message, string $file = null, int $line = null, int $col = null): void
{
$this->log('warning', $message, $file, $line, $col);
}
public function debug(string $message, string $file = null, int $line = null, int $col = null): void
{
$this->log('debug', $message, $file, $line, $col);
}
private function log(string $type, string $message, string $file = null, int $line = null, int $col = null): void
{
$message = strtr($message, self::ESCAPED_DATA);
if (!$file) {
$this->output->writeln(sprintf('::%s::%s', $type, $message));
return;
}
$this->output->writeln(sprintf('::%s file=%s,line=%s,col=%s::%s', $type, strtr($file, self::ESCAPED_PROPERTIES), strtr($line ?? 1, self::ESCAPED_PROPERTIES), strtr($col ?? 0, self::ESCAPED_PROPERTIES), $message));
}
}
<?php
namespace Symfony\Component\Console\Output;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
class TrimmedBufferOutput extends Output
{
private $maxLength;
private $buffer = '';
public function __construct(int $maxLength, ?int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = false, OutputFormatterInterface $formatter = null)
{
if ($maxLength <= 0) {
throw new InvalidArgumentException(sprintf('"%s()" expects a strictly positive maxLength. Got %d.', __METHOD__, $maxLength));
}
parent::__construct($verbosity, $decorated, $formatter);
$this->maxLength = $maxLength;
}
public function fetch()
{
$content = $this->buffer;
$this->buffer = '';
return $content;
}
protected function doWrite(string $message, bool $newline)
{
$this->buffer .= $message;
if ($newline) {
$this->buffer .= \PHP_EOL;
}
$this->buffer = substr($this->buffer, 0 - $this->maxLength);
}
}
<?php
namespace Symfony\Component\Console\Output;
interface ConsoleOutputInterface extends OutputInterface
{
public function getErrorOutput();
public function setErrorOutput(OutputInterface $error);
public function section(): ConsoleSectionOutput;
}
<?php
namespace Symfony\Component\Console\Output;
class BufferedOutput extends Output
{
private $buffer = '';
public function fetch()
{
$content = $this->buffer;
$this->buffer = '';
return $content;
}
protected function doWrite(string $message, bool $newline)
{
$this->buffer .= $message;
if ($newline) {
$this->buffer .= \PHP_EOL;
}
}
}
<?php
namespace Symfony\Component\Console\Output;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
interface OutputInterface
{
public const VERBOSITY_QUIET = 16;
public const VERBOSITY_NORMAL = 32;
public const VERBOSITY_VERBOSE = 64;
public const VERBOSITY_VERY_VERBOSE = 128;
public const VERBOSITY_DEBUG = 256;
public const OUTPUT_NORMAL = 1;
public const OUTPUT_RAW = 2;
public const OUTPUT_PLAIN = 4;
public function write($messages, bool $newline = false, int $options = 0);
public function writeln($messages, int $options = 0);
public function setVerbosity(int $level);
public function getVerbosity();
public function isQuiet();
public function isVerbose();
public function isVeryVerbose();
public function isDebug();
public function setDecorated(bool $decorated);
public function isDecorated();
public function setFormatter(OutputFormatterInterface $formatter);
public function getFormatter();
}
<?php
namespace Symfony\Component\Console\Output;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
abstract class Output implements OutputInterface
{
private $verbosity;
private $formatter;
public function __construct(?int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = false, OutputFormatterInterface $formatter = null)
{
$this->verbosity = $verbosity ?? self::VERBOSITY_NORMAL;
$this->formatter = $formatter ?? new OutputFormatter();
$this->formatter->setDecorated($decorated);
}
public function setFormatter(OutputFormatterInterface $formatter)
{
$this->formatter = $formatter;
}
public function getFormatter()
{
return $this->formatter;
}
public function setDecorated(bool $decorated)
{
$this->formatter->setDecorated($decorated);
}
public function isDecorated()
{
return $this->formatter->isDecorated();
}
public function setVerbosity(int $level)
{
$this->verbosity = $level;
}
public function getVerbosity()
{
return $this->verbosity;
}
public function isQuiet()
{
return self::VERBOSITY_QUIET === $this->verbosity;
}
public function isVerbose()
{
return self::VERBOSITY_VERBOSE <= $this->verbosity;
}
public function isVeryVerbose()
{
return self::VERBOSITY_VERY_VERBOSE <= $this->verbosity;
}
public function isDebug()
{
return self::VERBOSITY_DEBUG <= $this->verbosity;
}
public function writeln($messages, int $options = self::OUTPUT_NORMAL)
{
$this->write($messages, true, $options);
}
public function write($messages, bool $newline = false, int $options = self::OUTPUT_NORMAL)
{
if (!is_iterable($messages)) {
$messages = [$messages];
}
$types = self::OUTPUT_NORMAL | self::OUTPUT_RAW | self::OUTPUT_PLAIN;
$type = $types & $options ?: self::OUTPUT_NORMAL;
$verbosities = self::VERBOSITY_QUIET | self::VERBOSITY_NORMAL | self::VERBOSITY_VERBOSE | self::VERBOSITY_VERY_VERBOSE | self::VERBOSITY_DEBUG;
$verbosity = $verbosities & $options ?: self::VERBOSITY_NORMAL;
if ($verbosity > $this->getVerbosity()) {
return;
}
foreach ($messages as $message) {
switch ($type) {
case OutputInterface::OUTPUT_NORMAL:
$message = $this->formatter->format($message);
break;
case OutputInterface::OUTPUT_RAW:
break;
case OutputInterface::OUTPUT_PLAIN:
$message = strip_tags($this->formatter->format($message));
break;
}
$this->doWrite($message ?? '', $newline);
}
}
abstract protected function doWrite(string $message, bool $newline);
}
<?php
namespace Symfony\Component\Console\Output;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Terminal;
class ConsoleSectionOutput extends StreamOutput
{
private $content = [];
private $lines = 0;
private $sections;
private $terminal;
public function __construct($stream, array &$sections, int $verbosity, bool $decorated, OutputFormatterInterface $formatter)
{
parent::__construct($stream, $verbosity, $decorated, $formatter);
array_unshift($sections, $this);
$this->sections = &$sections;
$this->terminal = new Terminal();
}
public function clear(int $lines = null)
{
if (empty($this->content) || !$this->isDecorated()) {
return;
}
if ($lines) {
array_splice($this->content, -($lines * 2));
} else {
$lines = $this->lines;
$this->content = [];
}
$this->lines -= $lines;
parent::doWrite($this->popStreamContentUntilCurrentSection($lines), false);
}
public function overwrite($message)
{
$this->clear();
$this->writeln($message);
}
public function getContent(): string
{
return implode('', $this->content);
}
public function addContent(string $input)
{
foreach (explode(\PHP_EOL, $input) as $lineContent) {
$this->lines += ceil($this->getDisplayLength($lineContent) / $this->terminal->getWidth()) ?: 1;
$this->content[] = $lineContent;
$this->content[] = \PHP_EOL;
}
}
protected function doWrite(string $message, bool $newline)
{
if (!$this->isDecorated()) {
parent::doWrite($message, $newline);
return;
}
$erasedContent = $this->popStreamContentUntilCurrentSection();
$this->addContent($message);
parent::doWrite($message, true);
parent::doWrite($erasedContent, false);
}
private function popStreamContentUntilCurrentSection(int $numberOfLinesToClearFromCurrentSection = 0): string
{
$numberOfLinesToClear = $numberOfLinesToClearFromCurrentSection;
$erasedContent = [];
foreach ($this->sections as $section) {
if ($section === $this) {
break;
}
$numberOfLinesToClear += $section->lines;
$erasedContent[] = $section->getContent();
}
if ($numberOfLinesToClear > 0) {
parent::doWrite(sprintf("\x1b[%dA", $numberOfLinesToClear), false);
parent::doWrite("\x1b[0J", false);
}
return implode('', array_reverse($erasedContent));
}
private function getDisplayLength(string $text): int
{
return Helper::width(Helper::removeDecoration($this->getFormatter(), str_replace("\t", ' ', $text)));
}
}
<?php
namespace Symfony\Component\Console\Output;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface
{
private $stderr;
private $consoleSectionOutputs = [];
public function __construct(int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = null, OutputFormatterInterface $formatter = null)
{
parent::__construct($this->openOutputStream(), $verbosity, $decorated, $formatter);
if (null === $formatter) {
$this->stderr = new StreamOutput($this->openErrorStream(), $verbosity, $decorated);
return;
}
$actualDecorated = $this->isDecorated();
$this->stderr = new StreamOutput($this->openErrorStream(), $verbosity, $decorated, $this->getFormatter());
if (null === $decorated) {
$this->setDecorated($actualDecorated && $this->stderr->isDecorated());
}
}
public function section(): ConsoleSectionOutput
{
return new ConsoleSectionOutput($this->getStream(), $this->consoleSectionOutputs, $this->getVerbosity(), $this->isDecorated(), $this->getFormatter());
}
public function setDecorated(bool $decorated)
{
parent::setDecorated($decorated);
$this->stderr->setDecorated($decorated);
}
public function setFormatter(OutputFormatterInterface $formatter)
{
parent::setFormatter($formatter);
$this->stderr->setFormatter($formatter);
}
public function setVerbosity(int $level)
{
parent::setVerbosity($level);
$this->stderr->setVerbosity($level);
}
public function getErrorOutput()
{
return $this->stderr;
}
public function setErrorOutput(OutputInterface $error)
{
$this->stderr = $error;
}
protected function hasStdoutSupport()
{
return false === $this->isRunningOS400();
}
protected function hasStderrSupport()
{
return false === $this->isRunningOS400();
}
private function isRunningOS400(): bool
{
$checks = [
\function_exists('php_uname') ? php_uname('s') : '',
getenv('OSTYPE'),
\PHP_OS,
];
return false !== stripos(implode(';', $checks), 'OS400');
}
private function openOutputStream()
{
if (!$this->hasStdoutSupport()) {
return fopen('php://output', 'w');
}
return \defined('STDOUT') ? \STDOUT : (@fopen('php://stdout', 'w') ?: fopen('php://output', 'w'));
}
private function openErrorStream()
{
if (!$this->hasStderrSupport()) {
return fopen('php://output', 'w');
}
return \defined('STDERR') ? \STDERR : (@fopen('php://stderr', 'w') ?: fopen('php://output', 'w'));
}
}
<?php
namespace Symfony\Component\Console\Output;
use Symfony\Component\Console\Formatter\NullOutputFormatter;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
class NullOutput implements OutputInterface
{
private $formatter;
public function setFormatter(OutputFormatterInterface $formatter)
{
}
public function getFormatter()
{
if ($this->formatter) {
return $this->formatter;
}
return $this->formatter = new NullOutputFormatter();
}
public function setDecorated(bool $decorated)
{
}
public function isDecorated()
{
return false;
}
public function setVerbosity(int $level)
{
}
public function getVerbosity()
{
return self::VERBOSITY_QUIET;
}
public function isQuiet()
{
return true;
}
public function isVerbose()
{
return false;
}
public function isVeryVerbose()
{
return false;
}
public function isDebug()
{
return false;
}
public function writeln($messages, int $options = self::OUTPUT_NORMAL)
{
}
public function write($messages, bool $newline = false, int $options = self::OUTPUT_NORMAL)
{
}
}
<?php
namespace Symfony\Component\Console\Output;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
class StreamOutput extends Output
{
private $stream;
public function __construct($stream, int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = null, OutputFormatterInterface $formatter = null)
{
if (!\is_resource($stream) || 'stream' !== get_resource_type($stream)) {
throw new InvalidArgumentException('The StreamOutput class needs a stream as its first argument.');
}
$this->stream = $stream;
if (null === $decorated) {
$decorated = $this->hasColorSupport();
}
parent::__construct($verbosity, $decorated, $formatter);
}
public function getStream()
{
return $this->stream;
}
protected function doWrite(string $message, bool $newline)
{
if ($newline) {
$message .= \PHP_EOL;
}
@fwrite($this->stream, $message);
fflush($this->stream);
}
protected function hasColorSupport()
{
if (isset($_SERVER['NO_COLOR']) || false !== getenv('NO_COLOR')) {
return false;
}
if ('Hyper' === getenv('TERM_PROGRAM')) {
return true;
}
if (\DIRECTORY_SEPARATOR === '\\') {
return (\function_exists('sapi_windows_vt100_support')
&& @sapi_windows_vt100_support($this->stream))
|| false !== getenv('ANSICON')
|| 'ON' === getenv('ConEmuANSI')
|| 'xterm' === getenv('TERM');
}
return stream_isatty($this->stream);
}
}
<?php
namespace Symfony\Component\Console\Completion\Output;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Output\OutputInterface;
class BashCompletionOutput implements CompletionOutputInterface
{
public function write(CompletionSuggestions $suggestions, OutputInterface $output): void
{
$values = $suggestions->getValueSuggestions();
foreach ($suggestions->getOptionSuggestions() as $option) {
$values[] = '--'.$option->getName();
if ($option->isNegatable()) {
$values[] = '--no-'.$option->getName();
}
}
$output->writeln(implode("\n", $values));
}
}
<?php
namespace Symfony\Component\Console\Completion\Output;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Output\OutputInterface;
interface CompletionOutputInterface
{
public function write(CompletionSuggestions $suggestions, OutputInterface $output): void;
}
<?php
namespace Symfony\Component\Console\Completion;
class Suggestion
{
private $value;
public function __construct(string $value)
{
$this->value = $value;
}
public function getValue(): string
{
return $this->value;
}
public function __toString(): string
{
return $this->getValue();
}
}
<?php
namespace Symfony\Component\Console\Completion;
use Symfony\Component\Console\Input\InputOption;
final class CompletionSuggestions
{
private $valueSuggestions = [];
private $optionSuggestions = [];
public function suggestValue($value): self
{
$this->valueSuggestions[] = !$value instanceof Suggestion ? new Suggestion($value) : $value;
return $this;
}
public function suggestValues(array $values): self
{
foreach ($values as $value) {
$this->suggestValue($value);
}
return $this;
}
public function suggestOption(InputOption $option): self
{
$this->optionSuggestions[] = $option;
return $this;
}
public function suggestOptions(array $options): self
{
foreach ($options as $option) {
$this->suggestOption($option);
}
return $this;
}
public function getOptionSuggestions(): array
{
return $this->optionSuggestions;
}
public function getValueSuggestions(): array
{
return $this->valueSuggestions;
}
}
<?php
namespace Symfony\Component\Console\Completion;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
final class CompletionInput extends ArgvInput
{
public const TYPE_ARGUMENT_VALUE = 'argument_value';
public const TYPE_OPTION_VALUE = 'option_value';
public const TYPE_OPTION_NAME = 'option_name';
public const TYPE_NONE = 'none';
private $tokens;
private $currentIndex;
private $completionType;
private $completionName = null;
private $completionValue = '';
public static function fromString(string $inputStr, int $currentIndex): self
{
preg_match_all('/(?<=^|\s)([\'"]?)(.+?)(?<!\\\\)\1(?=$|\s)/', $inputStr, $tokens);
return self::fromTokens($tokens[0], $currentIndex);
}
public static function fromTokens(array $tokens, int $currentIndex): self
{
$input = new self($tokens);
$input->tokens = $tokens;
$input->currentIndex = $currentIndex;
return $input;
}
public function bind(InputDefinition $definition): void
{
parent::bind($definition);
$relevantToken = $this->getRelevantToken();
if ('-' === $relevantToken[0]) {
[$optionToken, $optionValue] = explode('=', $relevantToken, 2) + ['', ''];
$option = $this->getOptionFromToken($optionToken);
if (null === $option && !$this->isCursorFree()) {
$this->completionType = self::TYPE_OPTION_NAME;
$this->completionValue = $relevantToken;
return;
}
if (null !== $option && $option->acceptValue()) {
$this->completionType = self::TYPE_OPTION_VALUE;
$this->completionName = $option->getName();
$this->completionValue = $optionValue ?: (!str_starts_with($optionToken, '--') ? substr($optionToken, 2) : '');
return;
}
}
$previousToken = $this->tokens[$this->currentIndex - 1];
if ('-' === $previousToken[0] && '' !== trim($previousToken, '-')) {
$previousOption = $this->getOptionFromToken($previousToken);
if (null !== $previousOption && $previousOption->acceptValue()) {
$this->completionType = self::TYPE_OPTION_VALUE;
$this->completionName = $previousOption->getName();
$this->completionValue = $relevantToken;
return;
}
}
$this->completionType = self::TYPE_ARGUMENT_VALUE;
foreach ($this->definition->getArguments() as $argumentName => $argument) {
if (!isset($this->arguments[$argumentName])) {
break;
}
$argumentValue = $this->arguments[$argumentName];
$this->completionName = $argumentName;
if (\is_array($argumentValue)) {
$this->completionValue = $argumentValue ? $argumentValue[array_key_last($argumentValue)] : null;
} else {
$this->completionValue = $argumentValue;
}
}
if ($this->currentIndex >= \count($this->tokens)) {
if (!isset($this->arguments[$argumentName]) || $this->definition->getArgument($argumentName)->isArray()) {
$this->completionName = $argumentName;
$this->completionValue = '';
} else {
$this->completionType = self::TYPE_NONE;
$this->completionName = null;
$this->completionValue = '';
}
}
}
public function getCompletionType(): string
{
return $this->completionType;
}
public function getCompletionName(): ?string
{
return $this->completionName;
}
public function getCompletionValue(): string
{
return $this->completionValue;
}
public function mustSuggestOptionValuesFor(string $optionName): bool
{
return self::TYPE_OPTION_VALUE === $this->getCompletionType() && $optionName === $this->getCompletionName();
}
public function mustSuggestArgumentValuesFor(string $argumentName): bool
{
return self::TYPE_ARGUMENT_VALUE === $this->getCompletionType() && $argumentName === $this->getCompletionName();
}
protected function parseToken(string $token, bool $parseOptions): bool
{
try {
return parent::parseToken($token, $parseOptions);
} catch (RuntimeException $e) {
}
return $parseOptions;
}
private function getOptionFromToken(string $optionToken): ?InputOption
{
$optionName = ltrim($optionToken, '-');
if (!$optionName) {
return null;
}
if ('-' === ($optionToken[1] ?? ' ')) {
return $this->definition->hasOption($optionName) ? $this->definition->getOption($optionName) : null;
}
return $this->definition->hasShortcut($optionName[0]) ? $this->definition->getOptionForShortcut($optionName[0]) : null;
}
private function getRelevantToken(): string
{
return $this->tokens[$this->isCursorFree() ? $this->currentIndex - 1 : $this->currentIndex];
}
private function isCursorFree(): bool
{
$nrOfTokens = \count($this->tokens);
if ($this->currentIndex > $nrOfTokens) {
throw new \LogicException('Current index is invalid, it must be the number of input tokens or one more.');
}
return $this->currentIndex >= $nrOfTokens;
}
public function __toString()
{
$str = '';
foreach ($this->tokens as $i => $token) {
$str .= $token;
if ($this->currentIndex === $i) {
$str .= '|';
}
$str .= ' ';
}
if ($this->currentIndex > $i) {
$str .= '|';
}
return rtrim($str);
}
}
<?php
namespace Symfony\Component\Console\Style;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
abstract class OutputStyle implements OutputInterface, StyleInterface
{
private $output;
public function __construct(OutputInterface $output)
{
$this->output = $output;
}
public function newLine(int $count = 1)
{
$this->output->write(str_repeat(\PHP_EOL, $count));
}
public function createProgressBar(int $max = 0)
{
return new ProgressBar($this->output, $max);
}
public function write($messages, bool $newline = false, int $type = self::OUTPUT_NORMAL)
{
$this->output->write($messages, $newline, $type);
}
public function writeln($messages, int $type = self::OUTPUT_NORMAL)
{
$this->output->writeln($messages, $type);
}
public function setVerbosity(int $level)
{
$this->output->setVerbosity($level);
}
public function getVerbosity()
{
return $this->output->getVerbosity();
}
public function setDecorated(bool $decorated)
{
$this->output->setDecorated($decorated);
}
public function isDecorated()
{
return $this->output->isDecorated();
}
public function setFormatter(OutputFormatterInterface $formatter)
{
$this->output->setFormatter($formatter);
}
public function getFormatter()
{
return $this->output->getFormatter();
}
public function isQuiet()
{
return $this->output->isQuiet();
}
public function isVerbose()
{
return $this->output->isVerbose();
}
public function isVeryVerbose()
{
return $this->output->isVeryVerbose();
}
public function isDebug()
{
return $this->output->isDebug();
}
protected function getErrorOutput()
{
if (!$this->output instanceof ConsoleOutputInterface) {
return $this->output;
}
return $this->output->getErrorOutput();
}
}
<?php
namespace Symfony\Component\Console\Style;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Helper\SymfonyQuestionHelper;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Helper\TableCell;
use Symfony\Component\Console\Helper\TableSeparator;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\TrimmedBufferOutput;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Terminal;
class SymfonyStyle extends OutputStyle
{
public const MAX_LINE_LENGTH = 120;
private $input;
private $output;
private $questionHelper;
private $progressBar;
private $lineLength;
private $bufferedOutput;
public function __construct(InputInterface $input, OutputInterface $output)
{
$this->input = $input;
$this->bufferedOutput = new TrimmedBufferOutput(\DIRECTORY_SEPARATOR === '\\' ? 4 : 2, $output->getVerbosity(), false, clone $output->getFormatter());
$width = (new Terminal())->getWidth() ?: self::MAX_LINE_LENGTH;
$this->lineLength = min($width - (int) (\DIRECTORY_SEPARATOR === '\\'), self::MAX_LINE_LENGTH);
parent::__construct($this->output = $output);
}
public function block($messages, string $type = null, string $style = null, string $prefix = ' ', bool $padding = false, bool $escape = true)
{
$messages = \is_array($messages) ? array_values($messages) : [$messages];
$this->autoPrependBlock();
$this->writeln($this->createBlock($messages, $type, $style, $prefix, $padding, $escape));
$this->newLine();
}
public function title(string $message)
{
$this->autoPrependBlock();
$this->writeln([
sprintf('<comment>%s</>', OutputFormatter::escapeTrailingBackslash($message)),
sprintf('<comment>%s</>', str_repeat('=', Helper::width(Helper::removeDecoration($this->getFormatter(), $message)))),
]);
$this->newLine();
}
public function section(string $message)
{
$this->autoPrependBlock();
$this->writeln([
sprintf('<comment>%s</>', OutputFormatter::escapeTrailingBackslash($message)),
sprintf('<comment>%s</>', str_repeat('-', Helper::width(Helper::removeDecoration($this->getFormatter(), $message)))),
]);
$this->newLine();
}
public function listing(array $elements)
{
$this->autoPrependText();
$elements = array_map(function ($element) {
return sprintf(' * %s', $element);
}, $elements);
$this->writeln($elements);
$this->newLine();
}
public function text($message)
{
$this->autoPrependText();
$messages = \is_array($message) ? array_values($message) : [$message];
foreach ($messages as $message) {
$this->writeln(sprintf(' %s', $message));
}
}
public function comment($message)
{
$this->block($message, null, null, '<fg=default;bg=default> // </>', false, false);
}
public function success($message)
{
$this->block($message, 'OK', 'fg=black;bg=green', ' ', true);
}
public function error($message)
{
$this->block($message, 'ERROR', 'fg=white;bg=red', ' ', true);
}
public function warning($message)
{
$this->block($message, 'WARNING', 'fg=black;bg=yellow', ' ', true);
}
public function note($message)
{
$this->block($message, 'NOTE', 'fg=yellow', ' ! ');
}
public function info($message)
{
$this->block($message, 'INFO', 'fg=green', ' ', true);
}
public function caution($message)
{
$this->block($message, 'CAUTION', 'fg=white;bg=red', ' ! ', true);
}
public function table(array $headers, array $rows)
{
$this->createTable()
->setHeaders($headers)
->setRows($rows)
->render()
;
$this->newLine();
}
public function horizontalTable(array $headers, array $rows)
{
$this->createTable()
->setHorizontal(true)
->setHeaders($headers)
->setRows($rows)
->render()
;
$this->newLine();
}
public function definitionList(...$list)
{
$headers = [];
$row = [];
foreach ($list as $value) {
if ($value instanceof TableSeparator) {
$headers[] = $value;
$row[] = $value;
continue;
}
if (\is_string($value)) {
$headers[] = new TableCell($value, ['colspan' => 2]);
$row[] = null;
continue;
}
if (!\is_array($value)) {
throw new InvalidArgumentException('Value should be an array, string, or an instance of TableSeparator.');
}
$headers[] = key($value);
$row[] = current($value);
}
$this->horizontalTable($headers, [$row]);
}
public function ask(string $question, string $default = null, callable $validator = null)
{
$question = new Question($question, $default);
$question->setValidator($validator);
return $this->askQuestion($question);
}
public function askHidden(string $question, callable $validator = null)
{
$question = new Question($question);
$question->setHidden(true);
$question->setValidator($validator);
return $this->askQuestion($question);
}
public function confirm(string $question, bool $default = true)
{
return $this->askQuestion(new ConfirmationQuestion($question, $default));
}
public function choice(string $question, array $choices, $default = null)
{
if (null !== $default) {
$values = array_flip($choices);
$default = $values[$default] ?? $default;
}
return $this->askQuestion(new ChoiceQuestion($question, $choices, $default));
}
public function progressStart(int $max = 0)
{
$this->progressBar = $this->createProgressBar($max);
$this->progressBar->start();
}
public function progressAdvance(int $step = 1)
{
$this->getProgressBar()->advance($step);
}
public function progressFinish()
{
$this->getProgressBar()->finish();
$this->newLine(2);
$this->progressBar = null;
}
public function createProgressBar(int $max = 0)
{
$progressBar = parent::createProgressBar($max);
if ('\\' !== \DIRECTORY_SEPARATOR || 'Hyper' === getenv('TERM_PROGRAM')) {
$progressBar->setEmptyBarCharacter('░');
$progressBar->setProgressCharacter('');
$progressBar->setBarCharacter('▓');
}
return $progressBar;
}
public function progressIterate(iterable $iterable, int $max = null): iterable
{
yield from $this->createProgressBar()->iterate($iterable, $max);
$this->newLine(2);
}
public function askQuestion(Question $question)
{
if ($this->input->isInteractive()) {
$this->autoPrependBlock();
}
if (!$this->questionHelper) {
$this->questionHelper = new SymfonyQuestionHelper();
}
$answer = $this->questionHelper->ask($this->input, $this, $question);
if ($this->input->isInteractive()) {
$this->newLine();
$this->bufferedOutput->write("\n");
}
return $answer;
}
public function writeln($messages, int $type = self::OUTPUT_NORMAL)
{
if (!is_iterable($messages)) {
$messages = [$messages];
}
foreach ($messages as $message) {
parent::writeln($message, $type);
$this->writeBuffer($message, true, $type);
}
}
public function write($messages, bool $newline = false, int $type = self::OUTPUT_NORMAL)
{
if (!is_iterable($messages)) {
$messages = [$messages];
}
foreach ($messages as $message) {
parent::write($message, $newline, $type);
$this->writeBuffer($message, $newline, $type);
}
}
public function newLine(int $count = 1)
{
parent::newLine($count);
$this->bufferedOutput->write(str_repeat("\n", $count));
}
public function getErrorStyle()
{
return new self($this->input, $this->getErrorOutput());
}
public function createTable(): Table
{
$output = $this->output instanceof ConsoleOutputInterface ? $this->output->section() : $this->output;
$style = clone Table::getStyleDefinition('symfony-style-guide');
$style->setCellHeaderFormat('<info>%s</info>');
return (new Table($output))->setStyle($style);
}
private function getProgressBar(): ProgressBar
{
if (!$this->progressBar) {
throw new RuntimeException('The ProgressBar is not started.');
}
return $this->progressBar;
}
private function autoPrependBlock(): void
{
$chars = substr(str_replace(\PHP_EOL, "\n", $this->bufferedOutput->fetch()), -2);
if (!isset($chars[0])) {
$this->newLine();
return;
}
$this->newLine(2 - substr_count($chars, "\n"));
}
private function autoPrependText(): void
{
$fetched = $this->bufferedOutput->fetch();
if (!str_ends_with($fetched, "\n")) {
$this->newLine();
}
}
private function writeBuffer(string $message, bool $newLine, int $type): void
{
$this->bufferedOutput->write($message, $newLine, $type);
}
private function createBlock(iterable $messages, string $type = null, string $style = null, string $prefix = ' ', bool $padding = false, bool $escape = false): array
{
$indentLength = 0;
$prefixLength = Helper::width(Helper::removeDecoration($this->getFormatter(), $prefix));
$lines = [];
if (null !== $type) {
$type = sprintf('[%s] ', $type);
$indentLength = \strlen($type);
$lineIndentation = str_repeat(' ', $indentLength);
}
foreach ($messages as $key => $message) {
if ($escape) {
$message = OutputFormatter::escape($message);
}
$decorationLength = Helper::width($message) - Helper::width(Helper::removeDecoration($this->getFormatter(), $message));
$messageLineLength = min($this->lineLength - $prefixLength - $indentLength + $decorationLength, $this->lineLength);
$messageLines = explode(\PHP_EOL, wordwrap($message, $messageLineLength, \PHP_EOL, true));
foreach ($messageLines as $messageLine) {
$lines[] = $messageLine;
}
if (\count($messages) > 1 && $key < \count($messages) - 1) {
$lines[] = '';
}
}
$firstLineIndex = 0;
if ($padding && $this->isDecorated()) {
$firstLineIndex = 1;
array_unshift($lines, '');
$lines[] = '';
}
foreach ($lines as $i => &$line) {
if (null !== $type) {
$line = $firstLineIndex === $i ? $type.$line : $lineIndentation.$line;
}
$line = $prefix.$line;
$line .= str_repeat(' ', max($this->lineLength - Helper::width(Helper::removeDecoration($this->getFormatter(), $line)), 0));
if ($style) {
$line = sprintf('<%s>%s</>', $style, $line);
}
}
return $lines;
}
}
<?php
namespace Symfony\Component\Console\Style;
interface StyleInterface
{
public function title(string $message);
public function section(string $message);
public function listing(array $elements);
public function text($message);
public function success($message);
public function error($message);
public function warning($message);
public function note($message);
public function caution($message);
public function table(array $headers, array $rows);
public function ask(string $question, string $default = null, callable $validator = null);
public function askHidden(string $question, callable $validator = null);
public function confirm(string $question, bool $default = true);
public function choice(string $question, array $choices, $default = null);
public function newLine(int $count = 1);
public function progressStart(int $max = 0);
public function progressAdvance(int $step = 1);
public function progressFinish();
}
<?php
namespace Symfony\Component\Console\Question;
use Symfony\Component\Console\Exception\InvalidArgumentException;
class ChoiceQuestion extends Question
{
private $choices;
private $multiselect = false;
private $prompt = ' > ';
private $errorMessage = 'Value "%s" is invalid';
public function __construct(string $question, array $choices, $default = null)
{
if (!$choices) {
throw new \LogicException('Choice question must have at least 1 choice available.');
}
parent::__construct($question, $default);
$this->choices = $choices;
$this->setValidator($this->getDefaultValidator());
$this->setAutocompleterValues($choices);
}
public function getChoices()
{
return $this->choices;
}
public function setMultiselect(bool $multiselect)
{
$this->multiselect = $multiselect;
$this->setValidator($this->getDefaultValidator());
return $this;
}
public function isMultiselect()
{
return $this->multiselect;
}
public function getPrompt()
{
return $this->prompt;
}
public function setPrompt(string $prompt)
{
$this->prompt = $prompt;
return $this;
}
public function setErrorMessage(string $errorMessage)
{
$this->errorMessage = $errorMessage;
$this->setValidator($this->getDefaultValidator());
return $this;
}
private function getDefaultValidator(): callable
{
$choices = $this->choices;
$errorMessage = $this->errorMessage;
$multiselect = $this->multiselect;
$isAssoc = $this->isAssoc($choices);
return function ($selected) use ($choices, $errorMessage, $multiselect, $isAssoc) {
if ($multiselect) {
if (!preg_match('/^[^,]+(?:,[^,]+)*$/', (string) $selected, $matches)) {
throw new InvalidArgumentException(sprintf($errorMessage, $selected));
}
$selectedChoices = explode(',', (string) $selected);
} else {
$selectedChoices = [$selected];
}
if ($this->isTrimmable()) {
foreach ($selectedChoices as $k => $v) {
$selectedChoices[$k] = trim((string) $v);
}
}
$multiselectChoices = [];
foreach ($selectedChoices as $value) {
$results = [];
foreach ($choices as $key => $choice) {
if ($choice === $value) {
$results[] = $key;
}
}
if (\count($results) > 1) {
throw new InvalidArgumentException(sprintf('The provided answer is ambiguous. Value should be one of "%s".', implode('" or "', $results)));
}
$result = array_search($value, $choices);
if (!$isAssoc) {
if (false !== $result) {
$result = $choices[$result];
} elseif (isset($choices[$value])) {
$result = $choices[$value];
}
} elseif (false === $result && isset($choices[$value])) {
$result = $value;
}
if (false === $result) {
throw new InvalidArgumentException(sprintf($errorMessage, $value));
}
$multiselectChoices[] = $isAssoc ? (string) $result : $result;
}
if ($multiselect) {
return $multiselectChoices;
}
return current($multiselectChoices);
};
}
}
<?php
namespace Symfony\Component\Console\Question;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;
class Question
{
private $question;
private $attempts;
private $hidden = false;
private $hiddenFallback = true;
private $autocompleterCallback;
private $validator;
private $default;
private $normalizer;
private $trimmable = true;
private $multiline = false;
public function __construct(string $question, $default = null)
{
$this->question = $question;
$this->default = $default;
}
public function getQuestion()
{
return $this->question;
}
public function getDefault()
{
return $this->default;
}
public function isMultiline(): bool
{
return $this->multiline;
}
public function setMultiline(bool $multiline): self
{
$this->multiline = $multiline;
return $this;
}
public function isHidden()
{
return $this->hidden;
}
public function setHidden(bool $hidden)
{
if ($this->autocompleterCallback) {
throw new LogicException('A hidden question cannot use the autocompleter.');
}
$this->hidden = $hidden;
return $this;
}
public function isHiddenFallback()
{
return $this->hiddenFallback;
}
public function setHiddenFallback(bool $fallback)
{
$this->hiddenFallback = $fallback;
return $this;
}
public function getAutocompleterValues()
{
$callback = $this->getAutocompleterCallback();
return $callback ? $callback('') : null;
}
public function setAutocompleterValues(?iterable $values)
{
if (\is_array($values)) {
$values = $this->isAssoc($values) ? array_merge(array_keys($values), array_values($values)) : array_values($values);
$callback = static function () use ($values) {
return $values;
};
} elseif ($values instanceof \Traversable) {
$valueCache = null;
$callback = static function () use ($values, &$valueCache) {
return $valueCache ?? $valueCache = iterator_to_array($values, false);
};
} else {
$callback = null;
}
return $this->setAutocompleterCallback($callback);
}
public function getAutocompleterCallback(): ?callable
{
return $this->autocompleterCallback;
}
public function setAutocompleterCallback(callable $callback = null): self
{
if ($this->hidden && null !== $callback) {
throw new LogicException('A hidden question cannot use the autocompleter.');
}
$this->autocompleterCallback = $callback;
return $this;
}
public function setValidator(callable $validator = null)
{
$this->validator = $validator;
return $this;
}
public function getValidator()
{
return $this->validator;
}
public function setMaxAttempts(?int $attempts)
{
if (null !== $attempts && $attempts < 1) {
throw new InvalidArgumentException('Maximum number of attempts must be a positive value.');
}
$this->attempts = $attempts;
return $this;
}
public function getMaxAttempts()
{
return $this->attempts;
}
public function setNormalizer(callable $normalizer)
{
$this->normalizer = $normalizer;
return $this;
}
public function getNormalizer()
{
return $this->normalizer;
}
protected function isAssoc(array $array)
{
return (bool) \count(array_filter(array_keys($array), 'is_string'));
}
public function isTrimmable(): bool
{
return $this->trimmable;
}
public function setTrimmable(bool $trimmable): self
{
$this->trimmable = $trimmable;
return $this;
}
}
<?php
namespace Symfony\Component\Console\Question;
class ConfirmationQuestion extends Question
{
private $trueAnswerRegex;
public function __construct(string $question, bool $default = true, string $trueAnswerRegex = '/^y/i')
{
parent::__construct($question, $default);
$this->trueAnswerRegex = $trueAnswerRegex;
$this->setNormalizer($this->getDefaultNormalizer());
}
private function getDefaultNormalizer(): callable
{
$default = $this->getDefault();
$regex = $this->trueAnswerRegex;
return function ($answer) use ($default, $regex) {
if (\is_bool($answer)) {
return $answer;
}
$answerIsTrue = (bool) preg_match($regex, $answer);
if (false === $default) {
return $answer && $answerIsTrue;
}
return '' === $answer || $answerIsTrue;
};
}
}
<?php
namespace Symfony\Component\Console\Formatter;
use Symfony\Component\Console\Color;
class OutputFormatterStyle implements OutputFormatterStyleInterface
{
private $color;
private $foreground;
private $background;
private $options;
private $href;
private $handlesHrefGracefully;
public function __construct(string $foreground = null, string $background = null, array $options = [])
{
$this->color = new Color($this->foreground = $foreground ?: '', $this->background = $background ?: '', $this->options = $options);
}
public function setForeground(string $color = null)
{
$this->color = new Color($this->foreground = $color ?: '', $this->background, $this->options);
}
public function setBackground(string $color = null)
{
$this->color = new Color($this->foreground, $this->background = $color ?: '', $this->options);
}
public function setHref(string $url): void
{
$this->href = $url;
}
public function setOption(string $option)
{
$this->options[] = $option;
$this->color = new Color($this->foreground, $this->background, $this->options);
}
public function unsetOption(string $option)
{
$pos = array_search($option, $this->options);
if (false !== $pos) {
unset($this->options[$pos]);
}
$this->color = new Color($this->foreground, $this->background, $this->options);
}
public function setOptions(array $options)
{
$this->color = new Color($this->foreground, $this->background, $this->options = $options);
}
public function apply(string $text)
{
if (null === $this->handlesHrefGracefully) {
$this->handlesHrefGracefully = 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR')
&& (!getenv('KONSOLE_VERSION') || (int) getenv('KONSOLE_VERSION') > 201100);
}
if (null !== $this->href && $this->handlesHrefGracefully) {
$text = "\033]8;;$this->href\033\\$text\033]8;;\033\\";
}
return $this->color->apply($text);
}
}
<?php
namespace Symfony\Component\Console\Formatter;
interface OutputFormatterStyleInterface
{
public function setForeground(string $color = null);
public function setBackground(string $color = null);
public function setOption(string $option);
public function unsetOption(string $option);
public function setOptions(array $options);
public function apply(string $text);
}
<?php
namespace Symfony\Component\Console\Formatter;
final class NullOutputFormatterStyle implements OutputFormatterStyleInterface
{
public function apply(string $text): string
{
return $text;
}
public function setBackground(string $color = null): void
{
}
public function setForeground(string $color = null): void
{
}
public function setOption(string $option): void
{
}
public function setOptions(array $options): void
{
}
public function unsetOption(string $option): void
{
}
}
<?php
namespace Symfony\Component\Console\Formatter;
interface OutputFormatterInterface
{
public function setDecorated(bool $decorated);
public function isDecorated();
public function setStyle(string $name, OutputFormatterStyleInterface $style);
public function hasStyle(string $name);
public function getStyle(string $name);
public function format(?string $message);
}
<?php
namespace Symfony\Component\Console\Formatter;
use Symfony\Component\Console\Exception\InvalidArgumentException;
class OutputFormatter implements WrappableOutputFormatterInterface
{
private $decorated;
private $styles = [];
private $styleStack;
public function __clone()
{
$this->styleStack = clone $this->styleStack;
foreach ($this->styles as $key => $value) {
$this->styles[$key] = clone $value;
}
}
public static function escape(string $text)
{
$text = preg_replace('/([^\\\\]|^)([<>])/', '$1\\\\$2', $text);
return self::escapeTrailingBackslash($text);
}
public static function escapeTrailingBackslash(string $text): string
{
if (str_ends_with($text, '\\')) {
$len = \strlen($text);
$text = rtrim($text, '\\');
$text = str_replace("\0", '', $text);
$text .= str_repeat("\0", $len - \strlen($text));
}
return $text;
}
public function __construct(bool $decorated = false, array $styles = [])
{
$this->decorated = $decorated;
$this->setStyle('error', new OutputFormatterStyle('white', 'red'));
$this->setStyle('info', new OutputFormatterStyle('green'));
$this->setStyle('comment', new OutputFormatterStyle('yellow'));
$this->setStyle('question', new OutputFormatterStyle('black', 'cyan'));
foreach ($styles as $name => $style) {
$this->setStyle($name, $style);
}
$this->styleStack = new OutputFormatterStyleStack();
}
public function setDecorated(bool $decorated)
{
$this->decorated = $decorated;
}
public function isDecorated()
{
return $this->decorated;
}
public function setStyle(string $name, OutputFormatterStyleInterface $style)
{
$this->styles[strtolower($name)] = $style;
}
public function hasStyle(string $name)
{
return isset($this->styles[strtolower($name)]);
}
public function getStyle(string $name)
{
if (!$this->hasStyle($name)) {
throw new InvalidArgumentException(sprintf('Undefined style: "%s".', $name));
}
return $this->styles[strtolower($name)];
}
public function format(?string $message)
{
return $this->formatAndWrap($message, 0);
}
public function formatAndWrap(?string $message, int $width)
{
if (null === $message) {
return '';
}
$offset = 0;
$output = '';
$openTagRegex = '[a-z](?:[^\\\\<>]*+ | \\\\.)*';
$closeTagRegex = '[a-z][^<>]*+';
$currentLineLength = 0;
preg_match_all("#<(($openTagRegex) | /($closeTagRegex)?)>#ix", $message, $matches, \PREG_OFFSET_CAPTURE);
foreach ($matches[0] as $i => $match) {
$pos = $match[1];
$text = $match[0];
if (0 != $pos && '\\' == $message[$pos - 1]) {
continue;
}
$output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset), $output, $width, $currentLineLength);
$offset = $pos + \strlen($text);
if ($open = '/' != $text[1]) {
$tag = $matches[1][$i][0];
} else {
$tag = $matches[3][$i][0] ?? '';
}
if (!$open && !$tag) {
$this->styleStack->pop();
} elseif (null === $style = $this->createStyleFromString($tag)) {
$output .= $this->applyCurrentStyle($text, $output, $width, $currentLineLength);
} elseif ($open) {
$this->styleStack->push($style);
} else {
$this->styleStack->pop($style);
}
}
$output .= $this->applyCurrentStyle(substr($message, $offset), $output, $width, $currentLineLength);
return strtr($output, ["\0" => '\\', '\\<' => '<', '\\>' => '>']);
}
public function getStyleStack()
{
return $this->styleStack;
}
private function createStyleFromString(string $string): ?OutputFormatterStyleInterface
{
if (isset($this->styles[$string])) {
return $this->styles[$string];
}
if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', $string, $matches, \PREG_SET_ORDER)) {
return null;
}
$style = new OutputFormatterStyle();
foreach ($matches as $match) {
array_shift($match);
$match[0] = strtolower($match[0]);
if ('fg' == $match[0]) {
$style->setForeground(strtolower($match[1]));
} elseif ('bg' == $match[0]) {
$style->setBackground(strtolower($match[1]));
} elseif ('href' === $match[0]) {
$url = preg_replace('{\\\\([<>])}', '$1', $match[1]);
$style->setHref($url);
} elseif ('options' === $match[0]) {
preg_match_all('([^,;]+)', strtolower($match[1]), $options);
$options = array_shift($options);
foreach ($options as $option) {
$style->setOption($option);
}
} else {
return null;
}
}
return $style;
}
private function applyCurrentStyle(string $text, string $current, int $width, int &$currentLineLength): string
{
if ('' === $text) {
return '';
}
if (!$width) {
return $this->isDecorated() ? $this->styleStack->getCurrent()->apply($text) : $text;
}
if (!$currentLineLength && '' !== $current) {
$text = ltrim($text);
}
if ($currentLineLength) {
$prefix = substr($text, 0, $i = $width - $currentLineLength)."\n";
$text = substr($text, $i);
} else {
$prefix = '';
}
preg_match('~(\\n)$~', $text, $matches);
$text = $prefix.preg_replace('~([^\\n]{'.$width.'})\\ *~', "\$1\n", $text);
$text = rtrim($text, "\n").($matches[1] ?? '');
if (!$currentLineLength && '' !== $current && "\n" !== substr($current, -1)) {
$text = "\n".$text;
}
$lines = explode("\n", $text);
foreach ($lines as $line) {
$currentLineLength += \strlen($line);
if ($width <= $currentLineLength) {
$currentLineLength = 0;
}
}
if ($this->isDecorated()) {
foreach ($lines as $i => $line) {
$lines[$i] = $this->styleStack->getCurrent()->apply($line);
}
}
return implode("\n", $lines);
}
}
<?php
namespace Symfony\Component\Console\Formatter;
final class NullOutputFormatter implements OutputFormatterInterface
{
private $style;
public function format(?string $message): ?string
{
return null;
}
public function getStyle(string $name): OutputFormatterStyleInterface
{
return $this->style ?? $this->style = new NullOutputFormatterStyle();
}
public function hasStyle(string $name): bool
{
return false;
}
public function isDecorated(): bool
{
return false;
}
public function setDecorated(bool $decorated): void
{
}
public function setStyle(string $name, OutputFormatterStyleInterface $style): void
{
}
}
<?php
namespace Symfony\Component\Console\Formatter;
interface WrappableOutputFormatterInterface extends OutputFormatterInterface
{
public function formatAndWrap(?string $message, int $width);
}
<?php
namespace Symfony\Component\Console\Formatter;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Contracts\Service\ResetInterface;
class OutputFormatterStyleStack implements ResetInterface
{
private $styles;
private $emptyStyle;
public function __construct(OutputFormatterStyleInterface $emptyStyle = null)
{
$this->emptyStyle = $emptyStyle ?? new OutputFormatterStyle();
$this->reset();
}
public function reset()
{
$this->styles = [];
}
public function push(OutputFormatterStyleInterface $style)
{
$this->styles[] = $style;
}
public function pop(OutputFormatterStyleInterface $style = null)
{
if (empty($this->styles)) {
return $this->emptyStyle;
}
if (null === $style) {
return array_pop($this->styles);
}
foreach (array_reverse($this->styles, true) as $index => $stackedStyle) {
if ($style->apply('') === $stackedStyle->apply('')) {
$this->styles = \array_slice($this->styles, 0, $index);
return $stackedStyle;
}
}
throw new InvalidArgumentException('Incorrectly nested style tag found.');
}
public function getCurrent()
{
if (empty($this->styles)) {
return $this->emptyStyle;
}
return $this->styles[\count($this->styles) - 1];
}
public function setEmptyStyle(OutputFormatterStyleInterface $emptyStyle)
{
$this->emptyStyle = $emptyStyle;
return $this;
}
public function getEmptyStyle()
{
return $this->emptyStyle;
}
}
<?php
namespace Symfony\Component\Console;
use Symfony\Component\Console\Exception\InvalidArgumentException;
final class Color
{
private const COLORS = [
'black' => 0,
'red' => 1,
'green' => 2,
'yellow' => 3,
'blue' => 4,
'magenta' => 5,
'cyan' => 6,
'white' => 7,
'default' => 9,
];
private const BRIGHT_COLORS = [
'gray' => 0,
'bright-red' => 1,
'bright-green' => 2,
'bright-yellow' => 3,
'bright-blue' => 4,
'bright-magenta' => 5,
'bright-cyan' => 6,
'bright-white' => 7,
];
private const AVAILABLE_OPTIONS = [
'bold' => ['set' => 1, 'unset' => 22],
'underscore' => ['set' => 4, 'unset' => 24],
'blink' => ['set' => 5, 'unset' => 25],
'reverse' => ['set' => 7, 'unset' => 27],
'conceal' => ['set' => 8, 'unset' => 28],
];
private $foreground;
private $background;
private $options = [];
public function __construct(string $foreground = '', string $background = '', array $options = [])
{
$this->foreground = $this->parseColor($foreground);
$this->background = $this->parseColor($background, true);
foreach ($options as $option) {
if (!isset(self::AVAILABLE_OPTIONS[$option])) {
throw new InvalidArgumentException(sprintf('Invalid option specified: "%s". Expected one of (%s).', $option, implode(', ', array_keys(self::AVAILABLE_OPTIONS))));
}
$this->options[$option] = self::AVAILABLE_OPTIONS[$option];
}
}
public function apply(string $text): string
{
return $this->set().$text.$this->unset();
}
public function set(): string
{
$setCodes = [];
if ('' !== $this->foreground) {
$setCodes[] = $this->foreground;
}
if ('' !== $this->background) {
$setCodes[] = $this->background;
}
foreach ($this->options as $option) {
$setCodes[] = $option['set'];
}
if (0 === \count($setCodes)) {
return '';
}
return sprintf("\033[%sm", implode(';', $setCodes));
}
public function unset(): string
{
$unsetCodes = [];
if ('' !== $this->foreground) {
$unsetCodes[] = 39;
}
if ('' !== $this->background) {
$unsetCodes[] = 49;
}
foreach ($this->options as $option) {
$unsetCodes[] = $option['unset'];
}
if (0 === \count($unsetCodes)) {
return '';
}
return sprintf("\033[%sm", implode(';', $unsetCodes));
}
private function parseColor(string $color, bool $background = false): string
{
if ('' === $color) {
return '';
}
if ('#' === $color[0]) {
$color = substr($color, 1);
if (3 === \strlen($color)) {
$color = $color[0].$color[0].$color[1].$color[1].$color[2].$color[2];
}
if (6 !== \strlen($color)) {
throw new InvalidArgumentException(sprintf('Invalid "%s" color.', $color));
}
return ($background ? '4' : '3').$this->convertHexColorToAnsi(hexdec($color));
}
if (isset(self::COLORS[$color])) {
return ($background ? '4' : '3').self::COLORS[$color];
}
if (isset(self::BRIGHT_COLORS[$color])) {
return ($background ? '10' : '9').self::BRIGHT_COLORS[$color];
}
throw new InvalidArgumentException(sprintf('Invalid "%s" color; expected one of (%s).', $color, implode(', ', array_merge(array_keys(self::COLORS), array_keys(self::BRIGHT_COLORS)))));
}
private function convertHexColorToAnsi(int $color): string
{
$r = ($color >> 16) & 255;
$g = ($color >> 8) & 255;
$b = $color & 255;
if ('truecolor' !== getenv('COLORTERM')) {
return (string) $this->degradeHexColorToAnsi($r, $g, $b);
}
return sprintf('8;2;%d;%d;%d', $r, $g, $b);
}
private function degradeHexColorToAnsi(int $r, int $g, int $b): int
{
if (0 === round($this->getSaturation($r, $g, $b) / 50)) {
return 0;
}
return (round($b / 255) << 2) | (round($g / 255) << 1) | round($r / 255);
}
private function getSaturation(int $r, int $g, int $b): int
{
$r = $r / 255;
$g = $g / 255;
$b = $b / 255;
$v = max($r, $g, $b);
if (0 === $diff = $v - min($r, $g, $b)) {
return 0;
}
return (int) $diff * 100 / $v;
}
}
<?php
namespace Symfony\Component\Console\SignalRegistry;
final class SignalRegistry
{
private $signalHandlers = [];
public function __construct()
{
if (\function_exists('pcntl_async_signals')) {
pcntl_async_signals(true);
}
}
public function register(int $signal, callable $signalHandler): void
{
if (!isset($this->signalHandlers[$signal])) {
$previousCallback = pcntl_signal_get_handler($signal);
if (\is_callable($previousCallback)) {
$this->signalHandlers[$signal][] = $previousCallback;
}
}
$this->signalHandlers[$signal][] = $signalHandler;
pcntl_signal($signal, [$this, 'handle']);
}
public static function isSupported(): bool
{
if (!\function_exists('pcntl_signal')) {
return false;
}
if (\in_array('pcntl_signal', explode(',', \ini_get('disable_functions')))) {
return false;
}
return true;
}
public function handle(int $signal): void
{
$count = \count($this->signalHandlers[$signal]);
foreach ($this->signalHandlers[$signal] as $i => $signalHandler) {
$hasNext = $i !== $count - 1;
$signalHandler($signal, $hasNext);
}
}
}
<?php
namespace Symfony\Component\Console;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class SingleCommandApplication extends Command
{
private $version = 'UNKNOWN';
private $autoExit = true;
private $running = false;
public function setVersion(string $version): self
{
$this->version = $version;
return $this;
}
public function setAutoExit(bool $autoExit): self
{
$this->autoExit = $autoExit;
return $this;
}
public function run(InputInterface $input = null, OutputInterface $output = null): int
{
if ($this->running) {
return parent::run($input, $output);
}
$application = new Application($this->getName() ?: 'UNKNOWN', $this->version);
$application->setAutoExit($this->autoExit);
$this->setName($_SERVER['argv'][0]);
$application->add($this);
$application->setDefaultCommand($this->getName(), true);
$this->running = true;
try {
$ret = $application->run($input, $output);
} finally {
$this->running = false;
}
return $ret ?? 1;
}
}
<?php
namespace Symfony\Component\Console\CommandLoader;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\CommandNotFoundException;
interface CommandLoaderInterface
{
public function get(string $name);
public function has(string $name);
public function getNames();
}
<?php
namespace Symfony\Component\Console\CommandLoader;
use Psr\Container\ContainerInterface;
use Symfony\Component\Console\Exception\CommandNotFoundException;
class ContainerCommandLoader implements CommandLoaderInterface
{
private $container;
private $commandMap;
public function __construct(ContainerInterface $container, array $commandMap)
{
$this->container = $container;
$this->commandMap = $commandMap;
}
public function get(string $name)
{
if (!$this->has($name)) {
throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name));
}
return $this->container->get($this->commandMap[$name]);
}
public function has(string $name)
{
return isset($this->commandMap[$name]) && $this->container->has($this->commandMap[$name]);
}
public function getNames()
{
return array_keys($this->commandMap);
}
}
<?php
namespace Symfony\Component\Console\CommandLoader;
use Symfony\Component\Console\Exception\CommandNotFoundException;
class FactoryCommandLoader implements CommandLoaderInterface
{
private $factories;
public function __construct(array $factories)
{
$this->factories = $factories;
}
public function has(string $name)
{
return isset($this->factories[$name]);
}
public function get(string $name)
{
if (!isset($this->factories[$name])) {
throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name));
}
$factory = $this->factories[$name];
return $factory();
}
public function getNames()
{
return array_keys($this->factories);
}
}
<?php
namespace Symfony\Component\Console\Descriptor;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
class TextDescriptor extends Descriptor
{
protected function describeInputArgument(InputArgument $argument, array $options = [])
{
if (null !== $argument->getDefault() && (!\is_array($argument->getDefault()) || \count($argument->getDefault()))) {
$default = sprintf('<comment> [default: %s]</comment>', $this->formatDefaultValue($argument->getDefault()));
} else {
$default = '';
}
$totalWidth = $options['total_width'] ?? Helper::width($argument->getName());
$spacingWidth = $totalWidth - \strlen($argument->getName());
$this->writeText(sprintf(' <info>%s</info> %s%s%s',
$argument->getName(),
str_repeat(' ', $spacingWidth),
preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 4), $argument->getDescription()),
$default
), $options);
}
protected function describeInputOption(InputOption $option, array $options = [])
{
if ($option->acceptValue() && null !== $option->getDefault() && (!\is_array($option->getDefault()) || \count($option->getDefault()))) {
$default = sprintf('<comment> [default: %s]</comment>', $this->formatDefaultValue($option->getDefault()));
} else {
$default = '';
}
$value = '';
if ($option->acceptValue()) {
$value = '='.strtoupper($option->getName());
if ($option->isValueOptional()) {
$value = '['.$value.']';
}
}
$totalWidth = $options['total_width'] ?? $this->calculateTotalWidthForOptions([$option]);
$synopsis = sprintf('%s%s',
$option->getShortcut() ? sprintf('-%s, ', $option->getShortcut()) : ' ',
sprintf($option->isNegatable() ? '--%1$s|--no-%1$s' : '--%1$s%2$s', $option->getName(), $value)
);
$spacingWidth = $totalWidth - Helper::width($synopsis);
$this->writeText(sprintf(' <info>%s</info> %s%s%s%s',
$synopsis,
str_repeat(' ', $spacingWidth),
preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 4), $option->getDescription()),
$default,
$option->isArray() ? '<comment> (multiple values allowed)</comment>' : ''
), $options);
}
protected function describeInputDefinition(InputDefinition $definition, array $options = [])
{
$totalWidth = $this->calculateTotalWidthForOptions($definition->getOptions());
foreach ($definition->getArguments() as $argument) {
$totalWidth = max($totalWidth, Helper::width($argument->getName()));
}
if ($definition->getArguments()) {
$this->writeText('<comment>Arguments:</comment>', $options);
$this->writeText("\n");
foreach ($definition->getArguments() as $argument) {
$this->describeInputArgument($argument, array_merge($options, ['total_width' => $totalWidth]));
$this->writeText("\n");
}
}
if ($definition->getArguments() && $definition->getOptions()) {
$this->writeText("\n");
}
if ($definition->getOptions()) {
$laterOptions = [];
$this->writeText('<comment>Options:</comment>', $options);
foreach ($definition->getOptions() as $option) {
if (\strlen($option->getShortcut() ?? '') > 1) {
$laterOptions[] = $option;
continue;
}
$this->writeText("\n");
$this->describeInputOption($option, array_merge($options, ['total_width' => $totalWidth]));
}
foreach ($laterOptions as $option) {
$this->writeText("\n");
$this->describeInputOption($option, array_merge($options, ['total_width' => $totalWidth]));
}
}
}
protected function describeCommand(Command $command, array $options = [])
{
$command->mergeApplicationDefinition(false);
if ($description = $command->getDescription()) {
$this->writeText('<comment>Description:</comment>', $options);
$this->writeText("\n");
$this->writeText(' '.$description);
$this->writeText("\n\n");
}
$this->writeText('<comment>Usage:</comment>', $options);
foreach (array_merge([$command->getSynopsis(true)], $command->getAliases(), $command->getUsages()) as $usage) {
$this->writeText("\n");
$this->writeText(' '.OutputFormatter::escape($usage), $options);
}
$this->writeText("\n");
$definition = $command->getDefinition();
if ($definition->getOptions() || $definition->getArguments()) {
$this->writeText("\n");
$this->describeInputDefinition($definition, $options);
$this->writeText("\n");
}
$help = $command->getProcessedHelp();
if ($help && $help !== $description) {
$this->writeText("\n");
$this->writeText('<comment>Help:</comment>', $options);
$this->writeText("\n");
$this->writeText(' '.str_replace("\n", "\n ", $help), $options);
$this->writeText("\n");
}
}
protected function describeApplication(Application $application, array $options = [])
{
$describedNamespace = $options['namespace'] ?? null;
$description = new ApplicationDescription($application, $describedNamespace);
if (isset($options['raw_text']) && $options['raw_text']) {
$width = $this->getColumnWidth($description->getCommands());
foreach ($description->getCommands() as $command) {
$this->writeText(sprintf("%-{$width}s %s", $command->getName(), $command->getDescription()), $options);
$this->writeText("\n");
}
} else {
if ('' != $help = $application->getHelp()) {
$this->writeText("$help\n\n", $options);
}
$this->writeText("<comment>Usage:</comment>\n", $options);
$this->writeText(" command [options] [arguments]\n\n", $options);
$this->describeInputDefinition(new InputDefinition($application->getDefinition()->getOptions()), $options);
$this->writeText("\n");
$this->writeText("\n");
$commands = $description->getCommands();
$namespaces = $description->getNamespaces();
if ($describedNamespace && $namespaces) {
$describedNamespaceInfo = reset($namespaces);
foreach ($describedNamespaceInfo['commands'] as $name) {
$commands[$name] = $description->getCommand($name);
}
}
$width = $this->getColumnWidth(array_merge(...array_values(array_map(function ($namespace) use ($commands) {
return array_intersect($namespace['commands'], array_keys($commands));
}, array_values($namespaces)))));
if ($describedNamespace) {
$this->writeText(sprintf('<comment>Available commands for the "%s" namespace:</comment>', $describedNamespace), $options);
} else {
$this->writeText('<comment>Available commands:</comment>', $options);
}
foreach ($namespaces as $namespace) {
$namespace['commands'] = array_filter($namespace['commands'], function ($name) use ($commands) {
return isset($commands[$name]);
});
if (!$namespace['commands']) {
continue;
}
if (!$describedNamespace && ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) {
$this->writeText("\n");
$this->writeText(' <comment>'.$namespace['id'].'</comment>', $options);
}
foreach ($namespace['commands'] as $name) {
$this->writeText("\n");
$spacingWidth = $width - Helper::width($name);
$command = $commands[$name];
$commandAliases = $name === $command->getName() ? $this->getCommandAliasesText($command) : '';
$this->writeText(sprintf(' <info>%s</info>%s%s', $name, str_repeat(' ', $spacingWidth), $commandAliases.$command->getDescription()), $options);
}
}
$this->writeText("\n");
}
}
private function writeText(string $content, array $options = [])
{
$this->write(
isset($options['raw_text']) && $options['raw_text'] ? strip_tags($content) : $content,
isset($options['raw_output']) ? !$options['raw_output'] : true
);
}
private function getCommandAliasesText(Command $command): string
{
$text = '';
$aliases = $command->getAliases();
if ($aliases) {
$text = '['.implode('|', $aliases).'] ';
}
return $text;
}
private function formatDefaultValue($default): string
{
if (\INF === $default) {
return 'INF';
}
if (\is_string($default)) {
$default = OutputFormatter::escape($default);
} elseif (\is_array($default)) {
foreach ($default as $key => $value) {
if (\is_string($value)) {
$default[$key] = OutputFormatter::escape($value);
}
}
}
return str_replace('\\\\', '\\', json_encode($default, \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE));
}
private function getColumnWidth(array $commands): int
{
$widths = [];
foreach ($commands as $command) {
if ($command instanceof Command) {
$widths[] = Helper::width($command->getName());
foreach ($command->getAliases() as $alias) {
$widths[] = Helper::width($alias);
}
} else {
$widths[] = Helper::width($command);
}
}
return $widths ? max($widths) + 2 : 0;
}
private function calculateTotalWidthForOptions(array $options): int
{
$totalWidth = 0;
foreach ($options as $option) {
$nameLength = 1 + max(Helper::width($option->getShortcut()), 1) + 4 + Helper::width($option->getName());
if ($option->isNegatable()) {
$nameLength += 6 + Helper::width($option->getName());
} elseif ($option->acceptValue()) {
$valueLength = 1 + Helper::width($option->getName());
$valueLength += $option->isValueOptional() ? 2 : 0;
$nameLength += $valueLength;
}
$totalWidth = max($totalWidth, $nameLength);
}
return $totalWidth;
}
}
<?php
namespace Symfony\Component\Console\Descriptor;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\CommandNotFoundException;
class ApplicationDescription
{
public const GLOBAL_NAMESPACE = '_global';
private $application;
private $namespace;
private $showHidden;
private $namespaces;
private $commands;
private $aliases;
public function __construct(Application $application, string $namespace = null, bool $showHidden = false)
{
$this->application = $application;
$this->namespace = $namespace;
$this->showHidden = $showHidden;
}
public function getNamespaces(): array
{
if (null === $this->namespaces) {
$this->inspectApplication();
}
return $this->namespaces;
}
public function getCommands(): array
{
if (null === $this->commands) {
$this->inspectApplication();
}
return $this->commands;
}
public function getCommand(string $name): Command
{
if (!isset($this->commands[$name]) && !isset($this->aliases[$name])) {
throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name));
}
return $this->commands[$name] ?? $this->aliases[$name];
}
private function inspectApplication()
{
$this->commands = [];
$this->namespaces = [];
$all = $this->application->all($this->namespace ? $this->application->findNamespace($this->namespace) : null);
foreach ($this->sortCommands($all) as $namespace => $commands) {
$names = [];
foreach ($commands as $name => $command) {
if (!$command->getName() || (!$this->showHidden && $command->isHidden())) {
continue;
}
if ($command->getName() === $name) {
$this->commands[$name] = $command;
} else {
$this->aliases[$name] = $command;
}
$names[] = $name;
}
$this->namespaces[$namespace] = ['id' => $namespace, 'commands' => $names];
}
}
private function sortCommands(array $commands): array
{
$namespacedCommands = [];
$globalCommands = [];
$sortedCommands = [];
foreach ($commands as $name => $command) {
$key = $this->application->extractNamespace($name, 1);
if (\in_array($key, ['', self::GLOBAL_NAMESPACE], true)) {
$globalCommands[$name] = $command;
} else {
$namespacedCommands[$key][$name] = $command;
}
}
if ($globalCommands) {
ksort($globalCommands);
$sortedCommands[self::GLOBAL_NAMESPACE] = $globalCommands;
}
if ($namespacedCommands) {
ksort($namespacedCommands, \SORT_STRING);
foreach ($namespacedCommands as $key => $commandsSet) {
ksort($commandsSet);
$sortedCommands[$key] = $commandsSet;
}
}
return $sortedCommands;
}
}
<?php
namespace Symfony\Component\Console\Descriptor;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
class XmlDescriptor extends Descriptor
{
public function getInputDefinitionDocument(InputDefinition $definition): \DOMDocument
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($definitionXML = $dom->createElement('definition'));
$definitionXML->appendChild($argumentsXML = $dom->createElement('arguments'));
foreach ($definition->getArguments() as $argument) {
$this->appendDocument($argumentsXML, $this->getInputArgumentDocument($argument));
}
$definitionXML->appendChild($optionsXML = $dom->createElement('options'));
foreach ($definition->getOptions() as $option) {
$this->appendDocument($optionsXML, $this->getInputOptionDocument($option));
}
return $dom;
}
public function getCommandDocument(Command $command, bool $short = false): \DOMDocument
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($commandXML = $dom->createElement('command'));
$commandXML->setAttribute('id', $command->getName());
$commandXML->setAttribute('name', $command->getName());
$commandXML->setAttribute('hidden', $command->isHidden() ? 1 : 0);
$commandXML->appendChild($usagesXML = $dom->createElement('usages'));
$commandXML->appendChild($descriptionXML = $dom->createElement('description'));
$descriptionXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getDescription())));
if ($short) {
foreach ($command->getAliases() as $usage) {
$usagesXML->appendChild($dom->createElement('usage', $usage));
}
} else {
$command->mergeApplicationDefinition(false);
foreach (array_merge([$command->getSynopsis()], $command->getAliases(), $command->getUsages()) as $usage) {
$usagesXML->appendChild($dom->createElement('usage', $usage));
}
$commandXML->appendChild($helpXML = $dom->createElement('help'));
$helpXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getProcessedHelp())));
$definitionXML = $this->getInputDefinitionDocument($command->getDefinition());
$this->appendDocument($commandXML, $definitionXML->getElementsByTagName('definition')->item(0));
}
return $dom;
}
public function getApplicationDocument(Application $application, string $namespace = null, bool $short = false): \DOMDocument
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($rootXml = $dom->createElement('symfony'));
if ('UNKNOWN' !== $application->getName()) {
$rootXml->setAttribute('name', $application->getName());
if ('UNKNOWN' !== $application->getVersion()) {
$rootXml->setAttribute('version', $application->getVersion());
}
}
$rootXml->appendChild($commandsXML = $dom->createElement('commands'));
$description = new ApplicationDescription($application, $namespace, true);
if ($namespace) {
$commandsXML->setAttribute('namespace', $namespace);
}
foreach ($description->getCommands() as $command) {
$this->appendDocument($commandsXML, $this->getCommandDocument($command, $short));
}
if (!$namespace) {
$rootXml->appendChild($namespacesXML = $dom->createElement('namespaces'));
foreach ($description->getNamespaces() as $namespaceDescription) {
$namespacesXML->appendChild($namespaceArrayXML = $dom->createElement('namespace'));
$namespaceArrayXML->setAttribute('id', $namespaceDescription['id']);
foreach ($namespaceDescription['commands'] as $name) {
$namespaceArrayXML->appendChild($commandXML = $dom->createElement('command'));
$commandXML->appendChild($dom->createTextNode($name));
}
}
}
return $dom;
}
protected function describeInputArgument(InputArgument $argument, array $options = [])
{
$this->writeDocument($this->getInputArgumentDocument($argument));
}
protected function describeInputOption(InputOption $option, array $options = [])
{
$this->writeDocument($this->getInputOptionDocument($option));
}
protected function describeInputDefinition(InputDefinition $definition, array $options = [])
{
$this->writeDocument($this->getInputDefinitionDocument($definition));
}
protected function describeCommand(Command $command, array $options = [])
{
$this->writeDocument($this->getCommandDocument($command, $options['short'] ?? false));
}
protected function describeApplication(Application $application, array $options = [])
{
$this->writeDocument($this->getApplicationDocument($application, $options['namespace'] ?? null, $options['short'] ?? false));
}
private function appendDocument(\DOMNode $parentNode, \DOMNode $importedParent)
{
foreach ($importedParent->childNodes as $childNode) {
$parentNode->appendChild($parentNode->ownerDocument->importNode($childNode, true));
}
}
private function writeDocument(\DOMDocument $dom)
{
$dom->formatOutput = true;
$this->write($dom->saveXML());
}
private function getInputArgumentDocument(InputArgument $argument): \DOMDocument
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($objectXML = $dom->createElement('argument'));
$objectXML->setAttribute('name', $argument->getName());
$objectXML->setAttribute('is_required', $argument->isRequired() ? 1 : 0);
$objectXML->setAttribute('is_array', $argument->isArray() ? 1 : 0);
$objectXML->appendChild($descriptionXML = $dom->createElement('description'));
$descriptionXML->appendChild($dom->createTextNode($argument->getDescription()));
$objectXML->appendChild($defaultsXML = $dom->createElement('defaults'));
$defaults = \is_array($argument->getDefault()) ? $argument->getDefault() : (\is_bool($argument->getDefault()) ? [var_export($argument->getDefault(), true)] : ($argument->getDefault() ? [$argument->getDefault()] : []));
foreach ($defaults as $default) {
$defaultsXML->appendChild($defaultXML = $dom->createElement('default'));
$defaultXML->appendChild($dom->createTextNode($default));
}
return $dom;
}
private function getInputOptionDocument(InputOption $option): \DOMDocument
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($objectXML = $dom->createElement('option'));
$objectXML->setAttribute('name', '--'.$option->getName());
$pos = strpos($option->getShortcut() ?? '', '|');
if (false !== $pos) {
$objectXML->setAttribute('shortcut', '-'.substr($option->getShortcut(), 0, $pos));
$objectXML->setAttribute('shortcuts', '-'.str_replace('|', '|-', $option->getShortcut()));
} else {
$objectXML->setAttribute('shortcut', $option->getShortcut() ? '-'.$option->getShortcut() : '');
}
$objectXML->setAttribute('accept_value', $option->acceptValue() ? 1 : 0);
$objectXML->setAttribute('is_value_required', $option->isValueRequired() ? 1 : 0);
$objectXML->setAttribute('is_multiple', $option->isArray() ? 1 : 0);
$objectXML->appendChild($descriptionXML = $dom->createElement('description'));
$descriptionXML->appendChild($dom->createTextNode($option->getDescription()));
if ($option->acceptValue()) {
$defaults = \is_array($option->getDefault()) ? $option->getDefault() : (\is_bool($option->getDefault()) ? [var_export($option->getDefault(), true)] : ($option->getDefault() ? [$option->getDefault()] : []));
$objectXML->appendChild($defaultsXML = $dom->createElement('defaults'));
if (!empty($defaults)) {
foreach ($defaults as $default) {
$defaultsXML->appendChild($defaultXML = $dom->createElement('default'));
$defaultXML->appendChild($dom->createTextNode($default));
}
}
}
if ($option->isNegatable()) {
$dom->appendChild($objectXML = $dom->createElement('option'));
$objectXML->setAttribute('name', '--no-'.$option->getName());
$objectXML->setAttribute('shortcut', '');
$objectXML->setAttribute('accept_value', 0);
$objectXML->setAttribute('is_value_required', 0);
$objectXML->setAttribute('is_multiple', 0);
$objectXML->appendChild($descriptionXML = $dom->createElement('description'));
$descriptionXML->appendChild($dom->createTextNode('Negate the "--'.$option->getName().'" option'));
}
return $dom;
}
}
<?php
namespace Symfony\Component\Console\Descriptor;
use Symfony\Component\Console\Output\OutputInterface;
interface DescriptorInterface
{
public function describe(OutputInterface $output, object $object, array $options = []);
}
<?php
namespace Symfony\Component\Console\Descriptor;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
abstract class Descriptor implements DescriptorInterface
{
protected $output;
public function describe(OutputInterface $output, object $object, array $options = [])
{
$this->output = $output;
switch (true) {
case $object instanceof InputArgument:
$this->describeInputArgument($object, $options);
break;
case $object instanceof InputOption:
$this->describeInputOption($object, $options);
break;
case $object instanceof InputDefinition:
$this->describeInputDefinition($object, $options);
break;
case $object instanceof Command:
$this->describeCommand($object, $options);
break;
case $object instanceof Application:
$this->describeApplication($object, $options);
break;
default:
throw new InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_debug_type($object)));
}
}
protected function write(string $content, bool $decorated = false)
{
$this->output->write($content, false, $decorated ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW);
}
abstract protected function describeInputArgument(InputArgument $argument, array $options = []);
abstract protected function describeInputOption(InputOption $option, array $options = []);
abstract protected function describeInputDefinition(InputDefinition $definition, array $options = []);
abstract protected function describeCommand(Command $command, array $options = []);
abstract protected function describeApplication(Application $application, array $options = []);
}
<?php
namespace Symfony\Component\Console\Descriptor;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
class JsonDescriptor extends Descriptor
{
protected function describeInputArgument(InputArgument $argument, array $options = [])
{
$this->writeData($this->getInputArgumentData($argument), $options);
}
protected function describeInputOption(InputOption $option, array $options = [])
{
$this->writeData($this->getInputOptionData($option), $options);
if ($option->isNegatable()) {
$this->writeData($this->getInputOptionData($option, true), $options);
}
}
protected function describeInputDefinition(InputDefinition $definition, array $options = [])
{
$this->writeData($this->getInputDefinitionData($definition), $options);
}
protected function describeCommand(Command $command, array $options = [])
{
$this->writeData($this->getCommandData($command, $options['short'] ?? false), $options);
}
protected function describeApplication(Application $application, array $options = [])
{
$describedNamespace = $options['namespace'] ?? null;
$description = new ApplicationDescription($application, $describedNamespace, true);
$commands = [];
foreach ($description->getCommands() as $command) {
$commands[] = $this->getCommandData($command, $options['short'] ?? false);
}
$data = [];
if ('UNKNOWN' !== $application->getName()) {
$data['application']['name'] = $application->getName();
if ('UNKNOWN' !== $application->getVersion()) {
$data['application']['version'] = $application->getVersion();
}
}
$data['commands'] = $commands;
if ($describedNamespace) {
$data['namespace'] = $describedNamespace;
} else {
$data['namespaces'] = array_values($description->getNamespaces());
}
$this->writeData($data, $options);
}
private function writeData(array $data, array $options)
{
$flags = $options['json_encoding'] ?? 0;
$this->write(json_encode($data, $flags));
}
private function getInputArgumentData(InputArgument $argument): array
{
return [
'name' => $argument->getName(),
'is_required' => $argument->isRequired(),
'is_array' => $argument->isArray(),
'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $argument->getDescription()),
'default' => \INF === $argument->getDefault() ? 'INF' : $argument->getDefault(),
];
}
private function getInputOptionData(InputOption $option, bool $negated = false): array
{
return $negated ? [
'name' => '--no-'.$option->getName(),
'shortcut' => '',
'accept_value' => false,
'is_value_required' => false,
'is_multiple' => false,
'description' => 'Negate the "--'.$option->getName().'" option',
'default' => false,
] : [
'name' => '--'.$option->getName(),
'shortcut' => $option->getShortcut() ? '-'.str_replace('|', '|-', $option->getShortcut()) : '',
'accept_value' => $option->acceptValue(),
'is_value_required' => $option->isValueRequired(),
'is_multiple' => $option->isArray(),
'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $option->getDescription()),
'default' => \INF === $option->getDefault() ? 'INF' : $option->getDefault(),
];
}
private function getInputDefinitionData(InputDefinition $definition): array
{
$inputArguments = [];
foreach ($definition->getArguments() as $name => $argument) {
$inputArguments[$name] = $this->getInputArgumentData($argument);
}
$inputOptions = [];
foreach ($definition->getOptions() as $name => $option) {
$inputOptions[$name] = $this->getInputOptionData($option);
if ($option->isNegatable()) {
$inputOptions['no-'.$name] = $this->getInputOptionData($option, true);
}
}
return ['arguments' => $inputArguments, 'options' => $inputOptions];
}
private function getCommandData(Command $command, bool $short = false): array
{
$data = [
'name' => $command->getName(),
'description' => $command->getDescription(),
];
if ($short) {
$data += [
'usage' => $command->getAliases(),
];
} else {
$command->mergeApplicationDefinition(false);
$data += [
'usage' => array_merge([$command->getSynopsis()], $command->getUsages(), $command->getAliases()),
'help' => $command->getProcessedHelp(),
'definition' => $this->getInputDefinitionData($command->getDefinition()),
];
}
$data['hidden'] = $command->isHidden();
return $data;
}
}
<?php
namespace Symfony\Component\Console\Descriptor;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class MarkdownDescriptor extends Descriptor
{
public function describe(OutputInterface $output, object $object, array $options = [])
{
$decorated = $output->isDecorated();
$output->setDecorated(false);
parent::describe($output, $object, $options);
$output->setDecorated($decorated);
}
protected function write(string $content, bool $decorated = true)
{
parent::write($content, $decorated);
}
protected function describeInputArgument(InputArgument $argument, array $options = [])
{
$this->write(
'#### `'.($argument->getName() ?: '<none>')."`\n\n"
.($argument->getDescription() ? preg_replace('/\s*[\r\n]\s*/', "\n", $argument->getDescription())."\n\n" : '')
.'* Is required: '.($argument->isRequired() ? 'yes' : 'no')."\n"
.'* Is array: '.($argument->isArray() ? 'yes' : 'no')."\n"
.'* Default: `'.str_replace("\n", '', var_export($argument->getDefault(), true)).'`'
);
}
protected function describeInputOption(InputOption $option, array $options = [])
{
$name = '--'.$option->getName();
if ($option->isNegatable()) {
$name .= '|--no-'.$option->getName();
}
if ($option->getShortcut()) {
$name .= '|-'.str_replace('|', '|-', $option->getShortcut()).'';
}
$this->write(
'#### `'.$name.'`'."\n\n"
.($option->getDescription() ? preg_replace('/\s*[\r\n]\s*/', "\n", $option->getDescription())."\n\n" : '')
.'* Accept value: '.($option->acceptValue() ? 'yes' : 'no')."\n"
.'* Is value required: '.($option->isValueRequired() ? 'yes' : 'no')."\n"
.'* Is multiple: '.($option->isArray() ? 'yes' : 'no')."\n"
.'* Is negatable: '.($option->isNegatable() ? 'yes' : 'no')."\n"
.'* Default: `'.str_replace("\n", '', var_export($option->getDefault(), true)).'`'
);
}
protected function describeInputDefinition(InputDefinition $definition, array $options = [])
{
if ($showArguments = \count($definition->getArguments()) > 0) {
$this->write('### Arguments');
foreach ($definition->getArguments() as $argument) {
$this->write("\n\n");
if (null !== $describeInputArgument = $this->describeInputArgument($argument)) {
$this->write($describeInputArgument);
}
}
}
if (\count($definition->getOptions()) > 0) {
if ($showArguments) {
$this->write("\n\n");
}
$this->write('### Options');
foreach ($definition->getOptions() as $option) {
$this->write("\n\n");
if (null !== $describeInputOption = $this->describeInputOption($option)) {
$this->write($describeInputOption);
}
}
}
}
protected function describeCommand(Command $command, array $options = [])
{
if ($options['short'] ?? false) {
$this->write(
'`'.$command->getName()."`\n"
.str_repeat('-', Helper::width($command->getName()) + 2)."\n\n"
.($command->getDescription() ? $command->getDescription()."\n\n" : '')
.'### Usage'."\n\n"
.array_reduce($command->getAliases(), function ($carry, $usage) {
return $carry.'* `'.$usage.'`'."\n";
})
);
return;
}
$command->mergeApplicationDefinition(false);
$this->write(
'`'.$command->getName()."`\n"
.str_repeat('-', Helper::width($command->getName()) + 2)."\n\n"
.($command->getDescription() ? $command->getDescription()."\n\n" : '')
.'### Usage'."\n\n"
.array_reduce(array_merge([$command->getSynopsis()], $command->getAliases(), $command->getUsages()), function ($carry, $usage) {
return $carry.'* `'.$usage.'`'."\n";
})
);
if ($help = $command->getProcessedHelp()) {
$this->write("\n");
$this->write($help);
}
$definition = $command->getDefinition();
if ($definition->getOptions() || $definition->getArguments()) {
$this->write("\n\n");
$this->describeInputDefinition($definition);
}
}
protected function describeApplication(Application $application, array $options = [])
{
$describedNamespace = $options['namespace'] ?? null;
$description = new ApplicationDescription($application, $describedNamespace);
$title = $this->getApplicationTitle($application);
$this->write($title."\n".str_repeat('=', Helper::width($title)));
foreach ($description->getNamespaces() as $namespace) {
if (ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) {
$this->write("\n\n");
$this->write('**'.$namespace['id'].':**');
}
$this->write("\n\n");
$this->write(implode("\n", array_map(function ($commandName) use ($description) {
return sprintf('* [`%s`](#%s)', $commandName, str_replace(':', '', $description->getCommand($commandName)->getName()));
}, $namespace['commands'])));
}
foreach ($description->getCommands() as $command) {
$this->write("\n\n");
if (null !== $describeCommand = $this->describeCommand($command, $options)) {
$this->write($describeCommand);
}
}
}
private function getApplicationTitle(Application $application): string
{
if ('UNKNOWN' !== $application->getName()) {
if ('UNKNOWN' !== $application->getVersion()) {
return sprintf('%s %s', $application->getName(), $application->getVersion());
}
return $application->getName();
}
return 'Console Tool';
}
}
<?php
namespace Symfony\Component\Console\Tester;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\ArrayInput;
class CommandTester
{
use TesterTrait;
private $command;
public function __construct(Command $command)
{
$this->command = $command;
}
public function execute(array $input, array $options = [])
{
if (!isset($input['command'])
&& (null !== $application = $this->command->getApplication())
&& $application->getDefinition()->hasArgument('command')
) {
$input = array_merge(['command' => $this->command->getName()], $input);
}
$this->input = new ArrayInput($input);
$this->input->setStream(self::createStream($this->inputs));
if (isset($options['interactive'])) {
$this->input->setInteractive($options['interactive']);
}
if (!isset($options['decorated'])) {
$options['decorated'] = false;
}
$this->initOutput($options);
return $this->statusCode = $this->command->run($this->input, $this->output);
}
}
<?php
namespace Symfony\Component\Console\Tester;
use PHPUnit\Framework\Assert;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\StreamOutput;
use Symfony\Component\Console\Tester\Constraint\CommandIsSuccessful;
trait TesterTrait
{
private $output;
private $inputs = [];
private $captureStreamsIndependently = false;
private $input;
private $statusCode;
public function getDisplay(bool $normalize = false)
{
if (null === $this->output) {
throw new \RuntimeException('Output not initialized, did you execute the command before requesting the display?');
}
rewind($this->output->getStream());
$display = stream_get_contents($this->output->getStream());
if ($normalize) {
$display = str_replace(\PHP_EOL, "\n", $display);
}
return $display;
}
public function getErrorOutput(bool $normalize = false)
{
if (!$this->captureStreamsIndependently) {
throw new \LogicException('The error output is not available when the tester is run without "capture_stderr_separately" option set.');
}
rewind($this->output->getErrorOutput()->getStream());
$display = stream_get_contents($this->output->getErrorOutput()->getStream());
if ($normalize) {
$display = str_replace(\PHP_EOL, "\n", $display);
}
return $display;
}
public function getInput()
{
return $this->input;
}
public function getOutput()
{
return $this->output;
}
public function getStatusCode()
{
if (null === $this->statusCode) {
throw new \RuntimeException('Status code not initialized, did you execute the command before requesting the status code?');
}
return $this->statusCode;
}
public function assertCommandIsSuccessful(string $message = ''): void
{
Assert::assertThat($this->statusCode, new CommandIsSuccessful(), $message);
}
public function setInputs(array $inputs)
{
$this->inputs = $inputs;
return $this;
}
private function initOutput(array $options)
{
$this->captureStreamsIndependently = \array_key_exists('capture_stderr_separately', $options) && $options['capture_stderr_separately'];
if (!$this->captureStreamsIndependently) {
$this->output = new StreamOutput(fopen('php://memory', 'w', false));
if (isset($options['decorated'])) {
$this->output->setDecorated($options['decorated']);
}
if (isset($options['verbosity'])) {
$this->output->setVerbosity($options['verbosity']);
}
} else {
$this->output = new ConsoleOutput(
$options['verbosity'] ?? ConsoleOutput::VERBOSITY_NORMAL,
$options['decorated'] ?? null
);
$errorOutput = new StreamOutput(fopen('php://memory', 'w', false));
$errorOutput->setFormatter($this->output->getFormatter());
$errorOutput->setVerbosity($this->output->getVerbosity());
$errorOutput->setDecorated($this->output->isDecorated());
$reflectedOutput = new \ReflectionObject($this->output);
$strErrProperty = $reflectedOutput->getProperty('stderr');
$strErrProperty->setAccessible(true);
$strErrProperty->setValue($this->output, $errorOutput);
$reflectedParent = $reflectedOutput->getParentClass();
$streamProperty = $reflectedParent->getProperty('stream');
$streamProperty->setAccessible(true);
$streamProperty->setValue($this->output, fopen('php://memory', 'w', false));
}
}
private static function createStream(array $inputs)
{
$stream = fopen('php://memory', 'r+', false);
foreach ($inputs as $input) {
fwrite($stream, $input.\PHP_EOL);
}
rewind($stream);
return $stream;
}
}
<?php
namespace Symfony\Component\Console\Tester;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
class ApplicationTester
{
use TesterTrait;
private $application;
public function __construct(Application $application)
{
$this->application = $application;
}
public function run(array $input, array $options = [])
{
$prevShellVerbosity = getenv('SHELL_VERBOSITY');
try {
$this->input = new ArrayInput($input);
if (isset($options['interactive'])) {
$this->input->setInteractive($options['interactive']);
}
if ($this->inputs) {
$this->input->setStream(self::createStream($this->inputs));
}
$this->initOutput($options);
return $this->statusCode = $this->application->run($this->input, $this->output);
} finally {
if (false === $prevShellVerbosity) {
if (\function_exists('putenv')) {
@putenv('SHELL_VERBOSITY');
}
unset($_ENV['SHELL_VERBOSITY']);
unset($_SERVER['SHELL_VERBOSITY']);
} else {
if (\function_exists('putenv')) {
@putenv('SHELL_VERBOSITY='.$prevShellVerbosity);
}
$_ENV['SHELL_VERBOSITY'] = $prevShellVerbosity;
$_SERVER['SHELL_VERBOSITY'] = $prevShellVerbosity;
}
}
}
}
<?php
namespace Symfony\Component\Console\Tester;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
class CommandCompletionTester
{
private $command;
public function __construct(Command $command)
{
$this->command = $command;
}
public function complete(array $input): array
{
$currentIndex = \count($input);
if ('' === end($input)) {
array_pop($input);
}
array_unshift($input, $this->command->getName());
$completionInput = CompletionInput::fromTokens($input, $currentIndex);
$completionInput->bind($this->command->getDefinition());
$suggestions = new CompletionSuggestions();
$this->command->complete($completionInput, $suggestions);
$options = [];
foreach ($suggestions->getOptionSuggestions() as $option) {
$options[] = '--'.$option->getName();
}
return array_map('strval', array_merge($options, $suggestions->getValueSuggestions()));
}
}
<?php
namespace Symfony\Component\Console\Tester\Constraint;
use PHPUnit\Framework\Constraint\Constraint;
use Symfony\Component\Console\Command\Command;
final class CommandIsSuccessful extends Constraint
{
public function toString(): string
{
return 'is successful';
}
protected function matches($other): bool
{
return Command::SUCCESS === $other;
}
protected function failureDescription($other): string
{
return 'the command '.$this->toString();
}
protected function additionalFailureDescription($other): string
{
$mapping = [
Command::FAILURE => 'Command failed.',
Command::INVALID => 'Command was invalid.',
];
return $mapping[$other] ?? sprintf('Command returned exit status %d.', $other);
}
}
<?php
namespace Symfony\Component\Console\Command;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
final class LazyCommand extends Command
{
private $command;
private $isEnabled;
public function __construct(string $name, array $aliases, string $description, bool $isHidden, \Closure $commandFactory, ?bool $isEnabled = true)
{
$this->setName($name)
->setAliases($aliases)
->setHidden($isHidden)
->setDescription($description);
$this->command = $commandFactory;
$this->isEnabled = $isEnabled;
}
public function ignoreValidationErrors(): void
{
$this->getCommand()->ignoreValidationErrors();
}
public function setApplication(Application $application = null): void
{
if ($this->command instanceof parent) {
$this->command->setApplication($application);
}
parent::setApplication($application);
}
public function setHelperSet(HelperSet $helperSet): void
{
if ($this->command instanceof parent) {
$this->command->setHelperSet($helperSet);
}
parent::setHelperSet($helperSet);
}
public function isEnabled(): bool
{
return $this->isEnabled ?? $this->getCommand()->isEnabled();
}
public function run(InputInterface $input, OutputInterface $output): int
{
return $this->getCommand()->run($input, $output);
}
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
$this->getCommand()->complete($input, $suggestions);
}
public function setCode(callable $code): self
{
$this->getCommand()->setCode($code);
return $this;
}
public function mergeApplicationDefinition(bool $mergeArgs = true): void
{
$this->getCommand()->mergeApplicationDefinition($mergeArgs);
}
public function setDefinition($definition): self
{
$this->getCommand()->setDefinition($definition);
return $this;
}
public function getDefinition(): InputDefinition
{
return $this->getCommand()->getDefinition();
}
public function getNativeDefinition(): InputDefinition
{
return $this->getCommand()->getNativeDefinition();
}
public function addArgument(string $name, int $mode = null, string $description = '', $default = null): self
{
$this->getCommand()->addArgument($name, $mode, $description, $default);
return $this;
}
public function addOption(string $name, $shortcut = null, int $mode = null, string $description = '', $default = null): self
{
$this->getCommand()->addOption($name, $shortcut, $mode, $description, $default);
return $this;
}
public function setProcessTitle(string $title): self
{
$this->getCommand()->setProcessTitle($title);
return $this;
}
public function setHelp(string $help): self
{
$this->getCommand()->setHelp($help);
return $this;
}
public function getHelp(): string
{
return $this->getCommand()->getHelp();
}
public function getProcessedHelp(): string
{
return $this->getCommand()->getProcessedHelp();
}
public function getSynopsis(bool $short = false): string
{
return $this->getCommand()->getSynopsis($short);
}
public function addUsage(string $usage): self
{
$this->getCommand()->addUsage($usage);
return $this;
}
public function getUsages(): array
{
return $this->getCommand()->getUsages();
}
public function getHelper(string $name)
{
return $this->getCommand()->getHelper($name);
}
public function getCommand(): parent
{
if (!$this->command instanceof \Closure) {
return $this->command;
}
$command = $this->command = ($this->command)();
$command->setApplication($this->getApplication());
if (null !== $this->getHelperSet()) {
$command->setHelperSet($this->getHelperSet());
}
$command->setName($this->getName())
->setAliases($this->getAliases())
->setHidden($this->isHidden())
->setDescription($this->getDescription());
$command->getDefinition();
return $command;
}
}
<?php
namespace Symfony\Component\Console\Command;
use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Lock\LockFactory;
use Symfony\Component\Lock\LockInterface;
use Symfony\Component\Lock\Store\FlockStore;
use Symfony\Component\Lock\Store\SemaphoreStore;
trait LockableTrait
{
private $lock;
private function lock(string $name = null, bool $blocking = false): bool
{
if (!class_exists(SemaphoreStore::class)) {
throw new LogicException('To enable the locking feature you must install the symfony/lock component.');
}
if (null !== $this->lock) {
throw new LogicException('A lock is already in place.');
}
if (SemaphoreStore::isSupported()) {
$store = new SemaphoreStore();
} else {
$store = new FlockStore();
}
$this->lock = (new LockFactory($store))->createLock($name ?: $this->getName());
if (!$this->lock->acquire($blocking)) {
$this->lock = null;
return false;
}
return true;
}
private function release()
{
if ($this->lock) {
$this->lock->release();
$this->lock = null;
}
}
}
<?php
namespace Symfony\Component\Console\Command;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Descriptor\ApplicationDescription;
use Symfony\Component\Console\Helper\DescriptorHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class HelpCommand extends Command
{
private $command;
protected function configure()
{
$this->ignoreValidationErrors();
$this
->setName('help')
->setDefinition([
new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'),
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'),
])
->setDescription('Display help for a command')
->setHelp(<<<'EOF'
The <info>%command.name%</info> command displays help for a given command:
<info>%command.full_name% list</info>
You can also output the help in other formats by using the <comment>--format</comment> option:
<info>%command.full_name% --format=xml list</info>
To display the list of available commands, please use the <info>list</info> command.
EOF
)
;
}
public function setCommand(Command $command)
{
$this->command = $command;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
if (null === $this->command) {
$this->command = $this->getApplication()->find($input->getArgument('command_name'));
}
$helper = new DescriptorHelper();
$helper->describe($output, $this->command, [
'format' => $input->getOption('format'),
'raw_text' => $input->getOption('raw'),
]);
$this->command = null;
return 0;
}
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
if ($input->mustSuggestArgumentValuesFor('command_name')) {
$descriptor = new ApplicationDescription($this->getApplication());
$suggestions->suggestValues(array_keys($descriptor->getCommands()));
return;
}
if ($input->mustSuggestOptionValuesFor('format')) {
$helper = new DescriptorHelper();
$suggestions->suggestValues($helper->getFormats());
}
}
}
<?php
namespace Symfony\Component\Console\Command;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Completion\Output\BashCompletionOutput;
use Symfony\Component\Console\Completion\Output\CompletionOutputInterface;
use Symfony\Component\Console\Exception\CommandNotFoundException;
use Symfony\Component\Console\Exception\ExceptionInterface;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
final class CompleteCommand extends Command
{
protected static $defaultName = '|_complete';
protected static $defaultDescription = 'Internal command to provide shell completion suggestions';
private $completionOutputs;
private $isDebug = false;
public function __construct(array $completionOutputs = [])
{
$this->completionOutputs = $completionOutputs + ['bash' => BashCompletionOutput::class];
parent::__construct();
}
protected function configure(): void
{
$this
->addOption('shell', 's', InputOption::VALUE_REQUIRED, 'The shell type ("'.implode('", "', array_keys($this->completionOutputs)).'")')
->addOption('input', 'i', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'An array of input tokens (e.g. COMP_WORDS or argv)')
->addOption('current', 'c', InputOption::VALUE_REQUIRED, 'The index of the "input" array that the cursor is in (e.g. COMP_CWORD)')
->addOption('symfony', 'S', InputOption::VALUE_REQUIRED, 'The version of the completion script')
;
}
protected function initialize(InputInterface $input, OutputInterface $output)
{
$this->isDebug = filter_var(getenv('SYMFONY_COMPLETION_DEBUG'), \FILTER_VALIDATE_BOOLEAN);
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
try {
$shell = $input->getOption('shell');
if (!$shell) {
throw new \RuntimeException('The "--shell" option must be set.');
}
if (!$completionOutput = $this->completionOutputs[$shell] ?? false) {
throw new \RuntimeException(sprintf('Shell completion is not supported for your shell: "%s" (supported: "%s").', $shell, implode('", "', array_keys($this->completionOutputs))));
}
$completionInput = $this->createCompletionInput($input);
$suggestions = new CompletionSuggestions();
$this->log([
'',
'<comment>'.date('Y-m-d H:i:s').'</>',
'<info>Input:</> <comment>("|" indicates the cursor position)</>',
' '.(string) $completionInput,
'<info>Command:</>',
' '.(string) implode(' ', $_SERVER['argv']),
'<info>Messages:</>',
]);
$command = $this->findCommand($completionInput, $output);
if (null === $command) {
$this->log(' No command found, completing using the Application class.');
$this->getApplication()->complete($completionInput, $suggestions);
} elseif (
$completionInput->mustSuggestArgumentValuesFor('command')
&& $command->getName() !== $completionInput->getCompletionValue()
&& !\in_array($completionInput->getCompletionValue(), $command->getAliases(), true)
) {
$this->log(' No command found, completing using the Application class.');
$suggestions->suggestValues(array_filter(array_merge([$command->getName()], $command->getAliases())));
} else {
$command->mergeApplicationDefinition();
$completionInput->bind($command->getDefinition());
if (CompletionInput::TYPE_OPTION_NAME === $completionInput->getCompletionType()) {
$this->log(' Completing option names for the <comment>'.\get_class($command instanceof LazyCommand ? $command->getCommand() : $command).'</> command.');
$suggestions->suggestOptions($command->getDefinition()->getOptions());
} else {
$this->log([
' Completing using the <comment>'.\get_class($command instanceof LazyCommand ? $command->getCommand() : $command).'</> class.',
' Completing <comment>'.$completionInput->getCompletionType().'</> for <comment>'.$completionInput->getCompletionName().'</>',
]);
if (null !== $compval = $completionInput->getCompletionValue()) {
$this->log(' Current value: <comment>'.$compval.'</>');
}
$command->complete($completionInput, $suggestions);
}
}
$completionOutput = new $completionOutput();
$this->log('<info>Suggestions:</>');
if ($options = $suggestions->getOptionSuggestions()) {
$this->log(' --'.implode(' --', array_map(function ($o) { return $o->getName(); }, $options)));
} elseif ($values = $suggestions->getValueSuggestions()) {
$this->log(' '.implode(' ', $values));
} else {
$this->log(' <comment>No suggestions were provided</>');
}
$completionOutput->write($suggestions, $output);
} catch (\Throwable $e) {
$this->log([
'<error>Error!</error>',
(string) $e,
]);
if ($output->isDebug()) {
throw $e;
}
return self::FAILURE;
}
return self::SUCCESS;
}
private function createCompletionInput(InputInterface $input): CompletionInput
{
$currentIndex = $input->getOption('current');
if (!$currentIndex || !ctype_digit($currentIndex)) {
throw new \RuntimeException('The "--current" option must be set and it must be an integer.');
}
$completionInput = CompletionInput::fromTokens($input->getOption('input'), (int) $currentIndex);
try {
$completionInput->bind($this->getApplication()->getDefinition());
} catch (ExceptionInterface $e) {
}
return $completionInput;
}
private function findCommand(CompletionInput $completionInput, OutputInterface $output): ?Command
{
try {
$inputName = $completionInput->getFirstArgument();
if (null === $inputName) {
return null;
}
return $this->getApplication()->find($inputName);
} catch (CommandNotFoundException $e) {
}
return null;
}
private function log($messages): void
{
if (!$this->isDebug) {
return;
}
$commandName = basename($_SERVER['argv'][0]);
file_put_contents(sys_get_temp_dir().'/sf_'.$commandName.'.log', implode(\PHP_EOL, (array) $messages).\PHP_EOL, \FILE_APPEND);
}
}
<?php
namespace Symfony\Component\Console\Command;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Descriptor\ApplicationDescription;
use Symfony\Component\Console\Helper\DescriptorHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class ListCommand extends Command
{
protected function configure()
{
$this
->setName('list')
->setDefinition([
new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'),
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'),
new InputOption('short', null, InputOption::VALUE_NONE, 'To skip describing commands\' arguments'),
])
->setDescription('List commands')
->setHelp(<<<'EOF'
The <info>%command.name%</info> command lists all commands:
<info>%command.full_name%</info>
You can also display the commands for a specific namespace:
<info>%command.full_name% test</info>
You can also output the information in other formats by using the <comment>--format</comment> option:
<info>%command.full_name% --format=xml</info>
It's also possible to get raw list of commands (useful for embedding command runner):
<info>%command.full_name% --raw</info>
EOF
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$helper = new DescriptorHelper();
$helper->describe($output, $this->getApplication(), [
'format' => $input->getOption('format'),
'raw_text' => $input->getOption('raw'),
'namespace' => $input->getArgument('namespace'),
'short' => $input->getOption('short'),
]);
return 0;
}
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
if ($input->mustSuggestArgumentValuesFor('namespace')) {
$descriptor = new ApplicationDescription($this->getApplication());
$suggestions->suggestValues(array_keys($descriptor->getNamespaces()));
return;
}
if ($input->mustSuggestOptionValuesFor('format')) {
$helper = new DescriptorHelper();
$suggestions->suggestValues($helper->getFormats());
}
}
}
<?php
namespace Symfony\Component\Console\Command;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\Process;
final class DumpCompletionCommand extends Command
{
protected static $defaultName = 'completion';
protected static $defaultDescription = 'Dump the shell completion script';
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
if ($input->mustSuggestArgumentValuesFor('shell')) {
$suggestions->suggestValues($this->getSupportedShells());
}
}
protected function configure()
{
$fullCommand = $_SERVER['PHP_SELF'];
$commandName = basename($fullCommand);
$fullCommand = @realpath($fullCommand) ?: $fullCommand;
$this
->setHelp(<<<EOH
The <info>%command.name%</> command dumps the shell completion script required
to use shell autocompletion (currently only bash completion is supported).
<comment>Static installation
-------------------</>
Dump the script to a global completion file and restart your shell:
<info>%command.full_name% bash | sudo tee /etc/bash_completion.d/{$commandName}</>
Or dump the script to a local file and source it:
<info>%command.full_name% bash > completion.sh</>
<comment># source the file whenever you use the project</>
<info>source completion.sh</>
<comment># or add this line at the end of your "~/.bashrc" file:</>
<info>source /path/to/completion.sh</>
<comment>Dynamic installation
--------------------</>
Add this to the end of your shell configuration file (e.g. <info>"~/.bashrc"</>):
<info>eval "$({$fullCommand} completion bash)"</>
EOH
)
->addArgument('shell', InputArgument::OPTIONAL, 'The shell type (e.g. "bash"), the value of the "$SHELL" env var will be used if this is not given')
->addOption('debug', null, InputOption::VALUE_NONE, 'Tail the completion debug log')
;
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$commandName = basename($_SERVER['argv'][0]);
if ($input->getOption('debug')) {
$this->tailDebugLog($commandName, $output);
return self::SUCCESS;
}
$shell = $input->getArgument('shell') ?? self::guessShell();
$completionFile = __DIR__.'/../Resources/completion.'.$shell;
if (!file_exists($completionFile)) {
$supportedShells = $this->getSupportedShells();
($output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output)
->writeln(sprintf('<error>Detected shell "%s", which is not supported by Symfony shell completion (supported shells: "%s").</>', $shell, implode('", "', $supportedShells)));
return self::INVALID;
}
$output->write(str_replace(['{{ COMMAND_NAME }}', '{{ VERSION }}'], [$commandName, $this->getApplication()->getVersion()], file_get_contents($completionFile)));
return self::SUCCESS;
}
private static function guessShell(): string
{
return basename($_SERVER['SHELL'] ?? '');
}
private function tailDebugLog(string $commandName, OutputInterface $output): void
{
$debugFile = sys_get_temp_dir().'/sf_'.$commandName.'.log';
if (!file_exists($debugFile)) {
touch($debugFile);
}
$process = new Process(['tail', '-f', $debugFile], null, null, null, 0);
$process->run(function (string $type, string $line) use ($output): void {
$output->write($line);
});
}
private function getSupportedShells(): array
{
return array_map(function ($f) {
return pathinfo($f, \PATHINFO_EXTENSION);
}, glob(__DIR__.'/../Resources/completion.*'));
}
}
<?php
namespace Symfony\Component\Console\Command;
interface SignalableCommandInterface
{
public function getSubscribedSignals(): array;
public function handleSignal(int $signal): void;
}
<?php
namespace Symfony\Component\Console\Command;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Exception\ExceptionInterface;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class Command
{
public const SUCCESS = 0;
public const FAILURE = 1;
public const INVALID = 2;
protected static $defaultName;
protected static $defaultDescription;
private $application;
private $name;
private $processTitle;
private $aliases = [];
private $definition;
private $hidden = false;
private $help = '';
private $description = '';
private $fullDefinition;
private $ignoreValidationErrors = false;
private $code;
private $synopsis = [];
private $usages = [];
private $helperSet;
public static function getDefaultName()
{
$class = static::class;
if (\PHP_VERSION_ID >= 80000 && $attribute = (new \ReflectionClass($class))->getAttributes(AsCommand::class)) {
return $attribute[0]->newInstance()->name;
}
$r = new \ReflectionProperty($class, 'defaultName');
return $class === $r->class ? static::$defaultName : null;
}
public static function getDefaultDescription(): ?string
{
$class = static::class;
if (\PHP_VERSION_ID >= 80000 && $attribute = (new \ReflectionClass($class))->getAttributes(AsCommand::class)) {
return $attribute[0]->newInstance()->description;
}
$r = new \ReflectionProperty($class, 'defaultDescription');
return $class === $r->class ? static::$defaultDescription : null;
}
public function __construct(string $name = null)
{
$this->definition = new InputDefinition();
if (null === $name && null !== $name = static::getDefaultName()) {
$aliases = explode('|', $name);
if ('' === $name = array_shift($aliases)) {
$this->setHidden(true);
$name = array_shift($aliases);
}
$this->setAliases($aliases);
}
if (null !== $name) {
$this->setName($name);
}
if ('' === $this->description) {
$this->setDescription(static::getDefaultDescription() ?? '');
}
$this->configure();
}
public function ignoreValidationErrors()
{
$this->ignoreValidationErrors = true;
}
public function setApplication(Application $application = null)
{
$this->application = $application;
if ($application) {
$this->setHelperSet($application->getHelperSet());
} else {
$this->helperSet = null;
}
$this->fullDefinition = null;
}
public function setHelperSet(HelperSet $helperSet)
{
$this->helperSet = $helperSet;
}
public function getHelperSet()
{
return $this->helperSet;
}
public function getApplication()
{
return $this->application;
}
public function isEnabled()
{
return true;
}
protected function configure()
{
}
protected function execute(InputInterface $input, OutputInterface $output)
{
throw new LogicException('You must override the execute() method in the concrete command class.');
}
protected function interact(InputInterface $input, OutputInterface $output)
{
}
protected function initialize(InputInterface $input, OutputInterface $output)
{
}
public function run(InputInterface $input, OutputInterface $output)
{
$this->mergeApplicationDefinition();
try {
$input->bind($this->getDefinition());
} catch (ExceptionInterface $e) {
if (!$this->ignoreValidationErrors) {
throw $e;
}
}
$this->initialize($input, $output);
if (null !== $this->processTitle) {
if (\function_exists('cli_set_process_title')) {
if (!@cli_set_process_title($this->processTitle)) {
if ('Darwin' === \PHP_OS) {
$output->writeln('<comment>Running "cli_set_process_title" as an unprivileged user is not supported on MacOS.</comment>', OutputInterface::VERBOSITY_VERY_VERBOSE);
} else {
cli_set_process_title($this->processTitle);
}
}
} elseif (\function_exists('setproctitle')) {
setproctitle($this->processTitle);
} elseif (OutputInterface::VERBOSITY_VERY_VERBOSE === $output->getVerbosity()) {
$output->writeln('<comment>Install the proctitle PECL to be able to change the process title.</comment>');
}
}
if ($input->isInteractive()) {
$this->interact($input, $output);
}
if ($input->hasArgument('command') && null === $input->getArgument('command')) {
$input->setArgument('command', $this->getName());
}
$input->validate();
if ($this->code) {
$statusCode = ($this->code)($input, $output);
} else {
$statusCode = $this->execute($input, $output);
if (!\is_int($statusCode)) {
throw new \TypeError(sprintf('Return value of "%s::execute()" must be of the type int, "%s" returned.', static::class, get_debug_type($statusCode)));
}
}
return is_numeric($statusCode) ? (int) $statusCode : 0;
}
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
}
public function setCode(callable $code)
{
if ($code instanceof \Closure) {
$r = new \ReflectionFunction($code);
if (null === $r->getClosureThis()) {
set_error_handler(static function () {});
try {
if ($c = \Closure::bind($code, $this)) {
$code = $c;
}
} finally {
restore_error_handler();
}
}
}
$this->code = $code;
return $this;
}
public function mergeApplicationDefinition(bool $mergeArgs = true)
{
if (null === $this->application) {
return;
}
$this->fullDefinition = new InputDefinition();
$this->fullDefinition->setOptions($this->definition->getOptions());
$this->fullDefinition->addOptions($this->application->getDefinition()->getOptions());
if ($mergeArgs) {
$this->fullDefinition->setArguments($this->application->getDefinition()->getArguments());
$this->fullDefinition->addArguments($this->definition->getArguments());
} else {
$this->fullDefinition->setArguments($this->definition->getArguments());
}
}
public function setDefinition($definition)
{
if ($definition instanceof InputDefinition) {
$this->definition = $definition;
} else {
$this->definition->setDefinition($definition);
}
$this->fullDefinition = null;
return $this;
}
public function getDefinition()
{
return $this->fullDefinition ?? $this->getNativeDefinition();
}
public function getNativeDefinition()
{
if (null === $this->definition) {
throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', static::class));
}
return $this->definition;
}
public function addArgument(string $name, int $mode = null, string $description = '', $default = null)
{
$this->definition->addArgument(new InputArgument($name, $mode, $description, $default));
if (null !== $this->fullDefinition) {
$this->fullDefinition->addArgument(new InputArgument($name, $mode, $description, $default));
}
return $this;
}
public function addOption(string $name, $shortcut = null, int $mode = null, string $description = '', $default = null)
{
$this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default));
if (null !== $this->fullDefinition) {
$this->fullDefinition->addOption(new InputOption($name, $shortcut, $mode, $description, $default));
}
return $this;
}
public function setName(string $name)
{
$this->validateName($name);
$this->name = $name;
return $this;
}
public function setProcessTitle(string $title)
{
$this->processTitle = $title;
return $this;
}
public function getName()
{
return $this->name;
}
public function setHidden(bool $hidden )
{
$this->hidden = $hidden;
return $this;
}
public function isHidden()
{
return $this->hidden;
}
public function setDescription(string $description)
{
$this->description = $description;
return $this;
}
public function getDescription()
{
return $this->description;
}
public function setHelp(string $help)
{
$this->help = $help;
return $this;
}
public function getHelp()
{
return $this->help;
}
public function getProcessedHelp()
{
$name = $this->name;
$isSingleCommand = $this->application && $this->application->isSingleCommand();
$placeholders = [
'%command.name%',
'%command.full_name%',
];
$replacements = [
$name,
$isSingleCommand ? $_SERVER['PHP_SELF'] : $_SERVER['PHP_SELF'].' '.$name,
];
return str_replace($placeholders, $replacements, $this->getHelp() ?: $this->getDescription());
}
public function setAliases(iterable $aliases)
{
$list = [];
foreach ($aliases as $alias) {
$this->validateName($alias);
$list[] = $alias;
}
$this->aliases = \is_array($aliases) ? $aliases : $list;
return $this;
}
public function getAliases()
{
return $this->aliases;
}
public function getSynopsis(bool $short = false)
{
$key = $short ? 'short' : 'long';
if (!isset($this->synopsis[$key])) {
$this->synopsis[$key] = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis($short)));
}
return $this->synopsis[$key];
}
public function addUsage(string $usage)
{
if (!str_starts_with($usage, $this->name)) {
$usage = sprintf('%s %s', $this->name, $usage);
}
$this->usages[] = $usage;
return $this;
}
public function getUsages()
{
return $this->usages;
}
public function getHelper(string $name)
{
if (null === $this->helperSet) {
throw new LogicException(sprintf('Cannot retrieve helper "%s" because there is no HelperSet defined. Did you forget to add your command to the application or to set the application on the command using the setApplication() method? You can also set the HelperSet directly using the setHelperSet() method.', $name));
}
return $this->helperSet->get($name);
}
private function validateName(string $name)
{
if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) {
throw new InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name));
}
}
}
<?php
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;
class TableStyle
{
private $paddingChar = ' ';
private $horizontalOutsideBorderChar = '-';
private $horizontalInsideBorderChar = '-';
private $verticalOutsideBorderChar = '|';
private $verticalInsideBorderChar = '|';
private $crossingChar = '+';
private $crossingTopRightChar = '+';
private $crossingTopMidChar = '+';
private $crossingTopLeftChar = '+';
private $crossingMidRightChar = '+';
private $crossingBottomRightChar = '+';
private $crossingBottomMidChar = '+';
private $crossingBottomLeftChar = '+';
private $crossingMidLeftChar = '+';
private $crossingTopLeftBottomChar = '+';
private $crossingTopMidBottomChar = '+';
private $crossingTopRightBottomChar = '+';
private $headerTitleFormat = '<fg=black;bg=white;options=bold> %s </>';
private $footerTitleFormat = '<fg=black;bg=white;options=bold> %s </>';
private $cellHeaderFormat = '<info>%s</info>';
private $cellRowFormat = '%s';
private $cellRowContentFormat = ' %s ';
private $borderFormat = '%s';
private $padType = \STR_PAD_RIGHT;
public function setPaddingChar(string $paddingChar)
{
if (!$paddingChar) {
throw new LogicException('The padding char must not be empty.');
}
$this->paddingChar = $paddingChar;
return $this;
}
public function getPaddingChar()
{
return $this->paddingChar;
}
public function setHorizontalBorderChars(string $outside, string $inside = null): self
{
$this->horizontalOutsideBorderChar = $outside;
$this->horizontalInsideBorderChar = $inside ?? $outside;
return $this;
}
public function setVerticalBorderChars(string $outside, string $inside = null): self
{
$this->verticalOutsideBorderChar = $outside;
$this->verticalInsideBorderChar = $inside ?? $outside;
return $this;
}
public function getBorderChars(): array
{
return [
$this->horizontalOutsideBorderChar,
$this->verticalOutsideBorderChar,
$this->horizontalInsideBorderChar,
$this->verticalInsideBorderChar,
];
}
public function setCrossingChars(string $cross, string $topLeft, string $topMid, string $topRight, string $midRight, string $bottomRight, string $bottomMid, string $bottomLeft, string $midLeft, string $topLeftBottom = null, string $topMidBottom = null, string $topRightBottom = null): self
{
$this->crossingChar = $cross;
$this->crossingTopLeftChar = $topLeft;
$this->crossingTopMidChar = $topMid;
$this->crossingTopRightChar = $topRight;
$this->crossingMidRightChar = $midRight;
$this->crossingBottomRightChar = $bottomRight;
$this->crossingBottomMidChar = $bottomMid;
$this->crossingBottomLeftChar = $bottomLeft;
$this->crossingMidLeftChar = $midLeft;
$this->crossingTopLeftBottomChar = $topLeftBottom ?? $midLeft;
$this->crossingTopMidBottomChar = $topMidBottom ?? $cross;
$this->crossingTopRightBottomChar = $topRightBottom ?? $midRight;
return $this;
}
public function setDefaultCrossingChar(string $char): self
{
return $this->setCrossingChars($char, $char, $char, $char, $char, $char, $char, $char, $char);
}
public function getCrossingChar()
{
return $this->crossingChar;
}
public function getCrossingChars(): array
{
return [
$this->crossingChar,
$this->crossingTopLeftChar,
$this->crossingTopMidChar,
$this->crossingTopRightChar,
$this->crossingMidRightChar,
$this->crossingBottomRightChar,
$this->crossingBottomMidChar,
$this->crossingBottomLeftChar,
$this->crossingMidLeftChar,
$this->crossingTopLeftBottomChar,
$this->crossingTopMidBottomChar,
$this->crossingTopRightBottomChar,
];
}
public function setCellHeaderFormat(string $cellHeaderFormat)
{
$this->cellHeaderFormat = $cellHeaderFormat;
return $this;
}
public function getCellHeaderFormat()
{
return $this->cellHeaderFormat;
}
public function setCellRowFormat(string $cellRowFormat)
{
$this->cellRowFormat = $cellRowFormat;
return $this;
}
public function getCellRowFormat()
{
return $this->cellRowFormat;
}
public function setCellRowContentFormat(string $cellRowContentFormat)
{
$this->cellRowContentFormat = $cellRowContentFormat;
return $this;
}
public function getCellRowContentFormat()
{
return $this->cellRowContentFormat;
}
public function setBorderFormat(string $borderFormat)
{
$this->borderFormat = $borderFormat;
return $this;
}
public function getBorderFormat()
{
return $this->borderFormat;
}
public function setPadType(int $padType)
{
if (!\in_array($padType, [\STR_PAD_LEFT, \STR_PAD_RIGHT, \STR_PAD_BOTH], true)) {
throw new InvalidArgumentException('Invalid padding type. Expected one of (STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH).');
}
$this->padType = $padType;
return $this;
}
public function getPadType()
{
return $this->padType;
}
public function getHeaderTitleFormat(): string
{
return $this->headerTitleFormat;
}
public function setHeaderTitleFormat(string $format): self
{
$this->headerTitleFormat = $format;
return $this;
}
public function getFooterTitleFormat(): string
{
return $this->footerTitleFormat;
}
public function setFooterTitleFormat(string $format): self
{
$this->footerTitleFormat = $format;
return $this;
}
}
<?php
namespace Symfony\Component\Console\Helper;
class TableRows implements \IteratorAggregate
{
private $generator;
public function __construct(\Closure $generator)
{
$this->generator = $generator;
}
public function getIterator(): \Traversable
{
return ($this->generator)();
}
}
<?php
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Style\SymfonyStyle;
class SymfonyQuestionHelper extends QuestionHelper
{
protected function writePrompt(OutputInterface $output, Question $question)
{
$text = OutputFormatter::escapeTrailingBackslash($question->getQuestion());
$default = $question->getDefault();
if ($question->isMultiline()) {
$text .= sprintf(' (press %s to continue)', $this->getEofShortcut());
}
switch (true) {
case null === $default:
$text = sprintf(' <info>%s</info>:', $text);
break;
case $question instanceof ConfirmationQuestion:
$text = sprintf(' <info>%s (yes/no)</info> [<comment>%s</comment>]:', $text, $default ? 'yes' : 'no');
break;
case $question instanceof ChoiceQuestion && $question->isMultiselect():
$choices = $question->getChoices();
$default = explode(',', $default);
foreach ($default as $key => $value) {
$default[$key] = $choices[trim($value)];
}
$text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, OutputFormatter::escape(implode(', ', $default)));
break;
case $question instanceof ChoiceQuestion:
$choices = $question->getChoices();
$text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, OutputFormatter::escape($choices[$default] ?? $default));
break;
default:
$text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, OutputFormatter::escape($default));
}
$output->writeln($text);
$prompt = ' > ';
if ($question instanceof ChoiceQuestion) {
$output->writeln($this->formatChoiceQuestionChoices($question, 'comment'));
$prompt = $question->getPrompt();
}
$output->write($prompt);
}
protected function writeError(OutputInterface $output, \Exception $error)
{
if ($output instanceof SymfonyStyle) {
$output->newLine();
$output->error($error->getMessage());
return;
}
parent::writeError($output, $error);
}
private function getEofShortcut(): string
{
if ('Windows' === \PHP_OS_FAMILY) {
return '<comment>Ctrl+Z</comment> then <comment>Enter</comment>';
}
return '<comment>Ctrl+D</comment>';
}
}
<?php
namespace Symfony\Component\Console\Helper;
interface HelperInterface
{
public function setHelperSet(HelperSet $helperSet = null);
public function getHelperSet();
public function getName();
}
<?php
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Exception\InvalidArgumentException;
class TableCell
{
private $value;
private $options = [
'rowspan' => 1,
'colspan' => 1,
'style' => null,
];
public function __construct(string $value = '', array $options = [])
{
$this->value = $value;
if ($diff = array_diff(array_keys($options), array_keys($this->options))) {
throw new InvalidArgumentException(sprintf('The TableCell does not support the following options: \'%s\'.', implode('\', \'', $diff)));
}
if (isset($options['style']) && !$options['style'] instanceof TableCellStyle) {
throw new InvalidArgumentException('The style option must be an instance of "TableCellStyle".');
}
$this->options = array_merge($this->options, $options);
}
public function __toString()
{
return $this->value;
}
public function getColspan()
{
return (int) $this->options['colspan'];
}
public function getRowspan()
{
return (int) $this->options['rowspan'];
}
public function getStyle(): ?TableCellStyle
{
return $this->options['style'];
}
}
<?php
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;
class ProcessHelper extends Helper
{
public function run(OutputInterface $output, $cmd, string $error = null, callable $callback = null, int $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE): Process
{
if (!class_exists(Process::class)) {
throw new \LogicException('The ProcessHelper cannot be run as the Process component is not installed. Try running "compose require symfony/process".');
}
if ($output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
}
$formatter = $this->getHelperSet()->get('debug_formatter');
if ($cmd instanceof Process) {
$cmd = [$cmd];
}
if (!\is_array($cmd)) {
throw new \TypeError(sprintf('The "command" argument of "%s()" must be an array or a "%s" instance, "%s" given.', __METHOD__, Process::class, get_debug_type($cmd)));
}
if (\is_string($cmd[0] ?? null)) {
$process = new Process($cmd);
$cmd = [];
} elseif (($cmd[0] ?? null) instanceof Process) {
$process = $cmd[0];
unset($cmd[0]);
} else {
throw new \InvalidArgumentException(sprintf('Invalid command provided to "%s()": the command should be an array whose first element is either the path to the binary to run or a "Process" object.', __METHOD__));
}
if ($verbosity <= $output->getVerbosity()) {
$output->write($formatter->start(spl_object_hash($process), $this->escapeString($process->getCommandLine())));
}
if ($output->isDebug()) {
$callback = $this->wrapCallback($output, $process, $callback);
}
$process->run($callback, $cmd);
if ($verbosity <= $output->getVerbosity()) {
$message = $process->isSuccessful() ? 'Command ran successfully' : sprintf('%s Command did not run successfully', $process->getExitCode());
$output->write($formatter->stop(spl_object_hash($process), $message, $process->isSuccessful()));
}
if (!$process->isSuccessful() && null !== $error) {
$output->writeln(sprintf('<error>%s</error>', $this->escapeString($error)));
}
return $process;
}
public function mustRun(OutputInterface $output, $cmd, string $error = null, callable $callback = null): Process
{
$process = $this->run($output, $cmd, $error, $callback);
if (!$process->isSuccessful()) {
throw new ProcessFailedException($process);
}
return $process;
}
public function wrapCallback(OutputInterface $output, Process $process, callable $callback = null): callable
{
if ($output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
}
$formatter = $this->getHelperSet()->get('debug_formatter');
return function ($type, $buffer) use ($output, $process, $callback, $formatter) {
$output->write($formatter->progress(spl_object_hash($process), $this->escapeString($buffer), Process::ERR === $type));
if (null !== $callback) {
$callback($type, $buffer);
}
};
}
private function escapeString(string $str): string
{
return str_replace('<', '\\<', $str);
}
public function getName(): string
{
return 'process';
}
}
<?php
namespace Symfony\Component\Console\Helper;
class TableSeparator extends TableCell
{
public function __construct(array $options = [])
{
parent::__construct('', $options);
}
}
<?php
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Formatter\OutputFormatter;
class FormatterHelper extends Helper
{
public function formatSection(string $section, string $message, string $style = 'info')
{
return sprintf('<%s>[%s]</%s> %s', $style, $section, $style, $message);
}
public function formatBlock($messages, string $style, bool $large = false)
{
if (!\is_array($messages)) {
$messages = [$messages];
}
$len = 0;
$lines = [];
foreach ($messages as $message) {
$message = OutputFormatter::escape($message);
$lines[] = sprintf($large ? ' %s ' : ' %s ', $message);
$len = max(self::width($message) + ($large ? 4 : 2), $len);
}
$messages = $large ? [str_repeat(' ', $len)] : [];
for ($i = 0; isset($lines[$i]); ++$i) {
$messages[] = $lines[$i].str_repeat(' ', $len - self::width($lines[$i]));
}
if ($large) {
$messages[] = str_repeat(' ', $len);
}
for ($i = 0; isset($messages[$i]); ++$i) {
$messages[$i] = sprintf('<%s>%s</%s>', $style, $messages[$i], $style);
}
return implode("\n", $messages);
}
public function truncate(string $message, int $length, string $suffix = '...')
{
$computedLength = $length - self::width($suffix);
if ($computedLength > self::width($message)) {
return $message;
}
return self::substr($message, 0, $length).$suffix;
}
public function getName()
{
return 'formatter';
}
}
<?php
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Exception\InvalidArgumentException;
class TableCellStyle
{
public const DEFAULT_ALIGN = 'left';
private const TAG_OPTIONS = [
'fg',
'bg',
'options',
];
private const ALIGN_MAP = [
'left' => \STR_PAD_RIGHT,
'center' => \STR_PAD_BOTH,
'right' => \STR_PAD_LEFT,
];
private $options = [
'fg' => 'default',
'bg' => 'default',
'options' => null,
'align' => self::DEFAULT_ALIGN,
'cellFormat' => null,
];
public function __construct(array $options = [])
{
if ($diff = array_diff(array_keys($options), array_keys($this->options))) {
throw new InvalidArgumentException(sprintf('The TableCellStyle does not support the following options: \'%s\'.', implode('\', \'', $diff)));
}
if (isset($options['align']) && !\array_key_exists($options['align'], self::ALIGN_MAP)) {
throw new InvalidArgumentException(sprintf('Wrong align value. Value must be following: \'%s\'.', implode('\', \'', array_keys(self::ALIGN_MAP))));
}
$this->options = array_merge($this->options, $options);
}
public function getOptions(): array
{
return $this->options;
}
public function getTagOptions()
{
return array_filter(
$this->getOptions(),
function ($key) {
return \in_array($key, self::TAG_OPTIONS) && isset($this->options[$key]);
},
\ARRAY_FILTER_USE_KEY
);
}
public function getPadByAlign()
{
return self::ALIGN_MAP[$this->getOptions()['align']];
}
public function getCellFormat(): ?string
{
return $this->getOptions()['cellFormat'];
}
}
<?php
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Cursor;
use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\ConsoleSectionOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Terminal;
final class ProgressBar
{
public const FORMAT_VERBOSE = 'verbose';
public const FORMAT_VERY_VERBOSE = 'very_verbose';
public const FORMAT_DEBUG = 'debug';
public const FORMAT_NORMAL = 'normal';
private const FORMAT_VERBOSE_NOMAX = 'verbose_nomax';
private const FORMAT_VERY_VERBOSE_NOMAX = 'very_verbose_nomax';
private const FORMAT_DEBUG_NOMAX = 'debug_nomax';
private const FORMAT_NORMAL_NOMAX = 'normal_nomax';
private $barWidth = 28;
private $barChar;
private $emptyBarChar = '-';
private $progressChar = '>';
private $format;
private $internalFormat;
private $redrawFreq = 1;
private $writeCount;
private $lastWriteTime;
private $minSecondsBetweenRedraws = 0;
private $maxSecondsBetweenRedraws = 1;
private $output;
private $step = 0;
private $max;
private $startTime;
private $stepWidth;
private $percent = 0.0;
private $formatLineCount;
private $messages = [];
private $overwrite = true;
private $terminal;
private $previousMessage;
private $cursor;
private static $formatters;
private static $formats;
public function __construct(OutputInterface $output, int $max = 0, float $minSecondsBetweenRedraws = 1 / 25)
{
if ($output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
}
$this->output = $output;
$this->setMaxSteps($max);
$this->terminal = new Terminal();
if (0 < $minSecondsBetweenRedraws) {
$this->redrawFreq = null;
$this->minSecondsBetweenRedraws = $minSecondsBetweenRedraws;
}
if (!$this->output->isDecorated()) {
$this->overwrite = false;
$this->redrawFreq = null;
}
$this->startTime = time();
$this->cursor = new Cursor($output);
}
public static function setPlaceholderFormatterDefinition(string $name, callable $callable): void
{
if (!self::$formatters) {
self::$formatters = self::initPlaceholderFormatters();
}
self::$formatters[$name] = $callable;
}
public static function getPlaceholderFormatterDefinition(string $name): ?callable
{
if (!self::$formatters) {
self::$formatters = self::initPlaceholderFormatters();
}
return self::$formatters[$name] ?? null;
}
public static function setFormatDefinition(string $name, string $format): void
{
if (!self::$formats) {
self::$formats = self::initFormats();
}
self::$formats[$name] = $format;
}
public static function getFormatDefinition(string $name): ?string
{
if (!self::$formats) {
self::$formats = self::initFormats();
}
return self::$formats[$name] ?? null;
}
public function setMessage(string $message, string $name = 'message')
{
$this->messages[$name] = $message;
}
public function getMessage(string $name = 'message')
{
return $this->messages[$name];
}
public function getStartTime(): int
{
return $this->startTime;
}
public function getMaxSteps(): int
{
return $this->max;
}
public function getProgress(): int
{
return $this->step;
}
private function getStepWidth(): int
{
return $this->stepWidth;
}
public function getProgressPercent(): float
{
return $this->percent;
}
public function getBarOffset(): float
{
return floor($this->max ? $this->percent * $this->barWidth : (null === $this->redrawFreq ? (int) (min(5, $this->barWidth / 15) * $this->writeCount) : $this->step) % $this->barWidth);
}
public function getEstimated(): float
{
if (!$this->step) {
return 0;
}
return round((time() - $this->startTime) / $this->step * $this->max);
}
public function getRemaining(): float
{
if (!$this->step) {
return 0;
}
return round((time() - $this->startTime) / $this->step * ($this->max - $this->step));
}
public function setBarWidth(int $size)
{
$this->barWidth = max(1, $size);
}
public function getBarWidth(): int
{
return $this->barWidth;
}
public function setBarCharacter(string $char)
{
$this->barChar = $char;
}
public function getBarCharacter(): string
{
return $this->barChar ?? ($this->max ? '=' : $this->emptyBarChar);
}
public function setEmptyBarCharacter(string $char)
{
$this->emptyBarChar = $char;
}
public function getEmptyBarCharacter(): string
{
return $this->emptyBarChar;
}
public function setProgressCharacter(string $char)
{
$this->progressChar = $char;
}
public function getProgressCharacter(): string
{
return $this->progressChar;
}
public function setFormat(string $format)
{
$this->format = null;
$this->internalFormat = $format;
}
public function setRedrawFrequency(?int $freq)
{
$this->redrawFreq = null !== $freq ? max(1, $freq) : null;
}
public function minSecondsBetweenRedraws(float $seconds): void
{
$this->minSecondsBetweenRedraws = $seconds;
}
public function maxSecondsBetweenRedraws(float $seconds): void
{
$this->maxSecondsBetweenRedraws = $seconds;
}
public function iterate(iterable $iterable, int $max = null): iterable
{
$this->start($max ?? (is_countable($iterable) ? \count($iterable) : 0));
foreach ($iterable as $key => $value) {
yield $key => $value;
$this->advance();
}
$this->finish();
}
public function start(int $max = null)
{
$this->startTime = time();
$this->step = 0;
$this->percent = 0.0;
if (null !== $max) {
$this->setMaxSteps($max);
}
$this->display();
}
public function advance(int $step = 1)
{
$this->setProgress($this->step + $step);
}
public function setOverwrite(bool $overwrite)
{
$this->overwrite = $overwrite;
}
public function setProgress(int $step)
{
if ($this->max && $step > $this->max) {
$this->max = $step;
} elseif ($step < 0) {
$step = 0;
}
$redrawFreq = $this->redrawFreq ?? (($this->max ?: 10) / 10);
$prevPeriod = (int) ($this->step / $redrawFreq);
$currPeriod = (int) ($step / $redrawFreq);
$this->step = $step;
$this->percent = $this->max ? (float) $this->step / $this->max : 0;
$timeInterval = microtime(true) - $this->lastWriteTime;
if ($this->max === $step) {
$this->display();
return;
}
if ($timeInterval < $this->minSecondsBetweenRedraws) {
return;
}
if ($prevPeriod !== $currPeriod || $timeInterval >= $this->maxSecondsBetweenRedraws) {
$this->display();
}
}
public function setMaxSteps(int $max)
{
$this->format = null;
$this->max = max(0, $max);
$this->stepWidth = $this->max ? Helper::width((string) $this->max) : 4;
}
public function finish(): void
{
if (!$this->max) {
$this->max = $this->step;
}
if ($this->step === $this->max && !$this->overwrite) {
return;
}
$this->setProgress($this->max);
}
public function display(): void
{
if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) {
return;
}
if (null === $this->format) {
$this->setRealFormat($this->internalFormat ?: $this->determineBestFormat());
}
$this->overwrite($this->buildLine());
}
public function clear(): void
{
if (!$this->overwrite) {
return;
}
if (null === $this->format) {
$this->setRealFormat($this->internalFormat ?: $this->determineBestFormat());
}
$this->overwrite('');
}
private function setRealFormat(string $format)
{
if (!$this->max && null !== self::getFormatDefinition($format.'_nomax')) {
$this->format = self::getFormatDefinition($format.'_nomax');
} elseif (null !== self::getFormatDefinition($format)) {
$this->format = self::getFormatDefinition($format);
} else {
$this->format = $format;
}
$this->formatLineCount = substr_count($this->format, "\n");
}
private function overwrite(string $message): void
{
if ($this->previousMessage === $message) {
return;
}
$originalMessage = $message;
if ($this->overwrite) {
if (null !== $this->previousMessage) {
if ($this->output instanceof ConsoleSectionOutput) {
$messageLines = explode("\n", $message);
$lineCount = \count($messageLines);
foreach ($messageLines as $messageLine) {
$messageLineLength = Helper::width(Helper::removeDecoration($this->output->getFormatter(), $messageLine));
if ($messageLineLength > $this->terminal->getWidth()) {
$lineCount += floor($messageLineLength / $this->terminal->getWidth());
}
}
$this->output->clear($lineCount);
} else {
for ($i = 0; $i < $this->formatLineCount; ++$i) {
$this->cursor->moveToColumn(1);
$this->cursor->clearLine();
$this->cursor->moveUp();
}
$this->cursor->moveToColumn(1);
$this->cursor->clearLine();
}
}
} elseif ($this->step > 0) {
$message = \PHP_EOL.$message;
}
$this->previousMessage = $originalMessage;
$this->lastWriteTime = microtime(true);
$this->output->write($message);
++$this->writeCount;
}
private function determineBestFormat(): string
{
switch ($this->output->getVerbosity()) {
case OutputInterface::VERBOSITY_VERBOSE:
return $this->max ? self::FORMAT_VERBOSE : self::FORMAT_VERBOSE_NOMAX;
case OutputInterface::VERBOSITY_VERY_VERBOSE:
return $this->max ? self::FORMAT_VERY_VERBOSE : self::FORMAT_VERY_VERBOSE_NOMAX;
case OutputInterface::VERBOSITY_DEBUG:
return $this->max ? self::FORMAT_DEBUG : self::FORMAT_DEBUG_NOMAX;
default:
return $this->max ? self::FORMAT_NORMAL : self::FORMAT_NORMAL_NOMAX;
}
}
private static function initPlaceholderFormatters(): array
{
return [
'bar' => function (self $bar, OutputInterface $output) {
$completeBars = $bar->getBarOffset();
$display = str_repeat($bar->getBarCharacter(), $completeBars);
if ($completeBars < $bar->getBarWidth()) {
$emptyBars = $bar->getBarWidth() - $completeBars - Helper::length(Helper::removeDecoration($output->getFormatter(), $bar->getProgressCharacter()));
$display .= $bar->getProgressCharacter().str_repeat($bar->getEmptyBarCharacter(), $emptyBars);
}
return $display;
},
'elapsed' => function (self $bar) {
return Helper::formatTime(time() - $bar->getStartTime());
},
'remaining' => function (self $bar) {
if (!$bar->getMaxSteps()) {
throw new LogicException('Unable to display the remaining time if the maximum number of steps is not set.');
}
return Helper::formatTime($bar->getRemaining());
},
'estimated' => function (self $bar) {
if (!$bar->getMaxSteps()) {
throw new LogicException('Unable to display the estimated time if the maximum number of steps is not set.');
}
return Helper::formatTime($bar->getEstimated());
},
'memory' => function (self $bar) {
return Helper::formatMemory(memory_get_usage(true));
},
'current' => function (self $bar) {
return str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', \STR_PAD_LEFT);
},
'max' => function (self $bar) {
return $bar->getMaxSteps();
},
'percent' => function (self $bar) {
return floor($bar->getProgressPercent() * 100);
},
];
}
private static function initFormats(): array
{
return [
self::FORMAT_NORMAL => ' %current%/%max% [%bar%] %percent:3s%%',
self::FORMAT_NORMAL_NOMAX => ' %current% [%bar%]',
self::FORMAT_VERBOSE => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%',
self::FORMAT_VERBOSE_NOMAX => ' %current% [%bar%] %elapsed:6s%',
self::FORMAT_VERY_VERBOSE => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%',
self::FORMAT_VERY_VERBOSE_NOMAX => ' %current% [%bar%] %elapsed:6s%',
self::FORMAT_DEBUG => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%',
self::FORMAT_DEBUG_NOMAX => ' %current% [%bar%] %elapsed:6s% %memory:6s%',
];
}
private function buildLine(): string
{
$regex = "{%([a-z\-_]+)(?:\:([^%]+))?%}i";
$callback = function ($matches) {
if ($formatter = $this::getPlaceholderFormatterDefinition($matches[1])) {
$text = $formatter($this, $this->output);
} elseif (isset($this->messages[$matches[1]])) {
$text = $this->messages[$matches[1]];
} else {
return $matches[0];
}
if (isset($matches[2])) {
$text = sprintf('%'.$matches[2], $text);
}
return $text;
};
$line = preg_replace_callback($regex, $callback, $this->format);
$linesLength = array_map(function ($subLine) {
return Helper::width(Helper::removeDecoration($this->output->getFormatter(), rtrim($subLine, "\r")));
}, explode("\n", $line));
$linesWidth = max($linesLength);
$terminalWidth = $this->terminal->getWidth();
if ($linesWidth <= $terminalWidth) {
return $line;
}
$this->setBarWidth($this->barWidth - $linesWidth + $terminalWidth);
return preg_replace_callback($regex, $callback, $this->format);
}
}
<?php
namespace Symfony\Component\Console\Helper;
class DebugFormatterHelper extends Helper
{
private const COLORS = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white', 'default'];
private $started = [];
private $count = -1;
public function start(string $id, string $message, string $prefix = 'RUN')
{
$this->started[$id] = ['border' => ++$this->count % \count(self::COLORS)];
return sprintf("%s<bg=blue;fg=white> %s </> <fg=blue>%s</>\n", $this->getBorder($id), $prefix, $message);
}
public function progress(string $id, string $buffer, bool $error = false, string $prefix = 'OUT', string $errorPrefix = 'ERR')
{
$message = '';
if ($error) {
if (isset($this->started[$id]['out'])) {
$message .= "\n";
unset($this->started[$id]['out']);
}
if (!isset($this->started[$id]['err'])) {
$message .= sprintf('%s<bg=red;fg=white> %s </> ', $this->getBorder($id), $errorPrefix);
$this->started[$id]['err'] = true;
}
$message .= str_replace("\n", sprintf("\n%s<bg=red;fg=white> %s </> ", $this->getBorder($id), $errorPrefix), $buffer);
} else {
if (isset($this->started[$id]['err'])) {
$message .= "\n";
unset($this->started[$id]['err']);
}
if (!isset($this->started[$id]['out'])) {
$message .= sprintf('%s<bg=green;fg=white> %s </> ', $this->getBorder($id), $prefix);
$this->started[$id]['out'] = true;
}
$message .= str_replace("\n", sprintf("\n%s<bg=green;fg=white> %s </> ", $this->getBorder($id), $prefix), $buffer);
}
return $message;
}
public function stop(string $id, string $message, bool $successful, string $prefix = 'RES')
{
$trailingEOL = isset($this->started[$id]['out']) || isset($this->started[$id]['err']) ? "\n" : '';
if ($successful) {
return sprintf("%s%s<bg=green;fg=white> %s </> <fg=green>%s</>\n", $trailingEOL, $this->getBorder($id), $prefix, $message);
}
$message = sprintf("%s%s<bg=red;fg=white> %s </> <fg=red>%s</>\n", $trailingEOL, $this->getBorder($id), $prefix, $message);
unset($this->started[$id]['out'], $this->started[$id]['err']);
return $message;
}
private function getBorder(string $id): string
{
return sprintf('<bg=%s> </>', self::COLORS[$this->started[$id]['border']]);
}
public function getName()
{
return 'debug_formatter';
}
}
<?php
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\InvalidArgumentException;
/**
@implements
*/
class HelperSet implements \IteratorAggregate
{
private $helpers = [];
private $command;
public function __construct(array $helpers = [])
{
foreach ($helpers as $alias => $helper) {
$this->set($helper, \is_int($alias) ? null : $alias);
}
}
public function set(HelperInterface $helper, string $alias = null)
{
$this->helpers[$helper->getName()] = $helper;
if (null !== $alias) {
$this->helpers[$alias] = $helper;
}
$helper->setHelperSet($this);
}
public function has(string $name)
{
return isset($this->helpers[$name]);
}
public function get(string $name)
{
if (!$this->has($name)) {
throw new InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name));
}
return $this->helpers[$name];
}
public function setCommand(Command $command = null)
{
trigger_deprecation('symfony/console', '5.4', 'Method "%s()" is deprecated.', __METHOD__);
$this->command = $command;
}
public function getCommand()
{
trigger_deprecation('symfony/console', '5.4', 'Method "%s()" is deprecated.', __METHOD__);
return $this->command;
}
#[\ReturnTypeWillChange]
public function getIterator()
{
return new \ArrayIterator($this->helpers);
}
}
<?php
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\VarDumper\Cloner\ClonerInterface;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\CliDumper;
final class Dumper
{
private $output;
private $dumper;
private $cloner;
private $handler;
public function __construct(OutputInterface $output, CliDumper $dumper = null, ClonerInterface $cloner = null)
{
$this->output = $output;
$this->dumper = $dumper;
$this->cloner = $cloner;
if (class_exists(CliDumper::class)) {
$this->handler = function ($var): string {
$dumper = $this->dumper ?? $this->dumper = new CliDumper(null, null, CliDumper::DUMP_LIGHT_ARRAY | CliDumper::DUMP_COMMA_SEPARATOR);
$dumper->setColors($this->output->isDecorated());
return rtrim($dumper->dump(($this->cloner ?? $this->cloner = new VarCloner())->cloneVar($var)->withRefHandles(false), true));
};
} else {
$this->handler = function ($var): string {
switch (true) {
case null === $var:
return 'null';
case true === $var:
return 'true';
case false === $var:
return 'false';
case \is_string($var):
return '"'.$var.'"';
default:
return rtrim(print_r($var, true));
}
};
}
}
public function __invoke($var): string
{
return ($this->handler)($var);
}
}
<?php
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Input\InputAwareInterface;
use Symfony\Component\Console\Input\InputInterface;
abstract class InputAwareHelper extends Helper implements InputAwareInterface
{
protected $input;
public function setInput(InputInterface $input)
{
$this->input = $input;
}
}
<?php
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Cursor;
use Symfony\Component\Console\Exception\MissingInputException;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\StreamableInputInterface;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\ConsoleSectionOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Terminal;
use function Symfony\Component\String\s;
class QuestionHelper extends Helper
{
private $inputStream;
private static $stty = true;
private static $stdinIsInteractive;
public function ask(InputInterface $input, OutputInterface $output, Question $question)
{
if ($output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
}
if (!$input->isInteractive()) {
return $this->getDefaultAnswer($question);
}
if ($input instanceof StreamableInputInterface && $stream = $input->getStream()) {
$this->inputStream = $stream;
}
try {
if (!$question->getValidator()) {
return $this->doAsk($output, $question);
}
$interviewer = function () use ($output, $question) {
return $this->doAsk($output, $question);
};
return $this->validateAttempts($interviewer, $output, $question);
} catch (MissingInputException $exception) {
$input->setInteractive(false);
if (null === $fallbackOutput = $this->getDefaultAnswer($question)) {
throw $exception;
}
return $fallbackOutput;
}
}
public function getName()
{
return 'question';
}
public static function disableStty()
{
self::$stty = false;
}
private function doAsk(OutputInterface $output, Question $question)
{
$this->writePrompt($output, $question);
$inputStream = $this->inputStream ?: \STDIN;
$autocomplete = $question->getAutocompleterCallback();
if (null === $autocomplete || !self::$stty || !Terminal::hasSttyAvailable()) {
$ret = false;
if ($question->isHidden()) {
try {
$hiddenResponse = $this->getHiddenResponse($output, $inputStream, $question->isTrimmable());
$ret = $question->isTrimmable() ? trim($hiddenResponse) : $hiddenResponse;
} catch (RuntimeException $e) {
if (!$question->isHiddenFallback()) {
throw $e;
}
}
}
if (false === $ret) {
$ret = $this->readInput($inputStream, $question);
if (false === $ret) {
throw new MissingInputException('Aborted.');
}
if ($question->isTrimmable()) {
$ret = trim($ret);
}
}
} else {
$autocomplete = $this->autocomplete($output, $question, $inputStream, $autocomplete);
$ret = $question->isTrimmable() ? trim($autocomplete) : $autocomplete;
}
if ($output instanceof ConsoleSectionOutput) {
$output->addContent($ret);
}
$ret = \strlen($ret) > 0 ? $ret : $question->getDefault();
if ($normalizer = $question->getNormalizer()) {
return $normalizer($ret);
}
return $ret;
}
private function getDefaultAnswer(Question $question)
{
$default = $question->getDefault();
if (null === $default) {
return $default;
}
if ($validator = $question->getValidator()) {
return \call_user_func($question->getValidator(), $default);
} elseif ($question instanceof ChoiceQuestion) {
$choices = $question->getChoices();
if (!$question->isMultiselect()) {
return $choices[$default] ?? $default;
}
$default = explode(',', $default);
foreach ($default as $k => $v) {
$v = $question->isTrimmable() ? trim($v) : $v;
$default[$k] = $choices[$v] ?? $v;
}
}
return $default;
}
protected function writePrompt(OutputInterface $output, Question $question)
{
$message = $question->getQuestion();
if ($question instanceof ChoiceQuestion) {
$output->writeln(array_merge([
$question->getQuestion(),
], $this->formatChoiceQuestionChoices($question, 'info')));
$message = $question->getPrompt();
}
$output->write($message);
}
protected function formatChoiceQuestionChoices(ChoiceQuestion $question, string $tag)
{
$messages = [];
$maxWidth = max(array_map([__CLASS__, 'width'], array_keys($choices = $question->getChoices())));
foreach ($choices as $key => $value) {
$padding = str_repeat(' ', $maxWidth - self::width($key));
$messages[] = sprintf(" [<$tag>%s$padding</$tag>] %s", $key, $value);
}
return $messages;
}
protected function writeError(OutputInterface $output, \Exception $error)
{
if (null !== $this->getHelperSet() && $this->getHelperSet()->has('formatter')) {
$message = $this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error');
} else {
$message = '<error>'.$error->getMessage().'</error>';
}
$output->writeln($message);
}
private function autocomplete(OutputInterface $output, Question $question, $inputStream, callable $autocomplete): string
{
$cursor = new Cursor($output, $inputStream);
$fullChoice = '';
$ret = '';
$i = 0;
$ofs = -1;
$matches = $autocomplete($ret);
$numMatches = \count($matches);
$sttyMode = shell_exec('stty -g');
$isStdin = 'php://stdin' === (stream_get_meta_data($inputStream)['uri'] ?? null);
$r = [$inputStream];
$w = [];
shell_exec('stty -icanon -echo');
$output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white'));
while (!feof($inputStream)) {
while ($isStdin && 0 === @stream_select($r, $w, $w, 0, 100)) {
$r = [$inputStream];
}
$c = fread($inputStream, 1);
if (false === $c || ('' === $ret && '' === $c && null === $question->getDefault())) {
shell_exec('stty '.$sttyMode);
throw new MissingInputException('Aborted.');
} elseif ("\177" === $c) {
if (0 === $numMatches && 0 !== $i) {
--$i;
$cursor->moveLeft(s($fullChoice)->slice(-1)->width(false));
$fullChoice = self::substr($fullChoice, 0, $i);
}
if (0 === $i) {
$ofs = -1;
$matches = $autocomplete($ret);
$numMatches = \count($matches);
} else {
$numMatches = 0;
}
$ret = self::substr($ret, 0, $i);
} elseif ("\033" === $c) {
$c .= fread($inputStream, 2);
if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) {
if ('A' === $c[2] && -1 === $ofs) {
$ofs = 0;
}
if (0 === $numMatches) {
continue;
}
$ofs += ('A' === $c[2]) ? -1 : 1;
$ofs = ($numMatches + $ofs) % $numMatches;
}
} elseif (\ord($c) < 32) {
if ("\t" === $c || "\n" === $c) {
if ($numMatches > 0 && -1 !== $ofs) {
$ret = (string) $matches[$ofs];
$remainingCharacters = substr($ret, \strlen(trim($this->mostRecentlyEnteredValue($fullChoice))));
$output->write($remainingCharacters);
$fullChoice .= $remainingCharacters;
$i = (false === $encoding = mb_detect_encoding($fullChoice, null, true)) ? \strlen($fullChoice) : mb_strlen($fullChoice, $encoding);
$matches = array_filter(
$autocomplete($ret),
function ($match) use ($ret) {
return '' === $ret || str_starts_with($match, $ret);
}
);
$numMatches = \count($matches);
$ofs = -1;
}
if ("\n" === $c) {
$output->write($c);
break;
}
$numMatches = 0;
}
continue;
} else {
if ("\x80" <= $c) {
$c .= fread($inputStream, ["\xC0" => 1, "\xD0" => 1, "\xE0" => 2, "\xF0" => 3][$c & "\xF0"]);
}
$output->write($c);
$ret .= $c;
$fullChoice .= $c;
++$i;
$tempRet = $ret;
if ($question instanceof ChoiceQuestion && $question->isMultiselect()) {
$tempRet = $this->mostRecentlyEnteredValue($fullChoice);
}
$numMatches = 0;
$ofs = 0;
foreach ($autocomplete($ret) as $value) {
if (str_starts_with($value, $tempRet)) {
$matches[$numMatches++] = $value;
}
}
}
$cursor->clearLineAfter();
if ($numMatches > 0 && -1 !== $ofs) {
$cursor->savePosition();
$charactersEntered = \strlen(trim($this->mostRecentlyEnteredValue($fullChoice)));
$output->write('<hl>'.OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $charactersEntered)).'</hl>');
$cursor->restorePosition();
}
}
shell_exec('stty '.$sttyMode);
return $fullChoice;
}
private function mostRecentlyEnteredValue(string $entered): string
{
if (!str_contains($entered, ',')) {
return $entered;
}
$choices = explode(',', $entered);
if ('' !== $lastChoice = trim($choices[\count($choices) - 1])) {
return $lastChoice;
}
return $entered;
}
private function getHiddenResponse(OutputInterface $output, $inputStream, bool $trimmable = true): string
{
if ('\\' === \DIRECTORY_SEPARATOR) {
$exe = __DIR__.'/../Resources/bin/hiddeninput.exe';
if ('phar:' === substr(__FILE__, 0, 5)) {
$tmpExe = sys_get_temp_dir().'/hiddeninput.exe';
copy($exe, $tmpExe);
$exe = $tmpExe;
}
$sExec = shell_exec('"'.$exe.'"');
$value = $trimmable ? rtrim($sExec) : $sExec;
$output->writeln('');
if (isset($tmpExe)) {
unlink($tmpExe);
}
return $value;
}
if (self::$stty && Terminal::hasSttyAvailable()) {
$sttyMode = shell_exec('stty -g');
shell_exec('stty -echo');
} elseif ($this->isInteractiveInput($inputStream)) {
throw new RuntimeException('Unable to hide the response.');
}
$value = fgets($inputStream, 4096);
if (self::$stty && Terminal::hasSttyAvailable()) {
shell_exec('stty '.$sttyMode);
}
if (false === $value) {
throw new MissingInputException('Aborted.');
}
if ($trimmable) {
$value = trim($value);
}
$output->writeln('');
return $value;
}
private function validateAttempts(callable $interviewer, OutputInterface $output, Question $question)
{
$error = null;
$attempts = $question->getMaxAttempts();
while (null === $attempts || $attempts--) {
if (null !== $error) {
$this->writeError($output, $error);
}
try {
return $question->getValidator()($interviewer());
} catch (RuntimeException $e) {
throw $e;
} catch (\Exception $error) {
}
}
throw $error;
}
private function isInteractiveInput($inputStream): bool
{
if ('php://stdin' !== (stream_get_meta_data($inputStream)['uri'] ?? null)) {
return false;
}
if (null !== self::$stdinIsInteractive) {
return self::$stdinIsInteractive;
}
if (\function_exists('stream_isatty')) {
return self::$stdinIsInteractive = @stream_isatty(fopen('php://stdin', 'r'));
}
if (\function_exists('posix_isatty')) {
return self::$stdinIsInteractive = @posix_isatty(fopen('php://stdin', 'r'));
}
if (!\function_exists('exec')) {
return self::$stdinIsInteractive = true;
}
exec('stty 2> /dev/null', $output, $status);
return self::$stdinIsInteractive = 1 !== $status;
}
private function readInput($inputStream, Question $question)
{
if (!$question->isMultiline()) {
$cp = $this->setIOCodepage();
$ret = fgets($inputStream, 4096);
return $this->resetIOCodepage($cp, $ret);
}
$multiLineStreamReader = $this->cloneInputStream($inputStream);
if (null === $multiLineStreamReader) {
return false;
}
$ret = '';
$cp = $this->setIOCodepage();
while (false !== ($char = fgetc($multiLineStreamReader))) {
if (\PHP_EOL === "{$ret}{$char}") {
break;
}
$ret .= $char;
}
return $this->resetIOCodepage($cp, $ret);
}
private function setIOCodepage(): int
{
if (\function_exists('sapi_windows_cp_set')) {
$cp = sapi_windows_cp_get();
sapi_windows_cp_set(sapi_windows_cp_get('oem'));
return $cp;
}
return 0;
}
private function resetIOCodepage(int $cp, $input)
{
if (0 !== $cp) {
sapi_windows_cp_set($cp);
if (false !== $input && '' !== $input) {
$input = sapi_windows_cp_conv(sapi_windows_cp_get('oem'), $cp, $input);
}
}
return $input;
}
private function cloneInputStream($inputStream)
{
$streamMetaData = stream_get_meta_data($inputStream);
$seekable = $streamMetaData['seekable'] ?? false;
$mode = $streamMetaData['mode'] ?? 'rb';
$uri = $streamMetaData['uri'] ?? null;
if (null === $uri) {
return null;
}
$cloneStream = fopen($uri, $mode);
if (true === $seekable && !\in_array($mode, ['r', 'rb', 'rt'])) {
$offset = ftell($inputStream);
rewind($inputStream);
stream_copy_to_stream($inputStream, $cloneStream);
fseek($inputStream, $offset);
fseek($cloneStream, $offset);
}
return $cloneStream;
}
}
<?php
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
use Symfony\Component\String\UnicodeString;
abstract class Helper implements HelperInterface
{
protected $helperSet = null;
public function setHelperSet(HelperSet $helperSet = null)
{
$this->helperSet = $helperSet;
}
public function getHelperSet()
{
return $this->helperSet;
}
public static function strlen(?string $string)
{
trigger_deprecation('symfony/console', '5.3', 'Method "%s()" is deprecated and will be removed in Symfony 6.0. Use Helper::width() or Helper::length() instead.', __METHOD__);
return self::width($string);
}
public static function width(?string $string): int
{
$string ?? $string = '';
if (preg_match('//u', $string)) {
return (new UnicodeString($string))->width(false);
}
if (false === $encoding = mb_detect_encoding($string, null, true)) {
return \strlen($string);
}
return mb_strwidth($string, $encoding);
}
public static function length(?string $string): int
{
$string ?? $string = '';
if (preg_match('//u', $string)) {
return (new UnicodeString($string))->length();
}
if (false === $encoding = mb_detect_encoding($string, null, true)) {
return \strlen($string);
}
return mb_strlen($string, $encoding);
}
public static function substr(?string $string, int $from, int $length = null)
{
$string ?? $string = '';
if (false === $encoding = mb_detect_encoding($string, null, true)) {
return substr($string, $from, $length);
}
return mb_substr($string, $from, $length, $encoding);
}
public static function formatTime($secs)
{
static $timeFormats = [
[0, '< 1 sec'],
[1, '1 sec'],
[2, 'secs', 1],
[60, '1 min'],
[120, 'mins', 60],
[3600, '1 hr'],
[7200, 'hrs', 3600],
[86400, '1 day'],
[172800, 'days', 86400],
];
foreach ($timeFormats as $index => $format) {
if ($secs >= $format[0]) {
if ((isset($timeFormats[$index + 1]) && $secs < $timeFormats[$index + 1][0])
|| $index == \count($timeFormats) - 1
) {
if (2 == \count($format)) {
return $format[1];
}
return floor($secs / $format[2]).' '.$format[1];
}
}
}
}
public static function formatMemory(int $memory)
{
if ($memory >= 1024 * 1024 * 1024) {
return sprintf('%.1f GiB', $memory / 1024 / 1024 / 1024);
}
if ($memory >= 1024 * 1024) {
return sprintf('%.1f MiB', $memory / 1024 / 1024);
}
if ($memory >= 1024) {
return sprintf('%d KiB', $memory / 1024);
}
return sprintf('%d B', $memory);
}
public static function strlenWithoutDecoration(OutputFormatterInterface $formatter, ?string $string)
{
trigger_deprecation('symfony/console', '5.3', 'Method "%s()" is deprecated and will be removed in Symfony 6.0. Use Helper::removeDecoration() instead.', __METHOD__);
return self::width(self::removeDecoration($formatter, $string));
}
public static function removeDecoration(OutputFormatterInterface $formatter, ?string $string)
{
$isDecorated = $formatter->isDecorated();
$formatter->setDecorated(false);
$string = $formatter->format($string ?? '');
$string = preg_replace("/\033\[[^m]*m/", '', $string ?? '');
$string = preg_replace('/\\033]8;[^;]*;[^\\033]*\\033\\\\/', '', $string ?? '');
$formatter->setDecorated($isDecorated);
return $string;
}
}
<?php
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Formatter\WrappableOutputFormatterInterface;
use Symfony\Component\Console\Output\ConsoleSectionOutput;
use Symfony\Component\Console\Output\OutputInterface;
class Table
{
private const SEPARATOR_TOP = 0;
private const SEPARATOR_TOP_BOTTOM = 1;
private const SEPARATOR_MID = 2;
private const SEPARATOR_BOTTOM = 3;
private const BORDER_OUTSIDE = 0;
private const BORDER_INSIDE = 1;
private $headerTitle;
private $footerTitle;
private $headers = [];
private $rows = [];
private $horizontal = false;
private $effectiveColumnWidths = [];
private $numberOfColumns;
private $output;
private $style;
private $columnStyles = [];
private $columnWidths = [];
private $columnMaxWidths = [];
private static $styles;
private $rendered = false;
public function __construct(OutputInterface $output)
{
$this->output = $output;
if (!self::$styles) {
self::$styles = self::initStyles();
}
$this->setStyle('default');
}
public static function setStyleDefinition(string $name, TableStyle $style)
{
if (!self::$styles) {
self::$styles = self::initStyles();
}
self::$styles[$name] = $style;
}
public static function getStyleDefinition(string $name)
{
if (!self::$styles) {
self::$styles = self::initStyles();
}
if (isset(self::$styles[$name])) {
return self::$styles[$name];
}
throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
}
public function setStyle($name)
{
$this->style = $this->resolveStyle($name);
return $this;
}
public function getStyle()
{
return $this->style;
}
public function setColumnStyle(int $columnIndex, $name)
{
$this->columnStyles[$columnIndex] = $this->resolveStyle($name);
return $this;
}
public function getColumnStyle(int $columnIndex)
{
return $this->columnStyles[$columnIndex] ?? $this->getStyle();
}
public function setColumnWidth(int $columnIndex, int $width)
{
$this->columnWidths[$columnIndex] = $width;
return $this;
}
public function setColumnWidths(array $widths)
{
$this->columnWidths = [];
foreach ($widths as $index => $width) {
$this->setColumnWidth($index, $width);
}
return $this;
}
public function setColumnMaxWidth(int $columnIndex, int $width): self
{
if (!$this->output->getFormatter() instanceof WrappableOutputFormatterInterface) {
throw new \LogicException(sprintf('Setting a maximum column width is only supported when using a "%s" formatter, got "%s".', WrappableOutputFormatterInterface::class, get_debug_type($this->output->getFormatter())));
}
$this->columnMaxWidths[$columnIndex] = $width;
return $this;
}
public function setHeaders(array $headers)
{
$headers = array_values($headers);
if (!empty($headers) && !\is_array($headers[0])) {
$headers = [$headers];
}
$this->headers = $headers;
return $this;
}
public function setRows(array $rows)
{
$this->rows = [];
return $this->addRows($rows);
}
public function addRows(array $rows)
{
foreach ($rows as $row) {
$this->addRow($row);
}
return $this;
}
public function addRow($row)
{
if ($row instanceof TableSeparator) {
$this->rows[] = $row;
return $this;
}
if (!\is_array($row)) {
throw new InvalidArgumentException('A row must be an array or a TableSeparator instance.');
}
$this->rows[] = array_values($row);
return $this;
}
public function appendRow($row): self
{
if (!$this->output instanceof ConsoleSectionOutput) {
throw new RuntimeException(sprintf('Output should be an instance of "%s" when calling "%s".', ConsoleSectionOutput::class, __METHOD__));
}
if ($this->rendered) {
$this->output->clear($this->calculateRowCount());
}
$this->addRow($row);
$this->render();
return $this;
}
public function setRow($column, array $row)
{
$this->rows[$column] = $row;
return $this;
}
public function setHeaderTitle(?string $title): self
{
$this->headerTitle = $title;
return $this;
}
public function setFooterTitle(?string $title): self
{
$this->footerTitle = $title;
return $this;
}
public function setHorizontal(bool $horizontal = true): self
{
$this->horizontal = $horizontal;
return $this;
}
public function render()
{
$divider = new TableSeparator();
if ($this->horizontal) {
$rows = [];
foreach ($this->headers[0] ?? [] as $i => $header) {
$rows[$i] = [$header];
foreach ($this->rows as $row) {
if ($row instanceof TableSeparator) {
continue;
}
if (isset($row[$i])) {
$rows[$i][] = $row[$i];
} elseif ($rows[$i][0] instanceof TableCell && $rows[$i][0]->getColspan() >= 2) {
} else {
$rows[$i][] = null;
}
}
}
} else {
$rows = array_merge($this->headers, [$divider], $this->rows);
}
$this->calculateNumberOfColumns($rows);
$rowGroups = $this->buildTableRows($rows);
$this->calculateColumnsWidth($rowGroups);
$isHeader = !$this->horizontal;
$isFirstRow = $this->horizontal;
$hasTitle = (bool) $this->headerTitle;
foreach ($rowGroups as $rowGroup) {
$isHeaderSeparatorRendered = false;
foreach ($rowGroup as $row) {
if ($divider === $row) {
$isHeader = false;
$isFirstRow = true;
continue;
}
if ($row instanceof TableSeparator) {
$this->renderRowSeparator();
continue;
}
if (!$row) {
continue;
}
if ($isHeader && !$isHeaderSeparatorRendered) {
$this->renderRowSeparator(
$isHeader ? self::SEPARATOR_TOP : self::SEPARATOR_TOP_BOTTOM,
$hasTitle ? $this->headerTitle : null,
$hasTitle ? $this->style->getHeaderTitleFormat() : null
);
$hasTitle = false;
$isHeaderSeparatorRendered = true;
}
if ($isFirstRow) {
$this->renderRowSeparator(
$isHeader ? self::SEPARATOR_TOP : self::SEPARATOR_TOP_BOTTOM,
$hasTitle ? $this->headerTitle : null,
$hasTitle ? $this->style->getHeaderTitleFormat() : null
);
$isFirstRow = false;
$hasTitle = false;
}
if ($this->horizontal) {
$this->renderRow($row, $this->style->getCellRowFormat(), $this->style->getCellHeaderFormat());
} else {
$this->renderRow($row, $isHeader ? $this->style->getCellHeaderFormat() : $this->style->getCellRowFormat());
}
}
}
$this->renderRowSeparator(self::SEPARATOR_BOTTOM, $this->footerTitle, $this->style->getFooterTitleFormat());
$this->cleanup();
$this->rendered = true;
}
private function renderRowSeparator(int $type = self::SEPARATOR_MID, string $title = null, string $titleFormat = null)
{
if (0 === $count = $this->numberOfColumns) {
return;
}
$borders = $this->style->getBorderChars();
if (!$borders[0] && !$borders[2] && !$this->style->getCrossingChar()) {
return;
}
$crossings = $this->style->getCrossingChars();
if (self::SEPARATOR_MID === $type) {
[$horizontal, $leftChar, $midChar, $rightChar] = [$borders[2], $crossings[8], $crossings[0], $crossings[4]];
} elseif (self::SEPARATOR_TOP === $type) {
[$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[1], $crossings[2], $crossings[3]];
} elseif (self::SEPARATOR_TOP_BOTTOM === $type) {
[$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[9], $crossings[10], $crossings[11]];
} else {
[$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[7], $crossings[6], $crossings[5]];
}
$markup = $leftChar;
for ($column = 0; $column < $count; ++$column) {
$markup .= str_repeat($horizontal, $this->effectiveColumnWidths[$column]);
$markup .= $column === $count - 1 ? $rightChar : $midChar;
}
if (null !== $title) {
$titleLength = Helper::width(Helper::removeDecoration($formatter = $this->output->getFormatter(), $formattedTitle = sprintf($titleFormat, $title)));
$markupLength = Helper::width($markup);
if ($titleLength > $limit = $markupLength - 4) {
$titleLength = $limit;
$formatLength = Helper::width(Helper::removeDecoration($formatter, sprintf($titleFormat, '')));
$formattedTitle = sprintf($titleFormat, Helper::substr($title, 0, $limit - $formatLength - 3).'...');
}
$titleStart = intdiv($markupLength - $titleLength, 2);
if (false === mb_detect_encoding($markup, null, true)) {
$markup = substr_replace($markup, $formattedTitle, $titleStart, $titleLength);
} else {
$markup = mb_substr($markup, 0, $titleStart).$formattedTitle.mb_substr($markup, $titleStart + $titleLength);
}
}
$this->output->writeln(sprintf($this->style->getBorderFormat(), $markup));
}
private function renderColumnSeparator(int $type = self::BORDER_OUTSIDE): string
{
$borders = $this->style->getBorderChars();
return sprintf($this->style->getBorderFormat(), self::BORDER_OUTSIDE === $type ? $borders[1] : $borders[3]);
}
private function renderRow(array $row, string $cellFormat, string $firstCellFormat = null)
{
$rowContent = $this->renderColumnSeparator(self::BORDER_OUTSIDE);
$columns = $this->getRowColumns($row);
$last = \count($columns) - 1;
foreach ($columns as $i => $column) {
if ($firstCellFormat && 0 === $i) {
$rowContent .= $this->renderCell($row, $column, $firstCellFormat);
} else {
$rowContent .= $this->renderCell($row, $column, $cellFormat);
}
$rowContent .= $this->renderColumnSeparator($last === $i ? self::BORDER_OUTSIDE : self::BORDER_INSIDE);
}
$this->output->writeln($rowContent);
}
private function renderCell(array $row, int $column, string $cellFormat): string
{
$cell = $row[$column] ?? '';
$width = $this->effectiveColumnWidths[$column];
if ($cell instanceof TableCell && $cell->getColspan() > 1) {
foreach (range($column + 1, $column + $cell->getColspan() - 1) as $nextColumn) {
$width += $this->getColumnSeparatorWidth() + $this->effectiveColumnWidths[$nextColumn];
}
}
if (false !== $encoding = mb_detect_encoding($cell, null, true)) {
$width += \strlen($cell) - mb_strwidth($cell, $encoding);
}
$style = $this->getColumnStyle($column);
if ($cell instanceof TableSeparator) {
return sprintf($style->getBorderFormat(), str_repeat($style->getBorderChars()[2], $width));
}
$width += Helper::length($cell) - Helper::length(Helper::removeDecoration($this->output->getFormatter(), $cell));
$content = sprintf($style->getCellRowContentFormat(), $cell);
$padType = $style->getPadType();
if ($cell instanceof TableCell && $cell->getStyle() instanceof TableCellStyle) {
$isNotStyledByTag = !preg_match('/^<(\w+|(\w+=[\w,]+;?)*)>.+<\/(\w+|(\w+=\w+;?)*)?>$/', $cell);
if ($isNotStyledByTag) {
$cellFormat = $cell->getStyle()->getCellFormat();
if (!\is_string($cellFormat)) {
$tag = http_build_query($cell->getStyle()->getTagOptions(), '', ';');
$cellFormat = '<'.$tag.'>%s</>';
}
if (strstr($content, '</>')) {
$content = str_replace('</>', '', $content);
$width -= 3;
}
if (strstr($content, '<fg=default;bg=default>')) {
$content = str_replace('<fg=default;bg=default>', '', $content);
$width -= \strlen('<fg=default;bg=default>');
}
}
$padType = $cell->getStyle()->getPadByAlign();
}
return sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $padType));
}
private function calculateNumberOfColumns(array $rows)
{
$columns = [0];
foreach ($rows as $row) {
if ($row instanceof TableSeparator) {
continue;
}
$columns[] = $this->getNumberOfColumns($row);
}
$this->numberOfColumns = max($columns);
}
private function buildTableRows(array $rows): TableRows
{
$formatter = $this->output->getFormatter();
$unmergedRows = [];
for ($rowKey = 0; $rowKey < \count($rows); ++$rowKey) {
$rows = $this->fillNextRows($rows, $rowKey);
foreach ($rows[$rowKey] as $column => $cell) {
$colspan = $cell instanceof TableCell ? $cell->getColspan() : 1;
if (isset($this->columnMaxWidths[$column]) && Helper::width(Helper::removeDecoration($formatter, $cell)) > $this->columnMaxWidths[$column]) {
$cell = $formatter->formatAndWrap($cell, $this->columnMaxWidths[$column] * $colspan);
}
if (!strstr($cell ?? '', "\n")) {
continue;
}
$escaped = implode("\n", array_map([OutputFormatter::class, 'escapeTrailingBackslash'], explode("\n", $cell)));
$cell = $cell instanceof TableCell ? new TableCell($escaped, ['colspan' => $cell->getColspan()]) : $escaped;
$lines = explode("\n", str_replace("\n", "<fg=default;bg=default></>\n", $cell));
foreach ($lines as $lineKey => $line) {
if ($colspan > 1) {
$line = new TableCell($line, ['colspan' => $colspan]);
}
if (0 === $lineKey) {
$rows[$rowKey][$column] = $line;
} else {
if (!\array_key_exists($rowKey, $unmergedRows) || !\array_key_exists($lineKey, $unmergedRows[$rowKey])) {
$unmergedRows[$rowKey][$lineKey] = $this->copyRow($rows, $rowKey);
}
$unmergedRows[$rowKey][$lineKey][$column] = $line;
}
}
}
}
return new TableRows(function () use ($rows, $unmergedRows): \Traversable {
foreach ($rows as $rowKey => $row) {
$rowGroup = [$row instanceof TableSeparator ? $row : $this->fillCells($row)];
if (isset($unmergedRows[$rowKey])) {
foreach ($unmergedRows[$rowKey] as $row) {
$rowGroup[] = $row instanceof TableSeparator ? $row : $this->fillCells($row);
}
}
yield $rowGroup;
}
});
}
private function calculateRowCount(): int
{
$numberOfRows = \count(iterator_to_array($this->buildTableRows(array_merge($this->headers, [new TableSeparator()], $this->rows))));
if ($this->headers) {
++$numberOfRows;
}
if (\count($this->rows) > 0) {
++$numberOfRows;
}
return $numberOfRows;
}
private function fillNextRows(array $rows, int $line): array
{
$unmergedRows = [];
foreach ($rows[$line] as $column => $cell) {
if (null !== $cell && !$cell instanceof TableCell && !\is_scalar($cell) && !(\is_object($cell) && method_exists($cell, '__toString'))) {
throw new InvalidArgumentException(sprintf('A cell must be a TableCell, a scalar or an object implementing "__toString()", "%s" given.', get_debug_type($cell)));
}
if ($cell instanceof TableCell && $cell->getRowspan() > 1) {
$nbLines = $cell->getRowspan() - 1;
$lines = [$cell];
if (strstr($cell, "\n")) {
$lines = explode("\n", str_replace("\n", "<fg=default;bg=default>\n</>", $cell));
$nbLines = \count($lines) > $nbLines ? substr_count($cell, "\n") : $nbLines;
$rows[$line][$column] = new TableCell($lines[0], ['colspan' => $cell->getColspan(), 'style' => $cell->getStyle()]);
unset($lines[0]);
}
$unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, []), $unmergedRows);
foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) {
$value = $lines[$unmergedRowKey - $line] ?? '';
$unmergedRows[$unmergedRowKey][$column] = new TableCell($value, ['colspan' => $cell->getColspan(), 'style' => $cell->getStyle()]);
if ($nbLines === $unmergedRowKey - $line) {
break;
}
}
}
}
foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) {
if (isset($rows[$unmergedRowKey]) && \is_array($rows[$unmergedRowKey]) && ($this->getNumberOfColumns($rows[$unmergedRowKey]) + $this->getNumberOfColumns($unmergedRows[$unmergedRowKey]) <= $this->numberOfColumns)) {
foreach ($unmergedRow as $cellKey => $cell) {
array_splice($rows[$unmergedRowKey], $cellKey, 0, [$cell]);
}
} else {
$row = $this->copyRow($rows, $unmergedRowKey - 1);
foreach ($unmergedRow as $column => $cell) {
if (!empty($cell)) {
$row[$column] = $unmergedRow[$column];
}
}
array_splice($rows, $unmergedRowKey, 0, [$row]);
}
}
return $rows;
}
private function fillCells(iterable $row)
{
$newRow = [];
foreach ($row as $column => $cell) {
$newRow[] = $cell;
if ($cell instanceof TableCell && $cell->getColspan() > 1) {
foreach (range($column + 1, $column + $cell->getColspan() - 1) as $position) {
$newRow[] = '';
}
}
}
return $newRow ?: $row;
}
private function copyRow(array $rows, int $line): array
{
$row = $rows[$line];
foreach ($row as $cellKey => $cellValue) {
$row[$cellKey] = '';
if ($cellValue instanceof TableCell) {
$row[$cellKey] = new TableCell('', ['colspan' => $cellValue->getColspan()]);
}
}
return $row;
}
private function getNumberOfColumns(array $row): int
{
$columns = \count($row);
foreach ($row as $column) {
$columns += $column instanceof TableCell ? ($column->getColspan() - 1) : 0;
}
return $columns;
}
private function getRowColumns(array $row): array
{
$columns = range(0, $this->numberOfColumns - 1);
foreach ($row as $cellKey => $cell) {
if ($cell instanceof TableCell && $cell->getColspan() > 1) {
$columns = array_diff($columns, range($cellKey + 1, $cellKey + $cell->getColspan() - 1));
}
}
return $columns;
}
private function calculateColumnsWidth(iterable $groups)
{
for ($column = 0; $column < $this->numberOfColumns; ++$column) {
$lengths = [];
foreach ($groups as $group) {
foreach ($group as $row) {
if ($row instanceof TableSeparator) {
continue;
}
foreach ($row as $i => $cell) {
if ($cell instanceof TableCell) {
$textContent = Helper::removeDecoration($this->output->getFormatter(), $cell);
$textLength = Helper::width($textContent);
if ($textLength > 0) {
$contentColumns = str_split($textContent, ceil($textLength / $cell->getColspan()));
foreach ($contentColumns as $position => $content) {
$row[$i + $position] = $content;
}
}
}
}
$lengths[] = $this->getCellWidth($row, $column);
}
}
$this->effectiveColumnWidths[$column] = max($lengths) + Helper::width($this->style->getCellRowContentFormat()) - 2;
}
}
private function getColumnSeparatorWidth(): int
{
return Helper::width(sprintf($this->style->getBorderFormat(), $this->style->getBorderChars()[3]));
}
private function getCellWidth(array $row, int $column): int
{
$cellWidth = 0;
if (isset($row[$column])) {
$cell = $row[$column];
$cellWidth = Helper::width(Helper::removeDecoration($this->output->getFormatter(), $cell));
}
$columnWidth = $this->columnWidths[$column] ?? 0;
$cellWidth = max($cellWidth, $columnWidth);
return isset($this->columnMaxWidths[$column]) ? min($this->columnMaxWidths[$column], $cellWidth) : $cellWidth;
}
private function cleanup()
{
$this->effectiveColumnWidths = [];
$this->numberOfColumns = null;
}
private static function initStyles(): array
{
$borderless = new TableStyle();
$borderless
->setHorizontalBorderChars('=')
->setVerticalBorderChars(' ')
->setDefaultCrossingChar(' ')
;
$compact = new TableStyle();
$compact
->setHorizontalBorderChars('')
->setVerticalBorderChars('')
->setDefaultCrossingChar('')
->setCellRowContentFormat('%s ')
;
$styleGuide = new TableStyle();
$styleGuide
->setHorizontalBorderChars('-')
->setVerticalBorderChars(' ')
->setDefaultCrossingChar(' ')
->setCellHeaderFormat('%s')
;
$box = (new TableStyle())
->setHorizontalBorderChars('─')
->setVerticalBorderChars('│')
->setCrossingChars('┼', '┌', '┬', '┐', '┤', '┘', '┴', '└', '├')
;
$boxDouble = (new TableStyle())
->setHorizontalBorderChars('═', '─')
->setVerticalBorderChars('║', '│')
->setCrossingChars('┼', '╔', '╤', '╗', '╢', '╝', '╧', '╚', '╟', '╠', '╪', '╣')
;
return [
'default' => new TableStyle(),
'borderless' => $borderless,
'compact' => $compact,
'symfony-style-guide' => $styleGuide,
'box' => $box,
'box-double' => $boxDouble,
];
}
private function resolveStyle($name): TableStyle
{
if ($name instanceof TableStyle) {
return $name;
}
if (isset(self::$styles[$name])) {
return self::$styles[$name];
}
throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
}
}
<?php
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Console\Output\OutputInterface;
class ProgressIndicator
{
private const FORMATS = [
'normal' => ' %indicator% %message%',
'normal_no_ansi' => ' %message%',
'verbose' => ' %indicator% %message% (%elapsed:6s%)',
'verbose_no_ansi' => ' %message% (%elapsed:6s%)',
'very_verbose' => ' %indicator% %message% (%elapsed:6s%, %memory:6s%)',
'very_verbose_no_ansi' => ' %message% (%elapsed:6s%, %memory:6s%)',
];
private $output;
private $startTime;
private $format;
private $message;
private $indicatorValues;
private $indicatorCurrent;
private $indicatorChangeInterval;
private $indicatorUpdateTime;
private $started = false;
private static $formatters;
public function __construct(OutputInterface $output, string $format = null, int $indicatorChangeInterval = 100, array $indicatorValues = null)
{
$this->output = $output;
if (null === $format) {
$format = $this->determineBestFormat();
}
if (null === $indicatorValues) {
$indicatorValues = ['-', '\\', '|', '/'];
}
$indicatorValues = array_values($indicatorValues);
if (2 > \count($indicatorValues)) {
throw new InvalidArgumentException('Must have at least 2 indicator value characters.');
}
$this->format = self::getFormatDefinition($format);
$this->indicatorChangeInterval = $indicatorChangeInterval;
$this->indicatorValues = $indicatorValues;
$this->startTime = time();
}
public function setMessage(?string $message)
{
$this->message = $message;
$this->display();
}
public function start(string $message)
{
if ($this->started) {
throw new LogicException('Progress indicator already started.');
}
$this->message = $message;
$this->started = true;
$this->startTime = time();
$this->indicatorUpdateTime = $this->getCurrentTimeInMilliseconds() + $this->indicatorChangeInterval;
$this->indicatorCurrent = 0;
$this->display();
}
public function advance()
{
if (!$this->started) {
throw new LogicException('Progress indicator has not yet been started.');
}
if (!$this->output->isDecorated()) {
return;
}
$currentTime = $this->getCurrentTimeInMilliseconds();
if ($currentTime < $this->indicatorUpdateTime) {
return;
}
$this->indicatorUpdateTime = $currentTime + $this->indicatorChangeInterval;
++$this->indicatorCurrent;
$this->display();
}
public function finish(string $message)
{
if (!$this->started) {
throw new LogicException('Progress indicator has not yet been started.');
}
$this->message = $message;
$this->display();
$this->output->writeln('');
$this->started = false;
}
public static function getFormatDefinition(string $name)
{
return self::FORMATS[$name] ?? null;
}
public static function setPlaceholderFormatterDefinition(string $name, callable $callable)
{
if (!self::$formatters) {
self::$formatters = self::initPlaceholderFormatters();
}
self::$formatters[$name] = $callable;
}
public static function getPlaceholderFormatterDefinition(string $name)
{
if (!self::$formatters) {
self::$formatters = self::initPlaceholderFormatters();
}
return self::$formatters[$name] ?? null;
}
private function display()
{
if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) {
return;
}
$this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) {
if ($formatter = self::getPlaceholderFormatterDefinition($matches[1])) {
return $formatter($this);
}
return $matches[0];
}, $this->format ?? ''));
}
private function determineBestFormat(): string
{
switch ($this->output->getVerbosity()) {
case OutputInterface::VERBOSITY_VERBOSE:
return $this->output->isDecorated() ? 'verbose' : 'verbose_no_ansi';
case OutputInterface::VERBOSITY_VERY_VERBOSE:
case OutputInterface::VERBOSITY_DEBUG:
return $this->output->isDecorated() ? 'very_verbose' : 'very_verbose_no_ansi';
default:
return $this->output->isDecorated() ? 'normal' : 'normal_no_ansi';
}
}
private function overwrite(string $message)
{
if ($this->output->isDecorated()) {
$this->output->write("\x0D\x1B[2K");
$this->output->write($message);
} else {
$this->output->writeln($message);
}
}
private function getCurrentTimeInMilliseconds(): float
{
return round(microtime(true) * 1000);
}
private static function initPlaceholderFormatters(): array
{
return [
'indicator' => function (self $indicator) {
return $indicator->indicatorValues[$indicator->indicatorCurrent % \count($indicator->indicatorValues)];
},
'message' => function (self $indicator) {
return $indicator->message;
},
'elapsed' => function (self $indicator) {
return Helper::formatTime(time() - $indicator->startTime);
},
'memory' => function () {
return Helper::formatMemory(memory_get_usage(true));
},
];
}
}
<?php
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Descriptor\DescriptorInterface;
use Symfony\Component\Console\Descriptor\JsonDescriptor;
use Symfony\Component\Console\Descriptor\MarkdownDescriptor;
use Symfony\Component\Console\Descriptor\TextDescriptor;
use Symfony\Component\Console\Descriptor\XmlDescriptor;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Output\OutputInterface;
class DescriptorHelper extends Helper
{
private $descriptors = [];
public function __construct()
{
$this
->register('txt', new TextDescriptor())
->register('xml', new XmlDescriptor())
->register('json', new JsonDescriptor())
->register('md', new MarkdownDescriptor())
;
}
public function describe(OutputInterface $output, ?object $object, array $options = [])
{
$options = array_merge([
'raw_text' => false,
'format' => 'txt',
], $options);
if (!isset($this->descriptors[$options['format']])) {
throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $options['format']));
}
$descriptor = $this->descriptors[$options['format']];
$descriptor->describe($output, $object, $options);
}
public function register(string $format, DescriptorInterface $descriptor)
{
$this->descriptors[$format] = $descriptor;
return $this;
}
public function getName()
{
return 'descriptor';
}
public function getFormats(): array
{
return array_keys($this->descriptors);
}
}
<?php
namespace Symfony\Component\Console\DependencyInjection;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Command\LazyCommand;
use Symfony\Component\Console\CommandLoader\ContainerCommandLoader;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\TypedReference;
class AddConsoleCommandPass implements CompilerPassInterface
{
private $commandLoaderServiceId;
private $commandTag;
private $noPreloadTag;
private $privateTagName;
public function __construct(string $commandLoaderServiceId = 'console.command_loader', string $commandTag = 'console.command', string $noPreloadTag = 'container.no_preload', string $privateTagName = 'container.private')
{
if (0 < \func_num_args()) {
trigger_deprecation('symfony/console', '5.3', 'Configuring "%s" is deprecated.', __CLASS__);
}
$this->commandLoaderServiceId = $commandLoaderServiceId;
$this->commandTag = $commandTag;
$this->noPreloadTag = $noPreloadTag;
$this->privateTagName = $privateTagName;
}
public function process(ContainerBuilder $container)
{
$commandServices = $container->findTaggedServiceIds($this->commandTag, true);
$lazyCommandMap = [];
$lazyCommandRefs = [];
$serviceIds = [];
foreach ($commandServices as $id => $tags) {
$definition = $container->getDefinition($id);
$definition->addTag($this->noPreloadTag);
$class = $container->getParameterBag()->resolveValue($definition->getClass());
if (isset($tags[0]['command'])) {
$aliases = $tags[0]['command'];
} else {
if (!$r = $container->getReflectionClass($class)) {
throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
}
if (!$r->isSubclassOf(Command::class)) {
throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must be a subclass of "%s".', $id, $this->commandTag, Command::class));
}
$aliases = str_replace('%', '%%', $class::getDefaultName() ?? '');
}
$aliases = explode('|', $aliases ?? '');
$commandName = array_shift($aliases);
if ($isHidden = '' === $commandName) {
$commandName = array_shift($aliases);
}
if (null === $commandName) {
if (!$definition->isPublic() || $definition->isPrivate() || $definition->hasTag($this->privateTagName)) {
$commandId = 'console.command.public_alias.'.$id;
$container->setAlias($commandId, $id)->setPublic(true);
$id = $commandId;
}
$serviceIds[] = $id;
continue;
}
$description = $tags[0]['description'] ?? null;
unset($tags[0]);
$lazyCommandMap[$commandName] = $id;
$lazyCommandRefs[$id] = new TypedReference($id, $class);
foreach ($aliases as $alias) {
$lazyCommandMap[$alias] = $id;
}
foreach ($tags as $tag) {
if (isset($tag['command'])) {
$aliases[] = $tag['command'];
$lazyCommandMap[$tag['command']] = $id;
}
$description = $description ?? $tag['description'] ?? null;
}
$definition->addMethodCall('setName', [$commandName]);
if ($aliases) {
$definition->addMethodCall('setAliases', [$aliases]);
}
if ($isHidden) {
$definition->addMethodCall('setHidden', [true]);
}
if (!$description) {
if (!$r = $container->getReflectionClass($class)) {
throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
}
if (!$r->isSubclassOf(Command::class)) {
throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must be a subclass of "%s".', $id, $this->commandTag, Command::class));
}
$description = str_replace('%', '%%', $class::getDefaultDescription() ?? '');
}
if ($description) {
$definition->addMethodCall('setDescription', [$description]);
$container->register('.'.$id.'.lazy', LazyCommand::class)
->setArguments([$commandName, $aliases, $description, $isHidden, new ServiceClosureArgument($lazyCommandRefs[$id])]);
$lazyCommandRefs[$id] = new Reference('.'.$id.'.lazy');
}
}
$container
->register($this->commandLoaderServiceId, ContainerCommandLoader::class)
->setPublic(true)
->addTag($this->noPreloadTag)
->setArguments([ServiceLocatorTagPass::register($container, $lazyCommandRefs), $lazyCommandMap]);
$container->setParameter('console.command.ids', $serviceIds);
}
}
<?php
namespace Symfony\Component\Console\Exception;
class LogicException extends \LogicException implements ExceptionInterface
{
}
<?php
namespace Symfony\Component\Console\Exception;
class InvalidOptionException extends \InvalidArgumentException implements ExceptionInterface
{
}
<?php
namespace Symfony\Component\Console\Exception;
interface ExceptionInterface extends \Throwable
{
}
<?php
namespace Symfony\Component\Console\Exception;
class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}
<?php
namespace Symfony\Component\Console\Exception;
class NamespaceNotFoundException extends CommandNotFoundException
{
}
<?php
namespace Symfony\Component\Console\Exception;
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
{
}
<?php
namespace Symfony\Component\Console\Exception;
class MissingInputException extends RuntimeException implements ExceptionInterface
{
}
<?php
namespace Symfony\Component\Console\Exception;
class CommandNotFoundException extends \InvalidArgumentException implements ExceptionInterface
{
private $alternatives;
public function __construct(string $message, array $alternatives = [], int $code = 0, \Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
$this->alternatives = $alternatives;
}
public function getAlternatives()
{
return $this->alternatives;
}
}
<?php
namespace Symfony\Component\Console\Event;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
final class ConsoleTerminateEvent extends ConsoleEvent
{
private $exitCode;
public function __construct(Command $command, InputInterface $input, OutputInterface $output, int $exitCode)
{
parent::__construct($command, $input, $output);
$this->setExitCode($exitCode);
}
public function setExitCode(int $exitCode): void
{
$this->exitCode = $exitCode;
}
public function getExitCode(): int
{
return $this->exitCode;
}
}
<?php
namespace Symfony\Component\Console\Event;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
final class ConsoleErrorEvent extends ConsoleEvent
{
private $error;
private $exitCode;
public function __construct(InputInterface $input, OutputInterface $output, \Throwable $error, Command $command = null)
{
parent::__construct($command, $input, $output);
$this->error = $error;
}
public function getError(): \Throwable
{
return $this->error;
}
public function setError(\Throwable $error): void
{
$this->error = $error;
}
public function setExitCode(int $exitCode): void
{
$this->exitCode = $exitCode;
$r = new \ReflectionProperty($this->error, 'code');
$r->setAccessible(true);
$r->setValue($this->error, $this->exitCode);
}
public function getExitCode(): int
{
return $this->exitCode ?? (\is_int($this->error->getCode()) && 0 !== $this->error->getCode() ? $this->error->getCode() : 1);
}
}
<?php
namespace Symfony\Component\Console\Event;
final class ConsoleCommandEvent extends ConsoleEvent
{
public const RETURN_CODE_DISABLED = 113;
private $commandShouldRun = true;
public function disableCommand(): bool
{
return $this->commandShouldRun = false;
}
public function enableCommand(): bool
{
return $this->commandShouldRun = true;
}
public function commandShouldRun(): bool
{
return $this->commandShouldRun;
}
}
<?php
namespace Symfony\Component\Console\Event;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Contracts\EventDispatcher\Event;
class ConsoleEvent extends Event
{
protected $command;
private $input;
private $output;
public function __construct(?Command $command, InputInterface $input, OutputInterface $output)
{
$this->command = $command;
$this->input = $input;
$this->output = $output;
}
public function getCommand()
{
return $this->command;
}
public function getInput()
{
return $this->input;
}
public function getOutput()
{
return $this->output;
}
}
<?php
namespace Symfony\Component\Console\Event;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
final class ConsoleSignalEvent extends ConsoleEvent
{
private $handlingSignal;
public function __construct(Command $command, InputInterface $input, OutputInterface $output, int $handlingSignal)
{
parent::__construct($command, $input, $output);
$this->handlingSignal = $handlingSignal;
}
public function getHandlingSignal(): int
{
return $this->handlingSignal;
}
}
<?php
namespace Symfony\Component\Console\EventListener;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleErrorEvent;
use Symfony\Component\Console\Event\ConsoleEvent;
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class ErrorListener implements EventSubscriberInterface
{
private $logger;
public function __construct(LoggerInterface $logger = null)
{
$this->logger = $logger;
}
public function onConsoleError(ConsoleErrorEvent $event)
{
if (null === $this->logger) {
return;
}
$error = $event->getError();
if (!$inputString = $this->getInputString($event)) {
$this->logger->critical('An error occurred while using the console. Message: "{message}"', ['exception' => $error, 'message' => $error->getMessage()]);
return;
}
$this->logger->critical('Error thrown while running command "{command}". Message: "{message}"', ['exception' => $error, 'command' => $inputString, 'message' => $error->getMessage()]);
}
public function onConsoleTerminate(ConsoleTerminateEvent $event)
{
if (null === $this->logger) {
return;
}
$exitCode = $event->getExitCode();
if (0 === $exitCode) {
return;
}
if (!$inputString = $this->getInputString($event)) {
$this->logger->debug('The console exited with code "{code}"', ['code' => $exitCode]);
return;
}
$this->logger->debug('Command "{command}" exited with code "{code}"', ['command' => $inputString, 'code' => $exitCode]);
}
public static function getSubscribedEvents()
{
return [
ConsoleEvents::ERROR => ['onConsoleError', -128],
ConsoleEvents::TERMINATE => ['onConsoleTerminate', -128],
];
}
private static function getInputString(ConsoleEvent $event): ?string
{
$commandName = $event->getCommand() ? $event->getCommand()->getName() : null;
$input = $event->getInput();
if (method_exists($input, '__toString')) {
if ($commandName) {
return str_replace(["'$commandName'", "\"$commandName\""], $commandName, (string) $input);
}
return (string) $input;
}
return $commandName;
}
}
<?php
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 { 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 (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);
}
<?php
return array (
'A' => 'a',
'B' => 'b',
'C' => 'c',
'D' => 'd',
'E' => 'e',
'F' => 'f',
'G' => 'g',
'H' => 'h',
'I' => 'i',
'J' => 'j',
'K' => 'k',
'L' => 'l',
'M' => 'm',
'N' => 'n',
'O' => 'o',
'P' => 'p',
'Q' => 'q',
'R' => 'r',
'S' => 's',
'T' => 't',
'U' => 'u',
'V' => 'v',
'W' => 'w',
'X' => 'x',
'Y' => 'y',
'Z' => 'z',
'À' => 'à',
'Á' => 'á',
'Â' => 'â',
'Ã' => 'ã',
'Ä' => 'ä',
'Å' => 'å',
'Æ' => 'æ',
'Ç' => 'ç',
'È' => 'è',
'É' => 'é',
'Ê' => 'ê',
'Ë' => 'ë',
'Ì' => 'ì',
'Í' => 'í',
'Î' => 'î',
'Ï' => 'ï',
'Ð' => 'ð',
'Ñ' => 'ñ',
'Ò' => 'ò',
'Ó' => 'ó',
'Ô' => 'ô',
'Õ' => 'õ',
'Ö' => 'ö',
'Ø' => 'ø',
'Ù' => 'ù',
'Ú' => 'ú',
'Û' => 'û',
'Ü' => 'ü',
'Ý' => 'ý',
'Þ' => 'þ',
'Ā' => 'ā',
'Ă' => 'ă',
'Ą' => 'ą',
'Ć' => 'ć',
'Ĉ' => 'ĉ',
'Ċ' => 'ċ',
'Č' => 'č',
'Ď' => 'ď',
'Đ' => 'đ',
'Ē' => 'ē',
'Ĕ' => 'ĕ',
'Ė' => 'ė',
'Ę' => 'ę',
'Ě' => 'ě',
'Ĝ' => 'ĝ',
'Ğ' => 'ğ',
'Ġ' => 'ġ',
'Ģ' => 'ģ',
'Ĥ' => 'ĥ',
'Ħ' => 'ħ',
'Ĩ' => 'ĩ',
'Ī' => 'ī',
'Ĭ' => 'ĭ',
'Į' => 'į',
'İ' => 'i̇',
'IJ' => 'ij',
'Ĵ' => 'ĵ',
'Ķ' => 'ķ',
'Ĺ' => 'ĺ',
'Ļ' => 'ļ',
'Ľ' => 'ľ',
'Ŀ' => 'ŀ',
'Ł' => 'ł',
'Ń' => 'ń',
'Ņ' => 'ņ',
'Ň' => 'ň',
'Ŋ' => 'ŋ',
'Ō' => 'ō',
'Ŏ' => 'ŏ',
'Ő' => 'ő',
'Œ' => 'œ',
'Ŕ' => 'ŕ',
'Ŗ' => 'ŗ',
'Ř' => 'ř',
'Ś' => 'ś',
'Ŝ' => 'ŝ',
'Ş' => 'ş',
'Š' => 'š',
'Ţ' => 'ţ',
'Ť' => 'ť',
'Ŧ' => 'ŧ',
'Ũ' => 'ũ',
'Ū' => 'ū',
'Ŭ' => 'ŭ',
'Ů' => 'ů',
'Ű' => 'ű',
'Ų' => 'ų',
'Ŵ' => 'ŵ',
'Ŷ' => 'ŷ',
'Ÿ' => 'ÿ',
'Ź' => 'ź',
'Ż' => 'ż',
'Ž' => 'ž',
'Ɓ' => 'ɓ',
'Ƃ' => 'ƃ',
'Ƅ' => 'ƅ',
'Ɔ' => 'ɔ',
'Ƈ' => 'ƈ',
'Ɖ' => 'ɖ',
'Ɗ' => 'ɗ',
'Ƌ' => 'ƌ',
'Ǝ' => 'ǝ',
'Ə' => 'ə',
'Ɛ' => 'ɛ',
'Ƒ' => 'ƒ',
'Ɠ' => 'ɠ',
'Ɣ' => 'ɣ',
'Ɩ' => 'ɩ',
'Ɨ' => 'ɨ',
'Ƙ' => 'ƙ',
'Ɯ' => 'ɯ',
'Ɲ' => 'ɲ',
'Ɵ' => 'ɵ',
'Ơ' => 'ơ',
'Ƣ' => 'ƣ',
'Ƥ' => 'ƥ',
'Ʀ' => 'ʀ',
'Ƨ' => 'ƨ',
'Ʃ' => 'ʃ',
'Ƭ' => 'ƭ',
'Ʈ' => 'ʈ',
'Ư' => 'ư',
'Ʊ' => 'ʊ',
'Ʋ' => 'ʋ',
'Ƴ' => 'ƴ',
'Ƶ' => 'ƶ',
'Ʒ' => 'ʒ',
'Ƹ' => 'ƹ',
'Ƽ' => 'ƽ',
'DŽ' => 'dž',
'Dž' => 'dž',
'LJ' => 'lj',
'Lj' => 'lj',
'NJ' => 'nj',
'Nj' => 'nj',
'Ǎ' => 'ǎ',
'Ǐ' => 'ǐ',
'Ǒ' => 'ǒ',
'Ǔ' => 'ǔ',
'Ǖ' => 'ǖ',
'Ǘ' => 'ǘ',
'Ǚ' => 'ǚ',
'Ǜ' => 'ǜ',
'Ǟ' => 'ǟ',
'Ǡ' => 'ǡ',
'Ǣ' => 'ǣ',
'Ǥ' => 'ǥ',
'Ǧ' => 'ǧ',
'Ǩ' => 'ǩ',
'Ǫ' => 'ǫ',
'Ǭ' => 'ǭ',
'Ǯ' => 'ǯ',
'DZ' => 'dz',
'Dz' => 'dz',
'Ǵ' => 'ǵ',
'Ƕ' => 'ƕ',
'Ƿ' => 'ƿ',
'Ǹ' => 'ǹ',
'Ǻ' => 'ǻ',
'Ǽ' => 'ǽ',
'Ǿ' => 'ǿ',
'Ȁ' => 'ȁ',
'Ȃ' => 'ȃ',
'Ȅ' => 'ȅ',
'Ȇ' => 'ȇ',
'Ȉ' => 'ȉ',
'Ȋ' => 'ȋ',
'Ȍ' => 'ȍ',
'Ȏ' => 'ȏ',
'Ȑ' => 'ȑ',
'Ȓ' => 'ȓ',
'Ȕ' => 'ȕ',
'Ȗ' => 'ȗ',
'Ș' => 'ș',
'Ț' => 'ț',
'Ȝ' => 'ȝ',
'Ȟ' => 'ȟ',
'Ƞ' => 'ƞ',
'Ȣ' => 'ȣ',
'Ȥ' => 'ȥ',
'Ȧ' => 'ȧ',
'Ȩ' => 'ȩ',
'Ȫ' => 'ȫ',
'Ȭ' => 'ȭ',
'Ȯ' => 'ȯ',
'Ȱ' => 'ȱ',
'Ȳ' => 'ȳ',
'Ⱥ' => 'ⱥ',
'Ȼ' => 'ȼ',
'Ƚ' => 'ƚ',
'Ⱦ' => 'ⱦ',
'Ɂ' => 'ɂ',
'Ƀ' => 'ƀ',
'Ʉ' => 'ʉ',
'Ʌ' => 'ʌ',
'Ɇ' => 'ɇ',
'Ɉ' => 'ɉ',
'Ɋ' => 'ɋ',
'Ɍ' => 'ɍ',
'Ɏ' => 'ɏ',
'Ͱ' => 'ͱ',
'Ͳ' => 'ͳ',
'Ͷ' => 'ͷ',
'Ϳ' => 'ϳ',
'Ά' => 'ά',
'Έ' => 'έ',
'Ή' => 'ή',
'Ί' => 'ί',
'Ό' => 'ό',
'Ύ' => 'ύ',
'Ώ' => 'ώ',
'Α' => 'α',
'Β' => 'β',
'Γ' => 'γ',
'Δ' => 'δ',
'Ε' => 'ε',
'Ζ' => 'ζ',
'Η' => 'η',
'Θ' => 'θ',
'Ι' => 'ι',
'Κ' => 'κ',
'Λ' => 'λ',
'Μ' => 'μ',
'Ν' => 'ν',
'Ξ' => 'ξ',
'Ο' => 'ο',
'Π' => 'π',
'Ρ' => 'ρ',
'Σ' => 'σ',
'Τ' => 'τ',
'Υ' => 'υ',
'Φ' => 'φ',
'Χ' => 'χ',
'Ψ' => 'ψ',
'Ω' => 'ω',
'Ϊ' => 'ϊ',
'Ϋ' => 'ϋ',
'Ϗ' => 'ϗ',
'Ϙ' => 'ϙ',
'Ϛ' => 'ϛ',
'Ϝ' => 'ϝ',
'Ϟ' => 'ϟ',
'Ϡ' => 'ϡ',
'Ϣ' => 'ϣ',
'Ϥ' => 'ϥ',
'Ϧ' => 'ϧ',
'Ϩ' => 'ϩ',
'Ϫ' => 'ϫ',
'Ϭ' => 'ϭ',
'Ϯ' => 'ϯ',
'ϴ' => 'θ',
'Ϸ' => 'ϸ',
'Ϲ' => 'ϲ',
'Ϻ' => 'ϻ',
'Ͻ' => 'ͻ',
'Ͼ' => 'ͼ',
'Ͽ' => 'ͽ',
'Ѐ' => 'ѐ',
'Ё' => 'ё',
'Ђ' => 'ђ',
'Ѓ' => 'ѓ',
'Є' => 'є',
'Ѕ' => 'ѕ',
'І' => 'і',
'Ї' => 'ї',
'Ј' => 'ј',
'Љ' => 'љ',
'Њ' => 'њ',
'Ћ' => 'ћ',
'Ќ' => 'ќ',
'Ѝ' => 'ѝ',
'Ў' => 'ў',
'Џ' => 'џ',
'А' => 'а',
'Б' => 'б',
'В' => 'в',
'Г' => 'г',
'Д' => 'д',
'Е' => 'е',
'Ж' => 'ж',
'З' => 'з',
'И' => 'и',
'Й' => 'й',
'К' => 'к',
'Л' => 'л',
'М' => 'м',
'Н' => 'н',
'О' => 'о',
'П' => 'п',
'Р' => 'р',
'С' => 'с',
'Т' => 'т',
'У' => 'у',
'Ф' => 'ф',
'Х' => 'х',
'Ц' => 'ц',
'Ч' => 'ч',
'Ш' => 'ш',
'Щ' => 'щ',
'Ъ' => 'ъ',
'Ы' => 'ы',
'Ь' => 'ь',
'Э' => 'э',
'Ю' => 'ю',
'Я' => 'я',
'Ѡ' => 'ѡ',
'Ѣ' => 'ѣ',
'Ѥ' => 'ѥ',
'Ѧ' => 'ѧ',
'Ѩ' => 'ѩ',
'Ѫ' => 'ѫ',
'Ѭ' => 'ѭ',
'Ѯ' => 'ѯ',
'Ѱ' => 'ѱ',
'Ѳ' => 'ѳ',
'Ѵ' => 'ѵ',
'Ѷ' => 'ѷ',
'Ѹ' => 'ѹ',
'Ѻ' => 'ѻ',
'Ѽ' => 'ѽ',
'Ѿ' => 'ѿ',
'Ҁ' => 'ҁ',
'Ҋ' => 'ҋ',
'Ҍ' => 'ҍ',
'Ҏ' => 'ҏ',
'Ґ' => 'ґ',
'Ғ' => 'ғ',
'Ҕ' => 'ҕ',
'Җ' => 'җ',
'Ҙ' => 'ҙ',
'Қ' => 'қ',
'Ҝ' => 'ҝ',
'Ҟ' => 'ҟ',
'Ҡ' => 'ҡ',
'Ң' => 'ң',
'Ҥ' => 'ҥ',
'Ҧ' => 'ҧ',
'Ҩ' => 'ҩ',
'Ҫ' => 'ҫ',
'Ҭ' => 'ҭ',
'Ү' => 'ү',
'Ұ' => 'ұ',
'Ҳ' => 'ҳ',
'Ҵ' => 'ҵ',
'Ҷ' => 'ҷ',
'Ҹ' => 'ҹ',
'Һ' => 'һ',
'Ҽ' => 'ҽ',
'Ҿ' => 'ҿ',
'Ӏ' => 'ӏ',
'Ӂ' => 'ӂ',
'Ӄ' => 'ӄ',
'Ӆ' => 'ӆ',
'Ӈ' => 'ӈ',
'Ӊ' => 'ӊ',
'Ӌ' => 'ӌ',
'Ӎ' => 'ӎ',
'Ӑ' => 'ӑ',
'Ӓ' => 'ӓ',
'Ӕ' => 'ӕ',
'Ӗ' => 'ӗ',
'Ә' => 'ә',
'Ӛ' => 'ӛ',
'Ӝ' => 'ӝ',
'Ӟ' => 'ӟ',
'Ӡ' => 'ӡ',
'Ӣ' => 'ӣ',
'Ӥ' => 'ӥ',
'Ӧ' => 'ӧ',
'Ө' => 'ө',
'Ӫ' => 'ӫ',
'Ӭ' => 'ӭ',
'Ӯ' => 'ӯ',
'Ӱ' => 'ӱ',
'Ӳ' => 'ӳ',
'Ӵ' => 'ӵ',
'Ӷ' => 'ӷ',
'Ӹ' => 'ӹ',
'Ӻ' => 'ӻ',
'Ӽ' => 'ӽ',
'Ӿ' => 'ӿ',
'Ԁ' => 'ԁ',
'Ԃ' => 'ԃ',
'Ԅ' => 'ԅ',
'Ԇ' => 'ԇ',
'Ԉ' => 'ԉ',
'Ԋ' => 'ԋ',
'Ԍ' => 'ԍ',
'Ԏ' => 'ԏ',
'Ԑ' => 'ԑ',
'Ԓ' => 'ԓ',
'Ԕ' => 'ԕ',
'Ԗ' => 'ԗ',
'Ԙ' => 'ԙ',
'Ԛ' => 'ԛ',
'Ԝ' => 'ԝ',
'Ԟ' => 'ԟ',
'Ԡ' => 'ԡ',
'Ԣ' => 'ԣ',
'Ԥ' => 'ԥ',
'Ԧ' => 'ԧ',
'Ԩ' => 'ԩ',
'Ԫ' => 'ԫ',
'Ԭ' => 'ԭ',
'Ԯ' => 'ԯ',
'Ա' => 'ա',
'Բ' => 'բ',
'Գ' => 'գ',
'Դ' => 'դ',
'Ե' => 'ե',
'Զ' => 'զ',
'Է' => 'է',
'Ը' => 'ը',
'Թ' => 'թ',
'Ժ' => 'ժ',
'Ի' => 'ի',
'Լ' => 'լ',
'Խ' => 'խ',
'Ծ' => 'ծ',
'Կ' => 'կ',
'Հ' => 'հ',
'Ձ' => 'ձ',
'Ղ' => 'ղ',
'Ճ' => 'ճ',
'Մ' => 'մ',
'Յ' => 'յ',
'Ն' => 'ն',
'Շ' => 'շ',
'Ո' => 'ո',
'Չ' => 'չ',
'Պ' => 'պ',
'Ջ' => 'ջ',
'Ռ' => 'ռ',
'Ս' => 'ս',
'Վ' => 'վ',
'Տ' => 'տ',
'Ր' => 'ր',
'Ց' => 'ց',
'Ւ' => 'ւ',
'Փ' => 'փ',
'Ք' => 'ք',
'Օ' => 'օ',
'Ֆ' => 'ֆ',
'Ⴀ' => 'ⴀ',
'Ⴁ' => 'ⴁ',
'Ⴂ' => 'ⴂ',
'Ⴃ' => 'ⴃ',
'Ⴄ' => 'ⴄ',
'Ⴅ' => 'ⴅ',
'Ⴆ' => 'ⴆ',
'Ⴇ' => 'ⴇ',
'Ⴈ' => 'ⴈ',
'Ⴉ' => 'ⴉ',
'Ⴊ' => 'ⴊ',
'Ⴋ' => 'ⴋ',
'Ⴌ' => 'ⴌ',
'Ⴍ' => 'ⴍ',
'Ⴎ' => 'ⴎ',
'Ⴏ' => 'ⴏ',
'Ⴐ' => 'ⴐ',
'Ⴑ' => 'ⴑ',
'Ⴒ' => 'ⴒ',
'Ⴓ' => 'ⴓ',
'Ⴔ' => 'ⴔ',
'Ⴕ' => 'ⴕ',
'Ⴖ' => 'ⴖ',
'Ⴗ' => 'ⴗ',
'Ⴘ' => 'ⴘ',
'Ⴙ' => 'ⴙ',
'Ⴚ' => 'ⴚ',
'Ⴛ' => 'ⴛ',
'Ⴜ' => 'ⴜ',
'Ⴝ' => 'ⴝ',
'Ⴞ' => 'ⴞ',
'Ⴟ' => 'ⴟ',
'Ⴠ' => 'ⴠ',
'Ⴡ' => 'ⴡ',
'Ⴢ' => 'ⴢ',
'Ⴣ' => 'ⴣ',
'Ⴤ' => 'ⴤ',
'Ⴥ' => 'ⴥ',
'Ⴧ' => 'ⴧ',
'Ⴭ' => 'ⴭ',
'' => 'ꭰ',
'' => 'ꭱ',
'' => 'ꭲ',
'Ꭳ' => 'ꭳ',
'Ꭴ' => 'ꭴ',
'' => '',
'Ꭶ' => 'ꭶ',
'Ꭷ' => 'ꭷ',
'Ꭸ' => 'ꭸ',
'' => 'ꭹ',
'' => 'ꭺ',
'' => 'ꭻ',
'' => 'ꭼ',
'Ꭽ' => 'ꭽ',
'' => 'ꭾ',
'Ꭿ' => 'ꭿ',
'Ꮀ' => 'ꮀ',
'Ꮁ' => '',
'Ꮂ' => 'ꮂ',
'' => '',
'Ꮄ' => 'ꮄ',
'Ꮅ' => 'ꮅ',
'Ꮆ' => 'ꮆ',
'' => 'ꮇ',
'Ꮈ' => 'ꮈ',
'Ꮉ' => 'ꮉ',
'Ꮊ' => 'ꮊ',
'' => 'ꮋ',
'Ꮌ' => 'ꮌ',
'' => 'ꮍ',
'Ꮎ' => 'ꮎ',
'Ꮏ' => 'ꮏ',
'' => 'ꮐ',
'Ꮑ' => 'ꮑ',
'' => 'ꮒ',
'' => '',
'Ꮔ' => 'ꮔ',
'Ꮕ' => 'ꮕ',
'Ꮖ' => 'ꮖ',
'Ꮗ' => 'ꮗ',
'Ꮘ' => 'ꮘ',
'Ꮙ' => 'ꮙ',
'Ꮚ' => 'ꮚ',
'Ꮛ' => 'ꮛ',
'Ꮜ' => 'ꮜ',
'Ꮝ' => 'ꮝ',
'' => 'ꮞ',
'' => 'ꮟ',
'Ꮠ' => 'ꮠ',
'Ꮡ' => 'ꮡ',
'' => 'ꮢ',
'Ꮣ' => 'ꮣ',
'' => 'ꮤ',
'' => 'ꮥ',
'Ꮦ' => 'ꮦ',
'Ꮧ' => 'ꮧ',
'Ꮨ' => 'ꮨ',
'' => '',
'' => '',
'Ꮫ' => 'ꮫ',
'Ꮬ' => 'ꮬ',
'Ꮭ' => 'ꮭ',
'' => 'ꮮ',
'' => '',
'Ꮰ' => 'ꮰ',
'Ꮱ' => 'ꮱ',
'' => 'ꮲ',
'Ꮳ' => 'ꮳ',
'Ꮴ' => 'ꮴ',
'Ꮵ' => 'ꮵ',
'' => 'ꮶ',
'' => 'ꮷ',
'Ꮸ' => 'ꮸ',
'Ꮹ' => 'ꮹ',
'Ꮺ' => 'ꮺ',
'Ꮻ' => 'ꮻ',
'Ꮼ' => 'ꮼ',
'Ꮽ' => 'ꮽ',
'' => 'ꮾ',
'Ꮿ' => 'ꮿ',
'Ᏸ' => 'ᏸ',
'Ᏹ' => 'ᏹ',
'Ᏺ' => 'ᏺ',
'' => 'ᏻ',
'' => 'ᏼ',
'Ᏽ' => 'ᏽ',
'Ა' => 'ა',
'Ბ' => 'ბ',
'Გ' => 'გ',
'Დ' => 'დ',
'Ე' => 'ე',
'Ვ' => 'ვ',
'Ზ' => 'ზ',
'Თ' => 'თ',
'Ი' => 'ი',
'Კ' => 'კ',
'Ლ' => 'ლ',
'Მ' => 'მ',
'Ნ' => 'ნ',
'Ო' => 'ო',
'Პ' => 'პ',
'Ჟ' => 'ჟ',
'Რ' => 'რ',
'Ს' => 'ს',
'Ტ' => 'ტ',
'Უ' => 'უ',
'Ფ' => 'ფ',
'Ქ' => 'ქ',
'Ღ' => 'ღ',
'Ყ' => '',
'Შ' => 'შ',
'Ჩ' => 'ჩ',
'Ც' => 'ც',
'Ძ' => 'ძ',
'Წ' => 'წ',
'Ჭ' => 'ჭ',
'Ხ' => 'ხ',
'Ჯ' => 'ჯ',
'Ჰ' => 'ჰ',
'Ჱ' => 'ჱ',
'Ჲ' => 'ჲ',
'Ჳ' => 'ჳ',
'Ჴ' => 'ჴ',
'Ჵ' => 'ჵ',
'Ჶ' => 'ჶ',
'Ჷ' => 'ჷ',
'Ჸ' => 'ჸ',
'Ჹ' => 'ჹ',
'Ჺ' => 'ჺ',
'Ჽ' => 'ჽ',
'Ჾ' => 'ჾ',
'Ჿ' => '',
'Ḁ' => 'ḁ',
'Ḃ' => 'ḃ',
'Ḅ' => 'ḅ',
'Ḇ' => 'ḇ',
'Ḉ' => 'ḉ',
'Ḋ' => 'ḋ',
'Ḍ' => 'ḍ',
'Ḏ' => 'ḏ',
'Ḑ' => 'ḑ',
'Ḓ' => 'ḓ',
'Ḕ' => 'ḕ',
'Ḗ' => 'ḗ',
'Ḙ' => 'ḙ',
'Ḛ' => 'ḛ',
'Ḝ' => 'ḝ',
'Ḟ' => 'ḟ',
'Ḡ' => 'ḡ',
'Ḣ' => 'ḣ',
'Ḥ' => 'ḥ',
'Ḧ' => 'ḧ',
'Ḩ' => 'ḩ',
'Ḫ' => 'ḫ',
'Ḭ' => 'ḭ',
'Ḯ' => 'ḯ',
'Ḱ' => 'ḱ',
'Ḳ' => 'ḳ',
'Ḵ' => 'ḵ',
'Ḷ' => 'ḷ',
'Ḹ' => 'ḹ',
'Ḻ' => 'ḻ',
'Ḽ' => 'ḽ',
'Ḿ' => 'ḿ',
'Ṁ' => 'ṁ',
'Ṃ' => 'ṃ',
'Ṅ' => 'ṅ',
'Ṇ' => 'ṇ',
'Ṉ' => 'ṉ',
'Ṋ' => 'ṋ',
'Ṍ' => 'ṍ',
'Ṏ' => 'ṏ',
'Ṑ' => 'ṑ',
'Ṓ' => 'ṓ',
'Ṕ' => 'ṕ',
'Ṗ' => 'ṗ',
'Ṙ' => 'ṙ',
'Ṛ' => 'ṛ',
'Ṝ' => 'ṝ',
'Ṟ' => 'ṟ',
'Ṡ' => 'ṡ',
'Ṣ' => 'ṣ',
'Ṥ' => 'ṥ',
'Ṧ' => 'ṧ',
'Ṩ' => 'ṩ',
'Ṫ' => 'ṫ',
'Ṭ' => 'ṭ',
'Ṯ' => 'ṯ',
'Ṱ' => 'ṱ',
'Ṳ' => 'ṳ',
'Ṵ' => 'ṵ',
'Ṷ' => 'ṷ',
'Ṹ' => 'ṹ',
'Ṻ' => 'ṻ',
'Ṽ' => 'ṽ',
'Ṿ' => 'ṿ',
'Ẁ' => 'ẁ',
'Ẃ' => 'ẃ',
'Ẅ' => 'ẅ',
'Ẇ' => 'ẇ',
'Ẉ' => 'ẉ',
'Ẋ' => 'ẋ',
'Ẍ' => 'ẍ',
'Ẏ' => 'ẏ',
'Ẑ' => 'ẑ',
'Ẓ' => 'ẓ',
'Ẕ' => 'ẕ',
'ẞ' => 'ß',
'Ạ' => 'ạ',
'Ả' => 'ả',
'Ấ' => 'ấ',
'Ầ' => 'ầ',
'Ẩ' => 'ẩ',
'Ẫ' => 'ẫ',
'Ậ' => 'ậ',
'Ắ' => 'ắ',
'Ằ' => 'ằ',
'Ẳ' => 'ẳ',
'Ẵ' => 'ẵ',
'Ặ' => 'ặ',
'Ẹ' => 'ẹ',
'Ẻ' => 'ẻ',
'Ẽ' => 'ẽ',
'Ế' => 'ế',
'Ề' => 'ề',
'Ể' => 'ể',
'Ễ' => 'ễ',
'Ệ' => 'ệ',
'Ỉ' => 'ỉ',
'Ị' => 'ị',
'Ọ' => 'ọ',
'Ỏ' => 'ỏ',
'Ố' => 'ố',
'Ồ' => 'ồ',
'Ổ' => 'ổ',
'Ỗ' => 'ỗ',
'Ộ' => 'ộ',
'Ớ' => 'ớ',
'Ờ' => 'ờ',
'Ở' => 'ở',
'Ỡ' => 'ỡ',
'Ợ' => 'ợ',
'Ụ' => 'ụ',
'Ủ' => 'ủ',
'Ứ' => 'ứ',
'Ừ' => 'ừ',
'Ử' => 'ử',
'Ữ' => 'ữ',
'Ự' => 'ự',
'Ỳ' => 'ỳ',
'Ỵ' => 'ỵ',
'Ỷ' => 'ỷ',
'Ỹ' => 'ỹ',
'Ỻ' => 'ỻ',
'Ỽ' => 'ỽ',
'Ỿ' => 'ỿ',
'Ἀ' => 'ἀ',
'Ἁ' => 'ἁ',
'Ἂ' => 'ἂ',
'Ἃ' => 'ἃ',
'Ἄ' => 'ἄ',
'Ἅ' => 'ἅ',
'Ἆ' => 'ἆ',
'Ἇ' => 'ἇ',
'Ἐ' => 'ἐ',
'Ἑ' => 'ἑ',
'Ἒ' => 'ἒ',
'Ἓ' => 'ἓ',
'Ἔ' => 'ἔ',
'Ἕ' => 'ἕ',
'Ἠ' => 'ἠ',
'Ἡ' => 'ἡ',
'Ἢ' => 'ἢ',
'Ἣ' => 'ἣ',
'Ἤ' => 'ἤ',
'Ἥ' => 'ἥ',
'Ἦ' => 'ἦ',
'Ἧ' => 'ἧ',
'Ἰ' => 'ἰ',
'Ἱ' => 'ἱ',
'Ἲ' => 'ἲ',
'Ἳ' => 'ἳ',
'Ἴ' => 'ἴ',
'Ἵ' => 'ἵ',
'Ἶ' => 'ἶ',
'Ἷ' => 'ἷ',
'Ὀ' => 'ὀ',
'Ὁ' => 'ὁ',
'Ὂ' => 'ὂ',
'Ὃ' => 'ὃ',
'Ὄ' => 'ὄ',
'Ὅ' => 'ὅ',
'Ὑ' => 'ὑ',
'Ὓ' => 'ὓ',
'Ὕ' => 'ὕ',
'Ὗ' => 'ὗ',
'Ὠ' => 'ὠ',
'Ὡ' => 'ὡ',
'Ὢ' => 'ὢ',
'Ὣ' => 'ὣ',
'Ὤ' => 'ὤ',
'Ὥ' => 'ὥ',
'Ὦ' => 'ὦ',
'Ὧ' => 'ὧ',
'ᾈ' => 'ᾀ',
'ᾉ' => 'ᾁ',
'ᾊ' => 'ᾂ',
'ᾋ' => 'ᾃ',
'ᾌ' => 'ᾄ',
'ᾍ' => 'ᾅ',
'ᾎ' => 'ᾆ',
'ᾏ' => 'ᾇ',
'ᾘ' => 'ᾐ',
'ᾙ' => 'ᾑ',
'ᾚ' => 'ᾒ',
'ᾛ' => 'ᾓ',
'ᾜ' => 'ᾔ',
'ᾝ' => 'ᾕ',
'ᾞ' => 'ᾖ',
'ᾟ' => 'ᾗ',
'ᾨ' => 'ᾠ',
'ᾩ' => 'ᾡ',
'ᾪ' => 'ᾢ',
'ᾫ' => 'ᾣ',
'ᾬ' => 'ᾤ',
'ᾭ' => 'ᾥ',
'ᾮ' => 'ᾦ',
'ᾯ' => 'ᾧ',
'Ᾰ' => 'ᾰ',
'Ᾱ' => 'ᾱ',
'Ὰ' => 'ὰ',
'Ά' => 'ά',
'ᾼ' => 'ᾳ',
'Ὲ' => 'ὲ',
'Έ' => 'έ',
'Ὴ' => 'ὴ',
'Ή' => 'ή',
'ῌ' => 'ῃ',
'Ῐ' => 'ῐ',
'Ῑ' => 'ῑ',
'Ὶ' => 'ὶ',
'Ί' => 'ί',
'Ῠ' => 'ῠ',
'Ῡ' => 'ῡ',
'Ὺ' => 'ὺ',
'Ύ' => 'ύ',
'Ῥ' => 'ῥ',
'Ὸ' => 'ὸ',
'Ό' => 'ό',
'Ὼ' => 'ὼ',
'Ώ' => 'ώ',
'ῼ' => 'ῳ',
'Ω' => 'ω',
'' => 'k',
'Å' => 'å',
'Ⅎ' => 'ⅎ',
'' => '',
'Ⅱ' => 'ⅱ',
'Ⅲ' => 'ⅲ',
'Ⅳ' => 'ⅳ',
'' => '',
'Ⅵ' => 'ⅵ',
'Ⅶ' => 'ⅶ',
'Ⅷ' => 'ⅷ',
'Ⅸ' => 'ⅸ',
'' => '',
'Ⅺ' => 'ⅺ',
'Ⅻ' => 'ⅻ',
'' => '',
'' => '',
'' => '',
'' => 'ⅿ',
'Ↄ' => 'ↄ',
'Ⓐ' => 'ⓐ',
'Ⓑ' => 'ⓑ',
'Ⓒ' => 'ⓒ',
'Ⓓ' => 'ⓓ',
'Ⓔ' => 'ⓔ',
'Ⓕ' => 'ⓕ',
'Ⓖ' => 'ⓖ',
'Ⓗ' => 'ⓗ',
'Ⓘ' => 'ⓘ',
'Ⓙ' => 'ⓙ',
'Ⓚ' => 'ⓚ',
'Ⓛ' => 'ⓛ',
'Ⓜ' => 'ⓜ',
'Ⓝ' => 'ⓝ',
'Ⓞ' => 'ⓞ',
'Ⓟ' => 'ⓟ',
'Ⓠ' => 'ⓠ',
'Ⓡ' => 'ⓡ',
'Ⓢ' => 'ⓢ',
'Ⓣ' => 'ⓣ',
'Ⓤ' => 'ⓤ',
'Ⓥ' => 'ⓥ',
'Ⓦ' => 'ⓦ',
'Ⓧ' => 'ⓧ',
'Ⓨ' => 'ⓨ',
'Ⓩ' => 'ⓩ',
'Ⰰ' => 'ⰰ',
'Ⰱ' => 'ⰱ',
'Ⰲ' => 'ⰲ',
'Ⰳ' => 'ⰳ',
'Ⰴ' => 'ⰴ',
'Ⰵ' => 'ⰵ',
'Ⰶ' => 'ⰶ',
'Ⰷ' => 'ⰷ',
'Ⰸ' => 'ⰸ',
'Ⰹ' => 'ⰹ',
'Ⰺ' => 'ⰺ',
'Ⰻ' => 'ⰻ',
'Ⰼ' => 'ⰼ',
'Ⰽ' => 'ⰽ',
'Ⰾ' => 'ⰾ',
'Ⰿ' => 'ⰿ',
'Ⱀ' => 'ⱀ',
'Ⱁ' => 'ⱁ',
'Ⱂ' => 'ⱂ',
'Ⱃ' => 'ⱃ',
'Ⱄ' => 'ⱄ',
'Ⱅ' => 'ⱅ',
'Ⱆ' => 'ⱆ',
'Ⱇ' => 'ⱇ',
'Ⱈ' => 'ⱈ',
'Ⱉ' => 'ⱉ',
'Ⱊ' => 'ⱊ',
'Ⱋ' => 'ⱋ',
'Ⱌ' => 'ⱌ',
'Ⱍ' => 'ⱍ',
'Ⱎ' => 'ⱎ',
'Ⱏ' => 'ⱏ',
'Ⱐ' => 'ⱐ',
'Ⱑ' => 'ⱑ',
'Ⱒ' => 'ⱒ',
'Ⱓ' => 'ⱓ',
'Ⱔ' => 'ⱔ',
'Ⱕ' => 'ⱕ',
'Ⱖ' => 'ⱖ',
'Ⱗ' => 'ⱗ',
'Ⱘ' => 'ⱘ',
'Ⱙ' => 'ⱙ',
'Ⱚ' => 'ⱚ',
'Ⱛ' => 'ⱛ',
'Ⱜ' => 'ⱜ',
'Ⱝ' => 'ⱝ',
'Ⱞ' => 'ⱞ',
'Ⱡ' => 'ⱡ',
'Ɫ' => 'ɫ',
'Ᵽ' => 'ᵽ',
'Ɽ' => 'ɽ',
'Ⱨ' => 'ⱨ',
'Ⱪ' => 'ⱪ',
'Ⱬ' => 'ⱬ',
'Ɑ' => 'ɑ',
'Ɱ' => 'ɱ',
'Ɐ' => 'ɐ',
'Ɒ' => 'ɒ',
'Ⱳ' => 'ⱳ',
'Ⱶ' => 'ⱶ',
'Ȿ' => 'ȿ',
'Ɀ' => 'ɀ',
'Ⲁ' => 'ⲁ',
'Ⲃ' => 'ⲃ',
'Ⲅ' => '',
'Ⲇ' => 'ⲇ',
'Ⲉ' => 'ⲉ',
'Ⲋ' => 'ⲋ',
'Ⲍ' => 'ⲍ',
'' => 'ⲏ',
'Ⲑ' => 'ⲑ',
'' => 'ⲓ',
'' => 'ⲕ',
'Ⲗ' => 'ⲗ',
'' => 'ⲙ',
'' => 'ⲛ',
'Ⲝ' => 'ⲝ',
'' => '',
'Ⲡ' => 'ⲡ',
'' => '',
'' => '',
'' => 'ⲧ',
'' => 'ⲩ',
'Ⲫ' => 'ⲫ',
'' => 'ⲭ',
'Ⲯ' => 'ⲯ',
'Ⲱ' => 'ⲱ',
'Ⲳ' => 'ⲳ',
'Ⲵ' => 'ⲵ',
'Ⲷ' => 'ⲷ',
'Ⲹ' => 'ⲹ',
'' => 'ⲻ',
'Ⲽ' => 'ⲽ',
'Ⲿ' => 'ⲿ',
'Ⳁ' => 'ⳁ',
'Ⳃ' => 'ⳃ',
'Ⳅ' => 'ⳅ',
'' => 'ⳇ',
'Ⳉ' => 'ⳉ',
'' => 'ⳋ',
'' => 'ⳍ',
'Ⳏ' => 'ⳏ',
'' => 'ⳑ',
'' => 'ⳓ',
'Ⳕ' => 'ⳕ',
'Ⳗ' => 'ⳗ',
'Ⳙ' => 'ⳙ',
'Ⳛ' => 'ⳛ',
'Ⳝ' => 'ⳝ',
'Ⳟ' => 'ⳟ',
'Ⳡ' => 'ⳡ',
'Ⳣ' => 'ⳣ',
'Ⳬ' => 'ⳬ',
'Ⳮ' => 'ⳮ',
'Ⳳ' => 'ⳳ',
'Ꙁ' => 'ꙁ',
'Ꙃ' => 'ꙃ',
'' => 'ꙅ',
'Ꙇ' => '',
'Ꙉ' => 'ꙉ',
'Ꙋ' => 'ꙋ',
'Ꙍ' => 'ꙍ',
'Ꙏ' => 'ꙏ',
'Ꙑ' => 'ꙑ',
'Ꙓ' => 'ꙓ',
'Ꙕ' => 'ꙕ',
'Ꙗ' => 'ꙗ',
'Ꙙ' => 'ꙙ',
'Ꙛ' => 'ꙛ',
'Ꙝ' => 'ꙝ',
'Ꙟ' => 'ꙟ',
'Ꙡ' => 'ꙡ',
'Ꙣ' => 'ꙣ',
'Ꙥ' => 'ꙥ',
'Ꙧ' => 'ꙧ',
'Ꙩ' => 'ꙩ',
'Ꙫ' => 'ꙫ',
'Ꙭ' => 'ꙭ',
'Ꚁ' => 'ꚁ',
'Ꚃ' => 'ꚃ',
'Ꚅ' => 'ꚅ',
'Ꚇ' => 'ꚇ',
'Ꚉ' => 'ꚉ',
'Ꚋ' => 'ꚋ',
'Ꚍ' => 'ꚍ',
'Ꚏ' => 'ꚏ',
'Ꚑ' => 'ꚑ',
'Ꚓ' => 'ꚓ',
'Ꚕ' => 'ꚕ',
'Ꚗ' => 'ꚗ',
'Ꚙ' => 'ꚙ',
'Ꚛ' => 'ꚛ',
'Ꜣ' => 'ꜣ',
'Ꜥ' => 'ꜥ',
'Ꜧ' => 'ꜧ',
'Ꜩ' => 'ꜩ',
'Ꜫ' => 'ꜫ',
'Ꜭ' => 'ꜭ',
'Ꜯ' => 'ꜯ',
'Ꜳ' => 'ꜳ',
'Ꜵ' => 'ꜵ',
'Ꜷ' => 'ꜷ',
'Ꜹ' => 'ꜹ',
'Ꜻ' => 'ꜻ',
'Ꜽ' => 'ꜽ',
'Ꜿ' => 'ꜿ',
'Ꝁ' => 'ꝁ',
'Ꝃ' => 'ꝃ',
'Ꝅ' => 'ꝅ',
'Ꝇ' => 'ꝇ',
'Ꝉ' => 'ꝉ',
'Ꝋ' => 'ꝋ',
'Ꝍ' => 'ꝍ',
'Ꝏ' => 'ꝏ',
'Ꝑ' => 'ꝑ',
'Ꝓ' => 'ꝓ',
'Ꝕ' => 'ꝕ',
'Ꝗ' => 'ꝗ',
'Ꝙ' => 'ꝙ',
'' => 'ꝛ',
'Ꝝ' => 'ꝝ',
'Ꝟ' => 'ꝟ',
'Ꝡ' => 'ꝡ',
'Ꝣ' => 'ꝣ',
'Ꝥ' => 'ꝥ',
'Ꝧ' => 'ꝧ',
'Ꝩ' => 'ꝩ',
'' => 'ꝫ',
'Ꝭ' => 'ꝭ',
'' => 'ꝯ',
'Ꝺ' => 'ꝺ',
'Ꝼ' => 'ꝼ',
'Ᵹ' => 'ᵹ',
'Ꝿ' => 'ꝿ',
'Ꞁ' => 'ꞁ',
'Ꞃ' => 'ꞃ',
'Ꞅ' => 'ꞅ',
'Ꞇ' => 'ꞇ',
'Ꞌ' => '',
'Ɥ' => 'ɥ',
'Ꞑ' => 'ꞑ',
'Ꞓ' => 'ꞓ',
'Ꞗ' => 'ꞗ',
'' => '',
'Ꞛ' => 'ꞛ',
'Ꞝ' => 'ꞝ',
'Ꞟ' => '',
'Ꞡ' => 'ꞡ',
'Ꞣ' => 'ꞣ',
'Ꞥ' => 'ꞥ',
'Ꞧ' => 'ꞧ',
'Ꞩ' => 'ꞩ',
'Ɦ' => 'ɦ',
'' => 'ɜ',
'Ɡ' => 'ɡ',
'Ɬ' => 'ɬ',
'Ɪ' => 'ɪ',
'Ʞ' => 'ʞ',
'Ʇ' => 'ʇ',
'' => 'ʝ',
'' => 'ꭓ',
'' => 'ꞵ',
'Ꞷ' => 'ꞷ',
'Ꞹ' => 'ꞹ',
'Ꞻ' => 'ꞻ',
'Ꞽ' => 'ꞽ',
'Ꞿ' => 'ꞿ',
'Ꟃ' => 'ꟃ',
'Ꞔ' => 'ꞔ',
'Ʂ' => 'ʂ',
'Ᶎ' => 'ᶎ',
'Ꟈ' => 'ꟈ',
'Ꟊ' => 'ꟊ',
'Ꟶ' => 'ꟶ',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'𐐀' => '𐐨',
'𐐁' => '𐐩',
'𐐂' => '𐐪',
'𐐃' => '𐐫',
'𐐄' => '𐐬',
'𐐅' => '𐐭',
'𐐆' => '𐐮',
'𐐇' => '𐐯',
'𐐈' => '𐐰',
'𐐉' => '𐐱',
'𐐊' => '𐐲',
'𐐋' => '𐐳',
'𐐌' => '𐐴',
'𐐍' => '𐐵',
'𐐎' => '𐐶',
'𐐏' => '𐐷',
'𐐐' => '𐐸',
'𐐑' => '𐐹',
'𐐒' => '𐐺',
'𐐓' => '𐐻',
'𐐔' => '𐐼',
'𐐕' => '𐐽',
'𐐖' => '𐐾',
'𐐗' => '𐐿',
'𐐘' => '𐑀',
'𐐙' => '𐑁',
'𐐚' => '𐑂',
'𐐛' => '𐑃',
'𐐜' => '𐑄',
'𐐝' => '𐑅',
'𐐞' => '𐑆',
'𐐟' => '𐑇',
'𐐠' => '𐑈',
'𐐡' => '𐑉',
'𐐢' => '𐑊',
'𐐣' => '𐑋',
'𐐤' => '𐑌',
'𐐥' => '𐑍',
'𐐦' => '𐑎',
'𐐧' => '𐑏',
'𐒰' => '𐓘',
'𐒱' => '𐓙',
'𐒲' => '𐓚',
'𐒳' => '𐓛',
'𐒴' => '𐓜',
'𐒵' => '𐓝',
'𐒶' => '𐓞',
'𐒷' => '𐓟',
'𐒸' => '𐓠',
'𐒹' => '𐓡',
'𐒺' => '𐓢',
'𐒻' => '𐓣',
'𐒼' => '𐓤',
'𐒽' => '𐓥',
'𐒾' => '𐓦',
'𐒿' => '𐓧',
'𐓀' => '𐓨',
'𐓁' => '𐓩',
'𐓂' => '𐓪',
'𐓃' => '𐓫',
'𐓄' => '𐓬',
'𐓅' => '𐓭',
'𐓆' => '𐓮',
'𐓇' => '𐓯',
'𐓈' => '𐓰',
'𐓉' => '𐓱',
'𐓊' => '𐓲',
'𐓋' => '𐓳',
'𐓌' => '𐓴',
'𐓍' => '𐓵',
'𐓎' => '𐓶',
'𐓏' => '𐓷',
'𐓐' => '𐓸',
'𐓑' => '𐓹',
'𐓒' => '𐓺',
'𐓓' => '𐓻',
'𐲀' => '𐳀',
'𐲁' => '𐳁',
'𐲂' => '𐳂',
'𐲃' => '𐳃',
'𐲄' => '𐳄',
'𐲅' => '𐳅',
'𐲆' => '𐳆',
'𐲇' => '𐳇',
'𐲈' => '𐳈',
'𐲉' => '𐳉',
'𐲊' => '𐳊',
'𐲋' => '𐳋',
'𐲌' => '𐳌',
'𐲍' => '𐳍',
'𐲎' => '𐳎',
'𐲏' => '𐳏',
'𐲐' => '𐳐',
'𐲑' => '𐳑',
'𐲒' => '𐳒',
'𐲓' => '𐳓',
'𐲔' => '𐳔',
'𐲕' => '𐳕',
'𐲖' => '𐳖',
'𐲗' => '𐳗',
'𐲘' => '𐳘',
'𐲙' => '𐳙',
'𐲚' => '𐳚',
'𐲛' => '𐳛',
'𐲜' => '𐳜',
'𐲝' => '𐳝',
'𐲞' => '𐳞',
'𐲟' => '𐳟',
'𐲠' => '𐳠',
'𐲡' => '𐳡',
'𐲢' => '𐳢',
'𐲣' => '𐳣',
'𐲤' => '𐳤',
'𐲥' => '𐳥',
'𐲦' => '𐳦',
'𐲧' => '𐳧',
'𐲨' => '𐳨',
'𐲩' => '𐳩',
'𐲪' => '𐳪',
'𐲫' => '𐳫',
'𐲬' => '𐳬',
'𐲭' => '𐳭',
'𐲮' => '𐳮',
'𐲯' => '𐳯',
'𐲰' => '𐳰',
'𐲱' => '𐳱',
'𐲲' => '𐳲',
'𑢠' => '𑣀',
'𑢡' => '𑣁',
'𑢢' => '𑣂',
'𑢣' => '𑣃',
'𑢤' => '𑣄',
'𑢥' => '𑣅',
'𑢦' => '𑣆',
'𑢧' => '𑣇',
'𑢨' => '𑣈',
'𑢩' => '𑣉',
'𑢪' => '𑣊',
'𑢫' => '𑣋',
'𑢬' => '𑣌',
'𑢭' => '𑣍',
'𑢮' => '𑣎',
'𑢯' => '𑣏',
'𑢰' => '𑣐',
'𑢱' => '𑣑',
'𑢲' => '𑣒',
'𑢳' => '𑣓',
'𑢴' => '𑣔',
'𑢵' => '𑣕',
'𑢶' => '𑣖',
'𑢷' => '𑣗',
'𑢸' => '𑣘',
'𑢹' => '𑣙',
'𑢺' => '𑣚',
'𑢻' => '𑣛',
'𑢼' => '𑣜',
'𑢽' => '𑣝',
'𑢾' => '𑣞',
'𑢿' => '𑣟',
'𖹀' => '𖹠',
'𖹁' => '𖹡',
'𖹂' => '𖹢',
'𖹃' => '𖹣',
'𖹄' => '𖹤',
'𖹅' => '𖹥',
'𖹆' => '𖹦',
'𖹇' => '𖹧',
'𖹈' => '𖹨',
'𖹉' => '𖹩',
'𖹊' => '𖹪',
'𖹋' => '𖹫',
'𖹌' => '𖹬',
'𖹍' => '𖹭',
'𖹎' => '𖹮',
'𖹏' => '𖹯',
'𖹐' => '𖹰',
'𖹑' => '𖹱',
'𖹒' => '𖹲',
'𖹓' => '𖹳',
'𖹔' => '𖹴',
'𖹕' => '𖹵',
'𖹖' => '𖹶',
'𖹗' => '𖹷',
'𖹘' => '𖹸',
'𖹙' => '𖹹',
'𖹚' => '𖹺',
'𖹛' => '𖹻',
'𖹜' => '𖹼',
'𖹝' => '𖹽',
'𖹞' => '𖹾',
'𖹟' => '𖹿',
'𞤀' => '𞤢',
'𞤁' => '𞤣',
'𞤂' => '𞤤',
'𞤃' => '𞤥',
'𞤄' => '𞤦',
'𞤅' => '𞤧',
'𞤆' => '𞤨',
'𞤇' => '𞤩',
'𞤈' => '𞤪',
'𞤉' => '𞤫',
'𞤊' => '𞤬',
'𞤋' => '𞤭',
'𞤌' => '𞤮',
'𞤍' => '𞤯',
'𞤎' => '𞤰',
'𞤏' => '𞤱',
'𞤐' => '𞤲',
'𞤑' => '𞤳',
'𞤒' => '𞤴',
'𞤓' => '𞤵',
'𞤔' => '𞤶',
'𞤕' => '𞤷',
'𞤖' => '𞤸',
'𞤗' => '𞤹',
'𞤘' => '𞤺',
'𞤙' => '𞤻',
'𞤚' => '𞤼',
'𞤛' => '𞤽',
'𞤜' => '𞤾',
'𞤝' => '𞤿',
'𞤞' => '𞥀',
'𞤟' => '𞥁',
'𞤠' => '𞥂',
'𞤡' => '𞥃',
);
<?php
return '/(?<![\x{0027}\x{002E}\x{003A}\x{005E}\x{0060}\x{00A8}\x{00AD}\x{00AF}\x{00B4}\x{00B7}\x{00B8}\x{02B0}-\x{02C1}\x{02C2}-\x{02C5}\x{02C6}-\x{02D1}\x{02D2}-\x{02DF}\x{02E0}-\x{02E4}\x{02E5}-\x{02EB}\x{02EC}\x{02ED}\x{02EE}\x{02EF}-\x{02FF}\x{0300}-\x{036F}\x{0374}\x{0375}\x{037A}\x{0384}-\x{0385}\x{0387}\x{0483}-\x{0487}\x{0488}-\x{0489}\x{0559}\x{0591}-\x{05BD}\x{05BF}\x{05C1}-\x{05C2}\x{05C4}-\x{05C5}\x{05C7}\x{05F4}\x{0600}-\x{0605}\x{0610}-\x{061A}\x{061C}\x{0640}\x{064B}-\x{065F}\x{0670}\x{06D6}-\x{06DC}\x{06DD}\x{06DF}-\x{06E4}\x{06E5}-\x{06E6}\x{06E7}-\x{06E8}\x{06EA}-\x{06ED}\x{070F}\x{0711}\x{0730}-\x{074A}\x{07A6}-\x{07B0}\x{07EB}-\x{07F3}\x{07F4}-\x{07F5}\x{07FA}\x{07FD}\x{0816}-\x{0819}\x{081A}\x{081B}-\x{0823}\x{0824}\x{0825}-\x{0827}\x{0828}\x{0829}-\x{082D}\x{0859}-\x{085B}\x{08D3}-\x{08E1}\x{08E2}\x{08E3}-\x{0902}\x{093A}\x{093C}\x{0941}-\x{0948}\x{094D}\x{0951}-\x{0957}\x{0962}-\x{0963}\x{0971}\x{0981}\x{09BC}\x{09C1}-\x{09C4}\x{09CD}\x{09E2}-\x{09E3}\x{09FE}\x{0A01}-\x{0A02}\x{0A3C}\x{0A41}-\x{0A42}\x{0A47}-\x{0A48}\x{0A4B}-\x{0A4D}\x{0A51}\x{0A70}-\x{0A71}\x{0A75}\x{0A81}-\x{0A82}\x{0ABC}\x{0AC1}-\x{0AC5}\x{0AC7}-\x{0AC8}\x{0ACD}\x{0AE2}-\x{0AE3}\x{0AFA}-\x{0AFF}\x{0B01}\x{0B3C}\x{0B3F}\x{0B41}-\x{0B44}\x{0B4D}\x{0B56}\x{0B62}-\x{0B63}\x{0B82}\x{0BC0}\x{0BCD}\x{0C00}\x{0C04}\x{0C3E}-\x{0C40}\x{0C46}-\x{0C48}\x{0C4A}-\x{0C4D}\x{0C55}-\x{0C56}\x{0C62}-\x{0C63}\x{0C81}\x{0CBC}\x{0CBF}\x{0CC6}\x{0CCC}-\x{0CCD}\x{0CE2}-\x{0CE3}\x{0D00}-\x{0D01}\x{0D3B}-\x{0D3C}\x{0D41}-\x{0D44}\x{0D4D}\x{0D62}-\x{0D63}\x{0DCA}\x{0DD2}-\x{0DD4}\x{0DD6}\x{0E31}\x{0E34}-\x{0E3A}\x{0E46}\x{0E47}-\x{0E4E}\x{0EB1}\x{0EB4}-\x{0EB9}\x{0EBB}-\x{0EBC}\x{0EC6}\x{0EC8}-\x{0ECD}\x{0F18}-\x{0F19}\x{0F35}\x{0F37}\x{0F39}\x{0F71}-\x{0F7E}\x{0F80}-\x{0F84}\x{0F86}-\x{0F87}\x{0F8D}-\x{0F97}\x{0F99}-\x{0FBC}\x{0FC6}\x{102D}-\x{1030}\x{1032}-\x{1037}\x{1039}-\x{103A}\x{103D}-\x{103E}\x{1058}-\x{1059}\x{105E}-\x{1060}\x{1071}-\x{1074}\x{1082}\x{1085}-\x{1086}\x{108D}\x{109D}\x{10FC}\x{135D}-\x{135F}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}-\x{1753}\x{1772}-\x{1773}\x{17B4}-\x{17B5}\x{17B7}-\x{17BD}\x{17C6}\x{17C9}-\x{17D3}\x{17D7}\x{17DD}\x{180B}-\x{180D}\x{180E}\x{1843}\x{1885}-\x{1886}\x{18A9}\x{1920}-\x{1922}\x{1927}-\x{1928}\x{1932}\x{1939}-\x{193B}\x{1A17}-\x{1A18}\x{1A1B}\x{1A56}\x{1A58}-\x{1A5E}\x{1A60}\x{1A62}\x{1A65}-\x{1A6C}\x{1A73}-\x{1A7C}\x{1A7F}\x{1AA7}\x{1AB0}-\x{1ABD}\x{1ABE}\x{1B00}-\x{1B03}\x{1B34}\x{1B36}-\x{1B3A}\x{1B3C}\x{1B42}\x{1B6B}-\x{1B73}\x{1B80}-\x{1B81}\x{1BA2}-\x{1BA5}\x{1BA8}-\x{1BA9}\x{1BAB}-\x{1BAD}\x{1BE6}\x{1BE8}-\x{1BE9}\x{1BED}\x{1BEF}-\x{1BF1}\x{1C2C}-\x{1C33}\x{1C36}-\x{1C37}\x{1C78}-\x{1C7D}\x{1CD0}-\x{1CD2}\x{1CD4}-\x{1CE0}\x{1CE2}-\x{1CE8}\x{1CED}\x{1CF4}\x{1CF8}-\x{1CF9}\x{1D2C}-\x{1D6A}\x{1D78}\x{1D9B}-\x{1DBF}\x{1DC0}-\x{1DF9}\x{1DFB}-\x{1DFF}\x{1FBD}\x{1FBF}-\x{1FC1}\x{1FCD}-\x{1FCF}\x{1FDD}-\x{1FDF}\x{1FED}-\x{1FEF}\x{1FFD}-\x{1FFE}\x{200B}-\x{200F}\x{2018}\x{2019}\x{2024}\x{2027}\x{202A}-\x{202E}\x{2060}-\x{2064}\x{2066}-\x{206F}\x{2071}\x{207F}\x{2090}-\x{209C}\x{20D0}-\x{20DC}\x{20DD}-\x{20E0}\x{20E1}\x{20E2}-\x{20E4}\x{20E5}-\x{20F0}\x{2C7C}-\x{2C7D}\x{2CEF}-\x{2CF1}\x{2D6F}\x{2D7F}\x{2DE0}-\x{2DFF}\x{2E2F}\x{3005}\x{302A}-\x{302D}\x{3031}-\x{3035}\x{303B}\x{3099}-\x{309A}\x{309B}-\x{309C}\x{309D}-\x{309E}\x{30FC}-\x{30FE}\x{A015}\x{A4F8}-\x{A4FD}\x{A60C}\x{A66F}\x{A670}-\x{A672}\x{A674}-\x{A67D}\x{A67F}\x{A69C}-\x{A69D}\x{A69E}-\x{A69F}\x{A6F0}-\x{A6F1}\x{A700}-\x{A716}\x{A717}-\x{A71F}\x{A720}-\x{A721}\x{A770}\x{A788}\x{A789}-\x{A78A}\x{A7F8}-\x{A7F9}\x{A802}\x{A806}\x{A80B}\x{A825}-\x{A826}\x{A8C4}-\x{A8C5}\x{A8E0}-\x{A8F1}\x{A8FF}\x{A926}-\x{A92D}\x{A947}-\x{A951}\x{A980}-\x{A982}\x{A9B3}\x{A9B6}-\x{A9B9}\x{A9BC}\x{A9CF}\x{A9E5}\x{A9E6}\x{AA29}-\x{AA2E}\x{AA31}-\x{AA32}\x{AA35}-\x{AA36}\x{AA43}\x{AA4C}\x{AA70}\x{AA7C}\x{AAB0}\x{AAB2}-\x{AAB4}\x{AAB7}-\x{AAB8}\x{AABE}-\x{AABF}\x{AAC1}\x{AADD}\x{AAEC}-\x{AAED}\x{AAF3}-\x{AAF4}\x{AAF6}\x{AB5B}\x{AB5C}-\x{AB5F}\x{ABE5}\x{ABE8}\x{ABED}\x{FB1E}\x{FBB2}-\x{FBC1}\x{FE00}-\x{FE0F}\x{FE13}\x{FE20}-\x{FE2F}\x{FE52}\x{FE55}\x{FEFF}\x{FF07}\x{FF0E}\x{FF1A}\x{FF3E}\x{FF40}\x{FF70}\x{FF9E}-\x{FF9F}\x{FFE3}\x{FFF9}-\x{FFFB}\x{101FD}\x{102E0}\x{10376}-\x{1037A}\x{10A01}-\x{10A03}\x{10A05}-\x{10A06}\x{10A0C}-\x{10A0F}\x{10A38}-\x{10A3A}\x{10A3F}\x{10AE5}-\x{10AE6}\x{10D24}-\x{10D27}\x{10F46}-\x{10F50}\x{11001}\x{11038}-\x{11046}\x{1107F}-\x{11081}\x{110B3}-\x{110B6}\x{110B9}-\x{110BA}\x{110BD}\x{110CD}\x{11100}-\x{11102}\x{11127}-\x{1112B}\x{1112D}-\x{11134}\x{11173}\x{11180}-\x{11181}\x{111B6}-\x{111BE}\x{111C9}-\x{111CC}\x{1122F}-\x{11231}\x{11234}\x{11236}-\x{11237}\x{1123E}\x{112DF}\x{112E3}-\x{112EA}\x{11300}-\x{11301}\x{1133B}-\x{1133C}\x{11340}\x{11366}-\x{1136C}\x{11370}-\x{11374}\x{11438}-\x{1143F}\x{11442}-\x{11444}\x{11446}\x{1145E}\x{114B3}-\x{114B8}\x{114BA}\x{114BF}-\x{114C0}\x{114C2}-\x{114C3}\x{115B2}-\x{115B5}\x{115BC}-\x{115BD}\x{115BF}-\x{115C0}\x{115DC}-\x{115DD}\x{11633}-\x{1163A}\x{1163D}\x{1163F}-\x{11640}\x{116AB}\x{116AD}\x{116B0}-\x{116B5}\x{116B7}\x{1171D}-\x{1171F}\x{11722}-\x{11725}\x{11727}-\x{1172B}\x{1182F}-\x{11837}\x{11839}-\x{1183A}\x{11A01}-\x{11A0A}\x{11A33}-\x{11A38}\x{11A3B}-\x{11A3E}\x{11A47}\x{11A51}-\x{11A56}\x{11A59}-\x{11A5B}\x{11A8A}-\x{11A96}\x{11A98}-\x{11A99}\x{11C30}-\x{11C36}\x{11C38}-\x{11C3D}\x{11C3F}\x{11C92}-\x{11CA7}\x{11CAA}-\x{11CB0}\x{11CB2}-\x{11CB3}\x{11CB5}-\x{11CB6}\x{11D31}-\x{11D36}\x{11D3A}\x{11D3C}-\x{11D3D}\x{11D3F}-\x{11D45}\x{11D47}\x{11D90}-\x{11D91}\x{11D95}\x{11D97}\x{11EF3}-\x{11EF4}\x{16AF0}-\x{16AF4}\x{16B30}-\x{16B36}\x{16B40}-\x{16B43}\x{16F8F}-\x{16F92}\x{16F93}-\x{16F9F}\x{16FE0}-\x{16FE1}\x{1BC9D}-\x{1BC9E}\x{1BCA0}-\x{1BCA3}\x{1D167}-\x{1D169}\x{1D173}-\x{1D17A}\x{1D17B}-\x{1D182}\x{1D185}-\x{1D18B}\x{1D1AA}-\x{1D1AD}\x{1D242}-\x{1D244}\x{1DA00}-\x{1DA36}\x{1DA3B}-\x{1DA6C}\x{1DA75}\x{1DA84}\x{1DA9B}-\x{1DA9F}\x{1DAA1}-\x{1DAAF}\x{1E000}-\x{1E006}\x{1E008}-\x{1E018}\x{1E01B}-\x{1E021}\x{1E023}-\x{1E024}\x{1E026}-\x{1E02A}\x{1E8D0}-\x{1E8D6}\x{1E944}-\x{1E94A}\x{1F3FB}-\x{1F3FF}\x{E0001}\x{E0020}-\x{E007F}\x{E0100}-\x{E01EF}])(\pL)(\pL*+)/u';
<?php
return array (
'a' => 'A',
'b' => 'B',
'c' => 'C',
'd' => 'D',
'e' => 'E',
'f' => 'F',
'g' => 'G',
'h' => 'H',
'i' => 'I',
'j' => 'J',
'k' => 'K',
'l' => 'L',
'm' => 'M',
'n' => 'N',
'o' => 'O',
'p' => 'P',
'q' => 'Q',
'r' => 'R',
's' => 'S',
't' => 'T',
'u' => 'U',
'v' => 'V',
'w' => 'W',
'x' => 'X',
'y' => 'Y',
'z' => 'Z',
'µ' => 'Μ',
'à' => 'À',
'á' => 'Á',
'â' => 'Â',
'ã' => 'Ã',
'ä' => 'Ä',
'å' => 'Å',
'æ' => 'Æ',
'ç' => 'Ç',
'è' => 'È',
'é' => 'É',
'ê' => 'Ê',
'ë' => 'Ë',
'ì' => 'Ì',
'í' => 'Í',
'î' => 'Î',
'ï' => 'Ï',
'ð' => 'Ð',
'ñ' => 'Ñ',
'ò' => 'Ò',
'ó' => 'Ó',
'ô' => 'Ô',
'õ' => 'Õ',
'ö' => 'Ö',
'ø' => 'Ø',
'ù' => 'Ù',
'ú' => 'Ú',
'û' => 'Û',
'ü' => 'Ü',
'ý' => 'Ý',
'þ' => 'Þ',
'ÿ' => 'Ÿ',
'ā' => 'Ā',
'ă' => 'Ă',
'ą' => 'Ą',
'ć' => 'Ć',
'ĉ' => 'Ĉ',
'ċ' => 'Ċ',
'č' => 'Č',
'ď' => 'Ď',
'đ' => 'Đ',
'ē' => 'Ē',
'ĕ' => 'Ĕ',
'ė' => 'Ė',
'ę' => 'Ę',
'ě' => 'Ě',
'ĝ' => 'Ĝ',
'ğ' => 'Ğ',
'ġ' => 'Ġ',
'ģ' => 'Ģ',
'ĥ' => 'Ĥ',
'ħ' => 'Ħ',
'ĩ' => 'Ĩ',
'ī' => 'Ī',
'ĭ' => 'Ĭ',
'į' => 'Į',
'ı' => 'I',
'ij' => 'IJ',
'ĵ' => 'Ĵ',
'ķ' => 'Ķ',
'ĺ' => 'Ĺ',
'ļ' => 'Ļ',
'ľ' => 'Ľ',
'ŀ' => 'Ŀ',
'ł' => 'Ł',
'ń' => 'Ń',
'ņ' => 'Ņ',
'ň' => 'Ň',
'ŋ' => 'Ŋ',
'ō' => 'Ō',
'ŏ' => 'Ŏ',
'ő' => 'Ő',
'œ' => 'Œ',
'ŕ' => 'Ŕ',
'ŗ' => 'Ŗ',
'ř' => 'Ř',
'ś' => 'Ś',
'ŝ' => 'Ŝ',
'ş' => 'Ş',
'š' => 'Š',
'ţ' => 'Ţ',
'ť' => 'Ť',
'ŧ' => 'Ŧ',
'ũ' => 'Ũ',
'ū' => 'Ū',
'ŭ' => 'Ŭ',
'ů' => 'Ů',
'ű' => 'Ű',
'ų' => 'Ų',
'ŵ' => 'Ŵ',
'ŷ' => 'Ŷ',
'ź' => 'Ź',
'ż' => 'Ż',
'ž' => 'Ž',
'ſ' => 'S',
'ƀ' => 'Ƀ',
'ƃ' => 'Ƃ',
'ƅ' => 'Ƅ',
'ƈ' => 'Ƈ',
'ƌ' => 'Ƌ',
'ƒ' => 'Ƒ',
'ƕ' => 'Ƕ',
'ƙ' => 'Ƙ',
'ƚ' => 'Ƚ',
'ƞ' => 'Ƞ',
'ơ' => 'Ơ',
'ƣ' => 'Ƣ',
'ƥ' => 'Ƥ',
'ƨ' => 'Ƨ',
'ƭ' => 'Ƭ',
'ư' => 'Ư',
'ƴ' => 'Ƴ',
'ƶ' => 'Ƶ',
'ƹ' => 'Ƹ',
'ƽ' => 'Ƽ',
'ƿ' => 'Ƿ',
'Dž' => 'DŽ',
'dž' => 'DŽ',
'Lj' => 'LJ',
'lj' => 'LJ',
'Nj' => 'NJ',
'nj' => 'NJ',
'ǎ' => 'Ǎ',
'ǐ' => 'Ǐ',
'ǒ' => 'Ǒ',
'ǔ' => 'Ǔ',
'ǖ' => 'Ǖ',
'ǘ' => 'Ǘ',
'ǚ' => 'Ǚ',
'ǜ' => 'Ǜ',
'ǝ' => 'Ǝ',
'ǟ' => 'Ǟ',
'ǡ' => 'Ǡ',
'ǣ' => 'Ǣ',
'ǥ' => 'Ǥ',
'ǧ' => 'Ǧ',
'ǩ' => 'Ǩ',
'ǫ' => 'Ǫ',
'ǭ' => 'Ǭ',
'ǯ' => 'Ǯ',
'Dz' => 'DZ',
'dz' => 'DZ',
'ǵ' => 'Ǵ',
'ǹ' => 'Ǹ',
'ǻ' => 'Ǻ',
'ǽ' => 'Ǽ',
'ǿ' => 'Ǿ',
'ȁ' => 'Ȁ',
'ȃ' => 'Ȃ',
'ȅ' => 'Ȅ',
'ȇ' => 'Ȇ',
'ȉ' => 'Ȉ',
'ȋ' => 'Ȋ',
'ȍ' => 'Ȍ',
'ȏ' => 'Ȏ',
'ȑ' => 'Ȑ',
'ȓ' => 'Ȓ',
'ȕ' => 'Ȕ',
'ȗ' => 'Ȗ',
'ș' => 'Ș',
'ț' => 'Ț',
'ȝ' => 'Ȝ',
'ȟ' => 'Ȟ',
'ȣ' => 'Ȣ',
'ȥ' => 'Ȥ',
'ȧ' => 'Ȧ',
'ȩ' => 'Ȩ',
'ȫ' => 'Ȫ',
'ȭ' => 'Ȭ',
'ȯ' => 'Ȯ',
'ȱ' => 'Ȱ',
'ȳ' => 'Ȳ',
'ȼ' => 'Ȼ',
'ȿ' => 'Ȿ',
'ɀ' => 'Ɀ',
'ɂ' => 'Ɂ',
'ɇ' => 'Ɇ',
'ɉ' => 'Ɉ',
'ɋ' => 'Ɋ',
'ɍ' => 'Ɍ',
'ɏ' => 'Ɏ',
'ɐ' => 'Ɐ',
'ɑ' => 'Ɑ',
'ɒ' => 'Ɒ',
'ɓ' => 'Ɓ',
'ɔ' => 'Ɔ',
'ɖ' => 'Ɖ',
'ɗ' => 'Ɗ',
'ə' => 'Ə',
'ɛ' => 'Ɛ',
'ɜ' => '',
'ɠ' => 'Ɠ',
'ɡ' => 'Ɡ',
'ɣ' => 'Ɣ',
'ɥ' => 'Ɥ',
'ɦ' => 'Ɦ',
'ɨ' => 'Ɨ',
'ɩ' => 'Ɩ',
'ɪ' => 'Ɪ',
'ɫ' => 'Ɫ',
'ɬ' => 'Ɬ',
'ɯ' => 'Ɯ',
'ɱ' => 'Ɱ',
'ɲ' => 'Ɲ',
'ɵ' => 'Ɵ',
'ɽ' => 'Ɽ',
'ʀ' => 'Ʀ',
'ʂ' => 'Ʂ',
'ʃ' => 'Ʃ',
'ʇ' => 'Ʇ',
'ʈ' => 'Ʈ',
'ʉ' => 'Ʉ',
'ʊ' => 'Ʊ',
'ʋ' => 'Ʋ',
'ʌ' => 'Ʌ',
'ʒ' => 'Ʒ',
'ʝ' => '',
'ʞ' => 'Ʞ',
'ͅ' => 'Ι',
'ͱ' => 'Ͱ',
'ͳ' => 'Ͳ',
'ͷ' => 'Ͷ',
'ͻ' => 'Ͻ',
'ͼ' => 'Ͼ',
'ͽ' => 'Ͽ',
'ά' => 'Ά',
'έ' => 'Έ',
'ή' => 'Ή',
'ί' => 'Ί',
'α' => 'Α',
'β' => 'Β',
'γ' => 'Γ',
'δ' => 'Δ',
'ε' => 'Ε',
'ζ' => 'Ζ',
'η' => 'Η',
'θ' => 'Θ',
'ι' => 'Ι',
'κ' => 'Κ',
'λ' => 'Λ',
'μ' => 'Μ',
'ν' => 'Ν',
'ξ' => 'Ξ',
'ο' => 'Ο',
'π' => 'Π',
'ρ' => 'Ρ',
'ς' => 'Σ',
'σ' => 'Σ',
'τ' => 'Τ',
'υ' => 'Υ',
'φ' => 'Φ',
'χ' => 'Χ',
'ψ' => 'Ψ',
'ω' => 'Ω',
'ϊ' => 'Ϊ',
'ϋ' => 'Ϋ',
'ό' => 'Ό',
'ύ' => 'Ύ',
'ώ' => 'Ώ',
'ϐ' => 'Β',
'ϑ' => 'Θ',
'ϕ' => 'Φ',
'ϖ' => 'Π',
'ϗ' => 'Ϗ',
'ϙ' => 'Ϙ',
'ϛ' => 'Ϛ',
'ϝ' => 'Ϝ',
'ϟ' => 'Ϟ',
'ϡ' => 'Ϡ',
'ϣ' => 'Ϣ',
'ϥ' => 'Ϥ',
'ϧ' => 'Ϧ',
'ϩ' => 'Ϩ',
'ϫ' => 'Ϫ',
'ϭ' => 'Ϭ',
'ϯ' => 'Ϯ',
'ϰ' => 'Κ',
'ϱ' => 'Ρ',
'ϲ' => 'Ϲ',
'ϳ' => 'Ϳ',
'ϵ' => 'Ε',
'ϸ' => 'Ϸ',
'ϻ' => 'Ϻ',
'а' => 'А',
'б' => 'Б',
'в' => 'В',
'г' => 'Г',
'д' => 'Д',
'е' => 'Е',
'ж' => 'Ж',
'з' => 'З',
'и' => 'И',
'й' => 'Й',
'к' => 'К',
'л' => 'Л',
'м' => 'М',
'н' => 'Н',
'о' => 'О',
'п' => 'П',
'р' => 'Р',
'с' => 'С',
'т' => 'Т',
'у' => 'У',
'ф' => 'Ф',
'х' => 'Х',
'ц' => 'Ц',
'ч' => 'Ч',
'ш' => 'Ш',
'щ' => 'Щ',
'ъ' => 'Ъ',
'ы' => 'Ы',
'ь' => 'Ь',
'э' => 'Э',
'ю' => 'Ю',
'я' => 'Я',
'ѐ' => 'Ѐ',
'ё' => 'Ё',
'ђ' => 'Ђ',
'ѓ' => 'Ѓ',
'є' => 'Є',
'ѕ' => 'Ѕ',
'і' => 'І',
'ї' => 'Ї',
'ј' => 'Ј',
'љ' => 'Љ',
'њ' => 'Њ',
'ћ' => 'Ћ',
'ќ' => 'Ќ',
'ѝ' => 'Ѝ',
'ў' => 'Ў',
'џ' => 'Џ',
'ѡ' => 'Ѡ',
'ѣ' => 'Ѣ',
'ѥ' => 'Ѥ',
'ѧ' => 'Ѧ',
'ѩ' => 'Ѩ',
'ѫ' => 'Ѫ',
'ѭ' => 'Ѭ',
'ѯ' => 'Ѯ',
'ѱ' => 'Ѱ',
'ѳ' => 'Ѳ',
'ѵ' => 'Ѵ',
'ѷ' => 'Ѷ',
'ѹ' => 'Ѹ',
'ѻ' => 'Ѻ',
'ѽ' => 'Ѽ',
'ѿ' => 'Ѿ',
'ҁ' => 'Ҁ',
'ҋ' => 'Ҋ',
'ҍ' => 'Ҍ',
'ҏ' => 'Ҏ',
'ґ' => 'Ґ',
'ғ' => 'Ғ',
'ҕ' => 'Ҕ',
'җ' => 'Җ',
'ҙ' => 'Ҙ',
'қ' => 'Қ',
'ҝ' => 'Ҝ',
'ҟ' => 'Ҟ',
'ҡ' => 'Ҡ',
'ң' => 'Ң',
'ҥ' => 'Ҥ',
'ҧ' => 'Ҧ',
'ҩ' => 'Ҩ',
'ҫ' => 'Ҫ',
'ҭ' => 'Ҭ',
'ү' => 'Ү',
'ұ' => 'Ұ',
'ҳ' => 'Ҳ',
'ҵ' => 'Ҵ',
'ҷ' => 'Ҷ',
'ҹ' => 'Ҹ',
'һ' => 'Һ',
'ҽ' => 'Ҽ',
'ҿ' => 'Ҿ',
'ӂ' => 'Ӂ',
'ӄ' => 'Ӄ',
'ӆ' => 'Ӆ',
'ӈ' => 'Ӈ',
'ӊ' => 'Ӊ',
'ӌ' => 'Ӌ',
'ӎ' => 'Ӎ',
'ӏ' => 'Ӏ',
'ӑ' => 'Ӑ',
'ӓ' => 'Ӓ',
'ӕ' => 'Ӕ',
'ӗ' => 'Ӗ',
'ә' => 'Ә',
'ӛ' => 'Ӛ',
'ӝ' => 'Ӝ',
'ӟ' => 'Ӟ',
'ӡ' => 'Ӡ',
'ӣ' => 'Ӣ',
'ӥ' => 'Ӥ',
'ӧ' => 'Ӧ',
'ө' => 'Ө',
'ӫ' => 'Ӫ',
'ӭ' => 'Ӭ',
'ӯ' => 'Ӯ',
'ӱ' => 'Ӱ',
'ӳ' => 'Ӳ',
'ӵ' => 'Ӵ',
'ӷ' => 'Ӷ',
'ӹ' => 'Ӹ',
'ӻ' => 'Ӻ',
'ӽ' => 'Ӽ',
'ӿ' => 'Ӿ',
'ԁ' => 'Ԁ',
'ԃ' => 'Ԃ',
'ԅ' => 'Ԅ',
'ԇ' => 'Ԇ',
'ԉ' => 'Ԉ',
'ԋ' => 'Ԋ',
'ԍ' => 'Ԍ',
'ԏ' => 'Ԏ',
'ԑ' => 'Ԑ',
'ԓ' => 'Ԓ',
'ԕ' => 'Ԕ',
'ԗ' => 'Ԗ',
'ԙ' => 'Ԙ',
'ԛ' => 'Ԛ',
'ԝ' => 'Ԝ',
'ԟ' => 'Ԟ',
'ԡ' => 'Ԡ',
'ԣ' => 'Ԣ',
'ԥ' => 'Ԥ',
'ԧ' => 'Ԧ',
'ԩ' => 'Ԩ',
'ԫ' => 'Ԫ',
'ԭ' => 'Ԭ',
'ԯ' => 'Ԯ',
'ա' => 'Ա',
'բ' => 'Բ',
'գ' => 'Գ',
'դ' => 'Դ',
'ե' => 'Ե',
'զ' => 'Զ',
'է' => 'Է',
'ը' => 'Ը',
'թ' => 'Թ',
'ժ' => 'Ժ',
'ի' => 'Ի',
'լ' => 'Լ',
'խ' => 'Խ',
'ծ' => 'Ծ',
'կ' => 'Կ',
'հ' => 'Հ',
'ձ' => 'Ձ',
'ղ' => 'Ղ',
'ճ' => 'Ճ',
'մ' => 'Մ',
'յ' => 'Յ',
'ն' => 'Ն',
'շ' => 'Շ',
'ո' => 'Ո',
'չ' => 'Չ',
'պ' => 'Պ',
'ջ' => 'Ջ',
'ռ' => 'Ռ',
'ս' => 'Ս',
'վ' => 'Վ',
'տ' => 'Տ',
'ր' => 'Ր',
'ց' => 'Ց',
'ւ' => 'Ւ',
'փ' => 'Փ',
'ք' => 'Ք',
'օ' => 'Օ',
'ֆ' => 'Ֆ',
'ა' => 'Ა',
'ბ' => 'Ბ',
'გ' => 'Გ',
'დ' => 'Დ',
'ე' => 'Ე',
'ვ' => 'Ვ',
'ზ' => 'Ზ',
'თ' => 'Თ',
'ი' => 'Ი',
'კ' => 'Კ',
'ლ' => 'Ლ',
'მ' => 'Მ',
'ნ' => 'Ნ',
'ო' => 'Ო',
'პ' => 'Პ',
'ჟ' => 'Ჟ',
'რ' => 'Რ',
'ს' => 'Ს',
'ტ' => 'Ტ',
'უ' => 'Უ',
'ფ' => 'Ფ',
'ქ' => 'Ქ',
'ღ' => 'Ღ',
'' => 'Ყ',
'შ' => 'Შ',
'ჩ' => 'Ჩ',
'ც' => 'Ც',
'ძ' => 'Ძ',
'წ' => 'Წ',
'ჭ' => 'Ჭ',
'ხ' => 'Ხ',
'ჯ' => 'Ჯ',
'ჰ' => 'Ჰ',
'ჱ' => 'Ჱ',
'ჲ' => 'Ჲ',
'ჳ' => 'Ჳ',
'ჴ' => 'Ჴ',
'ჵ' => 'Ჵ',
'ჶ' => 'Ჶ',
'ჷ' => 'Ჷ',
'ჸ' => 'Ჸ',
'ჹ' => 'Ჹ',
'ჺ' => 'Ჺ',
'ჽ' => 'Ჽ',
'ჾ' => 'Ჾ',
'' => 'Ჿ',
'ᏸ' => 'Ᏸ',
'ᏹ' => 'Ᏹ',
'ᏺ' => 'Ᏺ',
'ᏻ' => '',
'ᏼ' => '',
'ᏽ' => 'Ᏽ',
'ᲀ' => 'В',
'ᲁ' => 'Д',
'ᲂ' => 'О',
'ᲃ' => 'С',
'ᲄ' => 'Т',
'ᲅ' => 'Т',
'ᲆ' => 'Ъ',
'ᲇ' => 'Ѣ',
'ᲈ' => 'Ꙋ',
'ᵹ' => 'Ᵹ',
'ᵽ' => 'Ᵽ',
'ᶎ' => 'Ᶎ',
'ḁ' => 'Ḁ',
'ḃ' => 'Ḃ',
'ḅ' => 'Ḅ',
'ḇ' => 'Ḇ',
'ḉ' => 'Ḉ',
'ḋ' => 'Ḋ',
'ḍ' => 'Ḍ',
'ḏ' => 'Ḏ',
'ḑ' => 'Ḑ',
'ḓ' => 'Ḓ',
'ḕ' => 'Ḕ',
'ḗ' => 'Ḗ',
'ḙ' => 'Ḙ',
'ḛ' => 'Ḛ',
'ḝ' => 'Ḝ',
'ḟ' => 'Ḟ',
'ḡ' => 'Ḡ',
'ḣ' => 'Ḣ',
'ḥ' => 'Ḥ',
'ḧ' => 'Ḧ',
'ḩ' => 'Ḩ',
'ḫ' => 'Ḫ',
'ḭ' => 'Ḭ',
'ḯ' => 'Ḯ',
'ḱ' => 'Ḱ',
'ḳ' => 'Ḳ',
'ḵ' => 'Ḵ',
'ḷ' => 'Ḷ',
'ḹ' => 'Ḹ',
'ḻ' => 'Ḻ',
'ḽ' => 'Ḽ',
'ḿ' => 'Ḿ',
'ṁ' => 'Ṁ',
'ṃ' => 'Ṃ',
'ṅ' => 'Ṅ',
'ṇ' => 'Ṇ',
'ṉ' => 'Ṉ',
'ṋ' => 'Ṋ',
'ṍ' => 'Ṍ',
'ṏ' => 'Ṏ',
'ṑ' => 'Ṑ',
'ṓ' => 'Ṓ',
'ṕ' => 'Ṕ',
'ṗ' => 'Ṗ',
'ṙ' => 'Ṙ',
'ṛ' => 'Ṛ',
'ṝ' => 'Ṝ',
'ṟ' => 'Ṟ',
'ṡ' => 'Ṡ',
'ṣ' => 'Ṣ',
'ṥ' => 'Ṥ',
'ṧ' => 'Ṧ',
'ṩ' => 'Ṩ',
'ṫ' => 'Ṫ',
'ṭ' => 'Ṭ',
'ṯ' => 'Ṯ',
'ṱ' => 'Ṱ',
'ṳ' => 'Ṳ',
'ṵ' => 'Ṵ',
'ṷ' => 'Ṷ',
'ṹ' => 'Ṹ',
'ṻ' => 'Ṻ',
'ṽ' => 'Ṽ',
'ṿ' => 'Ṿ',
'ẁ' => 'Ẁ',
'ẃ' => 'Ẃ',
'ẅ' => 'Ẅ',
'ẇ' => 'Ẇ',
'ẉ' => 'Ẉ',
'ẋ' => 'Ẋ',
'ẍ' => 'Ẍ',
'ẏ' => 'Ẏ',
'ẑ' => 'Ẑ',
'ẓ' => 'Ẓ',
'ẕ' => 'Ẕ',
'ẛ' => 'Ṡ',
'ạ' => 'Ạ',
'ả' => 'Ả',
'ấ' => 'Ấ',
'ầ' => 'Ầ',
'ẩ' => 'Ẩ',
'ẫ' => 'Ẫ',
'ậ' => 'Ậ',
'ắ' => 'Ắ',
'ằ' => 'Ằ',
'ẳ' => 'Ẳ',
'ẵ' => 'Ẵ',
'ặ' => 'Ặ',
'ẹ' => 'Ẹ',
'ẻ' => 'Ẻ',
'ẽ' => 'Ẽ',
'ế' => 'Ế',
'ề' => 'Ề',
'ể' => 'Ể',
'ễ' => 'Ễ',
'ệ' => 'Ệ',
'ỉ' => 'Ỉ',
'ị' => 'Ị',
'ọ' => 'Ọ',
'ỏ' => 'Ỏ',
'ố' => 'Ố',
'ồ' => 'Ồ',
'ổ' => 'Ổ',
'ỗ' => 'Ỗ',
'ộ' => 'Ộ',
'ớ' => 'Ớ',
'ờ' => 'Ờ',
'ở' => 'Ở',
'ỡ' => 'Ỡ',
'ợ' => 'Ợ',
'ụ' => 'Ụ',
'ủ' => 'Ủ',
'ứ' => 'Ứ',
'ừ' => 'Ừ',
'ử' => 'Ử',
'ữ' => 'Ữ',
'ự' => 'Ự',
'ỳ' => 'Ỳ',
'ỵ' => 'Ỵ',
'ỷ' => 'Ỷ',
'ỹ' => 'Ỹ',
'ỻ' => 'Ỻ',
'ỽ' => 'Ỽ',
'ỿ' => 'Ỿ',
'ἀ' => 'Ἀ',
'ἁ' => 'Ἁ',
'ἂ' => 'Ἂ',
'ἃ' => 'Ἃ',
'ἄ' => 'Ἄ',
'ἅ' => 'Ἅ',
'ἆ' => 'Ἆ',
'ἇ' => 'Ἇ',
'ἐ' => 'Ἐ',
'ἑ' => 'Ἑ',
'ἒ' => 'Ἒ',
'ἓ' => 'Ἓ',
'ἔ' => 'Ἔ',
'ἕ' => 'Ἕ',
'ἠ' => 'Ἠ',
'ἡ' => 'Ἡ',
'ἢ' => 'Ἢ',
'ἣ' => 'Ἣ',
'ἤ' => 'Ἤ',
'ἥ' => 'Ἥ',
'ἦ' => 'Ἦ',
'ἧ' => 'Ἧ',
'ἰ' => 'Ἰ',
'ἱ' => 'Ἱ',
'ἲ' => 'Ἲ',
'ἳ' => 'Ἳ',
'ἴ' => 'Ἴ',
'ἵ' => 'Ἵ',
'ἶ' => 'Ἶ',
'ἷ' => 'Ἷ',
'ὀ' => 'Ὀ',
'ὁ' => 'Ὁ',
'ὂ' => 'Ὂ',
'ὃ' => 'Ὃ',
'ὄ' => 'Ὄ',
'ὅ' => 'Ὅ',
'ὑ' => 'Ὑ',
'ὓ' => 'Ὓ',
'ὕ' => 'Ὕ',
'ὗ' => 'Ὗ',
'ὠ' => 'Ὠ',
'ὡ' => 'Ὡ',
'ὢ' => 'Ὢ',
'ὣ' => 'Ὣ',
'ὤ' => 'Ὤ',
'ὥ' => 'Ὥ',
'ὦ' => 'Ὦ',
'ὧ' => 'Ὧ',
'ὰ' => 'Ὰ',
'ά' => 'Ά',
'ὲ' => 'Ὲ',
'έ' => 'Έ',
'ὴ' => 'Ὴ',
'ή' => 'Ή',
'ὶ' => 'Ὶ',
'ί' => 'Ί',
'ὸ' => 'Ὸ',
'ό' => 'Ό',
'ὺ' => 'Ὺ',
'ύ' => 'Ύ',
'ὼ' => 'Ὼ',
'ώ' => 'Ώ',
'ᾀ' => 'ἈΙ',
'ᾁ' => 'ἉΙ',
'ᾂ' => 'ἊΙ',
'ᾃ' => 'ἋΙ',
'ᾄ' => 'ἌΙ',
'ᾅ' => 'ἍΙ',
'ᾆ' => 'ἎΙ',
'ᾇ' => 'ἏΙ',
'ᾐ' => 'ἨΙ',
'ᾑ' => 'ἩΙ',
'ᾒ' => 'ἪΙ',
'ᾓ' => 'ἫΙ',
'ᾔ' => 'ἬΙ',
'ᾕ' => 'ἭΙ',
'ᾖ' => 'ἮΙ',
'ᾗ' => 'ἯΙ',
'ᾠ' => 'ὨΙ',
'ᾡ' => 'ὩΙ',
'ᾢ' => 'ὪΙ',
'ᾣ' => 'ὫΙ',
'ᾤ' => 'ὬΙ',
'ᾥ' => 'ὭΙ',
'ᾦ' => 'ὮΙ',
'ᾧ' => 'ὯΙ',
'ᾰ' => 'Ᾰ',
'ᾱ' => 'Ᾱ',
'ᾳ' => 'ΑΙ',
'' => 'Ι',
'ῃ' => 'ΗΙ',
'ῐ' => 'Ῐ',
'ῑ' => 'Ῑ',
'ῠ' => 'Ῠ',
'ῡ' => 'Ῡ',
'ῥ' => 'Ῥ',
'ῳ' => 'ΩΙ',
'ⅎ' => 'Ⅎ',
'' => '',
'ⅱ' => 'Ⅱ',
'ⅲ' => 'Ⅲ',
'ⅳ' => 'Ⅳ',
'' => '',
'ⅵ' => 'Ⅵ',
'ⅶ' => 'Ⅶ',
'ⅷ' => 'Ⅷ',
'ⅸ' => 'Ⅸ',
'' => '',
'ⅺ' => 'Ⅺ',
'ⅻ' => 'Ⅻ',
'' => '',
'' => '',
'' => '',
'ⅿ' => '',
'ↄ' => 'Ↄ',
'ⓐ' => 'Ⓐ',
'ⓑ' => 'Ⓑ',
'ⓒ' => 'Ⓒ',
'ⓓ' => 'Ⓓ',
'ⓔ' => 'Ⓔ',
'ⓕ' => 'Ⓕ',
'ⓖ' => 'Ⓖ',
'ⓗ' => 'Ⓗ',
'ⓘ' => 'Ⓘ',
'ⓙ' => 'Ⓙ',
'ⓚ' => 'Ⓚ',
'ⓛ' => 'Ⓛ',
'ⓜ' => 'Ⓜ',
'ⓝ' => 'Ⓝ',
'ⓞ' => 'Ⓞ',
'ⓟ' => 'Ⓟ',
'ⓠ' => 'Ⓠ',
'ⓡ' => 'Ⓡ',
'ⓢ' => 'Ⓢ',
'ⓣ' => 'Ⓣ',
'ⓤ' => 'Ⓤ',
'ⓥ' => 'Ⓥ',
'ⓦ' => 'Ⓦ',
'ⓧ' => 'Ⓧ',
'ⓨ' => 'Ⓨ',
'ⓩ' => 'Ⓩ',
'ⰰ' => 'Ⰰ',
'ⰱ' => 'Ⰱ',
'ⰲ' => 'Ⰲ',
'ⰳ' => 'Ⰳ',
'ⰴ' => 'Ⰴ',
'ⰵ' => 'Ⰵ',
'ⰶ' => 'Ⰶ',
'ⰷ' => 'Ⰷ',
'ⰸ' => 'Ⰸ',
'ⰹ' => 'Ⰹ',
'ⰺ' => 'Ⰺ',
'ⰻ' => 'Ⰻ',
'ⰼ' => 'Ⰼ',
'ⰽ' => 'Ⰽ',
'ⰾ' => 'Ⰾ',
'ⰿ' => 'Ⰿ',
'ⱀ' => 'Ⱀ',
'ⱁ' => 'Ⱁ',
'ⱂ' => 'Ⱂ',
'ⱃ' => 'Ⱃ',
'ⱄ' => 'Ⱄ',
'ⱅ' => 'Ⱅ',
'ⱆ' => 'Ⱆ',
'ⱇ' => 'Ⱇ',
'ⱈ' => 'Ⱈ',
'ⱉ' => 'Ⱉ',
'ⱊ' => 'Ⱊ',
'ⱋ' => 'Ⱋ',
'ⱌ' => 'Ⱌ',
'ⱍ' => 'Ⱍ',
'ⱎ' => 'Ⱎ',
'ⱏ' => 'Ⱏ',
'ⱐ' => 'Ⱐ',
'ⱑ' => 'Ⱑ',
'ⱒ' => 'Ⱒ',
'ⱓ' => 'Ⱓ',
'ⱔ' => 'Ⱔ',
'ⱕ' => 'Ⱕ',
'ⱖ' => 'Ⱖ',
'ⱗ' => 'Ⱗ',
'ⱘ' => 'Ⱘ',
'ⱙ' => 'Ⱙ',
'ⱚ' => 'Ⱚ',
'ⱛ' => 'Ⱛ',
'ⱜ' => 'Ⱜ',
'ⱝ' => 'Ⱝ',
'ⱞ' => 'Ⱞ',
'ⱡ' => 'Ⱡ',
'ⱥ' => 'Ⱥ',
'ⱦ' => 'Ⱦ',
'ⱨ' => 'Ⱨ',
'ⱪ' => 'Ⱪ',
'ⱬ' => 'Ⱬ',
'ⱳ' => 'Ⱳ',
'ⱶ' => 'Ⱶ',
'ⲁ' => 'Ⲁ',
'ⲃ' => 'Ⲃ',
'' => 'Ⲅ',
'ⲇ' => 'Ⲇ',
'ⲉ' => 'Ⲉ',
'ⲋ' => 'Ⲋ',
'ⲍ' => 'Ⲍ',
'ⲏ' => '',
'ⲑ' => 'Ⲑ',
'ⲓ' => '',
'ⲕ' => '',
'ⲗ' => 'Ⲗ',
'ⲙ' => '',
'ⲛ' => '',
'ⲝ' => 'Ⲝ',
'' => '',
'ⲡ' => 'Ⲡ',
'' => '',
'' => '',
'ⲧ' => '',
'ⲩ' => '',
'ⲫ' => 'Ⲫ',
'ⲭ' => '',
'ⲯ' => 'Ⲯ',
'ⲱ' => 'Ⲱ',
'ⲳ' => 'Ⲳ',
'ⲵ' => 'Ⲵ',
'ⲷ' => 'Ⲷ',
'ⲹ' => 'Ⲹ',
'ⲻ' => '',
'ⲽ' => 'Ⲽ',
'ⲿ' => 'Ⲿ',
'ⳁ' => 'Ⳁ',
'ⳃ' => 'Ⳃ',
'ⳅ' => 'Ⳅ',
'ⳇ' => '',
'ⳉ' => 'Ⳉ',
'ⳋ' => '',
'ⳍ' => '',
'ⳏ' => 'Ⳏ',
'ⳑ' => '',
'ⳓ' => '',
'ⳕ' => 'Ⳕ',
'ⳗ' => 'Ⳗ',
'ⳙ' => 'Ⳙ',
'ⳛ' => 'Ⳛ',
'ⳝ' => 'Ⳝ',
'ⳟ' => 'Ⳟ',
'ⳡ' => 'Ⳡ',
'ⳣ' => 'Ⳣ',
'ⳬ' => 'Ⳬ',
'ⳮ' => 'Ⳮ',
'ⳳ' => 'Ⳳ',
'ⴀ' => 'Ⴀ',
'ⴁ' => 'Ⴁ',
'ⴂ' => 'Ⴂ',
'ⴃ' => 'Ⴃ',
'ⴄ' => 'Ⴄ',
'ⴅ' => 'Ⴅ',
'ⴆ' => 'Ⴆ',
'ⴇ' => 'Ⴇ',
'ⴈ' => 'Ⴈ',
'ⴉ' => 'Ⴉ',
'ⴊ' => 'Ⴊ',
'ⴋ' => 'Ⴋ',
'ⴌ' => 'Ⴌ',
'ⴍ' => 'Ⴍ',
'ⴎ' => 'Ⴎ',
'ⴏ' => 'Ⴏ',
'ⴐ' => 'Ⴐ',
'ⴑ' => 'Ⴑ',
'ⴒ' => 'Ⴒ',
'ⴓ' => 'Ⴓ',
'ⴔ' => 'Ⴔ',
'ⴕ' => 'Ⴕ',
'ⴖ' => 'Ⴖ',
'ⴗ' => 'Ⴗ',
'ⴘ' => 'Ⴘ',
'ⴙ' => 'Ⴙ',
'ⴚ' => 'Ⴚ',
'ⴛ' => 'Ⴛ',
'ⴜ' => 'Ⴜ',
'ⴝ' => 'Ⴝ',
'ⴞ' => 'Ⴞ',
'ⴟ' => 'Ⴟ',
'ⴠ' => 'Ⴠ',
'ⴡ' => 'Ⴡ',
'ⴢ' => 'Ⴢ',
'ⴣ' => 'Ⴣ',
'ⴤ' => 'Ⴤ',
'ⴥ' => 'Ⴥ',
'ⴧ' => 'Ⴧ',
'ⴭ' => 'Ⴭ',
'ꙁ' => 'Ꙁ',
'ꙃ' => 'Ꙃ',
'ꙅ' => '',
'' => 'Ꙇ',
'ꙉ' => 'Ꙉ',
'ꙋ' => 'Ꙋ',
'ꙍ' => 'Ꙍ',
'ꙏ' => 'Ꙏ',
'ꙑ' => 'Ꙑ',
'ꙓ' => 'Ꙓ',
'ꙕ' => 'Ꙕ',
'ꙗ' => 'Ꙗ',
'ꙙ' => 'Ꙙ',
'ꙛ' => 'Ꙛ',
'ꙝ' => 'Ꙝ',
'ꙟ' => 'Ꙟ',
'ꙡ' => 'Ꙡ',
'ꙣ' => 'Ꙣ',
'ꙥ' => 'Ꙥ',
'ꙧ' => 'Ꙧ',
'ꙩ' => 'Ꙩ',
'ꙫ' => 'Ꙫ',
'ꙭ' => 'Ꙭ',
'ꚁ' => 'Ꚁ',
'ꚃ' => 'Ꚃ',
'ꚅ' => 'Ꚅ',
'ꚇ' => 'Ꚇ',
'ꚉ' => 'Ꚉ',
'ꚋ' => 'Ꚋ',
'ꚍ' => 'Ꚍ',
'ꚏ' => 'Ꚏ',
'ꚑ' => 'Ꚑ',
'ꚓ' => 'Ꚓ',
'ꚕ' => 'Ꚕ',
'ꚗ' => 'Ꚗ',
'ꚙ' => 'Ꚙ',
'ꚛ' => 'Ꚛ',
'ꜣ' => 'Ꜣ',
'ꜥ' => 'Ꜥ',
'ꜧ' => 'Ꜧ',
'ꜩ' => 'Ꜩ',
'ꜫ' => 'Ꜫ',
'ꜭ' => 'Ꜭ',
'ꜯ' => 'Ꜯ',
'ꜳ' => 'Ꜳ',
'ꜵ' => 'Ꜵ',
'ꜷ' => 'Ꜷ',
'ꜹ' => 'Ꜹ',
'ꜻ' => 'Ꜻ',
'ꜽ' => 'Ꜽ',
'ꜿ' => 'Ꜿ',
'ꝁ' => 'Ꝁ',
'ꝃ' => 'Ꝃ',
'ꝅ' => 'Ꝅ',
'ꝇ' => 'Ꝇ',
'ꝉ' => 'Ꝉ',
'ꝋ' => 'Ꝋ',
'ꝍ' => 'Ꝍ',
'ꝏ' => 'Ꝏ',
'ꝑ' => 'Ꝑ',
'ꝓ' => 'Ꝓ',
'ꝕ' => 'Ꝕ',
'ꝗ' => 'Ꝗ',
'ꝙ' => 'Ꝙ',
'ꝛ' => '',
'ꝝ' => 'Ꝝ',
'ꝟ' => 'Ꝟ',
'ꝡ' => 'Ꝡ',
'ꝣ' => 'Ꝣ',
'ꝥ' => 'Ꝥ',
'ꝧ' => 'Ꝧ',
'ꝩ' => 'Ꝩ',
'ꝫ' => '',
'ꝭ' => 'Ꝭ',
'ꝯ' => '',
'ꝺ' => 'Ꝺ',
'ꝼ' => 'Ꝼ',
'ꝿ' => 'Ꝿ',
'ꞁ' => 'Ꞁ',
'ꞃ' => 'Ꞃ',
'ꞅ' => 'Ꞅ',
'ꞇ' => 'Ꞇ',
'' => 'Ꞌ',
'ꞑ' => 'Ꞑ',
'ꞓ' => 'Ꞓ',
'ꞔ' => 'Ꞔ',
'ꞗ' => 'Ꞗ',
'' => '',
'ꞛ' => 'Ꞛ',
'ꞝ' => 'Ꞝ',
'' => 'Ꞟ',
'ꞡ' => 'Ꞡ',
'ꞣ' => 'Ꞣ',
'ꞥ' => 'Ꞥ',
'ꞧ' => 'Ꞧ',
'ꞩ' => 'Ꞩ',
'ꞵ' => '',
'ꞷ' => 'Ꞷ',
'ꞹ' => 'Ꞹ',
'ꞻ' => 'Ꞻ',
'ꞽ' => 'Ꞽ',
'ꞿ' => 'Ꞿ',
'ꟃ' => 'Ꟃ',
'ꟈ' => 'Ꟈ',
'ꟊ' => 'Ꟊ',
'ꟶ' => 'Ꟶ',
'ꭓ' => '',
'ꭰ' => '',
'ꭱ' => '',
'ꭲ' => '',
'ꭳ' => 'Ꭳ',
'ꭴ' => 'Ꭴ',
'' => '',
'ꭶ' => 'Ꭶ',
'ꭷ' => 'Ꭷ',
'ꭸ' => 'Ꭸ',
'ꭹ' => '',
'ꭺ' => '',
'ꭻ' => '',
'ꭼ' => '',
'ꭽ' => 'Ꭽ',
'ꭾ' => '',
'ꭿ' => 'Ꭿ',
'ꮀ' => 'Ꮀ',
'' => 'Ꮁ',
'ꮂ' => 'Ꮂ',
'' => '',
'ꮄ' => 'Ꮄ',
'ꮅ' => 'Ꮅ',
'ꮆ' => 'Ꮆ',
'ꮇ' => '',
'ꮈ' => 'Ꮈ',
'ꮉ' => 'Ꮉ',
'ꮊ' => 'Ꮊ',
'ꮋ' => '',
'ꮌ' => 'Ꮌ',
'ꮍ' => '',
'ꮎ' => 'Ꮎ',
'ꮏ' => 'Ꮏ',
'ꮐ' => '',
'ꮑ' => 'Ꮑ',
'ꮒ' => '',
'' => '',
'ꮔ' => 'Ꮔ',
'ꮕ' => 'Ꮕ',
'ꮖ' => 'Ꮖ',
'ꮗ' => 'Ꮗ',
'ꮘ' => 'Ꮘ',
'ꮙ' => 'Ꮙ',
'ꮚ' => 'Ꮚ',
'ꮛ' => 'Ꮛ',
'ꮜ' => 'Ꮜ',
'ꮝ' => 'Ꮝ',
'ꮞ' => '',
'ꮟ' => '',
'ꮠ' => 'Ꮠ',
'ꮡ' => 'Ꮡ',
'ꮢ' => '',
'ꮣ' => 'Ꮣ',
'ꮤ' => '',
'ꮥ' => '',
'ꮦ' => 'Ꮦ',
'ꮧ' => 'Ꮧ',
'ꮨ' => 'Ꮨ',
'' => '',
'' => '',
'ꮫ' => 'Ꮫ',
'ꮬ' => 'Ꮬ',
'ꮭ' => 'Ꮭ',
'ꮮ' => '',
'' => '',
'ꮰ' => 'Ꮰ',
'ꮱ' => 'Ꮱ',
'ꮲ' => '',
'ꮳ' => 'Ꮳ',
'ꮴ' => 'Ꮴ',
'ꮵ' => 'Ꮵ',
'ꮶ' => '',
'ꮷ' => '',
'ꮸ' => 'Ꮸ',
'ꮹ' => 'Ꮹ',
'ꮺ' => 'Ꮺ',
'ꮻ' => 'Ꮻ',
'ꮼ' => 'Ꮼ',
'ꮽ' => 'Ꮽ',
'ꮾ' => '',
'ꮿ' => 'Ꮿ',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'𐐨' => '𐐀',
'𐐩' => '𐐁',
'𐐪' => '𐐂',
'𐐫' => '𐐃',
'𐐬' => '𐐄',
'𐐭' => '𐐅',
'𐐮' => '𐐆',
'𐐯' => '𐐇',
'𐐰' => '𐐈',
'𐐱' => '𐐉',
'𐐲' => '𐐊',
'𐐳' => '𐐋',
'𐐴' => '𐐌',
'𐐵' => '𐐍',
'𐐶' => '𐐎',
'𐐷' => '𐐏',
'𐐸' => '𐐐',
'𐐹' => '𐐑',
'𐐺' => '𐐒',
'𐐻' => '𐐓',
'𐐼' => '𐐔',
'𐐽' => '𐐕',
'𐐾' => '𐐖',
'𐐿' => '𐐗',
'𐑀' => '𐐘',
'𐑁' => '𐐙',
'𐑂' => '𐐚',
'𐑃' => '𐐛',
'𐑄' => '𐐜',
'𐑅' => '𐐝',
'𐑆' => '𐐞',
'𐑇' => '𐐟',
'𐑈' => '𐐠',
'𐑉' => '𐐡',
'𐑊' => '𐐢',
'𐑋' => '𐐣',
'𐑌' => '𐐤',
'𐑍' => '𐐥',
'𐑎' => '𐐦',
'𐑏' => '𐐧',
'𐓘' => '𐒰',
'𐓙' => '𐒱',
'𐓚' => '𐒲',
'𐓛' => '𐒳',
'𐓜' => '𐒴',
'𐓝' => '𐒵',
'𐓞' => '𐒶',
'𐓟' => '𐒷',
'𐓠' => '𐒸',
'𐓡' => '𐒹',
'𐓢' => '𐒺',
'𐓣' => '𐒻',
'𐓤' => '𐒼',
'𐓥' => '𐒽',
'𐓦' => '𐒾',
'𐓧' => '𐒿',
'𐓨' => '𐓀',
'𐓩' => '𐓁',
'𐓪' => '𐓂',
'𐓫' => '𐓃',
'𐓬' => '𐓄',
'𐓭' => '𐓅',
'𐓮' => '𐓆',
'𐓯' => '𐓇',
'𐓰' => '𐓈',
'𐓱' => '𐓉',
'𐓲' => '𐓊',
'𐓳' => '𐓋',
'𐓴' => '𐓌',
'𐓵' => '𐓍',
'𐓶' => '𐓎',
'𐓷' => '𐓏',
'𐓸' => '𐓐',
'𐓹' => '𐓑',
'𐓺' => '𐓒',
'𐓻' => '𐓓',
'𐳀' => '𐲀',
'𐳁' => '𐲁',
'𐳂' => '𐲂',
'𐳃' => '𐲃',
'𐳄' => '𐲄',
'𐳅' => '𐲅',
'𐳆' => '𐲆',
'𐳇' => '𐲇',
'𐳈' => '𐲈',
'𐳉' => '𐲉',
'𐳊' => '𐲊',
'𐳋' => '𐲋',
'𐳌' => '𐲌',
'𐳍' => '𐲍',
'𐳎' => '𐲎',
'𐳏' => '𐲏',
'𐳐' => '𐲐',
'𐳑' => '𐲑',
'𐳒' => '𐲒',
'𐳓' => '𐲓',
'𐳔' => '𐲔',
'𐳕' => '𐲕',
'𐳖' => '𐲖',
'𐳗' => '𐲗',
'𐳘' => '𐲘',
'𐳙' => '𐲙',
'𐳚' => '𐲚',
'𐳛' => '𐲛',
'𐳜' => '𐲜',
'𐳝' => '𐲝',
'𐳞' => '𐲞',
'𐳟' => '𐲟',
'𐳠' => '𐲠',
'𐳡' => '𐲡',
'𐳢' => '𐲢',
'𐳣' => '𐲣',
'𐳤' => '𐲤',
'𐳥' => '𐲥',
'𐳦' => '𐲦',
'𐳧' => '𐲧',
'𐳨' => '𐲨',
'𐳩' => '𐲩',
'𐳪' => '𐲪',
'𐳫' => '𐲫',
'𐳬' => '𐲬',
'𐳭' => '𐲭',
'𐳮' => '𐲮',
'𐳯' => '𐲯',
'𐳰' => '𐲰',
'𐳱' => '𐲱',
'𐳲' => '𐲲',
'𑣀' => '𑢠',
'𑣁' => '𑢡',
'𑣂' => '𑢢',
'𑣃' => '𑢣',
'𑣄' => '𑢤',
'𑣅' => '𑢥',
'𑣆' => '𑢦',
'𑣇' => '𑢧',
'𑣈' => '𑢨',
'𑣉' => '𑢩',
'𑣊' => '𑢪',
'𑣋' => '𑢫',
'𑣌' => '𑢬',
'𑣍' => '𑢭',
'𑣎' => '𑢮',
'𑣏' => '𑢯',
'𑣐' => '𑢰',
'𑣑' => '𑢱',
'𑣒' => '𑢲',
'𑣓' => '𑢳',
'𑣔' => '𑢴',
'𑣕' => '𑢵',
'𑣖' => '𑢶',
'𑣗' => '𑢷',
'𑣘' => '𑢸',
'𑣙' => '𑢹',
'𑣚' => '𑢺',
'𑣛' => '𑢻',
'𑣜' => '𑢼',
'𑣝' => '𑢽',
'𑣞' => '𑢾',
'𑣟' => '𑢿',
'𖹠' => '𖹀',
'𖹡' => '𖹁',
'𖹢' => '𖹂',
'𖹣' => '𖹃',
'𖹤' => '𖹄',
'𖹥' => '𖹅',
'𖹦' => '𖹆',
'𖹧' => '𖹇',
'𖹨' => '𖹈',
'𖹩' => '𖹉',
'𖹪' => '𖹊',
'𖹫' => '𖹋',
'𖹬' => '𖹌',
'𖹭' => '𖹍',
'𖹮' => '𖹎',
'𖹯' => '𖹏',
'𖹰' => '𖹐',
'𖹱' => '𖹑',
'𖹲' => '𖹒',
'𖹳' => '𖹓',
'𖹴' => '𖹔',
'𖹵' => '𖹕',
'𖹶' => '𖹖',
'𖹷' => '𖹗',
'𖹸' => '𖹘',
'𖹹' => '𖹙',
'𖹺' => '𖹚',
'𖹻' => '𖹛',
'𖹼' => '𖹜',
'𖹽' => '𖹝',
'𖹾' => '𖹞',
'𖹿' => '𖹟',
'𞤢' => '𞤀',
'𞤣' => '𞤁',
'𞤤' => '𞤂',
'𞤥' => '𞤃',
'𞤦' => '𞤄',
'𞤧' => '𞤅',
'𞤨' => '𞤆',
'𞤩' => '𞤇',
'𞤪' => '𞤈',
'𞤫' => '𞤉',
'𞤬' => '𞤊',
'𞤭' => '𞤋',
'𞤮' => '𞤌',
'𞤯' => '𞤍',
'𞤰' => '𞤎',
'𞤱' => '𞤏',
'𞤲' => '𞤐',
'𞤳' => '𞤑',
'𞤴' => '𞤒',
'𞤵' => '𞤓',
'𞤶' => '𞤔',
'𞤷' => '𞤕',
'𞤸' => '𞤖',
'𞤹' => '𞤗',
'𞤺' => '𞤘',
'𞤻' => '𞤙',
'𞤼' => '𞤚',
'𞤽' => '𞤛',
'𞤾' => '𞤜',
'𞤿' => '𞤝',
'𞥀' => '𞤞',
'𞥁' => '𞤟',
'𞥂' => '𞤠',
'𞥃' => '𞤡',
'ß' => 'SS',
'ff' => 'FF',
'fi' => 'FI',
'fl' => 'FL',
'ffi' => 'FFI',
'ffl' => 'FFL',
'ſt' => 'ST',
'st' => 'ST',
'և' => 'ԵՒ',
'ﬓ' => 'ՄՆ',
'ﬔ' => 'ՄԵ',
'ﬕ' => 'ՄԻ',
'ﬖ' => 'ՎՆ',
'ﬗ' => 'ՄԽ',
'ʼn' => 'ʼN',
'ΐ' => 'Ϊ́',
'ΰ' => 'Ϋ́',
'ǰ' => 'J̌',
'ẖ' => 'H̱',
'ẗ' => 'T̈',
'ẘ' => 'W̊',
'ẙ' => 'Y̊',
'ẚ' => 'Aʾ',
'ὐ' => 'Υ̓',
'ὒ' => 'Υ̓̀',
'ὔ' => 'Υ̓́',
'ὖ' => 'Υ̓͂',
'ᾶ' => 'Α͂',
'ῆ' => 'Η͂',
'ῒ' => 'Ϊ̀',
'ΐ' => 'Ϊ́',
'ῖ' => 'Ι͂',
'ῗ' => 'Ϊ͂',
'ῢ' => 'Ϋ̀',
'ΰ' => 'Ϋ́',
'ῤ' => 'Ρ̓',
'ῦ' => 'Υ͂',
'ῧ' => 'Ϋ͂',
'ῶ' => 'Ω͂',
'ᾈ' => 'ἈΙ',
'ᾉ' => 'ἉΙ',
'ᾊ' => 'ἊΙ',
'ᾋ' => 'ἋΙ',
'ᾌ' => 'ἌΙ',
'ᾍ' => 'ἍΙ',
'ᾎ' => 'ἎΙ',
'ᾏ' => 'ἏΙ',
'ᾘ' => 'ἨΙ',
'ᾙ' => 'ἩΙ',
'ᾚ' => 'ἪΙ',
'ᾛ' => 'ἫΙ',
'ᾜ' => 'ἬΙ',
'ᾝ' => 'ἭΙ',
'ᾞ' => 'ἮΙ',
'ᾟ' => 'ἯΙ',
'ᾨ' => 'ὨΙ',
'ᾩ' => 'ὩΙ',
'ᾪ' => 'ὪΙ',
'ᾫ' => 'ὫΙ',
'ᾬ' => 'ὬΙ',
'ᾭ' => 'ὭΙ',
'ᾮ' => 'ὮΙ',
'ᾯ' => 'ὯΙ',
'ᾼ' => 'ΑΙ',
'ῌ' => 'ΗΙ',
'ῼ' => 'ΩΙ',
'ᾲ' => 'ᾺΙ',
'ᾴ' => 'ΆΙ',
'ῂ' => 'ῊΙ',
'ῄ' => 'ΉΙ',
'ῲ' => 'ῺΙ',
'ῴ' => 'ΏΙ',
'ᾷ' => 'Α͂Ι',
'ῇ' => 'Η͂Ι',
'ῷ' => 'Ω͂Ι',
);
<?php
namespace Symfony\Polyfill\Mbstring;
final class Mbstring
{
public const MB_CASE_FOLD = \PHP_INT_MAX;
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", 'ι'],
];
private static $encodingList = ['ASCII', 'UTF-8'];
private static $language = 'neutral';
private static $internalEncoding = 'UTF-8';
public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null)
{
if (\is_array($fromEncoding) || ($fromEncoding !== null && false !== strpos($fromEncoding, ','))) {
$fromEncoding = self::mb_detect_encoding($s, $fromEncoding);
} else {
$fromEncoding = self::getEncoding($fromEncoding);
}
$toEncoding = self::getEncoding($toEncoding);
if ('BASE64' === $fromEncoding) {
$s = base64_decode($s);
$fromEncoding = $toEncoding;
}
if ('BASE64' === $toEncoding) {
return base64_encode($s);
}
if ('HTML-ENTITIES' === $toEncoding || 'HTML' === $toEncoding) {
if ('HTML-ENTITIES' === $fromEncoding || 'HTML' === $fromEncoding) {
$fromEncoding = 'Windows-1252';
}
if ('UTF-8' !== $fromEncoding) {
$s = \iconv($fromEncoding, 'UTF-8//IGNORE', $s);
}
return preg_replace_callback('/[\x80-\xFF]+/', [__CLASS__, 'html_encoding_callback'], $s);
}
if ('HTML-ENTITIES' === $fromEncoding) {
$s = html_entity_decode($s, \ENT_COMPAT, 'UTF-8');
$fromEncoding = 'UTF-8';
}
return \iconv($fromEncoding, $toEncoding.'//IGNORE', $s);
}
public static function mb_convert_variables($toEncoding, $fromEncoding, &...$vars)
{
$ok = true;
array_walk_recursive($vars, function (&$v) use (&$ok, $toEncoding, $fromEncoding) {
if (false === $v = self::mb_convert_encoding($v, $toEncoding, $fromEncoding)) {
$ok = false;
}
});
return $ok ? $fromEncoding : false;
}
public static function mb_decode_mimeheader($s)
{
return \iconv_mime_decode($s, 2, self::$internalEncoding);
}
public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, $linefeed = null, $indent = null)
{
trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', \E_USER_WARNING);
}
public static function mb_decode_numericentity($s, $convmap, $encoding = null)
{
if (null !== $s && !is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) {
trigger_error('mb_decode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', \E_USER_WARNING);
return null;
}
if (!\is_array($convmap) || (80000 > \PHP_VERSION_ID && !$convmap)) {
return false;
}
if (null !== $encoding && !is_scalar($encoding)) {
trigger_error('mb_decode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', \E_USER_WARNING);
return '';
}
$s = (string) $s;
if ('' === $s) {
return '';
}
$encoding = self::getEncoding($encoding);
if ('UTF-8' === $encoding) {
$encoding = null;
if (!preg_match('//u', $s)) {
$s = @\iconv('UTF-8', 'UTF-8//IGNORE', $s);
}
} else {
$s = \iconv($encoding, 'UTF-8//IGNORE', $s);
}
$cnt = floor(\count($convmap) / 4) * 4;
for ($i = 0; $i < $cnt; $i += 4) {
$convmap[$i] += $convmap[$i + 2];
$convmap[$i + 1] += $convmap[$i + 2];
}
$s = preg_replace_callback('/&#(?:0*([0-9]+)|x0*([0-9a-fA-F]+))(?!&);?/', function (array $m) use ($cnt, $convmap) {
$c = isset($m[2]) ? (int) hexdec($m[2]) : $m[1];
for ($i = 0; $i < $cnt; $i += 4) {
if ($c >= $convmap[$i] && $c <= $convmap[$i + 1]) {
return self::mb_chr($c - $convmap[$i + 2]);
}
}
return $m[0];
}, $s);
if (null === $encoding) {
return $s;
}
return \iconv('UTF-8', $encoding.'//IGNORE', $s);
}
public static function mb_encode_numericentity($s, $convmap, $encoding = null, $is_hex = false)
{
if (null !== $s && !is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) {
trigger_error('mb_encode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', \E_USER_WARNING);
return null;
}
if (!\is_array($convmap) || (80000 > \PHP_VERSION_ID && !$convmap)) {
return false;
}
if (null !== $encoding && !is_scalar($encoding)) {
trigger_error('mb_encode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', \E_USER_WARNING);
return null;
}
if (null !== $is_hex && !is_scalar($is_hex)) {
trigger_error('mb_encode_numericentity() expects parameter 4 to be boolean, '.\gettype($s).' given', \E_USER_WARNING);
return null;
}
$s = (string) $s;
if ('' === $s) {
return '';
}
$encoding = self::getEncoding($encoding);
if ('UTF-8' === $encoding) {
$encoding = null;
if (!preg_match('//u', $s)) {
$s = @\iconv('UTF-8', 'UTF-8//IGNORE', $s);
}
} else {
$s = \iconv($encoding, 'UTF-8//IGNORE', $s);
}
static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4];
$cnt = floor(\count($convmap) / 4) * 4;
$i = 0;
$len = \strlen($s);
$result = '';
while ($i < $len) {
$ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"];
$uchr = substr($s, $i, $ulen);
$i += $ulen;
$c = self::mb_ord($uchr);
for ($j = 0; $j < $cnt; $j += 4) {
if ($c >= $convmap[$j] && $c <= $convmap[$j + 1]) {
$cOffset = ($c + $convmap[$j + 2]) & $convmap[$j + 3];
$result .= $is_hex ? sprintf('&#x%X;', $cOffset) : '&#'.$cOffset.';';
continue 2;
}
}
$result .= $uchr;
}
if (null === $encoding) {
return $result;
}
return \iconv('UTF-8', $encoding.'//IGNORE', $result);
}
public static function mb_convert_case($s, $mode, $encoding = null)
{
$s = (string) $s;
if ('' === $s) {
return '';
}
$encoding = self::getEncoding($encoding);
if ('UTF-8' === $encoding) {
$encoding = null;
if (!preg_match('//u', $s)) {
$s = @\iconv('UTF-8', 'UTF-8//IGNORE', $s);
}
} else {
$s = \iconv($encoding, 'UTF-8//IGNORE', $s);
}
if (\MB_CASE_TITLE == $mode) {
static $titleRegexp = null;
if (null === $titleRegexp) {
$titleRegexp = self::getData('titleCaseRegexp');
}
$s = preg_replace_callback($titleRegexp, [__CLASS__, 'title_case'], $s);
} else {
if (\MB_CASE_UPPER == $mode) {
static $upper = null;
if (null === $upper) {
$upper = self::getData('upperCase');
}
$map = $upper;
} else {
if (self::MB_CASE_FOLD === $mode) {
$s = str_replace(self::CASE_FOLD[0], self::CASE_FOLD[1], $s);
}
static $lower = null;
if (null === $lower) {
$lower = self::getData('lowerCase');
}
$map = $lower;
}
static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4];
$i = 0;
$len = \strlen($s);
while ($i < $len) {
$ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"];
$uchr = substr($s, $i, $ulen);
$i += $ulen;
if (isset($map[$uchr])) {
$uchr = $map[$uchr];
$nlen = \strlen($uchr);
if ($nlen == $ulen) {
$nlen = $i;
do {
$s[--$nlen] = $uchr[--$ulen];
} while ($ulen);
} else {
$s = substr_replace($s, $uchr, $i - $ulen, $ulen);
$len += $nlen - $ulen;
$i += $nlen - $ulen;
}
}
}
}
if (null === $encoding) {
return $s;
}
return \iconv('UTF-8', $encoding.'//IGNORE', $s);
}
public static function mb_internal_encoding($encoding = null)
{
if (null === $encoding) {
return self::$internalEncoding;
}
$normalizedEncoding = self::getEncoding($encoding);
if ('UTF-8' === $normalizedEncoding || false !== @\iconv($normalizedEncoding, $normalizedEncoding, ' ')) {
self::$internalEncoding = $normalizedEncoding;
return true;
}
if (80000 > \PHP_VERSION_ID) {
return false;
}
throw new \ValueError(sprintf('Argument #1 ($encoding) must be a valid encoding, "%s" given', $encoding));
}
public static function mb_language($lang = null)
{
if (null === $lang) {
return self::$language;
}
switch ($normalizedLang = strtolower($lang)) {
case 'uni':
case 'neutral':
self::$language = $normalizedLang;
return true;
}
if (80000 > \PHP_VERSION_ID) {
return false;
}
throw new \ValueError(sprintf('Argument #1 ($language) must be a valid language, "%s" given', $lang));
}
public static function mb_list_encodings()
{
return ['UTF-8'];
}
public static function mb_encoding_aliases($encoding)
{
switch (strtoupper($encoding)) {
case 'UTF8':
case 'UTF-8':
return ['utf8'];
}
return false;
}
public static function mb_check_encoding($var = null, $encoding = null)
{
if (null === $encoding) {
if (null === $var) {
return false;
}
$encoding = self::$internalEncoding;
}
return self::mb_detect_encoding($var, [$encoding]) || false !== @\iconv($encoding, $encoding, $var);
}
public static function mb_detect_encoding($str, $encodingList = null, $strict = false)
{
if (null === $encodingList) {
$encodingList = self::$encodingList;
} else {
if (!\is_array($encodingList)) {
$encodingList = array_map('trim', explode(',', $encodingList));
}
$encodingList = array_map('strtoupper', $encodingList);
}
foreach ($encodingList as $enc) {
switch ($enc) {
case 'ASCII':
if (!preg_match('/[\x80-\xFF]/', $str)) {
return $enc;
}
break;
case 'UTF8':
case 'UTF-8':
if (preg_match('//u', $str)) {
return 'UTF-8';
}
break;
default:
if (0 === strncmp($enc, 'ISO-8859-', 9)) {
return $enc;
}
}
}
return false;
}
public static function mb_detect_order($encodingList = null)
{
if (null === $encodingList) {
return self::$encodingList;
}
if (!\is_array($encodingList)) {
$encodingList = array_map('trim', explode(',', $encodingList));
}
$encodingList = array_map('strtoupper', $encodingList);
foreach ($encodingList as $enc) {
switch ($enc) {
default:
if (strncmp($enc, 'ISO-8859-', 9)) {
return false;
}
case 'ASCII':
case 'UTF8':
case 'UTF-8':
}
}
self::$encodingList = $encodingList;
return true;
}
public static function mb_strlen($s, $encoding = null)
{
$encoding = self::getEncoding($encoding);
if ('CP850' === $encoding || 'ASCII' === $encoding) {
return \strlen($s);
}
return @\iconv_strlen($s, $encoding);
}
public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null)
{
$encoding = self::getEncoding($encoding);
if ('CP850' === $encoding || 'ASCII' === $encoding) {
return strpos($haystack, $needle, $offset);
}
$needle = (string) $needle;
if ('' === $needle) {
if (80000 > \PHP_VERSION_ID) {
trigger_error(__METHOD__.': Empty delimiter', \E_USER_WARNING);
return false;
}
return 0;
}
return \iconv_strpos($haystack, $needle, $offset, $encoding);
}
public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null)
{
$encoding = self::getEncoding($encoding);
if ('CP850' === $encoding || 'ASCII' === $encoding) {
return strrpos($haystack, $needle, $offset);
}
if ($offset != (int) $offset) {
$offset = 0;
} elseif ($offset = (int) $offset) {
if ($offset < 0) {
if (0 > $offset += self::mb_strlen($needle)) {
$haystack = self::mb_substr($haystack, 0, $offset, $encoding);
}
$offset = 0;
} else {
$haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding);
}
}
$pos = '' !== $needle || 80000 > \PHP_VERSION_ID
? \iconv_strrpos($haystack, $needle, $encoding)
: self::mb_strlen($haystack, $encoding);
return false !== $pos ? $offset + $pos : false;
}
public static function mb_str_split($string, $split_length = 1, $encoding = null)
{
if (null !== $string && !is_scalar($string) && !(\is_object($string) && method_exists($string, '__toString'))) {
trigger_error('mb_str_split() expects parameter 1 to be string, '.\gettype($string).' given', \E_USER_WARNING);
return null;
}
if (1 > $split_length = (int) $split_length) {
if (80000 > \PHP_VERSION_ID) {
trigger_error('The length of each segment must be greater than zero', \E_USER_WARNING);
return false;
}
throw new \ValueError('Argument #2 ($length) must be greater than 0');
}
if (null === $encoding) {
$encoding = mb_internal_encoding();
}
if ('UTF-8' === $encoding = self::getEncoding($encoding)) {
$rx = '/(';
while (65535 < $split_length) {
$rx .= '.{65535}';
$split_length -= 65535;
}
$rx .= '.{'.$split_length.'})/us';
return preg_split($rx, $string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY);
}
$result = [];
$length = mb_strlen($string, $encoding);
for ($i = 0; $i < $length; $i += $split_length) {
$result[] = mb_substr($string, $i, $split_length, $encoding);
}
return $result;
}
public static function mb_strtolower($s, $encoding = null)
{
return self::mb_convert_case($s, \MB_CASE_LOWER, $encoding);
}
public static function mb_strtoupper($s, $encoding = null)
{
return self::mb_convert_case($s, \MB_CASE_UPPER, $encoding);
}
public static function mb_substitute_character($c = null)
{
if (null === $c) {
return 'none';
}
if (0 === strcasecmp($c, 'none')) {
return true;
}
if (80000 > \PHP_VERSION_ID) {
return false;
}
if (\is_int($c) || 'long' === $c || 'entity' === $c) {
return false;
}
throw new \ValueError('Argument #1 ($substitute_character) must be "none", "long", "entity" or a valid codepoint');
}
public static function mb_substr($s, $start, $length = null, $encoding = null)
{
$encoding = self::getEncoding($encoding);
if ('CP850' === $encoding || 'ASCII' === $encoding) {
return (string) substr($s, $start, null === $length ? 2147483647 : $length);
}
if ($start < 0) {
$start = \iconv_strlen($s, $encoding) + $start;
if ($start < 0) {
$start = 0;
}
}
if (null === $length) {
$length = 2147483647;
} elseif ($length < 0) {
$length = \iconv_strlen($s, $encoding) + $length - $start;
if ($length < 0) {
return '';
}
}
return (string) \iconv_substr($s, $start, $length, $encoding);
}
public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null)
{
$haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding);
$needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding);
return self::mb_strpos($haystack, $needle, $offset, $encoding);
}
public static function mb_stristr($haystack, $needle, $part = false, $encoding = null)
{
$pos = self::mb_stripos($haystack, $needle, 0, $encoding);
return self::getSubpart($pos, $part, $haystack, $encoding);
}
public static function mb_strrchr($haystack, $needle, $part = false, $encoding = null)
{
$encoding = self::getEncoding($encoding);
if ('CP850' === $encoding || 'ASCII' === $encoding) {
$pos = strrpos($haystack, $needle);
} else {
$needle = self::mb_substr($needle, 0, 1, $encoding);
$pos = \iconv_strrpos($haystack, $needle, $encoding);
}
return self::getSubpart($pos, $part, $haystack, $encoding);
}
public static function mb_strrichr($haystack, $needle, $part = false, $encoding = null)
{
$needle = self::mb_substr($needle, 0, 1, $encoding);
$pos = self::mb_strripos($haystack, $needle, $encoding);
return self::getSubpart($pos, $part, $haystack, $encoding);
}
public static function mb_strripos($haystack, $needle, $offset = 0, $encoding = null)
{
$haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding);
$needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding);
return self::mb_strrpos($haystack, $needle, $offset, $encoding);
}
public static function mb_strstr($haystack, $needle, $part = false, $encoding = null)
{
$pos = strpos($haystack, $needle);
if (false === $pos) {
return false;
}
if ($part) {
return substr($haystack, 0, $pos);
}
return substr($haystack, $pos);
}
public static function mb_get_info($type = 'all')
{
$info = [
'internal_encoding' => self::$internalEncoding,
'http_output' => 'pass',
'http_output_conv_mimetypes' => '^(text/|application/xhtml\+xml)',
'func_overload' => 0,
'func_overload_list' => 'no overload',
'mail_charset' => 'UTF-8',
'mail_header_encoding' => 'BASE64',
'mail_body_encoding' => 'BASE64',
'illegal_chars' => 0,
'encoding_translation' => 'Off',
'language' => self::$language,
'detect_order' => self::$encodingList,
'substitute_character' => 'none',
'strict_detection' => 'Off',
];
if ('all' === $type) {
return $info;
}
if (isset($info[$type])) {
return $info[$type];
}
return false;
}
public static function mb_http_input($type = '')
{
return false;
}
public static function mb_http_output($encoding = null)
{
return null !== $encoding ? 'pass' === $encoding : 'pass';
}
public static function mb_strwidth($s, $encoding = null)
{
$encoding = self::getEncoding($encoding);
if ('UTF-8' !== $encoding) {
$s = \iconv($encoding, 'UTF-8//IGNORE', $s);
}
$s = preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide);
return ($wide << 1) + \iconv_strlen($s, 'UTF-8');
}
public static function mb_substr_count($haystack, $needle, $encoding = null)
{
return substr_count($haystack, $needle);
}
public static function mb_output_handler($contents, $status)
{
return $contents;
}
public static function mb_chr($code, $encoding = null)
{
if (0x80 > $code %= 0x200000) {
$s = \chr($code);
} elseif (0x800 > $code) {
$s = \chr(0xC0 | $code >> 6).\chr(0x80 | $code & 0x3F);
} elseif (0x10000 > $code) {
$s = \chr(0xE0 | $code >> 12).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F);
} else {
$s = \chr(0xF0 | $code >> 18).\chr(0x80 | $code >> 12 & 0x3F).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F);
}
if ('UTF-8' !== $encoding = self::getEncoding($encoding)) {
$s = mb_convert_encoding($s, $encoding, 'UTF-8');
}
return $s;
}
public static function mb_ord($s, $encoding = null)
{
if ('UTF-8' !== $encoding = self::getEncoding($encoding)) {
$s = mb_convert_encoding($s, 'UTF-8', $encoding);
}
if (1 === \strlen($s)) {
return \ord($s);
}
$code = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0;
if (0xF0 <= $code) {
return (($code - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80;
}
if (0xE0 <= $code) {
return (($code - 0xE0) << 12) + (($s[2] - 0x80) << 6) + $s[3] - 0x80;
}
if (0xC0 <= $code) {
return (($code - 0xC0) << 6) + $s[2] - 0x80;
}
return $code;
}
private static function getSubpart($pos, $part, $haystack, $encoding)
{
if (false === $pos) {
return false;
}
if ($part) {
return self::mb_substr($haystack, 0, $pos, $encoding);
}
return self::mb_substr($haystack, $pos, null, $encoding);
}
private static function html_encoding_callback(array $m)
{
$i = 1;
$entities = '';
$m = unpack('C*', htmlentities($m[0], \ENT_COMPAT, 'UTF-8'));
while (isset($m[$i])) {
if (0x80 > $m[$i]) {
$entities .= \chr($m[$i++]);
continue;
}
if (0xF0 <= $m[$i]) {
$c = (($m[$i++] - 0xF0) << 18) + (($m[$i++] - 0x80) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80;
} elseif (0xE0 <= $m[$i]) {
$c = (($m[$i++] - 0xE0) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80;
} else {
$c = (($m[$i++] - 0xC0) << 6) + $m[$i++] - 0x80;
}
$entities .= '&#'.$c.';';
}
return $entities;
}
private static function title_case(array $s)
{
return self::mb_convert_case($s[1], \MB_CASE_UPPER, 'UTF-8').self::mb_convert_case($s[2], \MB_CASE_LOWER, 'UTF-8');
}
private static function getData($file)
{
if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) {
return require $file;
}
return false;
}
private static function getEncoding($encoding)
{
if (null === $encoding) {
return self::$internalEncoding;
}
if ('UTF-8' === $encoding) {
return 'UTF-8';
}
$encoding = strtoupper($encoding);
if ('8BIT' === $encoding || 'BINARY' === $encoding) {
return 'CP850';
}
if ('UTF8' === $encoding) {
return 'UTF-8';
}
return $encoding;
}
}
<?php
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 (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);
}
<?php
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); }
}
<?php
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);
final class Grapheme
{
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;
}
}
$caseInsensitive = $mode & 1;
$reverse = $mode & 2;
if ($caseInsensitive) {
$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;
}
}
<?php
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); }
}
<?php declare(strict_types=1);
namespace SebastianBergmann\Diff;
final class Diff
{
private $from;
private $to;
private $chunks;
public function __construct(string $from, string $to, array $chunks = [])
{
$this->from = $from;
$this->to = $to;
$this->chunks = $chunks;
}
public function getFrom(): string
{
return $this->from;
}
public function getTo(): string
{
return $this->to;
}
public function getChunks(): array
{
return $this->chunks;
}
public function setChunks(array $chunks): void
{
$this->chunks = $chunks;
}
}
<?php declare(strict_types=1);
namespace SebastianBergmann\Diff;
use function array_pop;
use function count;
use function max;
use function preg_match;
use function preg_split;
final class Parser
{
public function parse(string $string): array
{
$lines = preg_split('(\r\n|\r|\n)', $string);
if (!empty($lines) && $lines[count($lines) - 1] === '') {
array_pop($lines);
}
$lineCount = count($lines);
$diffs = [];
$diff = null;
$collected = [];
for ($i = 0; $i < $lineCount; ++$i) {
if (preg_match('#^---\h+"?(?P<file>[^\\v\\t"]+)#', $lines[$i], $fromMatch) &&
preg_match('#^\\+\\+\\+\\h+"?(?P<file>[^\\v\\t"]+)#', $lines[$i + 1], $toMatch)) {
if ($diff !== null) {
$this->parseFileDiff($diff, $collected);
$diffs[] = $diff;
$collected = [];
}
$diff = new Diff($fromMatch['file'], $toMatch['file']);
++$i;
} else {
if (preg_match('/^(?:diff --git |index [\da-f\.]+|[+-]{3} [ab])/', $lines[$i])) {
continue;
}
$collected[] = $lines[$i];
}
}
if ($diff !== null && count($collected)) {
$this->parseFileDiff($diff, $collected);
$diffs[] = $diff;
}
return $diffs;
}
private function parseFileDiff(Diff $diff, array $lines): void
{
$chunks = [];
$chunk = null;
$diffLines = [];
foreach ($lines as $line) {
if (preg_match('/^@@\s+-(?P<start>\d+)(?:,\s*(?P<startrange>\d+))?\s+\+(?P<end>\d+)(?:,\s*(?P<endrange>\d+))?\s+@@/', $line, $match)) {
$chunk = new Chunk(
(int) $match['start'],
isset($match['startrange']) ? max(1, (int) $match['startrange']) : 1,
(int) $match['end'],
isset($match['endrange']) ? max(1, (int) $match['endrange']) : 1
);
$chunks[] = $chunk;
$diffLines = [];
continue;
}
if (preg_match('/^(?P<type>[+ -])?(?P<line>.*)/', $line, $match)) {
$type = Line::UNCHANGED;
if ($match['type'] === '+') {
$type = Line::ADDED;
} elseif ($match['type'] === '-') {
$type = Line::REMOVED;
}
$diffLines[] = new Line($type, $match['line']);
if (null !== $chunk) {
$chunk->setLines($diffLines);
}
}
}
$diff->setChunks($chunks);
}
}
<?php declare(strict_types=1);
namespace SebastianBergmann\Diff;
use function array_reverse;
use function count;
use function max;
use SplFixedArray;
final class TimeEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator
{
public function calculate(array $from, array $to): array
{
$common = [];
$fromLength = count($from);
$toLength = count($to);
$width = $fromLength + 1;
$matrix = new SplFixedArray($width * ($toLength + 1));
for ($i = 0; $i <= $fromLength; ++$i) {
$matrix[$i] = 0;
}
for ($j = 0; $j <= $toLength; ++$j) {
$matrix[$j * $width] = 0;
}
for ($i = 1; $i <= $fromLength; ++$i) {
for ($j = 1; $j <= $toLength; ++$j) {
$o = ($j * $width) + $i;
$matrix[$o] = max(
$matrix[$o - 1],
$matrix[$o - $width],
$from[$i - 1] === $to[$j - 1] ? $matrix[$o - $width - 1] + 1 : 0
);
}
}
$i = $fromLength;
$j = $toLength;
while ($i > 0 && $j > 0) {
if ($from[$i - 1] === $to[$j - 1]) {
$common[] = $from[$i - 1];
--$i;
--$j;
} else {
$o = ($j * $width) + $i;
if ($matrix[$o - $width] > $matrix[$o - 1]) {
--$j;
} else {
--$i;
}
}
}
return array_reverse($common);
}
}
<?php declare(strict_types=1);
namespace SebastianBergmann\Diff\Output;
use function fclose;
use function fopen;
use function fwrite;
use function stream_get_contents;
use function substr;
use SebastianBergmann\Diff\Differ;
final class DiffOnlyOutputBuilder implements DiffOutputBuilderInterface
{
private $header;
public function __construct(string $header = "--- Original\n+++ New\n")
{
$this->header = $header;
}
public function getDiff(array $diff): string
{
$buffer = fopen('php://memory', 'r+b');
if ('' !== $this->header) {
fwrite($buffer, $this->header);
if ("\n" !== substr($this->header, -1, 1)) {
fwrite($buffer, "\n");
}
}
foreach ($diff as $diffEntry) {
if ($diffEntry[1] === Differ::ADDED) {
fwrite($buffer, '+' . $diffEntry[0]);
} elseif ($diffEntry[1] === Differ::REMOVED) {
fwrite($buffer, '-' . $diffEntry[0]);
} elseif ($diffEntry[1] === Differ::DIFF_LINE_END_WARNING) {
fwrite($buffer, ' ' . $diffEntry[0]);
continue;
} else {
continue;
}
$lc = substr($diffEntry[0], -1);
if ($lc !== "\n" && $lc !== "\r") {
fwrite($buffer, "\n");
}
}
$diff = stream_get_contents($buffer, -1, 0);
fclose($buffer);
return $diff;
}
}
<?php declare(strict_types=1);
namespace SebastianBergmann\Diff\Output;
interface DiffOutputBuilderInterface
{
public function getDiff(array $diff): string;
}
<?php declare(strict_types=1);
namespace SebastianBergmann\Diff\Output;
use function array_splice;
use function count;
use function fclose;
use function fopen;
use function fwrite;
use function max;
use function min;
use function stream_get_contents;
use function strlen;
use function substr;
use SebastianBergmann\Diff\Differ;
final class UnifiedDiffOutputBuilder extends AbstractChunkOutputBuilder
{
private $collapseRanges = true;
private $commonLineThreshold = 6;
private $contextLines = 3;
private $header;
private $addLineNumbers;
public function __construct(string $header = "--- Original\n+++ New\n", bool $addLineNumbers = false)
{
$this->header = $header;
$this->addLineNumbers = $addLineNumbers;
}
public function getDiff(array $diff): string
{
$buffer = fopen('php://memory', 'r+b');
if ('' !== $this->header) {
fwrite($buffer, $this->header);
if ("\n" !== substr($this->header, -1, 1)) {
fwrite($buffer, "\n");
}
}
if (0 !== count($diff)) {
$this->writeDiffHunks($buffer, $diff);
}
$diff = stream_get_contents($buffer, -1, 0);
fclose($buffer);
$last = substr($diff, -1);
return 0 !== strlen($diff) && "\n" !== $last && "\r" !== $last
? $diff . "\n"
: $diff;
}
private function writeDiffHunks($output, array $diff): void
{
$upperLimit = count($diff);
if (0 === $diff[$upperLimit - 1][1]) {
$lc = substr($diff[$upperLimit - 1][0], -1);
if ("\n" !== $lc) {
array_splice($diff, $upperLimit, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]);
}
} else {
$toFind = [1 => true, 2 => true];
for ($i = $upperLimit - 1; $i >= 0; --$i) {
if (isset($toFind[$diff[$i][1]])) {
unset($toFind[$diff[$i][1]]);
$lc = substr($diff[$i][0], -1);
if ("\n" !== $lc) {
array_splice($diff, $i + 1, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]);
}
if (!count($toFind)) {
break;
}
}
}
}
$cutOff = max($this->commonLineThreshold, $this->contextLines);
$hunkCapture = false;
$sameCount = $toRange = $fromRange = 0;
$toStart = $fromStart = 1;
$i = 0;
foreach ($diff as $i => $entry) {
if (0 === $entry[1]) {
if (false === $hunkCapture) {
++$fromStart;
++$toStart;
continue;
}
++$sameCount;
++$toRange;
++$fromRange;
if ($sameCount === $cutOff) {
$contextStartOffset = ($hunkCapture - $this->contextLines) < 0
? $hunkCapture
: $this->contextLines;
$this->writeHunk(
$diff,
$hunkCapture - $contextStartOffset,
$i - $cutOff + $this->contextLines + 1,
$fromStart - $contextStartOffset,
$fromRange - $cutOff + $contextStartOffset + $this->contextLines,
$toStart - $contextStartOffset,
$toRange - $cutOff + $contextStartOffset + $this->contextLines,
$output
);
$fromStart += $fromRange;
$toStart += $toRange;
$hunkCapture = false;
$sameCount = $toRange = $fromRange = 0;
}
continue;
}
$sameCount = 0;
if ($entry[1] === Differ::NO_LINE_END_EOF_WARNING) {
continue;
}
if (false === $hunkCapture) {
$hunkCapture = $i;
}
if (Differ::ADDED === $entry[1]) {
++$toRange;
}
if (Differ::REMOVED === $entry[1]) {
++$fromRange;
}
}
if (false === $hunkCapture) {
return;
}
$contextStartOffset = $hunkCapture - $this->contextLines < 0
? $hunkCapture
: $this->contextLines;
$contextEndOffset = min($sameCount, $this->contextLines);
$fromRange -= $sameCount;
$toRange -= $sameCount;
$this->writeHunk(
$diff,
$hunkCapture - $contextStartOffset,
$i - $sameCount + $contextEndOffset + 1,
$fromStart - $contextStartOffset,
$fromRange + $contextStartOffset + $contextEndOffset,
$toStart - $contextStartOffset,
$toRange + $contextStartOffset + $contextEndOffset,
$output
);
}
private function writeHunk(
array $diff,
int $diffStartIndex,
int $diffEndIndex,
int $fromStart,
int $fromRange,
int $toStart,
int $toRange,
$output
): void {
if ($this->addLineNumbers) {
fwrite($output, '@@ -' . $fromStart);
if (!$this->collapseRanges || 1 !== $fromRange) {
fwrite($output, ',' . $fromRange);
}
fwrite($output, ' +' . $toStart);
if (!$this->collapseRanges || 1 !== $toRange) {
fwrite($output, ',' . $toRange);
}
fwrite($output, " @@\n");
} else {
fwrite($output, "@@ @@\n");
}
for ($i = $diffStartIndex; $i < $diffEndIndex; ++$i) {
if ($diff[$i][1] === Differ::ADDED) {
fwrite($output, '+' . $diff[$i][0]);
} elseif ($diff[$i][1] === Differ::REMOVED) {
fwrite($output, '-' . $diff[$i][0]);
} elseif ($diff[$i][1] === Differ::OLD) {
fwrite($output, ' ' . $diff[$i][0]);
} elseif ($diff[$i][1] === Differ::NO_LINE_END_EOF_WARNING) {
fwrite($output, "\n");
} else {
fwrite($output, ' ' . $diff[$i][0]);
}
}
}
}
<?php declare(strict_types=1);
namespace SebastianBergmann\Diff\Output;
use function array_merge;
use function array_splice;
use function count;
use function fclose;
use function fopen;
use function fwrite;
use function is_bool;
use function is_int;
use function is_string;
use function max;
use function min;
use function sprintf;
use function stream_get_contents;
use function substr;
use SebastianBergmann\Diff\ConfigurationException;
use SebastianBergmann\Diff\Differ;
final class StrictUnifiedDiffOutputBuilder implements DiffOutputBuilderInterface
{
private static $default = [
'collapseRanges' => true,
'commonLineThreshold' => 6,
'contextLines' => 3,
'fromFile' => null,
'fromFileDate' => null,
'toFile' => null,
'toFileDate' => null,
];
private $changed;
private $collapseRanges;
private $commonLineThreshold;
private $header;
private $contextLines;
public function __construct(array $options = [])
{
$options = array_merge(self::$default, $options);
if (!is_bool($options['collapseRanges'])) {
throw new ConfigurationException('collapseRanges', 'a bool', $options['collapseRanges']);
}
if (!is_int($options['contextLines']) || $options['contextLines'] < 0) {
throw new ConfigurationException('contextLines', 'an int >= 0', $options['contextLines']);
}
if (!is_int($options['commonLineThreshold']) || $options['commonLineThreshold'] <= 0) {
throw new ConfigurationException('commonLineThreshold', 'an int > 0', $options['commonLineThreshold']);
}
$this->assertString($options, 'fromFile');
$this->assertString($options, 'toFile');
$this->assertStringOrNull($options, 'fromFileDate');
$this->assertStringOrNull($options, 'toFileDate');
$this->header = sprintf(
"--- %s%s\n+++ %s%s\n",
$options['fromFile'],
null === $options['fromFileDate'] ? '' : "\t" . $options['fromFileDate'],
$options['toFile'],
null === $options['toFileDate'] ? '' : "\t" . $options['toFileDate']
);
$this->collapseRanges = $options['collapseRanges'];
$this->commonLineThreshold = $options['commonLineThreshold'];
$this->contextLines = $options['contextLines'];
}
public function getDiff(array $diff): string
{
if (0 === count($diff)) {
return '';
}
$this->changed = false;
$buffer = fopen('php://memory', 'r+b');
fwrite($buffer, $this->header);
$this->writeDiffHunks($buffer, $diff);
if (!$this->changed) {
fclose($buffer);
return '';
}
$diff = stream_get_contents($buffer, -1, 0);
fclose($buffer);
$last = substr($diff, -1);
return "\n" !== $last && "\r" !== $last
? $diff . "\n"
: $diff;
}
private function writeDiffHunks($output, array $diff): void
{
$upperLimit = count($diff);
if (0 === $diff[$upperLimit - 1][1]) {
$lc = substr($diff[$upperLimit - 1][0], -1);
if ("\n" !== $lc) {
array_splice($diff, $upperLimit, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]);
}
} else {
$toFind = [1 => true, 2 => true];
for ($i = $upperLimit - 1; $i >= 0; --$i) {
if (isset($toFind[$diff[$i][1]])) {
unset($toFind[$diff[$i][1]]);
$lc = substr($diff[$i][0], -1);
if ("\n" !== $lc) {
array_splice($diff, $i + 1, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]);
}
if (!count($toFind)) {
break;
}
}
}
}
$cutOff = max($this->commonLineThreshold, $this->contextLines);
$hunkCapture = false;
$sameCount = $toRange = $fromRange = 0;
$toStart = $fromStart = 1;
$i = 0;
foreach ($diff as $i => $entry) {
if (0 === $entry[1]) {
if (false === $hunkCapture) {
++$fromStart;
++$toStart;
continue;
}
++$sameCount;
++$toRange;
++$fromRange;
if ($sameCount === $cutOff) {
$contextStartOffset = ($hunkCapture - $this->contextLines) < 0
? $hunkCapture
: $this->contextLines;
$this->writeHunk(
$diff,
$hunkCapture - $contextStartOffset,
$i - $cutOff + $this->contextLines + 1,
$fromStart - $contextStartOffset,
$fromRange - $cutOff + $contextStartOffset + $this->contextLines,
$toStart - $contextStartOffset,
$toRange - $cutOff + $contextStartOffset + $this->contextLines,
$output
);
$fromStart += $fromRange;
$toStart += $toRange;
$hunkCapture = false;
$sameCount = $toRange = $fromRange = 0;
}
continue;
}
$sameCount = 0;
if ($entry[1] === Differ::NO_LINE_END_EOF_WARNING) {
continue;
}
$this->changed = true;
if (false === $hunkCapture) {
$hunkCapture = $i;
}
if (Differ::ADDED === $entry[1]) {
++$toRange;
}
if (Differ::REMOVED === $entry[1]) {
++$fromRange;
}
}
if (false === $hunkCapture) {
return;
}
$contextStartOffset = $hunkCapture - $this->contextLines < 0
? $hunkCapture
: $this->contextLines;
$contextEndOffset = min($sameCount, $this->contextLines);
$fromRange -= $sameCount;
$toRange -= $sameCount;
$this->writeHunk(
$diff,
$hunkCapture - $contextStartOffset,
$i - $sameCount + $contextEndOffset + 1,
$fromStart - $contextStartOffset,
$fromRange + $contextStartOffset + $contextEndOffset,
$toStart - $contextStartOffset,
$toRange + $contextStartOffset + $contextEndOffset,
$output
);
}
private function writeHunk(
array $diff,
int $diffStartIndex,
int $diffEndIndex,
int $fromStart,
int $fromRange,
int $toStart,
int $toRange,
$output
): void {
fwrite($output, '@@ -' . $fromStart);
if (!$this->collapseRanges || 1 !== $fromRange) {
fwrite($output, ',' . $fromRange);
}
fwrite($output, ' +' . $toStart);
if (!$this->collapseRanges || 1 !== $toRange) {
fwrite($output, ',' . $toRange);
}
fwrite($output, " @@\n");
for ($i = $diffStartIndex; $i < $diffEndIndex; ++$i) {
if ($diff[$i][1] === Differ::ADDED) {
$this->changed = true;
fwrite($output, '+' . $diff[$i][0]);
} elseif ($diff[$i][1] === Differ::REMOVED) {
$this->changed = true;
fwrite($output, '-' . $diff[$i][0]);
} elseif ($diff[$i][1] === Differ::OLD) {
fwrite($output, ' ' . $diff[$i][0]);
} elseif ($diff[$i][1] === Differ::NO_LINE_END_EOF_WARNING) {
$this->changed = true;
fwrite($output, $diff[$i][0]);
}
}
}
private function assertString(array $options, string $option): void
{
if (!is_string($options[$option])) {
throw new ConfigurationException($option, 'a string', $options[$option]);
}
}
private function assertStringOrNull(array $options, string $option): void
{
if (null !== $options[$option] && !is_string($options[$option])) {
throw new ConfigurationException($option, 'a string or <null>', $options[$option]);
}
}
}
<?php declare(strict_types=1);
namespace SebastianBergmann\Diff\Output;
use function count;
abstract class AbstractChunkOutputBuilder implements DiffOutputBuilderInterface
{
protected function getCommonChunks(array $diff, int $lineThreshold = 5): array
{
$diffSize = count($diff);
$capturing = false;
$chunkStart = 0;
$chunkSize = 0;
$commonChunks = [];
for ($i = 0; $i < $diffSize; ++$i) {
if ($diff[$i][1] === 0 ) {
if ($capturing === false) {
$capturing = true;
$chunkStart = $i;
$chunkSize = 0;
} else {
++$chunkSize;
}
} elseif ($capturing !== false) {
if ($chunkSize >= $lineThreshold) {
$commonChunks[$chunkStart] = $chunkStart + $chunkSize;
}
$capturing = false;
}
}
if ($capturing !== false && $chunkSize >= $lineThreshold) {
$commonChunks[$chunkStart] = $chunkStart + $chunkSize;
}
return $commonChunks;
}
}
<?php declare(strict_types=1);
namespace SebastianBergmann\Diff;
interface LongestCommonSubsequenceCalculator
{
public function calculate(array $from, array $to): array;
}
<?php declare(strict_types=1);
namespace SebastianBergmann\Diff;
final class Chunk
{
private $start;
private $startRange;
private $end;
private $endRange;
private $lines;
public function __construct(int $start = 0, int $startRange = 1, int $end = 0, int $endRange = 1, array $lines = [])
{
$this->start = $start;
$this->startRange = $startRange;
$this->end = $end;
$this->endRange = $endRange;
$this->lines = $lines;
}
public function getStart(): int
{
return $this->start;
}
public function getStartRange(): int
{
return $this->startRange;
}
public function getEnd(): int
{
return $this->end;
}
public function getEndRange(): int
{
return $this->endRange;
}
public function getLines(): array
{
return $this->lines;
}
public function setLines(array $lines): void
{
foreach ($lines as $line) {
if (!$line instanceof Line) {
throw new InvalidArgumentException;
}
}
$this->lines = $lines;
}
}
<?php declare(strict_types=1);
namespace SebastianBergmann\Diff;
use const PHP_INT_SIZE;
use const PREG_SPLIT_DELIM_CAPTURE;
use const PREG_SPLIT_NO_EMPTY;
use function array_shift;
use function array_unshift;
use function array_values;
use function count;
use function current;
use function end;
use function get_class;
use function gettype;
use function is_array;
use function is_object;
use function is_string;
use function key;
use function min;
use function preg_split;
use function prev;
use function reset;
use function sprintf;
use function substr;
use SebastianBergmann\Diff\Output\DiffOutputBuilderInterface;
use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder;
final class Differ
{
public const OLD = 0;
public const ADDED = 1;
public const REMOVED = 2;
public const DIFF_LINE_END_WARNING = 3;
public const NO_LINE_END_EOF_WARNING = 4;
private $outputBuilder;
public function __construct($outputBuilder = null)
{
if ($outputBuilder instanceof DiffOutputBuilderInterface) {
$this->outputBuilder = $outputBuilder;
} elseif (null === $outputBuilder) {
$this->outputBuilder = new UnifiedDiffOutputBuilder;
} elseif (is_string($outputBuilder)) {
$this->outputBuilder = new UnifiedDiffOutputBuilder($outputBuilder);
} else {
throw new InvalidArgumentException(
sprintf(
'Expected builder to be an instance of DiffOutputBuilderInterface, <null> or a string, got %s.',
is_object($outputBuilder) ? 'instance of "' . get_class($outputBuilder) . '"' : gettype($outputBuilder) . ' "' . $outputBuilder . '"'
)
);
}
}
public function diff($from, $to, LongestCommonSubsequenceCalculator $lcs = null): string
{
$diff = $this->diffToArray(
$this->normalizeDiffInput($from),
$this->normalizeDiffInput($to),
$lcs
);
return $this->outputBuilder->getDiff($diff);
}
public function diffToArray($from, $to, LongestCommonSubsequenceCalculator $lcs = null): array
{
if (is_string($from)) {
$from = $this->splitStringByLines($from);
} elseif (!is_array($from)) {
throw new InvalidArgumentException('"from" must be an array or string.');
}
if (is_string($to)) {
$to = $this->splitStringByLines($to);
} elseif (!is_array($to)) {
throw new InvalidArgumentException('"to" must be an array or string.');
}
[$from, $to, $start, $end] = self::getArrayDiffParted($from, $to);
if ($lcs === null) {
$lcs = $this->selectLcsImplementation($from, $to);
}
$common = $lcs->calculate(array_values($from), array_values($to));
$diff = [];
foreach ($start as $token) {
$diff[] = [$token, self::OLD];
}
reset($from);
reset($to);
foreach ($common as $token) {
while (($fromToken = reset($from)) !== $token) {
$diff[] = [array_shift($from), self::REMOVED];
}
while (($toToken = reset($to)) !== $token) {
$diff[] = [array_shift($to), self::ADDED];
}
$diff[] = [$token, self::OLD];
array_shift($from);
array_shift($to);
}
while (($token = array_shift($from)) !== null) {
$diff[] = [$token, self::REMOVED];
}
while (($token = array_shift($to)) !== null) {
$diff[] = [$token, self::ADDED];
}
foreach ($end as $token) {
$diff[] = [$token, self::OLD];
}
if ($this->detectUnmatchedLineEndings($diff)) {
array_unshift($diff, ["#Warning: Strings contain different line endings!\n", self::DIFF_LINE_END_WARNING]);
}
return $diff;
}
private function normalizeDiffInput($input)
{
if (!is_array($input) && !is_string($input)) {
return (string) $input;
}
return $input;
}
private function splitStringByLines(string $input): array
{
return preg_split('/(.*\R)/', $input, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
}
private function selectLcsImplementation(array $from, array $to): LongestCommonSubsequenceCalculator
{
$memoryLimit = 100 * 1024 * 1024;
if ($this->calculateEstimatedFootprint($from, $to) > $memoryLimit) {
return new MemoryEfficientLongestCommonSubsequenceCalculator;
}
return new TimeEfficientLongestCommonSubsequenceCalculator;
}
private function calculateEstimatedFootprint(array $from, array $to)
{
$itemSize = PHP_INT_SIZE === 4 ? 76 : 144;
return $itemSize * min(count($from), count($to)) ** 2;
}
private function detectUnmatchedLineEndings(array $diff): bool
{
$newLineBreaks = ['' => true];
$oldLineBreaks = ['' => true];
foreach ($diff as $entry) {
if (self::OLD === $entry[1]) {
$ln = $this->getLinebreak($entry[0]);
$oldLineBreaks[$ln] = true;
$newLineBreaks[$ln] = true;
} elseif (self::ADDED === $entry[1]) {
$newLineBreaks[$this->getLinebreak($entry[0])] = true;
} elseif (self::REMOVED === $entry[1]) {
$oldLineBreaks[$this->getLinebreak($entry[0])] = true;
}
}
if (['' => true] === $newLineBreaks || ['' => true] === $oldLineBreaks) {
return false;
}
foreach ($newLineBreaks as $break => $set) {
if (!isset($oldLineBreaks[$break])) {
return true;
}
}
foreach ($oldLineBreaks as $break => $set) {
if (!isset($newLineBreaks[$break])) {
return true;
}
}
return false;
}
private function getLinebreak($line): string
{
if (!is_string($line)) {
return '';
}
$lc = substr($line, -1);
if ("\r" === $lc) {
return "\r";
}
if ("\n" !== $lc) {
return '';
}
if ("\r\n" === substr($line, -2)) {
return "\r\n";
}
return "\n";
}
private static function getArrayDiffParted(array &$from, array &$to): array
{
$start = [];
$end = [];
reset($to);
foreach ($from as $k => $v) {
$toK = key($to);
if ($toK === $k && $v === $to[$k]) {
$start[$k] = $v;
unset($from[$k], $to[$k]);
} else {
break;
}
}
end($from);
end($to);
do {
$fromK = key($from);
$toK = key($to);
if (null === $fromK || null === $toK || current($from) !== current($to)) {
break;
}
prev($from);
prev($to);
$end = [$fromK => $from[$fromK]] + $end;
unset($from[$fromK], $to[$toK]);
} while (true);
return [$from, $to, $start, $end];
}
}
<?php declare(strict_types=1);
namespace SebastianBergmann\Diff;
final class Line
{
public const ADDED = 1;
public const REMOVED = 2;
public const UNCHANGED = 3;
private $type;
private $content;
public function __construct(int $type = self::UNCHANGED, string $content = '')
{
$this->type = $type;
$this->content = $content;
}
public function getContent(): string
{
return $this->content;
}
public function getType(): int
{
return $this->type;
}
}
<?php declare(strict_types=1);
namespace SebastianBergmann\Diff;
use function get_class;
use function gettype;
use function is_object;
use function sprintf;
use Exception;
final class ConfigurationException extends InvalidArgumentException
{
public function __construct(
string $option,
string $expected,
$value,
int $code = 0,
Exception $previous = null
) {
parent::__construct(
sprintf(
'Option "%s" must be %s, got "%s".',
$option,
$expected,
is_object($value) ? get_class($value) : (null === $value ? '<null>' : gettype($value) . '#' . $value)
),
$code,
$previous
);
}
}
<?php declare(strict_types=1);
namespace SebastianBergmann\Diff;
class InvalidArgumentException extends \InvalidArgumentException implements Exception
{
}
<?php declare(strict_types=1);
namespace SebastianBergmann\Diff;
use Throwable;
interface Exception extends Throwable
{
}
<?php declare(strict_types=1);
namespace SebastianBergmann\Diff;
use function array_fill;
use function array_merge;
use function array_reverse;
use function array_slice;
use function count;
use function in_array;
use function max;
final class MemoryEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator
{
public function calculate(array $from, array $to): array
{
$cFrom = count($from);
$cTo = count($to);
if ($cFrom === 0) {
return [];
}
if ($cFrom === 1) {
if (in_array($from[0], $to, true)) {
return [$from[0]];
}
return [];
}
$i = (int) ($cFrom / 2);
$fromStart = array_slice($from, 0, $i);
$fromEnd = array_slice($from, $i);
$llB = $this->length($fromStart, $to);
$llE = $this->length(array_reverse($fromEnd), array_reverse($to));
$jMax = 0;
$max = 0;
for ($j = 0; $j <= $cTo; $j++) {
$m = $llB[$j] + $llE[$cTo - $j];
if ($m >= $max) {
$max = $m;
$jMax = $j;
}
}
$toStart = array_slice($to, 0, $jMax);
$toEnd = array_slice($to, $jMax);
return array_merge(
$this->calculate($fromStart, $toStart),
$this->calculate($fromEnd, $toEnd)
);
}
private function length(array $from, array $to): array
{
$current = array_fill(0, count($to) + 1, 0);
$cFrom = count($from);
$cTo = count($to);
for ($i = 0; $i < $cFrom; $i++) {
$prev = $current;
for ($j = 0; $j < $cTo; $j++) {
if ($from[$i] === $to[$j]) {
$current[$j + 1] = $prev[$j] + 1;
} else {
$current[$j + 1] = max($current[$j], $prev[$j + 1]);
}
}
}
return $current;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer;
use Symfony\Component\Finder\Finder as BaseFinder;
class Finder extends BaseFinder
{
public function __construct()
{
parent::__construct();
$this
->files()
->name('/\.php$/')
->exclude('vendor')
;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ClassNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class ProtectedToPrivateFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Converts `protected` variables and methods to `private` where possible.',
[
new CodeSample(
'<?php
final class Sample
{
protected $a;
protected function test()
{
}
}
'
),
]
);
}
public function getPriority(): int
{
return 66;
}
public function isCandidate(Tokens $tokens): bool
{
if (\defined('T_ENUM') && $tokens->isAllTokenKindsFound([T_ENUM, T_PROTECTED])) {
return true;
}
return $tokens->isAllTokenKindsFound([T_CLASS, T_FINAL, T_PROTECTED]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$tokensAnalyzer = new TokensAnalyzer($tokens);
$modifierKinds = [T_PUBLIC, T_PROTECTED, T_PRIVATE, T_FINAL, T_ABSTRACT, T_NS_SEPARATOR, T_STRING, CT::T_NULLABLE_TYPE, CT::T_ARRAY_TYPEHINT, T_STATIC, CT::T_TYPE_ALTERNATION, CT::T_TYPE_INTERSECTION];
if (\defined('T_READONLY')) {
$modifierKinds[] = T_READONLY;
}
$classesCandidate = [];
$classElementTypes = ['method' => true, 'property' => true, 'const' => true];
foreach ($tokensAnalyzer->getClassyElements() as $index => $element) {
$classIndex = $element['classIndex'];
if (!\array_key_exists($classIndex, $classesCandidate)) {
$classesCandidate[$classIndex] = $this->isClassCandidate($tokens, $classIndex);
}
if (false === $classesCandidate[$classIndex]) {
continue;
}
if (!isset($classElementTypes[$element['type']])) {
continue;
}
$previous = $index;
$isProtected = false;
$isFinal = false;
do {
$previous = $tokens->getPrevMeaningfulToken($previous);
if ($tokens[$previous]->isGivenKind(T_PROTECTED)) {
$isProtected = $previous;
} elseif ($tokens[$previous]->isGivenKind(T_FINAL)) {
$isFinal = $previous;
}
} while ($tokens[$previous]->isGivenKind($modifierKinds));
if (false === $isProtected) {
continue;
}
if ($isFinal && 'const' === $element['type']) {
continue;
}
$element['protected_index'] = $isProtected;
$tokens[$element['protected_index']] = new Token([T_PRIVATE, 'private']);
}
}
private function isClassCandidate(Tokens $tokens, int $classIndex): bool
{
if (\defined('T_ENUM') && $tokens[$classIndex]->isGivenKind(T_ENUM)) {
return true;
}
$prevToken = $tokens[$tokens->getPrevMeaningfulToken($classIndex)];
if (!$prevToken->isGivenKind(T_FINAL)) {
return false;
}
$classNameIndex = $tokens->getNextMeaningfulToken($classIndex);
$classExtendsIndex = $tokens->getNextMeaningfulToken($classNameIndex);
if ($tokens[$classExtendsIndex]->isGivenKind(T_EXTENDS)) {
return false;
}
if (!$tokens->isTokenKindFound(CT::T_USE_TRAIT)) {
return true;
}
$classOpenIndex = $tokens->getNextTokenOfKind($classNameIndex, ['{']);
$classCloseIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $classOpenIndex);
$useIndex = $tokens->getNextTokenOfKind($classOpenIndex, [[CT::T_USE_TRAIT]]);
return null === $useIndex || $useIndex > $classCloseIndex;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ClassNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class ClassDefinitionFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Whitespace around the keywords of a class, trait, enum or interfaces definition should be one space.',
[
new CodeSample(
'<?php
class Foo extends Bar implements Baz, BarBaz
{
}
final class Foo extends Bar implements Baz, BarBaz
{
}
trait Foo
{
}
$foo = new class extends Bar implements Baz, BarBaz {};
'
),
new CodeSample(
'<?php
class Foo
extends Bar
implements Baz, BarBaz
{}
',
['single_line' => true]
),
new CodeSample(
'<?php
class Foo
extends Bar
implements Baz
{}
',
['single_item_single_line' => true]
),
new CodeSample(
'<?php
interface Bar extends
Bar, BarBaz, FooBarBaz
{}
',
['multi_line_extends_each_single_line' => true]
),
new CodeSample(
'<?php
$foo = new class(){};
',
['space_before_parenthesis' => true]
),
new CodeSample(
"<?php\n\$foo = new class(\n \$bar,\n \$baz\n) {};\n",
['inline_constructor_arguments' => true]
),
]
);
}
public function getPriority(): int
{
return 36;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound(Token::getClassyTokenKinds());
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = $tokens->getSize() - 4; $index > 0; --$index) {
if ($tokens[$index]->isClassy()) {
$this->fixClassyDefinition($tokens, $index);
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('multi_line_extends_each_single_line', 'Whether definitions should be multiline.'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
(new FixerOptionBuilder('single_item_single_line', 'Whether definitions should be single line when including a single item.'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
(new FixerOptionBuilder('single_line', 'Whether definitions should be single line.'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
(new FixerOptionBuilder('space_before_parenthesis', 'Whether there should be a single space after the parenthesis of anonymous class (PSR12) or not.'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
(new FixerOptionBuilder('inline_constructor_arguments', 'Whether constructor argument list in anonymous classes should be single line.'))
->setAllowedTypes(['bool'])
->setDefault(true)
->getOption(),
]);
}
private function fixClassyDefinition(Tokens $tokens, int $classyIndex): void
{
$classDefInfo = $this->getClassyDefinitionInfo($tokens, $classyIndex);
if (false !== $classDefInfo['implements']) {
$classDefInfo['implements'] = $this->fixClassyDefinitionImplements(
$tokens,
$classDefInfo['open'],
$classDefInfo['implements']
);
}
if (false !== $classDefInfo['extends']) {
$classDefInfo['extends'] = $this->fixClassyDefinitionExtends(
$tokens,
false === $classDefInfo['implements'] ? $classDefInfo['open'] : $classDefInfo['implements']['start'],
$classDefInfo['extends']
);
}
$classDefInfo['open'] = $this->fixClassyDefinitionOpenSpacing($tokens, $classDefInfo);
if ($classDefInfo['implements']) {
$end = $classDefInfo['implements']['start'];
} elseif ($classDefInfo['extends']) {
$end = $classDefInfo['extends']['start'];
} else {
$end = $tokens->getPrevNonWhitespace($classDefInfo['open']);
}
if ($classDefInfo['anonymousClass'] && !$this->configuration['inline_constructor_arguments']) {
if (!$tokens[$end]->equals(')')) {
$start = $tokens->getPrevMeaningfulToken($end);
$this->makeClassyDefinitionSingleLine($tokens, $start, $end);
$end = $start;
}
if ($tokens[$end]->equals(')')) {
$end = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $end);
}
}
$this->makeClassyDefinitionSingleLine($tokens, $classDefInfo['start'], $end);
}
private function fixClassyDefinitionExtends(Tokens $tokens, int $classOpenIndex, array $classExtendsInfo): array
{
$endIndex = $tokens->getPrevNonWhitespace($classOpenIndex);
if (true === $this->configuration['single_line'] || false === $classExtendsInfo['multiLine']) {
$this->makeClassyDefinitionSingleLine($tokens, $classExtendsInfo['start'], $endIndex);
$classExtendsInfo['multiLine'] = false;
} elseif (true === $this->configuration['single_item_single_line'] && 1 === $classExtendsInfo['numberOfExtends']) {
$this->makeClassyDefinitionSingleLine($tokens, $classExtendsInfo['start'], $endIndex);
$classExtendsInfo['multiLine'] = false;
} elseif (true === $this->configuration['multi_line_extends_each_single_line'] && $classExtendsInfo['multiLine']) {
$this->makeClassyInheritancePartMultiLine($tokens, $classExtendsInfo['start'], $endIndex);
$classExtendsInfo['multiLine'] = true;
}
return $classExtendsInfo;
}
private function fixClassyDefinitionImplements(Tokens $tokens, int $classOpenIndex, array $classImplementsInfo): array
{
$endIndex = $tokens->getPrevNonWhitespace($classOpenIndex);
if (true === $this->configuration['single_line'] || false === $classImplementsInfo['multiLine']) {
$this->makeClassyDefinitionSingleLine($tokens, $classImplementsInfo['start'], $endIndex);
$classImplementsInfo['multiLine'] = false;
} elseif (true === $this->configuration['single_item_single_line'] && 1 === $classImplementsInfo['numberOfImplements']) {
$this->makeClassyDefinitionSingleLine($tokens, $classImplementsInfo['start'], $endIndex);
$classImplementsInfo['multiLine'] = false;
} else {
$this->makeClassyInheritancePartMultiLine($tokens, $classImplementsInfo['start'], $endIndex);
$classImplementsInfo['multiLine'] = true;
}
return $classImplementsInfo;
}
private function fixClassyDefinitionOpenSpacing(Tokens $tokens, array $classDefInfo): int
{
if ($classDefInfo['anonymousClass']) {
if (false !== $classDefInfo['implements']) {
$spacing = $classDefInfo['implements']['multiLine'] ? $this->whitespacesConfig->getLineEnding() : ' ';
} elseif (false !== $classDefInfo['extends']) {
$spacing = $classDefInfo['extends']['multiLine'] ? $this->whitespacesConfig->getLineEnding() : ' ';
} else {
$spacing = ' ';
}
} else {
$spacing = $this->whitespacesConfig->getLineEnding();
}
$openIndex = $tokens->getNextTokenOfKind($classDefInfo['classy'], ['{']);
if (' ' !== $spacing && str_contains($tokens[$openIndex - 1]->getContent(), "\n")) {
return $openIndex;
}
if ($tokens[$openIndex - 1]->isWhitespace()) {
if (' ' !== $spacing || !$tokens[$tokens->getPrevNonWhitespace($openIndex - 1)]->isComment()) {
$tokens[$openIndex - 1] = new Token([T_WHITESPACE, $spacing]);
}
return $openIndex;
}
$tokens->insertAt($openIndex, new Token([T_WHITESPACE, $spacing]));
return $openIndex + 1;
}
private function getClassyDefinitionInfo(Tokens $tokens, int $classyIndex): array
{
$openIndex = $tokens->getNextTokenOfKind($classyIndex, ['{']);
$extends = false;
$implements = false;
$anonymousClass = false;
if (!$tokens[$classyIndex]->isGivenKind(T_TRAIT)) {
$extends = $tokens->findGivenKind(T_EXTENDS, $classyIndex, $openIndex);
$extends = \count($extends) ? $this->getClassyInheritanceInfo($tokens, key($extends), 'numberOfExtends') : false;
if (!$tokens[$classyIndex]->isGivenKind(T_INTERFACE)) {
$implements = $tokens->findGivenKind(T_IMPLEMENTS, $classyIndex, $openIndex);
$implements = \count($implements) ? $this->getClassyInheritanceInfo($tokens, key($implements), 'numberOfImplements') : false;
$tokensAnalyzer = new TokensAnalyzer($tokens);
$anonymousClass = $tokensAnalyzer->isAnonymousClass($classyIndex);
}
}
if ($anonymousClass) {
$startIndex = $tokens->getPrevMeaningfulToken($classyIndex);
} else {
$prev = $tokens->getPrevMeaningfulToken($classyIndex);
$startIndex = $tokens[$prev]->isGivenKind([T_FINAL, T_ABSTRACT]) ? $prev : $classyIndex;
}
return [
'start' => $startIndex,
'classy' => $classyIndex,
'open' => $openIndex,
'extends' => $extends,
'implements' => $implements,
'anonymousClass' => $anonymousClass,
];
}
private function getClassyInheritanceInfo(Tokens $tokens, int $startIndex, string $label): array
{
$implementsInfo = ['start' => $startIndex, $label => 1, 'multiLine' => false];
++$startIndex;
$endIndex = $tokens->getNextTokenOfKind($startIndex, ['{', [T_IMPLEMENTS], [T_EXTENDS]]);
$endIndex = $tokens[$endIndex]->equals('{') ? $tokens->getPrevNonWhitespace($endIndex) : $endIndex;
for ($i = $startIndex; $i < $endIndex; ++$i) {
if ($tokens[$i]->equals(',')) {
++$implementsInfo[$label];
continue;
}
if (!$implementsInfo['multiLine'] && str_contains($tokens[$i]->getContent(), "\n")) {
$implementsInfo['multiLine'] = true;
}
}
return $implementsInfo;
}
private function makeClassyDefinitionSingleLine(Tokens $tokens, int $startIndex, int $endIndex): void
{
for ($i = $endIndex; $i >= $startIndex; --$i) {
if ($tokens[$i]->isWhitespace()) {
if ($tokens[$i - 1]->isComment() || $tokens[$i + 1]->isComment()) {
$content = $tokens[$i - 1]->getContent();
if (!('#' === $content || str_starts_with($content, '//'))) {
$content = $tokens[$i + 1]->getContent();
if (!('#' === $content || str_starts_with($content, '//'))) {
$tokens[$i] = new Token([T_WHITESPACE, ' ']);
}
}
continue;
}
if ($tokens[$i - 1]->isGivenKind(T_CLASS) && $tokens[$i + 1]->equals('(')) {
if (true === $this->configuration['space_before_parenthesis']) {
$tokens[$i] = new Token([T_WHITESPACE, ' ']);
} else {
$tokens->clearAt($i);
}
continue;
}
if (!$tokens[$i - 1]->equals(',') && $tokens[$i + 1]->equalsAny([',', ')']) || $tokens[$i - 1]->equals('(')) {
$tokens->clearAt($i);
continue;
}
$tokens[$i] = new Token([T_WHITESPACE, ' ']);
continue;
}
if ($tokens[$i]->equals(',') && !$tokens[$i + 1]->isWhitespace()) {
$tokens->insertAt($i + 1, new Token([T_WHITESPACE, ' ']));
continue;
}
if (true === $this->configuration['space_before_parenthesis'] && $tokens[$i]->isGivenKind(T_CLASS) && !$tokens[$i + 1]->isWhitespace()) {
$tokens->insertAt($i + 1, new Token([T_WHITESPACE, ' ']));
continue;
}
if (!$tokens[$i]->isComment()) {
continue;
}
if (!$tokens[$i + 1]->isWhitespace() && !$tokens[$i + 1]->isComment() && !str_contains($tokens[$i]->getContent(), "\n")) {
$tokens->insertAt($i + 1, new Token([T_WHITESPACE, ' ']));
}
if (!$tokens[$i - 1]->isWhitespace() && !$tokens[$i - 1]->isComment()) {
$tokens->insertAt($i, new Token([T_WHITESPACE, ' ']));
}
}
}
private function makeClassyInheritancePartMultiLine(Tokens $tokens, int $startIndex, int $endIndex): void
{
for ($i = $endIndex; $i > $startIndex; --$i) {
$previousInterfaceImplementingIndex = $tokens->getPrevTokenOfKind($i, [',', [T_IMPLEMENTS], [T_EXTENDS]]);
$breakAtIndex = $tokens->getNextMeaningfulToken($previousInterfaceImplementingIndex);
$this->makeClassyDefinitionSingleLine(
$tokens,
$breakAtIndex,
$i
);
$isOnOwnLine = false;
for ($j = $breakAtIndex; $j > $previousInterfaceImplementingIndex; --$j) {
if (str_contains($tokens[$j]->getContent(), "\n")) {
$isOnOwnLine = true;
break;
}
}
if (!$isOnOwnLine) {
if ($tokens[$breakAtIndex - 1]->isWhitespace()) {
$tokens[$breakAtIndex - 1] = new Token([
T_WHITESPACE,
$this->whitespacesConfig->getLineEnding().$this->whitespacesConfig->getIndent(),
]);
} else {
$tokens->insertAt($breakAtIndex, new Token([T_WHITESPACE, $this->whitespacesConfig->getLineEnding().$this->whitespacesConfig->getIndent()]));
}
}
$i = $previousInterfaceImplementingIndex + 1;
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ClassNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class SingleClassElementPerStatementFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
{
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound(Token::getClassyTokenKinds());
}
public function getPriority(): int
{
return 56;
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'There MUST NOT be more than one property or constant declared per statement.',
[
new CodeSample(
'<?php
final class Example
{
const FOO_1 = 1, FOO_2 = 2;
private static $bar1 = array(1,2,3), $bar2 = [1,2,3];
}
'
),
new CodeSample(
'<?php
final class Example
{
const FOO_1 = 1, FOO_2 = 2;
private static $bar1 = array(1,2,3), $bar2 = [1,2,3];
}
',
['elements' => ['property']]
),
]
);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$analyzer = new TokensAnalyzer($tokens);
$elements = array_reverse($analyzer->getClassyElements(), true);
foreach ($elements as $index => $element) {
if (!\in_array($element['type'], $this->configuration['elements'], true)) {
continue;
}
$this->fixElement($tokens, $element['type'], $index);
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
$values = ['const', 'property'];
return new FixerConfigurationResolver([
(new FixerOptionBuilder('elements', 'List of strings which element should be modified.'))
->setDefault($values)
->setAllowedTypes(['array'])
->setAllowedValues([new AllowedValueSubset($values)])
->getOption(),
]);
}
private function fixElement(Tokens $tokens, string $type, int $index): void
{
$tokensAnalyzer = new TokensAnalyzer($tokens);
$repeatIndex = $index;
while (true) {
$repeatIndex = $tokens->getNextMeaningfulToken($repeatIndex);
$repeatToken = $tokens[$repeatIndex];
if ($tokensAnalyzer->isArray($repeatIndex)) {
if ($repeatToken->isGivenKind(T_ARRAY)) {
$repeatIndex = $tokens->getNextTokenOfKind($repeatIndex, ['(']);
$repeatIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $repeatIndex);
} else {
$repeatIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, $repeatIndex);
}
continue;
}
if ($repeatToken->equals(';')) {
return;
}
if ($repeatToken->equals(',')) {
break;
}
}
$start = $tokens->getPrevTokenOfKind($index, [';', '{', '}']);
$this->expandElement(
$tokens,
$type,
$tokens->getNextMeaningfulToken($start),
$tokens->getNextTokenOfKind($index, [';'])
);
}
private function expandElement(Tokens $tokens, string $type, int $startIndex, int $endIndex): void
{
$divisionContent = null;
if ($tokens[$startIndex - 1]->isWhitespace()) {
$divisionContent = $tokens[$startIndex - 1]->getContent();
if (Preg::match('#(\n|\r\n)#', $divisionContent, $matches)) {
$divisionContent = $matches[0].trim($divisionContent, "\r\n");
}
}
for ($i = $endIndex - 1; $i > $startIndex; --$i) {
$token = $tokens[$i];
if ($token->equals(')')) {
$i = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $i);
continue;
}
if ($token->isGivenKind(CT::T_ARRAY_SQUARE_BRACE_CLOSE)) {
$i = $tokens->findBlockStart(Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, $i);
continue;
}
if (!$tokens[$i]->equals(',')) {
continue;
}
$tokens[$i] = new Token(';');
if ($tokens[$i + 1]->isWhitespace()) {
$tokens->clearAt($i + 1);
}
if (null !== $divisionContent && '' !== $divisionContent) {
$tokens->insertAt($i + 1, new Token([T_WHITESPACE, $divisionContent]));
}
$sequence = $this->getModifiersSequences($tokens, $type, $startIndex, $endIndex);
$tokens->insertAt($i + 2, $sequence);
}
}
private function getModifiersSequences(Tokens $tokens, string $type, int $startIndex, int $endIndex): array
{
if ('property' === $type) {
$tokenKinds = [T_PUBLIC, T_PROTECTED, T_PRIVATE, T_STATIC, T_VAR, T_STRING, T_NS_SEPARATOR, CT::T_NULLABLE_TYPE, CT::T_ARRAY_TYPEHINT, CT::T_TYPE_ALTERNATION, CT::T_TYPE_INTERSECTION];
if (\defined('T_READONLY')) {
$tokenKinds[] = T_READONLY;
}
} else {
$tokenKinds = [T_PUBLIC, T_PROTECTED, T_PRIVATE, T_CONST];
}
$sequence = [];
for ($i = $startIndex; $i < $endIndex - 1; ++$i) {
if ($tokens[$i]->isComment()) {
continue;
}
if (!$tokens[$i]->isWhitespace() && !$tokens[$i]->isGivenKind($tokenKinds)) {
break;
}
$sequence[] = clone $tokens[$i];
}
return $sequence;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ClassNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class OrderedClassElementsFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public const SORT_ALPHA = 'alpha';
public const SORT_NONE = 'none';
private const SUPPORTED_SORT_ALGORITHMS = [
self::SORT_NONE,
self::SORT_ALPHA,
];
private static array $typeHierarchy = [
'use_trait' => null,
'public' => null,
'protected' => null,
'private' => null,
'case' => ['public'],
'constant' => null,
'constant_public' => ['constant', 'public'],
'constant_protected' => ['constant', 'protected'],
'constant_private' => ['constant', 'private'],
'property' => null,
'property_static' => ['property'],
'property_public' => ['property', 'public'],
'property_protected' => ['property', 'protected'],
'property_private' => ['property', 'private'],
'property_public_readonly' => ['property_readonly', 'property_public'],
'property_protected_readonly' => ['property_readonly', 'property_protected'],
'property_private_readonly' => ['property_readonly', 'property_private'],
'property_public_static' => ['property_static', 'property_public'],
'property_protected_static' => ['property_static', 'property_protected'],
'property_private_static' => ['property_static', 'property_private'],
'method' => null,
'method_abstract' => ['method'],
'method_static' => ['method'],
'method_public' => ['method', 'public'],
'method_protected' => ['method', 'protected'],
'method_private' => ['method', 'private'],
'method_public_abstract' => ['method_abstract', 'method_public'],
'method_protected_abstract' => ['method_abstract', 'method_protected'],
'method_private_abstract' => ['method_abstract', 'method_private'],
'method_public_abstract_static' => ['method_abstract', 'method_static', 'method_public'],
'method_protected_abstract_static' => ['method_abstract', 'method_static', 'method_protected'],
'method_private_abstract_static' => ['method_abstract', 'method_static', 'method_private'],
'method_public_static' => ['method_static', 'method_public'],
'method_protected_static' => ['method_static', 'method_protected'],
'method_private_static' => ['method_static', 'method_private'],
];
private static array $specialTypes = [
'construct' => null,
'destruct' => null,
'magic' => null,
'phpunit' => null,
];
private array $typePosition;
public function configure(array $configuration): void
{
parent::configure($configuration);
$this->typePosition = [];
$pos = 0;
foreach ($this->configuration['order'] as $type) {
$this->typePosition[$type] = $pos++;
}
foreach (self::$typeHierarchy as $type => $parents) {
if (isset($this->typePosition[$type])) {
continue;
}
if (!$parents) {
$this->typePosition[$type] = null;
continue;
}
foreach ($parents as $parent) {
if (isset($this->typePosition[$parent])) {
$this->typePosition[$type] = $this->typePosition[$parent];
continue 2;
}
}
$this->typePosition[$type] = null;
}
$lastPosition = \count($this->configuration['order']);
foreach ($this->typePosition as &$pos) {
if (null === $pos) {
$pos = $lastPosition;
}
$pos *= 10;
}
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound(Token::getClassyTokenKinds());
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Orders the elements of classes/interfaces/traits/enums.',
[
new CodeSample(
'<?php
final class Example
{
use BarTrait;
use BazTrait;
const C1 = 1;
const C2 = 2;
protected static $protStatProp;
public static $pubStatProp1;
public $pubProp1;
protected $protProp;
var $pubProp2;
private static $privStatProp;
private $privProp;
public static $pubStatProp2;
public $pubProp3;
protected function __construct() {}
private static function privStatFunc() {}
public function pubFunc1() {}
public function __toString() {}
protected function protFunc() {}
function pubFunc2() {}
public static function pubStatFunc1() {}
public function pubFunc3() {}
static function pubStatFunc2() {}
private function privFunc() {}
public static function pubStatFunc3() {}
protected static function protStatFunc() {}
public function __destruct() {}
}
'
),
new CodeSample(
'<?php
class Example
{
public function A(){}
private function B(){}
}
',
['order' => ['method_private', 'method_public']]
),
new CodeSample(
'<?php
class Example
{
public function D(){}
public function B(){}
public function A(){}
public function C(){}
}
',
['order' => ['method_public'], 'sort_algorithm' => self::SORT_ALPHA]
),
]
);
}
public function getPriority(): int
{
return 65;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($i = 1, $count = $tokens->count(); $i < $count; ++$i) {
if (!$tokens[$i]->isClassy()) {
continue;
}
$i = $tokens->getNextTokenOfKind($i, ['{']);
$elements = $this->getElements($tokens, $i);
if (0 === \count($elements)) {
continue;
}
$sorted = $this->sortElements($elements);
$endIndex = $elements[\count($elements) - 1]['end'];
if ($sorted !== $elements) {
$this->sortTokens($tokens, $i, $endIndex, $sorted);
}
$i = $endIndex;
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('order', 'List of strings defining order of elements.'))
->setAllowedTypes(['array'])
->setAllowedValues([new AllowedValueSubset(array_keys(array_merge(self::$typeHierarchy, self::$specialTypes)))])
->setDefault([
'use_trait',
'case',
'constant_public',
'constant_protected',
'constant_private',
'property_public',
'property_protected',
'property_private',
'construct',
'destruct',
'magic',
'phpunit',
'method_public',
'method_protected',
'method_private',
])
->getOption(),
(new FixerOptionBuilder('sort_algorithm', 'How multiple occurrences of same type statements should be sorted'))
->setAllowedValues(self::SUPPORTED_SORT_ALGORITHMS)
->setDefault(self::SORT_NONE)
->getOption(),
]);
}
private function getElements(Tokens $tokens, int $startIndex): array
{
static $elementTokenKinds = [CT::T_USE_TRAIT, T_CASE, T_CONST, T_VARIABLE, T_FUNCTION];
++$startIndex;
$elements = [];
while (true) {
$element = [
'start' => $startIndex,
'visibility' => 'public',
'abstract' => false,
'static' => false,
'readonly' => false,
];
for ($i = $startIndex;; ++$i) {
$token = $tokens[$i];
if ($token->equals('}')) {
return $elements;
}
if ($token->isGivenKind(T_ABSTRACT)) {
$element['abstract'] = true;
continue;
}
if ($token->isGivenKind(T_STATIC)) {
$element['static'] = true;
continue;
}
if (\defined('T_READONLY') && $token->isGivenKind(T_READONLY)) {
$element['readonly'] = true;
}
if ($token->isGivenKind([T_PROTECTED, T_PRIVATE])) {
$element['visibility'] = strtolower($token->getContent());
continue;
}
if (!$token->isGivenKind($elementTokenKinds)) {
continue;
}
$type = $this->detectElementType($tokens, $i);
if (\is_array($type)) {
$element['type'] = $type[0];
$element['name'] = $type[1];
} else {
$element['type'] = $type;
}
if ('property' === $element['type']) {
$element['name'] = $tokens[$i]->getContent();
} elseif (\in_array($element['type'], ['use_trait', 'case', 'constant', 'method', 'magic', 'construct', 'destruct'], true)) {
$element['name'] = $tokens[$tokens->getNextMeaningfulToken($i)]->getContent();
}
$element['end'] = $this->findElementEnd($tokens, $i);
break;
}
$elements[] = $element;
$startIndex = $element['end'] + 1;
}
}
private function detectElementType(Tokens $tokens, int $index)
{
$token = $tokens[$index];
if ($token->isGivenKind(CT::T_USE_TRAIT)) {
return 'use_trait';
}
if ($token->isGivenKind(T_CASE)) {
return 'case';
}
if ($token->isGivenKind(T_CONST)) {
return 'constant';
}
if ($token->isGivenKind(T_VARIABLE)) {
return 'property';
}
$nameToken = $tokens[$tokens->getNextMeaningfulToken($index)];
if ($nameToken->equals([T_STRING, '__construct'], false)) {
return 'construct';
}
if ($nameToken->equals([T_STRING, '__destruct'], false)) {
return 'destruct';
}
if (
$nameToken->equalsAny([
[T_STRING, 'setUpBeforeClass'],
[T_STRING, 'doSetUpBeforeClass'],
[T_STRING, 'tearDownAfterClass'],
[T_STRING, 'doTearDownAfterClass'],
[T_STRING, 'setUp'],
[T_STRING, 'doSetUp'],
[T_STRING, 'assertPreConditions'],
[T_STRING, 'assertPostConditions'],
[T_STRING, 'tearDown'],
[T_STRING, 'doTearDown'],
], false)
) {
return ['phpunit', strtolower($nameToken->getContent())];
}
return str_starts_with($nameToken->getContent(), '__') ? 'magic' : 'method';
}
private function findElementEnd(Tokens $tokens, int $index): int
{
$index = $tokens->getNextTokenOfKind($index, ['{', ';']);
if ($tokens[$index]->equals('{')) {
$index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $index);
}
for (++$index; $tokens[$index]->isWhitespace(" \t") || $tokens[$index]->isComment(); ++$index);
--$index;
return $tokens[$index]->isWhitespace() ? $index - 1 : $index;
}
private function sortElements(array $elements): array
{
static $phpunitPositions = [
'setupbeforeclass' => 1,
'dosetupbeforeclass' => 2,
'teardownafterclass' => 3,
'doteardownafterclass' => 4,
'setup' => 5,
'dosetup' => 6,
'assertpreconditions' => 7,
'assertpostconditions' => 8,
'teardown' => 9,
'doteardown' => 10,
];
foreach ($elements as &$element) {
$type = $element['type'];
if (\array_key_exists($type, self::$specialTypes)) {
if (isset($this->typePosition[$type])) {
$element['position'] = $this->typePosition[$type];
if ('phpunit' === $type) {
$element['position'] += $phpunitPositions[$element['name']];
}
continue;
}
$type = 'method';
}
if (\in_array($type, ['constant', 'property', 'method'], true)) {
$type .= '_'.$element['visibility'];
if ($element['abstract']) {
$type .= '_abstract';
}
if ($element['static']) {
$type .= '_static';
}
if ($element['readonly']) {
$type .= '_readonly';
}
}
$element['position'] = $this->typePosition[$type];
}
unset($element);
usort($elements, function (array $a, array $b): int {
if ($a['position'] === $b['position']) {
return $this->sortGroupElements($a, $b);
}
return $a['position'] <=> $b['position'];
});
return $elements;
}
private function sortGroupElements(array $a, array $b): int
{
$selectedSortAlgorithm = $this->configuration['sort_algorithm'];
if (self::SORT_ALPHA === $selectedSortAlgorithm) {
return strcasecmp($a['name'], $b['name']);
}
return $a['start'] <=> $b['start'];
}
private function sortTokens(Tokens $tokens, int $startIndex, int $endIndex, array $elements): void
{
$replaceTokens = [];
foreach ($elements as $element) {
for ($i = $element['start']; $i <= $element['end']; ++$i) {
$replaceTokens[] = clone $tokens[$i];
}
}
$tokens->overrideRange($startIndex + 1, $endIndex, $replaceTokens);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ClassNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class OrderedInterfacesFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public const OPTION_DIRECTION = 'direction';
public const OPTION_ORDER = 'order';
public const DIRECTION_ASCEND = 'ascend';
public const DIRECTION_DESCEND = 'descend';
public const ORDER_ALPHA = 'alpha';
public const ORDER_LENGTH = 'length';
private const SUPPORTED_DIRECTION_OPTIONS = [
self::DIRECTION_ASCEND,
self::DIRECTION_DESCEND,
];
private const SUPPORTED_ORDER_OPTIONS = [
self::ORDER_ALPHA,
self::ORDER_LENGTH,
];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Orders the interfaces in an `implements` or `interface extends` clause.',
[
new CodeSample(
"<?php\n\nfinal class ExampleA implements Gamma, Alpha, Beta {}\n\ninterface ExampleB extends Gamma, Alpha, Beta {}\n"
),
new CodeSample(
"<?php\n\nfinal class ExampleA implements Gamma, Alpha, Beta {}\n\ninterface ExampleB extends Gamma, Alpha, Beta {}\n",
[self::OPTION_DIRECTION => self::DIRECTION_DESCEND]
),
new CodeSample(
"<?php\n\nfinal class ExampleA implements MuchLonger, Short, Longer {}\n\ninterface ExampleB extends MuchLonger, Short, Longer {}\n",
[self::OPTION_ORDER => self::ORDER_LENGTH]
),
new CodeSample(
"<?php\n\nfinal class ExampleA implements MuchLonger, Short, Longer {}\n\ninterface ExampleB extends MuchLonger, Short, Longer {}\n",
[
self::OPTION_ORDER => self::ORDER_LENGTH,
self::OPTION_DIRECTION => self::DIRECTION_DESCEND,
]
),
],
null,
"Risky for `implements` when specifying both an interface and its parent interface, because PHP doesn't break on `parent, child` but does on `child, parent`."
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_IMPLEMENTS)
|| $tokens->isAllTokenKindsFound([T_INTERFACE, T_EXTENDS]);
}
public function isRisky(): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_IMPLEMENTS)) {
if (!$token->isGivenKind(T_EXTENDS)) {
continue;
}
$nameTokenIndex = $tokens->getPrevMeaningfulToken($index);
$interfaceTokenIndex = $tokens->getPrevMeaningfulToken($nameTokenIndex);
$interfaceToken = $tokens[$interfaceTokenIndex];
if (!$interfaceToken->isGivenKind(T_INTERFACE)) {
continue;
}
}
$implementsStart = $index + 1;
$implementsEnd = $tokens->getPrevNonWhitespace($tokens->getNextTokenOfKind($implementsStart, ['{']));
$interfaces = $this->getInterfaces($tokens, $implementsStart, $implementsEnd);
if (1 === \count($interfaces)) {
continue;
}
foreach ($interfaces as $interfaceIndex => $interface) {
$interfaceTokens = Tokens::fromArray($interface, false);
$normalized = '';
$actualInterfaceIndex = $interfaceTokens->getNextMeaningfulToken(-1);
while ($interfaceTokens->offsetExists($actualInterfaceIndex)) {
$token = $interfaceTokens[$actualInterfaceIndex];
if ($token->isComment() || $token->isWhitespace()) {
break;
}
$normalized .= str_replace('\\', ' ', $token->getContent());
++$actualInterfaceIndex;
}
$interfaces[$interfaceIndex] = [
'tokens' => $interface,
'normalized' => $normalized,
'originalIndex' => $interfaceIndex,
];
}
usort($interfaces, function (array $first, array $second): int {
$score = self::ORDER_LENGTH === $this->configuration[self::OPTION_ORDER]
? \strlen($first['normalized']) - \strlen($second['normalized'])
: strcasecmp($first['normalized'], $second['normalized']);
if (self::DIRECTION_DESCEND === $this->configuration[self::OPTION_DIRECTION]) {
$score *= -1;
}
return $score;
});
$changed = false;
foreach ($interfaces as $interfaceIndex => $interface) {
if ($interface['originalIndex'] !== $interfaceIndex) {
$changed = true;
break;
}
}
if (!$changed) {
continue;
}
$newTokens = array_shift($interfaces)['tokens'];
foreach ($interfaces as $interface) {
array_push($newTokens, new Token(','), ...$interface['tokens']);
}
$tokens->overrideRange($implementsStart, $implementsEnd, $newTokens);
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder(self::OPTION_ORDER, 'How the interfaces should be ordered'))
->setAllowedValues(self::SUPPORTED_ORDER_OPTIONS)
->setDefault(self::ORDER_ALPHA)
->getOption(),
(new FixerOptionBuilder(self::OPTION_DIRECTION, 'Which direction the interfaces should be ordered'))
->setAllowedValues(self::SUPPORTED_DIRECTION_OPTIONS)
->setDefault(self::DIRECTION_ASCEND)
->getOption(),
]);
}
private function getInterfaces(Tokens $tokens, int $implementsStart, int $implementsEnd): array
{
$interfaces = [];
$interfaceIndex = 0;
for ($i = $implementsStart; $i <= $implementsEnd; ++$i) {
if ($tokens[$i]->equals(',')) {
++$interfaceIndex;
$interfaces[$interfaceIndex] = [];
continue;
}
$interfaces[$interfaceIndex][] = $tokens[$i];
}
return $interfaces;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ClassNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class NoPhp4ConstructorFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Convert PHP4-style constructors to `__construct`.',
[
new CodeSample('<?php
class Foo
{
public function Foo($bar)
{
}
}
'),
],
null,
'Risky when old style constructor being fixed is overridden or overrides parent one.'
);
}
public function getPriority(): int
{
return 75;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_CLASS);
}
public function isRisky(): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$tokensAnalyzer = new TokensAnalyzer($tokens);
$classes = array_keys($tokens->findGivenKind(T_CLASS));
$numClasses = \count($classes);
for ($i = 0; $i < $numClasses; ++$i) {
$index = $classes[$i];
if ($tokensAnalyzer->isAnonymousClass($index)) {
continue;
}
$nspIndex = $tokens->getPrevTokenOfKind($index, [[T_NAMESPACE, 'namespace']]);
if (null !== $nspIndex) {
$nspIndex = $tokens->getNextMeaningfulToken($nspIndex);
if (!$tokens[$nspIndex]->equals('{')) {
$nspIndex = $tokens->getNextTokenOfKind($nspIndex, [';', '{']);
if ($tokens[$nspIndex]->equals(';')) {
break;
}
$nspEnd = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $nspIndex);
if ($index < $nspEnd) {
for ($j = $i + 1; $j < $numClasses; ++$j) {
if ($classes[$j] < $nspEnd) {
++$i;
}
}
continue;
}
}
}
$classNameIndex = $tokens->getNextMeaningfulToken($index);
$className = $tokens[$classNameIndex]->getContent();
$classStart = $tokens->getNextTokenOfKind($classNameIndex, ['{']);
$classEnd = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $classStart);
$this->fixConstructor($tokens, $className, $classStart, $classEnd);
$this->fixParent($tokens, $classStart, $classEnd);
}
}
private function fixConstructor(Tokens $tokens, string $className, int $classStart, int $classEnd): void
{
$php4 = $this->findFunction($tokens, $className, $classStart, $classEnd);
if (null === $php4) {
return;
}
if (!empty($php4['modifiers'][T_ABSTRACT]) || !empty($php4['modifiers'][T_STATIC])) {
return;
}
$php5 = $this->findFunction($tokens, '__construct', $classStart, $classEnd);
if (null === $php5) {
$tokens[$php4['nameIndex']] = new Token([T_STRING, '__construct']);
$this->fixInfiniteRecursion($tokens, $php4['bodyIndex'], $php4['endIndex']);
return;
}
[$sequences, $case] = $this->getWrapperMethodSequence($tokens, '__construct', $php4['startIndex'], $php4['bodyIndex']);
foreach ($sequences as $seq) {
if (null !== $tokens->findSequence($seq, $php4['bodyIndex'] - 1, $php4['endIndex'], $case)) {
for ($i = $php4['startIndex']; $i <= $php4['endIndex']; ++$i) {
$tokens->clearAt($i);
}
return;
}
}
[$sequences, $case] = $this->getWrapperMethodSequence($tokens, $className, $php4['startIndex'], $php4['bodyIndex']);
foreach ($sequences as $seq) {
if (null !== $tokens->findSequence($seq, $php5['bodyIndex'] - 1, $php5['endIndex'], $case)) {
for ($i = $php5['startIndex']; $i <= $php5['endIndex']; ++$i) {
$tokens->clearAt($i);
}
$tokens[$php4['nameIndex']] = new Token([T_STRING, '__construct']);
return;
}
}
}
private function fixParent(Tokens $tokens, int $classStart, int $classEnd): void
{
foreach ($tokens->findGivenKind(T_EXTENDS) as $index => $token) {
$parentIndex = $tokens->getNextMeaningfulToken($index);
$parentClass = $tokens[$parentIndex]->getContent();
$parentSeq = $tokens->findSequence([
[T_STRING],
[T_DOUBLE_COLON],
[T_STRING, $parentClass],
'(',
], $classStart, $classEnd, [2 => false]);
if (null !== $parentSeq) {
$parentSeq = array_keys($parentSeq);
if ($tokens[$parentSeq[0]]->equalsAny([[T_STRING, 'parent'], [T_STRING, $parentClass]], false)) {
$tokens[$parentSeq[0]] = new Token([T_STRING, 'parent']);
$tokens[$parentSeq[2]] = new Token([T_STRING, '__construct']);
}
}
foreach (Token::getObjectOperatorKinds() as $objectOperatorKind) {
$parentSeq = $tokens->findSequence([
[T_VARIABLE, '$this'],
[$objectOperatorKind],
[T_STRING, $parentClass],
'(',
], $classStart, $classEnd, [2 => false]);
if (null !== $parentSeq) {
$parentSeq = array_keys($parentSeq);
$tokens[$parentSeq[0]] = new Token([
T_STRING,
'parent',
]);
$tokens[$parentSeq[1]] = new Token([
T_DOUBLE_COLON,
'::',
]);
$tokens[$parentSeq[2]] = new Token([T_STRING, '__construct']);
}
}
}
}
private function fixInfiniteRecursion(Tokens $tokens, int $start, int $end): void
{
foreach (Token::getObjectOperatorKinds() as $objectOperatorKind) {
$seq = [
[T_VARIABLE, '$this'],
[$objectOperatorKind],
[T_STRING, '__construct'],
];
while (true) {
$callSeq = $tokens->findSequence($seq, $start, $end, [2 => false]);
if (null === $callSeq) {
return;
}
$callSeq = array_keys($callSeq);
$tokens[$callSeq[0]] = new Token([T_STRING, 'parent']);
$tokens[$callSeq[1]] = new Token([T_DOUBLE_COLON, '::']);
}
}
}
private function getWrapperMethodSequence(Tokens $tokens, string $method, int $startIndex, int $bodyIndex): array
{
$sequences = [];
foreach (Token::getObjectOperatorKinds() as $objectOperatorKind) {
$seq = [
'{',
[T_VARIABLE, '$this'],
[$objectOperatorKind],
[T_STRING, $method],
'(',
];
$index = $startIndex;
while (true) {
$index = $tokens->getNextTokenOfKind($index, [[T_VARIABLE]]);
if (null === $index || $index >= $bodyIndex) {
break;
}
if (\count($seq) > 5) {
$seq[] = ',';
}
$seq[] = [T_VARIABLE, $tokens[$index]->getContent()];
}
$seq[] = ')';
$seq[] = ';';
$seq[] = '}';
$sequences[] = $seq;
}
return [$sequences, [3 => false]];
}
private function findFunction(Tokens $tokens, string $name, int $startIndex, int $endIndex): ?array
{
$function = $tokens->findSequence([
[T_FUNCTION],
[T_STRING, $name],
'(',
], $startIndex, $endIndex, false);
if (null === $function) {
return null;
}
$function = array_keys($function);
$possibleModifiers = [T_PUBLIC, T_PROTECTED, T_PRIVATE, T_STATIC, T_ABSTRACT, T_FINAL];
$modifiers = [];
$prevBlock = $tokens->getPrevMeaningfulToken($function[0]);
while (null !== $prevBlock && $tokens[$prevBlock]->isGivenKind($possibleModifiers)) {
$modifiers[$tokens[$prevBlock]->getId()] = $prevBlock;
$prevBlock = $tokens->getPrevMeaningfulToken($prevBlock);
}
if (isset($modifiers[T_ABSTRACT])) {
$bodyStart = null;
$funcEnd = $tokens->getNextTokenOfKind($function[2], [';']);
} else {
$bodyStart = $tokens->getNextTokenOfKind($function[2], ['{']);
$funcEnd = null !== $bodyStart ? $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $bodyStart) : null;
}
return [
'nameIndex' => $function[1],
'startIndex' => $prevBlock + 1,
'endIndex' => $funcEnd,
'bodyIndex' => $bodyStart,
'modifiers' => $modifiers,
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ClassNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
use Symfony\Component\OptionsResolver\Options;
final class FinalInternalClassFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public function configure(array $configuration): void
{
parent::configure($configuration);
$intersect = array_intersect_assoc(
$this->configuration['annotation_include'],
$this->configuration['annotation_exclude']
);
if (\count($intersect) > 0) {
throw new InvalidFixerConfigurationException($this->getName(), sprintf('Annotation cannot be used in both the include and exclude list, got duplicates: "%s".', implode('", "', array_keys($intersect))));
}
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Internal classes should be `final`.',
[
new CodeSample("<?php\n/**\n * @internal\n */\nclass Sample\n{\n}\n"),
new CodeSample(
"<?php\n/**\n * @CUSTOM\n */\nclass A{}\n\n/**\n * @CUSTOM\n * @not-fix\n */\nclass B{}\n",
[
'annotation_include' => ['@Custom'],
'annotation_exclude' => ['@not-fix'],
]
),
],
null,
'Changing classes to `final` might cause code execution to break.'
);
}
public function getPriority(): int
{
return 67;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_CLASS);
}
public function isRisky(): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$tokensAnalyzer = new TokensAnalyzer($tokens);
for ($index = $tokens->count() - 1; 0 <= $index; --$index) {
if (!$tokens[$index]->isGivenKind(T_CLASS) || $tokensAnalyzer->isAnonymousClass($index) || !$this->isClassCandidate($tokens, $index)) {
continue;
}
$tokens->insertAt(
$index,
[
new Token([T_FINAL, 'final']),
new Token([T_WHITESPACE, ' ']),
]
);
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
$annotationsAsserts = [static function (array $values): bool {
foreach ($values as $value) {
if (!\is_string($value) || '' === $value) {
return false;
}
}
return true;
}];
$annotationsNormalizer = static function (Options $options, array $value): array {
$newValue = [];
foreach ($value as $key) {
if ('@' === $key[0]) {
$key = substr($key, 1);
}
$newValue[strtolower($key)] = true;
}
return $newValue;
};
return new FixerConfigurationResolver([
(new FixerOptionBuilder('annotation_include', 'Class level annotations tags that must be set in order to fix the class. (case insensitive)'))
->setAllowedTypes(['array'])
->setAllowedValues($annotationsAsserts)
->setDefault(['@internal'])
->setNormalizer($annotationsNormalizer)
->getOption(),
(new FixerOptionBuilder('annotation_exclude', 'Class level annotations tags that must be omitted to fix the class, even if all of the white list ones are used as well. (case insensitive)'))
->setAllowedTypes(['array'])
->setAllowedValues($annotationsAsserts)
->setDefault([
'@final',
'@Entity',
'@ORM\Entity',
'@ORM\Mapping\Entity',
'@Mapping\Entity',
'@Document',
'@ODM\Document',
])
->setNormalizer($annotationsNormalizer)
->getOption(),
(new FixerOptionBuilder('consider_absent_docblock_as_internal_class', 'Should classes without any DocBlock be fixed to final?'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
]);
}
private function isClassCandidate(Tokens $tokens, int $index): bool
{
if ($tokens[$tokens->getPrevMeaningfulToken($index)]->isGivenKind([T_ABSTRACT, T_FINAL])) {
return false;
}
$docToken = $tokens[$tokens->getPrevNonWhitespace($index)];
if (!$docToken->isGivenKind(T_DOC_COMMENT)) {
return $this->configuration['consider_absent_docblock_as_internal_class'];
}
$doc = new DocBlock($docToken->getContent());
$tags = [];
foreach ($doc->getAnnotations() as $annotation) {
if (1 !== Preg::match('/@\S+(?=\s|$)/', $annotation->getContent(), $matches)) {
continue;
}
$tag = strtolower(substr(array_shift($matches), 1));
foreach ($this->configuration['annotation_exclude'] as $tagStart => $true) {
if (str_starts_with($tag, $tagStart)) {
return false;
}
}
$tags[$tag] = true;
}
foreach ($this->configuration['annotation_include'] as $tag => $true) {
if (!isset($tags[$tag])) {
return false;
}
}
return true;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ClassNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Tokens;
final class NoNullPropertyInitializationFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Properties MUST not be explicitly initialized with `null` except when they have a type declaration (PHP 7.4).',
[
new CodeSample(
'<?php
class Foo {
public $foo = null;
}
'
),
new CodeSample(
'<?php
class Foo {
public static $foo = null;
}
'
),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_CLASS, T_TRAIT]) && $tokens->isAnyTokenKindsFound([T_PUBLIC, T_PROTECTED, T_PRIVATE, T_VAR, T_STATIC]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$inClass = [];
$classLevel = 0;
for ($index = 0, $count = $tokens->count(); $index < $count; ++$index) {
if ($tokens[$index]->isGivenKind([T_CLASS, T_TRAIT])) {
++$classLevel;
$inClass[$classLevel] = 1;
$index = $tokens->getNextTokenOfKind($index, ['{']);
continue;
}
if (0 === $classLevel) {
continue;
}
if ($tokens[$index]->equals('{')) {
++$inClass[$classLevel];
continue;
}
if ($tokens[$index]->equals('}')) {
--$inClass[$classLevel];
if (0 === $inClass[$classLevel]) {
unset($inClass[$classLevel]);
--$classLevel;
}
continue;
}
if (1 !== $inClass[$classLevel]) {
continue;
}
if (!$tokens[$index]->isGivenKind([T_PUBLIC, T_PROTECTED, T_PRIVATE, T_VAR, T_STATIC])) {
continue;
}
while (true) {
$varTokenIndex = $index = $tokens->getNextMeaningfulToken($index);
if ($tokens[$index]->isGivenKind(T_STATIC)) {
$varTokenIndex = $index = $tokens->getNextMeaningfulToken($index);
}
if (!$tokens[$index]->isGivenKind(T_VARIABLE)) {
break;
}
$index = $tokens->getNextMeaningfulToken($index);
if ($tokens[$index]->equals('=')) {
$index = $tokens->getNextMeaningfulToken($index);
if ($tokens[$index]->isGivenKind(T_NS_SEPARATOR)) {
$index = $tokens->getNextMeaningfulToken($index);
}
if ($tokens[$index]->equals([T_STRING, 'null'], false)) {
for ($i = $varTokenIndex + 1; $i <= $index; ++$i) {
if (
!($tokens[$i]->isWhitespace() && str_contains($tokens[$i]->getContent(), "\n"))
&& !$tokens[$i]->isComment()
) {
$tokens->clearAt($i);
}
}
}
++$index;
}
if (!$tokens[$index]->equals(',')) {
break;
}
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ClassNotation;
use PhpCsFixer\AbstractProxyFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
final class FinalClassFixer extends AbstractProxyFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'All classes must be final, except abstract ones and Doctrine entities.',
[
new CodeSample(
'<?php
class MyApp {}
'
),
],
'No exception and no configuration are intentional. Beside Doctrine entities and of course abstract classes, there is no single reason not to declare all classes final. '
.'If you want to subclass a class, mark the parent class as abstract and create two child classes, one empty if necessary: you\'ll gain much more fine grained type-hinting. '
.'If you need to mock a standalone class, create an interface, or maybe it\'s a value-object that shouldn\'t be mocked at all. '
.'If you need to extend a standalone class, create an interface and use the Composite pattern. '
.'If you aren\'t ready yet for serious OOP, go with FinalInternalClassFixer, it\'s fine.',
'Risky when subclassing non-abstract classes.'
);
}
protected function createProxyFixers(): array
{
$fixer = new FinalInternalClassFixer();
$fixer->configure([
'annotation_include' => [],
'consider_absent_docblock_as_internal_class' => true,
]);
return [$fixer];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ClassNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NoBlankLinesAfterClassOpeningFixer extends AbstractFixer implements WhitespacesAwareFixerInterface
{
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound(Token::getClassyTokenKinds());
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'There should be no empty lines after class opening brace.',
[
new CodeSample(
'<?php
final class Sample
{
protected function foo()
{
}
}
'
),
]
);
}
public function getPriority(): int
{
return 0;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isClassy()) {
continue;
}
$startBraceIndex = $tokens->getNextTokenOfKind($index, ['{']);
if (!$tokens[$startBraceIndex + 1]->isWhitespace()) {
continue;
}
$this->fixWhitespace($tokens, $startBraceIndex + 1);
}
}
private function fixWhitespace(Tokens $tokens, int $index): void
{
$content = $tokens[$index]->getContent();
if (substr_count($content, "\n") > 1) {
$tokens[$index] = new Token([T_WHITESPACE, $this->whitespacesConfig->getLineEnding().substr($content, strrpos($content, "\n") + 1)]);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ClassNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class NoUnneededFinalMethodFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Removes `final` from methods where possible.',
[
new CodeSample(
'<?php
final class Foo
{
final public function foo1() {}
final protected function bar() {}
final private function baz() {}
}
class Bar
{
final private function bar1() {}
}
'
),
new CodeSample(
'<?php
final class Foo
{
final private function baz() {}
}
class Bar
{
final private function bar1() {}
}
',
['private_methods' => false]
),
],
null,
'Risky when child class overrides a `private` method.'
);
}
public function isCandidate(Tokens $tokens): bool
{
if (!$tokens->isAllTokenKindsFound([T_FINAL, T_FUNCTION])) {
return false;
}
if (\defined('T_ENUM') && $tokens->isTokenKindFound(T_ENUM)) {
return true;
}
return $tokens->isTokenKindFound(T_CLASS);
}
public function isRisky(): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($this->getMethods($tokens) as $element) {
$index = $element['method_final_index'];
if ($element['method_of_enum'] || $element['class_is_final']) {
$this->clearFinal($tokens, $index);
continue;
}
if (!$element['method_is_private'] || false === $this->configuration['private_methods'] || $element['method_is_constructor']) {
continue;
}
$this->clearFinal($tokens, $index);
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('private_methods', 'Private methods of non-`final` classes must not be declared `final`.'))
->setAllowedTypes(['bool'])
->setDefault(true)
->getOption(),
]);
}
private function getMethods(Tokens $tokens): \Generator
{
$tokensAnalyzer = new TokensAnalyzer($tokens);
$modifierKinds = [T_PUBLIC, T_PROTECTED, T_PRIVATE, T_FINAL, T_ABSTRACT, T_STATIC];
$enums = [];
$classesAreFinal = [];
$elements = $tokensAnalyzer->getClassyElements();
for (end($elements);; prev($elements)) {
$index = key($elements);
if (null === $index) {
break;
}
$element = current($elements);
if ('method' !== $element['type']) {
continue;
}
$classIndex = $element['classIndex'];
if (!\array_key_exists($classIndex, $enums)) {
$enums[$classIndex] = \defined('T_ENUM') && $tokens[$classIndex]->isGivenKind(T_ENUM);
}
$element['method_final_index'] = null;
$element['method_is_private'] = false;
$previous = $index;
do {
$previous = $tokens->getPrevMeaningfulToken($previous);
if ($tokens[$previous]->isGivenKind(T_PRIVATE)) {
$element['method_is_private'] = true;
} elseif ($tokens[$previous]->isGivenKind(T_FINAL)) {
$element['method_final_index'] = $previous;
}
} while ($tokens[$previous]->isGivenKind($modifierKinds));
if ($enums[$classIndex]) {
$element['method_of_enum'] = true;
yield $element;
continue;
}
if (!\array_key_exists($classIndex, $classesAreFinal)) {
$prevToken = $tokens[$tokens->getPrevMeaningfulToken($classIndex)];
$classesAreFinal[$classIndex] = $prevToken->isGivenKind(T_FINAL);
}
$element['method_of_enum'] = false;
$element['class_is_final'] = $classesAreFinal[$classIndex];
$element['method_is_constructor'] = '__construct' === strtolower($tokens[$tokens->getNextMeaningfulToken($index)]->getContent());
yield $element;
}
}
private function clearFinal(Tokens $tokens, ?int $index): void
{
if (null === $index) {
return;
}
$tokens->clearAt($index);
++$index;
if ($tokens[$index]->isWhitespace()) {
$tokens->clearAt($index);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ClassNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\FixerDefinition\VersionSpecification;
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
final class ClassAttributesSeparationFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
{
public const SPACING_NONE = 'none';
public const SPACING_ONE = 'one';
private const SPACING_ONLY_IF_META = 'only_if_meta';
private array $classElementTypes = [];
public function configure(array $configuration): void
{
parent::configure($configuration);
$this->classElementTypes = [];
foreach ($this->configuration['elements'] as $elementType => $spacing) {
$this->classElementTypes[$elementType] = $spacing;
}
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Class, trait and interface elements must be separated with one or none blank line.',
[
new CodeSample(
'<?php
final class Sample
{
protected function foo()
{
}
protected function bar()
{
}
}
'
),
new CodeSample(
'<?php
class Sample
{private $a; // foo
/** second in a hour */
private $b;
}
',
['elements' => ['property' => self::SPACING_ONE]]
),
new CodeSample(
'<?php
class Sample
{
const A = 1;
/** seconds in some hours */
const B = 3600;
}
',
['elements' => ['const' => self::SPACING_ONE]]
),
new CodeSample(
'<?php
class Sample
{
/** @var int */
const SECOND = 1;
/** @var int */
const MINUTE = 60;
const HOUR = 3600;
const DAY = 86400;
}
',
['elements' => ['const' => self::SPACING_ONLY_IF_META]]
),
new VersionSpecificCodeSample(
'<?php
class Sample
{
public $a;
#[SetUp]
public $b;
/** @var string */
public $c;
/** @internal */
#[Assert\String()]
public $d;
public $e;
}
',
new VersionSpecification(80000),
['elements' => ['property' => self::SPACING_ONLY_IF_META]]
),
]
);
}
public function getPriority(): int
{
return 55;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound(Token::getClassyTokenKinds());
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($this->getElementsByClass($tokens) as $class) {
$elements = $class['elements'];
$elementCount = \count($elements);
if (0 === $elementCount) {
continue;
}
if (isset($this->classElementTypes[$elements[0]['type']])) {
$this->fixSpaceBelowClassElement($tokens, $class);
$this->fixSpaceAboveClassElement($tokens, $class, 0);
}
for ($index = 1; $index < $elementCount; ++$index) {
if (isset($this->classElementTypes[$elements[$index]['type']])) {
$this->fixSpaceAboveClassElement($tokens, $class, $index);
}
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('elements', 'Dictionary of `const|method|property|trait_import|case` => `none|one|only_if_meta` values.'))
->setAllowedTypes(['array'])
->setAllowedValues([static function (array $option): bool {
foreach ($option as $type => $spacing) {
$supportedTypes = ['const', 'method', 'property', 'trait_import', 'case'];
if (!\in_array($type, $supportedTypes, true)) {
throw new InvalidOptionsException(
sprintf(
'Unexpected element type, expected any of "%s", got "%s".',
implode('", "', $supportedTypes),
\gettype($type).'#'.$type
)
);
}
$supportedSpacings = [self::SPACING_NONE, self::SPACING_ONE, self::SPACING_ONLY_IF_META];
if (!\in_array($spacing, $supportedSpacings, true)) {
throw new InvalidOptionsException(
sprintf(
'Unexpected spacing for element type "%s", expected any of "%s", got "%s".',
$spacing,
implode('", "', $supportedSpacings),
\is_object($spacing) ? \get_class($spacing) : (null === $spacing ? 'null' : \gettype($spacing).'#'.$spacing)
)
);
}
}
return true;
}])
->setDefault([
'const' => self::SPACING_ONE,
'method' => self::SPACING_ONE,
'property' => self::SPACING_ONE,
'trait_import' => self::SPACING_NONE,
'case' => self::SPACING_NONE,
])
->getOption(),
]);
}
private function fixSpaceAboveClassElement(Tokens $tokens, array $class, int $elementIndex): void
{
$element = $class['elements'][$elementIndex];
$elementAboveEnd = isset($class['elements'][$elementIndex + 1]) ? $class['elements'][$elementIndex + 1]['end'] : 0;
$nonWhiteAbove = $tokens->getPrevNonWhitespace($element['start']);
if ($nonWhiteAbove === $class['open']) {
$this->correctLineBreaks($tokens, $nonWhiteAbove, $element['start'], 1);
return;
}
if ($tokens[$nonWhiteAbove]->isGivenKind(T_COMMENT)) {
if ($elementAboveEnd === $nonWhiteAbove) {
$this->correctLineBreaks($tokens, $nonWhiteAbove, $element['start'], $this->determineRequiredLineCount($tokens, $class, $elementIndex));
return;
}
if ($tokens[$nonWhiteAbove + 1]->isWhitespace() && substr_count($tokens[$nonWhiteAbove + 1]->getContent(), "\n") > 1) {
$this->correctLineBreaks($tokens, $nonWhiteAbove, $element['start'], 2);
return;
}
if (
1 === $element['start'] - $nonWhiteAbove
|| $tokens[$nonWhiteAbove - 1]->isWhitespace() && substr_count($tokens[$nonWhiteAbove - 1]->getContent(), "\n") > 0
|| $tokens[$nonWhiteAbove + 1]->isWhitespace() && substr_count($tokens[$nonWhiteAbove + 1]->getContent(), "\n") > 0
) {
$this->correctLineBreaks($tokens, $nonWhiteAbove, $element['start'], 1);
$nonWhiteAbove = $this->findCommentBlockStart($tokens, $nonWhiteAbove, $elementAboveEnd);
$nonWhiteAboveComment = $tokens->getPrevNonWhitespace($nonWhiteAbove);
$this->correctLineBreaks($tokens, $nonWhiteAboveComment, $nonWhiteAbove, $nonWhiteAboveComment === $class['open'] ? 1 : 2);
} else {
$this->correctLineBreaks($tokens, $nonWhiteAbove, $element['start'], 2);
}
return;
}
if ($tokens[$nonWhiteAbove]->isGivenKind([T_DOC_COMMENT, CT::T_ATTRIBUTE_CLOSE])) {
$this->correctLineBreaks($tokens, $nonWhiteAbove, $element['start'], 1);
$nonWhiteAbove = $this->findCommentBlockStart($tokens, $nonWhiteAbove, $elementAboveEnd);
$nonWhiteAboveComment = $tokens->getPrevNonWhitespace($nonWhiteAbove);
$this->correctLineBreaks($tokens, $nonWhiteAboveComment, $nonWhiteAbove, $nonWhiteAboveComment === $class['open'] ? 1 : 2);
return;
}
$this->correctLineBreaks($tokens, $nonWhiteAbove, $element['start'], $this->determineRequiredLineCount($tokens, $class, $elementIndex));
}
private function determineRequiredLineCount(Tokens $tokens, array $class, int $elementIndex): int
{
$type = $class['elements'][$elementIndex]['type'];
$spacing = $this->classElementTypes[$type];
if (self::SPACING_ONE === $spacing) {
return 2;
}
if (self::SPACING_NONE === $spacing) {
if (!isset($class['elements'][$elementIndex + 1])) {
return 1;
}
$aboveElement = $class['elements'][$elementIndex + 1];
if ($aboveElement['type'] !== $type) {
return 2;
}
$aboveElementDocCandidateIndex = $tokens->getPrevNonWhitespace($aboveElement['start']);
return $tokens[$aboveElementDocCandidateIndex]->isGivenKind([T_DOC_COMMENT, CT::T_ATTRIBUTE_CLOSE]) ? 2 : 1;
}
if (self::SPACING_ONLY_IF_META === $spacing) {
$aboveElementDocCandidateIndex = $tokens->getPrevNonWhitespace($class['elements'][$elementIndex]['start']);
return $tokens[$aboveElementDocCandidateIndex]->isGivenKind([T_DOC_COMMENT, CT::T_ATTRIBUTE_CLOSE]) ? 2 : 1;
}
throw new \RuntimeException(sprintf('Unknown spacing "%s".', $spacing));
}
private function fixSpaceBelowClassElement(Tokens $tokens, array $class): void
{
$element = $class['elements'][0];
if ($class['close'] === $tokens->getNextNonWhitespace($element['end'])) {
$this->correctLineBreaks($tokens, $element['end'], $class['close'], 1);
}
}
private function correctLineBreaks(Tokens $tokens, int $startIndex, int $endIndex, int $reqLineCount): void
{
$lineEnding = $this->whitespacesConfig->getLineEnding();
++$startIndex;
$numbOfWhiteTokens = $endIndex - $startIndex;
if (0 === $numbOfWhiteTokens) {
$tokens->insertAt($startIndex, new Token([T_WHITESPACE, str_repeat($lineEnding, $reqLineCount)]));
return;
}
$lineBreakCount = $this->getLineBreakCount($tokens, $startIndex, $endIndex);
if ($reqLineCount === $lineBreakCount) {
return;
}
if ($lineBreakCount < $reqLineCount) {
$tokens[$startIndex] = new Token([
T_WHITESPACE,
str_repeat($lineEnding, $reqLineCount - $lineBreakCount).$tokens[$startIndex]->getContent(),
]);
return;
}
if (1 === $numbOfWhiteTokens) {
$tokens[$startIndex] = new Token([
T_WHITESPACE,
Preg::replace('/\r\n|\n/', '', $tokens[$startIndex]->getContent(), $lineBreakCount - $reqLineCount),
]);
return;
}
$toReplaceCount = $lineBreakCount - $reqLineCount;
for ($i = $startIndex; $i < $endIndex && $toReplaceCount > 0; ++$i) {
$tokenLineCount = substr_count($tokens[$i]->getContent(), "\n");
if ($tokenLineCount > 0) {
$tokens[$i] = new Token([
T_WHITESPACE,
Preg::replace('/\r\n|\n/', '', $tokens[$i]->getContent(), min($toReplaceCount, $tokenLineCount)),
]);
$toReplaceCount -= $tokenLineCount;
}
}
}
private function getLineBreakCount(Tokens $tokens, int $startIndex, int $endIndex): int
{
$lineCount = 0;
for ($i = $startIndex; $i < $endIndex; ++$i) {
$lineCount += substr_count($tokens[$i]->getContent(), "\n");
}
return $lineCount;
}
private function findCommentBlockStart(Tokens $tokens, int $start, int $elementAboveEnd): int
{
for ($i = $start; $i > $elementAboveEnd; --$i) {
if ($tokens[$i]->isGivenKind(CT::T_ATTRIBUTE_CLOSE)) {
$start = $i = $tokens->findBlockStart(Tokens::BLOCK_TYPE_ATTRIBUTE, $i);
continue;
}
if ($tokens[$i]->isComment()) {
$start = $i;
continue;
}
if (!$tokens[$i]->isWhitespace() || $this->getLineBreakCount($tokens, $i, $i + 1) > 1) {
break;
}
}
return $start;
}
private function getElementsByClass(Tokens $tokens): \Generator
{
$tokensAnalyzer = new TokensAnalyzer($tokens);
$class = $classIndex = false;
$elements = $tokensAnalyzer->getClassyElements();
for (end($elements);; prev($elements)) {
$index = key($elements);
if (null === $index) {
break;
}
$element = current($elements);
$element['index'] = $index;
if ($element['classIndex'] !== $classIndex) {
if (false !== $class) {
yield $class;
}
$classIndex = $element['classIndex'];
$classOpen = $tokens->getNextTokenOfKind($classIndex, ['{']);
$classEnd = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $classOpen);
$class = [
'index' => $classIndex,
'open' => $classOpen,
'close' => $classEnd,
'elements' => [],
];
}
unset($element['classIndex']);
$element['start'] = $this->getFirstTokenIndexOfClassElement($tokens, $class, $element);
$element['end'] = $this->getLastTokenIndexOfClassElement($tokens, $class, $element, $tokensAnalyzer);
$class['elements'][] = $element;
}
if (false !== $class) {
yield $class;
}
}
private function getFirstTokenIndexOfClassElement(Tokens $tokens, array $class, array $element): int
{
$modifierTypes = [T_PRIVATE, T_PROTECTED, T_PUBLIC, T_ABSTRACT, T_FINAL, T_STATIC, T_STRING, T_NS_SEPARATOR, T_VAR, CT::T_NULLABLE_TYPE, CT::T_ARRAY_TYPEHINT, CT::T_TYPE_ALTERNATION, CT::T_TYPE_INTERSECTION];
if (\defined('T_READONLY')) {
$modifierTypes[] = T_READONLY;
}
$firstElementAttributeIndex = $element['index'];
do {
$nonWhiteAbove = $tokens->getPrevMeaningfulToken($firstElementAttributeIndex);
if (null !== $nonWhiteAbove && $tokens[$nonWhiteAbove]->isGivenKind($modifierTypes)) {
$firstElementAttributeIndex = $nonWhiteAbove;
} else {
break;
}
} while ($firstElementAttributeIndex > $class['open']);
return $firstElementAttributeIndex;
}
private function getLastTokenIndexOfClassElement(Tokens $tokens, array $class, array $element, TokensAnalyzer $tokensAnalyzer): int
{
if ('method' === $element['type'] && !$tokens[$class['index']]->isGivenKind(T_INTERFACE)) {
$attributes = $tokensAnalyzer->getMethodAttributes($element['index']);
if (true === $attributes['abstract']) {
$elementEndIndex = $tokens->getNextTokenOfKind($element['index'], [';']);
} else {
$elementEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $tokens->getNextTokenOfKind($element['index'], ['{']));
}
} elseif ('trait_import' === $element['type']) {
$elementEndIndex = $element['index'];
do {
$elementEndIndex = $tokens->getNextMeaningfulToken($elementEndIndex);
} while ($tokens[$elementEndIndex]->isGivenKind([T_STRING, T_NS_SEPARATOR]) || $tokens[$elementEndIndex]->equals(','));
if (!$tokens[$elementEndIndex]->equals(';')) {
$elementEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $tokens->getNextTokenOfKind($element['index'], ['{']));
}
} else {
$elementEndIndex = $tokens->getNextTokenOfKind($element['index'], [';']);
}
$singleLineElement = true;
for ($i = $element['index'] + 1; $i < $elementEndIndex; ++$i) {
if (str_contains($tokens[$i]->getContent(), "\n")) {
$singleLineElement = false;
break;
}
}
if ($singleLineElement) {
while (true) {
$nextToken = $tokens[$elementEndIndex + 1];
if (($nextToken->isComment() || $nextToken->isWhitespace()) && !str_contains($nextToken->getContent(), "\n")) {
++$elementEndIndex;
} else {
break;
}
}
if ($tokens[$elementEndIndex]->isWhitespace()) {
$elementEndIndex = $tokens->getPrevNonWhitespace($elementEndIndex);
}
}
return $elementEndIndex;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ClassNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Tokens;
final class OrderedTraitsFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Trait `use` statements must be sorted alphabetically.',
[
new CodeSample("<?php class Foo { \nuse Z; use A; }\n"),
],
null,
'Risky when depending on order of the imports.'
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(CT::T_USE_TRAIT);
}
public function isRisky(): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($this->findUseStatementsGroups($tokens) as $uses) {
$this->sortUseStatements($tokens, $uses);
}
}
private function findUseStatementsGroups(Tokens $tokens): iterable
{
$uses = [];
for ($index = 1, $max = \count($tokens); $index < $max; ++$index) {
$token = $tokens[$index];
if ($token->isWhitespace() || $token->isComment()) {
continue;
}
if (!$token->isGivenKind(CT::T_USE_TRAIT)) {
if (\count($uses) > 0) {
yield $uses;
$uses = [];
}
continue;
}
$startIndex = $tokens->getNextNonWhitespace($tokens->getPrevMeaningfulToken($index));
$endIndex = $tokens->getNextTokenOfKind($index, [';', '{']);
if ($tokens[$endIndex]->equals('{')) {
$endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $endIndex);
}
$use = [];
for ($i = $startIndex; $i <= $endIndex; ++$i) {
$use[] = $tokens[$i];
}
$uses[$startIndex] = Tokens::fromArray($use);
$index = $endIndex;
}
}
private function sortUseStatements(Tokens $tokens, array $uses): void
{
foreach ($uses as $use) {
$this->sortMultipleTraitsInStatement($use);
}
$this->sort($tokens, $uses);
}
private function sortMultipleTraitsInStatement(Tokens $use): void
{
$traits = [];
$indexOfName = null;
$name = [];
for ($index = 0, $max = \count($use); $index < $max; ++$index) {
$token = $use[$index];
if ($token->isGivenKind([T_STRING, T_NS_SEPARATOR])) {
$name[] = $token;
if (null === $indexOfName) {
$indexOfName = $index;
}
continue;
}
if ($token->equalsAny([',', ';', '{'])) {
$traits[$indexOfName] = Tokens::fromArray($name);
$name = [];
$indexOfName = null;
}
if ($token->equals('{')) {
$index = $use->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $index);
}
}
$this->sort($use, $traits);
}
private function sort(Tokens $tokens, array $elements): void
{
$toTraitName = static function (Tokens $use): string {
$string = '';
foreach ($use as $token) {
if ($token->equalsAny([';', '{'])) {
break;
}
if ($token->isGivenKind([T_NS_SEPARATOR, T_STRING])) {
$string .= $token->getContent();
}
}
return ltrim($string, '\\');
};
$sortedElements = $elements;
uasort($sortedElements, static function (Tokens $useA, Tokens $useB) use ($toTraitName): int {
return strcasecmp($toTraitName($useA), $toTraitName($useB));
});
$sortedElements = array_combine(
array_keys($elements),
array_values($sortedElements)
);
foreach (array_reverse($sortedElements, true) as $index => $tokensToInsert) {
$tokens->overrideRange(
$index,
$index + \count($elements[$index]) - 1,
$tokensToInsert
);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ClassNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Analyzer\NamespacesAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class SelfAccessorFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Inside class or interface element `self` should be preferred to the class name itself.',
[
new CodeSample(
'<?php
class Sample
{
const BAZ = 1;
const BAR = Sample::BAZ;
public function getBar()
{
return Sample::BAR;
}
}
'
),
],
null,
'Risky when using dynamic calls like get_called_class() or late static binding.'
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_CLASS, T_INTERFACE]);
}
public function isRisky(): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$tokensAnalyzer = new TokensAnalyzer($tokens);
foreach ((new NamespacesAnalyzer())->getDeclarations($tokens) as $namespace) {
for ($index = $namespace->getScopeStartIndex(); $index < $namespace->getScopeEndIndex(); ++$index) {
if (!$tokens[$index]->isGivenKind([T_CLASS, T_INTERFACE]) || $tokensAnalyzer->isAnonymousClass($index)) {
continue;
}
$nameIndex = $tokens->getNextTokenOfKind($index, [[T_STRING]]);
$startIndex = $tokens->getNextTokenOfKind($nameIndex, ['{']);
$endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $startIndex);
$name = $tokens[$nameIndex]->getContent();
$this->replaceNameOccurrences($tokens, $namespace->getFullName(), $name, $startIndex, $endIndex);
$index = $endIndex;
}
}
}
private function replaceNameOccurrences(Tokens $tokens, string $namespace, string $name, int $startIndex, int $endIndex): void
{
$tokensAnalyzer = new TokensAnalyzer($tokens);
$insideMethodSignatureUntil = null;
for ($i = $startIndex; $i < $endIndex; ++$i) {
if ($i === $insideMethodSignatureUntil) {
$insideMethodSignatureUntil = null;
}
$token = $tokens[$i];
if ($token->isGivenKind(T_CLASS) && $tokensAnalyzer->isAnonymousClass($i)) {
$i = $tokens->getNextTokenOfKind($i, ['{']);
$i = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $i);
continue;
}
if ($token->isGivenKind(T_FUNCTION)) {
$i = $tokens->getNextTokenOfKind($i, ['(']);
$insideMethodSignatureUntil = $tokens->getNextTokenOfKind($i, ['{', ';']);
continue;
}
if (!$token->equals([T_STRING, $name], false)) {
continue;
}
$nextToken = $tokens[$tokens->getNextMeaningfulToken($i)];
if ($nextToken->isGivenKind(T_NS_SEPARATOR)) {
continue;
}
$classStartIndex = $i;
$prevToken = $tokens[$tokens->getPrevMeaningfulToken($i)];
if ($prevToken->isGivenKind(T_NS_SEPARATOR)) {
$classStartIndex = $this->getClassStart($tokens, $i, $namespace);
if (null === $classStartIndex) {
continue;
}
$prevToken = $tokens[$tokens->getPrevMeaningfulToken($classStartIndex)];
}
if ($prevToken->isGivenKind(T_STRING) || $prevToken->isObjectOperator()) {
continue;
}
if (
$prevToken->isGivenKind([T_INSTANCEOF, T_NEW])
|| $nextToken->isGivenKind(T_PAAMAYIM_NEKUDOTAYIM)
|| (
null !== $insideMethodSignatureUntil
&& $i < $insideMethodSignatureUntil
&& $prevToken->equalsAny(['(', ',', [CT::T_TYPE_COLON], [CT::T_NULLABLE_TYPE]])
)
) {
for ($j = $classStartIndex; $j < $i; ++$j) {
$tokens->clearTokenAndMergeSurroundingWhitespace($j);
}
$tokens[$i] = new Token([T_STRING, 'self']);
}
}
}
private function getClassStart(Tokens $tokens, int $index, string $namespace): ?int
{
$namespace = ('' !== $namespace ? '\\'.$namespace : '').'\\';
foreach (array_reverse(Preg::split('/(\\\\)/', $namespace, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE)) as $piece) {
$index = $tokens->getPrevMeaningfulToken($index);
if ('\\' === $piece) {
if (!$tokens[$index]->isGivenKind(T_NS_SEPARATOR)) {
return null;
}
} elseif (!$tokens[$index]->equals([T_STRING, $piece], false)) {
return null;
}
}
return $index;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ClassNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class FinalPublicMethodForAbstractClassFixer extends AbstractFixer
{
private array $magicMethods = [
'__construct' => true,
'__destruct' => true,
'__call' => true,
'__callstatic' => true,
'__get' => true,
'__set' => true,
'__isset' => true,
'__unset' => true,
'__sleep' => true,
'__wakeup' => true,
'__tostring' => true,
'__invoke' => true,
'__set_state' => true,
'__clone' => true,
'__debuginfo' => true,
];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'All `public` methods of `abstract` classes should be `final`.',
[
new CodeSample(
'<?php
abstract class AbstractMachine
{
public function start()
{}
}
'
),
],
'Enforce API encapsulation in an inheritance architecture. '
.'If you want to override a method, use the Template method pattern.',
'Risky when overriding `public` methods of `abstract` classes.'
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAllTokenKindsFound([T_CLASS, T_ABSTRACT, T_PUBLIC, T_FUNCTION]);
}
public function isRisky(): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$classes = array_keys($tokens->findGivenKind(T_CLASS));
while ($classIndex = array_pop($classes)) {
$prevToken = $tokens[$tokens->getPrevMeaningfulToken($classIndex)];
if (!$prevToken->isGivenKind(T_ABSTRACT)) {
continue;
}
$classOpen = $tokens->getNextTokenOfKind($classIndex, ['{']);
$classClose = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $classOpen);
$this->fixClass($tokens, $classOpen, $classClose);
}
}
private function fixClass(Tokens $tokens, int $classOpenIndex, int $classCloseIndex): void
{
for ($index = $classCloseIndex - 1; $index > $classOpenIndex; --$index) {
if ($tokens[$index]->equals('}')) {
$index = $tokens->findBlockStart(Tokens::BLOCK_TYPE_CURLY_BRACE, $index);
continue;
}
if (!$tokens[$index]->isGivenKind(T_PUBLIC)) {
continue;
}
$nextIndex = $tokens->getNextMeaningfulToken($index);
$nextToken = $tokens[$nextIndex];
if ($nextToken->isGivenKind(T_STATIC)) {
$nextIndex = $tokens->getNextMeaningfulToken($nextIndex);
$nextToken = $tokens[$nextIndex];
}
if (!$nextToken->isGivenKind(T_FUNCTION)) {
continue;
}
$nextIndex = $tokens->getNextMeaningfulToken($nextIndex);
$nextToken = $tokens[$nextIndex];
if (isset($this->magicMethods[strtolower($nextToken->getContent())])) {
continue;
}
$prevIndex = $tokens->getPrevMeaningfulToken($index);
$prevToken = $tokens[$prevIndex];
if ($prevToken->isGivenKind(T_STATIC)) {
$index = $prevIndex;
$prevIndex = $tokens->getPrevMeaningfulToken($index);
$prevToken = $tokens[$prevIndex];
}
if ($prevToken->isGivenKind([T_ABSTRACT, T_FINAL])) {
$index = $prevIndex;
continue;
}
$tokens->insertAt(
$index,
[
new Token([T_FINAL, 'final']),
new Token([T_WHITESPACE, ' ']),
]
);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ClassNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class SelfStaticAccessorFixer extends AbstractFixer
{
private $tokensAnalyzer;
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Inside a `final` class or anonymous class `self` should be preferred to `static`.',
[
new CodeSample(
'<?php
final class Sample
{
private static $A = 1;
public function getBar()
{
return static::class.static::test().static::$A;
}
private static function test()
{
return \'test\';
}
}
'
),
new CodeSample(
'<?php
final class Foo
{
public function bar()
{
return new static();
}
}
'
),
new CodeSample(
'<?php
final class Foo
{
public function isBar()
{
return $foo instanceof static;
}
}
'
),
new CodeSample(
'<?php
$a = new class() {
public function getBar()
{
return static::class;
}
};
'
),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAllTokenKindsFound([T_CLASS, T_STATIC]) && $tokens->isAnyTokenKindsFound([T_DOUBLE_COLON, T_NEW, T_INSTANCEOF]);
}
public function getPriority(): int
{
return -10;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$this->tokensAnalyzer = new TokensAnalyzer($tokens);
$classIndex = $tokens->getNextTokenOfKind(0, [[T_CLASS]]);
while (null !== $classIndex) {
if (
$this->tokensAnalyzer->isAnonymousClass($classIndex)
|| $tokens[$tokens->getPrevMeaningfulToken($classIndex)]->isGivenKind(T_FINAL)
) {
$classIndex = $this->fixClass($tokens, $classIndex);
}
$classIndex = $tokens->getNextTokenOfKind($classIndex, [[T_CLASS]]);
}
}
private function fixClass(Tokens $tokens, int $index): int
{
$index = $tokens->getNextTokenOfKind($index, ['{']);
$classOpenCount = 1;
while ($classOpenCount > 0) {
++$index;
if ($tokens[$index]->equals('{')) {
++$classOpenCount;
continue;
}
if ($tokens[$index]->equals('}')) {
--$classOpenCount;
continue;
}
if ($tokens[$index]->isGivenKind(T_FUNCTION)) {
if ($this->tokensAnalyzer->isLambda($index)) {
$index = $tokens->getNextTokenOfKind($index, ['{']);
$openCount = 1;
do {
$index = $tokens->getNextTokenOfKind($index, ['}', '{', [T_CLASS]]);
if ($tokens[$index]->equals('}')) {
--$openCount;
} elseif ($tokens[$index]->equals('{')) {
++$openCount;
} else {
$index = $this->fixClass($tokens, $index);
}
} while ($openCount > 0);
}
continue;
}
if ($tokens[$index]->isGivenKind([T_NEW, T_INSTANCEOF])) {
$index = $tokens->getNextMeaningfulToken($index);
if ($tokens[$index]->isGivenKind(T_STATIC)) {
$tokens[$index] = new Token([T_STRING, 'self']);
}
continue;
}
if (!$tokens[$index]->isGivenKind(T_STATIC)) {
continue;
}
$staticIndex = $index;
$index = $tokens->getNextMeaningfulToken($index);
if (!$tokens[$index]->isGivenKind(T_DOUBLE_COLON)) {
continue;
}
$tokens[$staticIndex] = new Token([T_STRING, 'self']);
}
return $index;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ClassNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class VisibilityRequiredFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Visibility MUST be declared on all properties and methods; `abstract` and `final` MUST be declared before the visibility; `static` MUST be declared after the visibility.',
[
new CodeSample(
'<?php
class Sample
{
var $a;
static protected $var_foo2;
function A()
{
}
}
'
),
new CodeSample(
'<?php
class Sample
{
const SAMPLE = 1;
}
',
['elements' => ['const']]
),
]
);
}
public function getPriority(): int
{
return 56;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound(Token::getClassyTokenKinds());
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('elements', 'The structural elements to fix (PHP >= 7.1 required for `const`).'))
->setAllowedTypes(['array'])
->setAllowedValues([new AllowedValueSubset(['property', 'method', 'const'])])
->setDefault(['property', 'method', 'const'])
->getOption(),
]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$tokensAnalyzer = new TokensAnalyzer($tokens);
$propertyTypeDeclarationKinds = [T_STRING, T_NS_SEPARATOR, CT::T_NULLABLE_TYPE, CT::T_ARRAY_TYPEHINT, CT::T_TYPE_ALTERNATION, CT::T_TYPE_INTERSECTION];
if (\defined('T_READONLY')) {
$propertyReadOnlyType = T_READONLY;
$propertyTypeDeclarationKinds[] = T_READONLY;
} else {
$propertyReadOnlyType = -999;
}
$expectedKindsGeneric = [T_ABSTRACT, T_FINAL, T_PRIVATE, T_PROTECTED, T_PUBLIC, T_STATIC, T_VAR];
$expectedKindsPropertyKinds = array_merge($expectedKindsGeneric, $propertyTypeDeclarationKinds);
foreach (array_reverse($tokensAnalyzer->getClassyElements(), true) as $index => $element) {
if (!\in_array($element['type'], $this->configuration['elements'], true)) {
continue;
}
$abstractFinalIndex = null;
$visibilityIndex = null;
$staticIndex = null;
$typeIndex = null;
$readOnlyIndex = null;
$prevIndex = $tokens->getPrevMeaningfulToken($index);
$expectedKinds = 'property' === $element['type']
? $expectedKindsPropertyKinds
: $expectedKindsGeneric
;
while ($tokens[$prevIndex]->isGivenKind($expectedKinds)) {
if ($tokens[$prevIndex]->isGivenKind([T_ABSTRACT, T_FINAL])) {
$abstractFinalIndex = $prevIndex;
} elseif ($tokens[$prevIndex]->isGivenKind(T_STATIC)) {
$staticIndex = $prevIndex;
} elseif ($tokens[$prevIndex]->isGivenKind($propertyReadOnlyType)) {
$readOnlyIndex = $prevIndex;
} elseif ($tokens[$prevIndex]->isGivenKind($propertyTypeDeclarationKinds)) {
$typeIndex = $prevIndex;
} else {
$visibilityIndex = $prevIndex;
}
$prevIndex = $tokens->getPrevMeaningfulToken($prevIndex);
}
if (null !== $typeIndex) {
$index = $typeIndex;
}
if ($tokens[$prevIndex]->equals(',')) {
continue;
}
$swapIndex = $staticIndex ?? $readOnlyIndex;
if (null !== $swapIndex) {
if ($this->isKeywordPlacedProperly($tokens, $swapIndex, $index)) {
$index = $swapIndex;
} else {
$this->moveTokenAndEnsureSingleSpaceFollows($tokens, $swapIndex, $index);
}
}
if (null === $visibilityIndex) {
$tokens->insertAt($index, [new Token([T_PUBLIC, 'public']), new Token([T_WHITESPACE, ' '])]);
} else {
if ($tokens[$visibilityIndex]->isGivenKind(T_VAR)) {
$tokens[$visibilityIndex] = new Token([T_PUBLIC, 'public']);
}
if ($this->isKeywordPlacedProperly($tokens, $visibilityIndex, $index)) {
$index = $visibilityIndex;
} else {
$this->moveTokenAndEnsureSingleSpaceFollows($tokens, $visibilityIndex, $index);
}
}
if (null === $abstractFinalIndex) {
continue;
}
if ($this->isKeywordPlacedProperly($tokens, $abstractFinalIndex, $index)) {
continue;
}
$this->moveTokenAndEnsureSingleSpaceFollows($tokens, $abstractFinalIndex, $index);
}
}
private function isKeywordPlacedProperly(Tokens $tokens, int $keywordIndex, int $comparedIndex): bool
{
return $keywordIndex + 2 === $comparedIndex && ' ' === $tokens[$keywordIndex + 1]->getContent();
}
private function moveTokenAndEnsureSingleSpaceFollows(Tokens $tokens, int $fromIndex, int $toIndex): void
{
$tokens->insertAt($toIndex, [$tokens[$fromIndex], new Token([T_WHITESPACE, ' '])]);
$tokens->clearAt($fromIndex);
if ($tokens[$fromIndex + 1]->isWhitespace()) {
$tokens->clearAt($fromIndex + 1);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ClassNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class SingleTraitInsertPerStatementFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Each trait `use` must be done as single statement.',
[
new CodeSample(
'<?php
final class Example
{
use Foo, Bar;
}
'
),
]
);
}
public function getPriority(): int
{
return 36;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(CT::T_USE_TRAIT);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = \count($tokens) - 1; 1 < $index; --$index) {
if ($tokens[$index]->isGivenKind(CT::T_USE_TRAIT)) {
$candidates = $this->getCandidates($tokens, $index);
if (\count($candidates) > 0) {
$this->fixTraitUse($tokens, $index, $candidates);
}
}
}
}
private function fixTraitUse(Tokens $tokens, int $useTraitIndex, array $candidates): void
{
foreach ($candidates as $commaIndex) {
$inserts = [
new Token([CT::T_USE_TRAIT, 'use']),
new Token([T_WHITESPACE, ' ']),
];
$nextImportStartIndex = $tokens->getNextMeaningfulToken($commaIndex);
if ($tokens[$nextImportStartIndex - 1]->isWhitespace()) {
if (1 === Preg::match('/\R/', $tokens[$nextImportStartIndex - 1]->getContent())) {
array_unshift($inserts, clone $tokens[$useTraitIndex - 1]);
}
$tokens->clearAt($nextImportStartIndex - 1);
}
$tokens[$commaIndex] = new Token(';');
$tokens->insertAt($nextImportStartIndex, $inserts);
}
}
private function getCandidates(Tokens $tokens, int $index): array
{
$indices = [];
$index = $tokens->getNextTokenOfKind($index, [',', ';', '{']);
while (!$tokens[$index]->equals(';')) {
if ($tokens[$index]->equals('{')) {
return [];
}
$indices[] = $index;
$index = $tokens->getNextTokenOfKind($index, [',', ';', '{']);
}
return array_reverse($indices);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ControlStructure;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NoUnneededCurlyBracesFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Removes unneeded curly braces that are superfluous and aren\'t part of a control structure\'s body.',
[
new CodeSample(
'<?php {
echo 1;
}
switch ($b) {
case 1: {
break;
}
}
'
),
new CodeSample(
'<?php
namespace Foo {
function Bar(){}
}
',
['namespaces' => true]
),
]
);
}
public function getPriority(): int
{
return 40;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound('}');
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($this->findCurlyBraceOpen($tokens) as $index) {
if ($this->isOverComplete($tokens, $index)) {
$this->clearOverCompleteBraces($tokens, $index, $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $index));
}
}
if (true === $this->configuration['namespaces']) {
$this->clearIfIsOverCompleteNamespaceBlock($tokens);
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('namespaces', 'Remove unneeded curly braces from bracketed namespaces.'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
]);
}
private function clearOverCompleteBraces(Tokens $tokens, int $openIndex, int $closeIndex): void
{
$tokens->clearTokenAndMergeSurroundingWhitespace($closeIndex);
$tokens->clearTokenAndMergeSurroundingWhitespace($openIndex);
}
private function findCurlyBraceOpen(Tokens $tokens): iterable
{
for ($i = \count($tokens) - 1; $i > 0; --$i) {
if ($tokens[$i]->equals('{')) {
yield $i;
}
}
}
private function isOverComplete(Tokens $tokens, int $index): bool
{
static $include = ['{', '}', [T_OPEN_TAG], ':', ';'];
return $tokens[$tokens->getPrevMeaningfulToken($index)]->equalsAny($include);
}
private function clearIfIsOverCompleteNamespaceBlock(Tokens $tokens): void
{
if (1 !== $tokens->countTokenKind(T_NAMESPACE)) {
return;
}
$index = $tokens->getNextTokenOfKind(0, [[T_NAMESPACE]]);
do {
$index = $tokens->getNextMeaningfulToken($index);
} while ($tokens[$index]->isGivenKind([T_STRING, T_NS_SEPARATOR]));
if (!$tokens[$index]->equals('{')) {
return;
}
$closeIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $index);
$afterCloseIndex = $tokens->getNextMeaningfulToken($closeIndex);
if (null !== $afterCloseIndex && (!$tokens[$afterCloseIndex]->isGivenKind(T_CLOSE_TAG) || null !== $tokens->getNextMeaningfulToken($afterCloseIndex))) {
return;
}
$tokens->clearTokenAndMergeSurroundingWhitespace($closeIndex);
$tokens[$index] = new Token(';');
if ($tokens[$index - 1]->isWhitespace(" \t") && !$tokens[$index - 2]->isComment()) {
$tokens->clearTokenAndMergeSurroundingWhitespace($index - 1);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ControlStructure;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NoAlternativeSyntaxFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Replace control structure alternative syntax to use braces.',
[
new CodeSample(
"<?php\nif(true):echo 't';else:echo 'f';endif;\n"
),
new CodeSample(
"<?php if (\$condition): ?>\nLorem ipsum.\n<?php endif; ?>\n",
['fix_non_monolithic_code' => true]
),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->hasAlternativeSyntax() && (true === $this->configuration['fix_non_monolithic_code'] || $tokens->isMonolithicPhp());
}
public function getPriority(): int
{
return 42;
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('fix_non_monolithic_code', 'Whether to also fix code with inline HTML.'))
->setAllowedTypes(['bool'])
->setDefault(true)
->getOption(),
]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = \count($tokens) - 1; 0 <= $index; --$index) {
$token = $tokens[$index];
$this->fixElseif($index, $token, $tokens);
$this->fixElse($index, $token, $tokens);
$this->fixOpenCloseControls($index, $token, $tokens);
}
}
private function findParenthesisEnd(Tokens $tokens, int $structureTokenIndex): int
{
$nextIndex = $tokens->getNextMeaningfulToken($structureTokenIndex);
$nextToken = $tokens[$nextIndex];
return $nextToken->equals('(')
? $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $nextIndex)
: $structureTokenIndex
;
}
private function fixOpenCloseControls(int $index, Token $token, Tokens $tokens): void
{
if ($token->isGivenKind([T_IF, T_FOREACH, T_WHILE, T_FOR, T_SWITCH, T_DECLARE])) {
$openIndex = $tokens->getNextTokenOfKind($index, ['(']);
$closeIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openIndex);
$afterParenthesisIndex = $tokens->getNextMeaningfulToken($closeIndex);
$afterParenthesis = $tokens[$afterParenthesisIndex];
if (!$afterParenthesis->equals(':')) {
return;
}
$items = [];
if (!$tokens[$afterParenthesisIndex - 1]->isWhitespace()) {
$items[] = new Token([T_WHITESPACE, ' ']);
}
$items[] = new Token('{');
if (!$tokens[$afterParenthesisIndex + 1]->isWhitespace()) {
$items[] = new Token([T_WHITESPACE, ' ']);
}
$tokens->clearAt($afterParenthesisIndex);
$tokens->insertAt($afterParenthesisIndex, $items);
}
if (!$token->isGivenKind([T_ENDIF, T_ENDFOREACH, T_ENDWHILE, T_ENDFOR, T_ENDSWITCH, T_ENDDECLARE])) {
return;
}
$nextTokenIndex = $tokens->getNextMeaningfulToken($index);
$nextToken = $tokens[$nextTokenIndex];
$tokens[$index] = new Token('}');
if ($nextToken->equals(';')) {
$tokens->clearAt($nextTokenIndex);
}
}
private function fixElse(int $index, Token $token, Tokens $tokens): void
{
if (!$token->isGivenKind(T_ELSE)) {
return;
}
$tokenAfterElseIndex = $tokens->getNextMeaningfulToken($index);
$tokenAfterElse = $tokens[$tokenAfterElseIndex];
if (!$tokenAfterElse->equals(':')) {
return;
}
$this->addBraces($tokens, new Token([T_ELSE, 'else']), $index, $tokenAfterElseIndex);
}
private function fixElseif(int $index, Token $token, Tokens $tokens): void
{
if (!$token->isGivenKind(T_ELSEIF)) {
return;
}
$parenthesisEndIndex = $this->findParenthesisEnd($tokens, $index);
$tokenAfterParenthesisIndex = $tokens->getNextMeaningfulToken($parenthesisEndIndex);
$tokenAfterParenthesis = $tokens[$tokenAfterParenthesisIndex];
if (!$tokenAfterParenthesis->equals(':')) {
return;
}
$this->addBraces($tokens, new Token([T_ELSEIF, 'elseif']), $index, $tokenAfterParenthesisIndex);
}
private function addBraces(Tokens $tokens, Token $token, int $index, int $colonIndex): void
{
$items = [
new Token('}'),
new Token([T_WHITESPACE, ' ']),
$token,
];
if (!$tokens[$index + 1]->isWhitespace()) {
$items[] = new Token([T_WHITESPACE, ' ']);
}
$tokens->clearAt($index);
$tokens->insertAt(
$index,
$items
);
$colonIndex += \count($items);
$items = [new Token('{')];
if (!$tokens[$colonIndex + 1]->isWhitespace()) {
$items[] = new Token([T_WHITESPACE, ' ']);
}
$tokens->clearAt($colonIndex);
$tokens->insertAt(
$colonIndex,
$items
);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ControlStructure;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class YodaStyleFixer extends AbstractFixer implements ConfigurableFixerInterface
{
private $candidatesMap;
private $candidateTypesConfiguration;
private $candidateTypes;
public function configure(array $configuration): void
{
parent::configure($configuration);
$this->resolveConfiguration();
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Write conditions in Yoda style (`true`), non-Yoda style (`[\'equal\' => false, \'identical\' => false, \'less_and_greater\' => false]`) or ignore those conditions (`null`) based on configuration.',
[
new CodeSample(
'<?php
if ($a === null) {
echo "null";
}
'
),
new CodeSample(
'<?php
$b = $c != 1; // equal
$a = 1 === $b; // identical
$c = $c > 3; // less than
',
[
'equal' => true,
'identical' => false,
'less_and_greater' => null,
]
),
new CodeSample(
'<?php
return $foo === count($bar);
',
[
'always_move_variable' => true,
]
),
new CodeSample(
'<?php
// Enforce non-Yoda style.
if (null === $a) {
echo "null";
}
',
[
'equal' => false,
'identical' => false,
'less_and_greater' => false,
]
),
]
);
}
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound($this->candidateTypes);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$this->fixTokens($tokens);
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('equal', 'Style for equal (`==`, `!=`) statements.'))
->setAllowedTypes(['bool', 'null'])
->setDefault(true)
->getOption(),
(new FixerOptionBuilder('identical', 'Style for identical (`===`, `!==`) statements.'))
->setAllowedTypes(['bool', 'null'])
->setDefault(true)
->getOption(),
(new FixerOptionBuilder('less_and_greater', 'Style for less and greater than (`<`, `<=`, `>`, `>=`) statements.'))
->setAllowedTypes(['bool', 'null'])
->setDefault(null)
->getOption(),
(new FixerOptionBuilder('always_move_variable', 'Whether variables should always be on non assignable side when applying Yoda style.'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
]);
}
private function findComparisonEnd(Tokens $tokens, int $index): int
{
++$index;
$count = \count($tokens);
while ($index < $count) {
$token = $tokens[$index];
if ($token->isGivenKind([T_WHITESPACE, T_COMMENT, T_DOC_COMMENT])) {
++$index;
continue;
}
if ($this->isOfLowerPrecedence($token)) {
break;
}
$block = Tokens::detectBlockType($token);
if (null === $block) {
++$index;
continue;
}
if (!$block['isStart']) {
break;
}
$index = $tokens->findBlockEnd($block['type'], $index) + 1;
}
$prev = $tokens->getPrevMeaningfulToken($index);
return $tokens[$prev]->isGivenKind(T_CLOSE_TAG) ? $tokens->getPrevMeaningfulToken($prev) : $prev;
}
private function findComparisonStart(Tokens $tokens, int $index): int
{
--$index;
$nonBlockFound = false;
while (0 <= $index) {
$token = $tokens[$index];
if ($token->isGivenKind([T_WHITESPACE, T_COMMENT, T_DOC_COMMENT])) {
--$index;
continue;
}
if ($token->isGivenKind([CT::T_NAMED_ARGUMENT_COLON])) {
break;
}
if ($this->isOfLowerPrecedence($token)) {
break;
}
$block = Tokens::detectBlockType($token);
if (null === $block) {
--$index;
$nonBlockFound = true;
continue;
}
if (
$block['isStart']
|| ($nonBlockFound && Tokens::BLOCK_TYPE_CURLY_BRACE === $block['type'])
) {
break;
}
$index = $tokens->findBlockStart($block['type'], $index) - 1;
}
return $tokens->getNextMeaningfulToken($index);
}
private function fixTokens(Tokens $tokens): Tokens
{
for ($i = \count($tokens) - 1; $i > 1; --$i) {
if ($tokens[$i]->isGivenKind($this->candidateTypes)) {
$yoda = $this->candidateTypesConfiguration[$tokens[$i]->getId()];
} elseif (
($tokens[$i]->equals('<') && \in_array('<', $this->candidateTypes, true))
|| ($tokens[$i]->equals('>') && \in_array('>', $this->candidateTypes, true))
) {
$yoda = $this->candidateTypesConfiguration[$tokens[$i]->getContent()];
} else {
continue;
}
$fixableCompareInfo = $this->getCompareFixableInfo($tokens, $i, $yoda);
if (null === $fixableCompareInfo) {
continue;
}
$i = $this->fixTokensCompare(
$tokens,
$fixableCompareInfo['left']['start'],
$fixableCompareInfo['left']['end'],
$i,
$fixableCompareInfo['right']['start'],
$fixableCompareInfo['right']['end']
);
}
return $tokens;
}
private function fixTokensCompare(
Tokens $tokens,
int $startLeft,
int $endLeft,
int $compareOperatorIndex,
int $startRight,
int $endRight
): int {
$type = $tokens[$compareOperatorIndex]->getId();
$content = $tokens[$compareOperatorIndex]->getContent();
if (\array_key_exists($type, $this->candidatesMap)) {
$tokens[$compareOperatorIndex] = clone $this->candidatesMap[$type];
} elseif (\array_key_exists($content, $this->candidatesMap)) {
$tokens[$compareOperatorIndex] = clone $this->candidatesMap[$content];
}
$right = $this->fixTokensComparePart($tokens, $startRight, $endRight);
$left = $this->fixTokensComparePart($tokens, $startLeft, $endLeft);
for ($i = $startRight; $i <= $endRight; ++$i) {
$tokens->clearAt($i);
}
for ($i = $startLeft; $i <= $endLeft; ++$i) {
$tokens->clearAt($i);
}
$tokens->insertAt($startRight, $left);
$tokens->insertAt($startLeft, $right);
return $startLeft;
}
private function fixTokensComparePart(Tokens $tokens, int $start, int $end): Tokens
{
$newTokens = $tokens->generatePartialCode($start, $end);
$newTokens = $this->fixTokens(Tokens::fromCode(sprintf('<?php %s;', $newTokens)));
$newTokens->clearAt(\count($newTokens) - 1);
$newTokens->clearAt(0);
$newTokens->clearEmptyTokens();
return $newTokens;
}
private function getCompareFixableInfo(Tokens $tokens, int $index, bool $yoda): ?array
{
$left = $this->getLeftSideCompareFixableInfo($tokens, $index);
$right = $this->getRightSideCompareFixableInfo($tokens, $index);
if (!$yoda && $this->isOfLowerPrecedenceAssignment($tokens[$tokens->getNextMeaningfulToken($right['end'])])) {
return null;
}
if ($this->isListStatement($tokens, $left['start'], $left['end']) || $this->isListStatement($tokens, $right['start'], $right['end'])) {
return null;
}
$strict = $this->configuration['always_move_variable'];
$leftSideIsVariable = $this->isVariable($tokens, $left['start'], $left['end'], $strict);
$rightSideIsVariable = $this->isVariable($tokens, $right['start'], $right['end'], $strict);
if (!($leftSideIsVariable ^ $rightSideIsVariable)) {
return null;
}
if (!$strict) {
$leftSideIsVariable = $leftSideIsVariable && !$tokens[$left['start']]->equals('(');
$rightSideIsVariable = $rightSideIsVariable && !$tokens[$right['start']]->equals('(');
}
return ($yoda && !$leftSideIsVariable) || (!$yoda && !$rightSideIsVariable)
? null
: ['left' => $left, 'right' => $right]
;
}
private function getLeftSideCompareFixableInfo(Tokens $tokens, int $index): array
{
return [
'start' => $this->findComparisonStart($tokens, $index),
'end' => $tokens->getPrevMeaningfulToken($index),
];
}
private function getRightSideCompareFixableInfo(Tokens $tokens, int $index): array
{
return [
'start' => $tokens->getNextMeaningfulToken($index),
'end' => $this->findComparisonEnd($tokens, $index),
];
}
private function isListStatement(Tokens $tokens, int $index, int $end): bool
{
for ($i = $index; $i <= $end; ++$i) {
if ($tokens[$i]->isGivenKind([T_LIST, CT::T_DESTRUCTURING_SQUARE_BRACE_OPEN, CT::T_DESTRUCTURING_SQUARE_BRACE_CLOSE])) {
return true;
}
}
return false;
}
private function isOfLowerPrecedence(Token $token): bool
{
static $tokens;
if (null === $tokens) {
$tokens = [
T_BOOLEAN_AND,
T_BOOLEAN_OR,
T_CASE,
T_DOUBLE_ARROW,
T_ECHO,
T_GOTO,
T_LOGICAL_AND,
T_LOGICAL_OR,
T_LOGICAL_XOR,
T_OPEN_TAG,
T_OPEN_TAG_WITH_ECHO,
T_PRINT,
T_RETURN,
T_THROW,
T_COALESCE,
T_YIELD,
];
}
static $otherTokens = [
'&', '|', '^',
'?', ':',
',', ';',
];
return $this->isOfLowerPrecedenceAssignment($token) || $token->isGivenKind($tokens) || $token->equalsAny($otherTokens);
}
private function isOfLowerPrecedenceAssignment(Token $token): bool
{
static $tokens;
if (null === $tokens) {
$tokens = [
T_AND_EQUAL,
T_CONCAT_EQUAL,
T_DIV_EQUAL,
T_MINUS_EQUAL,
T_MOD_EQUAL,
T_MUL_EQUAL,
T_OR_EQUAL,
T_PLUS_EQUAL,
T_POW_EQUAL,
T_SL_EQUAL,
T_SR_EQUAL,
T_XOR_EQUAL,
T_COALESCE_EQUAL,
];
}
return $token->equals('=') || $token->isGivenKind($tokens);
}
private function isVariable(Tokens $tokens, int $start, int $end, bool $strict): bool
{
$tokenAnalyzer = new TokensAnalyzer($tokens);
if ($start === $end) {
return $tokens[$start]->isGivenKind(T_VARIABLE);
}
if ($tokens[$start]->equals('(')) {
return true;
}
if ($strict) {
for ($index = $start; $index <= $end; ++$index) {
if (
$tokens[$index]->isCast()
|| $tokens[$index]->isGivenKind(T_INSTANCEOF)
|| $tokens[$index]->equals('!')
|| $tokenAnalyzer->isBinaryOperator($index)
) {
return false;
}
}
}
$index = $start;
while (
$tokens[$index]->equals('(')
&& $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index) === $end
) {
$index = $tokens->getNextMeaningfulToken($index);
$end = $tokens->getPrevMeaningfulToken($end);
}
$expectString = false;
while ($index <= $end) {
$current = $tokens[$index];
if ($current->isComment() || $current->isWhitespace() || $tokens->isEmptyAt($index)) {
++$index;
continue;
}
if ($index === $end) {
return $current->isGivenKind($expectString ? T_STRING : T_VARIABLE);
}
if ($current->isGivenKind([T_LIST, CT::T_DESTRUCTURING_SQUARE_BRACE_OPEN, CT::T_DESTRUCTURING_SQUARE_BRACE_CLOSE])) {
return false;
}
$nextIndex = $tokens->getNextMeaningfulToken($index);
$next = $tokens[$nextIndex];
if ($current->isGivenKind(T_STRING) && $next->isGivenKind(T_DOUBLE_COLON)) {
$index = $tokens->getNextMeaningfulToken($nextIndex);
continue;
}
if ($current->isGivenKind(T_NS_SEPARATOR) && $next->isGivenKind(T_STRING)) {
$index = $nextIndex;
continue;
}
if ($current->isGivenKind(T_STRING) && $next->isGivenKind(T_NS_SEPARATOR)) {
$index = $nextIndex;
continue;
}
if ($current->isGivenKind([T_STRING, T_VARIABLE]) && $next->isObjectOperator()) {
$index = $tokens->getNextMeaningfulToken($nextIndex);
$expectString = true;
continue;
}
if (
$current->isGivenKind($expectString ? T_STRING : T_VARIABLE)
&& $next->equalsAny(['[', [CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN, '{']])
) {
$index = $tokens->findBlockEnd(
$next->equals('[') ? Tokens::BLOCK_TYPE_INDEX_SQUARE_BRACE : Tokens::BLOCK_TYPE_ARRAY_INDEX_CURLY_BRACE,
$nextIndex
);
if ($index === $end) {
return true;
}
$index = $tokens->getNextMeaningfulToken($index);
if (!$tokens[$index]->equalsAny(['[', [CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN, '{']]) && !$tokens[$index]->isObjectOperator()) {
return false;
}
$index = $tokens->getNextMeaningfulToken($index);
$expectString = true;
continue;
}
if ($strict && $current->isGivenKind([T_STRING, T_VARIABLE]) && $next->equals('(')) {
return false;
}
if ($expectString && $current->isGivenKind(CT::T_DYNAMIC_PROP_BRACE_OPEN)) {
$index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_DYNAMIC_PROP_BRACE, $index);
if ($index === $end) {
return true;
}
$index = $tokens->getNextMeaningfulToken($index);
if (!$tokens[$index]->isObjectOperator()) {
return false;
}
$index = $tokens->getNextMeaningfulToken($index);
$expectString = true;
continue;
}
break;
}
return !$this->isConstant($tokens, $start, $end);
}
private function isConstant(Tokens $tokens, int $index, int $end): bool
{
$expectArrayOnly = false;
$expectNumberOnly = false;
$expectNothing = false;
for (; $index <= $end; ++$index) {
$token = $tokens[$index];
if ($token->isComment() || $token->isWhitespace()) {
continue;
}
if ($expectNothing) {
return false;
}
if ($expectArrayOnly) {
if ($token->equalsAny(['(', ')', [CT::T_ARRAY_SQUARE_BRACE_CLOSE]])) {
continue;
}
return false;
}
if ($token->isGivenKind([T_ARRAY, CT::T_ARRAY_SQUARE_BRACE_OPEN])) {
$expectArrayOnly = true;
continue;
}
if ($expectNumberOnly && !$token->isGivenKind([T_LNUMBER, T_DNUMBER])) {
return false;
}
if ($token->equals('-')) {
$expectNumberOnly = true;
continue;
}
if (
$token->isGivenKind([T_LNUMBER, T_DNUMBER, T_CONSTANT_ENCAPSED_STRING])
|| $token->equalsAny([[T_STRING, 'true'], [T_STRING, 'false'], [T_STRING, 'null']])
) {
$expectNothing = true;
continue;
}
return false;
}
return true;
}
private function resolveConfiguration(): void
{
$candidateTypes = [];
$this->candidatesMap = [];
if (null !== $this->configuration['equal']) {
$candidateTypes[T_IS_EQUAL] = $this->configuration['equal'];
$candidateTypes[T_IS_NOT_EQUAL] = $this->configuration['equal'];
}
if (null !== $this->configuration['identical']) {
$candidateTypes[T_IS_IDENTICAL] = $this->configuration['identical'];
$candidateTypes[T_IS_NOT_IDENTICAL] = $this->configuration['identical'];
}
if (null !== $this->configuration['less_and_greater']) {
$candidateTypes[T_IS_SMALLER_OR_EQUAL] = $this->configuration['less_and_greater'];
$this->candidatesMap[T_IS_SMALLER_OR_EQUAL] = new Token([T_IS_GREATER_OR_EQUAL, '>=']);
$candidateTypes[T_IS_GREATER_OR_EQUAL] = $this->configuration['less_and_greater'];
$this->candidatesMap[T_IS_GREATER_OR_EQUAL] = new Token([T_IS_SMALLER_OR_EQUAL, '<=']);
$candidateTypes['<'] = $this->configuration['less_and_greater'];
$this->candidatesMap['<'] = new Token('>');
$candidateTypes['>'] = $this->configuration['less_and_greater'];
$this->candidatesMap['>'] = new Token('<');
}
$this->candidateTypesConfiguration = $candidateTypes;
$this->candidateTypes = array_keys($candidateTypes);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ControlStructure;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class EmptyLoopConditionFixer extends AbstractFixer implements ConfigurableFixerInterface
{
private const STYLE_FOR = 'for';
private const STYLE_WHILE = 'while';
private const TOKEN_LOOP_KINDS = [T_FOR, T_WHILE];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Empty loop-condition must be in configured style.',
[
new CodeSample("<?php\nfor(;;) {\n foo();\n}\n\ndo {\n foo();\n} while(true); // do while\n"),
new CodeSample("<?php\nwhile(true) {\n foo();\n}\n", ['style' => 'for']),
]
);
}
public function getPriority(): int
{
return 1;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound(self::TOKEN_LOOP_KINDS);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
if (self::STYLE_WHILE === $this->configuration['style']) {
$candidateLoopKinds = [T_FOR, T_WHILE];
$replacement = [new Token([T_WHILE, 'while']), new Token([T_WHITESPACE, ' ']), new Token('('), new Token([T_STRING, 'true']), new Token(')')];
$fixLoop = static function (int $index, int $openIndex, int $endIndex) use ($tokens, $replacement): void {
if (self::isForLoopWithEmptyCondition($tokens, $index, $openIndex, $endIndex)) {
self::clearNotCommentsInRange($tokens, $index, $endIndex);
self::cloneAndInsert($tokens, $index, $replacement);
} elseif (self::isWhileLoopWithEmptyCondition($tokens, $index, $openIndex, $endIndex)) {
$doIndex = self::getDoIndex($tokens, $index);
if (null !== $doIndex) {
self::clearNotCommentsInRange($tokens, $index, $tokens->getNextMeaningfulToken($endIndex));
$tokens->clearAt($doIndex);
self::cloneAndInsert($tokens, $doIndex, $replacement);
}
}
};
} else {
$candidateLoopKinds = [T_WHILE];
$replacement = [new Token([T_FOR, 'for']), new Token('('), new Token(';'), new Token(';'), new Token(')')];
$fixLoop = static function (int $index, int $openIndex, int $endIndex) use ($tokens, $replacement): void {
if (!self::isWhileLoopWithEmptyCondition($tokens, $index, $openIndex, $endIndex)) {
return;
}
$doIndex = self::getDoIndex($tokens, $index);
if (null === $doIndex) {
self::clearNotCommentsInRange($tokens, $index, $endIndex);
self::cloneAndInsert($tokens, $index, $replacement);
} else {
self::clearNotCommentsInRange($tokens, $index, $tokens->getNextMeaningfulToken($endIndex));
$tokens->clearAt($doIndex);
self::cloneAndInsert($tokens, $doIndex, $replacement);
}
};
}
for ($index = $tokens->count() - 1; $index > 0; --$index) {
if ($tokens[$index]->isGivenKind($candidateLoopKinds)) {
$openIndex = $tokens->getNextTokenOfKind($index, ['(']);
$endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openIndex);
$fixLoop($index, $openIndex, $endIndex);
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('style', 'Style of empty loop-condition.'))
->setAllowedTypes(['string'])
->setAllowedValues([self::STYLE_WHILE, self::STYLE_FOR])
->setDefault(self::STYLE_WHILE)
->getOption(),
]);
}
private static function clearNotCommentsInRange(Tokens $tokens, int $indexStart, int $indexEnd): void
{
for ($i = $indexStart; $i <= $indexEnd; ++$i) {
if (!$tokens[$i]->isComment()) {
$tokens->clearTokenAndMergeSurroundingWhitespace($i);
}
}
}
private static function cloneAndInsert(Tokens $tokens, int $index, array $replacement): void
{
$replacementClones = [];
foreach ($replacement as $token) {
$replacementClones[] = clone $token;
}
$tokens->insertAt($index, $replacementClones);
}
private static function getDoIndex(Tokens $tokens, int $index): ?int
{
$endIndex = $tokens->getPrevMeaningfulToken($index);
if (!$tokens[$endIndex]->equals('}')) {
return null;
}
$startIndex = $tokens->findBlockStart(Tokens::BLOCK_TYPE_CURLY_BRACE, $endIndex);
$index = $tokens->getPrevMeaningfulToken($startIndex);
return null === $index || !$tokens[$index]->isGivenKind(T_DO) ? null : $index;
}
private static function isForLoopWithEmptyCondition(Tokens $tokens, int $index, int $openIndex, int $endIndex): bool
{
if (!$tokens[$index]->isGivenKind(T_FOR)) {
return false;
}
$index = $tokens->getNextMeaningfulToken($openIndex);
if (null === $index || !$tokens[$index]->equals(';')) {
return false;
}
$index = $tokens->getNextMeaningfulToken($index);
return null !== $index && $tokens[$index]->equals(';') && $endIndex === $tokens->getNextMeaningfulToken($index);
}
private static function isWhileLoopWithEmptyCondition(Tokens $tokens, int $index, int $openIndex, int $endIndex): bool
{
if (!$tokens[$index]->isGivenKind(T_WHILE)) {
return false;
}
$index = $tokens->getNextMeaningfulToken($openIndex);
return null !== $index && $tokens[$index]->equals([T_STRING, 'true']) && $endIndex === $tokens->getNextMeaningfulToken($index);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ControlStructure;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\SwitchAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\ControlCaseStructuresAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class SwitchCaseSemicolonToColonFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'A case should be followed by a colon and not a semicolon.',
[
new CodeSample(
'<?php
switch ($a) {
case 1;
break;
default;
break;
}
'
),
]
);
}
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_SWITCH);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach (ControlCaseStructuresAnalyzer::findControlStructures($tokens, [T_SWITCH]) as $analysis) {
$default = $analysis->getDefaultAnalysis();
if (null !== $default) {
$this->fixTokenIfNeeded($tokens, $default->getColonIndex());
}
foreach ($analysis->getCases() as $caseAnalysis) {
$this->fixTokenIfNeeded($tokens, $caseAnalysis->getColonIndex());
}
}
}
private function fixTokenIfNeeded(Tokens $tokens, int $index): void
{
if ($tokens[$index]->equals(';')) {
$tokens[$index] = new Token(':');
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ControlStructure;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\WhitespacesAnalyzer;
use PhpCsFixer\Tokenizer\Tokens;
final class ControlStructureContinuationPositionFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
{
public const NEXT_LINE = 'next_line';
public const SAME_LINE = 'same_line';
private const CONTROL_CONTINUATION_TOKENS = [
T_CATCH,
T_ELSE,
T_ELSEIF,
T_FINALLY,
T_WHILE,
];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Control structure continuation keyword must be on the configured line.',
[
new CodeSample(
'<?php
if ($baz == true) {
echo "foo";
}
else {
echo "bar";
}
'
),
new CodeSample(
'<?php
if ($baz == true) {
echo "foo";
} else {
echo "bar";
}
',
['position' => self::NEXT_LINE]
),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound(self::CONTROL_CONTINUATION_TOKENS);
}
public function getPriority(): int
{
return parent::getPriority();
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('position', 'the position of the keyword that continues the control structure.'))
->setAllowedValues([self::NEXT_LINE, self::SAME_LINE])
->setDefault(self::SAME_LINE)
->getOption(),
]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$this->fixControlContinuationBraces($tokens);
}
private function fixControlContinuationBraces(Tokens $tokens): void
{
for ($index = \count($tokens) - 1; 0 < $index; --$index) {
$token = $tokens[$index];
if (!$token->isGivenKind(self::CONTROL_CONTINUATION_TOKENS)) {
continue;
}
$prevIndex = $tokens->getPrevNonWhitespace($index);
$prevToken = $tokens[$prevIndex];
if (!$prevToken->equals('}')) {
continue;
}
if ($token->isGivenKind(T_WHILE)) {
$prevIndex = $tokens->getPrevMeaningfulToken(
$tokens->findBlockStart(Tokens::BLOCK_TYPE_CURLY_BRACE, $prevIndex)
);
if (!$tokens[$prevIndex]->isGivenKind(T_DO)) {
continue;
}
}
$tokens->ensureWhitespaceAtIndex(
$index - 1,
1,
self::NEXT_LINE === $this->configuration['position'] ?
$this->whitespacesConfig->getLineEnding().WhitespacesAnalyzer::detectIndent($tokens, $index)
: ' '
);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ControlStructure;
use PhpCsFixer\AbstractNoUselessElseFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NoSuperfluousElseifFixer extends AbstractNoUselessElseFixer
{
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_ELSE, T_ELSEIF]);
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Replaces superfluous `elseif` with `if`.',
[
new CodeSample("<?php\nif (\$a) {\n return 1;\n} elseif (\$b) {\n return 2;\n}\n"),
]
);
}
public function getPriority(): int
{
return parent::getPriority();
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if ($this->isElseif($tokens, $index) && $this->isSuperfluousElse($tokens, $index)) {
$this->convertElseifToIf($tokens, $index);
}
}
}
private function isElseif(Tokens $tokens, int $index): bool
{
return
$tokens[$index]->isGivenKind(T_ELSEIF)
|| ($tokens[$index]->isGivenKind(T_ELSE) && $tokens[$tokens->getNextMeaningfulToken($index)]->isGivenKind(T_IF))
;
}
private function convertElseifToIf(Tokens $tokens, int $index): void
{
if ($tokens[$index]->isGivenKind(T_ELSE)) {
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
} else {
$tokens[$index] = new Token([T_IF, 'if']);
}
$whitespace = '';
for ($previous = $index - 1; $previous > 0; --$previous) {
$token = $tokens[$previous];
if ($token->isWhitespace() && Preg::match('/(\R\N*)$/', $token->getContent(), $matches)) {
$whitespace = $matches[1];
break;
}
}
if ('' === $whitespace) {
return;
}
$previousToken = $tokens[$index - 1];
if (!$previousToken->isWhitespace()) {
$tokens->insertAt($index, new Token([T_WHITESPACE, $whitespace]));
} elseif (!Preg::match('/\R/', $previousToken->getContent())) {
$tokens[$index - 1] = new Token([T_WHITESPACE, $whitespace]);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ControlStructure;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\SwitchAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\ControlCaseStructuresAnalyzer;
use PhpCsFixer\Tokenizer\Tokens;
final class SwitchCaseSpaceFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Removes extra spaces between colon and case value.',
[
new CodeSample(
'<?php
switch($a) {
case 1 :
break;
default :
return 2;
}
'
),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_SWITCH);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach (ControlCaseStructuresAnalyzer::findControlStructures($tokens, [T_SWITCH]) as $analysis) {
$default = $analysis->getDefaultAnalysis();
if (null !== $default) {
$index = $default->getIndex();
if (!$tokens[$index + 1]->isWhitespace() || !$tokens[$index + 2]->equalsAny([':', ';'])) {
continue;
}
$tokens->clearAt($index + 1);
}
foreach ($analysis->getCases() as $caseAnalysis) {
$colonIndex = $caseAnalysis->getColonIndex();
$valueIndex = $tokens->getPrevNonWhitespace($colonIndex);
if ($valueIndex === $colonIndex - 1 || $tokens[$valueIndex]->isComment()) {
continue;
}
$tokens->clearAt($valueIndex + 1);
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ControlStructure;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class EmptyLoopBodyFixer extends AbstractFixer implements ConfigurableFixerInterface
{
private const STYLE_BRACES = 'braces';
private const STYLE_SEMICOLON = 'semicolon';
private const TOKEN_LOOP_KINDS = [T_FOR, T_FOREACH, T_WHILE];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Empty loop-body must be in configured style.',
[
new CodeSample("<?php while(foo()){}\n"),
new CodeSample(
"<?php while(foo());\n",
[
'style' => 'braces',
]
),
]
);
}
public function getPriority(): int
{
return 39;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound(self::TOKEN_LOOP_KINDS);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
if (self::STYLE_BRACES === $this->configuration['style']) {
$analyzer = new TokensAnalyzer($tokens);
$fixLoop = static function (int $index, int $endIndex) use ($tokens, $analyzer): void {
if ($tokens[$index]->isGivenKind(T_WHILE) && $analyzer->isWhilePartOfDoWhile($index)) {
return;
}
$semiColonIndex = $tokens->getNextMeaningfulToken($endIndex);
if (!$tokens[$semiColonIndex]->equals(';')) {
return;
}
$tokens[$semiColonIndex] = new Token('{');
$tokens->insertAt($semiColonIndex + 1, new Token('}'));
};
} else {
$fixLoop = static function (int $index, int $endIndex) use ($tokens): void {
$braceOpenIndex = $tokens->getNextMeaningfulToken($endIndex);
if (!$tokens[$braceOpenIndex]->equals('{')) {
return;
}
$braceCloseIndex = $tokens->getNextMeaningfulToken($braceOpenIndex);
if (!$tokens[$braceCloseIndex]->equals('}')) {
return;
}
$tokens[$braceOpenIndex] = new Token(';');
$tokens->clearTokenAndMergeSurroundingWhitespace($braceCloseIndex);
};
}
for ($index = $tokens->count() - 1; $index > 0; --$index) {
if ($tokens[$index]->isGivenKind(self::TOKEN_LOOP_KINDS)) {
$endIndex = $tokens->getNextTokenOfKind($index, ['(']);
$endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $endIndex);
$fixLoop($index, $endIndex);
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('style', 'Style of empty loop-bodies.'))
->setAllowedTypes(['string'])
->setAllowedValues([self::STYLE_BRACES, self::STYLE_SEMICOLON])
->setDefault(self::STYLE_SEMICOLON)
->getOption(),
]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ControlStructure;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class SwitchContinueToBreakFixer extends AbstractFixer
{
private array $switchLevels = [];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Switch case must not be ended with `continue` but with `break`.',
[
new CodeSample(
'<?php
switch ($foo) {
case 1:
continue;
}
'
),
new CodeSample(
'<?php
switch ($foo) {
case 1:
while($bar) {
do {
continue 3;
} while(false);
if ($foo + 1 > 3) {
continue;
}
continue 2;
}
}
'
),
]
);
}
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAllTokenKindsFound([T_SWITCH, T_CONTINUE]) && !$tokens->hasAlternativeSyntax();
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$count = \count($tokens);
for ($index = 1; $index < $count - 1; ++$index) {
$index = $this->doFix($tokens, $index, 0, false);
}
}
private function doFix(Tokens $tokens, int $index, int $depth, bool $isInSwitch): int
{
$token = $tokens[$index];
if ($token->isGivenKind([T_FOREACH, T_FOR, T_WHILE])) {
$index = $tokens->getNextTokenOfKind($index, ['(']);
$index = $tokens->getNextTokenOfKind($index, [')']);
$index = $tokens->getNextTokenOfKind($index, ['{', ';', [T_CLOSE_TAG]]);
if (!$tokens[$index]->equals('{')) {
return $index;
}
return $this->fixInLoop($tokens, $index, $depth + 1);
}
if ($token->isGivenKind(T_DO)) {
return $this->fixInLoop($tokens, $tokens->getNextTokenOfKind($index, ['{']), $depth + 1);
}
if ($token->isGivenKind(T_SWITCH)) {
return $this->fixInSwitch($tokens, $index, $depth + 1);
}
if ($token->isGivenKind(T_CONTINUE)) {
return $this->fixContinueWhenActsAsBreak($tokens, $index, $isInSwitch, $depth);
}
return $index;
}
private function fixInSwitch(Tokens $tokens, int $switchIndex, int $depth): int
{
$this->switchLevels[] = $depth;
$openIndex = $tokens->getNextTokenOfKind($switchIndex, ['{']);
$closeIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $openIndex);
for ($index = $openIndex + 1; $index < $closeIndex; ++$index) {
$index = $this->doFix($tokens, $index, $depth, true);
}
array_pop($this->switchLevels);
return $closeIndex;
}
private function fixInLoop(Tokens $tokens, int $openIndex, int $depth): int
{
$openCount = 1;
while (true) {
++$openIndex;
$token = $tokens[$openIndex];
if ($token->equals('{')) {
++$openCount;
continue;
}
if ($token->equals('}')) {
--$openCount;
if (0 === $openCount) {
break;
}
continue;
}
$openIndex = $this->doFix($tokens, $openIndex, $depth, false);
}
return $openIndex;
}
private function fixContinueWhenActsAsBreak(Tokens $tokens, int $continueIndex, bool $isInSwitch, int $depth): int
{
$followingContinueIndex = $tokens->getNextMeaningfulToken($continueIndex);
$followingContinueToken = $tokens[$followingContinueIndex];
if ($isInSwitch && $followingContinueToken->equals(';')) {
$this->replaceContinueWithBreakToken($tokens, $continueIndex);
return $followingContinueIndex;
}
if (!$followingContinueToken->isGivenKind(T_LNUMBER)) {
return $followingContinueIndex;
}
$afterFollowingContinueIndex = $tokens->getNextMeaningfulToken($followingContinueIndex);
if (!$tokens[$afterFollowingContinueIndex]->equals(';')) {
return $afterFollowingContinueIndex;
}
$jump = $followingContinueToken->getContent();
$jump = str_replace('_', '', $jump);
if (\strlen($jump) > 2 && 'x' === $jump[1]) {
$jump = hexdec($jump);
} elseif (\strlen($jump) > 2 && 'b' === $jump[1]) {
$jump = bindec($jump);
} elseif (\strlen($jump) > 1 && '0' === $jump[0]) {
$jump = octdec($jump);
} elseif (1 === Preg::match('#^\d+$#', $jump)) {
$jump = (float) $jump;
} else {
return $afterFollowingContinueIndex;
}
if ($jump > PHP_INT_MAX) {
return $afterFollowingContinueIndex;
}
$jump = (int) $jump;
if ($isInSwitch && (1 === $jump || 0 === $jump)) {
$this->replaceContinueWithBreakToken($tokens, $continueIndex);
return $afterFollowingContinueIndex;
}
$jumpDestination = $depth - $jump + 1;
if (\in_array($jumpDestination, $this->switchLevels, true)) {
$this->replaceContinueWithBreakToken($tokens, $continueIndex);
return $afterFollowingContinueIndex;
}
return $afterFollowingContinueIndex;
}
private function replaceContinueWithBreakToken(Tokens $tokens, int $index): void
{
$tokens[$index] = new Token([T_BREAK, 'break']);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ControlStructure;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class SimplifiedIfReturnFixer extends AbstractFixer
{
private array $sequences = [
[
'isNegative' => false,
'sequence' => [
'{', [T_RETURN], [T_STRING, 'true'], ';', '}',
[T_RETURN], [T_STRING, 'false'], ';',
],
],
[
'isNegative' => true,
'sequence' => [
'{', [T_RETURN], [T_STRING, 'false'], ';', '}',
[T_RETURN], [T_STRING, 'true'], ';',
],
],
[
'isNegative' => false,
'sequence' => [
[T_RETURN], [T_STRING, 'true'], ';',
[T_RETURN], [T_STRING, 'false'], ';',
],
],
[
'isNegative' => true,
'sequence' => [
[T_RETURN], [T_STRING, 'false'], ';',
[T_RETURN], [T_STRING, 'true'], ';',
],
],
];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Simplify `if` control structures that return the boolean result of their condition.',
[new CodeSample("<?php\nif (\$foo) { return true; } return false;\n")]
);
}
public function getPriority(): int
{
return 1;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAllTokenKindsFound([T_IF, T_RETURN, T_STRING]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($ifIndex = $tokens->count() - 1; 0 <= $ifIndex; --$ifIndex) {
if (!$tokens[$ifIndex]->isGivenKind([T_IF, T_ELSEIF])) {
continue;
}
if ($tokens[$tokens->getPrevMeaningfulToken($ifIndex)]->equals(')')) {
continue;
}
$startParenthesisIndex = $tokens->getNextTokenOfKind($ifIndex, ['(']);
$endParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $startParenthesisIndex);
$firstCandidateIndex = $tokens->getNextMeaningfulToken($endParenthesisIndex);
foreach ($this->sequences as $sequenceSpec) {
$sequenceFound = $tokens->findSequence($sequenceSpec['sequence'], $firstCandidateIndex);
if (null === $sequenceFound) {
continue;
}
$firstSequenceIndex = key($sequenceFound);
if ($firstSequenceIndex !== $firstCandidateIndex) {
continue;
}
$indicesToClear = array_keys($sequenceFound);
array_pop($indicesToClear);
rsort($indicesToClear);
foreach ($indicesToClear as $index) {
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
}
$newTokens = [
new Token([T_RETURN, 'return']),
new Token([T_WHITESPACE, ' ']),
];
if ($sequenceSpec['isNegative']) {
$newTokens[] = new Token('!');
} else {
$newTokens[] = new Token([T_BOOL_CAST, '(bool)']);
}
$tokens->overrideRange($ifIndex, $ifIndex, $newTokens);
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ControlStructure;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\AlternativeSyntaxAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class ControlStructureBracesFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'The body of each control structure MUST be enclosed within braces.',
[new CodeSample("<?php\nif (foo()) echo 'Hello!';\n")]
);
}
public function isCandidate(Tokens $tokens): bool
{
return true;
}
public function getPriority(): int
{
return 1;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$alternativeSyntaxAnalyzer = new AlternativeSyntaxAnalyzer();
$controlTokens = $this->getControlTokens();
for ($index = $tokens->count() - 1; 0 <= $index; --$index) {
$token = $tokens[$index];
if (!$token->isGivenKind($controlTokens)) {
continue;
}
if (
$token->isGivenKind(T_ELSE)
&& $tokens[$tokens->getNextMeaningfulToken($index)]->isGivenKind(T_IF)
) {
continue;
}
$parenthesisEndIndex = $this->findParenthesisEnd($tokens, $index);
$nextAfterParenthesisEndIndex = $tokens->getNextMeaningfulToken($parenthesisEndIndex);
$tokenAfterParenthesis = $tokens[$nextAfterParenthesisEndIndex];
if ($tokenAfterParenthesis->equalsAny([';', '{', ':'])) {
continue;
}
$statementEndIndex = null;
if ($tokenAfterParenthesis->isGivenKind([T_IF, T_FOR, T_FOREACH, T_SWITCH, T_WHILE])) {
$tokenAfterParenthesisBlockEnd = $tokens->findBlockEnd(
Tokens::BLOCK_TYPE_PARENTHESIS_BRACE,
$tokens->getNextMeaningfulToken($nextAfterParenthesisEndIndex)
);
if ($tokens[$tokens->getNextMeaningfulToken($tokenAfterParenthesisBlockEnd)]->equals(':')) {
$statementEndIndex = $alternativeSyntaxAnalyzer->findAlternativeSyntaxBlockEnd($tokens, $nextAfterParenthesisEndIndex);
$tokenAfterStatementEndIndex = $tokens->getNextMeaningfulToken($statementEndIndex);
if ($tokens[$tokenAfterStatementEndIndex]->equals(';')) {
$statementEndIndex = $tokenAfterStatementEndIndex;
}
}
}
if (null === $statementEndIndex) {
$statementEndIndex = $this->findStatementEnd($tokens, $parenthesisEndIndex);
}
$tokensToInsertAfterStatement = [
new Token([T_WHITESPACE, ' ']),
new Token('}'),
];
if (!$tokens[$statementEndIndex]->equalsAny([';', '}'])) {
array_unshift($tokensToInsertAfterStatement, new Token(';'));
}
$tokens->insertSlices([$statementEndIndex + 1 => $tokensToInsertAfterStatement]);
$tokens->insertSlices([$parenthesisEndIndex + 1 => [
new Token([T_WHITESPACE, ' ']),
new Token('{'),
]]);
}
}
private function findParenthesisEnd(Tokens $tokens, int $structureTokenIndex): int
{
$nextIndex = $tokens->getNextMeaningfulToken($structureTokenIndex);
$nextToken = $tokens[$nextIndex];
if (!$nextToken->equals('(')) {
return $structureTokenIndex;
}
return $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $nextIndex);
}
private function findStatementEnd(Tokens $tokens, int $parenthesisEndIndex): int
{
$nextIndex = $tokens->getNextMeaningfulToken($parenthesisEndIndex);
$nextToken = $tokens[$nextIndex];
if (!$nextToken) {
return $parenthesisEndIndex;
}
if ($nextToken->equals('{')) {
return $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $nextIndex);
}
if ($nextToken->isGivenKind($this->getControlTokens())) {
$parenthesisEndIndex = $this->findParenthesisEnd($tokens, $nextIndex);
$endIndex = $this->findStatementEnd($tokens, $parenthesisEndIndex);
if ($nextToken->isGivenKind([T_IF, T_TRY, T_DO])) {
$openingTokenKind = $nextToken->getId();
while (true) {
$nextIndex = $tokens->getNextMeaningfulToken($endIndex);
$nextToken = isset($nextIndex) ? $tokens[$nextIndex] : null;
if ($nextToken && $nextToken->isGivenKind($this->getControlContinuationTokensForOpeningToken($openingTokenKind))) {
$parenthesisEndIndex = $this->findParenthesisEnd($tokens, $nextIndex);
$endIndex = $this->findStatementEnd($tokens, $parenthesisEndIndex);
if ($nextToken->isGivenKind($this->getFinalControlContinuationTokensForOpeningToken($openingTokenKind))) {
return $endIndex;
}
} else {
break;
}
}
}
return $endIndex;
}
$index = $parenthesisEndIndex;
while (true) {
$token = $tokens[++$index];
if ($token->equals('{')) {
$index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $index);
continue;
}
if ($token->equals(';')) {
return $index;
}
if ($token->isGivenKind(T_CLOSE_TAG)) {
return $tokens->getPrevNonWhitespace($index);
}
}
}
private function getControlTokens(): array
{
static $tokens = [
T_DECLARE,
T_DO,
T_ELSE,
T_ELSEIF,
T_FINALLY,
T_FOR,
T_FOREACH,
T_IF,
T_WHILE,
T_TRY,
T_CATCH,
T_SWITCH,
];
return $tokens;
}
private function getControlContinuationTokensForOpeningToken(int $openingTokenKind): array
{
if (T_IF === $openingTokenKind) {
return [
T_ELSE,
T_ELSEIF,
];
}
if (T_DO === $openingTokenKind) {
return [T_WHILE];
}
if (T_TRY === $openingTokenKind) {
return [
T_CATCH,
T_FINALLY,
];
}
return [];
}
private function getFinalControlContinuationTokensForOpeningToken(int $openingTokenKind): array
{
if (T_IF === $openingTokenKind) {
return [T_ELSE];
}
if (T_TRY === $openingTokenKind) {
return [T_FINALLY];
}
return [];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ControlStructure;
use PhpCsFixer\AbstractNoUselessElseFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Tokens;
final class NoUselessElseFixer extends AbstractNoUselessElseFixer
{
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_ELSE);
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'There should not be useless `else` cases.',
[
new CodeSample("<?php\nif (\$a) {\n return 1;\n} else {\n return 2;\n}\n"),
]
);
}
public function getPriority(): int
{
return parent::getPriority();
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_ELSE)) {
continue;
}
if ($tokens[$tokens->getNextMeaningfulToken($index)]->equalsAny([':', [T_IF]])) {
continue;
}
$this->fixEmptyElse($tokens, $index);
if ($tokens->isEmptyAt($index)) {
continue;
}
if ($this->isSuperfluousElse($tokens, $index)) {
$this->clearElse($tokens, $index);
}
}
}
private function fixEmptyElse(Tokens $tokens, int $index): void
{
$next = $tokens->getNextMeaningfulToken($index);
if ($tokens[$next]->equals('{')) {
$close = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $next);
if (1 === $close - $next) {
$this->clearElse($tokens, $index);
} elseif ($tokens->getNextMeaningfulToken($next) === $close) {
$this->clearElse($tokens, $index);
}
return;
}
$end = $tokens->getNextTokenOfKind($index, [';', [T_CLOSE_TAG]]);
if ($next === $end) {
$this->clearElse($tokens, $index);
}
}
private function clearElse(Tokens $tokens, int $index): void
{
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
$next = $tokens->getNextMeaningfulToken($index);
if (!$tokens[$next]->equals('{')) {
return;
}
$tokens->clearTokenAndMergeSurroundingWhitespace($tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $next));
$tokens->clearTokenAndMergeSurroundingWhitespace($next);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ControlStructure;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class ElseifFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'The keyword `elseif` should be used instead of `else if` so that all control keywords look like single words.',
[new CodeSample("<?php\nif (\$a) {\n} else if (\$b) {\n}\n")]
);
}
public function getPriority(): int
{
return 40;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAllTokenKindsFound([T_IF, T_ELSE]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_ELSE)) {
continue;
}
$ifTokenIndex = $tokens->getNextMeaningfulToken($index);
if (!$tokens[$ifTokenIndex]->isGivenKind(T_IF)) {
continue;
}
$conditionEndBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $tokens->getNextMeaningfulToken($ifTokenIndex));
$afterConditionIndex = $tokens->getNextMeaningfulToken($conditionEndBraceIndex);
if ($tokens[$afterConditionIndex]->equals(':')) {
continue;
}
$tokens->clearAt($index + 1);
$tokens[$index] = new Token([T_ELSEIF, 'elseif']);
$tokens->clearAt($ifTokenIndex);
$beforeIfTokenIndex = $tokens->getPrevNonWhitespace($ifTokenIndex);
if ($tokens[$beforeIfTokenIndex]->isComment() && $tokens[$ifTokenIndex + 1]->isWhitespace()) {
$tokens->clearAt($ifTokenIndex + 1);
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ControlStructure;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerConfiguration\InvalidOptionsForEnvException;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\FixerDefinition\VersionSpecification;
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
use Symfony\Component\OptionsResolver\Options;
final class TrailingCommaInMultilineFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public const ELEMENTS_ARRAYS = 'arrays';
public const ELEMENTS_ARGUMENTS = 'arguments';
public const ELEMENTS_PARAMETERS = 'parameters';
private const MATCH_EXPRESSIONS = 'match';
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Multi-line arrays, arguments list, parameters list and `match` expressions must have a trailing comma.',
[
new CodeSample("<?php\narray(\n 1,\n 2\n);\n"),
new VersionSpecificCodeSample(
<<<'SAMPLE'
<?php
$x = [
'foo',
<<<EOD
bar
EOD
];
SAMPLE
,
new VersionSpecification(70300),
['after_heredoc' => true]
),
new VersionSpecificCodeSample("<?php\nfoo(\n 1,\n 2\n);\n", new VersionSpecification(70300), ['elements' => [self::ELEMENTS_ARGUMENTS]]),
new VersionSpecificCodeSample("<?php\nfunction foo(\n \$x,\n \$y\n)\n{\n}\n", new VersionSpecification(80000), ['elements' => [self::ELEMENTS_PARAMETERS]]),
]
);
}
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_ARRAY, CT::T_ARRAY_SQUARE_BRACE_OPEN, '(']);
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('after_heredoc', 'Whether a trailing comma should also be placed after heredoc end.'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
(new FixerOptionBuilder('elements', sprintf('Where to fix multiline trailing comma (PHP >= 8.0 for `%s` and `%s`).', self::ELEMENTS_PARAMETERS, self::MATCH_EXPRESSIONS)))
->setAllowedTypes(['array'])
->setAllowedValues([new AllowedValueSubset([self::ELEMENTS_ARRAYS, self::ELEMENTS_ARGUMENTS, self::ELEMENTS_PARAMETERS, self::MATCH_EXPRESSIONS])])
->setDefault([self::ELEMENTS_ARRAYS])
->setNormalizer(static function (Options $options, $value) {
if (\PHP_VERSION_ID < 80000) {
foreach ([self::ELEMENTS_PARAMETERS, self::MATCH_EXPRESSIONS] as $option) {
if (\in_array($option, $value, true)) {
throw new InvalidOptionsForEnvException(sprintf('"%s" option can only be enabled with PHP 8.0+.', $option));
}
}
}
return $value;
})
->getOption(),
]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$fixArrays = \in_array(self::ELEMENTS_ARRAYS, $this->configuration['elements'], true);
$fixArguments = \in_array(self::ELEMENTS_ARGUMENTS, $this->configuration['elements'], true);
$fixParameters = \PHP_VERSION_ID >= 80000 && \in_array(self::ELEMENTS_PARAMETERS, $this->configuration['elements'], true);
$fixMatch = \PHP_VERSION_ID >= 80000 && \in_array(self::MATCH_EXPRESSIONS, $this->configuration['elements'], true);
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if (
$fixArrays
&& (
$tokens[$index]->equals('(') && $tokens[$prevIndex]->isGivenKind(T_ARRAY)
|| $tokens[$index]->isGivenKind(CT::T_ARRAY_SQUARE_BRACE_OPEN)
)
) {
$this->fixBlock($tokens, $index);
continue;
}
if (!$tokens[$index]->equals('(')) {
continue;
}
$prevPrevIndex = $tokens->getPrevMeaningfulToken($prevIndex);
if ($fixArguments
&& $tokens[$prevIndex]->equalsAny([']', [T_CLASS], [T_STRING], [T_VARIABLE], [T_STATIC]])
&& !$tokens[$prevPrevIndex]->isGivenKind(T_FUNCTION)
) {
$this->fixBlock($tokens, $index);
continue;
}
if (
$fixParameters
&& (
$tokens[$prevIndex]->isGivenKind(T_STRING) && $tokens[$prevPrevIndex]->isGivenKind(T_FUNCTION)
|| $tokens[$prevIndex]->isGivenKind([T_FN, T_FUNCTION])
)
) {
$this->fixBlock($tokens, $index);
}
if ($fixMatch && $tokens[$prevIndex]->isGivenKind(T_MATCH)) {
$this->fixMatch($tokens, $index);
}
}
}
private function fixBlock(Tokens $tokens, int $startIndex): void
{
$tokensAnalyzer = new TokensAnalyzer($tokens);
if (!$tokensAnalyzer->isBlockMultiline($tokens, $startIndex)) {
return;
}
$blockType = Tokens::detectBlockType($tokens[$startIndex]);
$endIndex = $tokens->findBlockEnd($blockType['type'], $startIndex);
$beforeEndIndex = $tokens->getPrevMeaningfulToken($endIndex);
$beforeEndToken = $tokens[$beforeEndIndex];
if (
$startIndex !== $beforeEndIndex && !$beforeEndToken->equals(',')
&& (true === $this->configuration['after_heredoc'] || !$beforeEndToken->isGivenKind(T_END_HEREDOC))
) {
$tokens->insertAt($beforeEndIndex + 1, new Token(','));
$endToken = $tokens[$endIndex];
if (!$endToken->isComment() && !$endToken->isWhitespace()) {
$tokens->ensureWhitespaceAtIndex($endIndex, 1, ' ');
}
}
}
private function fixMatch(Tokens $tokens, int $index): void
{
$index = $tokens->getNextTokenOfKind($index, ['{']);
$closeIndex = $index;
$isMultiline = false;
$depth = 1;
do {
++$closeIndex;
if ($tokens[$closeIndex]->equals('{')) {
++$depth;
} elseif ($tokens[$closeIndex]->equals('}')) {
--$depth;
} elseif (!$isMultiline && str_contains($tokens[$closeIndex]->getContent(), "\n")) {
$isMultiline = true;
}
} while ($depth > 0);
if (!$isMultiline) {
return;
}
$previousIndex = $tokens->getPrevMeaningfulToken($closeIndex);
if (!$tokens[$previousIndex]->equals(',')) {
$tokens->insertAt($previousIndex + 1, new Token(','));
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ControlStructure;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\BlocksAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class IncludeFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Include/Require and file path should be divided with a single space. File path should not be placed under brackets.',
[
new CodeSample(
'<?php
require ("sample1.php");
require_once "sample2.php";
include "sample3.php";
include_once("sample4.php");
'
),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_REQUIRE, T_REQUIRE_ONCE, T_INCLUDE, T_INCLUDE_ONCE]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$this->clearIncludies($tokens, $this->findIncludies($tokens));
}
private function clearIncludies(Tokens $tokens, array $includies): void
{
$blocksAnalyzer = new BlocksAnalyzer();
foreach ($includies as $includy) {
if ($includy['end'] && !$tokens[$includy['end']]->isGivenKind(T_CLOSE_TAG)) {
$afterEndIndex = $tokens->getNextNonWhitespace($includy['end']);
if (null === $afterEndIndex || !$tokens[$afterEndIndex]->isComment()) {
$tokens->removeLeadingWhitespace($includy['end']);
}
}
$braces = $includy['braces'];
if (null !== $braces) {
$prevIndex = $tokens->getPrevMeaningfulToken($includy['begin']);
$nextIndex = $tokens->getNextMeaningfulToken($braces['close']);
if (!$tokens[$nextIndex]->equalsAny([';', [T_CLOSE_TAG]]) && !$blocksAnalyzer->isBlock($tokens, $prevIndex, $nextIndex)) {
continue;
}
$this->removeWhitespaceAroundIfPossible($tokens, $braces['open']);
$this->removeWhitespaceAroundIfPossible($tokens, $braces['close']);
$tokens->clearTokenAndMergeSurroundingWhitespace($braces['open']);
$tokens->clearTokenAndMergeSurroundingWhitespace($braces['close']);
}
$nextIndex = $tokens->getNonEmptySibling($includy['begin'], 1);
if ($tokens[$nextIndex]->isWhitespace()) {
$tokens[$nextIndex] = new Token([T_WHITESPACE, ' ']);
} elseif (null !== $braces || $tokens[$nextIndex]->isGivenKind([T_VARIABLE, T_CONSTANT_ENCAPSED_STRING, T_COMMENT])) {
$tokens->insertAt($includy['begin'] + 1, new Token([T_WHITESPACE, ' ']));
}
}
}
private function findIncludies(Tokens $tokens): array
{
static $includyTokenKinds = [T_REQUIRE, T_REQUIRE_ONCE, T_INCLUDE, T_INCLUDE_ONCE];
$includies = [];
foreach ($tokens->findGivenKind($includyTokenKinds) as $includyTokens) {
foreach ($includyTokens as $index => $token) {
$includy = [
'begin' => $index,
'braces' => null,
'end' => $tokens->getNextTokenOfKind($index, [';', [T_CLOSE_TAG]]),
];
$braceOpenIndex = $tokens->getNextMeaningfulToken($index);
if ($tokens[$braceOpenIndex]->equals('(')) {
$braceCloseIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $braceOpenIndex);
$includy['braces'] = [
'open' => $braceOpenIndex,
'close' => $braceCloseIndex,
];
}
$includies[$index] = $includy;
}
}
krsort($includies);
return $includies;
}
private function removeWhitespaceAroundIfPossible(Tokens $tokens, int $index): void
{
$nextIndex = $tokens->getNextNonWhitespace($index);
if (null === $nextIndex || !$tokens[$nextIndex]->isComment()) {
$tokens->removeLeadingWhitespace($index);
}
$prevIndex = $tokens->getPrevNonWhitespace($index);
if (null === $prevIndex || !$tokens[$prevIndex]->isComment()) {
$tokens->removeTrailingWhitespace($index);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ControlStructure;
use PhpCsFixer\AbstractProxyFixer;
use PhpCsFixer\Fixer\Basic\NoTrailingCommaInSinglelineFixer;
use PhpCsFixer\Fixer\DeprecatedFixerInterface;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
final class NoTrailingCommaInListCallFixer extends AbstractProxyFixer implements DeprecatedFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Remove trailing commas in list function calls.',
[new CodeSample("<?php\nlist(\$a, \$b,) = foo();\n")]
);
}
public function getSuccessorsNames(): array
{
return array_keys($this->proxyFixers);
}
protected function createProxyFixers(): array
{
$fixer = new NoTrailingCommaInSinglelineFixer();
$fixer->configure(['elements' => ['array_destructuring']]);
return [$fixer];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ControlStructure;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class NoUnneededControlParenthesesFixer extends AbstractFixer implements ConfigurableFixerInterface
{
private const BLOCK_TYPES = [
Tokens::BLOCK_TYPE_ARRAY_INDEX_CURLY_BRACE,
Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE,
Tokens::BLOCK_TYPE_CURLY_BRACE,
Tokens::BLOCK_TYPE_DESTRUCTURING_SQUARE_BRACE,
Tokens::BLOCK_TYPE_DYNAMIC_PROP_BRACE,
Tokens::BLOCK_TYPE_DYNAMIC_VAR_BRACE,
Tokens::BLOCK_TYPE_INDEX_SQUARE_BRACE,
Tokens::BLOCK_TYPE_PARENTHESIS_BRACE,
];
private const BEFORE_TYPES = [
';',
'{',
[T_OPEN_TAG],
[T_OPEN_TAG_WITH_ECHO],
[T_ECHO],
[T_PRINT],
[T_RETURN],
[T_THROW],
[T_YIELD],
[T_YIELD_FROM],
[T_BREAK],
[T_CONTINUE],
[T_REQUIRE],
[T_REQUIRE_ONCE],
[T_INCLUDE],
[T_INCLUDE_ONCE],
];
private const NOOP_TYPES = [
'$',
[T_CONSTANT_ENCAPSED_STRING],
[T_DNUMBER],
[T_DOUBLE_COLON],
[T_LNUMBER],
[T_NS_SEPARATOR],
[T_OBJECT_OPERATOR],
[T_STRING],
[T_VARIABLE],
[T_STATIC],
[T_CLASS_C],
[T_DIR],
[T_FILE],
[T_FUNC_C],
[T_LINE],
[T_METHOD_C],
[T_NS_C],
[T_TRAIT_C],
];
private const CONFIG_OPTIONS = [
'break',
'clone',
'continue',
'echo_print',
'negative_instanceof',
'others',
'return',
'switch_case',
'yield',
'yield_from',
];
private const TOKEN_TYPE_CONFIG_MAP = [
T_BREAK => 'break',
T_CASE => 'switch_case',
T_CONTINUE => 'continue',
T_ECHO => 'echo_print',
T_PRINT => 'echo_print',
T_RETURN => 'return',
T_YIELD => 'yield',
T_YIELD_FROM => 'yield_from',
];
private const TOKEN_TYPE_NO_CONFIG = [
T_REQUIRE,
T_REQUIRE_ONCE,
T_INCLUDE,
T_INCLUDE_ONCE,
];
private TokensAnalyzer $tokensAnalyzer;
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Removes unneeded parentheses around control statements.',
[
new CodeSample(
'<?php
while ($x) { while ($y) { break (2); } }
clone($a);
while ($y) { continue (2); }
echo("foo");
print("foo");
return (1 + 2);
switch ($a) { case($x); }
yield(2);
'
),
new CodeSample(
'<?php
while ($x) { while ($y) { break (2); } }
clone($a);
while ($y) { continue (2); }
',
['statements' => ['break', 'continue']]
),
]
);
}
public function getPriority(): int
{
return 30;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound(['(', CT::T_BRACE_CLASS_INSTANTIATION_OPEN]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$this->tokensAnalyzer = new TokensAnalyzer($tokens);
foreach ($tokens as $openIndex => $token) {
if ($token->equals('(')) {
$closeIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openIndex);
} elseif ($token->isGivenKind(CT::T_BRACE_CLASS_INSTANTIATION_OPEN)) {
$closeIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_BRACE_CLASS_INSTANTIATION, $openIndex);
} else {
continue;
}
$beforeOpenIndex = $tokens->getPrevMeaningfulToken($openIndex);
$afterCloseIndex = $tokens->getNextMeaningfulToken($closeIndex);
if ($tokens->getNextMeaningfulToken($openIndex) === $closeIndex) {
if ($this->isExitStatement($tokens, $beforeOpenIndex)) {
$this->removeUselessParenthesisPair($tokens, $beforeOpenIndex, $afterCloseIndex, $openIndex, $closeIndex, 'others');
}
continue;
}
if ($this->isKnownNegativePre($tokens[$beforeOpenIndex])) {
continue;
}
if ($this->isUselessWrapped($tokens, $beforeOpenIndex, $afterCloseIndex)) {
$this->removeUselessParenthesisPair($tokens, $beforeOpenIndex, $afterCloseIndex, $openIndex, $closeIndex, $this->getConfigType($tokens, $beforeOpenIndex));
continue;
}
if ($this->isCloneStatement($tokens, $beforeOpenIndex)) {
if ($this->isWrappedCloneArgument($tokens, $beforeOpenIndex, $openIndex, $closeIndex, $afterCloseIndex)) {
$this->removeUselessParenthesisPair($tokens, $beforeOpenIndex, $afterCloseIndex, $openIndex, $closeIndex, 'clone');
}
continue;
}
$instanceOfIndex = $this->getIndexOfInstanceOfStatement($tokens, $openIndex, $closeIndex);
if (null !== $instanceOfIndex) {
if ($this->isWrappedInstanceOf($tokens, $instanceOfIndex, $beforeOpenIndex, $openIndex, $closeIndex, $afterCloseIndex)) {
$this->removeUselessParenthesisPair(
$tokens,
$beforeOpenIndex,
$afterCloseIndex,
$openIndex,
$closeIndex,
$tokens[$beforeOpenIndex]->equals('!') ? 'negative_instanceof' : 'others'
);
}
continue;
}
if ($this->isWrappedPartOfOperation($tokens, $beforeOpenIndex, $openIndex, $closeIndex, $afterCloseIndex)) {
$this->removeUselessParenthesisPair($tokens, $beforeOpenIndex, $afterCloseIndex, $openIndex, $closeIndex, $this->getConfigType($tokens, $beforeOpenIndex));
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
$defaults = array_filter(
self::CONFIG_OPTIONS,
static function (string $option): bool {
return 'negative_instanceof' !== $option && 'others' !== $option && 'yield_from' !== $option;
}
);
return new FixerConfigurationResolver([
(new FixerOptionBuilder('statements', 'List of control statements to fix.'))
->setAllowedTypes(['array'])
->setAllowedValues([new AllowedValueSubset(self::CONFIG_OPTIONS)])
->setDefault(array_values($defaults))
->getOption(),
]);
}
private function isUselessWrapped(Tokens $tokens, int $beforeOpenIndex, int $afterCloseIndex): bool
{
return
$this->isSingleStatement($tokens, $beforeOpenIndex, $afterCloseIndex)
|| $this->isWrappedFnBody($tokens, $beforeOpenIndex, $afterCloseIndex)
|| $this->isWrappedForElement($tokens, $beforeOpenIndex, $afterCloseIndex)
|| $this->isWrappedLanguageConstructArgument($tokens, $beforeOpenIndex, $afterCloseIndex)
|| $this->isWrappedSequenceElement($tokens, $beforeOpenIndex, $afterCloseIndex)
;
}
private function isExitStatement(Tokens $tokens, int $beforeOpenIndex): bool
{
return $tokens[$beforeOpenIndex]->isGivenKind(T_EXIT);
}
private function isCloneStatement(Tokens $tokens, int $beforeOpenIndex): bool
{
return $tokens[$beforeOpenIndex]->isGivenKind(T_CLONE);
}
private function isWrappedCloneArgument(Tokens $tokens, int $beforeOpenIndex, int $openIndex, int $closeIndex, int $afterCloseIndex): bool
{
$beforeOpenIndex = $tokens->getPrevMeaningfulToken($beforeOpenIndex);
if (
!(
$tokens[$beforeOpenIndex]->equals('?')
|| $this->isSimpleAssignment($tokens, $beforeOpenIndex, $afterCloseIndex)
|| $this->isSingleStatement($tokens, $beforeOpenIndex, $afterCloseIndex)
|| $this->isWrappedFnBody($tokens, $beforeOpenIndex, $afterCloseIndex)
|| $this->isWrappedForElement($tokens, $beforeOpenIndex, $afterCloseIndex)
|| $this->isWrappedSequenceElement($tokens, $beforeOpenIndex, $afterCloseIndex)
)
) {
return false;
}
$newCandidateIndex = $tokens->getNextMeaningfulToken($openIndex);
if ($tokens[$newCandidateIndex]->isGivenKind(T_NEW)) {
$openIndex = $newCandidateIndex;
}
return !$this->containsOperation($tokens, $openIndex, $closeIndex);
}
private function getIndexOfInstanceOfStatement(Tokens $tokens, int $openIndex, int $closeIndex): ?int
{
$instanceOfIndex = $tokens->findGivenKind(T_INSTANCEOF, $openIndex, $closeIndex);
return 1 === \count($instanceOfIndex) ? array_key_first($instanceOfIndex) : null;
}
private function isWrappedInstanceOf(Tokens $tokens, int $instanceOfIndex, int $beforeOpenIndex, int $openIndex, int $closeIndex, int $afterCloseIndex): bool
{
if (
$this->containsOperation($tokens, $openIndex, $instanceOfIndex)
|| $this->containsOperation($tokens, $instanceOfIndex, $closeIndex)
) {
return false;
}
if ($tokens[$beforeOpenIndex]->equals('!')) {
$beforeOpenIndex = $tokens->getPrevMeaningfulToken($beforeOpenIndex);
}
return
$this->isSimpleAssignment($tokens, $beforeOpenIndex, $afterCloseIndex)
|| $this->isSingleStatement($tokens, $beforeOpenIndex, $afterCloseIndex)
|| $this->isWrappedFnBody($tokens, $beforeOpenIndex, $afterCloseIndex)
|| $this->isWrappedForElement($tokens, $beforeOpenIndex, $afterCloseIndex)
|| $this->isWrappedSequenceElement($tokens, $beforeOpenIndex, $afterCloseIndex)
;
}
private function isWrappedPartOfOperation(Tokens $tokens, int $beforeOpenIndex, int $openIndex, int $closeIndex, int $afterCloseIndex): bool
{
if ($this->containsOperation($tokens, $openIndex, $closeIndex)) {
return false;
}
$boundariesMoved = false;
if ($this->isPreUnaryOperation($tokens, $beforeOpenIndex)) {
$beforeOpenIndex = $this->getBeforePreUnaryOperation($tokens, $beforeOpenIndex);
$boundariesMoved = true;
}
if ($this->isAccess($tokens, $afterCloseIndex)) {
$afterCloseIndex = $this->getAfterAccess($tokens, $afterCloseIndex);
$boundariesMoved = true;
if ($this->tokensAnalyzer->isUnarySuccessorOperator($afterCloseIndex)) {
$afterCloseIndex = $tokens->getNextMeaningfulToken($afterCloseIndex);
}
}
if ($boundariesMoved) {
if ($this->isKnownNegativePre($tokens[$beforeOpenIndex])) {
return false;
}
if ($this->isUselessWrapped($tokens, $beforeOpenIndex, $afterCloseIndex)) {
return true;
}
}
$beforeIsBinaryOperation = $this->tokensAnalyzer->isBinaryOperator($beforeOpenIndex);
$afterIsBinaryOperation = $this->tokensAnalyzer->isBinaryOperator($afterCloseIndex);
if ($beforeIsBinaryOperation && $afterIsBinaryOperation) {
return true;
}
$beforeToken = $tokens[$beforeOpenIndex];
$afterToken = $tokens[$afterCloseIndex];
$beforeIsBlockOpenOrComma = $beforeToken->equals(',') || null !== $this->getBlock($tokens, $beforeOpenIndex, true);
$afterIsBlockEndOrComma = $afterToken->equals(',') || null !== $this->getBlock($tokens, $afterCloseIndex, false);
if (($beforeIsBlockOpenOrComma && $afterIsBinaryOperation) || ($beforeIsBinaryOperation && $afterIsBlockEndOrComma)) {
return true;
}
if ($tokens[$beforeOpenIndex]->equals('}')) {
$beforeIsStatementOpen = !$this->closeCurlyBelongsToDynamicElement($tokens, $beforeOpenIndex);
} else {
$beforeIsStatementOpen = $beforeToken->equalsAny(self::BEFORE_TYPES) || $beforeToken->isGivenKind(T_CASE);
}
$afterIsStatementEnd = $afterToken->equalsAny([';', [T_CLOSE_TAG]]);
return
($beforeIsStatementOpen && $afterIsBinaryOperation)
|| ($beforeIsBinaryOperation && $afterIsStatementEnd)
;
}
private function isWrappedLanguageConstructArgument(Tokens $tokens, int $beforeOpenIndex, int $afterCloseIndex): bool
{
if (!$tokens[$beforeOpenIndex]->isGivenKind([T_PRINT, T_YIELD, T_YIELD_FROM, T_REQUIRE, T_REQUIRE_ONCE, T_INCLUDE, T_INCLUDE_ONCE])) {
return false;
}
$beforeOpenIndex = $tokens->getPrevMeaningfulToken($beforeOpenIndex);
return $this->isWrappedSequenceElement($tokens, $beforeOpenIndex, $afterCloseIndex);
}
private function isSingleStatement(Tokens $tokens, int $beforeOpenIndex, int $afterCloseIndex): bool
{
if ($tokens[$beforeOpenIndex]->isGivenKind(T_CASE)) {
return $tokens[$afterCloseIndex]->equalsAny([':', ';']);
}
if (!$tokens[$afterCloseIndex]->equalsAny([';', [T_CLOSE_TAG]])) {
return false;
}
if ($tokens[$beforeOpenIndex]->equals('}')) {
return !$this->closeCurlyBelongsToDynamicElement($tokens, $beforeOpenIndex);
}
return $tokens[$beforeOpenIndex]->equalsAny(self::BEFORE_TYPES);
}
private function isSimpleAssignment(Tokens $tokens, int $beforeOpenIndex, int $afterCloseIndex): bool
{
return $tokens[$beforeOpenIndex]->equals('=') && $tokens[$afterCloseIndex]->equalsAny([';', [T_CLOSE_TAG]]);
}
private function isWrappedSequenceElement(Tokens $tokens, int $startIndex, int $endIndex): bool
{
$startIsComma = $tokens[$startIndex]->equals(',');
$endIsComma = $tokens[$endIndex]->equals(',');
if ($startIsComma && $endIsComma) {
return true;
}
$blockTypeStart = $this->getBlock($tokens, $startIndex, true);
$blockTypeEnd = $this->getBlock($tokens, $endIndex, false);
return
($startIsComma && null !== $blockTypeEnd)
|| ($endIsComma && null !== $blockTypeStart)
|| (null !== $blockTypeEnd && null !== $blockTypeStart)
;
}
private function isWrappedForElement(Tokens $tokens, int $beforeOpenIndex, int $afterCloseIndex): bool
{
$forCandidateIndex = null;
if ($tokens[$beforeOpenIndex]->equals('(') && $tokens[$afterCloseIndex]->equals(';')) {
$forCandidateIndex = $tokens->getPrevMeaningfulToken($beforeOpenIndex);
} elseif ($tokens[$afterCloseIndex]->equals(')') && $tokens[$beforeOpenIndex]->equals(';')) {
$forCandidateIndex = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $afterCloseIndex);
$forCandidateIndex = $tokens->getPrevMeaningfulToken($forCandidateIndex);
}
return null !== $forCandidateIndex && $tokens[$forCandidateIndex]->isGivenKind(T_FOR);
}
private function isWrappedFnBody(Tokens $tokens, int $beforeOpenIndex, int $afterCloseIndex): bool
{
if (!$tokens[$beforeOpenIndex]->isGivenKind(T_DOUBLE_ARROW)) {
return false;
}
$beforeOpenIndex = $tokens->getPrevMeaningfulToken($beforeOpenIndex);
if ($tokens[$beforeOpenIndex]->isGivenKind(T_STRING)) {
while (true) {
$beforeOpenIndex = $tokens->getPrevMeaningfulToken($beforeOpenIndex);
if (!$tokens[$beforeOpenIndex]->isGivenKind([T_STRING, CT::T_TYPE_INTERSECTION, CT::T_TYPE_ALTERNATION])) {
break;
}
}
if (!$tokens[$beforeOpenIndex]->isGivenKind(CT::T_TYPE_COLON)) {
return false;
}
$beforeOpenIndex = $tokens->getPrevMeaningfulToken($beforeOpenIndex);
}
if (!$tokens[$beforeOpenIndex]->equals(')')) {
return false;
}
$beforeOpenIndex = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $beforeOpenIndex);
$beforeOpenIndex = $tokens->getPrevMeaningfulToken($beforeOpenIndex);
if ($tokens[$beforeOpenIndex]->isGivenKind(CT::T_RETURN_REF)) {
$beforeOpenIndex = $tokens->getPrevMeaningfulToken($beforeOpenIndex);
}
if (!$tokens[$beforeOpenIndex]->isGivenKind(T_FN)) {
return false;
}
return $tokens[$afterCloseIndex]->equalsAny([';', ',', [T_CLOSE_TAG]]);
}
private function isPreUnaryOperation(Tokens $tokens, int $index): bool
{
return $this->tokensAnalyzer->isUnaryPredecessorOperator($index) || $tokens[$index]->isCast();
}
private function getBeforePreUnaryOperation(Tokens $tokens, int $index): int
{
do {
$index = $tokens->getPrevMeaningfulToken($index);
} while ($this->isPreUnaryOperation($tokens, $index));
return $index;
}
private function isAccess(Tokens $tokens, int $index): bool
{
$token = $tokens[$index];
return $token->isObjectOperator() || $token->equals('[') || $token->isGivenKind([CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN]);
}
private function getAfterAccess(Tokens $tokens, int $index): int
{
while (true) {
$block = $this->getBlock($tokens, $index, true);
if (null !== $block) {
$index = $tokens->findBlockEnd($block['type'], $index);
$index = $tokens->getNextMeaningfulToken($index);
continue;
}
if (
$tokens[$index]->isObjectOperator()
|| $tokens[$index]->equalsAny(['$', [T_PAAMAYIM_NEKUDOTAYIM], [T_STRING], [T_VARIABLE]])
) {
$index = $tokens->getNextMeaningfulToken($index);
continue;
}
break;
}
return $index;
}
private function getBlock(Tokens $tokens, int $index, bool $isStart): ?array
{
$block = Tokens::detectBlockType($tokens[$index]);
return null !== $block && $isStart === $block['isStart'] && \in_array($block['type'], self::BLOCK_TYPES, true) ? $block : null;
}
private function isKnownNegativePre(Token $token): bool
{
static $knownNegativeTypes;
if (null === $knownNegativeTypes) {
$knownNegativeTypes = [
[CT::T_CLASS_CONSTANT],
[CT::T_DYNAMIC_VAR_BRACE_CLOSE],
[CT::T_RETURN_REF],
[CT::T_USE_LAMBDA],
[T_ARRAY],
[T_CATCH],
[T_CLASS],
[T_DECLARE],
[T_ELSEIF],
[T_EMPTY],
[T_EXIT],
[T_EVAL],
[T_FN],
[T_FOREACH],
[T_FOR],
[T_FUNCTION],
[T_HALT_COMPILER],
[T_IF],
[T_ISSET],
[T_LIST],
[T_STRING],
[T_SWITCH],
[T_STATIC],
[T_UNSET],
[T_VARIABLE],
[T_WHILE],
[T_REQUIRE],
[T_REQUIRE_ONCE],
[T_INCLUDE],
[T_INCLUDE_ONCE],
];
if (\defined('T_MATCH')) {
$knownNegativeTypes[] = T_MATCH;
}
}
return $token->equalsAny($knownNegativeTypes);
}
private function containsOperation(Tokens $tokens, int $startIndex, int $endIndex): bool
{
while (true) {
$startIndex = $tokens->getNextMeaningfulToken($startIndex);
if ($startIndex === $endIndex) {
break;
}
$block = Tokens::detectBlockType($tokens[$startIndex]);
if (null !== $block && $block['isStart']) {
$startIndex = $tokens->findBlockEnd($block['type'], $startIndex);
continue;
}
if (!$tokens[$startIndex]->equalsAny(self::NOOP_TYPES)) {
return true;
}
}
return false;
}
private function getConfigType(Tokens $tokens, int $beforeOpenIndex): ?string
{
if ($tokens[$beforeOpenIndex]->isGivenKind(self::TOKEN_TYPE_NO_CONFIG)) {
return null;
}
foreach (self::TOKEN_TYPE_CONFIG_MAP as $type => $configItem) {
if ($tokens[$beforeOpenIndex]->isGivenKind($type)) {
return $configItem;
}
}
return 'others';
}
private function removeUselessParenthesisPair(
Tokens $tokens,
int $beforeOpenIndex,
int $afterCloseIndex,
int $openIndex,
int $closeIndex,
?string $configType
): void {
$statements = $this->configuration['statements'];
if (null === $configType || !\in_array($configType, $statements, true)) {
return;
}
$needsSpaceAfter =
!$this->isAccess($tokens, $afterCloseIndex)
&& !$tokens[$afterCloseIndex]->equalsAny([';', ',', [T_CLOSE_TAG]])
&& null === $this->getBlock($tokens, $afterCloseIndex, false)
&& !($tokens[$afterCloseIndex]->equalsAny([':', ';']) && $tokens[$beforeOpenIndex]->isGivenKind(T_CASE))
;
$needsSpaceBefore =
!$this->isPreUnaryOperation($tokens, $beforeOpenIndex)
&& !$tokens[$beforeOpenIndex]->equalsAny(['}', [T_EXIT], [T_OPEN_TAG]])
&& null === $this->getBlock($tokens, $beforeOpenIndex, true)
;
$this->removeBrace($tokens, $closeIndex, $needsSpaceAfter);
$this->removeBrace($tokens, $openIndex, $needsSpaceBefore);
}
private function removeBrace(Tokens $tokens, int $index, bool $needsSpace): void
{
if ($needsSpace) {
foreach ([-1, 1] as $direction) {
$siblingIndex = $tokens->getNonEmptySibling($index, $direction);
if ($tokens[$siblingIndex]->isWhitespace() || $tokens[$siblingIndex]->isComment()) {
$needsSpace = false;
break;
}
}
}
if ($needsSpace) {
$tokens[$index] = new Token([T_WHITESPACE, ' ']);
} else {
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
}
}
private function closeCurlyBelongsToDynamicElement(Tokens $tokens, int $beforeOpenIndex): bool
{
$index = $tokens->findBlockStart(Tokens::BLOCK_TYPE_CURLY_BRACE, $beforeOpenIndex);
$index = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$index]->isGivenKind(T_DOUBLE_COLON)) {
return true;
}
if ($tokens[$index]->equals(':')) {
$index = $tokens->getPrevTokenOfKind($index, [[T_CASE], '?']);
return !$tokens[$index]->isGivenKind(T_CASE);
}
return false;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ControlStructure;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Analyzer\WhitespacesAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
use Symfony\Component\OptionsResolver\Options;
final class NoBreakCommentFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'There must be a comment when fall-through is intentional in a non-empty case body.',
[
new CodeSample(
'<?php
switch ($foo) {
case 1:
foo();
case 2:
bar();
// no break
break;
case 3:
baz();
}
'
),
new CodeSample(
'<?php
switch ($foo) {
case 1:
foo();
case 2:
foo();
}
',
['comment_text' => 'some comment']
),
],
'Adds a "no break" comment before fall-through cases, and removes it if there is no fall-through.'
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_SWITCH);
}
public function getPriority(): int
{
return 0;
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('comment_text', 'The text to use in the added comment and to detect it.'))
->setAllowedTypes(['string'])
->setAllowedValues([
static function (string $value): bool {
if (Preg::match('/\R/', $value)) {
throw new InvalidOptionsException('The comment text must not contain new lines.');
}
return true;
},
])
->setNormalizer(static function (Options $options, string $value): string {
return rtrim($value);
})
->setDefault('no break')
->getOption(),
]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = \count($tokens) - 1; $index >= 0; --$index) {
if ($tokens[$index]->isGivenKind(T_DEFAULT)) {
if ($tokens[$tokens->getNextMeaningfulToken($index)]->isGivenKind(T_DOUBLE_ARROW)) {
continue;
}
} elseif (!$tokens[$index]->isGivenKind(T_CASE)) {
continue;
}
$this->fixCase($tokens, $tokens->getNextTokenOfKind($index, [':', ';']));
}
}
private function fixCase(Tokens $tokens, int $casePosition): void
{
$empty = true;
$fallThrough = true;
$commentPosition = null;
for ($i = $casePosition + 1, $max = \count($tokens); $i < $max; ++$i) {
if ($tokens[$i]->isGivenKind([T_SWITCH, T_IF, T_ELSE, T_ELSEIF, T_FOR, T_FOREACH, T_WHILE, T_DO, T_FUNCTION, T_CLASS])) {
$empty = false;
$i = $this->getStructureEnd($tokens, $i);
continue;
}
if ($tokens[$i]->isGivenKind([T_BREAK, T_CONTINUE, T_RETURN, T_EXIT, T_GOTO])) {
$fallThrough = false;
continue;
}
if ($tokens[$i]->isGivenKind(T_THROW)) {
$previousIndex = $tokens->getPrevMeaningfulToken($i);
if ($previousIndex === $casePosition || $tokens[$previousIndex]->equalsAny(['{', ';', '}', [T_OPEN_TAG]])) {
$fallThrough = false;
}
continue;
}
if ($tokens[$i]->equals('}') || $tokens[$i]->isGivenKind(T_ENDSWITCH)) {
if (null !== $commentPosition) {
$this->removeComment($tokens, $commentPosition);
}
break;
}
if ($this->isNoBreakComment($tokens[$i])) {
$commentPosition = $i;
continue;
}
if ($tokens[$i]->isGivenKind([T_CASE, T_DEFAULT])) {
if (!$empty && $fallThrough) {
if (null !== $commentPosition && $tokens->getPrevNonWhitespace($i) !== $commentPosition) {
$this->removeComment($tokens, $commentPosition);
$commentPosition = null;
}
if (null === $commentPosition) {
$this->insertCommentAt($tokens, $i);
} else {
$text = $this->configuration['comment_text'];
$tokens[$commentPosition] = new Token([
$tokens[$commentPosition]->getId(),
str_ireplace($text, $text, $tokens[$commentPosition]->getContent()),
]);
$this->ensureNewLineAt($tokens, $commentPosition);
}
} elseif (null !== $commentPosition) {
$this->removeComment($tokens, $commentPosition);
}
break;
}
if (!$tokens[$i]->isGivenKind([T_COMMENT, T_WHITESPACE])) {
$empty = false;
}
}
}
private function isNoBreakComment(Token $token): bool
{
if (!$token->isComment()) {
return false;
}
$text = preg_quote($this->configuration['comment_text'], '~');
return 1 === Preg::match("~^((//|#)\\s*{$text}\\s*)|(/\\*\\*?\\s*{$text}(\\s+.*)*\\*/)$~i", $token->getContent());
}
private function insertCommentAt(Tokens $tokens, int $casePosition): void
{
$lineEnding = $this->whitespacesConfig->getLineEnding();
$newlinePosition = $this->ensureNewLineAt($tokens, $casePosition);
$newlineToken = $tokens[$newlinePosition];
$nbNewlines = substr_count($newlineToken->getContent(), $lineEnding);
if ($newlineToken->isGivenKind(T_OPEN_TAG) && Preg::match('/\R/', $newlineToken->getContent())) {
++$nbNewlines;
} elseif ($tokens[$newlinePosition - 1]->isGivenKind(T_OPEN_TAG) && Preg::match('/\R/', $tokens[$newlinePosition - 1]->getContent())) {
++$nbNewlines;
if (!Preg::match('/\R/', $newlineToken->getContent())) {
$tokens[$newlinePosition] = new Token([$newlineToken->getId(), $lineEnding.$newlineToken->getContent()]);
}
}
if ($nbNewlines > 1) {
Preg::match('/^(.*?)(\R\h*)$/s', $newlineToken->getContent(), $matches);
$indent = WhitespacesAnalyzer::detectIndent($tokens, $newlinePosition - 1);
$tokens[$newlinePosition] = new Token([$newlineToken->getId(), $matches[1].$lineEnding.$indent]);
$tokens->insertAt(++$newlinePosition, new Token([T_WHITESPACE, $matches[2]]));
}
$tokens->insertAt($newlinePosition, new Token([T_COMMENT, '// '.$this->configuration['comment_text']]));
$this->ensureNewLineAt($tokens, $newlinePosition);
}
private function ensureNewLineAt(Tokens $tokens, int $position): int
{
$lineEnding = $this->whitespacesConfig->getLineEnding();
$content = $lineEnding.WhitespacesAnalyzer::detectIndent($tokens, $position);
$whitespaceToken = $tokens[$position - 1];
if (!$whitespaceToken->isGivenKind(T_WHITESPACE)) {
if ($whitespaceToken->isGivenKind(T_OPEN_TAG)) {
$content = Preg::replace('/\R/', '', $content);
if (!Preg::match('/\R/', $whitespaceToken->getContent())) {
$tokens[$position - 1] = new Token([T_OPEN_TAG, Preg::replace('/\s+$/', $lineEnding, $whitespaceToken->getContent())]);
}
}
if ('' !== $content) {
$tokens->insertAt($position, new Token([T_WHITESPACE, $content]));
return $position;
}
return $position - 1;
}
if ($tokens[$position - 2]->isGivenKind(T_OPEN_TAG) && Preg::match('/\R/', $tokens[$position - 2]->getContent())) {
$content = Preg::replace('/^\R/', '', $content);
}
if (!Preg::match('/\R/', $whitespaceToken->getContent())) {
$tokens[$position - 1] = new Token([T_WHITESPACE, $content]);
}
return $position - 1;
}
private function removeComment(Tokens $tokens, int $commentPosition): void
{
if ($tokens[$tokens->getPrevNonWhitespace($commentPosition)]->isGivenKind(T_OPEN_TAG)) {
$whitespacePosition = $commentPosition + 1;
$regex = '/^\R\h*/';
} else {
$whitespacePosition = $commentPosition - 1;
$regex = '/\R\h*$/';
}
$whitespaceToken = $tokens[$whitespacePosition];
if ($whitespaceToken->isGivenKind(T_WHITESPACE)) {
$content = Preg::replace($regex, '', $whitespaceToken->getContent());
$tokens->ensureWhitespaceAtIndex($whitespacePosition, 0, $content);
}
$tokens->clearTokenAndMergeSurroundingWhitespace($commentPosition);
}
private function getStructureEnd(Tokens $tokens, int $position): int
{
$initialToken = $tokens[$position];
if ($initialToken->isGivenKind([T_FOR, T_FOREACH, T_WHILE, T_IF, T_ELSEIF, T_SWITCH, T_FUNCTION])) {
$position = $tokens->findBlockEnd(
Tokens::BLOCK_TYPE_PARENTHESIS_BRACE,
$tokens->getNextTokenOfKind($position, ['('])
);
} elseif ($initialToken->isGivenKind(T_CLASS)) {
$openParenthesisPosition = $tokens->getNextMeaningfulToken($position);
if ('(' === $tokens[$openParenthesisPosition]->getContent()) {
$position = $tokens->findBlockEnd(
Tokens::BLOCK_TYPE_PARENTHESIS_BRACE,
$openParenthesisPosition
);
}
}
$position = $tokens->getNextMeaningfulToken($position);
if ('{' !== $tokens[$position]->getContent()) {
return $tokens->getNextTokenOfKind($position, [';']);
}
$position = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $position);
if ($initialToken->isGivenKind(T_DO)) {
$position = $tokens->findBlockEnd(
Tokens::BLOCK_TYPE_PARENTHESIS_BRACE,
$tokens->getNextTokenOfKind($position, ['('])
);
return $tokens->getNextTokenOfKind($position, [';']);
}
return $position;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\FunctionNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class RegularCallableCallFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Callables must be called without using `call_user_func*` when possible.',
[
new CodeSample(
'<?php
call_user_func("var_dump", 1, 2);
call_user_func("Bar\Baz::d", 1, 2);
call_user_func_array($callback, [1, 2]);
'
),
new CodeSample(
'<?php
call_user_func(function ($a, $b) { var_dump($a, $b); }, 1, 2);
call_user_func(static function ($a, $b) { var_dump($a, $b); }, 1, 2);
'
),
],
null,
'Risky when the `call_user_func` or `call_user_func_array` function is overridden or when are used in constructions that should be avoided, like `call_user_func_array(\'foo\', [\'bar\' => \'baz\'])` or `call_user_func($foo, $foo = \'bar\')`.'
);
}
public function getPriority(): int
{
return 2;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_STRING);
}
public function isRisky(): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$functionsAnalyzer = new FunctionsAnalyzer();
$argumentsAnalyzer = new ArgumentsAnalyzer();
for ($index = $tokens->count() - 1; $index > 0; --$index) {
if (!$tokens[$index]->equalsAny([[T_STRING, 'call_user_func'], [T_STRING, 'call_user_func_array']], false)) {
continue;
}
if (!$functionsAnalyzer->isGlobalFunctionCall($tokens, $index)) {
continue;
}
$openParenthesis = $tokens->getNextMeaningfulToken($index);
$closeParenthesis = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openParenthesis);
$arguments = $argumentsAnalyzer->getArguments($tokens, $openParenthesis, $closeParenthesis);
if (1 > \count($arguments)) {
return;
}
$this->processCall($tokens, $index, $arguments);
}
}
private function processCall(Tokens $tokens, int $index, array $arguments): void
{
$firstArgIndex = $tokens->getNextMeaningfulToken(
$tokens->getNextMeaningfulToken($index)
);
$firstArgToken = $tokens[$firstArgIndex];
if ($firstArgToken->isGivenKind(T_CONSTANT_ENCAPSED_STRING)) {
$afterFirstArgIndex = $tokens->getNextMeaningfulToken($firstArgIndex);
if (!$tokens[$afterFirstArgIndex]->equalsAny([',', ')'])) {
return;
}
$firstArgTokenContent = $firstArgToken->getContent();
if (!$this->isValidFunctionInvoke($firstArgTokenContent)) {
return;
}
$newCallTokens = Tokens::fromCode('<?php '.substr(str_replace('\\\\', '\\', $firstArgToken->getContent()), 1, -1).'();');
$newCallTokensSize = $newCallTokens->count();
$newCallTokens->clearAt(0);
$newCallTokens->clearRange($newCallTokensSize - 3, $newCallTokensSize - 1);
$newCallTokens->clearEmptyTokens();
$this->replaceCallUserFuncWithCallback($tokens, $index, $newCallTokens, $firstArgIndex, $firstArgIndex);
} elseif (
$firstArgToken->isGivenKind(T_FUNCTION)
|| (
$firstArgToken->isGivenKind(T_STATIC)
&& $tokens[$tokens->getNextMeaningfulToken($firstArgIndex)]->isGivenKind(T_FUNCTION)
)
) {
$firstArgEndIndex = $tokens->findBlockEnd(
Tokens::BLOCK_TYPE_CURLY_BRACE,
$tokens->getNextTokenOfKind($firstArgIndex, ['{'])
);
$newCallTokens = $this->getTokensSubcollection($tokens, $firstArgIndex, $firstArgEndIndex);
$newCallTokens->insertAt($newCallTokens->count(), new Token(')'));
$newCallTokens->insertAt(0, new Token('('));
$this->replaceCallUserFuncWithCallback($tokens, $index, $newCallTokens, $firstArgIndex, $firstArgEndIndex);
} elseif ($firstArgToken->isGivenKind(T_VARIABLE)) {
$firstArgEndIndex = reset($arguments);
foreach ($arguments as $argumentStart => $argumentEnd) {
if ($firstArgEndIndex === $argumentEnd) {
continue;
}
for ($i = $argumentStart; $i <= $argumentEnd; ++$i) {
if ($tokens[$i]->equals($firstArgToken)) {
return;
}
}
}
$newCallTokens = $this->getTokensSubcollection($tokens, $firstArgIndex, $firstArgEndIndex);
$complex = false;
for ($newCallIndex = \count($newCallTokens) - 1; $newCallIndex >= 0; --$newCallIndex) {
if ($newCallTokens[$newCallIndex]->isGivenKind([T_WHITESPACE, T_COMMENT, T_DOC_COMMENT, T_VARIABLE])) {
continue;
}
$blockType = Tokens::detectBlockType($newCallTokens[$newCallIndex]);
if (null !== $blockType && (Tokens::BLOCK_TYPE_ARRAY_INDEX_CURLY_BRACE === $blockType['type'] || Tokens::BLOCK_TYPE_INDEX_SQUARE_BRACE === $blockType['type'])) {
$newCallIndex = $newCallTokens->findBlockStart($blockType['type'], $newCallIndex);
continue;
}
$complex = true;
break;
}
if ($complex) {
$newCallTokens->insertAt($newCallTokens->count(), new Token(')'));
$newCallTokens->insertAt(0, new Token('('));
}
$this->replaceCallUserFuncWithCallback($tokens, $index, $newCallTokens, $firstArgIndex, $firstArgEndIndex);
}
}
private function replaceCallUserFuncWithCallback(Tokens $tokens, int $callIndex, Tokens $newCallTokens, int $firstArgStartIndex, int $firstArgEndIndex): void
{
$tokens->clearRange($firstArgStartIndex, $firstArgEndIndex);
$afterFirstArgIndex = $tokens->getNextMeaningfulToken($firstArgEndIndex);
$afterFirstArgToken = $tokens[$afterFirstArgIndex];
if ($afterFirstArgToken->equals(',')) {
$useEllipsis = $tokens[$callIndex]->equals([T_STRING, 'call_user_func_array'], false);
if ($useEllipsis) {
$secondArgIndex = $tokens->getNextMeaningfulToken($afterFirstArgIndex);
$tokens->insertAt($secondArgIndex, new Token([T_ELLIPSIS, '...']));
}
$tokens->clearAt($afterFirstArgIndex);
$tokens->removeTrailingWhitespace($afterFirstArgIndex);
}
$tokens->overrideRange($callIndex, $callIndex, $newCallTokens);
$prevIndex = $tokens->getPrevMeaningfulToken($callIndex);
if ($tokens[$prevIndex]->isGivenKind(T_NS_SEPARATOR)) {
$tokens->clearTokenAndMergeSurroundingWhitespace($prevIndex);
}
}
private function getTokensSubcollection(Tokens $tokens, int $indexStart, int $indexEnd): Tokens
{
$size = $indexEnd - $indexStart + 1;
$subCollection = new Tokens($size);
for ($i = 0; $i < $size; ++$i) {
$toClone = $tokens[$i + $indexStart];
$subCollection[$i] = clone $toClone;
}
return $subCollection;
}
private function isValidFunctionInvoke(string $name): bool
{
if (\strlen($name) < 3 || 'b' === $name[0] || 'B' === $name[0]) {
return false;
}
$name = substr($name, 1, -1);
if ($name !== trim($name)) {
return false;
}
return true;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\FunctionNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class LambdaNotUsedImportFixer extends AbstractFixer
{
private $argumentsAnalyzer;
private $functionAnalyzer;
private $tokensAnalyzer;
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Lambda must not import variables it doesn\'t use.',
[new CodeSample("<?php\n\$foo = function() use (\$bar) {};\n")]
);
}
public function getPriority(): int
{
return 31;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAllTokenKindsFound([T_FUNCTION, CT::T_USE_LAMBDA]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$this->argumentsAnalyzer = new ArgumentsAnalyzer();
$this->functionAnalyzer = new FunctionsAnalyzer();
$this->tokensAnalyzer = new TokensAnalyzer($tokens);
for ($index = $tokens->count() - 4; $index > 0; --$index) {
$lambdaUseIndex = $this->getLambdaUseIndex($tokens, $index);
if (false !== $lambdaUseIndex) {
$this->fixLambda($tokens, $lambdaUseIndex);
}
}
}
private function fixLambda(Tokens $tokens, int $lambdaUseIndex): void
{
$lambdaUseOpenBraceIndex = $tokens->getNextTokenOfKind($lambdaUseIndex, ['(']);
$lambdaUseCloseBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $lambdaUseOpenBraceIndex);
$arguments = $this->argumentsAnalyzer->getArguments($tokens, $lambdaUseOpenBraceIndex, $lambdaUseCloseBraceIndex);
$imports = $this->filterArguments($tokens, $arguments);
if (0 === \count($imports)) {
return;
}
$notUsedImports = $this->findNotUsedLambdaImports($tokens, $imports, $lambdaUseCloseBraceIndex);
$notUsedImportsCount = \count($notUsedImports);
if (0 === $notUsedImportsCount) {
return;
}
if ($notUsedImportsCount === \count($arguments)) {
$this->clearImportsAndUse($tokens, $lambdaUseIndex, $lambdaUseCloseBraceIndex);
return;
}
$this->clearImports($tokens, array_reverse($notUsedImports));
}
private function findNotUsedLambdaImports(Tokens $tokens, array $imports, int $lambdaUseCloseBraceIndex): array
{
static $riskyKinds = [
CT::T_DYNAMIC_VAR_BRACE_OPEN,
T_EVAL,
T_INCLUDE,
T_INCLUDE_ONCE,
T_REQUIRE,
T_REQUIRE_ONCE,
];
$lambdaOpenIndex = $tokens->getNextTokenOfKind($lambdaUseCloseBraceIndex, ['{']);
$curlyBracesLevel = 0;
for ($index = $lambdaOpenIndex;; ++$index) {
$token = $tokens[$index];
if ($token->equals('{')) {
++$curlyBracesLevel;
continue;
}
if ($token->equals('}')) {
--$curlyBracesLevel;
if (0 === $curlyBracesLevel) {
break;
}
continue;
}
if ($token->isGivenKind(T_STRING) && 'compact' === strtolower($token->getContent()) && $this->functionAnalyzer->isGlobalFunctionCall($tokens, $index)) {
return [];
}
if ($token->isGivenKind($riskyKinds)) {
return [];
}
if ($token->equals('$')) {
$nextIndex = $tokens->getNextMeaningfulToken($index);
if ($tokens[$nextIndex]->isGivenKind(T_VARIABLE)) {
return [];
}
}
if ($token->isGivenKind(T_VARIABLE)) {
$content = $token->getContent();
if (isset($imports[$content])) {
unset($imports[$content]);
if (0 === \count($imports)) {
return $imports;
}
}
}
if ($token->isGivenKind(T_STRING_VARNAME)) {
$content = '$'.$token->getContent();
if (isset($imports[$content])) {
unset($imports[$content]);
if (0 === \count($imports)) {
return $imports;
}
}
}
if ($token->isClassy()) {
$index = $tokens->getNextTokenOfKind($index, ['(', '{']);
if ($tokens[$index]->equals('(')) {
$closeBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index);
$arguments = $this->argumentsAnalyzer->getArguments($tokens, $index, $closeBraceIndex);
$imports = $this->countImportsUsedAsArgument($tokens, $imports, $arguments);
$index = $tokens->getNextTokenOfKind($closeBraceIndex, ['{']);
}
$index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $index);
continue;
}
if ($token->isGivenKind(T_FUNCTION)) {
$lambdaUseOpenBraceIndex = $tokens->getNextTokenOfKind($index, ['(']);
$lambdaUseCloseBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $lambdaUseOpenBraceIndex);
$arguments = $this->argumentsAnalyzer->getArguments($tokens, $lambdaUseOpenBraceIndex, $lambdaUseCloseBraceIndex);
$imports = $this->countImportsUsedAsArgument($tokens, $imports, $arguments);
$index = $tokens->getNextTokenOfKind($index, [[CT::T_USE_LAMBDA], '{']);
if ($tokens[$index]->isGivenKind(CT::T_USE_LAMBDA)) {
$lambdaUseOpenBraceIndex = $tokens->getNextTokenOfKind($index, ['(']);
$lambdaUseCloseBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $lambdaUseOpenBraceIndex);
$arguments = $this->argumentsAnalyzer->getArguments($tokens, $lambdaUseOpenBraceIndex, $lambdaUseCloseBraceIndex);
$imports = $this->countImportsUsedAsArgument($tokens, $imports, $arguments);
$index = $tokens->getNextTokenOfKind($lambdaUseCloseBraceIndex, ['{']);
}
$index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $index);
continue;
}
}
return $imports;
}
private function countImportsUsedAsArgument(Tokens $tokens, array $imports, array $arguments): array
{
foreach ($arguments as $start => $end) {
$info = $this->argumentsAnalyzer->getArgumentInfo($tokens, $start, $end);
$content = $info->getName();
if (isset($imports[$content])) {
unset($imports[$content]);
if (0 === \count($imports)) {
return $imports;
}
}
}
return $imports;
}
private function getLambdaUseIndex(Tokens $tokens, int $index)
{
if (!$tokens[$index]->isGivenKind(T_FUNCTION) || !$this->tokensAnalyzer->isLambda($index)) {
return false;
}
$lambdaUseIndex = $tokens->getNextMeaningfulToken($index);
if ($tokens[$lambdaUseIndex]->isGivenKind(CT::T_RETURN_REF)) {
$lambdaUseIndex = $tokens->getNextMeaningfulToken($lambdaUseIndex);
}
$lambdaUseIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $lambdaUseIndex);
$lambdaUseIndex = $tokens->getNextMeaningfulToken($lambdaUseIndex);
if (!$tokens[$lambdaUseIndex]->isGivenKind(CT::T_USE_LAMBDA)) {
return false;
}
return $lambdaUseIndex;
}
private function filterArguments(Tokens $tokens, array $arguments): array
{
$imports = [];
foreach ($arguments as $start => $end) {
$info = $this->argumentsAnalyzer->getArgumentInfo($tokens, $start, $end);
$argument = $info->getNameIndex();
if ($tokens[$tokens->getPrevMeaningfulToken($argument)]->equals('&')) {
continue;
}
$argumentCandidate = $tokens[$argument];
if ('$this' === $argumentCandidate->getContent()) {
continue;
}
if ($this->tokensAnalyzer->isSuperGlobal($argument)) {
continue;
}
$imports[$argumentCandidate->getContent()] = $argument;
}
return $imports;
}
private function clearImports(Tokens $tokens, array $imports): void
{
foreach ($imports as $removeIndex) {
$tokens->clearTokenAndMergeSurroundingWhitespace($removeIndex);
$previousRemoveIndex = $tokens->getPrevMeaningfulToken($removeIndex);
if ($tokens[$previousRemoveIndex]->equals(',')) {
$tokens->clearTokenAndMergeSurroundingWhitespace($previousRemoveIndex);
} elseif ($tokens[$previousRemoveIndex]->equals('(')) {
$tokens->clearTokenAndMergeSurroundingWhitespace($tokens->getNextMeaningfulToken($removeIndex));
}
}
}
private function clearImportsAndUse(Tokens $tokens, int $lambdaUseIndex, int $lambdaUseCloseBraceIndex): void
{
for ($i = $lambdaUseCloseBraceIndex; $i >= $lambdaUseIndex; --$i) {
if ($tokens[$i]->isComment()) {
continue;
}
if ($tokens[$i]->isWhitespace()) {
$previousIndex = $tokens->getPrevNonWhitespace($i);
if ($tokens[$previousIndex]->isComment()) {
continue;
}
}
$tokens->clearTokenAndMergeSurroundingWhitespace($i);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\FunctionNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\DocBlock\Annotation;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class VoidReturnFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Add `void` return type to functions with missing or empty return statements, but priority is given to `@return` annotations. Requires PHP >= 7.1.',
[
new CodeSample(
"<?php\nfunction foo(\$a) {};\n"
),
],
null,
'Modifies the signature of functions.'
);
}
public function getPriority(): int
{
return 5;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_FUNCTION);
}
public function isRisky(): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
static $excludedFunctions = [
[T_STRING, '__clone'],
[T_STRING, '__construct'],
[T_STRING, '__debugInfo'],
[T_STRING, '__destruct'],
[T_STRING, '__isset'],
[T_STRING, '__serialize'],
[T_STRING, '__set_state'],
[T_STRING, '__sleep'],
[T_STRING, '__toString'],
];
for ($index = $tokens->count() - 1; 0 <= $index; --$index) {
if (!$tokens[$index]->isGivenKind(T_FUNCTION)) {
continue;
}
$functionName = $tokens->getNextMeaningfulToken($index);
if ($tokens[$functionName]->equalsAny($excludedFunctions, false)) {
continue;
}
$startIndex = $tokens->getNextTokenOfKind($index, ['{', ';']);
if ($this->hasReturnTypeHint($tokens, $startIndex)) {
continue;
}
if ($tokens[$startIndex]->equals(';')) {
if ($this->hasVoidReturnAnnotation($tokens, $index)) {
$this->fixFunctionDefinition($tokens, $startIndex);
}
continue;
}
if ($this->hasReturnAnnotation($tokens, $index)) {
continue;
}
$endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $startIndex);
if ($this->hasVoidReturn($tokens, $startIndex, $endIndex)) {
$this->fixFunctionDefinition($tokens, $startIndex);
}
}
}
private function hasReturnAnnotation(Tokens $tokens, int $index): bool
{
foreach ($this->findReturnAnnotations($tokens, $index) as $return) {
if (['void'] !== $return->getTypes()) {
return true;
}
}
return false;
}
private function hasVoidReturnAnnotation(Tokens $tokens, int $index): bool
{
foreach ($this->findReturnAnnotations($tokens, $index) as $return) {
if (['void'] === $return->getTypes()) {
return true;
}
}
return false;
}
private function hasReturnTypeHint(Tokens $tokens, int $index): bool
{
$endFuncIndex = $tokens->getPrevTokenOfKind($index, [')']);
$nextIndex = $tokens->getNextMeaningfulToken($endFuncIndex);
return $tokens[$nextIndex]->isGivenKind(CT::T_TYPE_COLON);
}
private function hasVoidReturn(Tokens $tokens, int $startIndex, int $endIndex): bool
{
$tokensAnalyzer = new TokensAnalyzer($tokens);
for ($i = $startIndex; $i < $endIndex; ++$i) {
if (
($tokens[$i]->isGivenKind(T_CLASS) && $tokensAnalyzer->isAnonymousClass($i))
|| ($tokens[$i]->isGivenKind(T_FUNCTION) && $tokensAnalyzer->isLambda($i))
) {
$i = $tokens->getNextTokenOfKind($i, ['{']);
$i = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $i);
continue;
}
if ($tokens[$i]->isGivenKind([T_YIELD, T_YIELD_FROM])) {
return false;
}
if (!$tokens[$i]->isGivenKind(T_RETURN)) {
continue;
}
$i = $tokens->getNextMeaningfulToken($i);
if (!$tokens[$i]->equals(';')) {
return false;
}
}
return true;
}
private function fixFunctionDefinition(Tokens $tokens, int $index): void
{
$endFuncIndex = $tokens->getPrevTokenOfKind($index, [')']);
$tokens->insertAt($endFuncIndex + 1, [
new Token([CT::T_TYPE_COLON, ':']),
new Token([T_WHITESPACE, ' ']),
new Token([T_STRING, 'void']),
]);
}
private function findReturnAnnotations(Tokens $tokens, int $index): array
{
do {
$index = $tokens->getPrevNonWhitespace($index);
} while ($tokens[$index]->isGivenKind([
T_ABSTRACT,
T_FINAL,
T_PRIVATE,
T_PROTECTED,
T_PUBLIC,
T_STATIC,
]));
if (!$tokens[$index]->isGivenKind(T_DOC_COMMENT)) {
return [];
}
$doc = new DocBlock($tokens[$index]->getContent());
return $doc->getAnnotationsOfType('return');
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\FunctionNotation;
use PhpCsFixer\AbstractProxyFixer;
use PhpCsFixer\Fixer\Basic\NoTrailingCommaInSinglelineFixer;
use PhpCsFixer\Fixer\DeprecatedFixerInterface;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
final class NoTrailingCommaInSinglelineFunctionCallFixer extends AbstractProxyFixer implements DeprecatedFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'When making a method or function call on a single line there MUST NOT be a trailing comma after the last argument.',
[new CodeSample("<?php\nfoo(\$a,);\n")]
);
}
public function getPriority(): int
{
return 3;
}
public function getSuccessorsNames(): array
{
return array_keys($this->proxyFixers);
}
protected function createProxyFixers(): array
{
$fixer = new NoTrailingCommaInSinglelineFixer();
$fixer->configure(['elements' => ['arguments', 'array_destructuring']]);
return [$fixer];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\FunctionNotation;
use PhpCsFixer\AbstractPhpdocToTypeDeclarationFixer;
use PhpCsFixer\DocBlock\Annotation;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\FixerDefinition\VersionSpecification;
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpdocToPropertyTypeFixer extends AbstractPhpdocToTypeDeclarationFixer
{
private array $skippedTypes = [
'mixed' => true,
'resource' => true,
'null' => true,
];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'EXPERIMENTAL: Takes `@var` annotation of non-mixed types and adjusts accordingly the property signature. Requires PHP >= 7.4.',
[
new VersionSpecificCodeSample(
'<?php
class Foo {
/** @var int */
private $foo;
/** @var \Traversable */
private $bar;
}
',
new VersionSpecification(70400)
),
new VersionSpecificCodeSample(
'<?php
class Foo {
/** @var int */
private $foo;
/** @var \Traversable */
private $bar;
}
',
new VersionSpecification(70400),
['scalar_types' => false]
),
],
null,
'This rule is EXPERIMENTAL and [1] is not covered with backward compatibility promise. [2] `@var` annotation is mandatory for the fixer to make changes, signatures of properties without it (no docblock) will not be fixed. [3] Manual actions might be required for newly typed properties that are read before initialization.'
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOC_COMMENT);
}
public function getPriority(): int
{
return 7;
}
protected function isSkippedType(string $type): bool
{
return isset($this->skippedTypes[$type]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = $tokens->count() - 1; 0 < $index; --$index) {
if ($tokens[$index]->isGivenKind([T_CLASS, T_TRAIT])) {
$this->fixClass($tokens, $index);
}
}
}
private function fixClass(Tokens $tokens, int $index): void
{
$index = $tokens->getNextTokenOfKind($index, ['{']);
$classEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $index);
for (; $index < $classEndIndex; ++$index) {
if ($tokens[$index]->isGivenKind(T_FUNCTION)) {
$index = $tokens->getNextTokenOfKind($index, ['{', ';']);
if ($tokens[$index]->equals('{')) {
$index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $index);
}
continue;
}
if (!$tokens[$index]->isGivenKind(T_DOC_COMMENT)) {
continue;
}
$docCommentIndex = $index;
$propertyIndices = $this->findNextUntypedPropertiesDeclaration($tokens, $docCommentIndex);
if ([] === $propertyIndices) {
continue;
}
$typeInfo = $this->resolveApplicableType(
$propertyIndices,
$this->getAnnotationsFromDocComment('var', $tokens, $docCommentIndex)
);
if (null === $typeInfo) {
continue;
}
[$propertyType, $isNullable] = $typeInfo;
if (\in_array($propertyType, ['callable', 'never', 'void'], true)) {
continue;
}
$newTokens = array_merge(
$this->createTypeDeclarationTokens($propertyType, $isNullable),
[new Token([T_WHITESPACE, ' '])]
);
$tokens->insertAt(current($propertyIndices), $newTokens);
$index = max($propertyIndices) + \count($newTokens) + 1;
$classEndIndex += \count($newTokens);
}
}
private function findNextUntypedPropertiesDeclaration(Tokens $tokens, int $index): array
{
do {
$index = $tokens->getNextMeaningfulToken($index);
} while ($tokens[$index]->isGivenKind([
T_PRIVATE,
T_PROTECTED,
T_PUBLIC,
T_STATIC,
T_VAR,
]));
if (!$tokens[$index]->isGivenKind(T_VARIABLE)) {
return [];
}
$properties = [];
while (!$tokens[$index]->equals(';')) {
if ($tokens[$index]->isGivenKind(T_VARIABLE)) {
$properties[$tokens[$index]->getContent()] = $index;
}
$index = $tokens->getNextMeaningfulToken($index);
}
return $properties;
}
private function resolveApplicableType(array $propertyIndices, array $annotations): ?array
{
$propertyTypes = [];
foreach ($annotations as $annotation) {
$propertyName = $annotation->getVariableName();
if (null === $propertyName) {
if (1 !== \count($propertyIndices)) {
continue;
}
$propertyName = key($propertyIndices);
}
if (!isset($propertyIndices[$propertyName])) {
continue;
}
$typeInfo = $this->getCommonTypeFromAnnotation($annotation, false);
if (!isset($propertyTypes[$propertyName])) {
$propertyTypes[$propertyName] = [];
} elseif ($typeInfo !== $propertyTypes[$propertyName]) {
return null;
}
$propertyTypes[$propertyName] = $typeInfo;
}
if (\count($propertyTypes) !== \count($propertyIndices)) {
return null;
}
$type = array_shift($propertyTypes);
foreach ($propertyTypes as $propertyType) {
if ($propertyType !== $type) {
return null;
}
}
return $type;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\FunctionNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class StaticLambdaFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Lambdas not (indirect) referencing `$this` must be declared `static`.',
[new CodeSample("<?php\n\$a = function () use (\$b)\n{ echo \$b;\n};\n")],
null,
'Risky when using `->bindTo` on lambdas without referencing to `$this`.'
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_FUNCTION, T_FN]);
}
public function isRisky(): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$analyzer = new TokensAnalyzer($tokens);
$expectedFunctionKinds = [T_FUNCTION, T_FN];
for ($index = $tokens->count() - 4; $index > 0; --$index) {
if (!$tokens[$index]->isGivenKind($expectedFunctionKinds) || !$analyzer->isLambda($index)) {
continue;
}
$prev = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$prev]->isGivenKind(T_STATIC)) {
continue;
}
$argumentsStartIndex = $tokens->getNextTokenOfKind($index, ['(']);
$argumentsEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $argumentsStartIndex);
if ($tokens[$index]->isGivenKind(T_FUNCTION)) {
$lambdaOpenIndex = $tokens->getNextTokenOfKind($argumentsEndIndex, ['{']);
$lambdaEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $lambdaOpenIndex);
} else {
$lambdaOpenIndex = $tokens->getNextTokenOfKind($argumentsEndIndex, [[T_DOUBLE_ARROW]]);
$lambdaEndIndex = $this->findExpressionEnd($tokens, $lambdaOpenIndex);
}
if ($this->hasPossibleReferenceToThis($tokens, $lambdaOpenIndex, $lambdaEndIndex)) {
continue;
}
$tokens->insertAt(
$index,
[
new Token([T_STATIC, 'static']),
new Token([T_WHITESPACE, ' ']),
]
);
$index -= 4;
}
}
private function findExpressionEnd(Tokens $tokens, int $index): int
{
$nextIndex = $tokens->getNextMeaningfulToken($index);
while (null !== $nextIndex) {
$nextToken = $tokens[$nextIndex];
if ($nextToken->equalsAny([',', ';', [T_CLOSE_TAG]])) {
break;
}
$blockType = Tokens::detectBlockType($nextToken);
if (null !== $blockType && $blockType['isStart']) {
$nextIndex = $tokens->findBlockEnd($blockType['type'], $nextIndex);
}
$index = $nextIndex;
$nextIndex = $tokens->getNextMeaningfulToken($index);
}
return $index;
}
private function hasPossibleReferenceToThis(Tokens $tokens, int $startIndex, int $endIndex): bool
{
for ($i = $startIndex; $i <= $endIndex; ++$i) {
if ($tokens[$i]->isGivenKind(T_VARIABLE) && '$this' === strtolower($tokens[$i]->getContent())) {
return true;
}
if ($tokens[$i]->isGivenKind([
T_INCLUDE,
T_INCLUDE_ONCE,
T_REQUIRE,
T_REQUIRE_ONCE,
CT::T_DYNAMIC_VAR_BRACE_OPEN,
T_EVAL,
])) {
return true;
}
if ($tokens[$i]->equals('$')) {
$nextIndex = $tokens->getNextMeaningfulToken($i);
if ($tokens[$nextIndex]->isGivenKind(T_VARIABLE)) {
return true;
}
}
if ($tokens[$i]->equals([T_STRING, 'parent'], false)) {
return true;
}
}
return false;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\FunctionNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class ReturnTypeDeclarationFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Adjust spacing around colon in return type declarations and backed enum types.',
[
new CodeSample(
"<?php\nfunction foo(int \$a):string {};\n"
),
new CodeSample(
"<?php\nfunction foo(int \$a):string {};\n",
['space_before' => 'none']
),
new CodeSample(
"<?php\nfunction foo(int \$a):string {};\n",
['space_before' => 'one']
),
],
'Rule is applied only in a PHP 7+ environment.'
);
}
public function getPriority(): int
{
return -17;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(CT::T_TYPE_COLON);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$oneSpaceBefore = 'one' === $this->configuration['space_before'];
for ($index = 0, $limit = $tokens->count(); $index < $limit; ++$index) {
if (!$tokens[$index]->isGivenKind(CT::T_TYPE_COLON)) {
continue;
}
$previousIndex = $index - 1;
$previousToken = $tokens[$previousIndex];
if ($previousToken->isWhitespace()) {
if (!$tokens[$tokens->getPrevNonWhitespace($index - 1)]->isComment()) {
if ($oneSpaceBefore) {
$tokens[$previousIndex] = new Token([T_WHITESPACE, ' ']);
} else {
$tokens->clearAt($previousIndex);
}
}
} elseif ($oneSpaceBefore) {
$tokenWasAdded = $tokens->ensureWhitespaceAtIndex($index, 0, ' ');
if ($tokenWasAdded) {
++$limit;
}
++$index;
}
++$index;
$tokenWasAdded = $tokens->ensureWhitespaceAtIndex($index, 0, ' ');
if ($tokenWasAdded) {
++$limit;
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('space_before', 'Spacing to apply before colon.'))
->setAllowedValues(['one', 'none'])
->setDefault('none')
->getOption(),
]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\FunctionNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\TypeAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\Tokens;
final class FunctionTypehintSpaceFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Ensure single space between function\'s argument and its typehint.',
[
new CodeSample("<?php\nfunction sample(array\$a)\n{}\n"),
new CodeSample("<?php\nfunction sample(array \$a)\n{}\n"),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_FUNCTION, T_FN]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$functionsAnalyzer = new FunctionsAnalyzer();
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
$token = $tokens[$index];
if (!$token->isGivenKind([T_FUNCTION, T_FN])) {
continue;
}
$arguments = $functionsAnalyzer->getFunctionArguments($tokens, $index);
foreach (array_reverse($arguments) as $argument) {
$type = $argument->getTypeAnalysis();
if (!$type instanceof TypeAnalysis) {
continue;
}
$tokens->ensureWhitespaceAtIndex($type->getEndIndex() + 1, 0, ' ');
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\FunctionNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\FixerDefinition\VersionSpecification;
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class MethodArgumentSpaceFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'In method arguments and method call, there MUST NOT be a space before each comma and there MUST be one space after each comma. Argument lists MAY be split across multiple lines, where each subsequent line is indented once. When doing so, the first item in the list MUST be on the next line, and there MUST be only one argument per line.',
[
new CodeSample(
"<?php\nfunction sample(\$a=10,\$b=20,\$c=30) {}\nsample(1, 2);\n",
null
),
new CodeSample(
"<?php\nfunction sample(\$a=10,\$b=20,\$c=30) {}\nsample(1, 2);\n",
['keep_multiple_spaces_after_comma' => false]
),
new CodeSample(
"<?php\nfunction sample(\$a=10,\$b=20,\$c=30) {}\nsample(1, 2);\n",
['keep_multiple_spaces_after_comma' => true]
),
new CodeSample(
"<?php\nfunction sample(\$a=10,\n \$b=20,\$c=30) {}\nsample(1,\n 2);\n",
['on_multiline' => 'ensure_fully_multiline']
),
new CodeSample(
"<?php\nfunction sample(\n \$a=10,\n \$b=20,\n \$c=30\n) {}\nsample(\n 1,\n 2\n);\n",
['on_multiline' => 'ensure_single_line']
),
new CodeSample(
"<?php\nfunction sample(\$a=10,\n \$b=20,\$c=30) {}\nsample(1, \n 2);\nsample('foo', 'foobarbaz', 'baz');\nsample('foobar', 'bar', 'baz');\n",
[
'on_multiline' => 'ensure_fully_multiline',
'keep_multiple_spaces_after_comma' => true,
]
),
new CodeSample(
"<?php\nfunction sample(\$a=10,\n \$b=20,\$c=30) {}\nsample(1, \n 2);\nsample('foo', 'foobarbaz', 'baz');\nsample('foobar', 'bar', 'baz');\n",
[
'on_multiline' => 'ensure_fully_multiline',
'keep_multiple_spaces_after_comma' => false,
]
),
new VersionSpecificCodeSample(
<<<'SAMPLE'
<?php
sample(
<<<EOD
foo
EOD
,
'bar'
);
SAMPLE
,
new VersionSpecification(70300),
['after_heredoc' => true]
),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound('(');
}
public function configure(array $configuration): void
{
parent::configure($configuration);
if (isset($configuration['ensure_fully_multiline'])) {
$this->configuration['on_multiline'] = $this->configuration['ensure_fully_multiline']
? 'ensure_fully_multiline'
: 'ignore';
}
}
public function getPriority(): int
{
return 30;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$expectedTokens = [T_LIST, T_FUNCTION, CT::T_USE_LAMBDA, T_FN, T_CLASS];
for ($index = $tokens->count() - 1; $index > 0; --$index) {
$token = $tokens[$index];
if (!$token->equals('(')) {
continue;
}
$meaningfulTokenBeforeParenthesis = $tokens[$tokens->getPrevMeaningfulToken($index)];
if (
$meaningfulTokenBeforeParenthesis->isKeyword()
&& !$meaningfulTokenBeforeParenthesis->isGivenKind($expectedTokens)
) {
continue;
}
$isMultiline = $this->fixFunction($tokens, $index);
if (
$isMultiline
&& 'ensure_fully_multiline' === $this->configuration['on_multiline']
&& !$meaningfulTokenBeforeParenthesis->isGivenKind(T_LIST)
) {
$this->ensureFunctionFullyMultiline($tokens, $index);
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('keep_multiple_spaces_after_comma', 'Whether keep multiple spaces after comma.'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
(new FixerOptionBuilder(
'on_multiline',
'Defines how to handle function arguments lists that contain newlines.'
))
->setAllowedValues(['ignore', 'ensure_single_line', 'ensure_fully_multiline'])
->setDefault('ensure_fully_multiline')
->getOption(),
(new FixerOptionBuilder('after_heredoc', 'Whether the whitespace between heredoc end and comma should be removed.'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
]);
}
private function fixFunction(Tokens $tokens, int $startFunctionIndex): bool
{
$isMultiline = false;
$endFunctionIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $startFunctionIndex);
$firstWhitespaceIndex = $this->findWhitespaceIndexAfterParenthesis($tokens, $startFunctionIndex, $endFunctionIndex);
$lastWhitespaceIndex = $this->findWhitespaceIndexAfterParenthesis($tokens, $endFunctionIndex, $startFunctionIndex);
foreach ([$firstWhitespaceIndex, $lastWhitespaceIndex] as $index) {
if (null === $index || !Preg::match('/\R/', $tokens[$index]->getContent())) {
continue;
}
if ('ensure_single_line' !== $this->configuration['on_multiline']) {
$isMultiline = true;
continue;
}
$newLinesRemoved = $this->ensureSingleLine($tokens, $index);
if (!$newLinesRemoved) {
$isMultiline = true;
}
}
for ($index = $endFunctionIndex - 1; $index > $startFunctionIndex; --$index) {
$token = $tokens[$index];
if ($token->equals(')')) {
$index = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index);
continue;
}
if ($token->isGivenKind(CT::T_ARRAY_SQUARE_BRACE_CLOSE)) {
$index = $tokens->findBlockStart(Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, $index);
continue;
}
if ($token->equals('}')) {
$index = $tokens->findBlockStart(Tokens::BLOCK_TYPE_CURLY_BRACE, $index);
continue;
}
if ($token->equals(',')) {
$this->fixSpace($tokens, $index);
if (!$isMultiline && $this->isNewline($tokens[$index + 1])) {
$isMultiline = true;
}
}
}
return $isMultiline;
}
private function findWhitespaceIndexAfterParenthesis(Tokens $tokens, int $startParenthesisIndex, int $endParenthesisIndex): ?int
{
$direction = $endParenthesisIndex > $startParenthesisIndex ? 1 : -1;
$startIndex = $startParenthesisIndex + $direction;
$endIndex = $endParenthesisIndex - $direction;
for ($index = $startIndex; $index !== $endIndex; $index += $direction) {
$token = $tokens[$index];
if ($token->isWhitespace()) {
return $index;
}
if (!$token->isComment()) {
break;
}
}
return null;
}
private function ensureSingleLine(Tokens $tokens, int $index): bool
{
$previousToken = $tokens[$index - 1];
if ($previousToken->isComment() && !str_starts_with($previousToken->getContent(), '/*')) {
return false;
}
$content = Preg::replace('/\R\h*/', '', $tokens[$index]->getContent());
$tokens->ensureWhitespaceAtIndex($index, 0, $content);
return true;
}
private function ensureFunctionFullyMultiline(Tokens $tokens, int $startFunctionIndex): void
{
$searchIndex = $startFunctionIndex;
do {
$prevWhitespaceTokenIndex = $tokens->getPrevTokenOfKind(
$searchIndex,
[[T_WHITESPACE]]
);
$searchIndex = $prevWhitespaceTokenIndex;
} while (null !== $prevWhitespaceTokenIndex
&& !str_contains($tokens[$prevWhitespaceTokenIndex]->getContent(), "\n")
);
if (null === $prevWhitespaceTokenIndex) {
$existingIndentation = '';
} else {
$existingIndentation = $tokens[$prevWhitespaceTokenIndex]->getContent();
$lastLineIndex = strrpos($existingIndentation, "\n");
$existingIndentation = false === $lastLineIndex
? $existingIndentation
: substr($existingIndentation, $lastLineIndex + 1)
;
}
$indentation = $existingIndentation.$this->whitespacesConfig->getIndent();
$endFunctionIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $startFunctionIndex);
$wasWhitespaceBeforeEndFunctionAddedAsNewToken = $tokens->ensureWhitespaceAtIndex(
$tokens[$endFunctionIndex - 1]->isWhitespace() ? $endFunctionIndex - 1 : $endFunctionIndex,
0,
$this->whitespacesConfig->getLineEnding().$existingIndentation
);
if ($wasWhitespaceBeforeEndFunctionAddedAsNewToken) {
++$endFunctionIndex;
}
for ($index = $endFunctionIndex - 1; $index > $startFunctionIndex; --$index) {
$token = $tokens[$index];
if ($token->equals(')')) {
$index = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index);
continue;
}
if ($token->isGivenKind(CT::T_ARRAY_SQUARE_BRACE_CLOSE)) {
$index = $tokens->findBlockStart(Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, $index);
continue;
}
if ($token->equals('}')) {
$index = $tokens->findBlockStart(Tokens::BLOCK_TYPE_CURLY_BRACE, $index);
continue;
}
if ($token->equals(',') && !$tokens[$tokens->getNextMeaningfulToken($index)]->equals(')')) {
$this->fixNewline($tokens, $index, $indentation);
}
}
$this->fixNewline($tokens, $startFunctionIndex, $indentation, false);
}
private function fixNewline(Tokens $tokens, int $index, string $indentation, bool $override = true): void
{
if ($tokens[$index + 1]->isComment()) {
return;
}
if ($tokens[$index + 2]->isComment()) {
$nextMeaningfulTokenIndex = $tokens->getNextMeaningfulToken($index + 2);
if (!$this->isNewline($tokens[$nextMeaningfulTokenIndex - 1])) {
$tokens->ensureWhitespaceAtIndex($nextMeaningfulTokenIndex, 0, $this->whitespacesConfig->getLineEnding().$indentation);
}
return;
}
$nextMeaningfulTokenIndex = $tokens->getNextMeaningfulToken($index);
if ($tokens[$nextMeaningfulTokenIndex]->equals(')')) {
return;
}
$tokens->ensureWhitespaceAtIndex($index + 1, 0, $this->whitespacesConfig->getLineEnding().$indentation);
}
private function fixSpace(Tokens $tokens, int $index): void
{
if ($tokens[$index - 1]->isWhitespace()) {
$prevIndex = $tokens->getPrevNonWhitespace($index - 1);
if (
!$tokens[$prevIndex]->equals(',') && !$tokens[$prevIndex]->isComment()
&& (true === $this->configuration['after_heredoc'] || !$tokens[$prevIndex]->isGivenKind(T_END_HEREDOC))
) {
$tokens->clearAt($index - 1);
}
}
$nextIndex = $index + 1;
$nextToken = $tokens[$nextIndex];
if ($nextToken->isWhitespace()) {
$newContent = $nextToken->getContent();
if ('ensure_single_line' === $this->configuration['on_multiline']) {
$newContent = Preg::replace('/\R/', '', $newContent);
}
if (
(false === $this->configuration['keep_multiple_spaces_after_comma'] || Preg::match('/\R/', $newContent))
&& !$this->isCommentLastLineToken($tokens, $index + 2)
) {
$newContent = ltrim($newContent, " \t");
}
$tokens[$nextIndex] = new Token([T_WHITESPACE, '' === $newContent ? ' ' : $newContent]);
return;
}
if (!$this->isCommentLastLineToken($tokens, $index + 1)) {
$tokens->insertAt($index + 1, new Token([T_WHITESPACE, ' ']));
}
}
private function isCommentLastLineToken(Tokens $tokens, int $index): bool
{
if (!$tokens[$index]->isComment() || !$tokens[$index + 1]->isWhitespace()) {
return false;
}
$content = $tokens[$index + 1]->getContent();
return $content !== ltrim($content, "\r\n");
}
private function isNewline(Token $token): bool
{
return $token->isWhitespace() && str_contains($token->getContent(), "\n");
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\FunctionNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\NamespacesAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\NamespaceUsesAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class DateTimeCreateFromFormatCallFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'The first argument of `DateTime::createFromFormat` method must start with `!`.',
[
new CodeSample("<?php \\DateTime::createFromFormat('Y-m-d', '2022-02-11');\n"),
],
"Consider this code:
`DateTime::createFromFormat('Y-m-d', '2022-02-11')`.
What value will be returned? '2022-02-11 00:00:00.0'? No, actual return value has 'H:i:s' section like '2022-02-11 16:55:37.0'.
Change 'Y-m-d' to '!Y-m-d', return value will be '2022-02-11 00:00:00.0'.
So, adding `!` to format string will make return value more intuitive.",
'Risky when depending on the actual timings being used even when not explicit set in format.'
);
}
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOUBLE_COLON);
}
public function isRisky(): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$argumentsAnalyzer = new ArgumentsAnalyzer();
$namespacesAnalyzer = new NamespacesAnalyzer();
$namespaceUsesAnalyzer = new NamespaceUsesAnalyzer();
foreach ($namespacesAnalyzer->getDeclarations($tokens) as $namespace) {
$scopeStartIndex = $namespace->getScopeStartIndex();
$useDeclarations = $namespaceUsesAnalyzer->getDeclarationsInNamespace($tokens, $namespace);
for ($index = $namespace->getScopeEndIndex(); $index > $scopeStartIndex; --$index) {
if (!$tokens[$index]->isGivenKind(T_DOUBLE_COLON)) {
continue;
}
$functionNameIndex = $tokens->getNextMeaningfulToken($index);
if (!$tokens[$functionNameIndex]->equals([T_STRING, 'createFromFormat'], false)) {
continue;
}
if (!$tokens[$tokens->getNextMeaningfulToken($functionNameIndex)]->equals('(')) {
continue;
}
$classNameIndex = $tokens->getPrevMeaningfulToken($index);
if (!$tokens[$classNameIndex]->equalsAny([[T_STRING, 'DateTime'], [T_STRING, 'DateTimeImmutable']], false)) {
continue;
}
$preClassNameIndex = $tokens->getPrevMeaningfulToken($classNameIndex);
if ($tokens[$preClassNameIndex]->isGivenKind(T_NS_SEPARATOR)) {
if ($tokens[$tokens->getPrevMeaningfulToken($preClassNameIndex)]->isGivenKind(T_STRING)) {
continue;
}
} elseif (!$namespace->isGlobalNamespace()) {
continue;
} else {
foreach ($useDeclarations as $useDeclaration) {
foreach (['datetime', 'datetimeimmutable'] as $name) {
if ($name === strtolower($useDeclaration->getShortName()) && $name !== strtolower($useDeclaration->getFullName())) {
continue 3;
}
}
}
}
$openIndex = $tokens->getNextTokenOfKind($functionNameIndex, ['(']);
$closeIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openIndex);
$argumentIndex = $this->getFirstArgumentTokenIndex($tokens, $argumentsAnalyzer->getArguments($tokens, $openIndex, $closeIndex));
if (null === $argumentIndex) {
continue;
}
$format = $tokens[$argumentIndex]->getContent();
if (\strlen($format) < 3) {
continue;
}
$offset = 'b' === $format[0] || 'B' === $format[0] ? 2 : 1;
if ('!' === $format[$offset]) {
continue;
}
$tokens->clearAt($argumentIndex);
$tokens->insertAt($argumentIndex, new Token([T_CONSTANT_ENCAPSED_STRING, substr_replace($format, '!', $offset, 0)]));
}
}
}
private function getFirstArgumentTokenIndex(Tokens $tokens, array $arguments): ?int
{
if (2 !== \count($arguments)) {
return null;
}
$argumentStartIndex = array_key_first($arguments);
$argumentEndIndex = $arguments[$argumentStartIndex];
$argumentStartIndex = $tokens->getNextMeaningfulToken($argumentStartIndex - 1);
if (
$argumentStartIndex !== $argumentEndIndex
&& $tokens->getNextMeaningfulToken($argumentStartIndex) <= $argumentEndIndex
) {
return null;
}
return !$tokens[$argumentStartIndex]->isGivenKind(T_CONSTANT_ENCAPSED_STRING)
? null
: $argumentStartIndex;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\FunctionNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\Tokens;
final class NoUselessSprintfFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'There must be no `sprintf` calls with only the first argument.',
[
new CodeSample(
"<?php\n\$foo = sprintf('bar');\n"
),
],
null,
'Risky when if the `sprintf` function is overridden.'
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_STRING);
}
public function isRisky(): bool
{
return true;
}
public function getPriority(): int
{
return 42;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$functionAnalyzer = new FunctionsAnalyzer();
$argumentsAnalyzer = new ArgumentsAnalyzer();
for ($index = \count($tokens) - 1; $index > 0; --$index) {
if (!$tokens[$index]->isGivenKind(T_STRING)) {
continue;
}
if ('sprintf' !== strtolower($tokens[$index]->getContent())) {
continue;
}
if (!$functionAnalyzer->isGlobalFunctionCall($tokens, $index)) {
continue;
}
$openParenthesisIndex = $tokens->getNextTokenOfKind($index, ['(']);
if ($tokens[$tokens->getNextMeaningfulToken($openParenthesisIndex)]->isGivenKind(T_ELLIPSIS)) {
continue;
}
$closeParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openParenthesisIndex);
if (1 !== $argumentsAnalyzer->countArguments($tokens, $openParenthesisIndex, $closeParenthesisIndex)) {
continue;
}
$tokens->clearTokenAndMergeSurroundingWhitespace($closeParenthesisIndex);
$prevMeaningfulTokenIndex = $tokens->getPrevMeaningfulToken($closeParenthesisIndex);
if ($tokens[$prevMeaningfulTokenIndex]->equals(',')) {
$tokens->clearTokenAndMergeSurroundingWhitespace($prevMeaningfulTokenIndex);
}
$tokens->clearTokenAndMergeSurroundingWhitespace($openParenthesisIndex);
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
$prevMeaningfulTokenIndex = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$prevMeaningfulTokenIndex]->isGivenKind(T_NS_SEPARATOR)) {
$tokens->clearTokenAndMergeSurroundingWhitespace($prevMeaningfulTokenIndex);
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\FunctionNotation;
use PhpCsFixer\AbstractFopenFlagFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class FopenFlagOrderFixer extends AbstractFopenFlagFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Order the flags in `fopen` calls, `b` and `t` must be last.',
[new CodeSample("<?php\n\$a = fopen(\$foo, 'br+');\n")],
null,
'Risky when the function `fopen` is overridden.'
);
}
protected function fixFopenFlagToken(Tokens $tokens, int $argumentStartIndex, int $argumentEndIndex): void
{
$argumentFlagIndex = null;
for ($i = $argumentStartIndex; $i <= $argumentEndIndex; ++$i) {
if ($tokens[$i]->isGivenKind([T_WHITESPACE, T_COMMENT, T_DOC_COMMENT])) {
continue;
}
if (null !== $argumentFlagIndex) {
return;
}
$argumentFlagIndex = $i;
}
if (null === $argumentFlagIndex || !$tokens[$argumentFlagIndex]->isGivenKind(T_CONSTANT_ENCAPSED_STRING)) {
return;
}
$content = $tokens[$argumentFlagIndex]->getContent();
$contentQuote = $content[0];
if ('b' === $contentQuote || 'B' === $contentQuote) {
$binPrefix = $contentQuote;
$contentQuote = $content[1];
$mode = substr($content, 2, -1);
} else {
$binPrefix = '';
$mode = substr($content, 1, -1);
}
$modeLength = \strlen($mode);
if ($modeLength < 2) {
return;
}
if (false === $this->isValidModeString($mode)) {
return;
}
$split = $this->sortFlags(Preg::split('#([^\+]\+?)#', $mode, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE));
$newContent = $binPrefix.$contentQuote.implode('', $split).$contentQuote;
if ($content !== $newContent) {
$tokens[$argumentFlagIndex] = new Token([T_CONSTANT_ENCAPSED_STRING, $newContent]);
}
}
private function sortFlags(array $flags): array
{
usort(
$flags,
static function (string $flag1, string $flag2): int {
if ($flag1 === $flag2) {
return 0;
}
if ('b' === $flag1) {
return 1;
}
if ('b' === $flag2) {
return -1;
}
if ('t' === $flag1) {
return 1;
}
if ('t' === $flag2) {
return -1;
}
return $flag1 < $flag2 ? -1 : 1;
}
);
return $flags;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\FunctionNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class CombineNestedDirnameFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Replace multiple nested calls of `dirname` by only one call with second `$level` parameter. Requires PHP >= 7.0.',
[
new CodeSample(
"<?php\ndirname(dirname(dirname(\$path)));\n"
),
],
null,
'Risky when the function `dirname` is overridden.'
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_STRING);
}
public function isRisky(): bool
{
return true;
}
public function getPriority(): int
{
return 35;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = $tokens->count() - 1; 0 <= $index; --$index) {
$dirnameInfo = $this->getDirnameInfo($tokens, $index);
if (!$dirnameInfo) {
continue;
}
$prev = $tokens->getPrevMeaningfulToken($dirnameInfo['indexes'][0]);
if (!$tokens[$prev]->equals('(')) {
continue;
}
$prev = $tokens->getPrevMeaningfulToken($prev);
$firstArgumentEnd = $dirnameInfo['end'];
$dirnameInfoArray = [$dirnameInfo];
while ($dirnameInfo = $this->getDirnameInfo($tokens, $prev, $firstArgumentEnd)) {
$dirnameInfoArray[] = $dirnameInfo;
$prev = $tokens->getPrevMeaningfulToken($dirnameInfo['indexes'][0]);
if (!$tokens[$prev]->equals('(')) {
break;
}
$prev = $tokens->getPrevMeaningfulToken($prev);
$firstArgumentEnd = $dirnameInfo['end'];
}
if (\count($dirnameInfoArray) > 1) {
$this->combineDirnames($tokens, $dirnameInfoArray);
}
$index = $prev;
}
}
private function getDirnameInfo(Tokens $tokens, int $index, ?int $firstArgumentEndIndex = null)
{
if (!$tokens[$index]->equals([T_STRING, 'dirname'], false)) {
return false;
}
if (!(new FunctionsAnalyzer())->isGlobalFunctionCall($tokens, $index)) {
return false;
}
$info = ['indexes' => []];
$prev = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$prev]->isGivenKind(T_NS_SEPARATOR)) {
$info['indexes'][] = $prev;
}
$info['indexes'][] = $index;
$next = $tokens->getNextMeaningfulToken($index);
$info['indexes'][] = $next;
if (null !== $firstArgumentEndIndex) {
$next = $tokens->getNextMeaningfulToken($firstArgumentEndIndex);
} else {
$next = $tokens->getNextMeaningfulToken($next);
if ($tokens[$next]->equals(')')) {
return false;
}
while (!$tokens[$next]->equalsAny([',', ')'])) {
$blockType = Tokens::detectBlockType($tokens[$next]);
if (null !== $blockType) {
$next = $tokens->findBlockEnd($blockType['type'], $next);
}
$next = $tokens->getNextMeaningfulToken($next);
}
}
$info['indexes'][] = $next;
if ($tokens[$next]->equals(',')) {
$next = $tokens->getNextMeaningfulToken($next);
$info['indexes'][] = $next;
}
if ($tokens[$next]->equals(')')) {
$info['levels'] = 1;
$info['end'] = $next;
return $info;
}
if (!$tokens[$next]->isGivenKind(T_LNUMBER)) {
return false;
}
$info['secondArgument'] = $next;
$info['levels'] = (int) $tokens[$next]->getContent();
$next = $tokens->getNextMeaningfulToken($next);
if ($tokens[$next]->equals(',')) {
$info['indexes'][] = $next;
$next = $tokens->getNextMeaningfulToken($next);
}
if (!$tokens[$next]->equals(')')) {
return false;
}
$info['indexes'][] = $next;
$info['end'] = $next;
return $info;
}
private function combineDirnames(Tokens $tokens, array $dirnameInfoArray): void
{
$outerDirnameInfo = array_pop($dirnameInfoArray);
$levels = $outerDirnameInfo['levels'];
foreach ($dirnameInfoArray as $dirnameInfo) {
$levels += $dirnameInfo['levels'];
foreach ($dirnameInfo['indexes'] as $index) {
$tokens->removeLeadingWhitespace($index);
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
}
}
$levelsToken = new Token([T_LNUMBER, (string) $levels]);
if (isset($outerDirnameInfo['secondArgument'])) {
$tokens[$outerDirnameInfo['secondArgument']] = $levelsToken;
} else {
$prev = $tokens->getPrevMeaningfulToken($outerDirnameInfo['end']);
$items = [];
if (!$tokens[$prev]->equals(',')) {
$items = [new Token(','), new Token([T_WHITESPACE, ' '])];
}
$items[] = $levelsToken;
$tokens->insertAt($outerDirnameInfo['end'], $items);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\FunctionNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Tokens;
final class NoSpacesAfterFunctionNameFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'When making a method or function call, there MUST NOT be a space between the method or function name and the opening parenthesis.',
[new CodeSample("<?php\nrequire ('sample.php');\necho (test (3));\nexit (1);\n\$func ();\n")]
);
}
public function getPriority(): int
{
return 2;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound(array_merge($this->getFunctionyTokenKinds(), [T_STRING]));
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$functionyTokens = $this->getFunctionyTokenKinds();
$languageConstructionTokens = $this->getLanguageConstructionTokenKinds();
$braceTypes = $this->getBraceAfterVariableKinds();
foreach ($tokens as $index => $token) {
if (!$token->equals('(')) {
continue;
}
$lastTokenIndex = $tokens->getPrevNonWhitespace($index);
$endParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index);
$nextNonWhiteSpace = $tokens->getNextMeaningfulToken($endParenthesisIndex);
if (
null !== $nextNonWhiteSpace
&& $tokens[$nextNonWhiteSpace]->equals('?')
&& $tokens[$lastTokenIndex]->isGivenKind($languageConstructionTokens)
) {
continue;
}
if ($tokens[$lastTokenIndex]->isGivenKind($functionyTokens)) {
$this->fixFunctionCall($tokens, $index);
} elseif ($tokens[$lastTokenIndex]->isGivenKind(T_STRING)) {
$possibleDefinitionIndex = $tokens->getPrevMeaningfulToken($lastTokenIndex);
if (!$tokens[$possibleDefinitionIndex]->isGivenKind(T_FUNCTION)) {
$this->fixFunctionCall($tokens, $index);
}
} elseif ($tokens[$lastTokenIndex]->equalsAny($braceTypes)) {
$block = Tokens::detectBlockType($tokens[$lastTokenIndex]);
if (
Tokens::BLOCK_TYPE_ARRAY_INDEX_CURLY_BRACE === $block['type']
|| Tokens::BLOCK_TYPE_DYNAMIC_VAR_BRACE === $block['type']
|| Tokens::BLOCK_TYPE_INDEX_SQUARE_BRACE === $block['type']
|| Tokens::BLOCK_TYPE_PARENTHESIS_BRACE === $block['type']
) {
$this->fixFunctionCall($tokens, $index);
}
}
}
}
private function fixFunctionCall(Tokens $tokens, int $index): void
{
if ($tokens[$index - 1]->isWhitespace()) {
$tokens->clearAt($index - 1);
}
}
private function getBraceAfterVariableKinds(): array
{
static $tokens = [
')',
']',
[CT::T_DYNAMIC_VAR_BRACE_CLOSE],
[CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE],
];
return $tokens;
}
private function getFunctionyTokenKinds(): array
{
static $tokens = [
T_ARRAY,
T_ECHO,
T_EMPTY,
T_EVAL,
T_EXIT,
T_INCLUDE,
T_INCLUDE_ONCE,
T_ISSET,
T_LIST,
T_PRINT,
T_REQUIRE,
T_REQUIRE_ONCE,
T_UNSET,
T_VARIABLE,
];
return $tokens;
}
private function getLanguageConstructionTokenKinds(): array
{
static $languageConstructionTokens = [
T_ECHO,
T_PRINT,
T_INCLUDE,
T_INCLUDE_ONCE,
T_REQUIRE,
T_REQUIRE_ONCE,
];
return $languageConstructionTokens;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\FunctionNotation;
use PhpCsFixer\AbstractPhpdocToTypeDeclarationFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\FixerDefinition\VersionSpecification;
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpdocToReturnTypeFixer extends AbstractPhpdocToTypeDeclarationFixer
{
private array $excludeFuncNames = [
[T_STRING, '__construct'],
[T_STRING, '__destruct'],
[T_STRING, '__clone'],
];
private array $skippedTypes = [
'mixed' => true,
'resource' => true,
'null' => true,
];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'EXPERIMENTAL: Takes `@return` annotation of non-mixed types and adjusts accordingly the function signature. Requires PHP >= 7.0.',
[
new CodeSample(
'<?php
/** @return \My\Bar */
function f1()
{}
/** @return void */
function f2()
{}
'
),
new VersionSpecificCodeSample(
'<?php
/** @return object */
function my_foo()
{}
',
new VersionSpecification(70200)
),
new CodeSample(
'<?php
/** @return Foo */
function foo() {}
/** @return string */
function bar() {}
',
['scalar_types' => false]
),
new VersionSpecificCodeSample(
'<?php
final class Foo {
/**
* @return static
*/
public function create($prototype) {
return new static($prototype);
}
}
',
new VersionSpecification(80000)
),
],
null,
'This rule is EXPERIMENTAL and [1] is not covered with backward compatibility promise. [2] `@return` annotation is mandatory for the fixer to make changes, signatures of methods without it (no docblock, inheritdocs) will not be fixed. [3] Manual actions are required if inherited signatures are not properly documented.'
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_FUNCTION, T_FN]);
}
public function getPriority(): int
{
return 13;
}
protected function isSkippedType(string $type): bool
{
return isset($this->skippedTypes[$type]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
if (\PHP_VERSION_ID >= 80000) {
unset($this->skippedTypes['mixed']);
}
for ($index = $tokens->count() - 1; 0 < $index; --$index) {
if (!$tokens[$index]->isGivenKind([T_FUNCTION, T_FN])) {
continue;
}
$funcName = $tokens->getNextMeaningfulToken($index);
if ($tokens[$funcName]->equalsAny($this->excludeFuncNames, false)) {
continue;
}
$docCommentIndex = $this->findFunctionDocComment($tokens, $index);
if (null === $docCommentIndex) {
continue;
}
$returnTypeAnnotation = $this->getAnnotationsFromDocComment('return', $tokens, $docCommentIndex);
if (1 !== \count($returnTypeAnnotation)) {
continue;
}
$typeInfo = $this->getCommonTypeFromAnnotation(current($returnTypeAnnotation), true);
if (null === $typeInfo) {
continue;
}
[$returnType, $isNullable] = $typeInfo;
$startIndex = $tokens->getNextTokenOfKind($index, ['{', ';']);
if ($this->hasReturnTypeHint($tokens, $startIndex)) {
continue;
}
if (!$this->isValidSyntax(sprintf('<?php function f():%s {}', $returnType))) {
continue;
}
$endFuncIndex = $tokens->getPrevTokenOfKind($startIndex, [')']);
$tokens->insertAt(
$endFuncIndex + 1,
array_merge(
[
new Token([CT::T_TYPE_COLON, ':']),
new Token([T_WHITESPACE, ' ']),
],
$this->createTypeDeclarationTokens($returnType, $isNullable)
)
);
}
}
private function hasReturnTypeHint(Tokens $tokens, int $index): bool
{
$endFuncIndex = $tokens->getPrevTokenOfKind($index, [')']);
$nextIndex = $tokens->getNextMeaningfulToken($endFuncIndex);
return $tokens[$nextIndex]->isGivenKind(CT::T_TYPE_COLON);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\FunctionNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\NamespacesAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
final class NativeFunctionInvocationFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public const SET_ALL = '@all';
public const SET_COMPILER_OPTIMIZED = '@compiler_optimized';
public const SET_INTERNAL = '@internal';
private $functionFilter;
public function configure(array $configuration): void
{
parent::configure($configuration);
$this->functionFilter = $this->getFunctionFilter();
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Add leading `\` before function invocation to speed up resolving.',
[
new CodeSample(
'<?php
function baz($options)
{
if (!array_key_exists("foo", $options)) {
throw new \InvalidArgumentException();
}
return json_encode($options);
}
'
),
new CodeSample(
'<?php
function baz($options)
{
if (!array_key_exists("foo", $options)) {
throw new \InvalidArgumentException();
}
return json_encode($options);
}
',
[
'exclude' => [
'json_encode',
],
]
),
new CodeSample(
'<?php
namespace space1 {
echo count([1]);
}
namespace {
echo count([1]);
}
',
['scope' => 'all']
),
new CodeSample(
'<?php
namespace space1 {
echo count([1]);
}
namespace {
echo count([1]);
}
',
['scope' => 'namespaced']
),
new CodeSample(
'<?php
myGlobalFunction();
count();
',
['include' => ['myGlobalFunction']]
),
new CodeSample(
'<?php
myGlobalFunction();
count();
',
['include' => ['@all']]
),
new CodeSample(
'<?php
myGlobalFunction();
count();
',
['include' => ['@internal']]
),
new CodeSample(
'<?php
$a .= str_repeat($a, 4);
$c = get_class($d);
',
['include' => ['@compiler_optimized']]
),
],
null,
'Risky when any of the functions are overridden.'
);
}
public function getPriority(): int
{
return 1;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_STRING);
}
public function isRisky(): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
if ('all' === $this->configuration['scope']) {
$this->fixFunctionCalls($tokens, $this->functionFilter, 0, \count($tokens) - 1, false);
return;
}
$namespaces = (new NamespacesAnalyzer())->getDeclarations($tokens);
foreach (array_reverse($namespaces) as $namespace) {
$this->fixFunctionCalls($tokens, $this->functionFilter, $namespace->getScopeStartIndex(), $namespace->getScopeEndIndex(), $namespace->isGlobalNamespace());
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('exclude', 'List of functions to ignore.'))
->setAllowedTypes(['array'])
->setAllowedValues([static function (array $value): bool {
foreach ($value as $functionName) {
if (!\is_string($functionName) || '' === trim($functionName) || trim($functionName) !== $functionName) {
throw new InvalidOptionsException(sprintf(
'Each element must be a non-empty, trimmed string, got "%s" instead.',
get_debug_type($functionName)
));
}
}
return true;
}])
->setDefault([])
->getOption(),
(new FixerOptionBuilder('include', 'List of function names or sets to fix. Defined sets are `@internal` (all native functions), `@all` (all global functions) and `@compiler_optimized` (functions that are specially optimized by Zend).'))
->setAllowedTypes(['array'])
->setAllowedValues([static function (array $value): bool {
foreach ($value as $functionName) {
if (!\is_string($functionName) || '' === trim($functionName) || trim($functionName) !== $functionName) {
throw new InvalidOptionsException(sprintf(
'Each element must be a non-empty, trimmed string, got "%s" instead.',
get_debug_type($functionName)
));
}
$sets = [
self::SET_ALL,
self::SET_INTERNAL,
self::SET_COMPILER_OPTIMIZED,
];
if (str_starts_with($functionName, '@') && !\in_array($functionName, $sets, true)) {
throw new InvalidOptionsException(sprintf('Unknown set "%s", known sets are "%s".', $functionName, implode('", "', $sets)));
}
}
return true;
}])
->setDefault([self::SET_COMPILER_OPTIMIZED])
->getOption(),
(new FixerOptionBuilder('scope', 'Only fix function calls that are made within a namespace or fix all.'))
->setAllowedValues(['all', 'namespaced'])
->setDefault('all')
->getOption(),
(new FixerOptionBuilder('strict', 'Whether leading `\` of function call not meant to have it should be removed.'))
->setAllowedTypes(['bool'])
->setDefault(true)
->getOption(),
]);
}
private function fixFunctionCalls(Tokens $tokens, callable $functionFilter, int $start, int $end, bool $tryToRemove): void
{
$functionsAnalyzer = new FunctionsAnalyzer();
$tokensToInsert = [];
for ($index = $start; $index < $end; ++$index) {
if (!$functionsAnalyzer->isGlobalFunctionCall($tokens, $index)) {
continue;
}
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if (!$functionFilter($tokens[$index]->getContent()) || $tryToRemove) {
if (false === $this->configuration['strict']) {
continue;
}
if ($tokens[$prevIndex]->isGivenKind(T_NS_SEPARATOR)) {
$tokens->clearTokenAndMergeSurroundingWhitespace($prevIndex);
}
continue;
}
if ($tokens[$prevIndex]->isGivenKind(T_NS_SEPARATOR)) {
continue;
}
$tokensToInsert[$index] = new Token([T_NS_SEPARATOR, '\\']);
}
$tokens->insertSlices($tokensToInsert);
}
private function getFunctionFilter(): callable
{
$exclude = $this->normalizeFunctionNames($this->configuration['exclude']);
if (\in_array(self::SET_ALL, $this->configuration['include'], true)) {
if (\count($exclude) > 0) {
return static function (string $functionName) use ($exclude): bool {
return !isset($exclude[strtolower($functionName)]);
};
}
return static function (): bool {
return true;
};
}
$include = [];
if (\in_array(self::SET_INTERNAL, $this->configuration['include'], true)) {
$include = $this->getAllInternalFunctionsNormalized();
} elseif (\in_array(self::SET_COMPILER_OPTIMIZED, $this->configuration['include'], true)) {
$include = $this->getAllCompilerOptimizedFunctionsNormalized();
}
foreach ($this->configuration['include'] as $additional) {
if (!str_starts_with($additional, '@')) {
$include[strtolower($additional)] = true;
}
}
if (\count($exclude) > 0) {
return static function (string $functionName) use ($include, $exclude): bool {
return isset($include[strtolower($functionName)]) && !isset($exclude[strtolower($functionName)]);
};
}
return static function (string $functionName) use ($include): bool {
return isset($include[strtolower($functionName)]);
};
}
private function getAllCompilerOptimizedFunctionsNormalized(): array
{
return $this->normalizeFunctionNames([
'array_key_exists',
'array_slice',
'assert',
'boolval',
'call_user_func',
'call_user_func_array',
'chr',
'count',
'defined',
'doubleval',
'floatval',
'func_get_args',
'func_num_args',
'get_called_class',
'get_class',
'gettype',
'in_array',
'intval',
'is_array',
'is_bool',
'is_double',
'is_float',
'is_int',
'is_integer',
'is_long',
'is_null',
'is_object',
'is_real',
'is_resource',
'is_scalar',
'is_string',
'ord',
'sizeof',
'strlen',
'strval',
'constant',
'define',
'dirname',
'extension_loaded',
'function_exists',
'is_callable',
'ini_get',
]);
}
private function getAllInternalFunctionsNormalized(): array
{
return $this->normalizeFunctionNames(get_defined_functions()['internal']);
}
private function normalizeFunctionNames(array $functionNames): array
{
foreach ($functionNames as $index => $functionName) {
$functionNames[strtolower($functionName)] = true;
unset($functionNames[$index]);
}
return $functionNames;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\FunctionNotation;
use PhpCsFixer\AbstractFopenFlagFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class FopenFlagsFixer extends AbstractFopenFlagFixer implements ConfigurableFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'The flags in `fopen` calls must omit `t`, and `b` must be omitted or included consistently.',
[
new CodeSample("<?php\n\$a = fopen(\$foo, 'rwt');\n"),
new CodeSample("<?php\n\$a = fopen(\$foo, 'rwt');\n", ['b_mode' => false]),
],
null,
'Risky when the function `fopen` is overridden.'
);
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('b_mode', 'The `b` flag must be used (`true`) or omitted (`false`).'))
->setAllowedTypes(['bool'])
->setDefault(true)
->getOption(),
]);
}
protected function fixFopenFlagToken(Tokens $tokens, int $argumentStartIndex, int $argumentEndIndex): void
{
$argumentFlagIndex = null;
for ($i = $argumentStartIndex; $i <= $argumentEndIndex; ++$i) {
if ($tokens[$i]->isGivenKind([T_WHITESPACE, T_COMMENT, T_DOC_COMMENT])) {
continue;
}
if (null !== $argumentFlagIndex) {
return;
}
$argumentFlagIndex = $i;
}
if (null === $argumentFlagIndex || !$tokens[$argumentFlagIndex]->isGivenKind(T_CONSTANT_ENCAPSED_STRING)) {
return;
}
$content = $tokens[$argumentFlagIndex]->getContent();
$contentQuote = $content[0];
if ('b' === $contentQuote || 'B' === $contentQuote) {
$binPrefix = $contentQuote;
$contentQuote = $content[1];
$mode = substr($content, 2, -1);
} else {
$binPrefix = '';
$mode = substr($content, 1, -1);
}
if (false === $this->isValidModeString($mode)) {
return;
}
$mode = str_replace('t', '', $mode);
if (true === $this->configuration['b_mode']) {
if (!str_contains($mode, 'b')) {
$mode .= 'b';
}
} else {
$mode = str_replace('b', '', $mode);
}
$newContent = $binPrefix.$contentQuote.$mode.$contentQuote;
if ($content !== $newContent) {
$tokens[$argumentFlagIndex] = new Token([T_CONSTANT_ENCAPSED_STRING, $newContent]);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\FunctionNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\ArgumentAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NullableTypeDeclarationForDefaultNullValueFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Adds or removes `?` before type declarations for parameters with a default `null` value.',
[
new CodeSample(
"<?php\nfunction sample(string \$str = null)\n{}\n"
),
new CodeSample(
"<?php\nfunction sample(?string \$str = null)\n{}\n",
['use_nullable_type_declaration' => false]
),
],
'Rule is applied only in a PHP 7.1+ environment.'
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_VARIABLE) && $tokens->isAnyTokenKindsFound([T_FUNCTION, T_FN]);
}
public function getPriority(): int
{
return 1;
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('use_nullable_type_declaration', 'Whether to add or remove `?` before type declarations for parameters with a default `null` value.'))
->setAllowedTypes(['bool'])
->setDefault(true)
->getOption(),
]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$functionsAnalyzer = new FunctionsAnalyzer();
$tokenKinds = [T_FUNCTION, T_FN];
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
$token = $tokens[$index];
if (!$token->isGivenKind($tokenKinds)) {
continue;
}
$arguments = $functionsAnalyzer->getFunctionArguments($tokens, $index);
$this->fixFunctionParameters($tokens, $arguments);
}
}
private function fixFunctionParameters(Tokens $tokens, array $arguments): void
{
$constructorPropertyModifiers = [
CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC,
CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED,
CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE,
];
if (\defined('T_READONLY')) {
$constructorPropertyModifiers[] = T_READONLY;
}
foreach (array_reverse($arguments) as $argumentInfo) {
if (
!$argumentInfo->hasTypeAnalysis()
|| str_contains($argumentInfo->getTypeAnalysis()->getName(), '|')
|| !$argumentInfo->hasDefault() || 'null' !== strtolower($argumentInfo->getDefault())
) {
continue;
}
$argumentTypeInfo = $argumentInfo->getTypeAnalysis();
if (\PHP_VERSION_ID >= 80000 && false === $this->configuration['use_nullable_type_declaration']) {
$visibility = $tokens[$tokens->getPrevMeaningfulToken($argumentTypeInfo->getStartIndex())];
if ($visibility->isGivenKind($constructorPropertyModifiers)) {
continue;
}
}
if (true === $this->configuration['use_nullable_type_declaration']) {
if (!$argumentTypeInfo->isNullable() && 'mixed' !== $argumentTypeInfo->getName()) {
$tokens->insertAt($argumentTypeInfo->getStartIndex(), new Token([CT::T_NULLABLE_TYPE, '?']));
}
} else {
if ($argumentTypeInfo->isNullable()) {
$tokens->removeTrailingWhitespace($argumentTypeInfo->getStartIndex());
$tokens->clearTokenAndMergeSurroundingWhitespace($argumentTypeInfo->getStartIndex());
}
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\FunctionNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Tokens;
final class NoUnreachableDefaultArgumentValueFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'In function arguments there must not be arguments with default values before non-default ones.',
[
new CodeSample(
'<?php
function example($foo = "two words", $bar) {}
'
),
],
null,
'Modifies the signature of functions; therefore risky when using systems (such as some Symfony components) that rely on those (for example through reflection).'
);
}
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_FUNCTION, T_FN]);
}
public function isRisky(): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$functionKinds = [T_FUNCTION, T_FN];
for ($i = 0, $l = $tokens->count(); $i < $l; ++$i) {
if (!$tokens[$i]->isGivenKind($functionKinds)) {
continue;
}
$startIndex = $tokens->getNextTokenOfKind($i, ['(']);
$i = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $startIndex);
$this->fixFunctionDefinition($tokens, $startIndex, $i);
}
}
private function fixFunctionDefinition(Tokens $tokens, int $startIndex, int $endIndex): void
{
$lastArgumentIndex = $this->getLastNonDefaultArgumentIndex($tokens, $startIndex, $endIndex);
if (null === $lastArgumentIndex) {
return;
}
for ($i = $lastArgumentIndex; $i > $startIndex; --$i) {
$token = $tokens[$i];
if ($token->isGivenKind(T_VARIABLE)) {
$lastArgumentIndex = $i;
continue;
}
if (!$token->equals('=') || $this->isNonNullableTypehintedNullableVariable($tokens, $i)) {
continue;
}
$this->removeDefaultValue($tokens, $i, $this->getDefaultValueEndIndex($tokens, $lastArgumentIndex));
}
}
private function getLastNonDefaultArgumentIndex(Tokens $tokens, int $startIndex, int $endIndex): ?int
{
for ($i = $endIndex - 1; $i > $startIndex; --$i) {
$token = $tokens[$i];
if ($token->equals('=')) {
$i = $tokens->getPrevMeaningfulToken($i);
continue;
}
if ($token->isGivenKind(T_VARIABLE) && !$this->isEllipsis($tokens, $i)) {
return $i;
}
}
return null;
}
private function isEllipsis(Tokens $tokens, int $variableIndex): bool
{
return $tokens[$tokens->getPrevMeaningfulToken($variableIndex)]->isGivenKind(T_ELLIPSIS);
}
private function getDefaultValueEndIndex(Tokens $tokens, int $index): int
{
do {
$index = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$index]->isGivenKind(CT::T_ATTRIBUTE_CLOSE)) {
$index = $tokens->findBlockStart(Tokens::BLOCK_TYPE_ATTRIBUTE, $index);
}
} while (!$tokens[$index]->equals(','));
return $tokens->getPrevMeaningfulToken($index);
}
private function removeDefaultValue(Tokens $tokens, int $startIndex, int $endIndex): void
{
for ($i = $startIndex; $i <= $endIndex;) {
$tokens->clearTokenAndMergeSurroundingWhitespace($i);
$this->clearWhitespacesBeforeIndex($tokens, $i);
$i = $tokens->getNextMeaningfulToken($i);
}
}
private function isNonNullableTypehintedNullableVariable(Tokens $tokens, int $index): bool
{
$nextToken = $tokens[$tokens->getNextMeaningfulToken($index)];
if (!$nextToken->equals([T_STRING, 'null'], false)) {
return false;
}
$variableIndex = $tokens->getPrevMeaningfulToken($index);
$searchTokens = [',', '(', [T_STRING], [CT::T_ARRAY_TYPEHINT], [T_CALLABLE]];
$typehintKinds = [T_STRING, CT::T_ARRAY_TYPEHINT, T_CALLABLE];
$prevIndex = $tokens->getPrevTokenOfKind($variableIndex, $searchTokens);
if (!$tokens[$prevIndex]->isGivenKind($typehintKinds)) {
return false;
}
return !$tokens[$tokens->getPrevMeaningfulToken($prevIndex)]->isGivenKind(CT::T_NULLABLE_TYPE);
}
private function clearWhitespacesBeforeIndex(Tokens $tokens, int $index): void
{
$prevIndex = $tokens->getNonEmptySibling($index, -1);
if (!$tokens[$prevIndex]->isWhitespace()) {
return;
}
$prevNonWhiteIndex = $tokens->getPrevNonWhitespace($prevIndex);
if (null === $prevNonWhiteIndex || !$tokens[$prevNonWhiteIndex]->isComment()) {
$tokens->clearTokenAndMergeSurroundingWhitespace($prevIndex);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\FunctionNotation;
use PhpCsFixer\AbstractPhpdocToTypeDeclarationFixer;
use PhpCsFixer\DocBlock\Annotation;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpdocToParamTypeFixer extends AbstractPhpdocToTypeDeclarationFixer
{
private const EXCLUDE_FUNC_NAMES = [
[T_STRING, '__clone'],
[T_STRING, '__destruct'],
];
private const SKIPPED_TYPES = [
'mixed' => true,
'resource' => true,
'static' => true,
'void' => true,
];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'EXPERIMENTAL: Takes `@param` annotations of non-mixed types and adjusts accordingly the function signature. Requires PHP >= 7.0.',
[
new CodeSample(
'<?php
/**
* @param string $foo
* @param string|null $bar
*/
function f($foo, $bar)
{}
'
),
new CodeSample(
'<?php
/** @param Foo $foo */
function foo($foo) {}
/** @param string $foo */
function bar($foo) {}
',
['scalar_types' => false]
),
],
null,
'This rule is EXPERIMENTAL and [1] is not covered with backward compatibility promise. [2] `@param` annotation is mandatory for the fixer to make changes, signatures of methods without it (no docblock, inheritdocs) will not be fixed. [3] Manual actions are required if inherited signatures are not properly documented.'
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_FUNCTION);
}
public function getPriority(): int
{
return 8;
}
protected function isSkippedType(string $type): bool
{
return isset(self::SKIPPED_TYPES[$type]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = $tokens->count() - 1; 0 < $index; --$index) {
if (!$tokens[$index]->isGivenKind(T_FUNCTION)) {
continue;
}
$funcName = $tokens->getNextMeaningfulToken($index);
if ($tokens[$funcName]->equalsAny(self::EXCLUDE_FUNC_NAMES, false)) {
continue;
}
$docCommentIndex = $this->findFunctionDocComment($tokens, $index);
if (null === $docCommentIndex) {
continue;
}
foreach ($this->getAnnotationsFromDocComment('param', $tokens, $docCommentIndex) as $paramTypeAnnotation) {
$typeInfo = $this->getCommonTypeFromAnnotation($paramTypeAnnotation, false);
if (null === $typeInfo) {
continue;
}
[$paramType, $isNullable] = $typeInfo;
$startIndex = $tokens->getNextTokenOfKind($index, ['(']);
$variableIndex = $this->findCorrectVariable($tokens, $startIndex, $paramTypeAnnotation);
if (null === $variableIndex) {
continue;
}
$byRefIndex = $tokens->getPrevMeaningfulToken($variableIndex);
if ($tokens[$byRefIndex]->equals('&')) {
$variableIndex = $byRefIndex;
}
if ($this->hasParamTypeHint($tokens, $variableIndex)) {
continue;
}
if (!$this->isValidSyntax(sprintf('<?php function f(%s $x) {}', $paramType))) {
continue;
}
$tokens->insertAt($variableIndex, array_merge(
$this->createTypeDeclarationTokens($paramType, $isNullable),
[new Token([T_WHITESPACE, ' '])]
));
}
}
}
private function findCorrectVariable(Tokens $tokens, int $startIndex, Annotation $paramTypeAnnotation): ?int
{
$endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $startIndex);
for ($index = $startIndex + 1; $index < $endIndex; ++$index) {
if (!$tokens[$index]->isGivenKind(T_VARIABLE)) {
continue;
}
$variableName = $tokens[$index]->getContent();
if ($paramTypeAnnotation->getVariableName() === $variableName) {
return $index;
}
}
return null;
}
private function hasParamTypeHint(Tokens $tokens, int $index): bool
{
$prevIndex = $tokens->getPrevMeaningfulToken($index);
return !$tokens[$prevIndex]->equalsAny([',', '(']);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\FunctionNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class ImplodeCallFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Function `implode` must be called with 2 arguments in the documented order.',
[
new CodeSample("<?php\nimplode(\$pieces, '');\n"),
new CodeSample("<?php\nimplode(\$pieces);\n"),
],
null,
'Risky when the function `implode` is overridden.'
);
}
public function isRisky(): bool
{
return true;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_STRING);
}
public function getPriority(): int
{
return 37;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$functionsAnalyzer = new FunctionsAnalyzer();
for ($index = \count($tokens) - 1; $index > 0; --$index) {
if (!$tokens[$index]->equals([T_STRING, 'implode'], false)) {
continue;
}
if (!$functionsAnalyzer->isGlobalFunctionCall($tokens, $index)) {
continue;
}
$argumentsIndices = $this->getArgumentIndices($tokens, $index);
if (1 === \count($argumentsIndices)) {
$firstArgumentIndex = key($argumentsIndices);
$tokens->insertAt($firstArgumentIndex, [
new Token([T_CONSTANT_ENCAPSED_STRING, "''"]),
new Token(','),
new Token([T_WHITESPACE, ' ']),
]);
continue;
}
if (2 === \count($argumentsIndices)) {
[$firstArgumentIndex, $secondArgumentIndex] = array_keys($argumentsIndices);
if ($tokens[$firstArgumentIndex]->isGivenKind(T_CONSTANT_ENCAPSED_STRING)) {
continue;
}
if (!$tokens[$secondArgumentIndex]->isGivenKind(T_CONSTANT_ENCAPSED_STRING)) {
continue;
}
$firstArgumentEndIndex = $argumentsIndices[key($argumentsIndices)];
$newSecondArgumentTokens = [];
for ($i = key($argumentsIndices); $i <= $firstArgumentEndIndex; ++$i) {
$newSecondArgumentTokens[] = clone $tokens[$i];
$tokens->clearAt($i);
}
$tokens->insertAt($firstArgumentIndex, clone $tokens[$secondArgumentIndex]);
++$secondArgumentIndex;
$tokens->clearAt($secondArgumentIndex);
$tokens->insertAt($secondArgumentIndex, $newSecondArgumentTokens);
}
}
}
private function getArgumentIndices(Tokens $tokens, int $functionNameIndex): array
{
$argumentsAnalyzer = new ArgumentsAnalyzer();
$openParenthesis = $tokens->getNextTokenOfKind($functionNameIndex, ['(']);
$closeParenthesis = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openParenthesis);
$indices = [];
foreach ($argumentsAnalyzer->getArguments($tokens, $openParenthesis, $closeParenthesis) as $startIndexCandidate => $endIndex) {
$indices[$tokens->getNextMeaningfulToken($startIndexCandidate - 1)] = $tokens->getPrevMeaningfulToken($endIndex + 1);
}
return $indices;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\FunctionNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\FixerDefinition\VersionSpecification;
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class UseArrowFunctionsFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Anonymous functions with one-liner return statement must use arrow functions.',
[
new VersionSpecificCodeSample(
<<<'SAMPLE'
<?php
foo(function ($a) use ($b) {
return $a + $b;
});
SAMPLE
,
new VersionSpecification(70400)
),
],
null,
'Risky when using `isset()` on outside variables that are not imported with `use ()`.'
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAllTokenKindsFound([T_FUNCTION, T_RETURN]);
}
public function isRisky(): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$analyzer = new TokensAnalyzer($tokens);
for ($index = $tokens->count() - 1; $index > 0; --$index) {
if (!$tokens[$index]->isGivenKind(T_FUNCTION) || !$analyzer->isLambda($index)) {
continue;
}
$parametersStart = $tokens->getNextMeaningfulToken($index);
if ($tokens[$parametersStart]->isGivenKind(CT::T_RETURN_REF)) {
$parametersStart = $tokens->getNextMeaningfulToken($parametersStart);
}
$parametersEnd = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $parametersStart);
if ($this->isMultilined($tokens, $parametersStart, $parametersEnd)) {
continue;
}
$next = $tokens->getNextMeaningfulToken($parametersEnd);
$useStart = null;
$useEnd = null;
if ($tokens[$next]->isGivenKind(CT::T_USE_LAMBDA)) {
$useStart = $next;
if ($tokens[$useStart - 1]->isGivenKind(T_WHITESPACE)) {
--$useStart;
}
$next = $tokens->getNextMeaningfulToken($next);
while (!$tokens[$next]->equals(')')) {
if ($tokens[$next]->equals('&')) {
continue 2;
}
$next = $tokens->getNextMeaningfulToken($next);
}
$useEnd = $next;
$next = $tokens->getNextMeaningfulToken($next);
}
$braceOpen = $tokens[$next]->equals('{') ? $next : $tokens->getNextTokenOfKind($next, ['{']);
$return = $braceOpen + 1;
if ($tokens[$return]->isGivenKind(T_WHITESPACE)) {
++$return;
}
if (!$tokens[$return]->isGivenKind(T_RETURN)) {
continue;
}
$semicolon = $tokens->getNextTokenOfKind($return, ['{', ';']);
if (!$tokens[$semicolon]->equals(';')) {
continue;
}
$braceClose = $semicolon + 1;
if ($tokens[$braceClose]->isGivenKind(T_WHITESPACE)) {
++$braceClose;
}
if (!$tokens[$braceClose]->equals('}')) {
continue;
}
if ($this->isMultilined($tokens, $return, $semicolon)) {
continue;
}
$this->transform($tokens, $index, $useStart, $useEnd, $braceOpen, $return, $semicolon, $braceClose);
}
}
private function isMultilined(Tokens $tokens, int $start, int $end): bool
{
for ($i = $start; $i < $end; ++$i) {
if (str_contains($tokens[$i]->getContent(), "\n")) {
return true;
}
}
return false;
}
private function transform(Tokens $tokens, int $index, ?int $useStart, ?int $useEnd, int $braceOpen, int $return, int $semicolon, int $braceClose): void
{
$tokensToInsert = [new Token([T_DOUBLE_ARROW, '=>'])];
if ($tokens->getNextMeaningfulToken($return) === $semicolon) {
$tokensToInsert[] = new Token([T_WHITESPACE, ' ']);
$tokensToInsert[] = new Token([T_STRING, 'null']);
}
$tokens->clearRange($semicolon, $braceClose);
$tokens->clearRange($braceOpen + 1, $return);
$tokens->overrideRange($braceOpen, $braceOpen, $tokensToInsert);
if (null !== $useStart) {
$tokens->clearRange($useStart, $useEnd);
}
$tokens[$index] = new Token([T_FN, 'fn']);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\FunctionNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class SingleLineThrowFixer extends AbstractFixer
{
private const REMOVE_WHITESPACE_AFTER_TOKENS = ['['];
private const REMOVE_WHITESPACE_AROUND_TOKENS = ['(', [T_DOUBLE_COLON]];
private const REMOVE_WHITESPACE_BEFORE_TOKENS = [')', ']', ',', ';'];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Throwing exception must be done in single line.',
[
new CodeSample("<?php\nthrow new Exception(\n 'Error.',\n 500\n);\n"),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_THROW);
}
public function getPriority(): int
{
return 36;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = 0, $count = $tokens->count(); $index < $count; ++$index) {
if (!$tokens[$index]->isGivenKind(T_THROW)) {
continue;
}
$endCandidateIndex = $tokens->getNextMeaningfulToken($index);
while (!$tokens[$endCandidateIndex]->equalsAny([')', ']', ',', ';'])) {
$blockType = Tokens::detectBlockType($tokens[$endCandidateIndex]);
if (null !== $blockType) {
if (Tokens::BLOCK_TYPE_CURLY_BRACE === $blockType['type'] || !$blockType['isStart']) {
break;
}
$endCandidateIndex = $tokens->findBlockEnd($blockType['type'], $endCandidateIndex);
}
$endCandidateIndex = $tokens->getNextMeaningfulToken($endCandidateIndex);
}
$this->trimNewLines($tokens, $index, $tokens->getPrevMeaningfulToken($endCandidateIndex));
}
}
private function trimNewLines(Tokens $tokens, int $startIndex, int $endIndex): void
{
for ($index = $startIndex; $index < $endIndex; ++$index) {
$content = $tokens[$index]->getContent();
if ($tokens[$index]->isGivenKind(T_COMMENT)) {
if (str_starts_with($content, '//')) {
$content = '/*'.substr($content, 2).' */';
$tokens->clearAt($index + 1);
} elseif (str_starts_with($content, '#')) {
$content = '/*'.substr($content, 1).' */';
$tokens->clearAt($index + 1);
} elseif (0 !== Preg::match('/\R/', $content)) {
$content = Preg::replace('/\R/', ' ', $content);
}
$tokens[$index] = new Token([T_COMMENT, $content]);
continue;
}
if (!$tokens[$index]->isGivenKind(T_WHITESPACE)) {
continue;
}
if (0 === Preg::match('/\R/', $content)) {
continue;
}
$prevIndex = $tokens->getNonEmptySibling($index, -1);
if ($this->isPreviousTokenToClear($tokens[$prevIndex])) {
$tokens->clearAt($index);
continue;
}
$nextIndex = $tokens->getNonEmptySibling($index, 1);
if (
$this->isNextTokenToClear($tokens[$nextIndex])
&& !$tokens[$prevIndex]->isGivenKind(T_FUNCTION)
) {
$tokens->clearAt($index);
continue;
}
$tokens[$index] = new Token([T_WHITESPACE, ' ']);
}
}
private function isPreviousTokenToClear(Token $token): bool
{
static $tokens = null;
if (null === $tokens) {
$tokens = array_merge(self::REMOVE_WHITESPACE_AFTER_TOKENS, self::REMOVE_WHITESPACE_AROUND_TOKENS);
}
return $token->equalsAny($tokens) || $token->isObjectOperator();
}
private function isNextTokenToClear(Token $token): bool
{
static $tokens = null;
if (null === $tokens) {
$tokens = array_merge(self::REMOVE_WHITESPACE_AROUND_TOKENS, self::REMOVE_WHITESPACE_BEFORE_TOKENS);
}
return $token->equalsAny($tokens) || $token->isObjectOperator();
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\FunctionNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\FixerDefinition\VersionSpecification;
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class FunctionDeclarationFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public const SPACING_NONE = 'none';
public const SPACING_ONE = 'one';
private const SUPPORTED_SPACINGS = [self::SPACING_NONE, self::SPACING_ONE];
private string $singleLineWhitespaceOptions = " \t";
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_FUNCTION, T_FN]);
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Spaces should be properly placed in a function declaration.',
[
new CodeSample(
'<?php
class Foo
{
public static function bar ( $baz , $foo )
{
return false;
}
}
function foo ($bar, $baz)
{
return false;
}
'
),
new CodeSample(
'<?php
$f = function () {};
',
['closure_function_spacing' => self::SPACING_NONE]
),
new VersionSpecificCodeSample(
'<?php
$f = fn () => null;
',
new VersionSpecification(70400),
['closure_fn_spacing' => self::SPACING_NONE]
),
]
);
}
public function getPriority(): int
{
return 31;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$tokensAnalyzer = new TokensAnalyzer($tokens);
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
$token = $tokens[$index];
if (!$token->isGivenKind([T_FUNCTION, T_FN])) {
continue;
}
$startParenthesisIndex = $tokens->getNextTokenOfKind($index, ['(', ';', [T_CLOSE_TAG]]);
if (!$tokens[$startParenthesisIndex]->equals('(')) {
continue;
}
$endParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $startParenthesisIndex);
if (false === $this->configuration['trailing_comma_single_line']
&& !$tokens->isPartialCodeMultiline($index, $endParenthesisIndex)
) {
$commaIndex = $tokens->getPrevMeaningfulToken($endParenthesisIndex);
if ($tokens[$commaIndex]->equals(',')) {
$tokens->clearTokenAndMergeSurroundingWhitespace($commaIndex);
}
}
$startBraceIndex = $tokens->getNextTokenOfKind($endParenthesisIndex, [';', '{', [T_DOUBLE_ARROW]]);
if (
$tokens[$startBraceIndex]->equalsAny(['{', [T_DOUBLE_ARROW]])
&& (
!$tokens[$startBraceIndex - 1]->isWhitespace()
|| $tokens[$startBraceIndex - 1]->isWhitespace($this->singleLineWhitespaceOptions)
)
) {
$tokens->ensureWhitespaceAtIndex($startBraceIndex - 1, 1, ' ');
}
$afterParenthesisIndex = $tokens->getNextNonWhitespace($endParenthesisIndex);
$afterParenthesisToken = $tokens[$afterParenthesisIndex];
if ($afterParenthesisToken->isGivenKind(CT::T_USE_LAMBDA)) {
$tokens->ensureWhitespaceAtIndex($afterParenthesisIndex + 1, 0, ' ');
$useStartParenthesisIndex = $tokens->getNextTokenOfKind($afterParenthesisIndex, ['(']);
$useEndParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $useStartParenthesisIndex);
if (false === $this->configuration['trailing_comma_single_line']
&& !$tokens->isPartialCodeMultiline($index, $useEndParenthesisIndex)
) {
$commaIndex = $tokens->getPrevMeaningfulToken($useEndParenthesisIndex);
if ($tokens[$commaIndex]->equals(',')) {
$tokens->clearTokenAndMergeSurroundingWhitespace($commaIndex);
}
}
$this->fixParenthesisInnerEdge($tokens, $useStartParenthesisIndex, $useEndParenthesisIndex);
$tokens->ensureWhitespaceAtIndex($afterParenthesisIndex - 1, 1, ' ');
}
$this->fixParenthesisInnerEdge($tokens, $startParenthesisIndex, $endParenthesisIndex);
$isLambda = $tokensAnalyzer->isLambda($index);
if (!$isLambda && $tokens[$startParenthesisIndex - 1]->isWhitespace() && !$tokens[$tokens->getPrevNonWhitespace($startParenthesisIndex - 1)]->isComment()) {
$tokens->clearAt($startParenthesisIndex - 1);
}
$option = $token->isGivenKind(T_FN) ? 'closure_fn_spacing' : 'closure_function_spacing';
if ($isLambda && self::SPACING_NONE === $this->configuration[$option]) {
if ($tokens[$index + 1]->isWhitespace()) {
$tokens->clearAt($index + 1);
}
} else {
$tokens->ensureWhitespaceAtIndex($index + 1, 0, ' ');
}
if ($isLambda) {
$prev = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$prev]->isGivenKind(T_STATIC)) {
$tokens->ensureWhitespaceAtIndex($prev + 1, 0, ' ');
}
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('closure_function_spacing', 'Spacing to use before open parenthesis for closures.'))
->setDefault(self::SPACING_ONE)
->setAllowedValues(self::SUPPORTED_SPACINGS)
->getOption(),
(new FixerOptionBuilder('closure_fn_spacing', 'Spacing to use before open parenthesis for short arrow functions.'))
->setDefault(self::SPACING_ONE)
->setAllowedValues(self::SUPPORTED_SPACINGS)
->getOption(),
(new FixerOptionBuilder('trailing_comma_single_line', 'Whether trailing commas are allowed in single line signatures.'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
]);
}
private function fixParenthesisInnerEdge(Tokens $tokens, int $start, int $end): void
{
do {
--$end;
} while ($tokens->isEmptyAt($end));
if ($tokens[$end]->isWhitespace($this->singleLineWhitespaceOptions)) {
$tokens->clearAt($end);
}
if ($tokens[$start + 1]->isWhitespace($this->singleLineWhitespaceOptions)) {
$tokens->clearAt($start + 1);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
use Symfony\Component\OptionsResolver\Options;
final class GeneralPhpdocTagRenameFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Renames PHPDoc tags.',
[
new CodeSample("<?php\n/**\n * @inheritDocs\n * {@inheritdocs}\n */\n", [
'replacements' => [
'inheritDocs' => 'inheritDoc',
],
]),
new CodeSample("<?php\n/**\n * @inheritDocs\n * {@inheritdocs}\n */\n", [
'replacements' => [
'inheritDocs' => 'inheritDoc',
],
'fix_annotation' => false,
]),
new CodeSample("<?php\n/**\n * @inheritDocs\n * {@inheritdocs}\n */\n", [
'replacements' => [
'inheritDocs' => 'inheritDoc',
],
'fix_inline' => false,
]),
new CodeSample("<?php\n/**\n * @inheritDocs\n * {@inheritdocs}\n */\n", [
'replacements' => [
'inheritDocs' => 'inheritDoc',
],
'case_sensitive' => true,
]),
]
);
}
public function getPriority(): int
{
return 11;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOC_COMMENT);
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('fix_annotation', 'Whether annotation tags should be fixed.'))
->setAllowedTypes(['bool'])
->setDefault(true)
->getOption(),
(new FixerOptionBuilder('fix_inline', 'Whether inline tags should be fixed.'))
->setAllowedTypes(['bool'])
->setDefault(true)
->getOption(),
(new FixerOptionBuilder('replacements', 'A map of tags to replace.'))
->setAllowedTypes(['array'])
->setNormalizer(static function (Options $options, $value): array {
$normalizedValue = [];
foreach ($value as $from => $to) {
if (!\is_string($from)) {
throw new InvalidOptionsException('Tag to replace must be a string.');
}
if (!\is_string($to)) {
throw new InvalidOptionsException(sprintf(
'Tag to replace to from "%s" must be a string.',
$from
));
}
if (1 !== Preg::match('#^\S+$#', $to) || str_contains($to, '*/')) {
throw new InvalidOptionsException(sprintf(
'Tag "%s" cannot be replaced by invalid tag "%s".',
$from,
$to
));
}
$from = trim($from);
$to = trim($to);
if (!$options['case_sensitive']) {
$lowercaseFrom = strtolower($from);
if (isset($normalizedValue[$lowercaseFrom]) && $normalizedValue[$lowercaseFrom] !== $to) {
throw new InvalidOptionsException(sprintf(
'Tag "%s" cannot be configured to be replaced with several different tags when case sensitivity is off.',
$from
));
}
$from = $lowercaseFrom;
}
$normalizedValue[$from] = $to;
}
foreach ($normalizedValue as $from => $to) {
if (isset($normalizedValue[$to]) && $normalizedValue[$to] !== $to) {
throw new InvalidOptionsException(sprintf(
'Cannot change tag "%1$s" to tag "%2$s", as the tag "%2$s" is configured to be replaced to "%3$s".',
$from,
$to,
$normalizedValue[$to]
));
}
}
return $normalizedValue;
})
->setDefault([])
->getOption(),
(new FixerOptionBuilder('case_sensitive', 'Whether tags should be replaced only if they have exact same casing.'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
if (0 === \count($this->configuration['replacements'])) {
return;
}
if (true === $this->configuration['fix_annotation']) {
if ($this->configuration['fix_inline']) {
$regex = '/"[^"]*"(*SKIP)(*FAIL)|\b(?<=@)(%s)\b/';
} else {
$regex = '/"[^"]*"(*SKIP)(*FAIL)|(?<!\{@)(?<=@)(%s)(?!\})/';
}
} else {
$regex = '/(?<={@)(%s)(?=[ \t}])/';
}
$caseInsensitive = false === $this->configuration['case_sensitive'];
$replacements = $this->configuration['replacements'];
$regex = sprintf($regex, implode('|', array_keys($replacements)));
if ($caseInsensitive) {
$regex .= 'i';
}
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_DOC_COMMENT)) {
continue;
}
$tokens[$index] = new Token([T_DOC_COMMENT, Preg::replaceCallback(
$regex,
static function (array $matches) use ($caseInsensitive, $replacements) {
if ($caseInsensitive) {
$matches[1] = strtolower($matches[1]);
}
return $replacements[$matches[1]];
},
$token->getContent()
)]);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
use Symfony\Component\OptionsResolver\Options;
final class PhpdocReturnSelfReferenceFixer extends AbstractFixer implements ConfigurableFixerInterface
{
private static array $toTypes = [
'$this',
'static',
'self',
];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'The type of `@return` annotations of methods returning a reference to itself must the configured one.',
[
new CodeSample(
'<?php
class Sample
{
/**
* @return this
*/
public function test1()
{
return $this;
}
/**
* @return $self
*/
public function test2()
{
return $this;
}
}
'
),
new CodeSample(
'<?php
class Sample
{
/**
* @return this
*/
public function test1()
{
return $this;
}
/**
* @return $self
*/
public function test2()
{
return $this;
}
}
',
['replacements' => ['this' => 'self']]
),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return \count($tokens) > 10 && $tokens->isAllTokenKindsFound([T_DOC_COMMENT, T_FUNCTION]) && $tokens->isAnyTokenKindsFound(Token::getClassyTokenKinds());
}
public function getPriority(): int
{
return 10;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$tokensAnalyzer = new TokensAnalyzer($tokens);
foreach ($tokensAnalyzer->getClassyElements() as $index => $element) {
if ('method' === $element['type']) {
$this->fixMethod($tokens, $index);
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
$default = [
'this' => '$this',
'@this' => '$this',
'$self' => 'self',
'@self' => 'self',
'$static' => 'static',
'@static' => 'static',
];
return new FixerConfigurationResolver([
(new FixerOptionBuilder('replacements', 'Mapping between replaced return types with new ones.'))
->setAllowedTypes(['array'])
->setNormalizer(static function (Options $options, array $value) use ($default): array {
$normalizedValue = [];
foreach ($value as $from => $to) {
if (\is_string($from)) {
$from = strtolower($from);
}
if (!isset($default[$from])) {
throw new InvalidOptionsException(sprintf(
'Unknown key "%s", expected any of "%s".',
\gettype($from).'#'.$from,
implode('", "', array_keys($default))
));
}
if (!\in_array($to, self::$toTypes, true)) {
throw new InvalidOptionsException(sprintf(
'Unknown value "%s", expected any of "%s".',
\is_object($to) ? \get_class($to) : \gettype($to).(\is_resource($to) ? '' : '#'.$to),
implode('", "', self::$toTypes)
));
}
$normalizedValue[$from] = $to;
}
return $normalizedValue;
})
->setDefault($default)
->getOption(),
]);
}
private function fixMethod(Tokens $tokens, int $index): void
{
static $methodModifiers = [T_STATIC, T_FINAL, T_ABSTRACT, T_PRIVATE, T_PROTECTED, T_PUBLIC];
while (true) {
$tokenIndex = $tokens->getPrevMeaningfulToken($index);
if (!$tokens[$tokenIndex]->isGivenKind($methodModifiers)) {
break;
}
$index = $tokenIndex;
}
$docIndex = $tokens->getPrevNonWhitespace($index);
if (!$tokens[$docIndex]->isGivenKind(T_DOC_COMMENT)) {
return;
}
$docBlock = new DocBlock($tokens[$docIndex]->getContent());
$returnsBlock = $docBlock->getAnnotationsOfType('return');
if (0 === \count($returnsBlock)) {
return;
}
$returnsBlock = $returnsBlock[0];
$types = $returnsBlock->getTypes();
if (0 === \count($types)) {
return;
}
$newTypes = [];
foreach ($types as $type) {
$newTypes[] = $this->configuration['replacements'][strtolower($type)] ?? $type;
}
if ($types === $newTypes) {
return;
}
$returnsBlock->setTypes($newTypes);
$tokens[$docIndex] = new Token([T_DOC_COMMENT, $docBlock->getContent()]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\DocBlock\Line;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpdocAddMissingParamAnnotationFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'PHPDoc should contain `@param` for all params.',
[
new CodeSample(
'<?php
/**
* @param int $bar
*
* @return void
*/
function f9(string $foo, $bar, $baz) {}
'
),
new CodeSample(
'<?php
/**
* @param int $bar
*
* @return void
*/
function f9(string $foo, $bar, $baz) {}
',
['only_untyped' => true]
),
new CodeSample(
'<?php
/**
* @param int $bar
*
* @return void
*/
function f9(string $foo, $bar, $baz) {}
',
['only_untyped' => false]
),
]
);
}
public function getPriority(): int
{
return 10;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOC_COMMENT);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$argumentsAnalyzer = new ArgumentsAnalyzer();
for ($index = 0, $limit = $tokens->count(); $index < $limit; ++$index) {
$token = $tokens[$index];
if (!$token->isGivenKind(T_DOC_COMMENT)) {
continue;
}
$tokenContent = $token->getContent();
if (false !== stripos($tokenContent, 'inheritdoc')) {
continue;
}
if (!str_contains($tokenContent, "\n")) {
continue;
}
$mainIndex = $index;
$index = $tokens->getNextMeaningfulToken($index);
if (null === $index) {
return;
}
while ($tokens[$index]->isGivenKind([
T_ABSTRACT,
T_FINAL,
T_PRIVATE,
T_PROTECTED,
T_PUBLIC,
T_STATIC,
T_VAR,
])) {
$index = $tokens->getNextMeaningfulToken($index);
}
if (!$tokens[$index]->isGivenKind(T_FUNCTION)) {
continue;
}
$openIndex = $tokens->getNextTokenOfKind($index, ['(']);
$index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openIndex);
$arguments = [];
foreach ($argumentsAnalyzer->getArguments($tokens, $openIndex, $index) as $start => $end) {
$argumentInfo = $this->prepareArgumentInformation($tokens, $start, $end);
if (false === $this->configuration['only_untyped'] || '' === $argumentInfo['type']) {
$arguments[$argumentInfo['name']] = $argumentInfo;
}
}
if (0 === \count($arguments)) {
continue;
}
$doc = new DocBlock($tokenContent);
$lastParamLine = null;
foreach ($doc->getAnnotationsOfType('param') as $annotation) {
$pregMatched = Preg::match('/^[^$]+(\$\w+).*$/s', $annotation->getContent(), $matches);
if (1 === $pregMatched) {
unset($arguments[$matches[1]]);
}
$lastParamLine = max($lastParamLine, $annotation->getEnd());
}
if (0 === \count($arguments)) {
continue;
}
$lines = $doc->getLines();
$linesCount = \count($lines);
Preg::match('/^(\s*).*$/', $lines[$linesCount - 1]->getContent(), $matches);
$indent = $matches[1];
$newLines = [];
foreach ($arguments as $argument) {
$type = $argument['type'] ?: 'mixed';
if (!str_starts_with($type, '?') && 'null' === strtolower($argument['default'])) {
$type = 'null|'.$type;
}
$newLines[] = new Line(sprintf(
'%s* @param %s %s%s',
$indent,
$type,
$argument['name'],
$this->whitespacesConfig->getLineEnding()
));
}
array_splice(
$lines,
$lastParamLine ? $lastParamLine + 1 : $linesCount - 1,
0,
$newLines
);
$tokens[$mainIndex] = new Token([T_DOC_COMMENT, implode('', $lines)]);
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('only_untyped', 'Whether to add missing `@param` annotations for untyped parameters only.'))
->setDefault(true)
->setAllowedTypes(['bool'])
->getOption(),
]);
}
private function prepareArgumentInformation(Tokens $tokens, int $start, int $end): array
{
$info = [
'default' => '',
'name' => '',
'type' => '',
];
$sawName = false;
for ($index = $start; $index <= $end; ++$index) {
$token = $tokens[$index];
if ($token->isComment() || $token->isWhitespace()) {
continue;
}
if ($token->isGivenKind(T_VARIABLE)) {
$sawName = true;
$info['name'] = $token->getContent();
continue;
}
if ($token->equals('=')) {
continue;
}
if ($sawName) {
$info['default'] .= $token->getContent();
} elseif (!$token->equals('&')) {
if ($token->isGivenKind(T_ELLIPSIS)) {
if ('' === $info['type']) {
$info['type'] = 'array';
} else {
$info['type'] .= '[]';
}
} else {
$info['type'] .= $token->getContent();
}
}
}
return $info;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\DocBlock\Line;
use PhpCsFixer\DocBlock\ShortDescription;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpdocTrimConsecutiveBlankLineSeparationFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Removes extra blank lines after summary and after description in PHPDoc.',
[
new CodeSample(
'<?php
/**
* Summary.
*
*
* Description that contain 4 lines,
*
*
* while 2 of them are blank!
*
*
* @param string $foo
*
*
* @dataProvider provideFixCases
*/
function fnc($foo) {}
'
),
]
);
}
public function getPriority(): int
{
return -41;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOC_COMMENT);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_DOC_COMMENT)) {
continue;
}
$doc = new DocBlock($token->getContent());
$summaryEnd = (new ShortDescription($doc))->getEnd();
if (null !== $summaryEnd) {
$this->fixSummary($doc, $summaryEnd);
$this->fixDescription($doc, $summaryEnd);
}
$this->fixAllTheRest($doc);
$tokens[$index] = new Token([T_DOC_COMMENT, $doc->getContent()]);
}
}
private function fixSummary(DocBlock $doc, int $summaryEnd): void
{
$nonBlankLineAfterSummary = $this->findNonBlankLine($doc, $summaryEnd);
$this->removeExtraBlankLinesBetween($doc, $summaryEnd, $nonBlankLineAfterSummary);
}
private function fixDescription(DocBlock $doc, int $summaryEnd): void
{
$annotationStart = $this->findFirstAnnotationOrEnd($doc);
$descriptionEnd = $this->reverseFindLastUsefulContent($doc, $annotationStart);
if (null === $descriptionEnd || $summaryEnd === $descriptionEnd) {
return;
}
if ($annotationStart === \count($doc->getLines()) - 1) {
return;
}
$this->removeExtraBlankLinesBetween($doc, $descriptionEnd, $annotationStart);
}
private function fixAllTheRest(DocBlock $doc): void
{
$annotationStart = $this->findFirstAnnotationOrEnd($doc);
$lastLine = $this->reverseFindLastUsefulContent($doc, \count($doc->getLines()) - 1);
if (null !== $lastLine && $annotationStart !== $lastLine) {
$this->removeExtraBlankLinesBetween($doc, $annotationStart, $lastLine);
}
}
private function removeExtraBlankLinesBetween(DocBlock $doc, int $from, int $to): void
{
for ($index = $from + 1; $index < $to; ++$index) {
$line = $doc->getLine($index);
$next = $doc->getLine($index + 1);
$this->removeExtraBlankLine($line, $next);
}
}
private function removeExtraBlankLine(Line $current, Line $next): void
{
if (!$current->isTheEnd() && !$current->containsUsefulContent()
&& !$next->isTheEnd() && !$next->containsUsefulContent()) {
$current->remove();
}
}
private function findNonBlankLine(DocBlock $doc, int $after): ?int
{
foreach ($doc->getLines() as $index => $line) {
if ($index <= $after) {
continue;
}
if ($line->containsATag() || $line->containsUsefulContent() || $line->isTheEnd()) {
return $index;
}
}
return null;
}
private function findFirstAnnotationOrEnd(DocBlock $doc): int
{
$index = null;
foreach ($doc->getLines() as $index => $line) {
if ($line->containsATag()) {
return $index;
}
}
return $index;
}
private function reverseFindLastUsefulContent(DocBlock $doc, int $from): ?int
{
for ($index = $from - 1; $index >= 0; --$index) {
if ($doc->getLine($index)->containsUsefulContent()) {
return $index;
}
}
return null;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
use Symfony\Component\OptionsResolver\Options;
final class PhpdocTagTypeFixer extends AbstractFixer implements ConfigurableFixerInterface
{
private const TAG_REGEX = '/^(?:
(?<tag>
(?:@(?<tag_name>.+?)(?:\s.+)?)
)
|
{(?<inlined_tag>
(?:@(?<inlined_tag_name>.+?)(?:\s.+)?)
)}
)$/x';
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOC_COMMENT);
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Forces PHPDoc tags to be either regular annotations or inline.',
[
new CodeSample(
"<?php\n/**\n * {@api}\n */\n"
),
new CodeSample(
"<?php\n/**\n * @inheritdoc\n */\n",
['tags' => ['inheritdoc' => 'inline']]
),
]
);
}
public function getPriority(): int
{
return 0;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
if (0 === \count($this->configuration['tags'])) {
return;
}
$regularExpression = sprintf(
'/({?@(?:%s).*?(?:(?=\s\*\/)|(?=\n)}?))/i',
implode('|', array_map(
static function (string $tag): string {
return preg_quote($tag, '/');
},
array_keys($this->configuration['tags'])
))
);
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_DOC_COMMENT)) {
continue;
}
$parts = Preg::split(
$regularExpression,
$token->getContent(),
-1,
PREG_SPLIT_DELIM_CAPTURE
);
for ($i = 1, $max = \count($parts) - 1; $i < $max; $i += 2) {
if (!Preg::match(self::TAG_REGEX, $parts[$i], $matches)) {
continue;
}
if ('' !== $matches['tag']) {
$tag = $matches['tag'];
$tagName = $matches['tag_name'];
} else {
$tag = $matches['inlined_tag'];
$tagName = $matches['inlined_tag_name'];
}
$tagName = strtolower($tagName);
if (!isset($this->configuration['tags'][$tagName])) {
continue;
}
if ('inline' === $this->configuration['tags'][$tagName]) {
$parts[$i] = '{'.$tag.'}';
continue;
}
if (!$this->tagIsSurroundedByText($parts, $i)) {
$parts[$i] = $tag;
}
}
$tokens[$index] = new Token([T_DOC_COMMENT, implode('', $parts)]);
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('tags', 'The list of tags to fix'))
->setAllowedTypes(['array'])
->setAllowedValues([static function (array $value): bool {
foreach ($value as $type) {
if (!\in_array($type, ['annotation', 'inline'], true)) {
throw new InvalidOptionsException("Unknown tag type \"{$type}\".");
}
}
return true;
}])
->setDefault([
'api' => 'annotation',
'author' => 'annotation',
'copyright' => 'annotation',
'deprecated' => 'annotation',
'example' => 'annotation',
'global' => 'annotation',
'inheritDoc' => 'annotation',
'internal' => 'annotation',
'license' => 'annotation',
'method' => 'annotation',
'package' => 'annotation',
'param' => 'annotation',
'property' => 'annotation',
'return' => 'annotation',
'see' => 'annotation',
'since' => 'annotation',
'throws' => 'annotation',
'todo' => 'annotation',
'uses' => 'annotation',
'var' => 'annotation',
'version' => 'annotation',
])
->setNormalizer(static function (Options $options, $value): array {
$normalized = [];
foreach ($value as $tag => $type) {
$normalized[strtolower($tag)] = $type;
}
return $normalized;
})
->getOption(),
]);
}
private function tagIsSurroundedByText(array $parts, int $index): bool
{
return
Preg::match('/(^|\R)\h*[^@\s]\N*/', $this->cleanComment($parts[$index - 1]))
|| Preg::match('/^.*?\R\s*[^@\s]/', $this->cleanComment($parts[$index + 1]))
;
}
private function cleanComment(string $comment): string
{
$comment = Preg::replace('/^\/\*\*|\*\/$/', '', $comment);
return Preg::replace('/(\R)(\h*\*)?\h*/', '$1', $comment);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\DocBlock\ShortDescription;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpdocSummaryFixer extends AbstractFixer implements WhitespacesAwareFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'PHPDoc summary should end in either a full stop, exclamation mark, or question mark.',
[new CodeSample('<?php
/**
* Foo function is great
*/
function foo () {}
')]
);
}
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOC_COMMENT);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_DOC_COMMENT)) {
continue;
}
$doc = new DocBlock($token->getContent());
$end = (new ShortDescription($doc))->getEnd();
if (null !== $end) {
$line = $doc->getLine($end);
$content = rtrim($line->getContent());
if (!$this->isCorrectlyFormatted($content)) {
$line->setContent($content.'.'.$this->whitespacesConfig->getLineEnding());
$tokens[$index] = new Token([T_DOC_COMMENT, $doc->getContent()]);
}
}
}
}
private function isCorrectlyFormatted(string $content): bool
{
if (false !== stripos($content, '{@inheritdoc}')) {
return true;
}
return $content !== rtrim($content, '.。!?¡¿!?');
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractProxyFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
final class PhpdocNoPackageFixer extends AbstractProxyFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'`@package` and `@subpackage` annotations should be omitted from PHPDoc.',
[
new CodeSample(
'<?php
/**
* @internal
* @package Foo
* subpackage Bar
*/
class Baz
{
}
'
),
]
);
}
public function getPriority(): int
{
return parent::getPriority();
}
protected function createProxyFixers(): array
{
$fixer = new GeneralPhpdocAnnotationRemoveFixer();
$fixer->configure(['annotations' => ['package', 'subpackage']]);
return [$fixer];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Tokens;
final class NoEmptyPhpdocFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'There should not be empty PHPDoc blocks.',
[new CodeSample("<?php /** */\n")]
);
}
public function getPriority(): int
{
return 3;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOC_COMMENT);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_DOC_COMMENT)) {
continue;
}
if (Preg::match('#^/\*\*[\s\*]*\*/$#', $token->getContent())) {
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractProxyFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
final class PhpdocNoAccessFixer extends AbstractProxyFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'`@access` annotations should be omitted from PHPDoc.',
[
new CodeSample(
'<?php
class Foo
{
/**
* @internal
* @access private
*/
private $bar;
}
'
),
]
);
}
public function getPriority(): int
{
return parent::getPriority();
}
protected function createProxyFixers(): array
{
$fixer = new GeneralPhpdocAnnotationRemoveFixer();
$fixer->configure(['annotations' => ['access']]);
return [$fixer];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractPhpdocTypesFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
final class PhpdocScalarFixer extends AbstractPhpdocTypesFixer implements ConfigurableFixerInterface
{
private static array $types = [
'boolean' => 'bool',
'callback' => 'callable',
'double' => 'float',
'integer' => 'int',
'real' => 'float',
'str' => 'string',
];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Scalar types should always be written in the same form. `int` not `integer`, `bool` not `boolean`, `float` not `real` or `double`.',
[
new CodeSample('<?php
/**
* @param integer $a
* @param boolean $b
* @param real $c
*
* @return double
*/
function sample($a, $b, $c)
{
return sample2($a, $b, $c);
}
'),
new CodeSample(
'<?php
/**
* @param integer $a
* @param boolean $b
* @param real $c
*/
function sample($a, $b, $c)
{
return sample2($a, $b, $c);
}
',
['types' => ['boolean']]
),
]
);
}
public function getPriority(): int
{
return 15;
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
$types = array_keys(self::$types);
return new FixerConfigurationResolver([
(new FixerOptionBuilder('types', 'A list of types to fix.'))
->setAllowedValues([new AllowedValueSubset($types)])
->setDefault($types)
->getOption(),
]);
}
protected function normalize(string $type): string
{
if (\in_array($type, $this->configuration['types'], true)) {
return self::$types[$type];
}
return $type;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\DocBlock\Annotation;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class GeneralPhpdocAnnotationRemoveFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Configured annotations should be omitted from PHPDoc.',
[
new CodeSample(
'<?php
/**
* @internal
* @author John Doe
* @AuThOr Jane Doe
*/
function foo() {}
',
['annotations' => ['author']]
),
new CodeSample(
'<?php
/**
* @internal
* @author John Doe
* @AuThOr Jane Doe
*/
function foo() {}
',
['annotations' => ['author'], 'case_sensitive' => false]
),
new CodeSample(
'<?php
/**
* @author John Doe
* @package ACME API
* @subpackage Authorization
* @version 1.0
*/
function foo() {}
',
['annotations' => ['package', 'subpackage']]
),
]
);
}
public function getPriority(): int
{
return 10;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOC_COMMENT);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
if (0 === \count($this->configuration['annotations'])) {
return;
}
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_DOC_COMMENT)) {
continue;
}
$doc = new DocBlock($token->getContent());
$annotations = $this->getAnnotationsToRemove($doc);
if (0 === \count($annotations)) {
continue;
}
foreach ($annotations as $annotation) {
$annotation->remove();
}
if ('' === $doc->getContent()) {
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
} else {
$tokens[$index] = new Token([T_DOC_COMMENT, $doc->getContent()]);
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('annotations', 'List of annotations to remove, e.g. `["author"]`.'))
->setAllowedTypes(['array'])
->setDefault([])
->getOption(),
(new FixerOptionBuilder('case_sensitive', 'Should annotations be case sensitive.'))
->setAllowedTypes(['bool'])
->setDefault(true)
->getOption(),
]);
}
private function getAnnotationsToRemove(DocBlock $doc): array
{
if (true === $this->configuration['case_sensitive']) {
return $doc->getAnnotationsOfType($this->configuration['annotations']);
}
$typesToSearchFor = array_map(function (string $type): string {
return strtolower($type);
}, $this->configuration['annotations']);
$annotations = [];
foreach ($doc->getAnnotations() as $annotation) {
$tagName = strtolower($annotation->getTag()->getName());
if (\in_array($tagName, $typesToSearchFor, true)) {
$annotations[] = $annotation;
}
}
return $annotations;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use Symfony\Component\OptionsResolver\Options;
final class PhpdocOrderByValueFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Order phpdoc tags by value.',
[
new CodeSample(
'<?php
/**
* @covers Foo
* @covers Bar
*/
final class MyTest extends \PHPUnit_Framework_TestCase
{}
'
),
new CodeSample(
'<?php
/**
* @author Bob
* @author Alice
*/
final class MyTest extends \PHPUnit_Framework_TestCase
{}
',
[
'annotations' => [
'author',
],
]
),
]
);
}
public function getPriority(): int
{
return -10;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAllTokenKindsFound([T_CLASS, T_DOC_COMMENT]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
if ([] === $this->configuration['annotations']) {
return;
}
for ($index = $tokens->count() - 1; $index > 0; --$index) {
foreach ($this->configuration['annotations'] as $type => $typeLowerCase) {
$findPattern = sprintf(
'/@%s\s.+@%s\s/s',
$type,
$type
);
if (
!$tokens[$index]->isGivenKind(T_DOC_COMMENT)
|| 0 === Preg::match($findPattern, $tokens[$index]->getContent())
) {
continue;
}
$docBlock = new DocBlock($tokens[$index]->getContent());
$annotations = $docBlock->getAnnotationsOfType($type);
$annotationMap = [];
if (\in_array($type, ['property', 'property-read', 'property-write'], true)) {
$replacePattern = sprintf(
'/(?s)\*\s*@%s\s+(?P<optionalTypes>.+\s+)?\$(?P<comparableContent>[^\s]+).*/',
$type
);
$replacement = '\2';
} elseif ('method' === $type) {
$replacePattern = '/(?s)\*\s*@method\s+(?P<optionalReturnTypes>.+\s+)?(?P<comparableContent>.+)\(.*/';
$replacement = '\2';
} else {
$replacePattern = sprintf(
'/\*\s*@%s\s+(?P<comparableContent>.+)/',
$typeLowerCase
);
$replacement = '\1';
}
foreach ($annotations as $annotation) {
$rawContent = $annotation->getContent();
$comparableContent = Preg::replace(
$replacePattern,
$replacement,
strtolower(trim($rawContent))
);
$annotationMap[$comparableContent] = $rawContent;
}
$orderedAnnotationMap = $annotationMap;
ksort($orderedAnnotationMap, SORT_STRING);
if ($orderedAnnotationMap === $annotationMap) {
continue;
}
$lines = $docBlock->getLines();
foreach (array_reverse($annotations) as $annotation) {
array_splice(
$lines,
$annotation->getStart(),
$annotation->getEnd() - $annotation->getStart() + 1,
array_pop($orderedAnnotationMap)
);
}
$tokens[$index] = new Token([T_DOC_COMMENT, implode('', $lines)]);
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
$allowedValues = [
'author',
'covers',
'coversNothing',
'dataProvider',
'depends',
'group',
'internal',
'method',
'mixin',
'property',
'property-read',
'property-write',
'requires',
'throws',
'uses',
];
return new FixerConfigurationResolver([
(new FixerOptionBuilder('annotations', 'List of annotations to order, e.g. `["covers"]`.'))
->setAllowedTypes([
'array',
])
->setAllowedValues([
new AllowedValueSubset($allowedValues),
])
->setNormalizer(static function (Options $options, $value): array {
$normalized = [];
foreach ($value as $annotation) {
$normalized[$annotation] = strtolower($annotation);
}
return $normalized;
})
->setDefault([
'covers',
])
->getOption(),
]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpdocAnnotationWithoutDotFixer extends AbstractFixer
{
private array $tags = ['throws', 'return', 'param', 'internal', 'deprecated', 'var', 'type'];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'PHPDoc annotation descriptions should not be a sentence.',
[new CodeSample('<?php
/**
* @param string $bar Some string.
*/
function foo ($bar) {}
')]
);
}
public function getPriority(): int
{
return 17;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOC_COMMENT);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_DOC_COMMENT)) {
continue;
}
$doc = new DocBlock($token->getContent());
$annotations = $doc->getAnnotations();
if (0 === \count($annotations)) {
continue;
}
foreach ($annotations as $annotation) {
if (
!$annotation->getTag()->valid() || !\in_array($annotation->getTag()->getName(), $this->tags, true)
) {
continue;
}
$lineAfterAnnotation = $doc->getLine($annotation->getEnd() + 1);
if (null !== $lineAfterAnnotation) {
$lineAfterAnnotationTrimmed = ltrim($lineAfterAnnotation->getContent());
if ('' === $lineAfterAnnotationTrimmed || !str_starts_with($lineAfterAnnotationTrimmed, '*')) {
continue;
}
}
$content = $annotation->getContent();
if (
1 !== Preg::match('/[.。]\h*$/u', $content)
|| 0 !== Preg::match('/[.。](?!\h*$)/u', $content, $matches)
) {
continue;
}
$endLine = $doc->getLine($annotation->getEnd());
$endLine->setContent(Preg::replace('/(?<![.。])[.。]\h*(\H+)$/u', '\1', $endLine->getContent()));
$startLine = $doc->getLine($annotation->getStart());
$optionalTypeRegEx = $annotation->supportTypes()
? sprintf('(?:%s\s+(?:\$\w+\s+)?)?', preg_quote(implode('|', $annotation->getTypes()), '/'))
: '';
$content = Preg::replaceCallback(
'/^(\s*\*\s*@\w+\s+'.$optionalTypeRegEx.')(\p{Lu}?(?=\p{Ll}|\p{Zs}))(.*)$/',
static function (array $matches): string {
return $matches[1].mb_strtolower($matches[2]).$matches[3];
},
$startLine->getContent(),
1
);
$startLine->setContent($content);
}
$tokens[$index] = new Token([T_DOC_COMMENT, $doc->getContent()]);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\WhitespacesAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class PhpdocLineSpanFixer extends AbstractFixer implements WhitespacesAwareFixerInterface, ConfigurableFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Changes doc blocks from single to multi line, or reversed. Works for class constants, properties and methods only.',
[
new CodeSample("<?php\n\nclass Foo{\n /** @var bool */\n public \$var;\n}\n"),
new CodeSample(
"<?php\n\nclass Foo{\n /**\n * @var bool\n */\n public \$var;\n}\n",
['property' => 'single']
),
]
);
}
public function getPriority(): int
{
return 7;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOC_COMMENT);
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('const', 'Whether const blocks should be single or multi line'))
->setAllowedValues(['single', 'multi', null])
->setDefault('multi')
->getOption(),
(new FixerOptionBuilder('property', 'Whether property doc blocks should be single or multi line'))
->setAllowedValues(['single', 'multi', null])
->setDefault('multi')
->getOption(),
(new FixerOptionBuilder('method', 'Whether method doc blocks should be single or multi line'))
->setAllowedValues(['single', 'multi', null])
->setDefault('multi')
->getOption(),
]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$analyzer = new TokensAnalyzer($tokens);
foreach ($analyzer->getClassyElements() as $index => $element) {
if (!$this->hasDocBlock($tokens, $index)) {
continue;
}
$type = $element['type'];
if (!isset($this->configuration[$type])) {
continue;
}
$docIndex = $this->getDocBlockIndex($tokens, $index);
$doc = new DocBlock($tokens[$docIndex]->getContent());
if ('multi' === $this->configuration[$type]) {
$doc->makeMultiLine(WhitespacesAnalyzer::detectIndent($tokens, $docIndex), $this->whitespacesConfig->getLineEnding());
} elseif ('single' === $this->configuration[$type]) {
$doc->makeSingleLine();
}
$tokens->offsetSet($docIndex, new Token([T_DOC_COMMENT, $doc->getContent()]));
}
}
private function hasDocBlock(Tokens $tokens, int $index): bool
{
$docBlockIndex = $this->getDocBlockIndex($tokens, $index);
return $tokens[$docBlockIndex]->isGivenKind(T_DOC_COMMENT);
}
private function getDocBlockIndex(Tokens $tokens, int $index): int
{
$propertyPartKinds = [
T_PUBLIC,
T_PROTECTED,
T_PRIVATE,
T_FINAL,
T_ABSTRACT,
T_COMMENT,
T_VAR,
T_STATIC,
T_STRING,
T_NS_SEPARATOR,
CT::T_ARRAY_TYPEHINT,
CT::T_NULLABLE_TYPE,
];
if (\defined('T_ATTRIBUTE')) {
$propertyPartKinds[] = T_ATTRIBUTE;
}
if (\defined('T_READONLY')) {
$propertyPartKinds[] = T_READONLY;
}
do {
$index = $tokens->getPrevNonWhitespace($index);
if ($tokens[$index]->isGivenKind(CT::T_ATTRIBUTE_CLOSE)) {
$index = $tokens->getPrevTokenOfKind($index, [[T_ATTRIBUTE]]);
}
} while ($tokens[$index]->isGivenKind($propertyPartKinds));
return $index;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractProxyFixer;
use PhpCsFixer\ConfigurationException\InvalidConfigurationException;
use PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
final class PhpdocTagCasingFixer extends AbstractProxyFixer implements ConfigurableFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Fixes casing of PHPDoc tags.',
[
new CodeSample("<?php\n/**\n * @inheritdoc\n */\n"),
new CodeSample("<?php\n/**\n * @inheritdoc\n * @Foo\n */\n", [
'tags' => ['foo'],
]),
]
);
}
public function getPriority(): int
{
return parent::getPriority();
}
public function configure(array $configuration): void
{
parent::configure($configuration);
$replacements = [];
foreach ($this->configuration['tags'] as $tag) {
$replacements[$tag] = $tag;
}
$generalPhpdocTagRenameFixer = $this->proxyFixers['general_phpdoc_tag_rename'];
try {
$generalPhpdocTagRenameFixer->configure([
'fix_annotation' => true,
'fix_inline' => true,
'replacements' => $replacements,
'case_sensitive' => false,
]);
} catch (InvalidConfigurationException $exception) {
throw new InvalidFixerConfigurationException(
$this->getName(),
Preg::replace('/^\[.+?\] /', '', $exception->getMessage()),
$exception
);
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('tags', 'List of tags to fix with their expected casing.'))
->setAllowedTypes(['array'])
->setDefault(['inheritDoc'])
->getOption(),
]);
}
protected function createProxyFixers(): array
{
return [new GeneralPhpdocTagRenameFixer()];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class AlignMultilineCommentFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
{
private $tokenKinds;
public function configure(array $configuration): void
{
parent::configure($configuration);
$this->tokenKinds = [T_DOC_COMMENT];
if ('phpdocs_only' !== $this->configuration['comment_type']) {
$this->tokenKinds[] = T_COMMENT;
}
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Each line of multi-line DocComments must have an asterisk [PSR-5] and must be aligned with the first one.',
[
new CodeSample(
'<?php
/**
* This is a DOC Comment
with a line not prefixed with asterisk
*/
'
),
new CodeSample(
'<?php
/*
* This is a doc-like multiline comment
*/
',
['comment_type' => 'phpdocs_like']
),
new CodeSample(
'<?php
/*
* This is a doc-like multiline comment
with a line not prefixed with asterisk
*/
',
['comment_type' => 'all_multiline']
),
]
);
}
public function getPriority(): int
{
return 27;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound($this->tokenKinds);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$lineEnding = $this->whitespacesConfig->getLineEnding();
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind($this->tokenKinds)) {
continue;
}
$whitespace = '';
$previousIndex = $index - 1;
if ($tokens[$previousIndex]->isWhitespace()) {
$whitespace = $tokens[$previousIndex]->getContent();
--$previousIndex;
}
if ($tokens[$previousIndex]->isGivenKind(T_OPEN_TAG)) {
$whitespace = Preg::replace('/\S/', '', $tokens[$previousIndex]->getContent()).$whitespace;
}
if (1 !== Preg::match('/\R(\h*)$/', $whitespace, $matches)) {
continue;
}
if ($token->isGivenKind(T_COMMENT) && 'all_multiline' !== $this->configuration['comment_type'] && 1 === Preg::match('/\R(?:\R|\s*[^\s\*])/', $token->getContent())) {
continue;
}
$indentation = $matches[1];
$lines = Preg::split('/\R/u', $token->getContent());
foreach ($lines as $lineNumber => $line) {
if (0 === $lineNumber) {
continue;
}
$line = ltrim($line);
if ($token->isGivenKind(T_COMMENT) && (!isset($line[0]) || '*' !== $line[0])) {
continue;
}
if (!isset($line[0])) {
$line = '*';
} elseif ('*' !== $line[0]) {
$line = '* '.$line;
}
$lines[$lineNumber] = $indentation.' '.$line;
}
$tokens[$index] = new Token([$token->getId(), implode($lineEnding, $lines)]);
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('comment_type', 'Whether to fix PHPDoc comments only (`phpdocs_only`), any multi-line comment whose lines all start with an asterisk (`phpdocs_like`) or any multi-line comment (`all_multiline`).'))
->setAllowedValues(['phpdocs_only', 'phpdocs_like', 'all_multiline'])
->setDefault('phpdocs_only')
->getOption(),
]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractProxyFixer;
use PhpCsFixer\ConfigurationException\InvalidConfigurationException;
use PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
final class PhpdocNoAliasTagFixer extends AbstractProxyFixer implements ConfigurableFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'No alias PHPDoc tags should be used.',
[
new CodeSample(
'<?php
/**
* @property string $foo
* @property-read string $bar
*
* @link baz
*/
final class Example
{
}
'
),
new CodeSample(
'<?php
/**
* @property string $foo
* @property-read string $bar
*
* @link baz
*/
final class Example
{
}
',
['replacements' => ['link' => 'website']]
),
]
);
}
public function getPriority(): int
{
return parent::getPriority();
}
public function configure(array $configuration): void
{
parent::configure($configuration);
$generalPhpdocTagRenameFixer = $this->proxyFixers['general_phpdoc_tag_rename'];
try {
$generalPhpdocTagRenameFixer->configure([
'fix_annotation' => true,
'fix_inline' => false,
'replacements' => $this->configuration['replacements'],
'case_sensitive' => true,
]);
} catch (InvalidConfigurationException $exception) {
throw new InvalidFixerConfigurationException(
$this->getName(),
Preg::replace('/^\[.+?\] /', '', $exception->getMessage()),
$exception
);
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('replacements', 'Mapping between replaced annotations with new ones.'))
->setAllowedTypes(['array'])
->setDefault([
'property-read' => 'property',
'property-write' => 'property',
'type' => 'var',
'link' => 'see',
])
->getOption(),
]);
}
protected function createProxyFixers(): array
{
return [new GeneralPhpdocTagRenameFixer()];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\DocBlock\Annotation;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\DocBlock\TypeExpression;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpdocTypesOrderFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Sorts PHPDoc types.',
[
new CodeSample(
'<?php
/**
* @param string|null $bar
*/
'
),
new CodeSample(
'<?php
/**
* @param null|string $bar
*/
',
['null_adjustment' => 'always_last']
),
new CodeSample(
'<?php
/**
* @param null|string|int|\Foo $bar
*/
',
['sort_algorithm' => 'alpha']
),
new CodeSample(
'<?php
/**
* @param null|string|int|\Foo $bar
*/
',
[
'sort_algorithm' => 'alpha',
'null_adjustment' => 'always_last',
]
),
new CodeSample(
'<?php
/**
* @param null|string|int|\Foo $bar
*/
',
[
'sort_algorithm' => 'alpha',
'null_adjustment' => 'none',
]
),
]
);
}
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOC_COMMENT);
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('sort_algorithm', 'The sorting algorithm to apply.'))
->setAllowedValues(['alpha', 'none'])
->setDefault('alpha')
->getOption(),
(new FixerOptionBuilder('null_adjustment', 'Forces the position of `null` (overrides `sort_algorithm`).'))
->setAllowedValues(['always_first', 'always_last', 'none'])
->setDefault('always_first')
->getOption(),
]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_DOC_COMMENT)) {
continue;
}
$doc = new DocBlock($token->getContent());
$annotations = $doc->getAnnotationsOfType(Annotation::getTagsWithTypes());
if (0 === \count($annotations)) {
continue;
}
foreach ($annotations as $annotation) {
$annotation->setTypes(
$this->sortTypes(
$annotation->getTypeExpression()
)
);
$line = $doc->getLine($annotation->getStart());
$line->setContent(Preg::replaceCallback('/(@method\s+.+?\s+\w+\()(.*)\)/', function (array $matches) {
$sorted = Preg::replaceCallback('/([^\s,]+)([\s]+\$[^\s,]+)/', function (array $matches): string {
return $this->sortJoinedTypes($matches[1]).$matches[2];
}, $matches[2]);
return $matches[1].$sorted.')';
}, $line->getContent()));
}
$tokens[$index] = new Token([T_DOC_COMMENT, $doc->getContent()]);
}
}
private function sortTypes(TypeExpression $typeExpression): array
{
$normalizeType = static function (string $type): string {
return Preg::replace('/^\\??\\\?/', '', $type);
};
$typeExpression->sortTypes(
function (TypeExpression $a, TypeExpression $b) use ($normalizeType): int {
$a = $normalizeType($a->toString());
$b = $normalizeType($b->toString());
$lowerCaseA = strtolower($a);
$lowerCaseB = strtolower($b);
if ('none' !== $this->configuration['null_adjustment']) {
if ('null' === $lowerCaseA && 'null' !== $lowerCaseB) {
return 'always_last' === $this->configuration['null_adjustment'] ? 1 : -1;
}
if ('null' !== $lowerCaseA && 'null' === $lowerCaseB) {
return 'always_last' === $this->configuration['null_adjustment'] ? -1 : 1;
}
}
if ('alpha' === $this->configuration['sort_algorithm']) {
return strcasecmp($a, $b);
}
return 0;
}
);
return $typeExpression->getTypes();
}
private function sortJoinedTypes(string $types): string
{
$typeExpression = new TypeExpression($types, null, []);
return implode('|', $this->sortTypes($typeExpression));
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpdocTrimFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'PHPDoc should start and end with content, excluding the very first and last line of the docblocks.',
[new CodeSample('<?php
/**
*
* Foo must be final class.
*
*
*/
final class Foo {}
')]
);
}
public function getPriority(): int
{
return -5;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOC_COMMENT);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_DOC_COMMENT)) {
continue;
}
$content = $token->getContent();
$content = $this->fixStart($content);
$content = $this->fixEnd($content);
$tokens[$index] = new Token([T_DOC_COMMENT, $content]);
}
}
private function fixStart(string $content): string
{
return Preg::replace(
'~
(^/\*\*) # DocComment begin
(?:
\R\h*(?:\*\h*)? # lines without useful content
(?!\R\h*\*/) # not followed by a DocComment end
)+
(\R\h*(?:\*\h*)?\S) # first line with useful content
~x',
'$1$2',
$content
);
}
private function fixEnd(string $content): string
{
return Preg::replace(
'~
(\R\h*(?:\*\h*)?\S.*?) # last line with useful content
(?:
(?<!/\*\*) # not preceded by a DocComment start
\R\h*(?:\*\h*)? # lines without useful content
)+
(\R\h*\*/$) # DocComment end
~xu',
'$1$2',
$content
);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpdocVarAnnotationCorrectOrderFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'`@var` and `@type` annotations must have type and name in the correct order.',
[new CodeSample('<?php
/** @var $foo int */
$foo = 2 + 2;
')]
);
}
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOC_COMMENT);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_DOC_COMMENT)) {
continue;
}
if (false === stripos($token->getContent(), '@var') && false === stripos($token->getContent(), '@type')) {
continue;
}
$newContent = Preg::replace(
'/(@(?:type|var)\s*)(\$\S+)(\h+)([^\$](?:[^<\s]|<[^>]*>)*)(\s|\*)/i',
'$1$4$3$2$5',
$token->getContent()
);
if ($newContent === $token->getContent()) {
continue;
}
$tokens[$index] = new Token([$token->getId(), $newContent]);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpdocSingleLineVarSpacingFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Single line `@var` PHPDoc should have proper spacing.',
[new CodeSample("<?php /**@var MyClass \$a */\n\$a = test();\n")]
);
}
public function getPriority(): int
{
return -10;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_COMMENT, T_DOC_COMMENT]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isComment()) {
continue;
}
$content = $token->getContent();
$fixedContent = $this->fixTokenContent($content);
if ($content !== $fixedContent) {
$tokens[$index] = new Token([T_DOC_COMMENT, $fixedContent]);
}
}
}
private function fixTokenContent(string $content): string
{
return Preg::replaceCallback(
'#^/\*\*\h*@var\h+(\S+)\h*(\$\S+)?\h*([^\n]*)\*/$#',
static function (array $matches) {
$content = '/** @var';
for ($i = 1, $m = \count($matches); $i < $m; ++$i) {
if ('' !== $matches[$i]) {
$content .= ' '.$matches[$i];
}
}
return rtrim($content).' */';
},
$content
);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NoBlankLinesAfterPhpdocFixer extends AbstractFixer
{
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOC_COMMENT);
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'There should not be blank lines between docblock and the documented element.',
[
new CodeSample(
'<?php
/**
* This is the bar class.
*/
class Bar {}
'
),
]
);
}
public function getPriority(): int
{
return -20;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
static $forbiddenSuccessors = [
T_BREAK,
T_COMMENT,
T_CONTINUE,
T_DECLARE,
T_DOC_COMMENT,
T_GOTO,
T_NAMESPACE,
T_RETURN,
T_THROW,
T_USE,
T_WHITESPACE,
];
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_DOC_COMMENT)) {
continue;
}
$next = $tokens->getNextNonWhitespace($index);
if ($index + 2 === $next && false === $tokens[$next]->isGivenKind($forbiddenSuccessors)) {
$this->fixWhitespace($tokens, $index + 1);
}
}
}
private function fixWhitespace(Tokens $tokens, int $index): void
{
$content = $tokens[$index]->getContent();
if (substr_count($content, "\n") > 1) {
$tokens[$index] = new Token([T_WHITESPACE, substr($content, strrpos($content, "\n"))]);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractPhpdocTypesFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
final class PhpdocTypesFixer extends AbstractPhpdocTypesFixer implements ConfigurableFixerInterface
{
private const POSSIBLE_TYPES = [
'simple' => [
'array',
'bool',
'callable',
'float',
'int',
'iterable',
'null',
'object',
'string',
],
'alias' => [
'boolean',
'callback',
'double',
'integer',
'real',
],
'meta' => [
'$this',
'false',
'mixed',
'parent',
'resource',
'scalar',
'self',
'static',
'true',
'void',
],
];
private string $patternToFix = '';
public function configure(array $configuration): void
{
parent::configure($configuration);
$typesToFix = array_merge(...array_map(static function (string $group): array {
return self::POSSIBLE_TYPES[$group];
}, $this->configuration['groups']));
$this->patternToFix = sprintf(
'/(?<![a-zA-Z0-9_\x80-\xff]\\\\)(\b|.(?=\$))(%s)\b(?!\\\\)/i',
implode(
'|',
array_map(
static function (string $type): string {
return preg_quote($type, '/');
},
$typesToFix
)
)
);
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'The correct case must be used for standard PHP types in PHPDoc.',
[
new CodeSample(
'<?php
/**
* @param STRING|String[] $bar
*
* @return inT[]
*/
'
),
new CodeSample(
'<?php
/**
* @param BOOL $foo
*
* @return MIXED
*/
',
['groups' => ['simple', 'alias']]
),
]
);
}
public function getPriority(): int
{
return 16;
}
protected function normalize(string $type): string
{
return Preg::replaceCallback(
$this->patternToFix,
function (array $matches): string {
return strtolower($matches[0]);
},
$type
);
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
$possibleGroups = array_keys(self::POSSIBLE_TYPES);
return new FixerConfigurationResolver([
(new FixerOptionBuilder('groups', 'Type groups to fix.'))
->setAllowedTypes(['array'])
->setAllowedValues([new AllowedValueSubset($possibleGroups)])
->setDefault($possibleGroups)
->getOption(),
]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\DocBlock\Annotation;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpdocNoEmptyReturnFixer extends AbstractFixer
{
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOC_COMMENT);
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'`@return void` and `@return null` annotations should be omitted from PHPDoc.',
[
new CodeSample(
'<?php
/**
* @return null
*/
function foo() {}
'
),
new CodeSample(
'<?php
/**
* @return void
*/
function foo() {}
'
),
]
);
}
public function getPriority(): int
{
return 4;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_DOC_COMMENT)) {
continue;
}
$doc = new DocBlock($token->getContent());
$annotations = $doc->getAnnotationsOfType('return');
if (0 === \count($annotations)) {
continue;
}
foreach ($annotations as $annotation) {
$this->fixAnnotation($annotation);
}
$newContent = $doc->getContent();
if ($newContent === $token->getContent()) {
continue;
}
if ('' === $newContent) {
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
continue;
}
$tokens[$index] = new Token([T_DOC_COMMENT, $doc->getContent()]);
}
}
private function fixAnnotation(Annotation $annotation): void
{
$types = $annotation->getNormalizedTypes();
if (1 === \count($types) && ('null' === $types[0] || 'void' === $types[0])) {
$annotation->remove();
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\DocBlock\Annotation;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\DocBlock\TagComparator;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
final class PhpdocSeparationFixer extends AbstractFixer implements ConfigurableFixerInterface
{
private array $groups;
public function getDefinition(): FixerDefinitionInterface
{
$code = <<<'EOF'
<?php
/**
* Hello there!
*
* @author John Doe
* @custom Test!
*
* @throws Exception|RuntimeException foo
* @param string $foo
*
* @param bool $bar Bar
* @return int Return the number of changes.
*/
EOF;
return new FixerDefinition(
'Annotations in PHPDoc should be grouped together so that annotations of the same type immediately follow each other. Annotations of a different type are separated by a single blank line.',
[
new CodeSample($code),
new CodeSample($code, ['groups' => [...TagComparator::DEFAULT_GROUPS, ['param', 'return']]]),
new CodeSample($code, ['groups' => [['author', 'throws', 'custom'], ['return', 'param']]]),
],
);
}
public function configure(array $configuration): void
{
parent::configure($configuration);
$this->groups = $this->configuration['groups'];
}
public function getPriority(): int
{
return -3;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOC_COMMENT);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_DOC_COMMENT)) {
continue;
}
$doc = new DocBlock($token->getContent());
$this->fixDescription($doc);
$this->fixAnnotations($doc);
$tokens[$index] = new Token([T_DOC_COMMENT, $doc->getContent()]);
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
$allowTagToBelongToOnlyOneGroup = function ($groups) {
$tags = [];
foreach ($groups as $groupIndex => $group) {
foreach ($group as $member) {
if (isset($tags[$member])) {
if ($groupIndex === $tags[$member]) {
throw new InvalidOptionsException(
'The option "groups" value is invalid. '.
'The "'.$member.'" tag is specified more than once.'
);
}
throw new InvalidOptionsException(
'The option "groups" value is invalid. '.
'The "'.$member.'" tag belongs to more than one group.'
);
}
$tags[$member] = $groupIndex;
}
}
return true;
};
return new FixerConfigurationResolver([
(new FixerOptionBuilder('groups', 'Sets of annotation types to be grouped together.'))
->setAllowedTypes(['string[][]'])
->setDefault(TagComparator::DEFAULT_GROUPS)
->setAllowedValues([$allowTagToBelongToOnlyOneGroup])
->getOption(),
]);
}
private function fixDescription(DocBlock $doc): void
{
foreach ($doc->getLines() as $index => $line) {
if ($line->containsATag()) {
break;
}
if ($line->containsUsefulContent()) {
$next = $doc->getLine($index + 1);
if (null !== $next && $next->containsATag()) {
$line->addBlank();
break;
}
}
}
}
private function fixAnnotations(DocBlock $doc): void
{
foreach ($doc->getAnnotations() as $index => $annotation) {
$next = $doc->getAnnotation($index + 1);
if (null === $next) {
break;
}
if (TagComparator::shouldBeTogether($annotation->getTag(), $next->getTag(), $this->groups)) {
$this->ensureAreTogether($doc, $annotation, $next);
} else {
$this->ensureAreSeparate($doc, $annotation, $next);
}
}
}
private function ensureAreTogether(DocBlock $doc, Annotation $first, Annotation $second): void
{
$pos = $first->getEnd();
$final = $second->getStart();
for ($pos = $pos + 1; $pos < $final; ++$pos) {
$doc->getLine($pos)->remove();
}
}
private function ensureAreSeparate(DocBlock $doc, Annotation $first, Annotation $second): void
{
$pos = $first->getEnd();
$final = $second->getStart() - 1;
if ($pos === $final) {
$doc->getLine($pos)->addBlank();
return;
}
for ($pos = $pos + 1; $pos < $final; ++$pos) {
$doc->getLine($pos)->remove();
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
final class PhpdocOrderFixer extends AbstractFixer implements ConfigurableFixerInterface
{
/**
@const
@TODO:
*/
private const ORDER_DEFAULT = ['param', 'throws', 'return'];
public function getDefinition(): FixerDefinitionInterface
{
$code = <<<'EOF'
<?php
/**
* Hello there!
*
* @throws Exception|RuntimeException foo
* @custom Test!
* @return int Return the number of changes.
* @param string $foo
* @param bool $bar Bar
*/
EOF;
return new FixerDefinition(
'Annotations in PHPDoc should be ordered in defined sequence.',
[
new CodeSample($code),
new CodeSample($code, ['order' => self::ORDER_DEFAULT]),
new CodeSample($code, ['order' => ['param', 'return', 'throws']]),
new CodeSample($code, ['order' => ['param', 'custom', 'throws', 'return']]),
],
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOC_COMMENT);
}
public function getPriority(): int
{
return -2;
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('order', 'Sequence in which annotations in PHPDoc should be ordered.'))
->setAllowedTypes(['string[]'])
->setAllowedValues([function ($order) {
if (\count($order) < 2) {
throw new InvalidOptionsException('The option "order" value is invalid. Minimum two tags are required.');
}
return true;
}])
->setDefault(self::ORDER_DEFAULT)
->getOption(),
]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_DOC_COMMENT)) {
continue;
}
$content = $token->getContent();
$successors = $this->configuration['order'];
while (\count($successors) >= 3) {
$predecessor = array_shift($successors);
$content = $this->moveAnnotationsBefore($predecessor, $successors, $content);
}
$predecessors = $this->configuration['order'];
$last = array_pop($predecessors);
$content = $this->moveAnnotationsAfter($last, $predecessors, $content);
$tokens[$index] = new Token([T_DOC_COMMENT, $content]);
}
}
private function moveAnnotationsBefore(string $move, array $before, string $content): string
{
$doc = new DocBlock($content);
$toBeMoved = $doc->getAnnotationsOfType($move);
if (0 === \count($toBeMoved)) {
return $content;
}
$others = $doc->getAnnotationsOfType($before);
if (0 === \count($others)) {
return $content;
}
$end = end($toBeMoved)->getEnd();
$line = $doc->getLine($end);
foreach ($others as $other) {
if ($other->getStart() < $end) {
$line->setContent($line->getContent().$other->getContent());
$other->remove();
}
}
return $doc->getContent();
}
private function moveAnnotationsAfter(string $move, array $after, string $content): string
{
$doc = new DocBlock($content);
$toBeMoved = $doc->getAnnotationsOfType($move);
if (0 === \count($toBeMoved)) {
return $content;
}
$others = $doc->getAnnotationsOfType($after);
if (0 === \count($others)) {
return $content;
}
$start = $toBeMoved[0]->getStart();
$line = $doc->getLine($start);
foreach (array_reverse($others) as $other) {
if ($other->getEnd() > $start) {
$line->setContent($other->getContent().$line->getContent());
$other->remove();
}
}
return $doc->getContent();
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpdocNoUselessInheritdocFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Classy that does not inherit must not have `@inheritdoc` tags.',
[
new CodeSample("<?php\n/** {@inheritdoc} */\nclass Sample\n{\n}\n"),
new CodeSample("<?php\nclass Sample\n{\n /**\n * @inheritdoc\n */\n public function Test()\n {\n }\n}\n"),
]
);
}
public function getPriority(): int
{
return 6;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOC_COMMENT) && $tokens->isAnyTokenKindsFound([T_CLASS, T_INTERFACE]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = 1, $count = \count($tokens) - 4; $index < $count; ++$index) {
if ($tokens[$index]->isGivenKind([T_CLASS, T_INTERFACE])) {
$index = $this->fixClassy($tokens, $index);
}
}
}
private function fixClassy(Tokens $tokens, int $index): int
{
$classOpenIndex = $tokens->getNextTokenOfKind($index, ['{']);
$classEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $classOpenIndex);
$extendingOrImplementing = $this->isExtendingOrImplementing($tokens, $index, $classOpenIndex);
if (!$extendingOrImplementing) {
$this->fixClassyOutside($tokens, $index);
}
if (!$extendingOrImplementing && $this->isUsingTrait($tokens, $index, $classOpenIndex, $classEndIndex)) {
$extendingOrImplementing = true;
}
$this->fixClassyInside($tokens, $classOpenIndex, $classEndIndex, !$extendingOrImplementing);
return $classEndIndex;
}
private function fixClassyInside(Tokens $tokens, int $classOpenIndex, int $classEndIndex, bool $fixThisLevel): void
{
for ($i = $classOpenIndex; $i < $classEndIndex; ++$i) {
if ($tokens[$i]->isGivenKind(T_CLASS)) {
$i = $this->fixClassy($tokens, $i);
} elseif ($fixThisLevel && $tokens[$i]->isGivenKind(T_DOC_COMMENT)) {
$this->fixToken($tokens, $i);
}
}
}
private function fixClassyOutside(Tokens $tokens, int $classIndex): void
{
$previousIndex = $tokens->getPrevNonWhitespace($classIndex);
if ($tokens[$previousIndex]->isGivenKind(T_DOC_COMMENT)) {
$this->fixToken($tokens, $previousIndex);
}
}
private function fixToken(Tokens $tokens, int $tokenIndex): void
{
$count = 0;
$content = Preg::replaceCallback(
'#(\h*(?:@{*|{*\h*@)\h*inheritdoc\h*)([^}]*)((?:}*)\h*)#i',
static function (array $matches): string {
return ' '.$matches[2];
},
$tokens[$tokenIndex]->getContent(),
-1,
$count
);
if ($count) {
$tokens[$tokenIndex] = new Token([T_DOC_COMMENT, $content]);
}
}
private function isExtendingOrImplementing(Tokens $tokens, int $classIndex, int $classOpenIndex): bool
{
for ($index = $classIndex; $index < $classOpenIndex; ++$index) {
if ($tokens[$index]->isGivenKind([T_EXTENDS, T_IMPLEMENTS])) {
return true;
}
}
return false;
}
private function isUsingTrait(Tokens $tokens, int $classIndex, int $classOpenIndex, int $classCloseIndex): bool
{
if ($tokens[$classIndex]->isGivenKind(T_INTERFACE)) {
return false;
}
$useIndex = $tokens->getNextTokenOfKind($classOpenIndex, [[CT::T_USE_TRAIT]]);
return null !== $useIndex && $useIndex < $classCloseIndex;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\DocBlock\Line;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpdocVarWithoutNameFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'`@var` and `@type` annotations of classy properties should not contain the name.',
[new CodeSample('<?php
final class Foo
{
/**
* @var int $bar
*/
public $bar;
/**
* @type $baz float
*/
public $baz;
}
')]
);
}
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOC_COMMENT) && $tokens->isAnyTokenKindsFound([T_CLASS, T_TRAIT]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_DOC_COMMENT)) {
continue;
}
$nextIndex = $tokens->getNextMeaningfulToken($index);
if (null === $nextIndex) {
continue;
}
if ($tokens[$nextIndex]->isGivenKind(T_STATIC)) {
$nextIndex = $tokens->getNextMeaningfulToken($nextIndex);
}
$propertyModifierKinds = [T_PRIVATE, T_PROTECTED, T_PUBLIC, T_VAR];
if (\defined('T_READONLY')) {
$propertyModifierKinds[] = T_READONLY;
}
if (!$tokens[$nextIndex]->isGivenKind($propertyModifierKinds)) {
continue;
}
$doc = new DocBlock($token->getContent());
$firstLevelLines = $this->getFirstLevelLines($doc);
$annotations = $doc->getAnnotationsOfType(['type', 'var']);
foreach ($annotations as $annotation) {
if (isset($firstLevelLines[$annotation->getStart()])) {
$this->fixLine($firstLevelLines[$annotation->getStart()]);
}
}
$tokens[$index] = new Token([T_DOC_COMMENT, $doc->getContent()]);
}
}
private function fixLine(Line $line): void
{
$content = $line->getContent();
Preg::matchAll('/ \$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $content, $matches);
if (isset($matches[0][0])) {
$line->setContent(str_replace($matches[0][0], '', $content));
}
}
private function getFirstLevelLines(DocBlock $docBlock): array
{
$nested = 0;
$lines = $docBlock->getLines();
foreach ($lines as $index => $line) {
$content = $line->getContent();
if (Preg::match('/\s*\*\s*}$/', $content)) {
--$nested;
}
if ($nested > 0) {
unset($lines[$index]);
}
if (Preg::match('/\s\{$/', $content)) {
++$nested;
}
}
return $lines;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\DocBlock\TypeExpression;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpdocAlignFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
{
public const ALIGN_LEFT = 'left';
public const ALIGN_VERTICAL = 'vertical';
private const ALIGNABLE_TAGS = [
'param',
'property',
'property-read',
'property-write',
'return',
'throws',
'type',
'var',
'method',
];
private const TAGS_WITH_NAME = [
'param',
'property',
'property-read',
'property-write',
];
private const TAGS_WITH_METHOD_SIGNATURE = [
'method',
];
private $regex;
private $regexCommentLine;
private $align;
public function configure(array $configuration): void
{
parent::configure($configuration);
$tagsWithNameToAlign = array_intersect($this->configuration['tags'], self::TAGS_WITH_NAME);
$tagsWithMethodSignatureToAlign = array_intersect($this->configuration['tags'], self::TAGS_WITH_METHOD_SIGNATURE);
$tagsWithoutNameToAlign = array_diff($this->configuration['tags'], $tagsWithNameToAlign, $tagsWithMethodSignatureToAlign);
$types = [];
$indent = '(?P<indent>(?:\ {2}|\t)*)';
if ([] !== $tagsWithNameToAlign) {
$types[] = '(?P<tag>'.implode('|', $tagsWithNameToAlign).')\s+(?P<hint>(?:'.TypeExpression::REGEX_TYPES.')?)\s+(?P<var>(?:&|\.{3})?\$\S+)';
}
if ([] !== $tagsWithoutNameToAlign) {
$types[] = '(?P<tag2>'.implode('|', $tagsWithoutNameToAlign).')\s+(?P<hint2>(?:'.TypeExpression::REGEX_TYPES.')?)';
}
if ([] !== $tagsWithMethodSignatureToAlign) {
$types[] = '(?P<tag3>'.implode('|', $tagsWithMethodSignatureToAlign).')(\s+(?P<static>static))?(\s+(?P<hint3>[^\s(]+)|)\s+(?P<signature>.+\))';
}
$desc = '(?:\s+(?P<desc>\V*))';
$this->regex = '/^'.$indent.'\ \*\ @(?J)(?:'.implode('|', $types).')'.$desc.'\s*$/ux';
$this->regexCommentLine = '/^'.$indent.' \*(?! @)(?:\s+(?P<desc>\V+))(?<!\*\/)\r?$/u';
$this->align = $this->configuration['align'];
}
public function getDefinition(): FixerDefinitionInterface
{
$code = <<<'EOF'
<?php
/**
* @param EngineInterface $templating
* @param string $format
* @param int $code an HTTP response status code
* @param bool $debug
* @param mixed &$reference a parameter passed by reference
*/
EOF;
return new FixerDefinition(
'All items of the given phpdoc tags must be either left-aligned or (by default) aligned vertically.',
[
new CodeSample($code),
new CodeSample($code, ['align' => self::ALIGN_VERTICAL]),
new CodeSample($code, ['align' => self::ALIGN_LEFT]),
]
);
}
public function getPriority(): int
{
return -42;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOC_COMMENT);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_DOC_COMMENT)) {
continue;
}
$content = $token->getContent();
$docBlock = new DocBlock($content);
$this->fixDocBlock($docBlock);
$newContent = $docBlock->getContent();
if ($newContent !== $content) {
$tokens[$index] = new Token([T_DOC_COMMENT, $newContent]);
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
$tags = new FixerOptionBuilder('tags', 'The tags that should be aligned.');
$tags
->setAllowedTypes(['array'])
->setAllowedValues([new AllowedValueSubset(self::ALIGNABLE_TAGS)])
->setDefault([
'method',
'param',
'property',
'return',
'throws',
'type',
'var',
])
;
$align = new FixerOptionBuilder('align', 'Align comments');
$align
->setAllowedTypes(['string'])
->setAllowedValues([self::ALIGN_LEFT, self::ALIGN_VERTICAL])
->setDefault(self::ALIGN_VERTICAL)
;
return new FixerConfigurationResolver([$tags->getOption(), $align->getOption()]);
}
private function fixDocBlock(DocBlock $docBlock): void
{
$lineEnding = $this->whitespacesConfig->getLineEnding();
for ($i = 0, $l = \count($docBlock->getLines()); $i < $l; ++$i) {
$matches = $this->getMatches($docBlock->getLine($i)->getContent());
if (null === $matches) {
continue;
}
$current = $i;
$items = [$matches];
while (true) {
if (null === $docBlock->getLine(++$i)) {
break 2;
}
$matches = $this->getMatches($docBlock->getLine($i)->getContent(), true);
if (null === $matches) {
break;
}
$items[] = $matches;
}
$hasStatic = false;
$tagMax = 0;
$hintMax = 0;
$varMax = 0;
foreach ($items as $item) {
if (null === $item['tag']) {
continue;
}
$hasStatic = $hasStatic || $item['static'];
$tagMax = max($tagMax, \strlen($item['tag']));
$hintMax = max($hintMax, \strlen($item['hint']));
$varMax = max($varMax, \strlen($item['var']));
}
$currTag = null;
foreach ($items as $j => $item) {
if (null === $item['tag']) {
if ('@' === $item['desc'][0]) {
$docBlock->getLine($current + $j)->setContent($item['indent'].' * '.$item['desc'].$lineEnding);
continue;
}
$extraIndent = 2;
if (\in_array($currTag, self::TAGS_WITH_NAME, true) || \in_array($currTag, self::TAGS_WITH_METHOD_SIGNATURE, true)) {
$extraIndent = 3;
}
if ($hasStatic) {
$extraIndent += 7;
}
$line =
$item['indent']
.' * '
.$this->getIndent(
$tagMax + $hintMax + $varMax + $extraIndent,
$this->getLeftAlignedDescriptionIndent($items, $j)
)
.$item['desc']
.$lineEnding;
$docBlock->getLine($current + $j)->setContent($line);
continue;
}
$currTag = $item['tag'];
$line =
$item['indent']
.' * @'
.$item['tag']
;
if ($hasStatic) {
$line .=
$this->getIndent(
$tagMax - \strlen($item['tag']) + 1,
$item['static'] ? 1 : 0
)
.($item['static'] ?: $this->getIndent(6 , 0))
;
$hintVerticalAlignIndent = 1;
} else {
$hintVerticalAlignIndent = $tagMax - \strlen($item['tag']) + 1;
}
$line .=
$this->getIndent(
$hintVerticalAlignIndent,
$item['hint'] ? 1 : 0
)
.$item['hint']
;
if (!empty($item['var'])) {
$line .=
$this->getIndent(($hintMax ?: -1) - \strlen($item['hint']) + 1)
.$item['var']
.(
!empty($item['desc'])
? $this->getIndent($varMax - \strlen($item['var']) + 1).$item['desc'].$lineEnding
: $lineEnding
)
;
} elseif (!empty($item['desc'])) {
$line .= $this->getIndent($hintMax - \strlen($item['hint']) + 1).$item['desc'].$lineEnding;
} else {
$line .= $lineEnding;
}
$docBlock->getLine($current + $j)->setContent($line);
}
}
}
private function getMatches(string $line, bool $matchCommentOnly = false): ?array
{
if (Preg::match($this->regex, $line, $matches)) {
if (!empty($matches['tag2'])) {
$matches['tag'] = $matches['tag2'];
$matches['hint'] = $matches['hint2'];
$matches['var'] = '';
}
if (!empty($matches['tag3'])) {
$matches['tag'] = $matches['tag3'];
$matches['hint'] = $matches['hint3'];
$matches['var'] = $matches['signature'];
if ('' === $matches['hint'] && '' !== $matches['static']) {
$matches['hint'] = $matches['static'];
$matches['static'] = '';
}
}
if (isset($matches['hint'])) {
$matches['hint'] = trim($matches['hint']);
}
if (!isset($matches['static'])) {
$matches['static'] = '';
}
return $matches;
}
if ($matchCommentOnly && Preg::match($this->regexCommentLine, $line, $matches)) {
$matches['tag'] = null;
$matches['var'] = '';
$matches['hint'] = '';
$matches['static'] = '';
return $matches;
}
return null;
}
private function getIndent(int $verticalAlignIndent, int $leftAlignIndent = 1): string
{
$indent = self::ALIGN_VERTICAL === $this->align ? $verticalAlignIndent : $leftAlignIndent;
return str_repeat(' ', $indent);
}
private function getLeftAlignedDescriptionIndent(array $items, int $index): int
{
if (self::ALIGN_LEFT !== $this->align) {
return 0;
}
$item = null;
for (; $index >= 0; --$index) {
$item = $items[$index];
if (null !== $item['tag']) {
break;
}
}
if (null === $item) {
return 0;
}
return
$this->getSentenceIndent($item['static']) +
$this->getSentenceIndent($item['tag']) +
$this->getSentenceIndent($item['hint']) +
$this->getSentenceIndent($item['var']);
}
private function getSentenceIndent(?string $sentence): int
{
if (null === $sentence) {
return 0;
}
$length = \strlen($sentence);
return 0 === $length ? 0 : $length + 1;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Analyzer\CommentsAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpdocToCommentFixer extends AbstractFixer implements ConfigurableFixerInterface
{
private array $ignoredTags = [];
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOC_COMMENT);
}
public function getPriority(): int
{
return 25;
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Docblocks should only be used on structural elements.',
[
new CodeSample(
'<?php
$first = true;// needed because by default first docblock is never fixed.
/** This should be a comment */
foreach($connections as $key => $sqlite) {
$sqlite->open($path);
}
'
),
new CodeSample(
'<?php
$first = true;// needed because by default first docblock is never fixed.
/** This should be a comment */
foreach($connections as $key => $sqlite) {
$sqlite->open($path);
}
/** @todo This should be a PHPDoc as the tag is on "ignored_tags" list */
foreach($connections as $key => $sqlite) {
$sqlite->open($path);
}
',
['ignored_tags' => ['todo']]
),
]
);
}
public function configure(array $configuration = null): void
{
parent::configure($configuration);
$this->ignoredTags = array_map(
static function (string $tag): string {
return strtolower($tag);
},
$this->configuration['ignored_tags']
);
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('ignored_tags', 'List of ignored tags (matched case insensitively)'))
->setAllowedTypes(['array'])
->setDefault([])
->getOption(),
]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$commentsAnalyzer = new CommentsAnalyzer();
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_DOC_COMMENT)) {
continue;
}
if ($commentsAnalyzer->isHeaderComment($tokens, $index)) {
continue;
}
if ($commentsAnalyzer->isBeforeStructuralElement($tokens, $index)) {
continue;
}
if (0 < Preg::matchAll('~\@([a-zA-Z0-9_\\\\-]+)\b~', $token->getContent(), $matches)) {
foreach ($matches[1] as $match) {
if (\in_array(strtolower($match), $this->ignoredTags, true)) {
continue 2;
}
}
}
$tokens[$index] = new Token([T_COMMENT, '/*'.ltrim($token->getContent(), '/*')]);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\DocBlock\Annotation;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\DocBlock\TypeExpression;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Analyzer\NamespaceUsesAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class NoSuperfluousPhpdocTagsFixer extends AbstractFixer implements ConfigurableFixerInterface
{
private const NO_TYPE_INFO = [
'types' => [],
'allows_null' => true,
];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Removes `@param`, `@return` and `@var` tags that don\'t provide any useful information.',
[
new CodeSample('<?php
class Foo {
/**
* @param Bar $bar
* @param mixed $baz
*
* @return Baz
*/
public function doFoo(Bar $bar, $baz): Baz {}
}
'),
new CodeSample('<?php
class Foo {
/**
* @param Bar $bar
* @param mixed $baz
*/
public function doFoo(Bar $bar, $baz) {}
}
', ['allow_mixed' => true]),
new CodeSample('<?php
class Foo {
/**
* @inheritDoc
*/
public function doFoo(Bar $bar, $baz) {}
}
', ['remove_inheritdoc' => true]),
new CodeSample('<?php
class Foo {
/**
* @param Bar $bar
* @param mixed $baz
* @param string|int|null $qux
*/
public function doFoo(Bar $bar, $baz /*, $qux = null */) {}
}
', ['allow_unused_params' => true]),
]
);
}
public function getPriority(): int
{
return 6;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOC_COMMENT);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$tokensAnalyzer = new TokensAnalyzer($tokens);
$namespaceUseAnalyzer = new NamespaceUsesAnalyzer();
$shortNames = [];
$currentSymbol = null;
$currentSymbolEndIndex = null;
foreach ($namespaceUseAnalyzer->getDeclarationsFromTokens($tokens) as $namespaceUseAnalysis) {
$shortNames[strtolower($namespaceUseAnalysis->getShortName())] = '\\'.strtolower($namespaceUseAnalysis->getFullName());
}
$symbolKinds = [T_CLASS, T_INTERFACE];
if (\defined('T_ENUM')) {
$symbolKinds[] = T_ENUM;
}
foreach ($tokens as $index => $token) {
if ($index === $currentSymbolEndIndex) {
$currentSymbol = null;
$currentSymbolEndIndex = null;
continue;
}
if ($token->isGivenKind(T_CLASS) && $tokensAnalyzer->isAnonymousClass($index)) {
continue;
}
if ($token->isGivenKind($symbolKinds)) {
$currentSymbol = $tokens[$tokens->getNextMeaningfulToken($index)]->getContent();
$currentSymbolEndIndex = $tokens->findBlockEnd(
Tokens::BLOCK_TYPE_CURLY_BRACE,
$tokens->getNextTokenOfKind($index, ['{']),
);
continue;
}
if (!$token->isGivenKind(T_DOC_COMMENT)) {
continue;
}
$documentedElement = $this->findDocumentedElement($tokens, $index);
if (null === $documentedElement) {
continue;
}
$content = $initialContent = $token->getContent();
if (true === $this->configuration['remove_inheritdoc']) {
$content = $this->removeSuperfluousInheritDoc($content);
}
if ('function' === $documentedElement['type']) {
$content = $this->fixFunctionDocComment($content, $tokens, $documentedElement, $currentSymbol, $shortNames);
} elseif ('property' === $documentedElement['type']) {
$content = $this->fixPropertyDocComment($content, $tokens, $documentedElement, $currentSymbol, $shortNames);
} elseif ('classy' === $documentedElement['type']) {
$content = $this->fixClassDocComment($content, $documentedElement);
} else {
throw new \RuntimeException('Unknown type.');
}
if ('' === $content) {
$content = '/** */';
}
if ($content !== $initialContent) {
$tokens[$index] = new Token([T_DOC_COMMENT, $content]);
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('allow_mixed', 'Whether type `mixed` without description is allowed (`true`) or considered superfluous (`false`)'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
(new FixerOptionBuilder('remove_inheritdoc', 'Remove `@inheritDoc` tags'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
(new FixerOptionBuilder('allow_unused_params', 'Whether `param` annotation without actual signature is allowed (`true`) or considered superfluous (`false`)'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
]);
}
private function findDocumentedElement(Tokens $tokens, int $docCommentIndex): ?array
{
$modifierKinds = [
T_PRIVATE,
T_PROTECTED,
T_PUBLIC,
T_ABSTRACT,
T_FINAL,
T_STATIC,
];
$typeKinds = [
CT::T_NULLABLE_TYPE,
CT::T_ARRAY_TYPEHINT,
CT::T_TYPE_ALTERNATION,
CT::T_TYPE_INTERSECTION,
T_STRING,
T_NS_SEPARATOR,
];
if (\defined('T_READONLY')) {
$modifierKinds[] = T_READONLY;
}
$element = [
'modifiers' => [],
'types' => [],
];
$index = $tokens->getNextMeaningfulToken($docCommentIndex);
if (null !== $index && \defined('T_ATTRIBUTE') && $tokens[$index]->isGivenKind(T_ATTRIBUTE)) {
do {
$index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ATTRIBUTE, $index);
$index = $tokens->getNextMeaningfulToken($index);
} while (null !== $index && $tokens[$index]->isGivenKind(T_ATTRIBUTE));
}
while (true) {
if (null === $index) {
break;
}
if ($tokens[$index]->isClassy()) {
$element['index'] = $index;
$element['type'] = 'classy';
return $element;
}
if ($tokens[$index]->isGivenKind(T_FUNCTION)) {
$element['index'] = $index;
$element['type'] = 'function';
return $element;
}
if ($tokens[$index]->isGivenKind(T_VARIABLE)) {
$element['index'] = $index;
$element['type'] = 'property';
return $element;
}
if ($tokens[$index]->isGivenKind($modifierKinds)) {
$element['modifiers'][$index] = $tokens[$index];
} elseif ($tokens[$index]->isGivenKind($typeKinds)) {
$element['types'][$index] = $tokens[$index];
} else {
break;
}
$index = $tokens->getNextMeaningfulToken($index);
}
return null;
}
private function fixFunctionDocComment(
string $content,
Tokens $tokens,
array $element,
?string $currentSymbol,
array $shortNames
): string {
$docBlock = new DocBlock($content);
$openingParenthesisIndex = $tokens->getNextTokenOfKind($element['index'], ['(']);
$closingParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openingParenthesisIndex);
$argumentsInfo = $this->getArgumentsInfo(
$tokens,
$openingParenthesisIndex + 1,
$closingParenthesisIndex - 1
);
foreach ($docBlock->getAnnotationsOfType('param') as $annotation) {
$argumentName = $annotation->getVariableName();
if (null === $argumentName) {
if ($this->annotationIsSuperfluous($annotation, self::NO_TYPE_INFO, $currentSymbol, $shortNames)) {
$annotation->remove();
}
continue;
}
if (!isset($argumentsInfo[$argumentName]) && true === $this->configuration['allow_unused_params']) {
continue;
}
if (!isset($argumentsInfo[$argumentName]) || $this->annotationIsSuperfluous($annotation, $argumentsInfo[$argumentName], $currentSymbol, $shortNames)) {
$annotation->remove();
}
}
$returnTypeInfo = $this->getReturnTypeInfo($tokens, $closingParenthesisIndex);
foreach ($docBlock->getAnnotationsOfType('return') as $annotation) {
if ($this->annotationIsSuperfluous($annotation, $returnTypeInfo, $currentSymbol, $shortNames)) {
$annotation->remove();
}
}
$this->removeSuperfluousModifierAnnotation($docBlock, $element);
return $docBlock->getContent();
}
private function fixPropertyDocComment(
string $content,
Tokens $tokens,
array $element,
?string $currentSymbol,
array $shortNames
): string {
if (\count($element['types']) > 0) {
$propertyTypeInfo = $this->parseTypeHint($tokens, array_key_first($element['types']));
} else {
$propertyTypeInfo = self::NO_TYPE_INFO;
}
$docBlock = new DocBlock($content);
foreach ($docBlock->getAnnotationsOfType('var') as $annotation) {
if ($this->annotationIsSuperfluous($annotation, $propertyTypeInfo, $currentSymbol, $shortNames)) {
$annotation->remove();
}
}
return $docBlock->getContent();
}
private function fixClassDocComment(string $content, array $element): string
{
$docBlock = new DocBlock($content);
$this->removeSuperfluousModifierAnnotation($docBlock, $element);
return $docBlock->getContent();
}
private function getArgumentsInfo(Tokens $tokens, int $start, int $end): array
{
$argumentsInfo = [];
for ($index = $start; $index <= $end; ++$index) {
$token = $tokens[$index];
if (!$token->isGivenKind(T_VARIABLE)) {
continue;
}
$beforeArgumentIndex = $tokens->getPrevTokenOfKind($index, ['(', ',']);
$typeIndex = $tokens->getNextMeaningfulToken($beforeArgumentIndex);
if ($typeIndex !== $index) {
$info = $this->parseTypeHint($tokens, $typeIndex);
} else {
$info = self::NO_TYPE_INFO;
}
if (!$info['allows_null']) {
$nextIndex = $tokens->getNextMeaningfulToken($index);
if (
$tokens[$nextIndex]->equals('=')
&& $tokens[$tokens->getNextMeaningfulToken($nextIndex)]->equals([T_STRING, 'null'], false)
) {
$info['allows_null'] = true;
}
}
$argumentsInfo[$token->getContent()] = $info;
}
return $argumentsInfo;
}
private function getReturnTypeInfo(Tokens $tokens, int $closingParenthesisIndex): array
{
$colonIndex = $tokens->getNextMeaningfulToken($closingParenthesisIndex);
return $tokens[$colonIndex]->isGivenKind(CT::T_TYPE_COLON)
? $this->parseTypeHint($tokens, $tokens->getNextMeaningfulToken($colonIndex))
: self::NO_TYPE_INFO
;
}
private function parseTypeHint(Tokens $tokens, int $index): array
{
$allowsNull = false;
$types = [];
while (true) {
$type = '';
if (\defined('T_READONLY') && $tokens[$index]->isGivenKind(T_READONLY)) {
$index = $tokens->getNextMeaningfulToken($index);
}
if ($tokens[$index]->isGivenKind([CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC, CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED, CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE])) {
$index = $tokens->getNextMeaningfulToken($index);
continue;
}
if ($tokens[$index]->isGivenKind(CT::T_NULLABLE_TYPE)) {
$allowsNull = true;
$index = $tokens->getNextMeaningfulToken($index);
}
while ($tokens[$index]->isGivenKind([T_NS_SEPARATOR, T_STATIC, T_STRING, CT::T_ARRAY_TYPEHINT, T_CALLABLE])) {
$type .= $tokens[$index]->getContent();
$index = $tokens->getNextMeaningfulToken($index);
}
if ('' === $type) {
break;
}
$types[] = $type;
if (!$tokens[$index]->isGivenKind([CT::T_TYPE_ALTERNATION, CT::T_TYPE_INTERSECTION])) {
break;
}
$index = $tokens->getNextMeaningfulToken($index);
}
return [
'types' => $types,
'allows_null' => $allowsNull,
];
}
private function annotationIsSuperfluous(
Annotation $annotation,
array $info,
?string $currentSymbol,
array $symbolShortNames
): bool {
if ('param' === $annotation->getTag()->getName()) {
$regex = '{@param(?:\s+'.TypeExpression::REGEX_TYPES.')?(?:\s+(?:\&\s*)?(?:\.{3}\s*)?\$\S+)?(?:\s+(?<description>(?!\*+\/)\S+))?}sx';
} elseif ('var' === $annotation->getTag()->getName()) {
$regex = '{@var(?:\s+'.TypeExpression::REGEX_TYPES.')?(?:\s+\$\S+)?(?:\s+(?<description>(?!\*\/)\S+))?}sx';
} else {
$regex = '{@return(?:\s+'.TypeExpression::REGEX_TYPES.')?(?:\s+(?<description>(?!\*\/)\S+))?}sx';
}
if (1 !== Preg::match($regex, $annotation->getContent(), $matches)) {
return false;
}
if (isset($matches['description'])) {
return false;
}
if (!isset($matches['types']) || '' === $matches['types']) {
return true;
}
$annotationTypes = $this->toComparableNames($annotation->getTypes(), $currentSymbol, $symbolShortNames);
if (['null'] === $annotationTypes) {
return false;
}
if (['mixed'] === $annotationTypes && [] === $info['types']) {
return false === $this->configuration['allow_mixed'];
}
$actualTypes = $info['types'];
if ($info['allows_null']) {
$actualTypes[] = 'null';
}
return $annotationTypes === $this->toComparableNames($actualTypes, $currentSymbol, $symbolShortNames);
}
private function toComparableNames(array $types, ?string $currentSymbol, array $symbolShortNames): array
{
$normalized = array_map(
static function (string $type) use ($currentSymbol, $symbolShortNames): string {
if ('self' === $type && null !== $currentSymbol) {
$type = $currentSymbol;
}
$type = strtolower($type);
if (str_contains($type, '&')) {
$intersects = explode('&', $type);
sort($intersects);
return implode('&', $intersects);
}
return $symbolShortNames[$type] ?? $type;
},
$types
);
sort($normalized);
return $normalized;
}
private function removeSuperfluousInheritDoc(string $docComment): string
{
return Preg::replace('~
# $1: before @inheritDoc tag
(
# beginning of comment or a PHPDoc tag
(?:
^/\*\*
(?:
\R
[ \t]*(?:\*[ \t]*)?
)*?
|
@\N+
)
# empty comment lines
(?:
\R
[ \t]*(?:\*[ \t]*?)?
)*
)
# spaces before @inheritDoc tag
[ \t]*
# @inheritDoc tag
(?:@inheritDocs?|\{@inheritDocs?\})
# $2: after @inheritDoc tag
(
# empty comment lines
(?:
\R
[ \t]*(?:\*[ \t]*)?
)*
# a PHPDoc tag or end of comment
(?:
@\N+
|
(?:
\R
[ \t]*(?:\*[ \t]*)?
)*
[ \t]*\*/$
)
)
~ix', '$1$2', $docComment);
}
private function removeSuperfluousModifierAnnotation(DocBlock $docBlock, array $element): void
{
foreach (['abstract' => T_ABSTRACT, 'final' => T_FINAL] as $annotationType => $modifierToken) {
$annotations = $docBlock->getAnnotationsOfType($annotationType);
foreach ($element['modifiers'] as $token) {
if ($token->isGivenKind($modifierToken)) {
foreach ($annotations as $annotation) {
$annotation->remove();
}
}
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Utils;
final class PhpdocIndentFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Docblocks should have the same indentation as the documented subject.',
[new CodeSample('<?php
class DocBlocks
{
/**
* Test constants
*/
const INDENT = 1;
}
')]
);
}
public function getPriority(): int
{
return 20;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOC_COMMENT);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_DOC_COMMENT)) {
continue;
}
$nextIndex = $tokens->getNextMeaningfulToken($index);
if (null === $nextIndex || $tokens[$nextIndex]->equals('}')) {
continue;
}
$prevIndex = $index - 1;
$prevToken = $tokens[$prevIndex];
if (
$prevToken->isGivenKind(T_OPEN_TAG)
|| ($prevToken->isWhitespace(" \t") && !$tokens[$index - 2]->isGivenKind(T_OPEN_TAG))
|| $prevToken->equalsAny([';', ',', '{', '('])
) {
continue;
}
if ($tokens[$nextIndex - 1]->isWhitespace()) {
$indent = Utils::calculateTrailingWhitespaceIndent($tokens[$nextIndex - 1]);
} else {
$indent = '';
}
$newPrevContent = $this->fixWhitespaceBeforeDocblock($prevToken->getContent(), $indent);
if ('' !== $newPrevContent) {
if ($prevToken->isArray()) {
$tokens[$prevIndex] = new Token([$prevToken->getId(), $newPrevContent]);
} else {
$tokens[$prevIndex] = new Token($newPrevContent);
}
} else {
$tokens->clearAt($prevIndex);
}
$tokens[$index] = new Token([T_DOC_COMMENT, $this->fixDocBlock($token->getContent(), $indent)]);
}
}
private function fixDocBlock(string $content, string $indent): string
{
return ltrim(Preg::replace('/^\h*\*/m', $indent.' *', $content));
}
private function fixWhitespaceBeforeDocblock(string $content, string $indent): string
{
return rtrim($content, " \t").$indent;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Phpdoc;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpdocInlineTagNormalizerFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOC_COMMENT);
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Fixes PHPDoc inline tags.',
[
new CodeSample(
"<?php\n/**\n * @{TUTORIAL}\n * {{ @link }}\n * @inheritDoc\n */\n"
),
new CodeSample(
"<?php\n/**\n * @{TUTORIAL}\n * {{ @link }}\n * @inheritDoc\n */\n",
['tags' => ['TUTORIAL']]
),
]
);
}
public function getPriority(): int
{
return 0;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
if (0 === \count($this->configuration['tags'])) {
return;
}
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_DOC_COMMENT)) {
continue;
}
$content = Preg::replaceCallback(
sprintf(
'#(?:@{+|{+\h*@)\h*(%s)s?([^}]*)(?:}+)#i',
implode('|', array_map(static function (string $tag): string {
return preg_quote($tag, '/');
}, $this->configuration['tags']))
),
static function (array $matches): string {
$doc = trim($matches[2]);
if ('' === $doc) {
return '{@'.$matches[1].'}';
}
return '{@'.$matches[1].' '.$doc.'}';
},
$token->getContent()
);
$tokens[$index] = new Token([T_DOC_COMMENT, $content]);
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('tags', 'The list of tags to normalize'))
->setAllowedTypes(['array'])
->setDefault(['example', 'id', 'internal', 'inheritdoc', 'inheritdocs', 'link', 'source', 'toc', 'tutorial'])
->getOption(),
]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ClassUsage;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class DateTimeImmutableFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Class `DateTimeImmutable` should be used instead of `DateTime`.',
[new CodeSample("<?php\nnew DateTime();\n")],
null,
'Risky when the code relies on modifying `DateTime` objects or if any of the `date_create*` functions are overridden.'
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_STRING);
}
public function isRisky(): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$functionsAnalyzer = new FunctionsAnalyzer();
$functionMap = [
'date_create' => 'date_create_immutable',
'date_create_from_format' => 'date_create_immutable_from_format',
];
$isInNamespace = false;
$isImported = false;
for ($index = 0, $limit = $tokens->count(); $index < $limit; ++$index) {
$token = $tokens[$index];
if ($token->isGivenKind(T_NAMESPACE)) {
$isInNamespace = true;
continue;
}
if ($isInNamespace && $token->isGivenKind(T_USE)) {
$nextIndex = $tokens->getNextMeaningfulToken($index);
if ('datetime' !== strtolower($tokens[$nextIndex]->getContent())) {
continue;
}
$nextNextIndex = $tokens->getNextMeaningfulToken($nextIndex);
if ($tokens[$nextNextIndex]->equals(';')) {
$isImported = true;
}
$index = $nextNextIndex;
continue;
}
if (!$token->isGivenKind(T_STRING)) {
continue;
}
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$prevIndex]->isGivenKind(T_FUNCTION)) {
continue;
}
$lowercaseContent = strtolower($token->getContent());
if ('datetime' === $lowercaseContent) {
$this->fixClassUsage($tokens, $index, $isInNamespace, $isImported);
$limit = $tokens->count();
continue;
}
if (isset($functionMap[$lowercaseContent]) && $functionsAnalyzer->isGlobalFunctionCall($tokens, $index)) {
$tokens[$index] = new Token([T_STRING, $functionMap[$lowercaseContent]]);
}
}
}
private function fixClassUsage(Tokens $tokens, int $index, bool $isInNamespace, bool $isImported): void
{
$nextIndex = $tokens->getNextMeaningfulToken($index);
if ($tokens[$nextIndex]->isGivenKind(T_DOUBLE_COLON)) {
$nextNextIndex = $tokens->getNextMeaningfulToken($nextIndex);
if ($tokens[$nextNextIndex]->isGivenKind(T_STRING)) {
$nextNextNextIndex = $tokens->getNextMeaningfulToken($nextNextIndex);
if (!$tokens[$nextNextNextIndex]->equals('(')) {
return;
}
}
}
$isUsedAlone = false;
$isUsedWithLeadingBackslash = false;
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$prevIndex]->isGivenKind(T_NS_SEPARATOR)) {
$prevPrevIndex = $tokens->getPrevMeaningfulToken($prevIndex);
if (!$tokens[$prevPrevIndex]->isGivenKind(T_STRING)) {
$isUsedWithLeadingBackslash = true;
}
} elseif (!$tokens[$prevIndex]->isGivenKind(T_DOUBLE_COLON) && !$tokens[$prevIndex]->isObjectOperator()) {
$isUsedAlone = true;
}
if ($isUsedWithLeadingBackslash || $isUsedAlone && ($isInNamespace && $isImported || !$isInNamespace)) {
$tokens[$index] = new Token([T_STRING, \DateTimeImmutable::class]);
if ($isInNamespace && $isUsedAlone) {
$tokens->insertAt($index, new Token([T_NS_SEPARATOR, '\\']));
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Tokens;
interface FixerInterface
{
public function isCandidate(Tokens $tokens): bool;
public function isRisky(): bool;
public function fix(\SplFileInfo $file, Tokens $tokens): void;
public function getDefinition(): FixerDefinitionInterface;
public function getName(): string;
public function getPriority(): int;
public function supports(\SplFileInfo $file): bool;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\LanguageConstruct;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class CombineConsecutiveIssetsFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Using `isset($var) &&` multiple times should be done in one call.',
[new CodeSample("<?php\n\$a = isset(\$a) && isset(\$b);\n")]
);
}
public function getPriority(): int
{
return 3;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAllTokenKindsFound([T_ISSET, T_BOOLEAN_AND]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$tokenCount = $tokens->count();
for ($index = 1; $index < $tokenCount; ++$index) {
if (!$tokens[$index]->isGivenKind(T_ISSET)
|| !$tokens[$tokens->getPrevMeaningfulToken($index)]->equalsAny(['(', '{', ';', '=', [T_OPEN_TAG], [T_BOOLEAN_AND], [T_BOOLEAN_OR]])) {
continue;
}
$issetInfo = $this->getIssetInfo($tokens, $index);
$issetCloseBraceIndex = end($issetInfo);
$insertLocation = prev($issetInfo) + 1;
$booleanAndTokenIndex = $tokens->getNextMeaningfulToken($issetCloseBraceIndex);
while ($tokens[$booleanAndTokenIndex]->isGivenKind(T_BOOLEAN_AND)) {
$issetIndex = $tokens->getNextMeaningfulToken($booleanAndTokenIndex);
if (!$tokens[$issetIndex]->isGivenKind(T_ISSET)) {
$index = $issetIndex;
break;
}
$nextIssetInfo = $this->getIssetInfo($tokens, $issetIndex);
$nextMeaningfulTokenIndex = $tokens->getNextMeaningfulToken(end($nextIssetInfo));
$nextMeaningfulToken = $tokens[$nextMeaningfulTokenIndex];
if (!$nextMeaningfulToken->equalsAny([')', '}', ';', [T_CLOSE_TAG], [T_BOOLEAN_AND], [T_BOOLEAN_OR]])) {
$index = $nextMeaningfulTokenIndex;
break;
}
$clones = $this->getTokenClones($tokens, \array_slice($nextIssetInfo, 1, -1));
$this->clearTokens($tokens, array_merge($nextIssetInfo, [$issetIndex, $booleanAndTokenIndex]));
array_unshift($clones, new Token(','), new Token([T_WHITESPACE, ' ']));
$tokens->insertAt($insertLocation, $clones);
$numberOfTokensInserted = \count($clones);
$tokenCount += $numberOfTokensInserted;
$issetCloseBraceIndex += $numberOfTokensInserted;
$insertLocation += $numberOfTokensInserted;
$booleanAndTokenIndex = $tokens->getNextMeaningfulToken($issetCloseBraceIndex);
}
}
}
private function clearTokens(Tokens $tokens, array $indices): void
{
foreach ($indices as $index) {
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
}
}
private function getIssetInfo(Tokens $tokens, int $index): array
{
$openIndex = $tokens->getNextMeaningfulToken($index);
$braceOpenCount = 1;
$meaningfulTokenIndices = [$openIndex];
for ($i = $openIndex + 1;; ++$i) {
if ($tokens[$i]->isWhitespace() || $tokens[$i]->isComment()) {
continue;
}
$meaningfulTokenIndices[] = $i;
if ($tokens[$i]->equals(')')) {
--$braceOpenCount;
if (0 === $braceOpenCount) {
break;
}
} elseif ($tokens[$i]->equals('(')) {
++$braceOpenCount;
}
}
return $meaningfulTokenIndices;
}
private function getTokenClones(Tokens $tokens, array $indices): array
{
$clones = [];
foreach ($indices as $i) {
$clones[] = clone $tokens[$i];
}
return $clones;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\LanguageConstruct;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class ExplicitIndirectVariableFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Add curly braces to indirect variables to make them clear to understand. Requires PHP >= 7.0.',
[
new CodeSample(
<<<'EOT'
<?php
echo $$foo;
echo $$foo['bar'];
echo $foo->$bar['baz'];
echo $foo->$callback($baz);
EOT
),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_VARIABLE);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = $tokens->count() - 1; $index > 1; --$index) {
$token = $tokens[$index];
if (!$token->isGivenKind(T_VARIABLE)) {
continue;
}
$prevIndex = $tokens->getPrevMeaningfulToken($index);
$prevToken = $tokens[$prevIndex];
if (!$prevToken->equals('$') && !$prevToken->isObjectOperator()) {
continue;
}
$openingBrace = CT::T_DYNAMIC_VAR_BRACE_OPEN;
$closingBrace = CT::T_DYNAMIC_VAR_BRACE_CLOSE;
if ($prevToken->isObjectOperator()) {
$openingBrace = CT::T_DYNAMIC_PROP_BRACE_OPEN;
$closingBrace = CT::T_DYNAMIC_PROP_BRACE_CLOSE;
}
$tokens->overrideRange($index, $index, [
new Token([$openingBrace, '{']),
new Token([T_VARIABLE, $token->getContent()]),
new Token([$closingBrace, '}']),
]);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\LanguageConstruct;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class ErrorSuppressionFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public const OPTION_MUTE_DEPRECATION_ERROR = 'mute_deprecation_error';
public const OPTION_NOISE_REMAINING_USAGES = 'noise_remaining_usages';
public const OPTION_NOISE_REMAINING_USAGES_EXCLUDE = 'noise_remaining_usages_exclude';
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Error control operator should be added to deprecation notices and/or removed from other cases.',
[
new CodeSample("<?php\ntrigger_error('Warning.', E_USER_DEPRECATED);\n"),
new CodeSample(
"<?php\n@mkdir(\$dir);\n@unlink(\$path);\n",
[self::OPTION_NOISE_REMAINING_USAGES => true]
),
new CodeSample(
"<?php\n@mkdir(\$dir);\n@unlink(\$path);\n",
[
self::OPTION_NOISE_REMAINING_USAGES => true,
self::OPTION_NOISE_REMAINING_USAGES_EXCLUDE => ['unlink'],
]
),
],
null,
'Risky because adding/removing `@` might cause changes to code behaviour or if `trigger_error` function is overridden.'
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_STRING);
}
public function isRisky(): bool
{
return true;
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder(self::OPTION_MUTE_DEPRECATION_ERROR, 'Whether to add `@` in deprecation notices.'))
->setAllowedTypes(['bool'])
->setDefault(true)
->getOption(),
(new FixerOptionBuilder(self::OPTION_NOISE_REMAINING_USAGES, 'Whether to remove `@` in remaining usages.'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
(new FixerOptionBuilder(self::OPTION_NOISE_REMAINING_USAGES_EXCLUDE, 'List of global functions to exclude from removing `@`'))
->setAllowedTypes(['array'])
->setDefault([])
->getOption(),
]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$functionsAnalyzer = new FunctionsAnalyzer();
$excludedFunctions = array_map(static function (string $function): string {
return strtolower($function);
}, $this->configuration[self::OPTION_NOISE_REMAINING_USAGES_EXCLUDE]);
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
$token = $tokens[$index];
if (true === $this->configuration[self::OPTION_NOISE_REMAINING_USAGES] && $token->equals('@')) {
$tokens->clearAt($index);
continue;
}
if (!$functionsAnalyzer->isGlobalFunctionCall($tokens, $index)) {
continue;
}
$functionIndex = $index;
$startIndex = $index;
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$prevIndex]->isGivenKind(T_NS_SEPARATOR)) {
$startIndex = $prevIndex;
$prevIndex = $tokens->getPrevMeaningfulToken($startIndex);
}
$index = $prevIndex;
if ($this->isDeprecationErrorCall($tokens, $functionIndex)) {
if (false === $this->configuration[self::OPTION_MUTE_DEPRECATION_ERROR]) {
continue;
}
if ($tokens[$prevIndex]->equals('@')) {
continue;
}
$tokens->insertAt($startIndex, new Token('@'));
continue;
}
if (!$tokens[$prevIndex]->equals('@')) {
continue;
}
if (true === $this->configuration[self::OPTION_NOISE_REMAINING_USAGES] && !\in_array($tokens[$functionIndex]->getContent(), $excludedFunctions, true)) {
$tokens->clearAt($index);
}
}
}
private function isDeprecationErrorCall(Tokens $tokens, int $index): bool
{
if ('trigger_error' !== strtolower($tokens[$index]->getContent())) {
return false;
}
$endBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $tokens->getNextTokenOfKind($index, [[T_STRING], '(']));
$prevIndex = $tokens->getPrevMeaningfulToken($endBraceIndex);
if ($tokens[$prevIndex]->equals(',')) {
$prevIndex = $tokens->getPrevMeaningfulToken($prevIndex);
}
return $tokens[$prevIndex]->equals([T_STRING, 'E_USER_DEPRECATED']);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\LanguageConstruct;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class DeclareEqualNormalizeFixer extends AbstractFixer implements ConfigurableFixerInterface
{
private $callback;
public function configure(array $configuration): void
{
parent::configure($configuration);
$this->callback = 'none' === $this->configuration['space'] ? 'removeWhitespaceAroundToken' : 'ensureWhitespaceAroundToken';
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Equal sign in declare statement should be surrounded by spaces or not following configuration.',
[
new CodeSample("<?php\ndeclare(ticks = 1);\n"),
new CodeSample("<?php\ndeclare(ticks=1);\n", ['space' => 'single']),
]
);
}
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DECLARE);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$callback = $this->callback;
for ($index = 0, $count = $tokens->count(); $index < $count - 6; ++$index) {
if (!$tokens[$index]->isGivenKind(T_DECLARE)) {
continue;
}
$openParenthesisIndex = $tokens->getNextMeaningfulToken($index);
$closeParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openParenthesisIndex);
for ($i = $closeParenthesisIndex; $i > $openParenthesisIndex; --$i) {
if ($tokens[$i]->equals('=')) {
$this->{$callback}($tokens, $i);
}
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('space', 'Spacing to apply around the equal sign.'))
->setAllowedValues(['single', 'none'])
->setDefault('none')
->getOption(),
]);
}
private function ensureWhitespaceAroundToken(Tokens $tokens, int $index): void
{
if ($tokens[$index + 1]->isWhitespace()) {
if (' ' !== $tokens[$index + 1]->getContent()) {
$tokens[$index + 1] = new Token([T_WHITESPACE, ' ']);
}
} else {
$tokens->insertAt($index + 1, new Token([T_WHITESPACE, ' ']));
}
if ($tokens[$index - 1]->isWhitespace()) {
if (' ' !== $tokens[$index - 1]->getContent() && !$tokens[$tokens->getPrevNonWhitespace($index - 1)]->isComment()) {
$tokens[$index - 1] = new Token([T_WHITESPACE, ' ']);
}
} else {
$tokens->insertAt($index, new Token([T_WHITESPACE, ' ']));
}
}
private function removeWhitespaceAroundToken(Tokens $tokens, int $index): void
{
if (!$tokens[$tokens->getPrevNonWhitespace($index)]->isComment()) {
$tokens->removeLeadingWhitespace($index);
}
$tokens->removeTrailingWhitespace($index);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\LanguageConstruct;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Tokens;
final class DeclareParenthesesFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'There must not be spaces around `declare` statement parentheses.',
[new CodeSample("<?php declare ( strict_types=1 );\n")]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DECLARE);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = $tokens->count() - 1; 0 <= $index; --$index) {
$token = $tokens[$index];
if (!$token->isGivenKind(T_DECLARE)) {
continue;
}
$tokens->removeTrailingWhitespace($index);
$startParenthesisIndex = $tokens->getNextTokenOfKind($index, ['(']);
$tokens->removeTrailingWhitespace($startParenthesisIndex);
$endParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $startParenthesisIndex);
$tokens->removeLeadingWhitespace($endParenthesisIndex);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\LanguageConstruct;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class FunctionToConstantFixer extends AbstractFixer implements ConfigurableFixerInterface
{
private static $availableFunctions;
private array $functionsFixMap;
public function __construct()
{
if (null === self::$availableFunctions) {
self::$availableFunctions = [
'get_called_class' => [
new Token([T_STATIC, 'static']),
new Token([T_DOUBLE_COLON, '::']),
new Token([CT::T_CLASS_CONSTANT, 'class']),
],
'get_class' => [new Token([T_CLASS_C, '__CLASS__'])],
'get_class_this' => [
new Token([T_STATIC, 'static']),
new Token([T_DOUBLE_COLON, '::']),
new Token([CT::T_CLASS_CONSTANT, 'class']),
],
'php_sapi_name' => [new Token([T_STRING, 'PHP_SAPI'])],
'phpversion' => [new Token([T_STRING, 'PHP_VERSION'])],
'pi' => [new Token([T_STRING, 'M_PI'])],
];
}
parent::__construct();
}
public function configure(array $configuration): void
{
parent::configure($configuration);
$this->functionsFixMap = [];
foreach ($this->configuration['functions'] as $key) {
$this->functionsFixMap[$key] = self::$availableFunctions[$key];
}
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Replace core functions calls returning constants with the constants.',
[
new CodeSample(
"<?php\necho phpversion();\necho pi();\necho php_sapi_name();\nclass Foo\n{\n public function Bar()\n {\n echo get_class();\n echo get_called_class();\n }\n}\n"
),
new CodeSample(
"<?php\necho phpversion();\necho pi();\nclass Foo\n{\n public function Bar()\n {\n echo get_class();\n get_class(\$this);\n echo get_called_class();\n }\n}\n",
['functions' => ['get_called_class', 'get_class_this', 'phpversion']]
),
],
null,
'Risky when any of the configured functions to replace are overridden.'
);
}
public function getPriority(): int
{
return 1;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_STRING);
}
public function isRisky(): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$functionAnalyzer = new FunctionsAnalyzer();
for ($index = $tokens->count() - 4; $index > 0; --$index) {
$candidate = $this->getReplaceCandidate($tokens, $functionAnalyzer, $index);
if (null === $candidate) {
continue;
}
$this->fixFunctionCallToConstant(
$tokens,
$index,
$candidate[0],
$candidate[1],
$candidate[2]
);
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
$functionNames = array_keys(self::$availableFunctions);
return new FixerConfigurationResolver([
(new FixerOptionBuilder('functions', 'List of function names to fix.'))
->setAllowedTypes(['array'])
->setAllowedValues([new AllowedValueSubset($functionNames)])
->setDefault([
'get_called_class',
'get_class',
'get_class_this',
'php_sapi_name',
'phpversion',
'pi',
])
->getOption(),
]);
}
private function fixFunctionCallToConstant(Tokens $tokens, int $index, int $braceOpenIndex, int $braceCloseIndex, array $replacements): void
{
for ($i = $braceCloseIndex; $i >= $braceOpenIndex; --$i) {
if ($tokens[$i]->isGivenKind([T_WHITESPACE, T_COMMENT, T_DOC_COMMENT])) {
continue;
}
$tokens->clearTokenAndMergeSurroundingWhitespace($i);
}
if ($replacements[0]->isGivenKind([T_CLASS_C, T_STATIC])) {
$prevIndex = $tokens->getPrevMeaningfulToken($index);
$prevToken = $tokens[$prevIndex];
if ($prevToken->isGivenKind(T_NS_SEPARATOR)) {
$tokens->clearAt($prevIndex);
}
}
$tokens->clearAt($index);
$tokens->insertAt($index, $replacements);
}
private function getReplaceCandidate(
Tokens $tokens,
FunctionsAnalyzer $functionAnalyzer,
int $index
): ?array {
if (!$tokens[$index]->isGivenKind(T_STRING)) {
return null;
}
$lowerContent = strtolower($tokens[$index]->getContent());
if ('get_class' === $lowerContent) {
return $this->fixGetClassCall($tokens, $functionAnalyzer, $index);
}
if (!isset($this->functionsFixMap[$lowerContent])) {
return null;
}
if (!$functionAnalyzer->isGlobalFunctionCall($tokens, $index)) {
return null;
}
$braceOpenIndex = $tokens->getNextMeaningfulToken($index);
if (!$tokens[$braceOpenIndex]->equals('(')) {
return null;
}
$braceCloseIndex = $tokens->getNextMeaningfulToken($braceOpenIndex);
if (!$tokens[$braceCloseIndex]->equals(')')) {
return null;
}
return $this->getReplacementTokenClones($lowerContent, $braceOpenIndex, $braceCloseIndex);
}
private function fixGetClassCall(
Tokens $tokens,
FunctionsAnalyzer $functionAnalyzer,
int $index
): ?array {
if (!isset($this->functionsFixMap['get_class']) && !isset($this->functionsFixMap['get_class_this'])) {
return null;
}
if (!$functionAnalyzer->isGlobalFunctionCall($tokens, $index)) {
return null;
}
$braceOpenIndex = $tokens->getNextMeaningfulToken($index);
$braceCloseIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $braceOpenIndex);
if ($braceCloseIndex === $tokens->getNextMeaningfulToken($braceOpenIndex)) {
if (isset($this->functionsFixMap['get_class'])) {
return $this->getReplacementTokenClones('get_class', $braceOpenIndex, $braceCloseIndex);
}
} elseif (isset($this->functionsFixMap['get_class_this'])) {
$isThis = false;
for ($i = $braceOpenIndex + 1; $i < $braceCloseIndex; ++$i) {
if ($tokens[$i]->equalsAny([[T_WHITESPACE], [T_COMMENT], [T_DOC_COMMENT], ')'])) {
continue;
}
if ($tokens[$i]->isGivenKind(T_VARIABLE) && '$this' === strtolower($tokens[$i]->getContent())) {
$isThis = true;
continue;
}
if (false === $isThis && $tokens[$i]->equals('(')) {
continue;
}
$isThis = false;
break;
}
if ($isThis) {
return $this->getReplacementTokenClones('get_class_this', $braceOpenIndex, $braceCloseIndex);
}
}
return null;
}
private function getReplacementTokenClones(string $lowerContent, int $braceOpenIndex, int $braceCloseIndex): array
{
$clones = [];
foreach ($this->functionsFixMap[$lowerContent] as $token) {
$clones[] = clone $token;
}
return [
$braceOpenIndex,
$braceCloseIndex,
$clones,
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\LanguageConstruct;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class SingleSpaceAfterConstructFixer extends AbstractFixer implements ConfigurableFixerInterface
{
private static array $tokenMap = [
'abstract' => T_ABSTRACT,
'as' => T_AS,
'attribute' => CT::T_ATTRIBUTE_CLOSE,
'break' => T_BREAK,
'case' => T_CASE,
'catch' => T_CATCH,
'class' => T_CLASS,
'clone' => T_CLONE,
'comment' => T_COMMENT,
'const' => T_CONST,
'const_import' => CT::T_CONST_IMPORT,
'continue' => T_CONTINUE,
'do' => T_DO,
'echo' => T_ECHO,
'else' => T_ELSE,
'elseif' => T_ELSEIF,
'enum' => null,
'extends' => T_EXTENDS,
'final' => T_FINAL,
'finally' => T_FINALLY,
'for' => T_FOR,
'foreach' => T_FOREACH,
'function' => T_FUNCTION,
'function_import' => CT::T_FUNCTION_IMPORT,
'global' => T_GLOBAL,
'goto' => T_GOTO,
'if' => T_IF,
'implements' => T_IMPLEMENTS,
'include' => T_INCLUDE,
'include_once' => T_INCLUDE_ONCE,
'instanceof' => T_INSTANCEOF,
'insteadof' => T_INSTEADOF,
'interface' => T_INTERFACE,
'match' => null,
'named_argument' => CT::T_NAMED_ARGUMENT_COLON,
'namespace' => T_NAMESPACE,
'new' => T_NEW,
'open_tag_with_echo' => T_OPEN_TAG_WITH_ECHO,
'php_doc' => T_DOC_COMMENT,
'php_open' => T_OPEN_TAG,
'print' => T_PRINT,
'private' => T_PRIVATE,
'protected' => T_PROTECTED,
'public' => T_PUBLIC,
'readonly' => null,
'require' => T_REQUIRE,
'require_once' => T_REQUIRE_ONCE,
'return' => T_RETURN,
'static' => T_STATIC,
'switch' => T_SWITCH,
'throw' => T_THROW,
'trait' => T_TRAIT,
'try' => T_TRY,
'type_colon' => CT::T_TYPE_COLON,
'use' => T_USE,
'use_lambda' => CT::T_USE_LAMBDA,
'use_trait' => CT::T_USE_TRAIT,
'var' => T_VAR,
'while' => T_WHILE,
'yield' => T_YIELD,
'yield_from' => T_YIELD_FROM,
];
private array $fixTokenMap = [];
public function configure(array $configuration): void
{
parent::configure($configuration);
if (\defined('T_MATCH')) {
self::$tokenMap['match'] = T_MATCH;
}
if (\defined('T_READONLY')) {
self::$tokenMap['readonly'] = T_READONLY;
}
if (\defined('T_ENUM')) {
self::$tokenMap['enum'] = T_ENUM;
}
$this->fixTokenMap = [];
foreach ($this->configuration['constructs'] as $key) {
if (null !== self::$tokenMap[$key]) {
$this->fixTokenMap[$key] = self::$tokenMap[$key];
}
}
if (isset($this->fixTokenMap['public'])) {
$this->fixTokenMap['constructor_public'] = CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC;
}
if (isset($this->fixTokenMap['protected'])) {
$this->fixTokenMap['constructor_protected'] = CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED;
}
if (isset($this->fixTokenMap['private'])) {
$this->fixTokenMap['constructor_private'] = CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE;
}
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Ensures a single space after language constructs.',
[
new CodeSample(
'<?php
throw new \Exception();
'
),
new CodeSample(
'<?php
echo "Hello!";
',
[
'constructs' => [
'echo',
],
]
),
new CodeSample(
'<?php
yield from baz();
',
[
'constructs' => [
'yield_from',
],
]
),
]
);
}
public function getPriority(): int
{
return 36;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound(array_values($this->fixTokenMap)) && !$tokens->hasAlternativeSyntax();
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$tokenKinds = array_values($this->fixTokenMap);
for ($index = $tokens->count() - 2; $index >= 0; --$index) {
$token = $tokens[$index];
if (!$token->isGivenKind($tokenKinds)) {
continue;
}
$whitespaceTokenIndex = $index + 1;
if ($tokens[$whitespaceTokenIndex]->equalsAny([',', ';', ')', [CT::T_ARRAY_SQUARE_BRACE_CLOSE], [CT::T_DESTRUCTURING_SQUARE_BRACE_CLOSE]])) {
continue;
}
if (
$token->isGivenKind(T_STATIC)
&& !$tokens[$tokens->getNextMeaningfulToken($index)]->isGivenKind([T_FUNCTION, T_VARIABLE])
) {
continue;
}
if ($token->isGivenKind(T_OPEN_TAG)) {
if ($tokens[$whitespaceTokenIndex]->equals([T_WHITESPACE]) && !str_contains($tokens[$whitespaceTokenIndex]->getContent(), "\n") && !str_contains($token->getContent(), "\n")) {
$tokens->clearAt($whitespaceTokenIndex);
}
continue;
}
if ($token->isGivenKind(T_CLASS) && $tokens[$tokens->getNextMeaningfulToken($index)]->equals('(')) {
continue;
}
if ($token->isGivenKind([T_EXTENDS, T_IMPLEMENTS]) && $this->isMultilineExtendsOrImplementsWithMoreThanOneAncestor($tokens, $index)) {
continue;
}
if ($token->isGivenKind(T_RETURN) && $this->isMultiLineReturn($tokens, $index)) {
continue;
}
if ($token->isGivenKind(T_CONST) && $this->isMultilineConstant($tokens, $index)) {
continue;
}
if ($token->isComment() || $token->isGivenKind(CT::T_ATTRIBUTE_CLOSE)) {
if ($tokens[$whitespaceTokenIndex]->equals([T_WHITESPACE]) && str_contains($tokens[$whitespaceTokenIndex]->getContent(), "\n")) {
continue;
}
}
$tokens->ensureWhitespaceAtIndex($whitespaceTokenIndex, 0, ' ');
if (
$token->isGivenKind(T_YIELD_FROM)
&& 'yield from' !== strtolower($token->getContent())
) {
$tokens[$index] = new Token([T_YIELD_FROM, Preg::replace(
'/\s+/',
' ',
$token->getContent()
)]);
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
$defaults = self::$tokenMap;
$tokens = array_keys($defaults);
unset($defaults['type_colon']);
return new FixerConfigurationResolver([
(new FixerOptionBuilder('constructs', 'List of constructs which must be followed by a single space.'))
->setAllowedTypes(['array'])
->setAllowedValues([new AllowedValueSubset($tokens)])
->setDefault(array_keys($defaults))
->getOption(),
]);
}
private function isMultiLineReturn(Tokens $tokens, int $index): bool
{
++$index;
$tokenFollowingReturn = $tokens[$index];
if (
!$tokenFollowingReturn->isGivenKind(T_WHITESPACE)
|| !str_contains($tokenFollowingReturn->getContent(), "\n")
) {
return false;
}
$nestedCount = 0;
for ($indexEnd = \count($tokens) - 1, ++$index; $index < $indexEnd; ++$index) {
if (str_contains($tokens[$index]->getContent(), "\n")) {
return true;
}
if ($tokens[$index]->equals('{')) {
++$nestedCount;
} elseif ($tokens[$index]->equals('}')) {
--$nestedCount;
} elseif (0 === $nestedCount && $tokens[$index]->equalsAny([';', [T_CLOSE_TAG]])) {
break;
}
}
return false;
}
private function isMultilineExtendsOrImplementsWithMoreThanOneAncestor(Tokens $tokens, int $index): bool
{
$hasMoreThanOneAncestor = false;
while (++$index) {
$token = $tokens[$index];
if ($token->equals(',')) {
$hasMoreThanOneAncestor = true;
continue;
}
if ($token->equals('{')) {
return false;
}
if ($hasMoreThanOneAncestor && str_contains($token->getContent(), "\n")) {
return true;
}
}
return false;
}
private function isMultilineConstant(Tokens $tokens, int $index): bool
{
$scopeEnd = $tokens->getNextTokenOfKind($index, [';', [T_CLOSE_TAG]]) - 1;
$hasMoreThanOneConstant = null !== $tokens->findSequence([new Token(',')], $index + 1, $scopeEnd);
return $hasMoreThanOneConstant && $tokens->isPartialCodeMultiline($index, $scopeEnd);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\LanguageConstruct;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\FixerDefinition\VersionSpecification;
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class GetClassToClassKeywordFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Replace `get_class` calls on object variables with class keyword syntax.',
[
new VersionSpecificCodeSample(
"<?php\nget_class(\$a);\n",
new VersionSpecification(80000)
),
new VersionSpecificCodeSample(
"<?php\n\n\$date = new \\DateTimeImmutable();\n\$class = get_class(\$date);\n",
new VersionSpecification(80000)
),
],
null,
'Risky if the `get_class` function is overridden.'
);
}
public function getPriority(): int
{
return 1;
}
public function isCandidate(Tokens $tokens): bool
{
return \PHP_VERSION_ID >= 80000 && $tokens->isAllTokenKindsFound([T_STRING, T_VARIABLE]);
}
public function isRisky(): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$functionsAnalyzer = new FunctionsAnalyzer();
$indicesToClear = [];
$tokenSlices = [];
for ($index = $tokens->count() - 1; $index > 0; --$index) {
if (!$tokens[$index]->equals([T_STRING, 'get_class'], false)) {
continue;
}
if (!$functionsAnalyzer->isGlobalFunctionCall($tokens, $index)) {
continue;
}
$braceOpenIndex = $tokens->getNextMeaningfulToken($index);
$braceCloseIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $braceOpenIndex);
if ($braceCloseIndex === $tokens->getNextMeaningfulToken($braceOpenIndex)) {
continue;
}
$meaningfulTokensCount = 0;
$variableTokensIndices = [];
for ($i = $braceOpenIndex + 1; $i < $braceCloseIndex; ++$i) {
if (!$tokens[$i]->equalsAny([[T_WHITESPACE], [T_COMMENT], [T_DOC_COMMENT], '(', ')'])) {
++$meaningfulTokensCount;
}
if (!$tokens[$i]->isGivenKind(T_VARIABLE)) {
continue;
}
if ('$this' === strtolower($tokens[$i]->getContent())) {
continue 2;
}
$variableTokensIndices[] = $i;
}
if ($meaningfulTokensCount > 1 || 1 !== \count($variableTokensIndices)) {
continue;
}
$indicesToClear[$index] = [$braceOpenIndex, current($variableTokensIndices), $braceCloseIndex];
}
foreach ($indicesToClear as $index => $items) {
$tokenSlices[$index] = $this->getReplacementTokenSlices($tokens, $items[1]);
$this->clearGetClassCall($tokens, $index, $items[0], $items[2]);
}
$tokens->insertSlices($tokenSlices);
}
private function getReplacementTokenSlices(Tokens $tokens, int $variableIndex): array
{
return [
new Token([T_VARIABLE, $tokens[$variableIndex]->getContent()]),
new Token([T_DOUBLE_COLON, '::']),
new Token([CT::T_CLASS_CONSTANT, 'class']),
];
}
private function clearGetClassCall(Tokens $tokens, int $index, int $braceOpenIndex, int $braceCloseIndex): void
{
for ($i = $braceOpenIndex; $i <= $braceCloseIndex; ++$i) {
if ($tokens[$i]->isGivenKind([T_WHITESPACE, T_COMMENT, T_DOC_COMMENT])) {
continue;
}
$tokens->clearTokenAndMergeSurroundingWhitespace($i);
}
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$prevIndex]->isGivenKind(T_NS_SEPARATOR)) {
$tokens->clearAt($prevIndex);
}
$tokens->clearAt($index);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\LanguageConstruct;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class IsNullFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Replaces `is_null($var)` expression with `null === $var`.',
[
new CodeSample("<?php\n\$a = is_null(\$b);\n"),
],
null,
'Risky when the function `is_null` is overridden.'
);
}
public function getPriority(): int
{
return 1;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_STRING);
}
public function isRisky(): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
static $sequenceNeeded = [[T_STRING, 'is_null'], '('];
$functionsAnalyzer = new FunctionsAnalyzer();
$currIndex = 0;
while (true) {
$matches = $tokens->findSequence($sequenceNeeded, $currIndex, $tokens->count() - 1, false);
if (null === $matches) {
break;
}
$matches = array_keys($matches);
[$isNullIndex, $currIndex] = $matches;
if (!$functionsAnalyzer->isGlobalFunctionCall($tokens, $matches[0])) {
continue;
}
$next = $tokens->getNextMeaningfulToken($currIndex);
if ($tokens[$next]->equals(')')) {
continue;
}
$prevTokenIndex = $tokens->getPrevMeaningfulToken($matches[0]);
if ($tokens[$prevTokenIndex]->isGivenKind(T_NS_SEPARATOR)) {
$tokens->removeTrailingWhitespace($prevTokenIndex);
$tokens->clearAt($prevTokenIndex);
$prevTokenIndex = $tokens->getPrevMeaningfulToken($prevTokenIndex);
}
$isInvertedNullCheck = false;
if ($tokens[$prevTokenIndex]->equals('!')) {
$isInvertedNullCheck = true;
$tokens->removeTrailingWhitespace($prevTokenIndex);
$tokens->clearAt($prevTokenIndex);
}
$referenceEnd = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $matches[1]);
$isContainingDangerousConstructs = false;
for ($paramTokenIndex = $matches[1]; $paramTokenIndex <= $referenceEnd; ++$paramTokenIndex) {
if (\in_array($tokens[$paramTokenIndex]->getContent(), ['?', '?:', '=', '??'], true)) {
$isContainingDangerousConstructs = true;
break;
}
}
$parentLeftToken = $tokens[$tokens->getPrevMeaningfulToken($isNullIndex)];
$parentRightToken = $tokens[$tokens->getNextMeaningfulToken($referenceEnd)];
$parentOperations = [T_IS_EQUAL, T_IS_NOT_EQUAL, T_IS_IDENTICAL, T_IS_NOT_IDENTICAL];
$wrapIntoParentheses = $parentLeftToken->isCast() || $parentLeftToken->isGivenKind($parentOperations) || $parentRightToken->isGivenKind($parentOperations);
$prevIndex = $tokens->getPrevMeaningfulToken($referenceEnd);
if ($tokens[$prevIndex]->equals(',')) {
$tokens->clearTokenAndMergeSurroundingWhitespace($prevIndex);
}
if (!$isContainingDangerousConstructs) {
$tokens->removeLeadingWhitespace($referenceEnd);
$tokens->clearAt($referenceEnd);
$tokens->removeLeadingWhitespace($matches[1]);
$tokens->removeTrailingWhitespace($matches[1]);
$tokens->clearAt($matches[1]);
}
$replacement = [
new Token([T_STRING, 'null']),
new Token([T_WHITESPACE, ' ']),
new Token($isInvertedNullCheck ? [T_IS_NOT_IDENTICAL, '!=='] : [T_IS_IDENTICAL, '===']),
new Token([T_WHITESPACE, ' ']),
];
if ($wrapIntoParentheses) {
array_unshift($replacement, new Token('('));
$tokens->insertAt($referenceEnd + 1, new Token(')'));
}
$tokens->overrideRange($isNullIndex, $isNullIndex, $replacement);
$currIndex = $isNullIndex;
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\LanguageConstruct;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class CombineConsecutiveUnsetsFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Calling `unset` on multiple items should be done in one call.',
[new CodeSample("<?php\nunset(\$a); unset(\$b);\n")]
);
}
public function getPriority(): int
{
return 24;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_UNSET);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
if (!$tokens[$index]->isGivenKind(T_UNSET)) {
continue;
}
$previousUnsetCall = $this->getPreviousUnsetCall($tokens, $index);
if (\is_int($previousUnsetCall)) {
$index = $previousUnsetCall;
continue;
}
[$previousUnset, , $previousUnsetBraceEnd] = $previousUnsetCall;
$tokensAddCount = $this->moveTokens(
$tokens,
$nextUnsetContentStart = $tokens->getNextTokenOfKind($index, ['(']),
$nextUnsetContentEnd = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $nextUnsetContentStart),
$previousUnsetBraceEnd - 1
);
if (!$tokens[$previousUnsetBraceEnd]->isWhitespace()) {
$tokens->insertAt($previousUnsetBraceEnd, new Token([T_WHITESPACE, ' ']));
++$tokensAddCount;
}
$tokens->insertAt($previousUnsetBraceEnd, new Token(','));
++$tokensAddCount;
$this->clearOffsetTokens($tokens, $tokensAddCount, [$index, $nextUnsetContentStart, $nextUnsetContentEnd]);
$nextUnsetSemicolon = $tokens->getNextMeaningfulToken($nextUnsetContentEnd);
if (null !== $nextUnsetSemicolon && $tokens[$nextUnsetSemicolon]->equals(';')) {
$tokens->clearTokenAndMergeSurroundingWhitespace($nextUnsetSemicolon);
}
$index = $previousUnset + 1;
}
}
private function clearOffsetTokens(Tokens $tokens, int $offset, array $indices): void
{
foreach ($indices as $index) {
$tokens->clearTokenAndMergeSurroundingWhitespace($index + $offset);
}
}
private function getPreviousUnsetCall(Tokens $tokens, int $index)
{
$previousUnsetSemicolon = $tokens->getPrevMeaningfulToken($index);
if (null === $previousUnsetSemicolon) {
return $index;
}
if (!$tokens[$previousUnsetSemicolon]->equals(';')) {
return $previousUnsetSemicolon;
}
$previousUnsetBraceEnd = $tokens->getPrevMeaningfulToken($previousUnsetSemicolon);
if (null === $previousUnsetBraceEnd) {
return $index;
}
if (!$tokens[$previousUnsetBraceEnd]->equals(')')) {
return $previousUnsetBraceEnd;
}
$previousUnsetBraceStart = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $previousUnsetBraceEnd);
$previousUnset = $tokens->getPrevMeaningfulToken($previousUnsetBraceStart);
if (null === $previousUnset) {
return $index;
}
if (!$tokens[$previousUnset]->isGivenKind(T_UNSET)) {
return $previousUnset;
}
return [
$previousUnset,
$previousUnsetBraceStart,
$previousUnsetBraceEnd,
$previousUnsetSemicolon,
];
}
private function moveTokens(Tokens $tokens, int $start, int $end, int $to): int
{
$added = 0;
for ($i = $start + 1; $i < $end; $i += 2) {
if ($tokens[$i]->isWhitespace() && $tokens[$to + 1]->isWhitespace()) {
$tokens[$to + 1] = new Token([T_WHITESPACE, $tokens[$to + 1]->getContent().$tokens[$i]->getContent()]);
} else {
$tokens->insertAt(++$to, clone $tokens[$i]);
++$end;
++$added;
}
$tokens->clearAt($i + 1);
}
return $added;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\LanguageConstruct;
use PhpCsFixer\AbstractFunctionReferenceFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class DirConstantFixer extends AbstractFunctionReferenceFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Replaces `dirname(__FILE__)` expression with equivalent `__DIR__` constant.',
[new CodeSample("<?php\n\$a = dirname(__FILE__);\n")],
null,
'Risky when the function `dirname` is overridden.'
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAllTokenKindsFound([T_STRING, T_FILE]);
}
public function getPriority(): int
{
return 40;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$currIndex = 0;
do {
$boundaries = $this->find('dirname', $tokens, $currIndex, $tokens->count() - 1);
if (null === $boundaries) {
return;
}
[$functionNameIndex, $openParenthesis, $closeParenthesis] = $boundaries;
$currIndex = $openParenthesis;
$fileCandidateRightIndex = $tokens->getPrevMeaningfulToken($closeParenthesis);
$trailingCommaIndex = null;
if ($tokens[$fileCandidateRightIndex]->equals(',')) {
$trailingCommaIndex = $fileCandidateRightIndex;
$fileCandidateRightIndex = $tokens->getPrevMeaningfulToken($fileCandidateRightIndex);
}
$fileCandidateRight = $tokens[$fileCandidateRightIndex];
if (!$fileCandidateRight->isGivenKind(T_FILE)) {
continue;
}
$fileCandidateLeftIndex = $tokens->getNextMeaningfulToken($openParenthesis);
$fileCandidateLeft = $tokens[$fileCandidateLeftIndex];
if (!$fileCandidateLeft->isGivenKind(T_FILE)) {
continue;
}
$namespaceCandidateIndex = $tokens->getPrevMeaningfulToken($functionNameIndex);
$namespaceCandidate = $tokens[$namespaceCandidateIndex];
if ($namespaceCandidate->isGivenKind(T_NS_SEPARATOR)) {
$tokens->removeTrailingWhitespace($namespaceCandidateIndex);
$tokens->clearAt($namespaceCandidateIndex);
}
if (null !== $trailingCommaIndex) {
if (!$tokens[$tokens->getNextNonWhitespace($trailingCommaIndex)]->isComment()) {
$tokens->removeTrailingWhitespace($trailingCommaIndex);
}
$tokens->clearTokenAndMergeSurroundingWhitespace($trailingCommaIndex);
}
if (!$tokens[$tokens->getNextNonWhitespace($closeParenthesis)]->isComment()) {
$tokens->removeLeadingWhitespace($closeParenthesis);
}
$tokens->clearTokenAndMergeSurroundingWhitespace($closeParenthesis);
if (!$tokens[$tokens->getNextNonWhitespace($openParenthesis)]->isComment()) {
$tokens->removeLeadingWhitespace($openParenthesis);
}
$tokens->removeTrailingWhitespace($openParenthesis);
$tokens->clearTokenAndMergeSurroundingWhitespace($openParenthesis);
$tokens[$fileCandidateLeftIndex] = new Token([T_DIR, '__DIR__']);
$tokens->clearTokenAndMergeSurroundingWhitespace($functionNameIndex);
} while (null !== $currIndex);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\LanguageConstruct;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\DeprecatedFixerInterface;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\NamespacesAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class ClassKeywordRemoveFixer extends AbstractFixer implements DeprecatedFixerInterface
{
private array $imports = [];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Converts `::class` keywords to FQCN strings.',
[
new CodeSample(
'<?php
use Foo\Bar\Baz;
$className = Baz::class;
'
),
]
);
}
public function getSuccessorsNames(): array
{
return [];
}
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(CT::T_CLASS_CONSTANT);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$namespacesAnalyzer = new NamespacesAnalyzer();
$previousNamespaceScopeEndIndex = 0;
foreach ($namespacesAnalyzer->getDeclarations($tokens) as $declaration) {
$this->replaceClassKeywordsSection($tokens, '', $previousNamespaceScopeEndIndex, $declaration->getStartIndex());
$this->replaceClassKeywordsSection($tokens, $declaration->getFullName(), $declaration->getStartIndex(), $declaration->getScopeEndIndex());
$previousNamespaceScopeEndIndex = $declaration->getScopeEndIndex();
}
$this->replaceClassKeywordsSection($tokens, '', $previousNamespaceScopeEndIndex, $tokens->count() - 1);
}
private function storeImports(Tokens $tokens, int $startIndex, int $endIndex): void
{
$tokensAnalyzer = new TokensAnalyzer($tokens);
$this->imports = [];
foreach ($tokensAnalyzer->getImportUseIndexes() as $index) {
if ($index < $startIndex || $index > $endIndex) {
continue;
}
$import = '';
while ($index = $tokens->getNextMeaningfulToken($index)) {
if ($tokens[$index]->equalsAny([';', [CT::T_GROUP_IMPORT_BRACE_OPEN]]) || $tokens[$index]->isGivenKind(T_AS)) {
break;
}
$import .= $tokens[$index]->getContent();
}
if ($tokens[$index]->isGivenKind(CT::T_GROUP_IMPORT_BRACE_OPEN)) {
$groupEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_GROUP_IMPORT_BRACE, $index);
$groupImports = array_map(
static function (string $import): string {
return trim($import);
},
explode(',', $tokens->generatePartialCode($index + 1, $groupEndIndex - 1))
);
foreach ($groupImports as $groupImport) {
$groupImportParts = array_map(static function (string $import): string {
return trim($import);
}, explode(' as ', $groupImport));
if (2 === \count($groupImportParts)) {
$this->imports[$groupImportParts[1]] = $import.$groupImportParts[0];
} else {
$this->imports[] = $import.$groupImport;
}
}
} elseif ($tokens[$index]->isGivenKind(T_AS)) {
$aliasIndex = $tokens->getNextMeaningfulToken($index);
$alias = $tokens[$aliasIndex]->getContent();
$this->imports[$alias] = $import;
} else {
$this->imports[] = $import;
}
}
}
private function replaceClassKeywordsSection(Tokens $tokens, string $namespace, int $startIndex, int $endIndex): void
{
if ($endIndex - $startIndex < 3) {
return;
}
$this->storeImports($tokens, $startIndex, $endIndex);
$ctClassTokens = $tokens->findGivenKind(CT::T_CLASS_CONSTANT, $startIndex, $endIndex);
foreach (array_reverse(array_keys($ctClassTokens)) as $classIndex) {
$this->replaceClassKeyword($tokens, $namespace, $classIndex);
}
}
private function replaceClassKeyword(Tokens $tokens, string $namespacePrefix, int $classIndex): void
{
$classEndIndex = $tokens->getPrevMeaningfulToken($classIndex);
$classEndIndex = $tokens->getPrevMeaningfulToken($classEndIndex);
if (!$tokens[$classEndIndex]->isGivenKind(T_STRING)) {
return;
}
if ($tokens[$classEndIndex]->equalsAny([[T_STRING, 'self'], [T_STATIC, 'static'], [T_STRING, 'parent']], false)) {
return;
}
$classBeginIndex = $classEndIndex;
while (true) {
$prev = $tokens->getPrevMeaningfulToken($classBeginIndex);
if (!$tokens[$prev]->isGivenKind([T_NS_SEPARATOR, T_STRING])) {
break;
}
$classBeginIndex = $prev;
}
$classString = $tokens->generatePartialCode(
$tokens[$classBeginIndex]->isGivenKind(T_NS_SEPARATOR)
? $tokens->getNextMeaningfulToken($classBeginIndex)
: $classBeginIndex,
$classEndIndex
);
$classImport = false;
if ($tokens[$classBeginIndex]->isGivenKind(T_NS_SEPARATOR)) {
$namespacePrefix = '';
} else {
foreach ($this->imports as $alias => $import) {
if ($classString === $alias) {
$classImport = $import;
break;
}
$classStringArray = explode('\\', $classString);
$namespaceToTest = $classStringArray[0];
if (0 === strcmp($namespaceToTest, substr($import, -\strlen($namespaceToTest)))) {
$classImport = $import;
break;
}
}
}
for ($i = $classBeginIndex; $i <= $classIndex; ++$i) {
if (!$tokens[$i]->isComment() && !($tokens[$i]->isWhitespace() && str_contains($tokens[$i]->getContent(), "\n"))) {
$tokens->clearAt($i);
}
}
$tokens->insertAt($classBeginIndex, new Token([
T_CONSTANT_ENCAPSED_STRING,
"'".$this->makeClassFQN($namespacePrefix, $classImport, $classString)."'",
]));
}
private function makeClassFQN(string $namespacePrefix, $classImport, string $classString): string
{
if (false === $classImport) {
return ('' !== $namespacePrefix ? ($namespacePrefix.'\\') : '').$classString;
}
$classStringArray = explode('\\', $classString);
$classStringLength = \count($classStringArray);
$classImportArray = explode('\\', $classImport);
$classImportLength = \count($classImportArray);
if (1 === $classStringLength) {
return $classImport;
}
return implode('\\', array_merge(
\array_slice($classImportArray, 0, $classImportLength - $classStringLength + 1),
$classStringArray
));
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\LanguageConstruct;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NoUnsetOnPropertyFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Properties should be set to `null` instead of using `unset`.',
[new CodeSample("<?php\nunset(\$this->a);\n")],
null,
'Risky when relying on attributes to be removed using `unset` rather than be set to `null`.'.
' Changing variables to `null` instead of unsetting means these still show up when looping over class variables'.
' and reference properties remain unbroken.'.
' With PHP 7.4, this rule might introduce `null` assignments to properties whose type declaration does not allow it.'
);
}
public function isRisky(): bool
{
return true;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_UNSET)
&& $tokens->isAnyTokenKindsFound([T_OBJECT_OPERATOR, T_PAAMAYIM_NEKUDOTAYIM]);
}
public function getPriority(): int
{
return 25;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
if (!$tokens[$index]->isGivenKind(T_UNSET)) {
continue;
}
$unsetsInfo = $this->getUnsetsInfo($tokens, $index);
if (!$this->isAnyUnsetToTransform($unsetsInfo)) {
continue;
}
$isLastUnset = true;
foreach (array_reverse($unsetsInfo) as $unsetInfo) {
$this->updateTokens($tokens, $unsetInfo, $isLastUnset);
$isLastUnset = false;
}
}
}
private function getUnsetsInfo(Tokens $tokens, int $index): array
{
$argumentsAnalyzer = new ArgumentsAnalyzer();
$unsetStart = $tokens->getNextTokenOfKind($index, ['(']);
$unsetEnd = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $unsetStart);
$isFirst = true;
$unsets = [];
foreach ($argumentsAnalyzer->getArguments($tokens, $unsetStart, $unsetEnd) as $startIndex => $endIndex) {
$startIndex = $tokens->getNextMeaningfulToken($startIndex - 1);
$endIndex = $tokens->getPrevMeaningfulToken($endIndex + 1);
$unsets[] = [
'startIndex' => $startIndex,
'endIndex' => $endIndex,
'isToTransform' => $this->isProperty($tokens, $startIndex, $endIndex),
'isFirst' => $isFirst,
];
$isFirst = false;
}
return $unsets;
}
private function isProperty(Tokens $tokens, int $index, int $endIndex): bool
{
if ($tokens[$index]->isGivenKind(T_VARIABLE)) {
$nextIndex = $tokens->getNextMeaningfulToken($index);
if (null === $nextIndex || !$tokens[$nextIndex]->isGivenKind(T_OBJECT_OPERATOR)) {
return false;
}
$nextIndex = $tokens->getNextMeaningfulToken($nextIndex);
$nextNextIndex = $tokens->getNextMeaningfulToken($nextIndex);
if (null !== $nextNextIndex && $nextNextIndex < $endIndex) {
return false;
}
return null !== $nextIndex && $tokens[$nextIndex]->isGivenKind(T_STRING);
}
if ($tokens[$index]->isGivenKind([T_NS_SEPARATOR, T_STRING])) {
$nextIndex = $tokens->getTokenNotOfKindsSibling($index, 1, [T_DOUBLE_COLON, T_NS_SEPARATOR, T_STRING]);
$nextNextIndex = $tokens->getNextMeaningfulToken($nextIndex);
if (null !== $nextNextIndex && $nextNextIndex < $endIndex) {
return false;
}
return null !== $nextIndex && $tokens[$nextIndex]->isGivenKind(T_VARIABLE);
}
return false;
}
private function isAnyUnsetToTransform(array $unsetsInfo): bool
{
foreach ($unsetsInfo as $unsetInfo) {
if ($unsetInfo['isToTransform']) {
return true;
}
}
return false;
}
private function updateTokens(Tokens $tokens, array $unsetInfo, bool $isLastUnset): void
{
if ($unsetInfo['isFirst'] && $unsetInfo['isToTransform']) {
$braceIndex = $tokens->getPrevTokenOfKind($unsetInfo['startIndex'], ['(']);
$unsetIndex = $tokens->getPrevTokenOfKind($braceIndex, [[T_UNSET]]);
$tokens->clearTokenAndMergeSurroundingWhitespace($braceIndex);
$tokens->clearTokenAndMergeSurroundingWhitespace($unsetIndex);
}
if ($isLastUnset && $unsetInfo['isToTransform']) {
$braceIndex = $tokens->getNextTokenOfKind($unsetInfo['endIndex'], [')']);
$previousIndex = $tokens->getPrevMeaningfulToken($braceIndex);
if ($tokens[$previousIndex]->equals(',')) {
$tokens->clearTokenAndMergeSurroundingWhitespace($previousIndex);
}
$tokens->clearTokenAndMergeSurroundingWhitespace($braceIndex);
}
if (!$isLastUnset) {
$commaIndex = $tokens->getNextTokenOfKind($unsetInfo['endIndex'], [',']);
$tokens[$commaIndex] = new Token(';');
}
if (!$unsetInfo['isToTransform'] && !$isLastUnset) {
$tokens->insertAt($unsetInfo['endIndex'] + 1, new Token(')'));
}
if (!$unsetInfo['isToTransform'] && !$unsetInfo['isFirst']) {
$tokens->insertAt(
$unsetInfo['startIndex'],
[
new Token([T_UNSET, 'unset']),
new Token('('),
]
);
}
if ($unsetInfo['isToTransform']) {
$tokens->insertAt(
$unsetInfo['endIndex'] + 1,
[
new Token([T_WHITESPACE, ' ']),
new Token('='),
new Token([T_WHITESPACE, ' ']),
new Token([T_STRING, 'null']),
]
);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Alias;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class BacktickToShellExecFixer extends AbstractFixer
{
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound('`');
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Converts backtick operators to `shell_exec` calls.',
[
new CodeSample(
<<<'EOT'
<?php
$plain = `ls -lah`;
$withVar = `ls -lah $var1 ${var2} {$var3} {$var4[0]} {$var5->call()}`;
EOT
),
],
'Conversion is done only when it is non risky, so when special chars like single-quotes, double-quotes and backticks are not used inside the command.'
);
}
public function getPriority(): int
{
return 17;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$backtickStarted = false;
$backtickTokens = [];
for ($index = $tokens->count() - 1; $index > 0; --$index) {
$token = $tokens[$index];
if (!$token->equals('`')) {
if ($backtickStarted) {
$backtickTokens[$index] = $token;
}
continue;
}
$backtickTokens[$index] = $token;
if ($backtickStarted) {
$this->fixBackticks($tokens, $backtickTokens);
$backtickTokens = [];
}
$backtickStarted = !$backtickStarted;
}
}
private function fixBackticks(Tokens $tokens, array $backtickTokens): void
{
ksort($backtickTokens);
$openingBacktickIndex = key($backtickTokens);
end($backtickTokens);
$closingBacktickIndex = key($backtickTokens);
array_shift($backtickTokens);
array_pop($backtickTokens);
$count = \count($backtickTokens);
$newTokens = [
new Token([T_STRING, 'shell_exec']),
new Token('('),
];
if (1 !== $count) {
$newTokens[] = new Token('"');
}
foreach ($backtickTokens as $token) {
if (!$token->isGivenKind(T_ENCAPSED_AND_WHITESPACE)) {
$newTokens[] = $token;
continue;
}
$content = $token->getContent();
if (Preg::match('/[`"\']/u', $content)) {
return;
}
$kind = T_ENCAPSED_AND_WHITESPACE;
if (1 === $count) {
$content = '"'.$content.'"';
$kind = T_CONSTANT_ENCAPSED_STRING;
}
$newTokens[] = new Token([$kind, $content]);
}
if (1 !== $count) {
$newTokens[] = new Token('"');
}
$newTokens[] = new Token(')');
$tokens->overrideRange($openingBacktickIndex, $closingBacktickIndex, $newTokens);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Alias;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class ArrayPushFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Converts simple usages of `array_push($x, $y);` to `$x[] = $y;`.',
[new CodeSample("<?php\narray_push(\$x, \$y);\n")],
null,
'Risky when the function `array_push` is overridden.'
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_STRING) && $tokens->count() > 7;
}
public function isRisky(): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$functionsAnalyzer = new FunctionsAnalyzer();
for ($index = $tokens->count() - 7; $index > 0; --$index) {
if (!$tokens[$index]->equals([T_STRING, 'array_push'], false)) {
continue;
}
if (!$functionsAnalyzer->isGlobalFunctionCall($tokens, $index)) {
continue;
}
$callIndex = $index;
$index = $tokens->getPrevMeaningfulToken($index);
$namespaceSeparatorIndex = null;
if ($tokens[$index]->isGivenKind(T_NS_SEPARATOR)) {
$namespaceSeparatorIndex = $index;
$index = $tokens->getPrevMeaningfulToken($index);
}
if (!$tokens[$index]->equalsAny([';', '{', '}', ')', [T_OPEN_TAG]])) {
continue;
}
$openBraceIndex = $tokens->getNextMeaningfulToken($callIndex);
$blockType = Tokens::detectBlockType($tokens[$openBraceIndex]);
if (null === $blockType || Tokens::BLOCK_TYPE_PARENTHESIS_BRACE !== $blockType['type']) {
continue;
}
$closeBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openBraceIndex);
$afterCloseBraceIndex = $tokens->getNextMeaningfulToken($closeBraceIndex);
if (null !== $afterCloseBraceIndex && !$tokens[$afterCloseBraceIndex]->equalsAny([';', [T_CLOSE_TAG]])) {
continue;
}
$firstArgumentStop = $this->getFirstArgumentEnd($tokens, $openBraceIndex);
$firstArgumentStop = $tokens->getNextMeaningfulToken($firstArgumentStop);
if (!$tokens[$firstArgumentStop]->equals(',')) {
return;
}
$secondArgumentStart = $tokens->getNextMeaningfulToken($firstArgumentStop);
$secondArgumentStop = $this->getSecondArgumentEnd($tokens, $secondArgumentStart, $closeBraceIndex);
if (null === $secondArgumentStop) {
continue;
}
$tokens->clearTokenAndMergeSurroundingWhitespace($closeBraceIndex);
$tokens->clearTokenAndMergeSurroundingWhitespace($firstArgumentStop);
$tokens->insertAt(
$firstArgumentStop,
[
new Token('['),
new Token(']'),
new Token([T_WHITESPACE, ' ']),
new Token('='),
]
);
$tokens->clearTokenAndMergeSurroundingWhitespace($openBraceIndex);
$tokens->clearTokenAndMergeSurroundingWhitespace($callIndex);
if (null !== $namespaceSeparatorIndex) {
$tokens->clearTokenAndMergeSurroundingWhitespace($namespaceSeparatorIndex);
}
}
}
private function getFirstArgumentEnd(Tokens $tokens, int $index): int
{
$nextIndex = $tokens->getNextMeaningfulToken($index);
$nextToken = $tokens[$nextIndex];
while ($nextToken->equalsAny([
'$',
'[',
'(',
[CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN],
[CT::T_DYNAMIC_PROP_BRACE_OPEN],
[CT::T_DYNAMIC_VAR_BRACE_OPEN],
[CT::T_NAMESPACE_OPERATOR],
[T_NS_SEPARATOR],
[T_STATIC],
[T_STRING],
[T_VARIABLE],
])) {
$blockType = Tokens::detectBlockType($nextToken);
if (null !== $blockType) {
$nextIndex = $tokens->findBlockEnd($blockType['type'], $nextIndex);
}
$index = $nextIndex;
$nextIndex = $tokens->getNextMeaningfulToken($nextIndex);
$nextToken = $tokens[$nextIndex];
}
if ($nextToken->isGivenKind(T_OBJECT_OPERATOR)) {
return $this->getFirstArgumentEnd($tokens, $nextIndex);
}
if ($nextToken->isGivenKind(T_PAAMAYIM_NEKUDOTAYIM)) {
return $this->getFirstArgumentEnd($tokens, $tokens->getNextMeaningfulToken($nextIndex));
}
return $index;
}
private function getSecondArgumentEnd(Tokens $tokens, int $index, int $endIndex): ?int
{
if ($tokens[$index]->isGivenKind(T_ELLIPSIS)) {
return null;
}
for (; $index <= $endIndex; ++$index) {
$blockType = Tokens::detectBlockType($tokens[$index]);
while (null !== $blockType && $blockType['isStart']) {
$index = $tokens->findBlockEnd($blockType['type'], $index);
$index = $tokens->getNextMeaningfulToken($index);
$blockType = Tokens::detectBlockType($tokens[$index]);
}
if ($tokens[$index]->equals(',') || $tokens[$index]->isGivenKind([T_YIELD, T_YIELD_FROM, T_LOGICAL_AND, T_LOGICAL_OR, T_LOGICAL_XOR])) {
return null;
}
}
return $endIndex;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Alias;
use PhpCsFixer\AbstractFunctionReferenceFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class SetTypeToCastFixer extends AbstractFunctionReferenceFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Cast shall be used, not `settype`.',
[
new CodeSample(
'<?php
settype($foo, "integer");
settype($bar, "string");
settype($bar, "null");
'
),
],
null,
'Risky when the `settype` function is overridden or when used as the 2nd or 3rd expression in a `for` loop .'
);
}
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAllTokenKindsFound([T_CONSTANT_ENCAPSED_STRING, T_STRING, T_VARIABLE]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$map = [
'array' => [T_ARRAY_CAST, '(array)'],
'bool' => [T_BOOL_CAST, '(bool)'],
'boolean' => [T_BOOL_CAST, '(bool)'],
'double' => [T_DOUBLE_CAST, '(float)'],
'float' => [T_DOUBLE_CAST, '(float)'],
'int' => [T_INT_CAST, '(int)'],
'integer' => [T_INT_CAST, '(int)'],
'object' => [T_OBJECT_CAST, '(object)'],
'string' => [T_STRING_CAST, '(string)'],
];
$argumentsAnalyzer = new ArgumentsAnalyzer();
foreach (array_reverse($this->findSettypeCalls($tokens)) as $candidate) {
$functionNameIndex = $candidate[0];
$arguments = $argumentsAnalyzer->getArguments($tokens, $candidate[1], $candidate[2]);
if (2 !== \count($arguments)) {
continue;
}
$prev = $tokens->getPrevMeaningfulToken($functionNameIndex);
if (!$tokens[$prev]->equalsAny([';', '{', '}', [T_OPEN_TAG]])) {
continue;
}
reset($arguments);
$firstArgumentStart = key($arguments);
if ($tokens[$firstArgumentStart]->isComment() || $tokens[$firstArgumentStart]->isWhitespace()) {
$firstArgumentStart = $tokens->getNextMeaningfulToken($firstArgumentStart);
}
if (!$tokens[$firstArgumentStart]->isGivenKind(T_VARIABLE)) {
continue;
}
$commaIndex = $tokens->getNextMeaningfulToken($firstArgumentStart);
if (null === $commaIndex || !$tokens[$commaIndex]->equals(',')) {
continue;
}
next($arguments);
$secondArgumentStart = key($arguments);
$secondArgumentEnd = $arguments[$secondArgumentStart];
if ($tokens[$secondArgumentStart]->isComment() || $tokens[$secondArgumentStart]->isWhitespace()) {
$secondArgumentStart = $tokens->getNextMeaningfulToken($secondArgumentStart);
}
if (
!$tokens[$secondArgumentStart]->isGivenKind(T_CONSTANT_ENCAPSED_STRING)
|| $tokens->getNextMeaningfulToken($secondArgumentStart) < $secondArgumentEnd
) {
continue;
}
$type = strtolower(trim($tokens[$secondArgumentStart]->getContent(), '"\'"'));
if ('null' !== $type && !isset($map[$type])) {
continue;
}
$argumentToken = $tokens[$firstArgumentStart];
$this->removeSettypeCall(
$tokens,
$functionNameIndex,
$candidate[1],
$firstArgumentStart,
$commaIndex,
$secondArgumentStart,
$candidate[2]
);
if ('null' === $type) {
$this->fixSettypeNullCall($tokens, $functionNameIndex, $argumentToken);
} else {
$this->fixSettypeCall($tokens, $functionNameIndex, $argumentToken, new Token($map[$type]));
}
}
}
private function findSettypeCalls(Tokens $tokens): array
{
$candidates = [];
$end = \count($tokens);
for ($i = 1; $i < $end; ++$i) {
$candidate = $this->find('settype', $tokens, $i, $end);
if (null === $candidate) {
break;
}
$i = $candidate[1];
$candidates[] = $candidate;
}
return $candidates;
}
private function removeSettypeCall(
Tokens $tokens,
int $functionNameIndex,
int $openParenthesisIndex,
int $firstArgumentStart,
int $commaIndex,
int $secondArgumentStart,
int $closeParenthesisIndex
): void {
$tokens->clearTokenAndMergeSurroundingWhitespace($closeParenthesisIndex);
$prevIndex = $tokens->getPrevMeaningfulToken($closeParenthesisIndex);
if ($tokens[$prevIndex]->equals(',')) {
$tokens->clearTokenAndMergeSurroundingWhitespace($prevIndex);
}
$tokens->clearTokenAndMergeSurroundingWhitespace($secondArgumentStart);
$tokens->clearTokenAndMergeSurroundingWhitespace($commaIndex);
$tokens->clearTokenAndMergeSurroundingWhitespace($firstArgumentStart);
$tokens->clearTokenAndMergeSurroundingWhitespace($openParenthesisIndex);
$tokens->clearAt($functionNameIndex);
$tokens->clearEmptyTokens();
}
private function fixSettypeCall(
Tokens $tokens,
int $functionNameIndex,
Token $argumentToken,
Token $castToken
): void {
$tokens->insertAt(
$functionNameIndex,
[
clone $argumentToken,
new Token([T_WHITESPACE, ' ']),
new Token('='),
new Token([T_WHITESPACE, ' ']),
$castToken,
new Token([T_WHITESPACE, ' ']),
clone $argumentToken,
]
);
$tokens->removeTrailingWhitespace($functionNameIndex + 6);
}
private function fixSettypeNullCall(
Tokens $tokens,
int $functionNameIndex,
Token $argumentToken
): void {
$tokens->insertAt(
$functionNameIndex,
[
clone $argumentToken,
new Token([T_WHITESPACE, ' ']),
new Token('='),
new Token([T_WHITESPACE, ' ']),
new Token([T_STRING, 'null']),
]
);
$tokens->removeTrailingWhitespace($functionNameIndex + 4);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Alias;
use PhpCsFixer\AbstractFunctionReferenceFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
final class RandomApiMigrationFixer extends AbstractFunctionReferenceFixer implements ConfigurableFixerInterface
{
private static array $argumentCounts = [
'getrandmax' => [0],
'mt_rand' => [1, 2],
'rand' => [0, 2],
'srand' => [0, 1],
'random_int' => [0, 2],
];
public function configure(array $configuration): void
{
parent::configure($configuration);
foreach ($this->configuration['replacements'] as $functionName => $replacement) {
$this->configuration['replacements'][$functionName] = [
'alternativeName' => $replacement,
'argumentCount' => self::$argumentCounts[$functionName],
];
}
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Replaces `rand`, `srand`, `getrandmax` functions calls with their `mt_*` analogs or `random_int`.',
[
new CodeSample("<?php\n\$a = getrandmax();\n\$a = rand(\$b, \$c);\n\$a = srand();\n"),
new CodeSample(
"<?php\n\$a = getrandmax();\n\$a = rand(\$b, \$c);\n\$a = srand();\n",
['replacements' => ['getrandmax' => 'mt_getrandmax']]
),
new CodeSample(
"<?php \$a = rand(\$b, \$c);\n",
['replacements' => ['rand' => 'random_int']]
),
],
null,
'Risky when the configured functions are overridden. Or when relying on the seed based generating of the numbers.'
);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$argumentsAnalyzer = new ArgumentsAnalyzer();
foreach ($this->configuration['replacements'] as $functionIdentity => $functionReplacement) {
if ($functionIdentity === $functionReplacement['alternativeName']) {
continue;
}
$currIndex = 0;
do {
$boundaries = $this->find($functionIdentity, $tokens, $currIndex, $tokens->count() - 1);
if (null === $boundaries) {
continue 2;
}
[$functionName, $openParenthesis, $closeParenthesis] = $boundaries;
$count = $argumentsAnalyzer->countArguments($tokens, $openParenthesis, $closeParenthesis);
if (!\in_array($count, $functionReplacement['argumentCount'], true)) {
continue 2;
}
$currIndex = $openParenthesis;
$tokens[$functionName] = new Token([T_STRING, $functionReplacement['alternativeName']]);
if (0 === $count && 'random_int' === $functionReplacement['alternativeName']) {
$tokens->insertAt($currIndex + 1, [
new Token([T_LNUMBER, '0']),
new Token(','),
new Token([T_WHITESPACE, ' ']),
new Token([T_STRING, 'getrandmax']),
new Token('('),
new Token(')'),
]);
$currIndex += 6;
}
} while (null !== $currIndex);
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('replacements', 'Mapping between replaced functions with the new ones.'))
->setAllowedTypes(['array'])
->setAllowedValues([static function (array $value): bool {
foreach ($value as $functionName => $replacement) {
if (!\array_key_exists($functionName, self::$argumentCounts)) {
throw new InvalidOptionsException(sprintf(
'Function "%s" is not handled by the fixer.',
$functionName
));
}
if (!\is_string($replacement)) {
throw new InvalidOptionsException(sprintf(
'Replacement for function "%s" must be a string, "%s" given.',
$functionName,
get_debug_type($replacement)
));
}
}
return true;
}])
->setDefault([
'getrandmax' => 'mt_getrandmax',
'rand' => 'mt_rand',
'srand' => 'mt_srand',
])
->getOption(),
]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Alias;
use PhpCsFixer\AbstractFunctionReferenceFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class MbStrFunctionsFixer extends AbstractFunctionReferenceFixer
{
private static array $functionsMap = [
'str_split' => ['alternativeName' => 'mb_str_split', 'argumentCount' => [1, 2, 3]],
'stripos' => ['alternativeName' => 'mb_stripos', 'argumentCount' => [2, 3]],
'stristr' => ['alternativeName' => 'mb_stristr', 'argumentCount' => [2, 3]],
'strlen' => ['alternativeName' => 'mb_strlen', 'argumentCount' => [1]],
'strpos' => ['alternativeName' => 'mb_strpos', 'argumentCount' => [2, 3]],
'strrchr' => ['alternativeName' => 'mb_strrchr', 'argumentCount' => [2]],
'strripos' => ['alternativeName' => 'mb_strripos', 'argumentCount' => [2, 3]],
'strrpos' => ['alternativeName' => 'mb_strrpos', 'argumentCount' => [2, 3]],
'strstr' => ['alternativeName' => 'mb_strstr', 'argumentCount' => [2, 3]],
'strtolower' => ['alternativeName' => 'mb_strtolower', 'argumentCount' => [1]],
'strtoupper' => ['alternativeName' => 'mb_strtoupper', 'argumentCount' => [1]],
'substr' => ['alternativeName' => 'mb_substr', 'argumentCount' => [2, 3]],
'substr_count' => ['alternativeName' => 'mb_substr_count', 'argumentCount' => [2, 3, 4]],
];
private array $functions;
public function __construct()
{
parent::__construct();
$this->functions = array_filter(
self::$functionsMap,
static function (array $mapping): bool {
return (new \ReflectionFunction($mapping['alternativeName']))->isInternal();
}
);
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Replace non multibyte-safe functions with corresponding mb function.',
[
new CodeSample(
'<?php
$a = strlen($a);
$a = strpos($a, $b);
$a = strrpos($a, $b);
$a = substr($a, $b);
$a = strtolower($a);
$a = strtoupper($a);
$a = stripos($a, $b);
$a = strripos($a, $b);
$a = strstr($a, $b);
$a = stristr($a, $b);
$a = strrchr($a, $b);
$a = substr_count($a, $b);
'
),
],
null,
'Risky when any of the functions are overridden, or when relying on the string byte size rather than its length in characters.'
);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$argumentsAnalyzer = new ArgumentsAnalyzer();
foreach ($this->functions as $functionIdentity => $functionReplacement) {
$currIndex = 0;
do {
$boundaries = $this->find($functionIdentity, $tokens, $currIndex, $tokens->count() - 1);
if (null === $boundaries) {
continue 2;
}
[$functionName, $openParenthesis, $closeParenthesis] = $boundaries;
$count = $argumentsAnalyzer->countArguments($tokens, $openParenthesis, $closeParenthesis);
if (!\in_array($count, $functionReplacement['argumentCount'], true)) {
continue 2;
}
$currIndex = $openParenthesis;
$tokens[$functionName] = new Token([T_STRING, $functionReplacement['alternativeName']]);
} while (null !== $currIndex);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Alias;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NoMixedEchoPrintFixer extends AbstractFixer implements ConfigurableFixerInterface
{
private $callBack;
private $candidateTokenType;
public function configure(array $configuration): void
{
parent::configure($configuration);
if ('echo' === $this->configuration['use']) {
$this->candidateTokenType = T_PRINT;
$this->callBack = 'fixPrintToEcho';
} else {
$this->candidateTokenType = T_ECHO;
$this->callBack = 'fixEchoToPrint';
}
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Either language construct `print` or `echo` should be used.',
[
new CodeSample("<?php print 'example';\n"),
new CodeSample("<?php echo('example');\n", ['use' => 'print']),
]
);
}
public function getPriority(): int
{
return -10;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound($this->candidateTokenType);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$callBack = $this->callBack;
foreach ($tokens as $index => $token) {
if ($token->isGivenKind($this->candidateTokenType)) {
$this->{$callBack}($tokens, $index);
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('use', 'The desired language construct.'))
->setAllowedValues(['print', 'echo'])
->setDefault('echo')
->getOption(),
]);
}
private function fixEchoToPrint(Tokens $tokens, int $index): void
{
$nextTokenIndex = $tokens->getNextMeaningfulToken($index);
$endTokenIndex = $tokens->getNextTokenOfKind($index, [';', [T_CLOSE_TAG]]);
$canBeConverted = true;
for ($i = $nextTokenIndex; $i < $endTokenIndex; ++$i) {
if ($tokens[$i]->equalsAny(['(', [CT::T_ARRAY_SQUARE_BRACE_OPEN]])) {
$blockType = Tokens::detectBlockType($tokens[$i]);
$i = $tokens->findBlockEnd($blockType['type'], $i);
}
if ($tokens[$i]->equals(',')) {
$canBeConverted = false;
break;
}
}
if (false === $canBeConverted) {
return;
}
$tokens[$index] = new Token([T_PRINT, 'print']);
}
private function fixPrintToEcho(Tokens $tokens, int $index): void
{
$prevToken = $tokens[$tokens->getPrevMeaningfulToken($index)];
if (!$prevToken->equalsAny([';', '{', '}', ')', [T_OPEN_TAG], [T_ELSE]])) {
return;
}
$tokens[$index] = new Token([T_ECHO, 'echo']);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Alias;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NoAliasFunctionsFixer extends AbstractFixer implements ConfigurableFixerInterface
{
private const SETS = [
'@internal' => [
'diskfreespace' => 'disk_free_space',
'dns_check_record' => 'checkdnsrr',
'dns_get_mx' => 'getmxrr',
'session_commit' => 'session_write_close',
'stream_register_wrapper' => 'stream_wrapper_register',
'set_file_buffer' => 'stream_set_write_buffer',
'socket_set_blocking' => 'stream_set_blocking',
'socket_get_status' => 'stream_get_meta_data',
'socket_set_timeout' => 'stream_set_timeout',
'socket_getopt' => 'socket_get_option',
'socket_setopt' => 'socket_set_option',
'chop' => 'rtrim',
'close' => 'closedir',
'doubleval' => 'floatval',
'fputs' => 'fwrite',
'get_required_files' => 'get_included_files',
'ini_alter' => 'ini_set',
'is_double' => 'is_float',
'is_integer' => 'is_int',
'is_long' => 'is_int',
'is_real' => 'is_float',
'is_writeable' => 'is_writable',
'join' => 'implode',
'key_exists' => 'array_key_exists',
'magic_quotes_runtime' => 'set_magic_quotes_runtime',
'pos' => 'current',
'show_source' => 'highlight_file',
'sizeof' => 'count',
'strchr' => 'strstr',
'user_error' => 'trigger_error',
],
'@IMAP' => [
'imap_create' => 'imap_createmailbox',
'imap_fetchtext' => 'imap_body',
'imap_header' => 'imap_headerinfo',
'imap_listmailbox' => 'imap_list',
'imap_listsubscribed' => 'imap_lsub',
'imap_rename' => 'imap_renamemailbox',
'imap_scan' => 'imap_listscan',
'imap_scanmailbox' => 'imap_listscan',
],
'@ldap' => [
'ldap_close' => 'ldap_unbind',
'ldap_modify' => 'ldap_mod_replace',
],
'@mysqli' => [
'mysqli_execute' => 'mysqli_stmt_execute',
'mysqli_set_opt' => 'mysqli_options',
'mysqli_escape_string' => 'mysqli_real_escape_string',
],
'@pg' => [
'pg_exec' => 'pg_query',
],
'@oci' => [
'oci_free_cursor' => 'oci_free_statement',
],
'@odbc' => [
'odbc_do' => 'odbc_exec',
'odbc_field_precision' => 'odbc_field_len',
],
'@mbreg' => [
'mbereg' => 'mb_ereg',
'mbereg_match' => 'mb_ereg_match',
'mbereg_replace' => 'mb_ereg_replace',
'mbereg_search' => 'mb_ereg_search',
'mbereg_search_getpos' => 'mb_ereg_search_getpos',
'mbereg_search_getregs' => 'mb_ereg_search_getregs',
'mbereg_search_init' => 'mb_ereg_search_init',
'mbereg_search_pos' => 'mb_ereg_search_pos',
'mbereg_search_regs' => 'mb_ereg_search_regs',
'mbereg_search_setpos' => 'mb_ereg_search_setpos',
'mberegi' => 'mb_eregi',
'mberegi_replace' => 'mb_eregi_replace',
'mbregex_encoding' => 'mb_regex_encoding',
'mbsplit' => 'mb_split',
],
'@openssl' => [
'openssl_get_publickey' => 'openssl_pkey_get_public',
'openssl_get_privatekey' => 'openssl_pkey_get_private',
],
'@sodium' => [
'sodium_crypto_scalarmult_base' => 'sodium_crypto_box_publickey_from_secretkey',
],
'@exif' => [
'read_exif_data' => 'exif_read_data',
],
'@ftp' => [
'ftp_quit' => 'ftp_close',
],
'@posix' => [
'posix_errno' => 'posix_get_last_error',
],
'@pcntl' => [
'pcntl_errno' => 'pcntl_get_last_error',
],
'@time' => [
'mktime' => ['time', 0],
'gmmktime' => ['time', 0],
],
];
private array $aliases = [];
public function configure(array $configuration): void
{
parent::configure($configuration);
$this->aliases = [];
foreach ($this->configuration['sets'] as $set) {
if ('@all' === $set) {
$this->aliases = array_merge(...array_values(self::SETS));
break;
}
$this->aliases = array_merge($this->aliases, self::SETS[$set]);
}
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Master functions shall be used instead of aliases.',
[
new CodeSample(
'<?php
$a = chop($b);
close($b);
$a = doubleval($b);
$a = fputs($b, $c);
$a = get_required_files();
ini_alter($b, $c);
$a = is_double($b);
$a = is_integer($b);
$a = is_long($b);
$a = is_real($b);
$a = is_writeable($b);
$a = join($glue, $pieces);
$a = key_exists($key, $array);
magic_quotes_runtime($new_setting);
$a = pos($array);
$a = show_source($filename, true);
$a = sizeof($b);
$a = strchr($haystack, $needle);
$a = imap_header($imap_stream, 1);
user_error($message);
mbereg_search_getregs();
'
),
new CodeSample(
'<?php
$a = is_double($b);
mbereg_search_getregs();
',
['sets' => ['@mbreg']]
),
],
null,
'Risky when any of the alias functions are overridden.'
);
}
public function getPriority(): int
{
return 40;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_STRING);
}
public function isRisky(): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$functionsAnalyzer = new FunctionsAnalyzer();
$argumentsAnalyzer = new ArgumentsAnalyzer();
foreach ($tokens->findGivenKind(T_STRING) as $index => $token) {
$tokenContent = strtolower($token->getContent());
if (!isset($this->aliases[$tokenContent])) {
continue;
}
$openParenthesis = $tokens->getNextMeaningfulToken($index);
if (!$tokens[$openParenthesis]->equals('(')) {
continue;
}
if (!$functionsAnalyzer->isGlobalFunctionCall($tokens, $index)) {
continue;
}
if (\is_array($this->aliases[$tokenContent])) {
[$alias, $numberOfArguments] = $this->aliases[$tokenContent];
$count = $argumentsAnalyzer->countArguments($tokens, $openParenthesis, $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openParenthesis));
if ($numberOfArguments !== $count) {
continue;
}
} else {
$alias = $this->aliases[$tokenContent];
}
$tokens[$index] = new Token([T_STRING, $alias]);
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
$sets = [
'@all' => 'all listed sets',
'@internal' => 'native functions',
'@exif' => 'EXIF functions',
'@ftp' => 'FTP functions',
'@IMAP' => 'IMAP functions',
'@ldap' => 'LDAP functions',
'@mbreg' => 'from `ext-mbstring`',
'@mysqli' => 'mysqli functions',
'@oci' => 'oci functions',
'@odbc' => 'odbc functions',
'@openssl' => 'openssl functions',
'@pcntl' => 'PCNTL functions',
'@pg' => 'pg functions',
'@posix' => 'POSIX functions',
'@snmp' => 'SNMP functions',
'@sodium' => 'libsodium functions',
'@time' => 'time functions',
];
$list = "List of sets to fix. Defined sets are:\n\n";
foreach ($sets as $set => $description) {
$list .= sprintf("* `%s` (%s)\n", $set, $description);
}
return new FixerConfigurationResolver([
(new FixerOptionBuilder('sets', $list))
->setAllowedTypes(['array'])
->setAllowedValues([new AllowedValueSubset(array_keys($sets))])
->setDefault(['@internal', '@IMAP', '@pg'])
->getOption(),
]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Alias;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NoAliasLanguageConstructCallFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Master language constructs shall be used instead of aliases.',
[
new CodeSample(
'<?php
die;
'
),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_EXIT);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_EXIT)) {
continue;
}
if ('exit' === strtolower($token->getContent())) {
continue;
}
$tokens[$index] = new Token([T_EXIT, 'exit']);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Alias;
use PhpCsFixer\AbstractFunctionReferenceFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PowToExponentiationFixer extends AbstractFunctionReferenceFixer
{
public function isCandidate(Tokens $tokens): bool
{
return $tokens->count() > 7 && $tokens->isTokenKindFound(T_STRING);
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Converts `pow` to the `**` operator.',
[
new CodeSample(
"<?php\n pow(\$a, 1);\n"
),
],
null,
'Risky when the function `pow` is overridden.'
);
}
public function getPriority(): int
{
return 32;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$candidates = $this->findPowCalls($tokens);
$argumentsAnalyzer = new ArgumentsAnalyzer();
$numberOfTokensAdded = 0;
$previousCloseParenthesisIndex = \count($tokens);
foreach (array_reverse($candidates) as $candidate) {
if ($previousCloseParenthesisIndex < $candidate[2]) {
$previousCloseParenthesisIndex = $candidate[2];
$candidate[2] += $numberOfTokensAdded;
} else {
$previousCloseParenthesisIndex = $candidate[2];
$numberOfTokensAdded = 0;
}
$arguments = $argumentsAnalyzer->getArguments($tokens, $candidate[1], $candidate[2]);
if (2 !== \count($arguments)) {
continue;
}
for ($i = $candidate[1]; $i < $candidate[2]; ++$i) {
if ($tokens[$i]->isGivenKind(T_ELLIPSIS)) {
continue 2;
}
}
$numberOfTokensAdded += $this->fixPowToExponentiation(
$tokens,
$candidate[0],
$candidate[1],
$candidate[2],
$arguments
);
}
}
private function findPowCalls(Tokens $tokens): array
{
$candidates = [];
$end = \count($tokens) - 6;
for ($i = 1; $i < $end; ++$i) {
$candidate = $this->find('pow', $tokens, $i, $end);
if (null === $candidate) {
break;
}
$i = $candidate[1];
$candidates[] = $candidate;
}
return $candidates;
}
private function fixPowToExponentiation(Tokens $tokens, int $functionNameIndex, int $openParenthesisIndex, int $closeParenthesisIndex, array $arguments): int
{
$tokens[$tokens->getNextTokenOfKind(reset($arguments), [','])] = new Token([T_POW, '**']);
$tokens->clearAt($closeParenthesisIndex);
$previousIndex = $tokens->getPrevMeaningfulToken($closeParenthesisIndex);
if ($tokens[$previousIndex]->equals(',')) {
$tokens->clearAt($previousIndex);
}
$added = 0;
foreach (array_reverse($arguments, true) as $argumentStartIndex => $argumentEndIndex) {
if ($this->isParenthesisNeeded($tokens, $argumentStartIndex, $argumentEndIndex)) {
$tokens->insertAt($argumentEndIndex + 1, new Token(')'));
$tokens->insertAt($argumentStartIndex, new Token('('));
$added += 2;
}
}
$tokens->clearAt($openParenthesisIndex);
$tokens->clearAt($functionNameIndex);
$prevMeaningfulTokenIndex = $tokens->getPrevMeaningfulToken($functionNameIndex);
if ($tokens[$prevMeaningfulTokenIndex]->isGivenKind(T_NS_SEPARATOR)) {
$tokens->clearAt($prevMeaningfulTokenIndex);
}
return $added;
}
private function isParenthesisNeeded(Tokens $tokens, int $argumentStartIndex, int $argumentEndIndex): bool
{
static $allowedKinds = null;
if (null === $allowedKinds) {
$allowedKinds = $this->getAllowedKinds();
}
for ($i = $argumentStartIndex; $i <= $argumentEndIndex; ++$i) {
if ($tokens[$i]->isGivenKind($allowedKinds) || $tokens->isEmptyAt($i)) {
continue;
}
$blockType = Tokens::detectBlockType($tokens[$i]);
if (null !== $blockType) {
$i = $tokens->findBlockEnd($blockType['type'], $i);
continue;
}
if ($tokens[$i]->equals('$')) {
$i = $tokens->getNextMeaningfulToken($i);
if ($tokens[$i]->isGivenKind(CT::T_DYNAMIC_VAR_BRACE_OPEN)) {
$i = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_DYNAMIC_VAR_BRACE, $i);
continue;
}
}
if ($tokens[$i]->equals('+') && $tokens->getPrevMeaningfulToken($i) < $argumentStartIndex) {
continue;
}
return true;
}
return false;
}
private function getAllowedKinds(): array
{
return array_merge(
[
T_DNUMBER, T_LNUMBER, T_VARIABLE, T_STRING, T_CONSTANT_ENCAPSED_STRING, T_DOUBLE_CAST,
T_INT_CAST, T_INC, T_DEC, T_NS_SEPARATOR, T_WHITESPACE, T_DOUBLE_COLON, T_LINE, T_COMMENT, T_DOC_COMMENT,
CT::T_NAMESPACE_OPERATOR,
],
Token::getObjectOperatorKinds()
);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Alias;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class ModernizeStrposFixer extends AbstractFixer
{
private const REPLACEMENTS = [
[
'operator' => [T_IS_IDENTICAL, '==='],
'operand' => [T_LNUMBER, '0'],
'replacement' => [T_STRING, 'str_starts_with'],
'negate' => false,
],
[
'operator' => [T_IS_NOT_IDENTICAL, '!=='],
'operand' => [T_LNUMBER, '0'],
'replacement' => [T_STRING, 'str_starts_with'],
'negate' => true,
],
[
'operator' => [T_IS_NOT_IDENTICAL, '!=='],
'operand' => [T_STRING, 'false'],
'replacement' => [T_STRING, 'str_contains'],
'negate' => false,
],
[
'operator' => [T_IS_IDENTICAL, '==='],
'operand' => [T_STRING, 'false'],
'replacement' => [T_STRING, 'str_contains'],
'negate' => true,
],
];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Replace `strpos()` calls with `str_starts_with()` or `str_contains()` if possible.',
[
new CodeSample(
'<?php
if (strpos($haystack, $needle) === 0) {}
if (strpos($haystack, $needle) !== 0) {}
if (strpos($haystack, $needle) !== false) {}
if (strpos($haystack, $needle) === false) {}
'
),
],
null,
'Risky if `strpos`, `str_starts_with` or `str_contains` functions are overridden.'
);
}
public function getPriority(): int
{
return 37;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_STRING) && $tokens->isAnyTokenKindsFound([T_IS_IDENTICAL, T_IS_NOT_IDENTICAL]);
}
public function isRisky(): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$functionsAnalyzer = new FunctionsAnalyzer();
$argumentsAnalyzer = new ArgumentsAnalyzer();
for ($index = \count($tokens) - 1; $index > 0; --$index) {
if (!$tokens[$index]->equals([T_STRING, 'strpos'], false) || !$functionsAnalyzer->isGlobalFunctionCall($tokens, $index)) {
continue;
}
$openIndex = $tokens->getNextMeaningfulToken($index);
$closeIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openIndex);
$arguments = $argumentsAnalyzer->getArguments($tokens, $openIndex, $closeIndex);
if (2 !== \count($arguments)) {
continue;
}
$compareTokens = $this->getCompareTokens($tokens, $index, -1);
if (null === $compareTokens) {
$compareTokens = $this->getCompareTokens($tokens, $closeIndex, 1);
}
if (null !== $compareTokens) {
$this->fixCall($tokens, $index, $compareTokens);
}
}
}
private function fixCall(Tokens $tokens, int $functionIndex, array $operatorIndices): void
{
foreach (self::REPLACEMENTS as $replacement) {
if (!$tokens[$operatorIndices['operator_index']]->equals($replacement['operator'])) {
continue;
}
if (!$tokens[$operatorIndices['operand_index']]->equals($replacement['operand'], false)) {
continue;
}
$tokens->clearTokenAndMergeSurroundingWhitespace($operatorIndices['operator_index']);
$tokens->clearTokenAndMergeSurroundingWhitespace($operatorIndices['operand_index']);
$tokens->clearTokenAndMergeSurroundingWhitespace($functionIndex);
if ($replacement['negate']) {
$negateInsertIndex = $functionIndex;
$prevFunctionIndex = $tokens->getPrevMeaningfulToken($functionIndex);
if ($tokens[$prevFunctionIndex]->isGivenKind(T_NS_SEPARATOR)) {
$negateInsertIndex = $prevFunctionIndex;
}
$tokens->insertAt($negateInsertIndex, new Token('!'));
++$functionIndex;
}
$tokens->insertAt($functionIndex, new Token($replacement['replacement']));
break;
}
}
private function getCompareTokens(Tokens $tokens, int $offsetIndex, int $direction): ?array
{
$operatorIndex = $tokens->getMeaningfulTokenSibling($offsetIndex, $direction);
if (null !== $operatorIndex && $tokens[$operatorIndex]->isGivenKind(T_NS_SEPARATOR)) {
$operatorIndex = $tokens->getMeaningfulTokenSibling($operatorIndex, $direction);
}
if (null === $operatorIndex || !$tokens[$operatorIndex]->isGivenKind([T_IS_IDENTICAL, T_IS_NOT_IDENTICAL])) {
return null;
}
$operandIndex = $tokens->getMeaningfulTokenSibling($operatorIndex, $direction);
if (null === $operandIndex) {
return null;
}
$operand = $tokens[$operandIndex];
if (!$operand->equals([T_LNUMBER, '0']) && !$operand->equals([T_STRING, 'false'], false)) {
return null;
}
$precedenceTokenIndex = $tokens->getMeaningfulTokenSibling($operandIndex, $direction);
if (null !== $precedenceTokenIndex && $this->isOfHigherPrecedence($tokens[$precedenceTokenIndex])) {
return null;
}
return ['operator_index' => $operatorIndex, 'operand_index' => $operandIndex];
}
private function isOfHigherPrecedence(Token $token): bool
{
static $operatorsKinds = [
T_DEC,
T_INC,
T_INSTANCEOF,
T_IS_GREATER_OR_EQUAL,
T_IS_SMALLER_OR_EQUAL,
T_POW,
T_SL,
T_SR,
];
static $operatorsPerContent = [
'!',
'%',
'*',
'+',
'-',
'.',
'/',
'<',
'>',
'~',
];
return $token->isGivenKind($operatorsKinds) || $token->equalsAny($operatorsPerContent);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Alias;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\PregException;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class EregToPregFixer extends AbstractFixer
{
private static array $functions = [
['ereg', 'preg_match', ''],
['eregi', 'preg_match', 'i'],
['ereg_replace', 'preg_replace', ''],
['eregi_replace', 'preg_replace', 'i'],
['split', 'preg_split', ''],
['spliti', 'preg_split', 'i'],
];
private static array $delimiters = ['/', '#', '!'];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Replace deprecated `ereg` regular expression functions with `preg`.',
[new CodeSample("<?php \$x = ereg('[A-Z]');\n")],
null,
'Risky if the `ereg` function is overridden.'
);
}
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_STRING);
}
public function isRisky(): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$end = $tokens->count() - 1;
$functionsAnalyzer = new FunctionsAnalyzer();
foreach (self::$functions as $map) {
$seq = [[T_STRING, $map[0]], '(', [T_CONSTANT_ENCAPSED_STRING]];
$currIndex = 0;
while (true) {
$match = $tokens->findSequence($seq, $currIndex, $end, false);
if (null === $match) {
break;
}
$match = array_keys($match);
$currIndex = $match[2];
if (!$functionsAnalyzer->isGlobalFunctionCall($tokens, $match[0])) {
continue;
}
$next = $tokens->getNextMeaningfulToken($match[2]);
if (null === $next || !$tokens[$next]->equalsAny([',', ')'])) {
continue;
}
$regexTokenContent = $tokens[$match[2]]->getContent();
if ('b' === $regexTokenContent[0] || 'B' === $regexTokenContent[0]) {
$quote = $regexTokenContent[1];
$prefix = $regexTokenContent[0];
$string = substr($regexTokenContent, 2, -1);
} else {
$quote = $regexTokenContent[0];
$prefix = '';
$string = substr($regexTokenContent, 1, -1);
}
$delim = $this->getBestDelimiter($string);
$preg = $delim.addcslashes($string, $delim).$delim.'D'.$map[2];
if (!$this->checkPreg($preg)) {
continue;
}
$tokens[$match[0]] = new Token([T_STRING, $map[1]]);
$tokens[$match[2]] = new Token([T_CONSTANT_ENCAPSED_STRING, $prefix.$quote.$preg.$quote]);
}
}
}
private function checkPreg(string $pattern): bool
{
try {
Preg::match($pattern, '');
return true;
} catch (PregException $e) {
return false;
}
}
private function getBestDelimiter(string $pattern): string
{
$delimiters = [];
foreach (self::$delimiters as $k => $d) {
if (!str_contains($pattern, $d)) {
return $d;
}
$delimiters[$d] = [substr_count($pattern, $d), $k];
}
uasort($delimiters, static function (array $a, array $b): int {
if ($a[0] === $b[0]) {
return $a[1] <=> $b[1];
}
return $a[0] <=> $b[0];
});
return key($delimiters);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Tokens;
trait Indentation
{
private function getLineIndentation(Tokens $tokens, int $index): string
{
$newlineTokenIndex = $this->getPreviousNewlineTokenIndex($tokens, $index);
if (null === $newlineTokenIndex) {
return '';
}
return $this->extractIndent($this->computeNewLineContent($tokens, $newlineTokenIndex));
}
private function extractIndent(string $content): string
{
if (Preg::match('/\R(\h*)[^\r\n]*$/D', $content, $matches)) {
return $matches[1];
}
return '';
}
private function getPreviousNewlineTokenIndex(Tokens $tokens, int $index): ?int
{
while ($index > 0) {
$index = $tokens->getPrevTokenOfKind($index, [[T_WHITESPACE], [T_INLINE_HTML]]);
if (null === $index) {
break;
}
if ($this->isNewLineToken($tokens, $index)) {
return $index;
}
}
return null;
}
private function computeNewLineContent(Tokens $tokens, int $index): string
{
$content = $tokens[$index]->getContent();
if (0 !== $index && $tokens[$index - 1]->equalsAny([[T_OPEN_TAG], [T_CLOSE_TAG]])) {
$content = Preg::replace('/\S/', '', $tokens[$index - 1]->getContent()).$content;
}
return $content;
}
private function isNewLineToken(Tokens $tokens, int $index): bool
{
$token = $tokens[$index];
if (
$token->isGivenKind(T_OPEN_TAG)
&& isset($tokens[$index + 1])
&& !$tokens[$index + 1]->isWhitespace()
&& Preg::match('/\R/', $token->getContent())
) {
return true;
}
if (!$tokens[$index]->isGivenKind([T_WHITESPACE, T_INLINE_HTML])) {
return false;
}
return (bool) Preg::match('/\R/', $this->computeNewLineContent($tokens, $index));
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\NamespaceNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class BlankLineAfterNamespaceFixer extends AbstractFixer implements WhitespacesAwareFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'There MUST be one blank line after the namespace declaration.',
[
new CodeSample("<?php\nnamespace Sample\\Sample;\n\n\n\$a;\n"),
new CodeSample("<?php\nnamespace Sample\\Sample;\nClass Test{}\n"),
]
);
}
public function getPriority(): int
{
return -20;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_NAMESPACE);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$lastIndex = $tokens->count() - 1;
for ($index = $lastIndex; $index >= 0; --$index) {
$token = $tokens[$index];
if (!$token->isGivenKind(T_NAMESPACE)) {
continue;
}
$semicolonIndex = $tokens->getNextTokenOfKind($index, [';', '{', [T_CLOSE_TAG]]);
$semicolonToken = $tokens[$semicolonIndex];
if (!$semicolonToken->equals(';')) {
continue;
}
$indexToEnsureBlankLineAfter = $this->getIndexToEnsureBlankLineAfter($tokens, $semicolonIndex);
$indexToEnsureBlankLine = $tokens->getNonEmptySibling($indexToEnsureBlankLineAfter, 1);
if (null !== $indexToEnsureBlankLine && $tokens[$indexToEnsureBlankLine]->isWhitespace()) {
$tokens[$indexToEnsureBlankLine] = $this->getTokenToInsert($tokens[$indexToEnsureBlankLine]->getContent(), $indexToEnsureBlankLine === $lastIndex);
} else {
$tokens->insertAt($indexToEnsureBlankLineAfter + 1, $this->getTokenToInsert('', $indexToEnsureBlankLineAfter === $lastIndex));
}
}
}
private function getIndexToEnsureBlankLineAfter(Tokens $tokens, int $index): int
{
$indexToEnsureBlankLine = $index;
$nextIndex = $tokens->getNonEmptySibling($indexToEnsureBlankLine, 1);
while (null !== $nextIndex) {
$token = $tokens[$nextIndex];
if ($token->isWhitespace()) {
if (1 === Preg::match('/\R/', $token->getContent())) {
break;
}
$nextNextIndex = $tokens->getNonEmptySibling($nextIndex, 1);
if (!$tokens[$nextNextIndex]->isComment()) {
break;
}
}
if (!$token->isWhitespace() && !$token->isComment()) {
break;
}
$indexToEnsureBlankLine = $nextIndex;
$nextIndex = $tokens->getNonEmptySibling($indexToEnsureBlankLine, 1);
}
return $indexToEnsureBlankLine;
}
private function getTokenToInsert(string $currentContent, bool $isLastIndex): Token
{
$ending = $this->whitespacesConfig->getLineEnding();
$emptyLines = $isLastIndex ? $ending : $ending.$ending;
$indent = 1 === Preg::match('/^.*\R( *)$/s', $currentContent, $matches) ? $matches[1] : '';
return new Token([T_WHITESPACE, $emptyLines.$indent]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\NamespaceNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NoLeadingNamespaceWhitespaceFixer extends AbstractFixer implements WhitespacesAwareFixerInterface
{
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_NAMESPACE);
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'The namespace declaration line shouldn\'t contain leading whitespace.',
[
new CodeSample(
'<?php
namespace Test8a;
namespace Test8b;
'
),
]
);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = \count($tokens) - 1; 0 <= $index; --$index) {
$token = $tokens[$index];
if (!$token->isGivenKind(T_NAMESPACE)) {
continue;
}
$beforeNamespaceIndex = $index - 1;
$beforeNamespace = $tokens[$beforeNamespaceIndex];
if (!$beforeNamespace->isWhitespace()) {
if (!self::endsWithWhitespace($beforeNamespace->getContent())) {
$tokens->insertAt($index, new Token([T_WHITESPACE, $this->whitespacesConfig->getLineEnding()]));
}
continue;
}
$lastNewline = strrpos($beforeNamespace->getContent(), "\n");
if (false === $lastNewline) {
$beforeBeforeNamespace = $tokens[$index - 2];
if (self::endsWithWhitespace($beforeBeforeNamespace->getContent())) {
$tokens->clearAt($beforeNamespaceIndex);
} else {
$tokens[$beforeNamespaceIndex] = new Token([T_WHITESPACE, ' ']);
}
} else {
$tokens[$beforeNamespaceIndex] = new Token([T_WHITESPACE, substr($beforeNamespace->getContent(), 0, $lastNewline + 1)]);
}
}
}
private static function endsWithWhitespace(string $str): bool
{
if ('' === $str) {
return false;
}
return '' === trim(substr($str, -1));
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\NamespaceNotation;
use PhpCsFixer\AbstractLinesBeforeNamespaceFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Tokens;
final class SingleBlankLineBeforeNamespaceFixer extends AbstractLinesBeforeNamespaceFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'There should be exactly one blank line before a namespace declaration.',
[
new CodeSample("<?php namespace A {}\n"),
new CodeSample("<?php\n\n\nnamespace A{}\n"),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_NAMESPACE);
}
public function getPriority(): int
{
return -21;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
$token = $tokens[$index];
if ($token->isGivenKind(T_NAMESPACE)) {
$this->fixLinesBeforeNamespace($tokens, $index, 2, 2);
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\NamespaceNotation;
use PhpCsFixer\AbstractLinesBeforeNamespaceFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Tokens;
final class NoBlankLinesBeforeNamespaceFixer extends AbstractLinesBeforeNamespaceFixer
{
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_NAMESPACE);
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'There should be no blank lines before a namespace declaration.',
[
new CodeSample(
"<?php\n\n\n\nnamespace Example;\n"
),
]
);
}
public function getPriority(): int
{
return 0;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = 0, $limit = $tokens->count(); $index < $limit; ++$index) {
$token = $tokens[$index];
if (!$token->isGivenKind(T_NAMESPACE)) {
continue;
}
$this->fixLinesBeforeNamespace($tokens, $index, 0, 1);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\NamespaceNotation;
use PhpCsFixer\AbstractLinesBeforeNamespaceFixer;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\FixerDefinition\VersionSpecification;
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
use PhpCsFixer\Tokenizer\Tokens;
final class CleanNamespaceFixer extends AbstractLinesBeforeNamespaceFixer
{
public function getDefinition(): FixerDefinitionInterface
{
$samples = [];
foreach (['namespace Foo \\ Bar;', 'echo foo /* comment */ \\ bar();'] as $sample) {
$samples[] = new VersionSpecificCodeSample(
"<?php\n".$sample."\n",
new VersionSpecification(null, 80000 - 1)
);
}
return new FixerDefinition(
'Namespace must not contain spacing, comments or PHPDoc.',
$samples
);
}
public function isCandidate(Tokens $tokens): bool
{
return \PHP_VERSION_ID < 80000 && $tokens->isTokenKindFound(T_NS_SEPARATOR);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$count = $tokens->count();
for ($index = 0; $index < $count; ++$index) {
if ($tokens[$index]->isGivenKind(T_NS_SEPARATOR)) {
$previousIndex = $tokens->getPrevMeaningfulToken($index);
$index = $this->fixNamespace(
$tokens,
$tokens[$previousIndex]->isGivenKind(T_STRING) ? $previousIndex : $index
);
}
}
}
private function fixNamespace(Tokens $tokens, int $index): int
{
$tillIndex = $index;
while ($tokens[$tillIndex]->isGivenKind([T_NS_SEPARATOR, T_STRING])) {
$tillIndex = $tokens->getNextMeaningfulToken($tillIndex);
}
$tillIndex = $tokens->getPrevMeaningfulToken($tillIndex);
$spaceIndices = [];
for (; $index <= $tillIndex; ++$index) {
if ($tokens[$index]->isGivenKind(T_WHITESPACE)) {
$spaceIndices[] = $index;
} elseif ($tokens[$index]->isComment()) {
$tokens->clearAt($index);
}
}
if ($tokens[$index - 1]->isWhitespace()) {
array_pop($spaceIndices);
}
foreach ($spaceIndices as $i) {
$tokens->clearAt($i);
}
return $index;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\CastNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NoUnsetCastFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Variables must be set `null` instead of using `(unset)` casting.',
[new CodeSample("<?php\n\$a = (unset) \$b;\n")]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_UNSET_CAST);
}
public function getPriority(): int
{
return 0;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = \count($tokens) - 1; $index > 0; --$index) {
if ($tokens[$index]->isGivenKind(T_UNSET_CAST)) {
$this->fixUnsetCast($tokens, $index);
}
}
}
private function fixUnsetCast(Tokens $tokens, int $index): void
{
$assignmentIndex = $tokens->getPrevMeaningfulToken($index);
if (null === $assignmentIndex || !$tokens[$assignmentIndex]->equals('=')) {
return;
}
$varIndex = $tokens->getNextMeaningfulToken($index);
if (null === $varIndex || !$tokens[$varIndex]->isGivenKind(T_VARIABLE)) {
return;
}
$afterVar = $tokens->getNextMeaningfulToken($varIndex);
if (null === $afterVar || !$tokens[$afterVar]->equalsAny([';', [T_CLOSE_TAG]])) {
return;
}
$nextIsWhiteSpace = $tokens[$assignmentIndex + 1]->isWhitespace();
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
$tokens->clearTokenAndMergeSurroundingWhitespace($varIndex);
++$assignmentIndex;
if (!$nextIsWhiteSpace) {
$tokens->insertAt($assignmentIndex, new Token([T_WHITESPACE, ' ']));
}
++$assignmentIndex;
$tokens->insertAt($assignmentIndex, new Token([T_STRING, 'null']));
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\CastNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class CastSpacesFixer extends AbstractFixer implements ConfigurableFixerInterface
{
private const INSIDE_CAST_SPACE_REPLACE_MAP = [
' ' => '',
"\t" => '',
"\n" => '',
"\r" => '',
"\0" => '',
"\x0B" => '',
];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'A single space or none should be between cast and variable.',
[
new CodeSample(
"<?php\n\$bar = ( string ) \$a;\n\$foo = (int)\$b;\n"
),
new CodeSample(
"<?php\n\$bar = ( string ) \$a;\n\$foo = (int)\$b;\n",
['space' => 'single']
),
new CodeSample(
"<?php\n\$bar = ( string ) \$a;\n\$foo = (int) \$b;\n",
['space' => 'none']
),
]
);
}
public function getPriority(): int
{
return -10;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound(Token::getCastTokenKinds());
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isCast()) {
continue;
}
$tokens[$index] = new Token([
$token->getId(),
strtr($token->getContent(), self::INSIDE_CAST_SPACE_REPLACE_MAP),
]);
if ('single' === $this->configuration['space']) {
if ($tokens[$index + 1]->isWhitespace(" \t")) {
$tokens[$index + 1] = new Token([T_WHITESPACE, ' ']);
} elseif (!$tokens[$index + 1]->isWhitespace()) {
$tokens->insertAt($index + 1, new Token([T_WHITESPACE, ' ']));
}
continue;
}
if ($tokens[$index + 1]->isWhitespace()) {
$tokens->clearAt($index + 1);
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('space', 'spacing to apply between cast and variable.'))
->setAllowedValues(['none', 'single'])
->setDefault('single')
->getOption(),
]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\CastNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NoShortBoolCastFixer extends AbstractFixer
{
public function getPriority(): int
{
return -9;
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Short cast `bool` using double exclamation mark should not be used.',
[new CodeSample("<?php\n\$a = !!\$b;\n")]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound('!');
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = \count($tokens) - 1; $index > 1; --$index) {
if ($tokens[$index]->equals('!')) {
$index = $this->fixShortCast($tokens, $index);
}
}
}
private function fixShortCast(Tokens $tokens, int $index): int
{
for ($i = $index - 1; $i > 1; --$i) {
if ($tokens[$i]->equals('!')) {
$this->fixShortCastToBoolCast($tokens, $i, $index);
break;
}
if (!$tokens[$i]->isComment() && !$tokens[$i]->isWhitespace()) {
break;
}
}
return $i;
}
private function fixShortCastToBoolCast(Tokens $tokens, int $start, int $end): void
{
for (; $start <= $end; ++$start) {
if (
!$tokens[$start]->isComment()
&& !($tokens[$start]->isWhitespace() && $tokens[$start - 1]->isComment())
) {
$tokens->clearAt($start);
}
}
$tokens->insertAt($start, new Token([T_BOOL_CAST, '(bool)']));
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\CastNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\FixerDefinition\VersionSpecification;
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class ShortScalarCastFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Cast `(boolean)` and `(integer)` should be written as `(bool)` and `(int)`, `(double)` and `(real)` as `(float)`, `(binary)` as `(string)`.',
[
new VersionSpecificCodeSample(
"<?php\n\$a = (boolean) \$b;\n\$a = (integer) \$b;\n\$a = (double) \$b;\n\$a = (real) \$b;\n\n\$a = (binary) \$b;\n",
new VersionSpecification(null, 70399)
),
new VersionSpecificCodeSample(
"<?php\n\$a = (boolean) \$b;\n\$a = (integer) \$b;\n\$a = (double) \$b;\n\n\$a = (binary) \$b;\n",
new VersionSpecification(70400)
),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound(Token::getCastTokenKinds());
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
static $castMap = [
'boolean' => 'bool',
'integer' => 'int',
'double' => 'float',
'real' => 'float',
'binary' => 'string',
];
for ($index = 0, $count = $tokens->count(); $index < $count; ++$index) {
if (!$tokens[$index]->isCast()) {
continue;
}
$castFrom = trim(substr($tokens[$index]->getContent(), 1, -1));
$castFromLowered = strtolower($castFrom);
if (!\array_key_exists($castFromLowered, $castMap)) {
continue;
}
$tokens[$index] = new Token([
$tokens[$index]->getId(),
str_replace($castFrom, $castMap[$castFromLowered], $tokens[$index]->getContent()),
]);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\CastNotation;
use PhpCsFixer\AbstractFunctionReferenceFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class ModernizeTypesCastingFixer extends AbstractFunctionReferenceFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Replaces `intval`, `floatval`, `doubleval`, `strval` and `boolval` function calls with according type casting operator.',
[
new CodeSample(
'<?php
$a = intval($b);
$a = floatval($b);
$a = doubleval($b);
$a = strval ($b);
$a = boolval($b);
'
),
],
null,
'Risky if any of the functions `intval`, `floatval`, `doubleval`, `strval` or `boolval` are overridden.'
);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
static $replacement = [
'intval' => [T_INT_CAST, '(int)'],
'floatval' => [T_DOUBLE_CAST, '(float)'],
'doubleval' => [T_DOUBLE_CAST, '(float)'],
'strval' => [T_STRING_CAST, '(string)'],
'boolval' => [T_BOOL_CAST, '(bool)'],
];
$argumentsAnalyzer = new ArgumentsAnalyzer();
foreach ($replacement as $functionIdentity => $newToken) {
$currIndex = 0;
do {
$boundaries = $this->find($functionIdentity, $tokens, $currIndex, $tokens->count() - 1);
if (null === $boundaries) {
continue 2;
}
[$functionName, $openParenthesis, $closeParenthesis] = $boundaries;
$currIndex = $openParenthesis;
if (1 !== $argumentsAnalyzer->countArguments($tokens, $openParenthesis, $closeParenthesis)) {
continue;
}
$paramContentEnd = $closeParenthesis;
$commaCandidate = $tokens->getPrevMeaningfulToken($paramContentEnd);
if ($tokens[$commaCandidate]->equals(',')) {
$tokens->removeTrailingWhitespace($commaCandidate);
$tokens->clearAt($commaCandidate);
$paramContentEnd = $commaCandidate;
}
$countParamTokens = 0;
for ($paramContentIndex = $openParenthesis + 1; $paramContentIndex < $paramContentEnd; ++$paramContentIndex) {
if (!$tokens[$paramContentIndex]->isGivenKind(T_WHITESPACE)) {
++$countParamTokens;
}
}
$preserveParentheses = $countParamTokens > 1;
$afterCloseParenthesisIndex = $tokens->getNextMeaningfulToken($closeParenthesis);
$afterCloseParenthesisToken = $tokens[$afterCloseParenthesisIndex];
$wrapInParentheses = $afterCloseParenthesisToken->equalsAny(['[', '{']) || $afterCloseParenthesisToken->isGivenKind(T_POW);
$prevTokenIndex = $tokens->getPrevMeaningfulToken($functionName);
if ($tokens[$prevTokenIndex]->isGivenKind(T_NS_SEPARATOR)) {
$tokens->removeTrailingWhitespace($prevTokenIndex);
$tokens->clearAt($prevTokenIndex);
}
$replacementSequence = [
new Token($newToken),
new Token([T_WHITESPACE, ' ']),
];
if ($wrapInParentheses) {
array_unshift($replacementSequence, new Token('('));
}
if (!$preserveParentheses) {
$tokens->removeLeadingWhitespace($closeParenthesis);
$tokens->clearAt($closeParenthesis);
$tokens->removeLeadingWhitespace($openParenthesis);
$tokens->removeTrailingWhitespace($openParenthesis);
$tokens->clearAt($openParenthesis);
} else {
$tokens->removeTrailingWhitespace($functionName);
}
if ($wrapInParentheses) {
$tokens->insertAt($closeParenthesis, new Token(')'));
}
$tokens->overrideRange($functionName, $functionName, $replacementSequence);
$currIndex = $functionName;
} while (null !== $currIndex);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\CastNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\FixerDefinition\VersionSpecification;
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class LowercaseCastFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Cast should be written in lower case.',
[
new VersionSpecificCodeSample(
'<?php
$a = (BOOLEAN) $b;
$a = (BOOL) $b;
$a = (INTEGER) $b;
$a = (INT) $b;
$a = (DOUBLE) $b;
$a = (FLoaT) $b;
$a = (reaL) $b;
$a = (flOAT) $b;
$a = (sTRING) $b;
$a = (ARRAy) $b;
$a = (OBJect) $b;
$a = (UNset) $b;
$a = (Binary) $b;
',
new VersionSpecification(null, 70399)
),
new VersionSpecificCodeSample(
'<?php
$a = (BOOLEAN) $b;
$a = (BOOL) $b;
$a = (INTEGER) $b;
$a = (INT) $b;
$a = (DOUBLE) $b;
$a = (FLoaT) $b;
$a = (flOAT) $b;
$a = (sTRING) $b;
$a = (ARRAy) $b;
$a = (OBJect) $b;
$a = (UNset) $b;
$a = (Binary) $b;
',
new VersionSpecification(70400)
),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound(Token::getCastTokenKinds());
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = 0, $count = $tokens->count(); $index < $count; ++$index) {
if (!$tokens[$index]->isCast()) {
continue;
}
$tokens[$index] = new Token([$tokens[$index]->getId(), strtolower($tokens[$index]->getContent())]);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Casing;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class ConstantCaseFixer extends AbstractFixer implements ConfigurableFixerInterface
{
private $fixFunction;
public function configure(array $configuration): void
{
parent::configure($configuration);
if ('lower' === $this->configuration['case']) {
$this->fixFunction = static function (string $content): string {
return strtolower($content);
};
}
if ('upper' === $this->configuration['case']) {
$this->fixFunction = static function (string $content): string {
return strtoupper($content);
};
}
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'The PHP constants `true`, `false`, and `null` MUST be written using the correct casing.',
[
new CodeSample("<?php\n\$a = FALSE;\n\$b = True;\n\$c = nuLL;\n"),
new CodeSample("<?php\n\$a = FALSE;\n\$b = True;\n\$c = nuLL;\n", ['case' => 'upper']),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_STRING);
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('case', 'Whether to use the `upper` or `lower` case syntax.'))
->setAllowedValues(['upper', 'lower'])
->setDefault('lower')
->getOption(),
]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$fixFunction = $this->fixFunction;
foreach ($tokens as $index => $token) {
if (!$token->isNativeConstant()) {
continue;
}
if (
$this->isNeighbourAccepted($tokens, $tokens->getPrevMeaningfulToken($index))
&& $this->isNeighbourAccepted($tokens, $tokens->getNextMeaningfulToken($index))
&& !$this->isEnumCaseName($tokens, $index)
) {
$tokens[$index] = new Token([$token->getId(), $fixFunction($token->getContent())]);
}
}
}
private function isNeighbourAccepted(Tokens $tokens, int $index): bool
{
static $forbiddenTokens = null;
if (null === $forbiddenTokens) {
$forbiddenTokens = array_merge(
[
T_AS,
T_CLASS,
T_CONST,
T_EXTENDS,
T_IMPLEMENTS,
T_INSTANCEOF,
T_INSTEADOF,
T_INTERFACE,
T_NEW,
T_NS_SEPARATOR,
T_PAAMAYIM_NEKUDOTAYIM,
T_TRAIT,
T_USE,
CT::T_USE_TRAIT,
CT::T_USE_LAMBDA,
],
Token::getObjectOperatorKinds()
);
}
$token = $tokens[$index];
if ($token->equalsAny(['{', '}'])) {
return false;
}
return !$token->isGivenKind($forbiddenTokens);
}
private function isEnumCaseName(Tokens $tokens, int $index): bool
{
if (!\defined('T_ENUM') || !$tokens->isTokenKindFound(T_ENUM)) {
return false;
}
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if (null === $prevIndex || !$tokens[$prevIndex]->isGivenKind(T_CASE)) {
return false;
}
if (!$tokens->isTokenKindFound(T_SWITCH)) {
return true;
}
$prevIndex = $tokens->getPrevTokenOfKind($prevIndex, [[T_ENUM], [T_SWITCH]]);
return null !== $prevIndex && $tokens[$prevIndex]->isGivenKind(T_ENUM);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Casing;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\NamespacesAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\NamespaceUsesAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class ClassReferenceNameCasingFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'When referencing an internal class it must be written using the correct casing.',
[
new CodeSample("<?php\nthrow new \\exception();\n"),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_STRING);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$namespacesAnalyzer = new NamespacesAnalyzer();
$namespaceUsesAnalyzer = new NamespaceUsesAnalyzer();
$classNames = $this->getClassNames();
foreach ($namespacesAnalyzer->getDeclarations($tokens) as $namespace) {
$uses = [];
foreach ($namespaceUsesAnalyzer->getDeclarationsInNamespace($tokens, $namespace) as $use) {
$uses[strtolower($use->getShortName())] = true;
}
foreach ($this->getClassReference($tokens, $namespace) as $reference) {
$currentContent = $tokens[$reference]->getContent();
$lowerCurrentContent = strtolower($currentContent);
if (isset($classNames[$lowerCurrentContent]) && $currentContent !== $classNames[$lowerCurrentContent] && !isset($uses[$lowerCurrentContent])) {
$tokens[$reference] = new Token([T_STRING, $classNames[$lowerCurrentContent]]);
}
}
}
}
private function getClassReference(Tokens $tokens, NamespaceAnalysis $namespace): \Generator
{
static $notBeforeKinds;
static $blockKinds;
if (null === $notBeforeKinds) {
$notBeforeKinds = [
CT::T_USE_TRAIT,
T_AS,
T_CASE,
T_CLASS,
T_CONST,
T_DOUBLE_ARROW,
T_DOUBLE_COLON,
T_FUNCTION,
T_INTERFACE,
T_OBJECT_OPERATOR,
T_TRAIT,
];
if (\defined('T_ENUM')) {
$notBeforeKinds[] = T_ENUM;
}
}
if (null === $blockKinds) {
$blockKinds = ['before' => [','], 'after' => [',']];
foreach (Tokens::getBlockEdgeDefinitions() as $definition) {
$blockKinds['before'][] = $definition['start'];
$blockKinds['after'][] = $definition['end'];
}
}
$namespaceIsGlobal = $namespace->isGlobalNamespace();
for ($index = $namespace->getScopeStartIndex(); $index < $namespace->getScopeEndIndex(); ++$index) {
if (!$tokens[$index]->isGivenKind(T_STRING)) {
continue;
}
$nextIndex = $tokens->getNextMeaningfulToken($index);
if ($tokens[$nextIndex]->isGivenKind(T_NS_SEPARATOR)) {
continue;
}
$prevIndex = $tokens->getPrevMeaningfulToken($index);
$nextIndex = $tokens->getNextMeaningfulToken($index);
$isNamespaceSeparator = $tokens[$prevIndex]->isGivenKind(T_NS_SEPARATOR);
if (!$isNamespaceSeparator && !$namespaceIsGlobal) {
continue;
}
if ($isNamespaceSeparator) {
$prevIndex = $tokens->getPrevMeaningfulToken($prevIndex);
if ($tokens[$prevIndex]->isGivenKind(T_STRING)) {
continue;
}
} elseif ($tokens[$prevIndex]->isGivenKind($notBeforeKinds)) {
continue;
}
if ($tokens[$prevIndex]->equalsAny($blockKinds['before']) && $tokens[$nextIndex]->equalsAny($blockKinds['after'])) {
continue;
}
if (!$tokens[$prevIndex]->isGivenKind(T_NEW) && $tokens[$nextIndex]->equalsAny(['(', ';', [T_CLOSE_TAG]])) {
continue;
}
yield $index;
}
}
private function getClassNames(): array
{
static $classes = null;
if (null === $classes) {
$classes = [];
foreach (get_declared_classes() as $class) {
if ((new \ReflectionClass($class))->isInternal()) {
$classes[strtolower($class)] = $class;
}
}
}
return $classes;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Casing;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class LowercaseStaticReferenceFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Class static references `self`, `static` and `parent` MUST be in lower case.',
[
new CodeSample('<?php
class Foo extends Bar
{
public function baz1()
{
return STATIC::baz2();
}
public function baz2($x)
{
return $x instanceof Self;
}
public function baz3(PaRent $x)
{
return true;
}
}
'),
new CodeSample(
'<?php
class Foo extends Bar
{
public function baz(?self $x) : SELF
{
return false;
}
}
'
),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_STATIC, T_STRING]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->equalsAny([[T_STRING, 'self'], [T_STATIC, 'static'], [T_STRING, 'parent']], false)) {
continue;
}
$newContent = strtolower($token->getContent());
if ($token->getContent() === $newContent) {
continue;
}
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$prevIndex]->isGivenKind([T_CONST, T_DOUBLE_COLON, T_FUNCTION, T_NAMESPACE, T_NS_SEPARATOR]) || $tokens[$prevIndex]->isObjectOperator()) {
continue;
}
$nextIndex = $tokens->getNextMeaningfulToken($index);
if ($tokens[$nextIndex]->isGivenKind([T_FUNCTION, T_NS_SEPARATOR, T_PRIVATE, T_PROTECTED, T_PUBLIC, T_STRING, CT::T_NULLABLE_TYPE])) {
continue;
}
if ('static' === $newContent && $tokens[$nextIndex]->isGivenKind(T_VARIABLE)) {
continue;
}
$tokens[$index] = new Token([$token->getId(), $newContent]);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Casing;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class IntegerLiteralCaseFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Integer literals must be in correct case.',
[
new CodeSample(
"<?php\n\$foo = 0Xff;\n\$bar = 0B11111111;\n"
),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_LNUMBER);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_LNUMBER)) {
continue;
}
$content = $token->getContent();
if (1 !== Preg::match('#^0[bxoBXO][0-9a-fA-F]+$#', $content)) {
continue;
}
$newContent = '0'.strtolower($content[1]).strtoupper(substr($content, 2));
if ($content === $newContent) {
continue;
}
$tokens[$index] = new Token([T_LNUMBER, $newContent]);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Casing;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class LowercaseKeywordsFixer extends AbstractFixer
{
private static array $excludedTokens = [T_HALT_COMPILER];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'PHP keywords MUST be in lower case.',
[
new CodeSample(
'<?php
FOREACH($a AS $B) {
TRY {
NEW $C($a, ISSET($B));
WHILE($B) {
INCLUDE "test.php";
}
} CATCH(\Exception $e) {
EXIT(1);
}
}
'
),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound(Token::getKeywords());
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if ($token->isKeyword() && !$token->isGivenKind(self::$excludedTokens)) {
$tokens[$index] = new Token([$token->getId(), strtolower($token->getContent())]);
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Casing;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\FixerDefinition\VersionSpecification;
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\TypeAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NativeFunctionTypeDeclarationCasingFixer extends AbstractFixer
{
private array $hints;
private FunctionsAnalyzer $functionsAnalyzer;
public function __construct()
{
parent::__construct();
$this->hints = [
'array' => true,
'bool' => true,
'callable' => true,
'float' => true,
'int' => true,
'iterable' => true,
'object' => true,
'self' => true,
'string' => true,
'void' => true,
];
if (\PHP_VERSION_ID >= 80000) {
$this->hints['false'] = true;
$this->hints['mixed'] = true;
$this->hints['null'] = true;
$this->hints['static'] = true;
}
if (\PHP_VERSION_ID >= 80100) {
$this->hints['never'] = true;
}
if (\PHP_VERSION_ID >= 80200) {
$this->hints['true'] = true;
}
$this->functionsAnalyzer = new FunctionsAnalyzer();
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Native type hints for functions should use the correct case.',
[
new CodeSample("<?php\nclass Bar {\n public function Foo(CALLABLE \$bar)\n {\n return 1;\n }\n}\n"),
new CodeSample(
"<?php\nfunction Foo(INT \$a): Bool\n{\n return true;\n}\n"
),
new CodeSample(
"<?php\nfunction Foo(Iterable \$a): VOID\n{\n echo 'Hello world';\n}\n"
),
new VersionSpecificCodeSample(
"<?php\nfunction Foo(Object \$a)\n{\n return 'hi!';\n}\n",
new VersionSpecification(70200)
),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAllTokenKindsFound([T_FUNCTION, T_STRING]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
if ($tokens[$index]->isGivenKind(T_FUNCTION)) {
$this->fixFunctionReturnType($tokens, $index);
$this->fixFunctionArgumentTypes($tokens, $index);
}
}
}
private function fixFunctionArgumentTypes(Tokens $tokens, int $index): void
{
foreach ($this->functionsAnalyzer->getFunctionArguments($tokens, $index) as $argument) {
$this->fixArgumentType($tokens, $argument->getTypeAnalysis());
}
}
private function fixFunctionReturnType(Tokens $tokens, int $index): void
{
$this->fixArgumentType($tokens, $this->functionsAnalyzer->getFunctionReturnType($tokens, $index));
}
private function fixArgumentType(Tokens $tokens, ?TypeAnalysis $type = null): void
{
if (null === $type) {
return;
}
for ($index = $type->getStartIndex(); $index <= $type->getEndIndex(); ++$index) {
if ($tokens[$tokens->getNextMeaningfulToken($index)]->isGivenKind(T_NS_SEPARATOR)) {
continue;
}
$lowerCasedName = strtolower($tokens[$index]->getContent());
if (!isset($this->hints[$lowerCasedName])) {
continue;
}
$tokens[$index] = new Token([$tokens[$index]->getId(), $lowerCasedName]);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Casing;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class MagicConstantCasingFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Magic constants should be referred to using the correct casing.',
[new CodeSample("<?php\necho __dir__;\n")]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound($this->getMagicConstantTokens());
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$magicConstants = $this->getMagicConstants();
$magicConstantTokens = $this->getMagicConstantTokens();
foreach ($tokens as $index => $token) {
if ($token->isGivenKind($magicConstantTokens)) {
$tokens[$index] = new Token([$token->getId(), $magicConstants[$token->getId()]]);
}
}
}
private function getMagicConstants(): array
{
static $magicConstants = null;
if (null === $magicConstants) {
$magicConstants = [
T_LINE => '__LINE__',
T_FILE => '__FILE__',
T_DIR => '__DIR__',
T_FUNC_C => '__FUNCTION__',
T_CLASS_C => '__CLASS__',
T_METHOD_C => '__METHOD__',
T_NS_C => '__NAMESPACE__',
CT::T_CLASS_CONSTANT => 'class',
T_TRAIT_C => '__TRAIT__',
];
}
return $magicConstants;
}
private function getMagicConstantTokens(): array
{
static $magicConstantTokens = null;
if (null === $magicConstantTokens) {
$magicConstantTokens = array_keys($this->getMagicConstants());
}
return $magicConstantTokens;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Casing;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class MagicMethodCasingFixer extends AbstractFixer
{
private static array $magicNames = [
'__call' => '__call',
'__callstatic' => '__callStatic',
'__clone' => '__clone',
'__construct' => '__construct',
'__debuginfo' => '__debugInfo',
'__destruct' => '__destruct',
'__get' => '__get',
'__invoke' => '__invoke',
'__isset' => '__isset',
'__serialize' => '__serialize',
'__set' => '__set',
'__set_state' => '__set_state',
'__sleep' => '__sleep',
'__tostring' => '__toString',
'__unserialize' => '__unserialize',
'__unset' => '__unset',
'__wakeup' => '__wakeup',
];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Magic method definitions and calls must be using the correct casing.',
[
new CodeSample(
'<?php
class Foo
{
public function __Sleep()
{
}
}
'
),
new CodeSample(
'<?php
$foo->__INVOKE(1);
'
),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_STRING) && $tokens->isAnyTokenKindsFound(array_merge([T_FUNCTION, T_DOUBLE_COLON], Token::getObjectOperatorKinds()));
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$inClass = 0;
$tokenCount = \count($tokens);
for ($index = 1; $index < $tokenCount - 2; ++$index) {
if (0 === $inClass && $tokens[$index]->isClassy()) {
$inClass = 1;
$index = $tokens->getNextTokenOfKind($index, ['{']);
continue;
}
if (0 !== $inClass) {
if ($tokens[$index]->equals('{')) {
++$inClass;
continue;
}
if ($tokens[$index]->equals('}')) {
--$inClass;
continue;
}
}
if (!$tokens[$index]->isGivenKind(T_STRING)) {
continue;
}
$content = $tokens[$index]->getContent();
if (!str_starts_with($content, '__')) {
continue;
}
$name = strtolower($content);
if (!$this->isMagicMethodName($name)) {
continue;
}
$nameInCorrectCasing = $this->getMagicMethodNameInCorrectCasing($name);
if ($nameInCorrectCasing === $content) {
continue;
}
if ($this->isFunctionSignature($tokens, $index)) {
if (0 !== $inClass) {
$this->setTokenToCorrectCasing($tokens, $index, $nameInCorrectCasing);
}
continue;
}
if ($this->isMethodCall($tokens, $index)) {
$this->setTokenToCorrectCasing($tokens, $index, $nameInCorrectCasing);
continue;
}
if (
('__callstatic' === $name || '__set_state' === $name)
&& $this->isStaticMethodCall($tokens, $index)
) {
$this->setTokenToCorrectCasing($tokens, $index, $nameInCorrectCasing);
}
}
}
private function isFunctionSignature(Tokens $tokens, int $index): bool
{
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if (!$tokens[$prevIndex]->isGivenKind(T_FUNCTION)) {
return false;
}
return $tokens[$tokens->getNextMeaningfulToken($index)]->equals('(');
}
private function isMethodCall(Tokens $tokens, int $index): bool
{
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if (!$tokens[$prevIndex]->isObjectOperator()) {
return false;
}
return $tokens[$tokens->getNextMeaningfulToken($index)]->equals('(');
}
private function isStaticMethodCall(Tokens $tokens, int $index): bool
{
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if (!$tokens[$prevIndex]->isGivenKind(T_DOUBLE_COLON)) {
return false;
}
return $tokens[$tokens->getNextMeaningfulToken($index)]->equals('(');
}
private function isMagicMethodName(string $name): bool
{
return isset(self::$magicNames[$name]);
}
private function getMagicMethodNameInCorrectCasing(string $name): string
{
return self::$magicNames[$name];
}
private function setTokenToCorrectCasing(Tokens $tokens, int $index, string $nameInCorrectCasing): void
{
$tokens[$index] = new Token([T_STRING, $nameInCorrectCasing]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Casing;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NativeFunctionCasingFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Function defined by PHP should be called using the correct casing.',
[new CodeSample("<?php\nSTRLEN(\$str);\n")]
);
}
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_STRING);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$functionsAnalyzer = new FunctionsAnalyzer();
static $nativeFunctionNames = null;
if (null === $nativeFunctionNames) {
$nativeFunctionNames = $this->getNativeFunctionNames();
}
for ($index = 0, $count = $tokens->count(); $index < $count; ++$index) {
if (!$functionsAnalyzer->isGlobalFunctionCall($tokens, $index)) {
continue;
}
$lower = strtolower($tokens[$index]->getContent());
if (!\array_key_exists($lower, $nativeFunctionNames)) {
continue;
}
$tokens[$index] = new Token([T_STRING, $nativeFunctionNames[$lower]]);
}
}
private function getNativeFunctionNames(): array
{
$allFunctions = get_defined_functions();
$functions = [];
foreach ($allFunctions['internal'] as $function) {
$functions[strtolower($function)] = $function;
}
return $functions;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Comment;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Tokens;
final class NoEmptyCommentFixer extends AbstractFixer
{
private const TYPE_HASH = 1;
private const TYPE_DOUBLE_SLASH = 2;
private const TYPE_SLASH_ASTERISK = 3;
public function getPriority(): int
{
return 2;
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'There should not be any empty comments.',
[new CodeSample("<?php\n//\n#\n/* */\n")]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_COMMENT);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = 1, $count = \count($tokens); $index < $count; ++$index) {
if (!$tokens[$index]->isGivenKind(T_COMMENT)) {
continue;
}
[$blockStart, $index, $isEmpty] = $this->getCommentBlock($tokens, $index);
if (false === $isEmpty) {
continue;
}
for ($i = $blockStart; $i <= $index; ++$i) {
$tokens->clearTokenAndMergeSurroundingWhitespace($i);
}
}
}
private function getCommentBlock(Tokens $tokens, int $index): array
{
$commentType = $this->getCommentType($tokens[$index]->getContent());
$empty = $this->isEmptyComment($tokens[$index]->getContent());
if (self::TYPE_SLASH_ASTERISK === $commentType) {
return [$index, $index, $empty];
}
$start = $index;
$count = \count($tokens);
++$index;
for (; $index < $count; ++$index) {
if ($tokens[$index]->isComment()) {
if ($commentType !== $this->getCommentType($tokens[$index]->getContent())) {
break;
}
if ($empty) {
$empty = $this->isEmptyComment($tokens[$index]->getContent());
}
continue;
}
if (!$tokens[$index]->isWhitespace() || $this->getLineBreakCount($tokens, $index, $index + 1) > 1) {
break;
}
}
return [$start, $index - 1, $empty];
}
private function getCommentType(string $content): int
{
if (str_starts_with($content, '#')) {
return self::TYPE_HASH;
}
if ('*' === $content[1]) {
return self::TYPE_SLASH_ASTERISK;
}
return self::TYPE_DOUBLE_SLASH;
}
private function getLineBreakCount(Tokens $tokens, int $whiteStart, int $whiteEnd): int
{
$lineCount = 0;
for ($i = $whiteStart; $i < $whiteEnd; ++$i) {
$lineCount += Preg::matchAll('/\R/u', $tokens[$i]->getContent(), $matches);
}
return $lineCount;
}
private function isEmptyComment(string $content): bool
{
static $mapper = [
self::TYPE_HASH => '|^#\s*$|',
self::TYPE_SLASH_ASTERISK => '|^/\*[\s\*]*\*+/$|',
self::TYPE_DOUBLE_SLASH => '|^//\s*$|',
];
$type = $this->getCommentType($content);
return 1 === Preg::match($mapper[$type], $content);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Comment;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NoTrailingWhitespaceInCommentFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'There MUST be no trailing spaces inside comment or PHPDoc.',
[new CodeSample('<?php
// This is '.'
// a comment. '.'
')]
);
}
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_COMMENT, T_DOC_COMMENT]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if ($token->isGivenKind(T_DOC_COMMENT)) {
$tokens[$index] = new Token([T_DOC_COMMENT, Preg::replace('/(*ANY)[\h]+$/m', '', $token->getContent())]);
continue;
}
if ($token->isGivenKind(T_COMMENT)) {
if (str_starts_with($token->getContent(), '/*')) {
$tokens[$index] = new Token([T_COMMENT, Preg::replace('/(*ANY)[\h]+$/m', '', $token->getContent())]);
} elseif (isset($tokens[$index + 1]) && $tokens[$index + 1]->isWhitespace()) {
$trimmedContent = ltrim($tokens[$index + 1]->getContent(), " \t");
$tokens->ensureWhitespaceAtIndex($index + 1, 0, $trimmedContent);
}
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Comment;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use Symfony\Component\OptionsResolver\Options;
final class HeaderCommentFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
{
public const HEADER_PHPDOC = 'PHPDoc';
public const HEADER_COMMENT = 'comment';
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Add, replace or remove header comment.',
[
new CodeSample(
'<?php
declare(strict_types=1);
namespace A\B;
echo 1;
',
[
'header' => 'Made with love.',
]
),
new CodeSample(
'<?php
declare(strict_types=1);
namespace A\B;
echo 1;
',
[
'header' => 'Made with love.',
'comment_type' => 'PHPDoc',
'location' => 'after_open',
'separate' => 'bottom',
]
),
new CodeSample(
'<?php
declare(strict_types=1);
namespace A\B;
echo 1;
',
[
'header' => 'Made with love.',
'comment_type' => 'comment',
'location' => 'after_declare_strict',
]
),
new CodeSample(
'<?php
declare(strict_types=1);
/*
* Comment is not wanted here.
*/
namespace A\B;
echo 1;
',
[
'header' => '',
]
),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isMonolithicPhp();
}
public function getPriority(): int
{
return -30;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$location = $this->configuration['location'];
$locationIndices = [];
foreach (['after_open', 'after_declare_strict'] as $possibleLocation) {
$locationIndex = $this->findHeaderCommentInsertionIndex($tokens, $possibleLocation);
if (!isset($locationIndices[$locationIndex]) || $possibleLocation === $location) {
$locationIndices[$locationIndex] = $possibleLocation;
}
}
foreach ($locationIndices as $possibleLocation) {
$headerNewIndex = $this->findHeaderCommentInsertionIndex($tokens, $possibleLocation);
$headerCurrentIndex = $this->findHeaderCommentCurrentIndex($tokens, $headerNewIndex - 1);
if (null === $headerCurrentIndex) {
if ('' === $this->configuration['header'] || $possibleLocation !== $location) {
continue;
}
$this->insertHeader($tokens, $headerNewIndex);
continue;
}
$sameComment = $this->getHeaderAsComment() === $tokens[$headerCurrentIndex]->getContent();
$expectedLocation = $possibleLocation === $location;
if (!$sameComment || !$expectedLocation) {
if ($expectedLocation ^ $sameComment) {
$this->removeHeader($tokens, $headerCurrentIndex);
}
if ('' === $this->configuration['header']) {
continue;
}
if ($possibleLocation === $location) {
$this->insertHeader($tokens, $headerNewIndex);
}
continue;
}
$this->fixWhiteSpaceAroundHeader($tokens, $headerCurrentIndex);
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
$fixerName = $this->getName();
return new FixerConfigurationResolver([
(new FixerOptionBuilder('header', 'Proper header content.'))
->setAllowedTypes(['string'])
->setNormalizer(static function (Options $options, string $value) use ($fixerName): string {
if ('' === trim($value)) {
return '';
}
if (str_contains($value, '*/')) {
throw new InvalidFixerConfigurationException($fixerName, 'Cannot use \'*/\' in header.');
}
return $value;
})
->getOption(),
(new FixerOptionBuilder('comment_type', 'Comment syntax type.'))
->setAllowedValues([self::HEADER_PHPDOC, self::HEADER_COMMENT])
->setDefault(self::HEADER_COMMENT)
->getOption(),
(new FixerOptionBuilder('location', 'The location of the inserted header.'))
->setAllowedValues(['after_open', 'after_declare_strict'])
->setDefault('after_declare_strict')
->getOption(),
(new FixerOptionBuilder('separate', 'Whether the header should be separated from the file content with a new line.'))
->setAllowedValues(['both', 'top', 'bottom', 'none'])
->setDefault('both')
->getOption(),
]);
}
private function getHeaderAsComment(): string
{
$lineEnding = $this->whitespacesConfig->getLineEnding();
$comment = (self::HEADER_COMMENT === $this->configuration['comment_type'] ? '/*' : '/**').$lineEnding;
$lines = explode("\n", str_replace("\r", '', $this->configuration['header']));
foreach ($lines as $line) {
$comment .= rtrim(' * '.$line).$lineEnding;
}
return $comment.' */';
}
private function findHeaderCommentCurrentIndex(Tokens $tokens, int $headerNewIndex): ?int
{
$index = $tokens->getNextNonWhitespace($headerNewIndex);
if (null === $index || !$tokens[$index]->isComment()) {
return null;
}
$next = $index + 1;
if (!isset($tokens[$next]) || \in_array($this->configuration['separate'], ['top', 'none'], true) || !$tokens[$index]->isGivenKind(T_DOC_COMMENT)) {
return $index;
}
if ($tokens[$next]->isWhitespace()) {
if (!Preg::match('/^\h*\R\h*$/D', $tokens[$next]->getContent())) {
return $index;
}
++$next;
}
if (!isset($tokens[$next]) || !$tokens[$next]->isClassy() && !$tokens[$next]->isGivenKind(T_FUNCTION)) {
return $index;
}
return $this->getHeaderAsComment() === $tokens[$index]->getContent() ? $index : null;
}
private function findHeaderCommentInsertionIndex(Tokens $tokens, string $location): int
{
$openTagIndex = $tokens[0]->isGivenKind(T_OPEN_TAG) ? 0 : $tokens->getNextTokenOfKind(0, [[T_OPEN_TAG]]);
if (null === $openTagIndex) {
return 1;
}
if ('after_open' === $location) {
return $openTagIndex + 1;
}
$index = $tokens->getNextMeaningfulToken($openTagIndex);
if (null === $index) {
return $openTagIndex + 1;
}
if (!$tokens[$index]->isGivenKind(T_DECLARE)) {
return $openTagIndex + 1;
}
$next = $tokens->getNextMeaningfulToken($index);
if (null === $next || !$tokens[$next]->equals('(')) {
return $openTagIndex + 1;
}
$next = $tokens->getNextMeaningfulToken($next);
if (null === $next || !$tokens[$next]->equals([T_STRING, 'strict_types'], false)) {
return $openTagIndex + 1;
}
$next = $tokens->getNextMeaningfulToken($next);
if (null === $next || !$tokens[$next]->equals('=')) {
return $openTagIndex + 1;
}
$next = $tokens->getNextMeaningfulToken($next);
if (null === $next || !$tokens[$next]->isGivenKind(T_LNUMBER)) {
return $openTagIndex + 1;
}
$next = $tokens->getNextMeaningfulToken($next);
if (null === $next || !$tokens[$next]->equals(')')) {
return $openTagIndex + 1;
}
$next = $tokens->getNextMeaningfulToken($next);
if (null === $next || !$tokens[$next]->equals(';')) {
return $openTagIndex + 1;
}
return $next + 1;
}
private function fixWhiteSpaceAroundHeader(Tokens $tokens, int $headerIndex): void
{
$lineEnding = $this->whitespacesConfig->getLineEnding();
if (
('both' === $this->configuration['separate'] || 'bottom' === $this->configuration['separate'])
&& null !== $tokens->getNextMeaningfulToken($headerIndex)
) {
$expectedLineCount = 2;
} else {
$expectedLineCount = 1;
}
if ($headerIndex === \count($tokens) - 1) {
$tokens->insertAt($headerIndex + 1, new Token([T_WHITESPACE, str_repeat($lineEnding, $expectedLineCount)]));
} else {
$lineBreakCount = $this->getLineBreakCount($tokens, $headerIndex, 1);
if ($lineBreakCount < $expectedLineCount) {
$missing = str_repeat($lineEnding, $expectedLineCount - $lineBreakCount);
if ($tokens[$headerIndex + 1]->isWhitespace()) {
$tokens[$headerIndex + 1] = new Token([T_WHITESPACE, $missing.$tokens[$headerIndex + 1]->getContent()]);
} else {
$tokens->insertAt($headerIndex + 1, new Token([T_WHITESPACE, $missing]));
}
} elseif ($lineBreakCount > $expectedLineCount && $tokens[$headerIndex + 1]->isWhitespace()) {
$newLinesToRemove = $lineBreakCount - $expectedLineCount;
$tokens[$headerIndex + 1] = new Token([
T_WHITESPACE,
Preg::replace("/^\\R{{$newLinesToRemove}}/", '', $tokens[$headerIndex + 1]->getContent()),
]);
}
}
$expectedLineCount = 'both' === $this->configuration['separate'] || 'top' === $this->configuration['separate'] ? 2 : 1;
$prev = $tokens->getPrevNonWhitespace($headerIndex);
$regex = '/\h$/';
if ($tokens[$prev]->isGivenKind(T_OPEN_TAG) && Preg::match($regex, $tokens[$prev]->getContent())) {
$tokens[$prev] = new Token([T_OPEN_TAG, Preg::replace($regex, $lineEnding, $tokens[$prev]->getContent())]);
}
$lineBreakCount = $this->getLineBreakCount($tokens, $headerIndex, -1);
if ($lineBreakCount < $expectedLineCount) {
$tokens->insertAt($headerIndex, new Token([T_WHITESPACE, str_repeat($lineEnding, $expectedLineCount - $lineBreakCount)]));
}
}
private function getLineBreakCount(Tokens $tokens, int $index, int $direction): int
{
$whitespace = '';
for ($index += $direction; isset($tokens[$index]); $index += $direction) {
$token = $tokens[$index];
if ($token->isWhitespace()) {
$whitespace .= $token->getContent();
continue;
}
if (-1 === $direction && $token->isGivenKind(T_OPEN_TAG)) {
$whitespace .= $token->getContent();
}
if ('' !== $token->getContent()) {
break;
}
}
return substr_count($whitespace, "\n");
}
private function removeHeader(Tokens $tokens, int $index): void
{
$prevIndex = $index - 1;
$prevToken = $tokens[$prevIndex];
$newlineRemoved = false;
if ($prevToken->isWhitespace()) {
$content = $prevToken->getContent();
if (Preg::match('/\R/', $content)) {
$newlineRemoved = true;
}
$content = Preg::replace('/\R?\h*$/', '', $content);
$tokens->ensureWhitespaceAtIndex($prevIndex, 0, $content);
}
$nextIndex = $index + 1;
$nextToken = $tokens[$nextIndex] ?? null;
if (!$newlineRemoved && null !== $nextToken && $nextToken->isWhitespace()) {
$content = Preg::replace('/^\R/', '', $nextToken->getContent());
$tokens->ensureWhitespaceAtIndex($nextIndex, 0, $content);
}
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
}
private function insertHeader(Tokens $tokens, int $index): void
{
$tokens->insertAt($index, new Token([self::HEADER_COMMENT === $this->configuration['comment_type'] ? T_COMMENT : T_DOC_COMMENT, $this->getHeaderAsComment()]));
$this->fixWhiteSpaceAroundHeader($tokens, $index);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Comment;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Analyzer\CommentsAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Utils;
final class CommentToPhpdocFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
{
private array $ignoredTags = [];
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_COMMENT);
}
public function isRisky(): bool
{
return true;
}
public function getPriority(): int
{
return 26;
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Comments with annotation should be docblock when used on structural elements.',
[
new CodeSample("<?php /* header */ \$x = true; /* @var bool \$isFoo */ \$isFoo = true;\n"),
new CodeSample("<?php\n// @todo do something later\n\$foo = 1;\n\n// @var int \$a\n\$a = foo();\n", ['ignored_tags' => ['todo']]),
],
null,
'Risky as new docblocks might mean more, e.g. a Doctrine entity might have a new column in database.'
);
}
public function configure(array $configuration): void
{
parent::configure($configuration);
$this->ignoredTags = array_map(
static function (string $tag): string {
return strtolower($tag);
},
$this->configuration['ignored_tags']
);
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('ignored_tags', 'List of ignored tags'))
->setAllowedTypes(['array'])
->setDefault([])
->getOption(),
]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$commentsAnalyzer = new CommentsAnalyzer();
for ($index = 0, $limit = \count($tokens); $index < $limit; ++$index) {
$token = $tokens[$index];
if (!$token->isGivenKind(T_COMMENT)) {
continue;
}
if ($commentsAnalyzer->isHeaderComment($tokens, $index)) {
continue;
}
if (!$commentsAnalyzer->isBeforeStructuralElement($tokens, $index)) {
continue;
}
$commentIndices = $commentsAnalyzer->getCommentBlockIndices($tokens, $index);
if ($this->isCommentCandidate($tokens, $commentIndices)) {
$this->fixComment($tokens, $commentIndices);
}
$index = max($commentIndices);
}
}
private function isCommentCandidate(Tokens $tokens, array $indices): bool
{
return array_reduce(
$indices,
function (bool $carry, int $index) use ($tokens): bool {
if ($carry) {
return true;
}
if (1 !== Preg::match('~(?:#|//|/\*+|\R(?:\s*\*)?)\s*\@([a-zA-Z0-9_\\\\-]+)(?=\s|\(|$)~', $tokens[$index]->getContent(), $matches)) {
return false;
}
return !\in_array(strtolower($matches[1]), $this->ignoredTags, true);
},
false
);
}
private function fixComment(Tokens $tokens, array $indices): void
{
if (1 === \count($indices)) {
$this->fixCommentSingleLine($tokens, reset($indices));
} else {
$this->fixCommentMultiLine($tokens, $indices);
}
}
private function fixCommentSingleLine(Tokens $tokens, int $index): void
{
$message = $this->getMessage($tokens[$index]->getContent());
if ('' !== trim(substr($message, 0, 1))) {
$message = ' '.$message;
}
if ('' !== trim(substr($message, -1))) {
$message .= ' ';
}
$tokens[$index] = new Token([T_DOC_COMMENT, '/**'.$message.'*/']);
}
private function fixCommentMultiLine(Tokens $tokens, array $indices): void
{
$startIndex = reset($indices);
$indent = Utils::calculateTrailingWhitespaceIndent($tokens[$startIndex - 1]);
$newContent = '/**'.$this->whitespacesConfig->getLineEnding();
$count = max($indices);
for ($index = $startIndex; $index <= $count; ++$index) {
if (!$tokens[$index]->isComment()) {
continue;
}
if (str_contains($tokens[$index]->getContent(), '*/')) {
return;
}
$message = $this->getMessage($tokens[$index]->getContent());
if ('' !== trim(substr($message, 0, 1))) {
$message = ' '.$message;
}
$newContent .= $indent.' *'.$message.$this->whitespacesConfig->getLineEnding();
}
for ($index = $startIndex; $index <= $count; ++$index) {
$tokens->clearAt($index);
}
$newContent .= $indent.' */';
$tokens->insertAt($startIndex, new Token([T_DOC_COMMENT, $newContent]));
}
private function getMessage(string $content): string
{
if (str_starts_with($content, '#')) {
return substr($content, 1);
}
if (str_starts_with($content, '//')) {
return substr($content, 2);
}
return rtrim(ltrim($content, '/*'), '*/');
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Comment;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class SingleLineCommentStyleFixer extends AbstractFixer implements ConfigurableFixerInterface
{
private $asteriskEnabled;
private $hashEnabled;
public function configure(array $configuration): void
{
parent::configure($configuration);
$this->asteriskEnabled = \in_array('asterisk', $this->configuration['comment_types'], true);
$this->hashEnabled = \in_array('hash', $this->configuration['comment_types'], true);
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Single-line comments and multi-line comments with only one line of actual content should use the `//` syntax.',
[
new CodeSample(
'<?php
/* asterisk comment */
$a = 1;
# hash comment
$b = 2;
/*
* multi-line
* comment
*/
$c = 3;
'
),
new CodeSample(
'<?php
/* first comment */
$a = 1;
/*
* second comment
*/
$b = 2;
/*
* third
* comment
*/
$c = 3;
',
['comment_types' => ['asterisk']]
),
new CodeSample(
"<?php # comment\n",
['comment_types' => ['hash']]
),
]
);
}
public function getPriority(): int
{
return -31;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_COMMENT);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_COMMENT)) {
continue;
}
$content = $token->getContent();
$commentContent = substr($content, 2, -2) ?: '';
if ($this->hashEnabled && str_starts_with($content, '#')) {
if (isset($content[1]) && '[' === $content[1]) {
continue;
}
$tokens[$index] = new Token([$token->getId(), '//'.substr($content, 1)]);
continue;
}
if (
!$this->asteriskEnabled
|| str_contains($commentContent, '?>')
|| !str_starts_with($content, '/*')
|| 1 === Preg::match('/[^\s\*].*\R.*[^\s\*]/s', $commentContent)
) {
continue;
}
$nextTokenIndex = $index + 1;
if (isset($tokens[$nextTokenIndex])) {
$nextToken = $tokens[$nextTokenIndex];
if (!$nextToken->isWhitespace() || 1 !== Preg::match('/\R/', $nextToken->getContent())) {
continue;
}
$tokens[$nextTokenIndex] = new Token([$nextToken->getId(), ltrim($nextToken->getContent(), " \t")]);
}
$content = '//';
if (1 === Preg::match('/[^\s\*]/', $commentContent)) {
$content = '// '.Preg::replace('/[\s\*]*([^\s\*](?:.+[^\s\*])?)[\s\*]*/', '\1', $commentContent);
}
$tokens[$index] = new Token([$token->getId(), $content]);
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('comment_types', 'List of comment types to fix'))
->setAllowedTypes(['array'])
->setAllowedValues([new AllowedValueSubset(['asterisk', 'hash'])])
->setDefault(['asterisk', 'hash'])
->getOption(),
]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Comment;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class SingleLineCommentSpacingFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Single-line comments must have proper spacing.',
[
new CodeSample(
'<?php
//comment 1
#comment 2
/*comment 3*/
'
),
]
);
}
public function getPriority(): int
{
return 1;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_COMMENT);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = \count($tokens) - 1; 0 <= $index; --$index) {
$token = $tokens[$index];
if (!$token->isGivenKind(T_COMMENT)) {
continue;
}
$content = $token->getContent();
$contentLength = \strlen($content);
if ('/' === $content[0]) {
if ($contentLength < 3) {
continue;
}
if ('*' === $content[1]) {
if ($contentLength < 5 || '*' === $content[2] || str_contains($content, "\n")) {
continue;
}
$newContent = rtrim(substr($content, 0, -2)).' '.substr($content, -2);
$newContent = $this->fixCommentLeadingSpace($newContent, '/*');
} else {
$newContent = $this->fixCommentLeadingSpace($content, '//');
}
} else {
if ($contentLength < 2 || '[' === $content[1]) {
continue;
}
$newContent = $this->fixCommentLeadingSpace($content, '#');
}
if ($newContent !== $content) {
$tokens[$index] = new Token([T_COMMENT, $newContent]);
}
}
}
private function fixCommentLeadingSpace(string $content, string $prefix): string
{
if (0 !== Preg::match(sprintf('@^%s\h+.*$@', preg_quote($prefix, '@')), $content)) {
return $content;
}
$position = \strlen($prefix);
return substr($content, 0, $position).' '.substr($content, $position);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Comment;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class MultilineCommentOpeningClosingFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'DocBlocks must start with two asterisks, multiline comments must start with a single asterisk, after the opening slash. Both must end with a single asterisk before the closing slash.',
[
new CodeSample(
<<<'EOT'
<?php
/******
* Multiline comment with arbitrary asterisks count
******/
/**\
* Multiline comment that seems a DocBlock
*/
/**
* DocBlock with arbitrary asterisk count at the end
**/
EOT
),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_COMMENT, T_DOC_COMMENT]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
$originalContent = $token->getContent();
if (
!$token->isGivenKind(T_DOC_COMMENT)
&& !($token->isGivenKind(T_COMMENT) && str_starts_with($originalContent, '/*'))
) {
continue;
}
$newContent = $originalContent;
if ($token->isGivenKind(T_COMMENT)) {
$newContent = Preg::replace('/^\\/\\*{2,}(?!\\/)/', '/*', $newContent);
}
$newContent = Preg::replace('/(?<!\\/)\\*{2,}\\/$/', '*/', $newContent);
if ($newContent !== $originalContent) {
$tokens[$index] = new Token([$token->getId(), $newContent]);
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\PhpTag;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class LinebreakAfterOpeningTagFixer extends AbstractFixer implements WhitespacesAwareFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Ensure there is no code on the same line as the PHP open tag.',
[new CodeSample("<?php \$a = 1;\n\$b = 3;\n")]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_OPEN_TAG);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
if (!$tokens[0]->isGivenKind(T_OPEN_TAG) || !$tokens->isMonolithicPhp()) {
return;
}
if (str_contains($tokens[0]->getContent(), "\n")) {
return;
}
$newlineFound = false;
foreach ($tokens as $token) {
if ($token->isWhitespace() && str_contains($token->getContent(), "\n")) {
$newlineFound = true;
break;
}
}
if (!$newlineFound) {
return;
}
$tokens[0] = new Token([T_OPEN_TAG, rtrim($tokens[0]->getContent()).$this->whitespacesConfig->getLineEnding()]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\PhpTag;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class BlankLineAfterOpeningTagFixer extends AbstractFixer implements WhitespacesAwareFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Ensure there is no code on the same line as the PHP open tag and it is followed by a blank line.',
[new CodeSample("<?php \$a = 1;\n\$b = 1;\n")]
);
}
public function getPriority(): int
{
return 1;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_OPEN_TAG);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$lineEnding = $this->whitespacesConfig->getLineEnding();
if (!$tokens[0]->isGivenKind(T_OPEN_TAG) || !$tokens->isMonolithicPhp()) {
return;
}
$newlineFound = false;
foreach ($tokens as $token) {
if ($token->isWhitespace() && str_contains($token->getContent(), "\n")) {
$newlineFound = true;
break;
}
}
if (!$newlineFound) {
return;
}
$token = $tokens[0];
if (!str_contains($token->getContent(), "\n")) {
$tokens[0] = new Token([$token->getId(), rtrim($token->getContent()).$lineEnding]);
}
if (!str_contains($tokens[1]->getContent(), "\n")) {
if ($tokens[1]->isWhitespace()) {
$tokens[1] = new Token([T_WHITESPACE, $lineEnding.$tokens[1]->getContent()]);
} else {
$tokens->insertAt(1, new Token([T_WHITESPACE, $lineEnding]));
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\PhpTag;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class FullOpeningTagFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'PHP code must use the long `<?php` tags or short-echo `<?=` tags and not other tag variations.',
[
new CodeSample(
'<?
echo "Hello!";
'
),
]
);
}
public function getPriority(): int
{
return 98;
}
public function isCandidate(Tokens $tokens): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$content = $tokens->generateCode();
$newContent = Preg::replace('/<\?(?:phP|pHp|pHP|Php|PhP|PHp|PHP)?(\s|$)/', '<?php$1', $content, -1, $count);
if (!$count) {
return;
}
$newTokens = Tokens::fromCode($newContent);
$tokensOldContentLength = 0;
foreach ($newTokens as $index => $token) {
if ($token->isGivenKind(T_OPEN_TAG)) {
$tokenContent = $token->getContent();
$possibleOpenContent = substr($content, $tokensOldContentLength, 5);
if (false === $possibleOpenContent || '<?php' !== strtolower($possibleOpenContent)) { /**
@phpstan-ignore-line */
$tokenContent = '<? ';
}
$tokensOldContentLength += \strlen($tokenContent);
continue;
}
if ($token->isGivenKind([T_COMMENT, T_DOC_COMMENT, T_CONSTANT_ENCAPSED_STRING, T_ENCAPSED_AND_WHITESPACE, T_STRING])) {
$tokenContent = '';
$tokenContentLength = 0;
$parts = explode('<?php', $token->getContent());
$iLast = \count($parts) - 1;
foreach ($parts as $i => $part) {
$tokenContent .= $part;
$tokenContentLength += \strlen($part);
if ($i !== $iLast) {
$originalTokenContent = substr($content, $tokensOldContentLength + $tokenContentLength, 5);
if ('<?php' === strtolower($originalTokenContent)) {
$tokenContent .= $originalTokenContent;
$tokenContentLength += 5;
} else {
$tokenContent .= '<?';
$tokenContentLength += 2;
}
}
}
$newTokens[$index] = new Token([$token->getId(), $tokenContent]);
$token = $newTokens[$index];
}
$tokensOldContentLength += \strlen($token->getContent());
}
$tokens->overrideRange(0, $tokens->count() - 1, $newTokens);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\PhpTag;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NoClosingTagFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'The closing `?>` tag MUST be omitted from files containing only PHP.',
[new CodeSample("<?php\nclass Sample\n{\n}\n?>\n")]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_CLOSE_TAG);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
if (\count($tokens) < 2 || !$tokens->isMonolithicPhp() || !$tokens->isTokenKindFound(T_CLOSE_TAG)) {
return;
}
$closeTags = $tokens->findGivenKind(T_CLOSE_TAG);
$index = key($closeTags);
if (isset($tokens[$index - 1]) && $tokens[$index - 1]->isWhitespace()) {
$tokens->clearAt($index - 1);
}
$tokens->clearAt($index);
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if (!$tokens[$prevIndex]->equalsAny([';', '}', [T_OPEN_TAG]])) {
$tokens->insertAt($prevIndex + 1, new Token(';'));
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\PhpTag;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class EchoTagSyntaxFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public const OPTION_FORMAT = 'format';
public const OPTION_SHORTEN_SIMPLE_STATEMENTS_ONLY = 'shorten_simple_statements_only';
public const OPTION_LONG_FUNCTION = 'long_function';
public const FORMAT_SHORT = 'short';
public const FORMAT_LONG = 'long';
public const LONG_FUNCTION_ECHO = 'echo';
public const LONG_FUNCTION_PRINT = 'print';
private const SUPPORTED_FORMAT_OPTIONS = [
self::FORMAT_LONG,
self::FORMAT_SHORT,
];
private const SUPPORTED_LONGFUNCTION_OPTIONS = [
self::LONG_FUNCTION_ECHO,
self::LONG_FUNCTION_PRINT,
];
public function getDefinition(): FixerDefinitionInterface
{
$sample = <<<'EOT'
<?=1?>
<?php print '2' . '3'; ?>
<?php /* comment */ echo '2' . '3'; ?>
<?php print '2' . '3'; someFunction(); ?>
EOT
;
return new FixerDefinition(
'Replaces short-echo `<?=` with long format `<?php echo`/`<?php print` syntax, or vice-versa.',
[
new CodeSample($sample),
new CodeSample($sample, [self::OPTION_FORMAT => self::FORMAT_LONG]),
new CodeSample($sample, [self::OPTION_FORMAT => self::FORMAT_LONG, self::OPTION_LONG_FUNCTION => self::LONG_FUNCTION_PRINT]),
new CodeSample($sample, [self::OPTION_FORMAT => self::FORMAT_SHORT]),
new CodeSample($sample, [self::OPTION_FORMAT => self::FORMAT_SHORT, self::OPTION_SHORTEN_SIMPLE_STATEMENTS_ONLY => false]),
],
null
);
}
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
if (self::FORMAT_SHORT === $this->configuration[self::OPTION_FORMAT]) {
return $tokens->isAnyTokenKindsFound([T_ECHO, T_PRINT]);
}
return $tokens->isTokenKindFound(T_OPEN_TAG_WITH_ECHO);
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder(self::OPTION_FORMAT, 'The desired language construct.'))
->setAllowedValues(self::SUPPORTED_FORMAT_OPTIONS)
->setDefault(self::FORMAT_LONG)
->getOption(),
(new FixerOptionBuilder(self::OPTION_LONG_FUNCTION, 'The function to be used to expand the short echo tags'))
->setAllowedValues(self::SUPPORTED_LONGFUNCTION_OPTIONS)
->setDefault(self::LONG_FUNCTION_ECHO)
->getOption(),
(new FixerOptionBuilder(self::OPTION_SHORTEN_SIMPLE_STATEMENTS_ONLY, 'Render short-echo tags only in case of simple code'))
->setAllowedTypes(['bool'])
->setDefault(true)
->getOption(),
]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
if (self::FORMAT_SHORT === $this->configuration[self::OPTION_FORMAT]) {
$this->longToShort($tokens);
} else {
$this->shortToLong($tokens);
}
}
private function longToShort(Tokens $tokens): void
{
$count = $tokens->count();
for ($index = 0; $index < $count; ++$index) {
if (!$tokens[$index]->isGivenKind(T_OPEN_TAG)) {
continue;
}
$nextMeaningful = $tokens->getNextMeaningfulToken($index);
if (null === $nextMeaningful) {
return;
}
if (!$tokens[$nextMeaningful]->isGivenKind([T_ECHO, T_PRINT])) {
$index = $nextMeaningful;
continue;
}
if (true === $this->configuration[self::OPTION_SHORTEN_SIMPLE_STATEMENTS_ONLY] && $this->isComplexCode($tokens, $nextMeaningful + 1)) {
$index = $nextMeaningful;
continue;
}
$newTokens = $this->buildLongToShortTokens($tokens, $index, $nextMeaningful);
$tokens->overrideRange($index, $nextMeaningful, $newTokens);
$count = $tokens->count();
}
}
private function shortToLong(Tokens $tokens): void
{
if (self::LONG_FUNCTION_PRINT === $this->configuration[self::OPTION_LONG_FUNCTION]) {
$echoToken = [T_PRINT, 'print'];
} else {
$echoToken = [T_ECHO, 'echo'];
}
$index = -1;
while (true) {
$index = $tokens->getNextTokenOfKind($index, [[T_OPEN_TAG_WITH_ECHO]]);
if (null === $index) {
return;
}
$replace = [new Token([T_OPEN_TAG, '<?php ']), new Token($echoToken)];
if (!$tokens[$index + 1]->isWhitespace()) {
$replace[] = new Token([T_WHITESPACE, ' ']);
}
$tokens->overrideRange($index, $index, $replace);
++$index;
}
}
private function isComplexCode(Tokens $tokens, int $index): bool
{
$semicolonFound = false;
for ($count = $tokens->count(); $index < $count; ++$index) {
$token = $tokens[$index];
if ($token->isGivenKind(T_CLOSE_TAG)) {
return false;
}
if (';' === $token->getContent()) {
$semicolonFound = true;
} elseif ($semicolonFound && !$token->isWhitespace()) {
return true;
}
}
return false;
}
private function buildLongToShortTokens(Tokens $tokens, int $openTagIndex, int $echoTagIndex): array
{
$result = [new Token([T_OPEN_TAG_WITH_ECHO, '<?='])];
$start = $tokens->getNextNonWhitespace($openTagIndex);
if ($start === $echoTagIndex) {
return $result;
}
$end = $echoTagIndex - 1;
while ($tokens[$end]->isWhitespace()) {
--$end;
}
for ($index = $start; $index <= $end; ++$index) {
$result[] = clone $tokens[$index];
}
return $result;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ReturnNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Tokens;
final class SimplifiedNullReturnFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'A return statement wishing to return `void` should not return `null`.',
[
new CodeSample("<?php return null;\n"),
new CodeSample(
<<<'EOT'
<?php
function foo() { return null; }
function bar(): int { return null; }
function baz(): ?int { return null; }
function xyz(): void { return null; }
EOT
),
]
);
}
public function getPriority(): int
{
return 16;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_RETURN);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_RETURN)) {
continue;
}
if ($this->needFixing($tokens, $index)) {
$this->clear($tokens, $index);
}
}
}
private function clear(Tokens $tokens, int $index): void
{
while (!$tokens[++$index]->equals(';')) {
if ($this->shouldClearToken($tokens, $index)) {
$tokens->clearAt($index);
}
}
}
private function needFixing(Tokens $tokens, int $index): bool
{
if ($this->isStrictOrNullableReturnTypeFunction($tokens, $index)) {
return false;
}
$content = '';
while (!$tokens[$index]->equals(';')) {
$index = $tokens->getNextMeaningfulToken($index);
$content .= $tokens[$index]->getContent();
}
$content = ltrim($content, '(');
$content = rtrim($content, ');');
return 'null' === strtolower($content);
}
private function isStrictOrNullableReturnTypeFunction(Tokens $tokens, int $returnIndex): bool
{
$functionIndex = $returnIndex;
do {
$functionIndex = $tokens->getPrevTokenOfKind($functionIndex, [[T_FUNCTION]]);
if (null === $functionIndex) {
return false;
}
$openingCurlyBraceIndex = $tokens->getNextTokenOfKind($functionIndex, ['{']);
$closingCurlyBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $openingCurlyBraceIndex);
} while ($closingCurlyBraceIndex < $returnIndex);
$possibleVoidIndex = $tokens->getPrevMeaningfulToken($openingCurlyBraceIndex);
$isStrictReturnType = $tokens[$possibleVoidIndex]->isGivenKind(T_STRING) && 'void' !== $tokens[$possibleVoidIndex]->getContent();
$nullableTypeIndex = $tokens->getNextTokenOfKind($functionIndex, [[CT::T_NULLABLE_TYPE]]);
$isNullableReturnType = null !== $nullableTypeIndex && $nullableTypeIndex < $openingCurlyBraceIndex;
return $isStrictReturnType || $isNullableReturnType;
}
private function shouldClearToken(Tokens $tokens, int $index): bool
{
$token = $tokens[$index];
return !$token->isComment() && !($token->isWhitespace() && $tokens[$index + 1]->isComment());
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ReturnNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class ReturnAssignmentFixer extends AbstractFixer
{
private $tokensAnalyzer;
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Local, dynamic and directly referenced variables should not be assigned and directly returned by a function or method.',
[new CodeSample("<?php\nfunction a() {\n \$a = 1;\n return \$a;\n}\n")]
);
}
public function getPriority(): int
{
return -15;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAllTokenKindsFound([T_FUNCTION, T_RETURN, T_VARIABLE]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$tokenCount = \count($tokens);
$this->tokensAnalyzer = new TokensAnalyzer($tokens);
for ($index = 1; $index < $tokenCount; ++$index) {
if (!$tokens[$index]->isGivenKind(T_FUNCTION)) {
continue;
}
$next = $tokens->getNextMeaningfulToken($index);
if ($tokens[$next]->isGivenKind(CT::T_RETURN_REF)) {
continue;
}
$functionOpenIndex = $tokens->getNextTokenOfKind($index, ['{', ';']);
if ($tokens[$functionOpenIndex]->equals(';')) {
$index = $functionOpenIndex - 1;
continue;
}
$functionCloseIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $functionOpenIndex);
$totalTokensAdded = 0;
do {
$tokensAdded = $this->fixFunction(
$tokens,
$index,
$functionOpenIndex,
$functionCloseIndex
);
$totalTokensAdded += $tokensAdded;
} while ($tokensAdded > 0);
$index = $functionCloseIndex + $totalTokensAdded;
$tokenCount += $totalTokensAdded;
}
}
private function fixFunction(Tokens $tokens, int $functionIndex, int $functionOpenIndex, int $functionCloseIndex): int
{
static $riskyKinds = [
CT::T_DYNAMIC_VAR_BRACE_OPEN,
T_EVAL,
T_GLOBAL,
T_INCLUDE,
T_INCLUDE_ONCE,
T_REQUIRE,
T_REQUIRE_ONCE,
];
$inserted = 0;
$candidates = [];
$isRisky = false;
if ($tokens[$tokens->getNextMeaningfulToken($functionIndex)]->isGivenKind(CT::T_RETURN_REF)) {
$isRisky = true;
}
for ($index = $functionIndex + 1; $index < $functionOpenIndex; ++$index) {
if ($tokens[$index]->equals('&')) {
$isRisky = true;
break;
}
}
for ($index = $functionOpenIndex + 1; $index < $functionCloseIndex; ++$index) {
if ($tokens[$index]->isGivenKind(T_FUNCTION)) {
$nestedFunctionOpenIndex = $tokens->getNextTokenOfKind($index, ['{', ';']);
if ($tokens[$nestedFunctionOpenIndex]->equals(';')) {
$index = $nestedFunctionOpenIndex - 1;
continue;
}
$nestedFunctionCloseIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $nestedFunctionOpenIndex);
$tokensAdded = $this->fixFunction(
$tokens,
$index,
$nestedFunctionOpenIndex,
$nestedFunctionCloseIndex
);
$index = $nestedFunctionCloseIndex + $tokensAdded;
$functionCloseIndex += $tokensAdded;
$inserted += $tokensAdded;
}
if ($isRisky) {
continue;
}
if ($tokens[$index]->equals('&')) {
$isRisky = true;
continue;
}
if ($tokens[$index]->isGivenKind(T_RETURN)) {
$candidates[] = $index;
continue;
}
if ($tokens[$index]->isGivenKind($riskyKinds)) {
$isRisky = true;
continue;
}
if ($tokens[$index]->isGivenKind(T_STATIC)) {
$nextIndex = $tokens->getNextMeaningfulToken($index);
if (!$tokens[$nextIndex]->isGivenKind(T_FUNCTION)) {
$isRisky = true;
continue;
}
}
if ($tokens[$index]->equals('$')) {
$nextIndex = $tokens->getNextMeaningfulToken($index);
if ($tokens[$nextIndex]->isGivenKind(T_VARIABLE)) {
$isRisky = true;
continue;
}
}
if ($this->tokensAnalyzer->isSuperGlobal($index)) {
$isRisky = true;
continue;
}
}
if ($isRisky) {
return $inserted;
}
for ($i = \count($candidates) - 1; $i >= 0; --$i) {
$index = $candidates[$i];
$returnVarIndex = $tokens->getNextMeaningfulToken($index);
if (!$tokens[$returnVarIndex]->isGivenKind(T_VARIABLE)) {
continue;
}
$endReturnVarIndex = $tokens->getNextMeaningfulToken($returnVarIndex);
if (!$tokens[$endReturnVarIndex]->equalsAny([';', [T_CLOSE_TAG]])) {
continue;
}
$assignVarEndIndex = $tokens->getPrevMeaningfulToken($index);
if (!$tokens[$assignVarEndIndex]->equals(';')) {
continue;
}
while (true) {
$prevMeaningFul = $tokens->getPrevMeaningfulToken($assignVarEndIndex);
if (!$tokens[$prevMeaningFul]->equals(')')) {
break;
}
$assignVarEndIndex = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $prevMeaningFul);
}
$assignVarOperatorIndex = $tokens->getPrevTokenOfKind(
$assignVarEndIndex,
['=', ';', '{', '}', [T_OPEN_TAG], [T_OPEN_TAG_WITH_ECHO]]
);
if ($tokens[$assignVarOperatorIndex]->equals('}')) {
$startIndex = $this->isCloseBracePartOfDefinition($tokens, $assignVarOperatorIndex);
if (null === $startIndex) {
continue;
}
$assignVarOperatorIndex = $tokens->getPrevMeaningfulToken($startIndex);
}
if (!$tokens[$assignVarOperatorIndex]->equals('=')) {
continue;
}
$assignVarIndex = $tokens->getPrevMeaningfulToken($assignVarOperatorIndex);
if (!$tokens[$assignVarIndex]->equals($tokens[$returnVarIndex], false)) {
continue;
}
$beforeAssignVarIndex = $tokens->getPrevMeaningfulToken($assignVarIndex);
if (!$tokens[$beforeAssignVarIndex]->equalsAny([';', '{', '}'])) {
continue;
}
$inserted += $this->simplifyReturnStatement(
$tokens,
$assignVarIndex,
$assignVarOperatorIndex,
$index,
$endReturnVarIndex
);
}
return $inserted;
}
private function simplifyReturnStatement(
Tokens $tokens,
int $assignVarIndex,
int $assignVarOperatorIndex,
int $returnIndex,
int $returnVarEndIndex
): int {
$inserted = 0;
$originalIndent = $tokens[$assignVarIndex - 1]->isWhitespace()
? $tokens[$assignVarIndex - 1]->getContent()
: null
;
if ($tokens[$returnVarEndIndex]->equals(';')) {
$tokens->clearTokenAndMergeSurroundingWhitespace($returnVarEndIndex);
}
for ($i = $returnIndex; $i <= $returnVarEndIndex - 1; ++$i) {
$this->clearIfSave($tokens, $i);
}
if ($tokens[$returnIndex - 1]->isWhitespace()) {
$content = $tokens[$returnIndex - 1]->getContent();
$fistLinebreakPos = strrpos($content, "\n");
$content = false === $fistLinebreakPos
? ' '
: substr($content, $fistLinebreakPos)
;
$tokens[$returnIndex - 1] = new Token([T_WHITESPACE, $content]);
}
for ($i = $assignVarIndex; $i <= $assignVarOperatorIndex; ++$i) {
$this->clearIfSave($tokens, $i);
}
$tokens->insertAt($assignVarIndex, new Token([T_RETURN, 'return']));
++$inserted;
if (
null !== $originalIndent
&& $tokens[$assignVarIndex - 1]->isWhitespace()
&& $originalIndent !== $tokens[$assignVarIndex - 1]->getContent()
) {
$tokens[$assignVarIndex - 1] = new Token([T_WHITESPACE, $originalIndent]);
}
$nextIndex = $tokens->getNonEmptySibling($assignVarIndex, 1);
if (!$tokens[$nextIndex]->isWhitespace()) {
$tokens->insertAt($nextIndex, new Token([T_WHITESPACE, ' ']));
++$inserted;
}
return $inserted;
}
private function clearIfSave(Tokens $tokens, int $index): void
{
if ($tokens[$index]->isComment()) {
return;
}
if ($tokens[$index]->isWhitespace() && $tokens[$tokens->getPrevNonWhitespace($index)]->isComment()) {
return;
}
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
}
private function isCloseBracePartOfDefinition(Tokens $tokens, int $index): ?int
{
$index = $tokens->findBlockStart(Tokens::BLOCK_TYPE_CURLY_BRACE, $index);
$candidateIndex = $this->isOpenBraceOfLambda($tokens, $index);
if (null !== $candidateIndex) {
return $candidateIndex;
}
$candidateIndex = $this->isOpenBraceOfAnonymousClass($tokens, $index);
return $candidateIndex ?? $this->isOpenBraceOfMatch($tokens, $index);
}
private function isOpenBraceOfAnonymousClass(Tokens $tokens, int $index): ?int
{
do {
$index = $tokens->getPrevMeaningfulToken($index);
} while ($tokens[$index]->equalsAny([',', [T_STRING], [T_IMPLEMENTS], [T_EXTENDS]]));
if ($tokens[$index]->equals(')')) {
$index = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index);
$index = $tokens->getPrevMeaningfulToken($index);
}
if (!$tokens[$index]->isGivenKind(T_CLASS)) {
return null;
}
$index = $tokens->getPrevMeaningfulToken($index);
return $tokens[$index]->isGivenKind(T_NEW) ? $index : null;
}
private function isOpenBraceOfLambda(Tokens $tokens, int $index): ?int
{
$index = $tokens->getPrevMeaningfulToken($index);
if (!$tokens[$index]->equals(')')) {
return null;
}
$index = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index);
$index = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$index]->isGivenKind(CT::T_USE_LAMBDA)) {
$index = $tokens->getPrevTokenOfKind($index, [')']);
$index = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index);
$index = $tokens->getPrevMeaningfulToken($index);
}
if ($tokens[$index]->isGivenKind(CT::T_RETURN_REF)) {
$index = $tokens->getPrevMeaningfulToken($index);
}
if (!$tokens[$index]->isGivenKind(T_FUNCTION)) {
return null;
}
$staticCandidate = $tokens->getPrevMeaningfulToken($index);
return $tokens[$staticCandidate]->isGivenKind(T_STATIC) ? $staticCandidate : $index;
}
private function isOpenBraceOfMatch(Tokens $tokens, int $index): ?int
{
if (!\defined('T_MATCH') || !$tokens->isTokenKindFound(T_MATCH)) {
return null;
}
$index = $tokens->getPrevMeaningfulToken($index);
if (!$tokens[$index]->equals(')')) {
return null;
}
$index = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index);
$index = $tokens->getPrevMeaningfulToken($index);
return $tokens[$index]->isGivenKind(T_MATCH) ? $index : null;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ReturnNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Tokens;
final class NoUselessReturnFixer extends AbstractFixer
{
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAllTokenKindsFound([T_FUNCTION, T_RETURN]);
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'There should not be an empty `return` statement at the end of a function.',
[
new CodeSample(
'<?php
function example($b) {
if ($b) {
return;
}
return;
}
'
),
]
);
}
public function getPriority(): int
{
return -18;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_FUNCTION)) {
continue;
}
$index = $tokens->getNextTokenOfKind($index, [';', '{']);
if ($tokens[$index]->equals('{')) {
$this->fixFunction($tokens, $index, $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $index));
}
}
}
private function fixFunction(Tokens $tokens, int $start, int $end): void
{
for ($index = $end; $index > $start; --$index) {
if (!$tokens[$index]->isGivenKind(T_RETURN)) {
continue;
}
$nextAt = $tokens->getNextMeaningfulToken($index);
if (!$tokens[$nextAt]->equals(';')) {
continue;
}
if ($tokens->getNextMeaningfulToken($nextAt) !== $end) {
continue;
}
$previous = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$previous]->equalsAny([[T_ELSE], ')'])) {
continue;
}
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
$tokens->clearTokenAndMergeSurroundingWhitespace($nextAt);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Tokenizer\Tokens;
abstract class AbstractIncrementOperatorFixer extends AbstractFixer
{
final protected function findStart(Tokens $tokens, int $index): int
{
do {
$index = $tokens->getPrevMeaningfulToken($index);
$token = $tokens[$index];
$blockType = Tokens::detectBlockType($token);
if (null !== $blockType && !$blockType['isStart']) {
$index = $tokens->findBlockStart($blockType['type'], $index);
$token = $tokens[$index];
}
} while (!$token->equalsAny(['$', [T_VARIABLE]]));
$prevIndex = $tokens->getPrevMeaningfulToken($index);
$prevToken = $tokens[$prevIndex];
if ($prevToken->equals('$')) {
return $this->findStart($tokens, $index);
}
if ($prevToken->isObjectOperator()) {
return $this->findStart($tokens, $prevIndex);
}
if ($prevToken->isGivenKind(T_PAAMAYIM_NEKUDOTAYIM)) {
$prevPrevIndex = $tokens->getPrevMeaningfulToken($prevIndex);
if (!$tokens[$prevPrevIndex]->isGivenKind([T_STATIC, T_STRING])) {
return $this->findStart($tokens, $prevIndex);
}
$index = $tokens->getTokenNotOfKindsSibling($prevIndex, -1, [T_NS_SEPARATOR, T_STATIC, T_STRING]);
$index = $tokens->getNextMeaningfulToken($index);
}
return $index;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer;
interface DeprecatedFixerInterface extends FixerInterface
{
public function getSuccessorsNames(): array;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Basic;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\AttributeAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Tokens;
final class NoTrailingCommaInSinglelineFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'If a list of values separated by a comma is contained on a single line, then the last item MUST NOT have a trailing comma.',
[
new CodeSample("<?php\nfoo(\$a,);\n\$foo = array(1,);\n[\$foo, \$bar,] = \$array;\nuse a\\{ClassA, ClassB,};\n"),
new CodeSample("<?php\nfoo(\$a,);\n[\$foo, \$bar,] = \$array;\n", ['elements' => ['array_destructuring']]),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return
$tokens->isTokenKindFound(',')
&& $tokens->isAnyTokenKindsFound([')', CT::T_ARRAY_SQUARE_BRACE_CLOSE, CT::T_DESTRUCTURING_SQUARE_BRACE_CLOSE, CT::T_GROUP_IMPORT_BRACE_CLOSE])
;
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
$elements = ['arguments', 'array_destructuring', 'array', 'group_import'];
return new FixerConfigurationResolver([
(new FixerOptionBuilder('elements', 'Which elements to fix.'))
->setAllowedTypes(['array'])
->setAllowedValues([new AllowedValueSubset($elements)])
->setDefault($elements)
->getOption(),
]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
if (!$tokens[$index]->equals(')') && !$tokens[$index]->isGivenKind([CT::T_ARRAY_SQUARE_BRACE_CLOSE, CT::T_DESTRUCTURING_SQUARE_BRACE_CLOSE, CT::T_GROUP_IMPORT_BRACE_CLOSE])) {
continue;
}
$commaIndex = $tokens->getPrevMeaningfulToken($index);
if (!$tokens[$commaIndex]->equals(',')) {
continue;
}
$block = Tokens::detectBlockType($tokens[$index]);
$blockOpenIndex = $tokens->findBlockStart($block['type'], $index);
if ($tokens->isPartialCodeMultiline($blockOpenIndex, $index)) {
continue;
}
if (!$this->shouldBeCleared($tokens, $blockOpenIndex)) {
continue;
}
do {
$tokens->clearTokenAndMergeSurroundingWhitespace($commaIndex);
$commaIndex = $tokens->getPrevMeaningfulToken($commaIndex);
} while ($tokens[$commaIndex]->equals(','));
$tokens->removeTrailingWhitespace($commaIndex);
}
}
private function shouldBeCleared(Tokens $tokens, int $openIndex): bool
{
$elements = $this->configuration['elements'];
if ($tokens[$openIndex]->isGivenKind(CT::T_ARRAY_SQUARE_BRACE_OPEN)) {
return \in_array('array', $elements, true);
}
if ($tokens[$openIndex]->isGivenKind(CT::T_DESTRUCTURING_SQUARE_BRACE_OPEN)) {
return \in_array('array_destructuring', $elements, true);
}
if ($tokens[$openIndex]->isGivenKind(CT::T_GROUP_IMPORT_BRACE_OPEN)) {
return \in_array('group_import', $elements, true);
}
if (!$tokens[$openIndex]->equals('(')) {
return false;
}
$beforeOpen = $tokens->getPrevMeaningfulToken($openIndex);
if ($tokens[$beforeOpen]->isGivenKind(T_ARRAY)) {
return \in_array('array', $elements, true);
}
if ($tokens[$beforeOpen]->isGivenKind(T_LIST)) {
return \in_array('array_destructuring', $elements, true);
}
if ($tokens[$beforeOpen]->isGivenKind([T_UNSET, T_ISSET, T_VARIABLE, T_CLASS])) {
return \in_array('arguments', $elements, true);
}
if ($tokens[$beforeOpen]->isGivenKind(T_STRING)) {
return !AttributeAnalyzer::isAttribute($tokens, $beforeOpen) && \in_array('arguments', $elements, true);
}
if ($tokens[$beforeOpen]->equalsAny([')', ']', [CT::T_DYNAMIC_VAR_BRACE_CLOSE], [CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE]])) {
$block = Tokens::detectBlockType($tokens[$beforeOpen]);
return
(
Tokens::BLOCK_TYPE_ARRAY_INDEX_CURLY_BRACE === $block['type']
|| Tokens::BLOCK_TYPE_DYNAMIC_VAR_BRACE === $block['type']
|| Tokens::BLOCK_TYPE_INDEX_SQUARE_BRACE === $block['type']
|| Tokens::BLOCK_TYPE_PARENTHESIS_BRACE === $block['type']
) && \in_array('arguments', $elements, true)
;
}
return false;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Basic;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class EncodingFixer extends AbstractFixer
{
private string $BOM;
public function __construct()
{
parent::__construct();
$this->BOM = pack('CCC', 0xEF, 0xBB, 0xBF);
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'PHP code MUST use only UTF-8 without BOM (remove BOM).',
[
new CodeSample(
$this->BOM.'<?php
echo "Hello!";
'
),
]
);
}
public function getPriority(): int
{
return 100;
}
public function isCandidate(Tokens $tokens): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$content = $tokens[0]->getContent();
if (0 === strncmp($content, $this->BOM, 3)) {
$newContent = substr($content, 3);
if ('' === $newContent) {
$tokens->clearAt(0);
} else {
$tokens[0] = new Token([$tokens[0]->getId(), $newContent]);
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Basic;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NonPrintableCharacterFixer extends AbstractFixer implements ConfigurableFixerInterface
{
private array $symbolsReplace;
private static array $tokens = [
T_STRING_VARNAME,
T_INLINE_HTML,
T_VARIABLE,
T_COMMENT,
T_ENCAPSED_AND_WHITESPACE,
T_CONSTANT_ENCAPSED_STRING,
T_DOC_COMMENT,
];
public function __construct()
{
parent::__construct();
$this->symbolsReplace = [
pack('H*', 'e2808b') => ['', '200b'],
pack('H*', 'e28087') => [' ', '2007'],
pack('H*', 'e280af') => [' ', '202f'],
pack('H*', 'e281a0') => ['', '2060'],
pack('H*', 'c2a0') => [' ', 'a0'],
];
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Remove Zero-width space (ZWSP), Non-breaking space (NBSP) and other invisible unicode symbols.',
[
new CodeSample(
'<?php echo "'.pack('H*', 'e2808b').'Hello'.pack('H*', 'e28087').'World'.pack('H*', 'c2a0')."!\";\n"
),
new CodeSample(
'<?php echo "'.pack('H*', 'e2808b').'Hello'.pack('H*', 'e28087').'World'.pack('H*', 'c2a0')."!\";\n",
['use_escape_sequences_in_strings' => false]
),
],
null,
'Risky when strings contain intended invisible characters.'
);
}
public function isRisky(): bool
{
return true;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound(self::$tokens);
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('use_escape_sequences_in_strings', 'Whether characters should be replaced with escape sequences in strings.'))
->setAllowedTypes(['bool'])
->setDefault(true)
->getOption(),
]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$replacements = [];
$escapeSequences = [];
foreach ($this->symbolsReplace as $character => [$replacement, $codepoint]) {
$replacements[$character] = $replacement;
$escapeSequences[$character] = '\u{'.$codepoint.'}';
}
foreach ($tokens as $index => $token) {
$content = $token->getContent();
if (
$this->configuration['use_escape_sequences_in_strings']
&& $token->isGivenKind([T_CONSTANT_ENCAPSED_STRING, T_ENCAPSED_AND_WHITESPACE])
) {
if (!Preg::match('/'.implode('|', array_keys($escapeSequences)).'/', $content)) {
continue;
}
$previousToken = $tokens[$index - 1];
$stringTypeChanged = false;
$swapQuotes = false;
if ($previousToken->isGivenKind(T_START_HEREDOC)) {
$previousTokenContent = $previousToken->getContent();
if (str_contains($previousTokenContent, '\'')) {
$tokens[$index - 1] = new Token([T_START_HEREDOC, str_replace('\'', '', $previousTokenContent)]);
$stringTypeChanged = true;
}
} elseif (str_starts_with($content, "'")) {
$stringTypeChanged = true;
$swapQuotes = true;
}
if ($swapQuotes) {
$content = str_replace("\\'", "'", $content);
}
if ($stringTypeChanged) {
$content = Preg::replace('/(\\\\{1,2})/', '\\\\\\\\', $content);
$content = str_replace('$', '\$', $content);
}
if ($swapQuotes) {
$content = str_replace('"', '\"', $content);
$content = Preg::replace('/^\'(.*)\'$/s', '"$1"', $content);
}
$tokens[$index] = new Token([$token->getId(), strtr($content, $escapeSequences)]);
continue;
}
if ($token->isGivenKind(self::$tokens)) {
$newContent = strtr($content, $replacements);
if ($token->isGivenKind([T_STRING_VARNAME, T_VARIABLE]) && str_contains($newContent, ' ')) {
continue;
}
if ($token->isGivenKind([T_COMMENT, T_DOC_COMMENT]) && str_starts_with($newContent, '/*') && strpos($newContent, '*/') !== \strlen($newContent) - 2) {
continue;
}
$tokens[$index] = new Token([$token->getId(), $newContent]);
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Basic;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\Indentation;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\FixerDefinition\VersionSpecification;
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class CurlyBracesPositionFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
{
use Indentation;
public const NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END = 'next_line_unless_newline_at_signature_end';
public const SAME_LINE = 'same_line';
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Curly braces must be placed as configured.',
[
new CodeSample(
'<?php
class Foo {
}
function foo() {
}
$foo = function()
{
};
if (foo())
{
bar();
}
'
),
new VersionSpecificCodeSample(
'<?php
$foo = new class
{
};
',
new VersionSpecification(70000)
),
new CodeSample(
'<?php
if (foo()) {
bar();
}
',
['control_structures_opening_brace' => self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END]
),
new CodeSample(
'<?php
function foo()
{
}
',
['functions_opening_brace' => self::SAME_LINE]
),
new CodeSample(
'<?php
$foo = function () {
};
',
['anonymous_functions_opening_brace' => self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END]
),
new CodeSample(
'<?php
class Foo
{
}
',
['classes_opening_brace' => self::SAME_LINE]
),
new VersionSpecificCodeSample(
'<?php
$foo = new class {
};
',
new VersionSpecification(70000),
['anonymous_classes_opening_brace' => self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END]
),
new VersionSpecificCodeSample(
'<?php
$foo = new class { };
$bar = new class { private $baz; };
',
new VersionSpecification(70000),
['allow_single_line_empty_anonymous_classes' => true]
),
new CodeSample(
'<?php
$foo = function () { return true; };
$bar = function () { $result = true;
return $result; };
',
['allow_single_line_anonymous_functions' => true]
),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound('{');
}
public function getPriority(): int
{
return parent::getPriority();
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$classyTokens = Token::getClassyTokenKinds();
$controlStructureTokens = [T_DECLARE, T_DO, T_ELSE, T_ELSEIF, T_FINALLY, T_FOR, T_FOREACH, T_IF, T_WHILE, T_TRY, T_CATCH, T_SWITCH];
if (\defined('T_MATCH')) {
$controlStructureTokens[] = T_MATCH;
}
$tokensAnalyzer = new TokensAnalyzer($tokens);
$allowSingleLineUntil = null;
foreach ($tokens as $index => $token) {
$allowSingleLine = false;
$allowSingleLineIfEmpty = false;
if ($token->isGivenKind($classyTokens)) {
$openBraceIndex = $tokens->getNextTokenOfKind($index, ['{']);
if ($tokensAnalyzer->isAnonymousClass($index)) {
$allowSingleLineIfEmpty = $this->configuration['allow_single_line_empty_anonymous_classes'];
$positionOption = 'anonymous_classes_opening_brace';
} else {
$positionOption = 'classes_opening_brace';
}
} elseif ($token->isGivenKind(T_FUNCTION)) {
$openBraceIndex = $tokens->getNextTokenOfKind($index, ['{', ';']);
if ($tokens[$openBraceIndex]->equals(';')) {
continue;
}
if ($tokensAnalyzer->isLambda($index)) {
$allowSingleLine = $this->configuration['allow_single_line_anonymous_functions'];
$positionOption = 'anonymous_functions_opening_brace';
} else {
$positionOption = 'functions_opening_brace';
}
} elseif ($token->isGivenKind($controlStructureTokens)) {
$parenthesisEndIndex = $this->findParenthesisEnd($tokens, $index);
$openBraceIndex = $tokens->getNextMeaningfulToken($parenthesisEndIndex);
if (!$tokens[$openBraceIndex]->equals('{')) {
continue;
}
$positionOption = 'control_structures_opening_brace';
} else {
continue;
}
$closeBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $openBraceIndex);
$addNewlinesInsideBraces = true;
if ($allowSingleLine || $allowSingleLineIfEmpty || $index < $allowSingleLineUntil) {
$addNewlinesInsideBraces = false;
for ($indexInsideBraces = $openBraceIndex + 1; $indexInsideBraces < $closeBraceIndex; ++$indexInsideBraces) {
$tokenInsideBraces = $tokens[$indexInsideBraces];
if (
($allowSingleLineIfEmpty && !$tokenInsideBraces->isWhitespace() && !$tokenInsideBraces->isComment())
|| ($tokenInsideBraces->isWhitespace() && 1 === Preg::match('/\R/', $tokenInsideBraces->getContent()))
) {
$addNewlinesInsideBraces = true;
break;
}
}
if (!$addNewlinesInsideBraces && null === $allowSingleLineUntil) {
$allowSingleLineUntil = $closeBraceIndex;
}
}
if (
$addNewlinesInsideBraces
&& !$this->isFollowedByNewLine($tokens, $openBraceIndex)
&& !$this->hasCommentOnSameLine($tokens, $openBraceIndex)
&& !$tokens[$tokens->getNextMeaningfulToken($openBraceIndex)]->isGivenKind(T_CLOSE_TAG)
) {
$whitespace = $this->whitespacesConfig->getLineEnding().$this->getLineIndentation($tokens, $openBraceIndex);
if ($tokens->ensureWhitespaceAtIndex($openBraceIndex + 1, 0, $whitespace)) {
++$closeBraceIndex;
}
}
$whitespace = ' ';
if (self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END === $this->configuration[$positionOption]) {
$whitespace = $this->whitespacesConfig->getLineEnding().$this->getLineIndentation($tokens, $index);
$previousTokenIndex = $openBraceIndex;
do {
$previousTokenIndex = $tokens->getPrevMeaningfulToken($previousTokenIndex);
} while ($tokens[$previousTokenIndex]->isGivenKind([CT::T_TYPE_COLON, CT::T_NULLABLE_TYPE, T_STRING, T_NS_SEPARATOR, CT::T_ARRAY_TYPEHINT, T_STATIC, CT::T_TYPE_ALTERNATION, CT::T_TYPE_INTERSECTION]));
if ($tokens[$previousTokenIndex]->equals(')')) {
if ($tokens[--$previousTokenIndex]->isComment()) {
--$previousTokenIndex;
}
if (
$tokens[$previousTokenIndex]->isWhitespace()
&& 1 === Preg::match('/\R/', $tokens[$previousTokenIndex]->getContent())
) {
$whitespace = ' ';
}
}
}
$moveBraceToIndex = null;
if (' ' === $whitespace) {
$previousMeaningfulIndex = $tokens->getPrevMeaningfulToken($openBraceIndex);
for ($indexBeforeOpenBrace = $openBraceIndex - 1; $indexBeforeOpenBrace > $previousMeaningfulIndex; --$indexBeforeOpenBrace) {
if (!$tokens[$indexBeforeOpenBrace]->isComment()) {
continue;
}
$tokenBeforeOpenBrace = $tokens[--$indexBeforeOpenBrace];
if ($tokenBeforeOpenBrace->isWhitespace()) {
$moveBraceToIndex = $indexBeforeOpenBrace;
} elseif ($indexBeforeOpenBrace === $previousMeaningfulIndex) {
$moveBraceToIndex = $previousMeaningfulIndex + 1;
}
}
} elseif (!$tokens[$openBraceIndex - 1]->isWhitespace() || !Preg::match('/\R/', $tokens[$openBraceIndex - 1]->getContent())) {
for ($indexAfterOpenBrace = $openBraceIndex + 1; $indexAfterOpenBrace < $closeBraceIndex; ++$indexAfterOpenBrace) {
if ($tokens[$indexAfterOpenBrace]->isWhitespace() && Preg::match('/\R/', $tokens[$indexAfterOpenBrace]->getContent())) {
break;
}
if ($tokens[$indexAfterOpenBrace]->isComment() && !str_starts_with($tokens[$indexAfterOpenBrace]->getContent(), '/*')) {
$moveBraceToIndex = $indexAfterOpenBrace + 1;
}
}
}
if (null !== $moveBraceToIndex) {
$movedToken = clone $tokens[$openBraceIndex];
$delta = $openBraceIndex < $moveBraceToIndex ? 1 : -1;
if ($tokens[$openBraceIndex + $delta]->isWhitespace()) {
if (-1 === $delta && Preg::match('/\R/', $tokens[$openBraceIndex - 1]->getContent())) {
$content = Preg::replace('/^(\h*?\R)?\h*/', '', $tokens[$openBraceIndex + 1]->getContent());
if ('' !== $content) {
$tokens[$openBraceIndex + 1] = new Token([T_WHITESPACE, $content]);
} else {
$tokens->clearAt($openBraceIndex + 1);
}
} else {
$tokens->clearAt($openBraceIndex - 1);
}
}
for (; $openBraceIndex !== $moveBraceToIndex; $openBraceIndex += $delta) {
$siblingToken = $tokens[$openBraceIndex + $delta];
$tokens[$openBraceIndex] = $siblingToken;
}
$tokens[$openBraceIndex] = $movedToken;
$openBraceIndex = $moveBraceToIndex;
}
if ($tokens->ensureWhitespaceAtIndex($openBraceIndex - 1, 1, $whitespace)) {
++$closeBraceIndex;
if (null !== $allowSingleLineUntil) {
++$allowSingleLineUntil;
}
}
if (
!$addNewlinesInsideBraces
|| $tokens[$tokens->getPrevMeaningfulToken($closeBraceIndex)]->isGivenKind(T_OPEN_TAG)
) {
continue;
}
for ($prevIndex = $closeBraceIndex - 1; $tokens->isEmptyAt($prevIndex); --$prevIndex);
$prevToken = $tokens[$prevIndex];
if ($prevToken->isWhitespace() && 1 === Preg::match('/\R/', $prevToken->getContent())) {
continue;
}
$whitespace = $this->whitespacesConfig->getLineEnding().$this->getLineIndentation($tokens, $openBraceIndex);
$tokens->ensureWhitespaceAtIndex($prevIndex, 1, $whitespace);
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('control_structures_opening_brace', 'the position of the opening brace of control structures body.'))
->setAllowedValues([self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END, self::SAME_LINE])
->setDefault(self::SAME_LINE)
->getOption(),
(new FixerOptionBuilder('functions_opening_brace', 'the position of the opening brace of functions body.'))
->setAllowedValues([self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END, self::SAME_LINE])
->setDefault(self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END)
->getOption(),
(new FixerOptionBuilder('anonymous_functions_opening_brace', 'the position of the opening brace of anonymous functions body.'))
->setAllowedValues([self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END, self::SAME_LINE])
->setDefault(self::SAME_LINE)
->getOption(),
(new FixerOptionBuilder('classes_opening_brace', 'the position of the opening brace of classes body.'))
->setAllowedValues([self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END, self::SAME_LINE])
->setDefault(self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END)
->getOption(),
(new FixerOptionBuilder('anonymous_classes_opening_brace', 'the position of the opening brace of anonymous classes body.'))
->setAllowedValues([self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END, self::SAME_LINE])
->setDefault(self::SAME_LINE)
->getOption(),
(new FixerOptionBuilder('allow_single_line_empty_anonymous_classes', 'allow anonymous classes to have opening and closing braces on the same line.'))
->setAllowedTypes(['bool'])
->setDefault(true)
->getOption(),
(new FixerOptionBuilder('allow_single_line_anonymous_functions', 'allow anonymous functions to have opening and closing braces on the same line.'))
->setAllowedTypes(['bool'])
->setDefault(true)
->getOption(),
]);
}
private function findParenthesisEnd(Tokens $tokens, int $structureTokenIndex): int
{
$nextIndex = $tokens->getNextMeaningfulToken($structureTokenIndex);
$nextToken = $tokens[$nextIndex];
if (!$nextToken->equals('(')) {
return $structureTokenIndex;
}
return $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $nextIndex);
}
private function isFollowedByNewLine(Tokens $tokens, int $index): bool
{
for (++$index, $max = \count($tokens) - 1; $index < $max; ++$index) {
$token = $tokens[$index];
if (!$token->isComment()) {
return $token->isWhitespace() && 1 === Preg::match('/\R/', $token->getContent());
}
}
return false;
}
private function hasCommentOnSameLine(Tokens $tokens, int $index): bool
{
$token = $tokens[$index + 1];
if ($token->isWhitespace() && 1 !== Preg::match('/\R/', $token->getContent())) {
$token = $tokens[$index + 2];
}
return $token->isComment();
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Basic;
use PhpCsFixer\AbstractProxyFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\ControlStructure\ControlStructureBracesFixer;
use PhpCsFixer\Fixer\ControlStructure\ControlStructureContinuationPositionFixer;
use PhpCsFixer\Fixer\LanguageConstruct\DeclareParenthesesFixer;
use PhpCsFixer\Fixer\LanguageConstruct\SingleSpaceAfterConstructFixer;
use PhpCsFixer\Fixer\Whitespace\NoExtraBlankLinesFixer;
use PhpCsFixer\Fixer\Whitespace\StatementIndentationFixer;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Tokens;
final class BracesFixer extends AbstractProxyFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
{
public const LINE_NEXT = 'next';
public const LINE_SAME = 'same';
private $curlyBracesPositionFixer;
private $controlStructureContinuationPositionFixer;
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'The body of each structure MUST be enclosed by braces. Braces should be properly placed. Body of braces should be properly indented.',
[
new CodeSample(
'<?php
class Foo {
public function bar($baz) {
if ($baz = 900) echo "Hello!";
if ($baz = 9000)
echo "Wait!";
if ($baz == true)
{
echo "Why?";
}
else
{
echo "Ha?";
}
if (is_array($baz))
foreach ($baz as $b)
{
echo $b;
}
}
}
'
),
new CodeSample(
'<?php
$positive = function ($item) { return $item >= 0; };
$negative = function ($item) {
return $item < 0; };
',
['allow_single_line_closure' => true]
),
new CodeSample(
'<?php
class Foo
{
public function bar($baz)
{
if ($baz = 900) echo "Hello!";
if ($baz = 9000)
echo "Wait!";
if ($baz == true)
{
echo "Why?";
}
else
{
echo "Ha?";
}
if (is_array($baz))
foreach ($baz as $b)
{
echo $b;
}
}
}
',
['position_after_functions_and_oop_constructs' => self::LINE_SAME]
),
]
);
}
public function getPriority(): int
{
return 35;
}
public function configure(array $configuration = null): void
{
parent::configure($configuration);
$this->getCurlyBracesPositionFixer()->configure([
'control_structures_opening_brace' => $this->translatePositionOption($this->configuration['position_after_control_structures']),
'functions_opening_brace' => $this->translatePositionOption($this->configuration['position_after_functions_and_oop_constructs']),
'anonymous_functions_opening_brace' => $this->translatePositionOption($this->configuration['position_after_anonymous_constructs']),
'classes_opening_brace' => $this->translatePositionOption($this->configuration['position_after_functions_and_oop_constructs']),
'anonymous_classes_opening_brace' => $this->translatePositionOption($this->configuration['position_after_anonymous_constructs']),
'allow_single_line_empty_anonymous_classes' => $this->configuration['allow_single_line_anonymous_class_with_empty_body'],
'allow_single_line_anonymous_functions' => $this->configuration['allow_single_line_closure'],
]);
$this->getControlStructureContinuationPositionFixer()->configure([
'position' => self::LINE_NEXT === $this->configuration['position_after_control_structures']
? ControlStructureContinuationPositionFixer::NEXT_LINE
: ControlStructureContinuationPositionFixer::SAME_LINE,
]);
$this->configuration = $configuration;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = \count($tokens) - 1; $index > 0; --$index) {
if ($tokens[$index]->isGivenKind(CT::T_USE_LAMBDA)) {
$tokens->ensureWhitespaceAtIndex($index - 1, 1, ' ');
}
}
parent::applyFix($file, $tokens);
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('allow_single_line_anonymous_class_with_empty_body', 'Whether single line anonymous class with empty body notation should be allowed.'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
(new FixerOptionBuilder('allow_single_line_closure', 'Whether single line lambda notation should be allowed.'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
(new FixerOptionBuilder('position_after_functions_and_oop_constructs', 'whether the opening brace should be placed on "next" or "same" line after classy constructs (non-anonymous classes, interfaces, traits, methods and non-lambda functions).'))
->setAllowedValues([self::LINE_NEXT, self::LINE_SAME])
->setDefault(self::LINE_NEXT)
->getOption(),
(new FixerOptionBuilder('position_after_control_structures', 'whether the opening brace should be placed on "next" or "same" line after control structures.'))
->setAllowedValues([self::LINE_NEXT, self::LINE_SAME])
->setDefault(self::LINE_SAME)
->getOption(),
(new FixerOptionBuilder('position_after_anonymous_constructs', 'whether the opening brace should be placed on "next" or "same" line after anonymous constructs (anonymous classes and lambda functions).'))
->setAllowedValues([self::LINE_NEXT, self::LINE_SAME])
->setDefault(self::LINE_SAME)
->getOption(),
]);
}
protected function createProxyFixers(): array
{
$singleSpaceAfterConstructFixer = new SingleSpaceAfterConstructFixer();
$singleSpaceAfterConstructFixer->configure([
'constructs' => ['elseif', 'for', 'foreach', 'if', 'match', 'while', 'use_lambda'],
]);
$noExtraBlankLinesFixer = new NoExtraBlankLinesFixer();
$noExtraBlankLinesFixer->configure([
'tokens' => ['curly_brace_block'],
]);
return [
$singleSpaceAfterConstructFixer,
new ControlStructureBracesFixer(),
$noExtraBlankLinesFixer,
$this->getCurlyBracesPositionFixer(),
$this->getControlStructureContinuationPositionFixer(),
new DeclareParenthesesFixer(),
new NoMultipleStatementsPerLineFixer(),
new StatementIndentationFixer(true),
];
}
private function getCurlyBracesPositionFixer(): CurlyBracesPositionFixer
{
if (null === $this->curlyBracesPositionFixer) {
$this->curlyBracesPositionFixer = new CurlyBracesPositionFixer();
}
return $this->curlyBracesPositionFixer;
}
private function getControlStructureContinuationPositionFixer(): ControlStructureContinuationPositionFixer
{
if (null === $this->controlStructureContinuationPositionFixer) {
$this->controlStructureContinuationPositionFixer = new ControlStructureContinuationPositionFixer();
}
return $this->controlStructureContinuationPositionFixer;
}
private function translatePositionOption(string $option): string
{
return self::LINE_NEXT === $option
? CurlyBracesPositionFixer::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END
: CurlyBracesPositionFixer::SAME_LINE
;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Basic;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\FileSpecificCodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\StdinFileInfo;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class PsrAutoloadingFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Classes must be in a path that matches their namespace, be at least one namespace deep and the class name should match the file name.',
[
new FileSpecificCodeSample(
'<?php
namespace PhpCsFixer\FIXER\Basic;
class InvalidName {}
',
new \SplFileInfo(__FILE__)
),
new FileSpecificCodeSample(
'<?php
namespace PhpCsFixer\FIXER\Basic;
class InvalidName {}
',
new \SplFileInfo(__FILE__),
['dir' => './src']
),
],
null,
'This fixer may change your class name, which will break the code that depends on the old name.'
);
}
public function configure(array $configuration): void
{
parent::configure($configuration);
if (null !== $this->configuration['dir']) {
$realpath = realpath($this->configuration['dir']);
if (false === $realpath) {
throw new \InvalidArgumentException(sprintf('Failed to resolve configured directory "%s".', $this->configuration['dir']));
}
$this->configuration['dir'] = $realpath;
}
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound(Token::getClassyTokenKinds());
}
public function isRisky(): bool
{
return true;
}
public function getPriority(): int
{
return -10;
}
public function supports(\SplFileInfo $file): bool
{
if ($file instanceof StdinFileInfo) {
return false;
}
if (
('php' !== $file->getExtension())
|| 0 === Preg::match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $file->getBasename('.php'))
) {
return false;
}
try {
$tokens = Tokens::fromCode(sprintf('<?php class %s {}', $file->getBasename('.php')));
if ($tokens[3]->isKeyword() || $tokens[3]->isMagicConstant()) {
return false;
}
} catch (\ParseError $e) {
return false;
}
return !Preg::match('{[/\\\\](stub|fixture)s?[/\\\\]}i', $file->getRealPath());
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('dir', 'If provided, the directory where the project code is placed.'))
->setAllowedTypes(['null', 'string'])
->setDefault(null)
->getOption(),
]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$tokenAnalyzer = new TokensAnalyzer($tokens);
if (null !== $this->configuration['dir'] && !str_starts_with($file->getRealPath(), $this->configuration['dir'])) {
return;
}
$namespace = null;
$namespaceStartIndex = null;
$namespaceEndIndex = null;
$classyName = null;
$classyIndex = null;
foreach ($tokens as $index => $token) {
if ($token->isGivenKind(T_NAMESPACE)) {
if (null !== $namespace) {
return;
}
$namespaceStartIndex = $tokens->getNextMeaningfulToken($index);
$namespaceEndIndex = $tokens->getNextTokenOfKind($namespaceStartIndex, [';']);
$namespace = trim($tokens->generatePartialCode($namespaceStartIndex, $namespaceEndIndex - 1));
} elseif ($token->isClassy()) {
if ($tokenAnalyzer->isAnonymousClass($index)) {
continue;
}
if (null !== $classyName) {
return;
}
$classyIndex = $tokens->getNextMeaningfulToken($index);
$classyName = $tokens[$classyIndex]->getContent();
}
}
if (null === $classyName) {
return;
}
$expectedClassyName = $this->calculateClassyName($file, $namespace, $classyName);
if ($classyName !== $expectedClassyName) {
$tokens[$classyIndex] = new Token([T_STRING, $expectedClassyName]);
}
if (null === $this->configuration['dir'] || null === $namespace) {
return;
}
if (!is_dir($this->configuration['dir'])) {
return;
}
$configuredDir = realpath($this->configuration['dir']);
$fileDir = \dirname($file->getRealPath());
if (\strlen($configuredDir) >= \strlen($fileDir)) {
return;
}
$newNamespace = substr(str_replace('/', '\\', $fileDir), \strlen($configuredDir) + 1);
$originalNamespace = substr($namespace, -\strlen($newNamespace));
if ($originalNamespace !== $newNamespace && strtolower($originalNamespace) === strtolower($newNamespace)) {
$tokens->clearRange($namespaceStartIndex, $namespaceEndIndex);
$namespace = substr($namespace, 0, -\strlen($newNamespace)).$newNamespace;
$newNamespace = Tokens::fromCode('<?php namespace '.$namespace.';');
$newNamespace->clearRange(0, 2);
$newNamespace->clearEmptyTokens();
$tokens->insertAt($namespaceStartIndex, $newNamespace);
}
}
private function calculateClassyName(\SplFileInfo $file, ?string $namespace, string $currentName): string
{
$name = $file->getBasename('.php');
$maxNamespace = $this->calculateMaxNamespace($file, $namespace);
if (null !== $this->configuration['dir']) {
return ('' !== $maxNamespace ? (str_replace('\\', '_', $maxNamespace).'_') : '').$name;
}
$namespaceParts = array_reverse(explode('\\', $maxNamespace));
foreach ($namespaceParts as $namespacePart) {
$nameCandidate = sprintf('%s_%s', $namespacePart, $name);
if (strtolower($nameCandidate) !== strtolower(substr($currentName, -\strlen($nameCandidate)))) {
break;
}
$name = $nameCandidate;
}
return $name;
}
private function calculateMaxNamespace(\SplFileInfo $file, ?string $namespace): string
{
if (null === $this->configuration['dir']) {
$root = \dirname($file->getRealPath());
while ($root !== \dirname($root)) {
$root = \dirname($root);
}
} else {
$root = realpath($this->configuration['dir']);
}
$namespaceAccordingToFileLocation = trim(str_replace(\DIRECTORY_SEPARATOR, '\\', substr(\dirname($file->getRealPath()), \strlen($root))), '\\');
if (null === $namespace) {
return $namespaceAccordingToFileLocation;
}
$namespaceAccordingToFileLocationPartsReversed = array_reverse(explode('\\', $namespaceAccordingToFileLocation));
$namespacePartsReversed = array_reverse(explode('\\', $namespace));
foreach ($namespacePartsReversed as $key => $namespaceParte) {
if (!isset($namespaceAccordingToFileLocationPartsReversed[$key])) {
break;
}
if (strtolower($namespaceParte) !== strtolower($namespaceAccordingToFileLocationPartsReversed[$key])) {
break;
}
unset($namespaceAccordingToFileLocationPartsReversed[$key]);
}
return implode('\\', array_reverse($namespaceAccordingToFileLocationPartsReversed));
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Basic;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\FixerDefinition\VersionSpecification;
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class OctalNotationFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Literal octal must be in `0o` notation.',
[
new VersionSpecificCodeSample(
"<?php \$foo = 0123;\n",
new VersionSpecification(80100)
),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return \PHP_VERSION_ID >= 80100 && $tokens->isTokenKindFound(T_LNUMBER);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_LNUMBER)) {
continue;
}
$content = $token->getContent();
if (1 !== Preg::match('#^0\d+$#', $content)) {
continue;
}
$tokens[$index] = 1 === Preg::match('#^0+$#', $content)
? new Token([T_LNUMBER, '0'])
: new Token([T_LNUMBER, '0o'.substr($content, 1)])
;
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Basic;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\Indentation;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Tokens;
final class NoMultipleStatementsPerLineFixer extends AbstractFixer implements WhitespacesAwareFixerInterface
{
use Indentation;
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'There must not be more than one statement per line.',
[new CodeSample("<?php\nfoo(); bar();\n")]
);
}
public function getPriority(): int
{
return -1;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(';');
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = 1, $max = \count($tokens) - 1; $index < $max; ++$index) {
if ($tokens[$index]->isGivenKind(T_FOR)) {
$index = $tokens->findBlockEnd(
Tokens::BLOCK_TYPE_PARENTHESIS_BRACE,
$tokens->getNextTokenOfKind($index, ['('])
);
continue;
}
if (!$tokens[$index]->equals(';')) {
continue;
}
for ($nextIndex = $index + 1; $nextIndex < $max; ++$nextIndex) {
$token = $tokens[$nextIndex];
if ($token->isWhitespace() || $token->isComment()) {
if (1 === Preg::match('/\R/', $token->getContent())) {
break;
}
continue;
}
if (!$token->equalsAny(['}', [T_CLOSE_TAG], [T_ENDIF], [T_ENDFOR], [T_ENDSWITCH], [T_ENDWHILE], [T_ENDFOREACH]])) {
$whitespaceIndex = $index;
do {
$token = $tokens[++$whitespaceIndex];
} while ($token->isComment());
$newline = $this->whitespacesConfig->getLineEnding().$this->getLineIndentation($tokens, $index);
if ($tokens->ensureWhitespaceAtIndex($whitespaceIndex, 0, $newline)) {
++$max;
}
}
break;
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer;
use PhpCsFixer\WhitespacesFixerConfig;
interface WhitespacesAwareFixerInterface extends FixerInterface
{
public function setWhitespacesConfig(WhitespacesFixerConfig $config): void;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\StringNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NoTrailingWhitespaceInStringFixer extends AbstractFixer
{
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_CONSTANT_ENCAPSED_STRING, T_ENCAPSED_AND_WHITESPACE, T_INLINE_HTML]);
}
public function isRisky(): bool
{
return true;
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'There must be no trailing whitespace in strings.',
[
new CodeSample(
"<?php \$a = ' \n foo \n';\n"
),
],
null,
'Changing the whitespaces in strings might affect string comparisons and outputs.'
);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = $tokens->count() - 1, $last = true; $index >= 0; --$index, $last = false) {
$token = $tokens[$index];
if (!$token->isGivenKind([T_CONSTANT_ENCAPSED_STRING, T_ENCAPSED_AND_WHITESPACE, T_INLINE_HTML])) {
continue;
}
$isInlineHtml = $token->isGivenKind(T_INLINE_HTML);
$regex = $isInlineHtml && $last ? '/\h+(?=\R|$)/' : '/\h+(?=\R)/';
$content = Preg::replace($regex, '', $token->getContent());
if ($token->getContent() === $content) {
continue;
}
if (!$isInlineHtml || 0 === $index) {
$this->updateContent($tokens, $index, $content);
continue;
}
$prev = $index - 1;
if ($tokens[$prev]->equals([T_CLOSE_TAG, '?>']) && Preg::match('/^\R/', $content, $match)) {
$tokens[$prev] = new Token([T_CLOSE_TAG, $tokens[$prev]->getContent().$match[0]]);
$content = substr($content, \strlen($match[0]));
$content = false === $content ? '' : $content;
}
$this->updateContent($tokens, $index, $content);
}
}
private function updateContent(Tokens $tokens, int $index, string $content): void
{
if ('' === $content) {
$tokens->clearAt($index);
return;
}
$tokens[$index] = new Token([$tokens[$index]->getId(), $content]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\StringNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class SimpleToComplexStringVariableFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Converts explicit variables in double-quoted strings and heredoc syntax from simple to complex format (`${` to `{$`).',
[
new CodeSample(
<<<'EOT'
<?php
$name = 'World';
echo "Hello ${name}!";
EOT
),
new CodeSample(
<<<'EOT'
<?php
$name = 'World';
echo <<<TEST
Hello ${name}!
TEST;
EOT
),
],
"Doesn't touch implicit variables. Works together nicely with `explicit_string_variable`."
);
}
public function getPriority(): int
{
return -10;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOLLAR_OPEN_CURLY_BRACES);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = \count($tokens) - 3; $index > 0; --$index) {
$token = $tokens[$index];
if (!$token->isGivenKind(T_DOLLAR_OPEN_CURLY_BRACES)) {
continue;
}
$varnameToken = $tokens[$index + 1];
if (!$varnameToken->isGivenKind(T_STRING_VARNAME)) {
continue;
}
$dollarCloseToken = $tokens[$index + 2];
if (!$dollarCloseToken->isGivenKind(CT::T_DOLLAR_CLOSE_CURLY_BRACES)) {
continue;
}
$tokenOfStringBeforeToken = $tokens[$index - 1];
$stringContent = $tokenOfStringBeforeToken->getContent();
if (str_ends_with($stringContent, '$') && !str_ends_with($stringContent, '\\$')) {
$newContent = substr($stringContent, 0, -1).'\\$';
$tokenOfStringBeforeToken = new Token([T_ENCAPSED_AND_WHITESPACE, $newContent]);
}
$tokens->overrideRange($index - 1, $index + 2, [
$tokenOfStringBeforeToken,
new Token([T_CURLY_OPEN, '{']),
new Token([T_VARIABLE, '$'.$varnameToken->getContent()]),
new Token([CT::T_CURLY_CLOSE, '}']),
]);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\StringNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NoBinaryStringFixer extends AbstractFixer
{
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound(
[
T_CONSTANT_ENCAPSED_STRING,
T_START_HEREDOC,
'b"',
]
);
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'There should not be a binary flag before strings.',
[
new CodeSample("<?php \$a = b'foo';\n"),
new CodeSample("<?php \$a = b<<<EOT\nfoo\nEOT;\n"),
]
);
}
public function getPriority(): int
{
return 40;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if ($token->isGivenKind([T_CONSTANT_ENCAPSED_STRING, T_START_HEREDOC])) {
$content = $token->getContent();
if ('b' === strtolower($content[0])) {
$tokens[$index] = new Token([$token->getId(), substr($content, 1)]);
}
} elseif ($token->equals('b"')) {
$tokens[$index] = new Token('"');
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\StringNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class ExplicitStringVariableFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Converts implicit variables into explicit ones in double-quoted strings or heredoc syntax.',
[new CodeSample(
<<<'EOT'
<?php
$a = "My name is $name !";
$b = "I live in $state->country !";
$c = "I have $farm[0] chickens !";
EOT
)],
'The reasoning behind this rule is the following:'
."\n".'- When there are two valid ways of doing the same thing, using both is confusing, there should be a coding standard to follow'
."\n".'- PHP manual marks `"$var"` syntax as implicit and `"${var}"` syntax as explicit: explicit code should always be preferred'
."\n".'- Explicit syntax allows word concatenation inside strings, e.g. `"${var}IsAVar"`, implicit doesn\'t'
."\n".'- Explicit syntax is easier to detect for IDE/editors and therefore has colors/highlight with higher contrast, which is easier to read'
."\n".'Backtick operator is skipped because it is harder to handle; you can use `backtick_to_shell_exec` fixer to normalize backticks to strings'
);
}
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_VARIABLE);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$backtickStarted = false;
for ($index = \count($tokens) - 1; $index > 0; --$index) {
$token = $tokens[$index];
if ($token->equals('`')) {
$backtickStarted = !$backtickStarted;
continue;
}
if ($backtickStarted || !$token->isGivenKind(T_VARIABLE)) {
continue;
}
$prevToken = $tokens[$index - 1];
if (!$this->isStringPartToken($prevToken)) {
continue;
}
$distinctVariableIndex = $index;
$variableTokens = [
$distinctVariableIndex => [
'tokens' => [$index => $token],
'firstVariableTokenIndex' => $index,
'lastVariableTokenIndex' => $index,
],
];
$nextIndex = $index + 1;
$squareBracketCount = 0;
while (!$this->isStringPartToken($tokens[$nextIndex])) {
if ($tokens[$nextIndex]->isGivenKind(T_CURLY_OPEN)) {
$nextIndex = $tokens->getNextTokenOfKind($nextIndex, [[CT::T_CURLY_CLOSE]]);
} elseif ($tokens[$nextIndex]->isGivenKind(T_VARIABLE) && 1 !== $squareBracketCount) {
$distinctVariableIndex = $nextIndex;
$variableTokens[$distinctVariableIndex] = [
'tokens' => [$nextIndex => $tokens[$nextIndex]],
'firstVariableTokenIndex' => $nextIndex,
'lastVariableTokenIndex' => $nextIndex,
];
} else {
$variableTokens[$distinctVariableIndex]['tokens'][$nextIndex] = $tokens[$nextIndex];
$variableTokens[$distinctVariableIndex]['lastVariableTokenIndex'] = $nextIndex;
if ($tokens[$nextIndex]->equalsAny(['[', ']'])) {
++$squareBracketCount;
}
}
++$nextIndex;
}
krsort($variableTokens, SORT_NUMERIC);
foreach ($variableTokens as $distinctVariableSet) {
if (1 === \count($distinctVariableSet['tokens'])) {
$singleVariableIndex = key($distinctVariableSet['tokens']);
$singleVariableToken = current($distinctVariableSet['tokens']);
$tokens->overrideRange($singleVariableIndex, $singleVariableIndex, [
new Token([T_CURLY_OPEN, '{']),
new Token([T_VARIABLE, $singleVariableToken->getContent()]),
new Token([CT::T_CURLY_CLOSE, '}']),
]);
} else {
foreach ($distinctVariableSet['tokens'] as $variablePartIndex => $variablePartToken) {
if ($variablePartToken->isGivenKind(T_NUM_STRING)) {
$tokens[$variablePartIndex] = new Token([T_LNUMBER, $variablePartToken->getContent()]);
continue;
}
if ($variablePartToken->isGivenKind(T_STRING) && $tokens[$variablePartIndex + 1]->equals(']')) {
$tokens[$variablePartIndex] = new Token([T_CONSTANT_ENCAPSED_STRING, "'".$variablePartToken->getContent()."'"]);
}
}
$tokens->insertAt($distinctVariableSet['lastVariableTokenIndex'] + 1, new Token([CT::T_CURLY_CLOSE, '}']));
$tokens->insertAt($distinctVariableSet['firstVariableTokenIndex'], new Token([T_CURLY_OPEN, '{']));
}
}
}
}
private function isStringPartToken(Token $token): bool
{
return $token->isGivenKind(T_ENCAPSED_AND_WHITESPACE)
|| $token->isGivenKind(T_START_HEREDOC)
|| '"' === $token->getContent()
|| 'b"' === strtolower($token->getContent())
;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\StringNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class HeredocToNowdocFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Convert `heredoc` to `nowdoc` where possible.',
[
new CodeSample(
<<<'EOF'
<?php $a = <<<"TEST"
Foo
TEST;
EOF
),
]
);
}
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_START_HEREDOC);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_START_HEREDOC) || str_contains($token->getContent(), "'")) {
continue;
}
if ($tokens[$index + 1]->isGivenKind(T_END_HEREDOC)) {
$tokens[$index] = $this->convertToNowdoc($token);
continue;
}
if (
!$tokens[$index + 1]->isGivenKind(T_ENCAPSED_AND_WHITESPACE)
|| !$tokens[$index + 2]->isGivenKind(T_END_HEREDOC)
) {
continue;
}
$content = $tokens[$index + 1]->getContent();
if (Preg::match('/(?<!\\\\)(?:\\\\{2})*\\\\(?![$\\\\])/', $content)) {
continue;
}
$tokens[$index] = $this->convertToNowdoc($token);
$content = str_replace(['\\\\', '\\$'], ['\\', '$'], $content);
$tokens[$index + 1] = new Token([
$tokens[$index + 1]->getId(),
$content,
]);
}
}
private function convertToNowdoc(Token $token): Token
{
return new Token([
$token->getId(),
Preg::replace('/^([Bb]?<<<)(\h*)"?([^\s"]+)"?/', '$1$2\'$3\'', $token->getContent()),
]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\StringNotation;
use PhpCsFixer\AbstractFunctionReferenceFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class StringLengthToEmptyFixer extends AbstractFunctionReferenceFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'String tests for empty must be done against `\'\'`, not with `strlen`.',
[new CodeSample("<?php \$a = 0 === strlen(\$b) || \\STRLEN(\$c) < 1;\n")],
null,
'Risky when `strlen` is overridden, when called using a `stringable` object, also no longer triggers warning when called using non-string(able).'
);
}
public function getPriority(): int
{
return 1;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$argumentsAnalyzer = new ArgumentsAnalyzer();
foreach ($this->findStrLengthCalls($tokens) as $candidate) {
[$functionNameIndex, $openParenthesisIndex, $closeParenthesisIndex] = $candidate;
$arguments = $argumentsAnalyzer->getArguments($tokens, $openParenthesisIndex, $closeParenthesisIndex);
if (1 !== \count($arguments)) {
continue;
}
$nextIndex = $tokens->getNextMeaningfulToken($closeParenthesisIndex);
$previousIndex = $tokens->getPrevMeaningfulToken($functionNameIndex);
if ($tokens[$previousIndex]->isGivenKind(T_NS_SEPARATOR)) {
$namespaceSeparatorIndex = $previousIndex;
$previousIndex = $tokens->getPrevMeaningfulToken($previousIndex);
} else {
$namespaceSeparatorIndex = null;
}
if ($this->isOperatorOfInterest($tokens[$previousIndex])) {
$operatorIndex = $previousIndex;
$operandIndex = $tokens->getPrevMeaningfulToken($previousIndex);
if (!$this->isOperandOfInterest($tokens[$operandIndex])) {
continue;
}
$replacement = $this->getReplacementYoda($tokens[$operatorIndex], $tokens[$operandIndex]);
if (null === $replacement) {
continue;
}
if ($this->isOfHigherPrecedence($tokens[$nextIndex])) {
continue;
}
if ($this->isOfHigherPrecedence($tokens[$tokens->getPrevMeaningfulToken($operandIndex)])) {
continue;
}
} elseif ($this->isOperatorOfInterest($tokens[$nextIndex])) {
$operatorIndex = $nextIndex;
$operandIndex = $tokens->getNextMeaningfulToken($nextIndex);
if (!$this->isOperandOfInterest($tokens[$operandIndex])) {
continue;
}
$replacement = $this->getReplacementNotYoda($tokens[$operatorIndex], $tokens[$operandIndex]);
if (null === $replacement) {
continue;
}
if ($this->isOfHigherPrecedence($tokens[$tokens->getNextMeaningfulToken($operandIndex)])) {
continue;
}
if ($this->isOfHigherPrecedence($tokens[$previousIndex])) {
continue;
}
} else {
continue;
}
$keepParentheses = $this->keepParentheses($tokens, $openParenthesisIndex, $closeParenthesisIndex);
if (T_IS_IDENTICAL === $replacement) {
$operandContent = '===';
} else {
$operandContent = '!==';
}
$tokens[$operandIndex] = new Token([T_CONSTANT_ENCAPSED_STRING, "''"]);
$tokens[$operatorIndex] = new Token([$replacement, $operandContent]);
if (!$keepParentheses) {
$tokens->clearTokenAndMergeSurroundingWhitespace($closeParenthesisIndex);
$tokens->clearTokenAndMergeSurroundingWhitespace($openParenthesisIndex);
}
$tokens->clearTokenAndMergeSurroundingWhitespace($functionNameIndex);
if (null !== $namespaceSeparatorIndex) {
$tokens->clearTokenAndMergeSurroundingWhitespace($namespaceSeparatorIndex);
}
}
}
private function getReplacementYoda(Token $operator, Token $operand): ?int
{
if ('0' === $operand->getContent()) {
if ($operator->isGivenKind([T_IS_IDENTICAL, T_IS_GREATER_OR_EQUAL])) {
return T_IS_IDENTICAL;
}
if ($operator->isGivenKind(T_IS_NOT_IDENTICAL) || $operator->equals('<')) {
return T_IS_NOT_IDENTICAL;
}
return null;
}
if ($operator->isGivenKind(T_IS_SMALLER_OR_EQUAL)) {
return T_IS_NOT_IDENTICAL;
}
if ($operator->equals('>')) {
return T_IS_IDENTICAL;
}
return null;
}
private function getReplacementNotYoda(Token $operator, Token $operand): ?int
{
if ('0' === $operand->getContent()) {
if ($operator->isGivenKind([T_IS_IDENTICAL, T_IS_SMALLER_OR_EQUAL])) {
return T_IS_IDENTICAL;
}
if ($operator->isGivenKind(T_IS_NOT_IDENTICAL) || $operator->equals('>')) {
return T_IS_NOT_IDENTICAL;
}
return null;
}
if ($operator->isGivenKind(T_IS_GREATER_OR_EQUAL)) {
return T_IS_NOT_IDENTICAL;
}
if ($operator->equals('<')) {
return T_IS_IDENTICAL;
}
return null;
}
private function isOperandOfInterest(Token $token): bool
{
if (!$token->isGivenKind(T_LNUMBER)) {
return false;
}
$content = $token->getContent();
return '0' === $content || '1' === $content;
}
private function isOperatorOfInterest(Token $token): bool
{
return
$token->isGivenKind([T_IS_IDENTICAL, T_IS_NOT_IDENTICAL, T_IS_SMALLER_OR_EQUAL, T_IS_GREATER_OR_EQUAL])
|| $token->equals('<') || $token->equals('>')
;
}
private function isOfHigherPrecedence(Token $token): bool
{
static $operatorsPerContent = [
'!',
'%',
'*',
'+',
'-',
'.',
'/',
'~',
'?',
];
return $token->isGivenKind([T_INSTANCEOF, T_POW, T_SL, T_SR]) || $token->equalsAny($operatorsPerContent);
}
private function keepParentheses(Tokens $tokens, int $openParenthesisIndex, int $closeParenthesisIndex): bool
{
$i = $tokens->getNextMeaningfulToken($openParenthesisIndex);
if ($tokens[$i]->isCast()) {
$i = $tokens->getNextMeaningfulToken($i);
}
for (; $i < $closeParenthesisIndex; ++$i) {
$token = $tokens[$i];
if ($token->isGivenKind([T_VARIABLE, T_STRING]) || $token->isObjectOperator() || $token->isWhitespace() || $token->isComment()) {
continue;
}
$blockType = Tokens::detectBlockType($token);
if (null !== $blockType && $blockType['isStart']) {
$i = $tokens->findBlockEnd($blockType['type'], $i);
continue;
}
return true;
}
return false;
}
private function findStrLengthCalls(Tokens $tokens): \Generator
{
$candidates = [];
$count = \count($tokens);
for ($i = 0; $i < $count; ++$i) {
$candidate = $this->find('strlen', $tokens, $i, $count);
if (null === $candidate) {
break;
}
$i = $candidate[1];
$candidates[] = $candidate;
}
foreach (array_reverse($candidates) as $candidate) {
yield $candidate;
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\StringNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class SingleQuoteFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
$codeSample = <<<'EOF'
<?php
$a = "sample";
$b = "sample with 'single-quotes'";
EOF;
return new FixerDefinition(
'Convert double quotes to single quotes for simple strings.',
[
new CodeSample($codeSample),
new CodeSample(
$codeSample,
['strings_containing_single_quote_chars' => true]
),
]
);
}
public function getPriority(): int
{
return 10;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_CONSTANT_ENCAPSED_STRING);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_CONSTANT_ENCAPSED_STRING)) {
continue;
}
$content = $token->getContent();
$prefix = '';
if ('b' === strtolower($content[0])) {
$prefix = $content[0];
$content = substr($content, 1);
}
if (
'"' === $content[0]
&& (true === $this->configuration['strings_containing_single_quote_chars'] || !str_contains($content, "'"))
&& !Preg::match('/(?<!\\\\)(?:\\\\{2})*\\\\(?!["$\\\\])/', $content)
) {
$content = substr($content, 1, -1);
$content = str_replace(['\\"', '\\$', '\''], ['"', '$', '\\\''], $content);
$tokens[$index] = new Token([T_CONSTANT_ENCAPSED_STRING, $prefix.'\''.$content.'\'']);
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('strings_containing_single_quote_chars', 'Whether to fix double-quoted strings that contains single-quotes.'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\StringNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class EscapeImplicitBackslashesFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
$codeSample = <<<'EOF'
<?php
$singleQuoted = 'String with \" and My\Prefix\\';
$doubleQuoted = "Interpret my \n but not my \a";
$hereDoc = <<<HEREDOC
Interpret my \100 but not my \999
HEREDOC;
EOF;
return new FixerDefinition(
'Escape implicit backslashes in strings and heredocs to ease the understanding of which are special chars interpreted by PHP and which not.',
[
new CodeSample($codeSample),
new CodeSample(
$codeSample,
['single_quoted' => true]
),
new CodeSample(
$codeSample,
['double_quoted' => false]
),
new CodeSample(
$codeSample,
['heredoc_syntax' => false]
),
],
'In PHP double-quoted strings and heredocs some chars like `n`, `$` or `u` have special meanings if preceded by a backslash '
.'(and some are special only if followed by other special chars), while a backslash preceding other chars are interpreted like a plain '
.'backslash. The precise list of those special chars is hard to remember and to identify quickly: this fixer escapes backslashes '
."that do not start a special interpretation with the char after them.\n"
.'It is possible to fix also single-quoted strings: in this case there is no special chars apart from single-quote and backslash '
.'itself, so the fixer simply ensure that all backslashes are escaped. Both single and double backslashes are allowed in single-quoted '
.'strings, so the purpose in this context is mainly to have a uniformed way to have them written all over the codebase.'
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_ENCAPSED_AND_WHITESPACE, T_CONSTANT_ENCAPSED_STRING]);
}
public function getPriority(): int
{
return 15;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
static $singleQuotedRegex = '/(?<!\\\\)\\\\((?:\\\\\\\\)*)(?![\\\'\\\\])/';
static $doubleQuotedRegex = '/(?<!\\\\)\\\\((?:\\\\\\\\)*)(?![efnrtv$"\\\\0-7]|x[0-9A-Fa-f]|u{)/';
static $heredocSyntaxRegex = '/(?<!\\\\)\\\\((?:\\\\\\\\)*)(?![efnrtv$\\\\0-7]|x[0-9A-Fa-f]|u{)/';
$doubleQuoteOpened = false;
foreach ($tokens as $index => $token) {
$content = $token->getContent();
if ($token->equalsAny(['"', 'b"', 'B"'])) {
$doubleQuoteOpened = !$doubleQuoteOpened;
}
if (!$token->isGivenKind([T_ENCAPSED_AND_WHITESPACE, T_CONSTANT_ENCAPSED_STRING]) || !str_contains($content, '\\')) {
continue;
}
if ($token->isGivenKind(T_ENCAPSED_AND_WHITESPACE) && '\'' === substr(rtrim($tokens[$index - 1]->getContent()), -1)) {
continue;
}
$firstTwoCharacters = strtolower(substr($content, 0, 2));
$isSingleQuotedString = $token->isGivenKind(T_CONSTANT_ENCAPSED_STRING) && ('\'' === $content[0] || 'b\'' === $firstTwoCharacters);
$isDoubleQuotedString =
($token->isGivenKind(T_CONSTANT_ENCAPSED_STRING) && ('"' === $content[0] || 'b"' === $firstTwoCharacters))
|| ($token->isGivenKind(T_ENCAPSED_AND_WHITESPACE) && $doubleQuoteOpened)
;
$isHeredocSyntax = !$isSingleQuotedString && !$isDoubleQuotedString;
if (
(false === $this->configuration['single_quoted'] && $isSingleQuotedString)
|| (false === $this->configuration['double_quoted'] && $isDoubleQuotedString)
|| (false === $this->configuration['heredoc_syntax'] && $isHeredocSyntax)
) {
continue;
}
$regex = $heredocSyntaxRegex;
if ($isSingleQuotedString) {
$regex = $singleQuotedRegex;
} elseif ($isDoubleQuotedString) {
$regex = $doubleQuotedRegex;
}
$newContent = Preg::replace($regex, '\\\\\\\\$1', $content);
if ($newContent !== $content) {
$tokens[$index] = new Token([$token->getId(), $newContent]);
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('single_quoted', 'Whether to fix single-quoted strings.'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
(new FixerOptionBuilder('double_quoted', 'Whether to fix double-quoted strings.'))
->setAllowedTypes(['bool'])
->setDefault(true)
->getOption(),
(new FixerOptionBuilder('heredoc_syntax', 'Whether to fix heredoc syntax.'))
->setAllowedTypes(['bool'])
->setDefault(true)
->getOption(),
]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\StringNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class StringLineEndingFixer extends AbstractFixer implements WhitespacesAwareFixerInterface
{
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_CONSTANT_ENCAPSED_STRING, T_ENCAPSED_AND_WHITESPACE, T_INLINE_HTML]);
}
public function isRisky(): bool
{
return true;
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'All multi-line strings must use correct line ending.',
[
new CodeSample(
"<?php \$a = 'my\r\nmulti\nline\r\nstring';\r\n"
),
],
null,
'Changing the line endings of multi-line strings might affect string comparisons and outputs.'
);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$ending = $this->whitespacesConfig->getLineEnding();
foreach ($tokens as $tokenIndex => $token) {
if (!$token->isGivenKind([T_CONSTANT_ENCAPSED_STRING, T_ENCAPSED_AND_WHITESPACE, T_INLINE_HTML])) {
continue;
}
$tokens[$tokenIndex] = new Token([
$token->getId(),
Preg::replace(
'#\R#u',
$ending,
$token->getContent()
),
]);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\DoctrineAnnotation;
use Doctrine\Common\Annotations\DocLexer;
use PhpCsFixer\AbstractDoctrineAnnotationFixer;
use PhpCsFixer\Doctrine\Annotation\Tokens;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
final class DoctrineAnnotationIndentationFixer extends AbstractDoctrineAnnotationFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Doctrine annotations must be indented with four spaces.',
[
new CodeSample("<?php\n/**\n * @Foo(\n * foo=\"foo\"\n * )\n */\nclass Bar {}\n"),
new CodeSample(
"<?php\n/**\n * @Foo({@Bar,\n * @Baz})\n */\nclass Bar {}\n",
['indent_mixed_lines' => true]
),
]
);
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver(array_merge(
parent::createConfigurationDefinition()->getOptions(),
[
(new FixerOptionBuilder('indent_mixed_lines', 'Whether to indent lines that have content before closing parenthesis.'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
]
));
}
protected function fixAnnotations(Tokens $doctrineAnnotationTokens): void
{
$annotationPositions = [];
for ($index = 0, $max = \count($doctrineAnnotationTokens); $index < $max; ++$index) {
if (!$doctrineAnnotationTokens[$index]->isType(DocLexer::T_AT)) {
continue;
}
$annotationEndIndex = $doctrineAnnotationTokens->getAnnotationEnd($index);
if (null === $annotationEndIndex) {
return;
}
$annotationPositions[] = [$index, $annotationEndIndex];
$index = $annotationEndIndex;
}
$indentLevel = 0;
foreach ($doctrineAnnotationTokens as $index => $token) {
if (!$token->isType(DocLexer::T_NONE) || !str_contains($token->getContent(), "\n")) {
continue;
}
if (!$this->indentationCanBeFixed($doctrineAnnotationTokens, $index, $annotationPositions)) {
continue;
}
$braces = $this->getLineBracesCount($doctrineAnnotationTokens, $index);
$delta = $braces[0] - $braces[1];
$mixedBraces = 0 === $delta && $braces[0] > 0;
$extraIndentLevel = 0;
if ($indentLevel > 0 && ($delta < 0 || $mixedBraces)) {
--$indentLevel;
if (true === $this->configuration['indent_mixed_lines'] && $this->isClosingLineWithMeaningfulContent($doctrineAnnotationTokens, $index)) {
$extraIndentLevel = 1;
}
}
$token->setContent(Preg::replace(
'/(\n( +\*)?) *$/',
'$1'.str_repeat(' ', 4 * ($indentLevel + $extraIndentLevel) + 1),
$token->getContent()
));
if ($delta > 0 || $mixedBraces) {
++$indentLevel;
}
}
}
private function getLineBracesCount(Tokens $tokens, int $index): array
{
$opening = 0;
$closing = 0;
while (isset($tokens[++$index])) {
$token = $tokens[$index];
if ($token->isType(DocLexer::T_NONE) && str_contains($token->getContent(), "\n")) {
break;
}
if ($token->isType([DocLexer::T_OPEN_PARENTHESIS, DocLexer::T_OPEN_CURLY_BRACES])) {
++$opening;
continue;
}
if (!$token->isType([DocLexer::T_CLOSE_PARENTHESIS, DocLexer::T_CLOSE_CURLY_BRACES])) {
continue;
}
if ($opening > 0) {
--$opening;
} else {
++$closing;
}
}
return [$opening, $closing];
}
private function isClosingLineWithMeaningfulContent(Tokens $tokens, int $index): bool
{
while (isset($tokens[++$index])) {
$token = $tokens[$index];
if ($token->isType(DocLexer::T_NONE)) {
if (str_contains($token->getContent(), "\n")) {
return false;
}
continue;
}
return !$token->isType([DocLexer::T_CLOSE_PARENTHESIS, DocLexer::T_CLOSE_CURLY_BRACES]);
}
return false;
}
private function indentationCanBeFixed(Tokens $tokens, int $newLineTokenIndex, array $annotationPositions): bool
{
foreach ($annotationPositions as $position) {
if ($newLineTokenIndex >= $position[0] && $newLineTokenIndex <= $position[1]) {
return true;
}
}
for ($index = $newLineTokenIndex + 1, $max = \count($tokens); $index < $max; ++$index) {
$token = $tokens[$index];
if (str_contains($token->getContent(), "\n")) {
return false;
}
return $tokens[$index]->isType(DocLexer::T_AT);
}
return false;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\DoctrineAnnotation;
use Doctrine\Common\Annotations\DocLexer;
use PhpCsFixer\AbstractDoctrineAnnotationFixer;
use PhpCsFixer\Doctrine\Annotation\Tokens;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
final class DoctrineAnnotationArrayAssignmentFixer extends AbstractDoctrineAnnotationFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Doctrine annotations must use configured operator for assignment in arrays.',
[
new CodeSample(
"<?php\n/**\n * @Foo({bar : \"baz\"})\n */\nclass Bar {}\n"
),
new CodeSample(
"<?php\n/**\n * @Foo({bar = \"baz\"})\n */\nclass Bar {}\n",
['operator' => ':']
),
]
);
}
public function getPriority(): int
{
return 1;
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
$options = parent::createConfigurationDefinition()->getOptions();
$operator = new FixerOptionBuilder('operator', 'The operator to use.');
$options[] = $operator
->setAllowedValues(['=', ':'])
->setDefault('=')
->getOption()
;
return new FixerConfigurationResolver($options);
}
protected function fixAnnotations(Tokens $doctrineAnnotationTokens): void
{
$scopes = [];
foreach ($doctrineAnnotationTokens as $token) {
if ($token->isType(DocLexer::T_OPEN_PARENTHESIS)) {
$scopes[] = 'annotation';
continue;
}
if ($token->isType(DocLexer::T_OPEN_CURLY_BRACES)) {
$scopes[] = 'array';
continue;
}
if ($token->isType([DocLexer::T_CLOSE_PARENTHESIS, DocLexer::T_CLOSE_CURLY_BRACES])) {
array_pop($scopes);
continue;
}
if ('array' === end($scopes) && $token->isType([DocLexer::T_EQUALS, DocLexer::T_COLON])) {
$token->setContent($this->configuration['operator']);
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\DoctrineAnnotation;
use Doctrine\Common\Annotations\DocLexer;
use PhpCsFixer\AbstractDoctrineAnnotationFixer;
use PhpCsFixer\Doctrine\Annotation\Token;
use PhpCsFixer\Doctrine\Annotation\Tokens;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
final class DoctrineAnnotationSpacesFixer extends AbstractDoctrineAnnotationFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Fixes spaces in Doctrine annotations.',
[
new CodeSample(
"<?php\n/**\n * @Foo ( )\n */\nclass Bar {}\n\n/**\n * @Foo(\"bar\" ,\"baz\")\n */\nclass Bar2 {}\n\n/**\n * @Foo(foo = \"foo\", bar = {\"foo\":\"foo\", \"bar\"=\"bar\"})\n */\nclass Bar3 {}\n"
),
new CodeSample(
"<?php\n/**\n * @Foo(foo = \"foo\", bar = {\"foo\":\"foo\", \"bar\"=\"bar\"})\n */\nclass Bar {}\n",
['after_array_assignments_equals' => false, 'before_array_assignments_equals' => false]
),
],
'There must not be any space around parentheses; commas must be preceded by no space and followed by one space; there must be no space around named arguments assignment operator; there must be one space around array assignment operator.'
);
}
public function getPriority(): int
{
return 0;
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver(array_merge(
parent::createConfigurationDefinition()->getOptions(),
[
(new FixerOptionBuilder('around_parentheses', 'Whether to fix spaces around parentheses.'))
->setAllowedTypes(['bool'])
->setDefault(true)
->getOption(),
(new FixerOptionBuilder('around_commas', 'Whether to fix spaces around commas.'))
->setAllowedTypes(['bool'])
->setDefault(true)
->getOption(),
(new FixerOptionBuilder('before_argument_assignments', 'Whether to add, remove or ignore spaces before argument assignment operator.'))
->setAllowedTypes(['null', 'bool'])
->setDefault(false)
->getOption(),
(new FixerOptionBuilder('after_argument_assignments', 'Whether to add, remove or ignore spaces after argument assignment operator.'))
->setAllowedTypes(['null', 'bool'])
->setDefault(false)
->getOption(),
(new FixerOptionBuilder('before_array_assignments_equals', 'Whether to add, remove or ignore spaces before array `=` assignment operator.'))
->setAllowedTypes(['null', 'bool'])
->setDefault(true)
->getOption(),
(new FixerOptionBuilder('after_array_assignments_equals', 'Whether to add, remove or ignore spaces after array assignment `=` operator.'))
->setAllowedTypes(['null', 'bool'])
->setDefault(true)
->getOption(),
(new FixerOptionBuilder('before_array_assignments_colon', 'Whether to add, remove or ignore spaces before array `:` assignment operator.'))
->setAllowedTypes(['null', 'bool'])
->setDefault(true)
->getOption(),
(new FixerOptionBuilder('after_array_assignments_colon', 'Whether to add, remove or ignore spaces after array assignment `:` operator.'))
->setAllowedTypes(['null', 'bool'])
->setDefault(true)
->getOption(),
]
));
}
protected function fixAnnotations(Tokens $doctrineAnnotationTokens): void
{
if (true === $this->configuration['around_parentheses']) {
$this->fixSpacesAroundParentheses($doctrineAnnotationTokens);
}
if (true === $this->configuration['around_commas']) {
$this->fixSpacesAroundCommas($doctrineAnnotationTokens);
}
if (
null !== $this->configuration['before_argument_assignments']
|| null !== $this->configuration['after_argument_assignments']
|| null !== $this->configuration['before_array_assignments_equals']
|| null !== $this->configuration['after_array_assignments_equals']
|| null !== $this->configuration['before_array_assignments_colon']
|| null !== $this->configuration['after_array_assignments_colon']
) {
$this->fixAroundAssignments($doctrineAnnotationTokens);
}
}
private function fixSpacesAroundParentheses(Tokens $tokens): void
{
$inAnnotationUntilIndex = null;
foreach ($tokens as $index => $token) {
if (null !== $inAnnotationUntilIndex) {
if ($index === $inAnnotationUntilIndex) {
$inAnnotationUntilIndex = null;
continue;
}
} elseif ($tokens[$index]->isType(DocLexer::T_AT)) {
$endIndex = $tokens->getAnnotationEnd($index);
if (null !== $endIndex) {
$inAnnotationUntilIndex = $endIndex + 1;
}
continue;
}
if (null === $inAnnotationUntilIndex) {
continue;
}
if (!$token->isType([DocLexer::T_OPEN_PARENTHESIS, DocLexer::T_CLOSE_PARENTHESIS])) {
continue;
}
if ($token->isType(DocLexer::T_OPEN_PARENTHESIS)) {
$token = $tokens[$index - 1];
if ($token->isType(DocLexer::T_NONE)) {
$token->clear();
}
$token = $tokens[$index + 1];
} else {
$token = $tokens[$index - 1];
}
if ($token->isType(DocLexer::T_NONE)) {
if (str_contains($token->getContent(), "\n")) {
continue;
}
$token->clear();
}
}
}
private function fixSpacesAroundCommas(Tokens $tokens): void
{
$inAnnotationUntilIndex = null;
foreach ($tokens as $index => $token) {
if (null !== $inAnnotationUntilIndex) {
if ($index === $inAnnotationUntilIndex) {
$inAnnotationUntilIndex = null;
continue;
}
} elseif ($tokens[$index]->isType(DocLexer::T_AT)) {
$endIndex = $tokens->getAnnotationEnd($index);
if (null !== $endIndex) {
$inAnnotationUntilIndex = $endIndex;
}
continue;
}
if (null === $inAnnotationUntilIndex) {
continue;
}
if (!$token->isType(DocLexer::T_COMMA)) {
continue;
}
$token = $tokens[$index - 1];
if ($token->isType(DocLexer::T_NONE)) {
$token->clear();
}
if ($index < \count($tokens) - 1 && !Preg::match('/^\s/', $tokens[$index + 1]->getContent())) {
$tokens->insertAt($index + 1, new Token(DocLexer::T_NONE, ' '));
}
}
}
private function fixAroundAssignments(Tokens $tokens): void
{
$beforeArguments = $this->configuration['before_argument_assignments'];
$afterArguments = $this->configuration['after_argument_assignments'];
$beforeArraysEquals = $this->configuration['before_array_assignments_equals'];
$afterArraysEquals = $this->configuration['after_array_assignments_equals'];
$beforeArraysColon = $this->configuration['before_array_assignments_colon'];
$afterArraysColon = $this->configuration['after_array_assignments_colon'];
$scopes = [];
foreach ($tokens as $index => $token) {
$endScopeType = end($scopes);
if (false !== $endScopeType && $token->isType($endScopeType)) {
array_pop($scopes);
continue;
}
if ($tokens[$index]->isType(DocLexer::T_AT)) {
$scopes[] = DocLexer::T_CLOSE_PARENTHESIS;
continue;
}
if ($tokens[$index]->isType(DocLexer::T_OPEN_CURLY_BRACES)) {
$scopes[] = DocLexer::T_CLOSE_CURLY_BRACES;
continue;
}
if (DocLexer::T_CLOSE_PARENTHESIS === $endScopeType && $token->isType(DocLexer::T_EQUALS)) {
$this->updateSpacesAfter($tokens, $index, $afterArguments);
$this->updateSpacesBefore($tokens, $index, $beforeArguments);
continue;
}
if (DocLexer::T_CLOSE_CURLY_BRACES === $endScopeType) {
if ($token->isType(DocLexer::T_EQUALS)) {
$this->updateSpacesAfter($tokens, $index, $afterArraysEquals);
$this->updateSpacesBefore($tokens, $index, $beforeArraysEquals);
continue;
}
if ($token->isType(DocLexer::T_COLON)) {
$this->updateSpacesAfter($tokens, $index, $afterArraysColon);
$this->updateSpacesBefore($tokens, $index, $beforeArraysColon);
}
}
}
}
private function updateSpacesAfter(Tokens $tokens, int $index, ?bool $insert): void
{
$this->updateSpacesAt($tokens, $index + 1, $index + 1, $insert);
}
private function updateSpacesBefore(Tokens $tokens, int $index, ?bool $insert): void
{
$this->updateSpacesAt($tokens, $index - 1, $index, $insert);
}
private function updateSpacesAt(Tokens $tokens, int $index, int $insertIndex, ?bool $insert): void
{
if (null === $insert) {
return;
}
$token = $tokens[$index];
if ($insert) {
if (!$token->isType(DocLexer::T_NONE)) {
$tokens->insertAt($insertIndex, $token = new Token());
}
$token->setContent(' ');
} elseif ($token->isType(DocLexer::T_NONE)) {
$token->clear();
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\DoctrineAnnotation;
use Doctrine\Common\Annotations\DocLexer;
use PhpCsFixer\AbstractDoctrineAnnotationFixer;
use PhpCsFixer\Doctrine\Annotation\Token;
use PhpCsFixer\Doctrine\Annotation\Tokens;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
final class DoctrineAnnotationBracesFixer extends AbstractDoctrineAnnotationFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Doctrine annotations without arguments must use the configured syntax.',
[
new CodeSample(
"<?php\n/**\n * @Foo()\n */\nclass Bar {}\n"
),
new CodeSample(
"<?php\n/**\n * @Foo\n */\nclass Bar {}\n",
['syntax' => 'with_braces']
),
]
);
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver(array_merge(
parent::createConfigurationDefinition()->getOptions(),
[
(new FixerOptionBuilder('syntax', 'Whether to add or remove braces.'))
->setAllowedValues(['with_braces', 'without_braces'])
->setDefault('without_braces')
->getOption(),
]
));
}
protected function fixAnnotations(Tokens $doctrineAnnotationTokens): void
{
if ('without_braces' === $this->configuration['syntax']) {
$this->removesBracesFromAnnotations($doctrineAnnotationTokens);
} else {
$this->addBracesToAnnotations($doctrineAnnotationTokens);
}
}
private function addBracesToAnnotations(Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$tokens[$index]->isType(DocLexer::T_AT)) {
continue;
}
$braceIndex = $tokens->getNextMeaningfulToken($index + 1);
if (null !== $braceIndex && $tokens[$braceIndex]->isType(DocLexer::T_OPEN_PARENTHESIS)) {
continue;
}
$tokens->insertAt($index + 2, new Token(DocLexer::T_OPEN_PARENTHESIS, '('));
$tokens->insertAt($index + 3, new Token(DocLexer::T_CLOSE_PARENTHESIS, ')'));
}
}
private function removesBracesFromAnnotations(Tokens $tokens): void
{
for ($index = 0, $max = \count($tokens); $index < $max; ++$index) {
if (!$tokens[$index]->isType(DocLexer::T_AT)) {
continue;
}
$openBraceIndex = $tokens->getNextMeaningfulToken($index + 1);
if (null === $openBraceIndex) {
continue;
}
if (!$tokens[$openBraceIndex]->isType(DocLexer::T_OPEN_PARENTHESIS)) {
continue;
}
$closeBraceIndex = $tokens->getNextMeaningfulToken($openBraceIndex);
if (null === $closeBraceIndex) {
continue;
}
if (!$tokens[$closeBraceIndex]->isType(DocLexer::T_CLOSE_PARENTHESIS)) {
continue;
}
for ($currentIndex = $index + 2; $currentIndex <= $closeBraceIndex; ++$currentIndex) {
$tokens[$currentIndex]->clear();
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\PhpUnit;
use Composer\Semver\Comparator;
final class PhpUnitTargetVersion
{
public const VERSION_3_0 = '3.0';
public const VERSION_3_2 = '3.2';
public const VERSION_3_5 = '3.5';
public const VERSION_4_3 = '4.3';
public const VERSION_4_8 = '4.8';
public const VERSION_5_0 = '5.0';
public const VERSION_5_2 = '5.2';
public const VERSION_5_4 = '5.4';
public const VERSION_5_5 = '5.5';
public const VERSION_5_6 = '5.6';
public const VERSION_5_7 = '5.7';
public const VERSION_6_0 = '6.0';
public const VERSION_7_5 = '7.5';
public const VERSION_8_4 = '8.4';
public const VERSION_NEWEST = 'newest';
private function __construct()
{
}
public static function fulfills(string $candidate, string $target): bool
{
if (self::VERSION_NEWEST === $target) {
throw new \LogicException(sprintf('Parameter `target` shall not be provided as "%s", determine proper target for tested PHPUnit feature instead.', self::VERSION_NEWEST));
}
if (self::VERSION_NEWEST === $candidate) {
return true;
}
return Comparator::greaterThanOrEqualTo($candidate, $target);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\PhpUnit;
use PhpCsFixer\Fixer\AbstractPhpUnitFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpUnitStrictFixer extends AbstractPhpUnitFixer implements ConfigurableFixerInterface
{
private static array $assertionMap = [
'assertAttributeEquals' => 'assertAttributeSame',
'assertAttributeNotEquals' => 'assertAttributeNotSame',
'assertEquals' => 'assertSame',
'assertNotEquals' => 'assertNotSame',
];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'PHPUnit methods like `assertSame` should be used instead of `assertEquals`.',
[
new CodeSample(
'<?php
final class MyTest extends \PHPUnit_Framework_TestCase
{
public function testSomeTest()
{
$this->assertAttributeEquals(a(), b());
$this->assertAttributeNotEquals(a(), b());
$this->assertEquals(a(), b());
$this->assertNotEquals(a(), b());
}
}
'
),
new CodeSample(
'<?php
final class MyTest extends \PHPUnit_Framework_TestCase
{
public function testSomeTest()
{
$this->assertAttributeEquals(a(), b());
$this->assertAttributeNotEquals(a(), b());
$this->assertEquals(a(), b());
$this->assertNotEquals(a(), b());
}
}
',
['assertions' => ['assertEquals']]
),
],
null,
'Risky when any of the functions are overridden or when testing object equality.'
);
}
public function isRisky(): bool
{
return true;
}
protected function applyPhpUnitClassFix(Tokens $tokens, int $startIndex, int $endIndex): void
{
$argumentsAnalyzer = new ArgumentsAnalyzer();
$functionsAnalyzer = new FunctionsAnalyzer();
foreach ($this->configuration['assertions'] as $methodBefore) {
$methodAfter = self::$assertionMap[$methodBefore];
for ($index = $startIndex; $index < $endIndex; ++$index) {
$methodIndex = $tokens->getNextTokenOfKind($index, [[T_STRING, $methodBefore]]);
if (null === $methodIndex) {
break;
}
if (!$functionsAnalyzer->isTheSameClassCall($tokens, $methodIndex)) {
continue;
}
$openingParenthesisIndex = $tokens->getNextMeaningfulToken($methodIndex);
$argumentsCount = $argumentsAnalyzer->countArguments(
$tokens,
$openingParenthesisIndex,
$tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openingParenthesisIndex)
);
if (2 === $argumentsCount || 3 === $argumentsCount) {
$tokens[$methodIndex] = new Token([T_STRING, $methodAfter]);
}
$index = $methodIndex;
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('assertions', 'List of assertion methods to fix.'))
->setAllowedTypes(['array'])
->setAllowedValues([new AllowedValueSubset(array_keys(self::$assertionMap))])
->setDefault([
'assertAttributeEquals',
'assertAttributeNotEquals',
'assertEquals',
'assertNotEquals',
])
->getOption(),
]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\PhpUnit;
use PhpCsFixer\Fixer\AbstractPhpUnitFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpUnitDedicateAssertFixer extends AbstractPhpUnitFixer implements ConfigurableFixerInterface
{
private static array $fixMap = [
'array_key_exists' => [
'positive' => 'assertArrayHasKey',
'negative' => 'assertArrayNotHasKey',
'argument_count' => 2,
],
'empty' => [
'positive' => 'assertEmpty',
'negative' => 'assertNotEmpty',
],
'file_exists' => [
'positive' => 'assertFileExists',
'negative' => 'assertFileNotExists',
],
'is_array' => true,
'is_bool' => true,
'is_callable' => true,
'is_dir' => [
'positive' => 'assertDirectoryExists',
'negative' => 'assertDirectoryNotExists',
],
'is_double' => true,
'is_float' => true,
'is_infinite' => [
'positive' => 'assertInfinite',
'negative' => 'assertFinite',
],
'is_int' => true,
'is_integer' => true,
'is_long' => true,
'is_nan' => [
'positive' => 'assertNan',
'negative' => false,
],
'is_null' => [
'positive' => 'assertNull',
'negative' => 'assertNotNull',
],
'is_numeric' => true,
'is_object' => true,
'is_readable' => [
'positive' => 'assertIsReadable',
'negative' => 'assertNotIsReadable',
],
'is_real' => true,
'is_resource' => true,
'is_scalar' => true,
'is_string' => true,
'is_writable' => [
'positive' => 'assertIsWritable',
'negative' => 'assertNotIsWritable',
],
'str_contains' => [
'positive' => 'assertStringContainsString',
'negative' => 'assertStringNotContainsString',
'argument_count' => 2,
'swap_arguments' => true,
],
'str_ends_with' => [
'positive' => 'assertStringEndsWith',
'negative' => 'assertStringEndsNotWith',
'argument_count' => 2,
'swap_arguments' => true,
],
'str_starts_with' => [
'positive' => 'assertStringStartsWith',
'negative' => 'assertStringStartsNotWith',
'argument_count' => 2,
'swap_arguments' => true,
],
];
private array $functions = [];
public function configure(array $configuration): void
{
parent::configure($configuration);
$this->functions = [
'array_key_exists',
'file_exists',
'is_null',
'str_ends_with',
'str_starts_with',
];
if (PhpUnitTargetVersion::fulfills($this->configuration['target'], PhpUnitTargetVersion::VERSION_3_5)) {
$this->functions = array_merge($this->functions, [
'empty',
'is_array',
'is_bool',
'is_boolean',
'is_callable',
'is_double',
'is_float',
'is_int',
'is_integer',
'is_long',
'is_numeric',
'is_object',
'is_real',
'is_scalar',
'is_string',
]);
}
if (PhpUnitTargetVersion::fulfills($this->configuration['target'], PhpUnitTargetVersion::VERSION_5_0)) {
$this->functions = array_merge($this->functions, [
'is_infinite',
'is_nan',
]);
}
if (PhpUnitTargetVersion::fulfills($this->configuration['target'], PhpUnitTargetVersion::VERSION_5_6)) {
$this->functions = array_merge($this->functions, [
'is_dir',
'is_readable',
'is_writable',
]);
}
if (PhpUnitTargetVersion::fulfills($this->configuration['target'], PhpUnitTargetVersion::VERSION_7_5)) {
$this->functions = array_merge($this->functions, [
'str_contains',
]);
}
}
public function isRisky(): bool
{
return true;
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'PHPUnit assertions like `assertInternalType`, `assertFileExists`, should be used over `assertTrue`.',
[
new CodeSample(
'<?php
final class MyTest extends \PHPUnit_Framework_TestCase
{
public function testSomeTest()
{
$this->assertTrue(is_float( $a), "my message");
$this->assertTrue(is_nan($a));
}
}
'
),
new CodeSample(
'<?php
final class MyTest extends \PHPUnit_Framework_TestCase
{
public function testSomeTest()
{
$this->assertTrue(is_dir($a));
$this->assertTrue(is_writable($a));
$this->assertTrue(is_readable($a));
}
}
',
['target' => PhpUnitTargetVersion::VERSION_5_6]
),
],
null,
'Fixer could be risky if one is overriding PHPUnit\'s native methods.'
);
}
public function getPriority(): int
{
return -9;
}
protected function applyPhpUnitClassFix(Tokens $tokens, int $startIndex, int $endIndex): void
{
$argumentsAnalyzer = new ArgumentsAnalyzer();
foreach ($this->getPreviousAssertCall($tokens, $startIndex, $endIndex) as $assertCall) {
if ('asserttrue' === $assertCall['loweredName'] || 'assertfalse' === $assertCall['loweredName']) {
$this->fixAssertTrueFalse($tokens, $argumentsAnalyzer, $assertCall);
continue;
}
if (
'assertsame' === $assertCall['loweredName']
|| 'assertnotsame' === $assertCall['loweredName']
|| 'assertequals' === $assertCall['loweredName']
|| 'assertnotequals' === $assertCall['loweredName']
) {
$this->fixAssertSameEquals($tokens, $assertCall);
continue;
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('target', 'Target version of PHPUnit.'))
->setAllowedTypes(['string'])
->setAllowedValues([
PhpUnitTargetVersion::VERSION_3_0,
PhpUnitTargetVersion::VERSION_3_5,
PhpUnitTargetVersion::VERSION_5_0,
PhpUnitTargetVersion::VERSION_5_6,
PhpUnitTargetVersion::VERSION_NEWEST,
])
->setDefault(PhpUnitTargetVersion::VERSION_NEWEST)
->getOption(),
]);
}
private function fixAssertTrueFalse(Tokens $tokens, ArgumentsAnalyzer $argumentsAnalyzer, array $assertCall): void
{
$testDefaultNamespaceTokenIndex = null;
$testIndex = $tokens->getNextMeaningfulToken($assertCall['openBraceIndex']);
if (!$tokens[$testIndex]->isGivenKind([T_EMPTY, T_STRING])) {
if ($this->fixAssertTrueFalseInstanceof($tokens, $assertCall, $testIndex)) {
return;
}
if (!$tokens[$testIndex]->isGivenKind(T_NS_SEPARATOR)) {
return;
}
$testDefaultNamespaceTokenIndex = $testIndex;
$testIndex = $tokens->getNextMeaningfulToken($testIndex);
}
$testOpenIndex = $tokens->getNextMeaningfulToken($testIndex);
if (!$tokens[$testOpenIndex]->equals('(')) {
return;
}
$testCloseIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $testOpenIndex);
$assertCallCloseIndex = $tokens->getNextMeaningfulToken($testCloseIndex);
if (!$tokens[$assertCallCloseIndex]->equalsAny([')', ','])) {
return;
}
$content = strtolower($tokens[$testIndex]->getContent());
if (!\in_array($content, $this->functions, true)) {
return;
}
$arguments = $argumentsAnalyzer->getArguments($tokens, $testOpenIndex, $testCloseIndex);
$isPositive = 'asserttrue' === $assertCall['loweredName'];
if (\is_array(self::$fixMap[$content])) {
$expectedCount = self::$fixMap[$content]['argument_count'] ?? 1;
if ($expectedCount !== \count($arguments)) {
return;
}
$isPositive = $isPositive ? 'positive' : 'negative';
if (false === self::$fixMap[$content][$isPositive]) {
return;
}
$tokens[$assertCall['index']] = new Token([T_STRING, self::$fixMap[$content][$isPositive]]);
$this->removeFunctionCall($tokens, $testDefaultNamespaceTokenIndex, $testIndex, $testOpenIndex, $testCloseIndex);
if (self::$fixMap[$content]['swap_arguments'] ?? false) {
if (2 !== $expectedCount) {
throw new \RuntimeException('Can only swap two arguments, please update map or logic.');
}
$this->swapArguments($tokens, $arguments);
}
return;
}
if (1 !== \count($arguments)) {
return;
}
$type = substr($content, 3);
$tokens[$assertCall['index']] = new Token([T_STRING, $isPositive ? 'assertInternalType' : 'assertNotInternalType']);
$tokens[$testIndex] = new Token([T_CONSTANT_ENCAPSED_STRING, "'".$type."'"]);
$tokens[$testOpenIndex] = new Token(',');
$tokens->clearTokenAndMergeSurroundingWhitespace($testCloseIndex);
$commaIndex = $tokens->getPrevMeaningfulToken($testCloseIndex);
if ($tokens[$commaIndex]->equals(',')) {
$tokens->removeTrailingWhitespace($commaIndex);
$tokens->clearAt($commaIndex);
}
if (!$tokens[$testOpenIndex + 1]->isWhitespace()) {
$tokens->insertAt($testOpenIndex + 1, new Token([T_WHITESPACE, ' ']));
}
if (null !== $testDefaultNamespaceTokenIndex) {
$tokens->clearTokenAndMergeSurroundingWhitespace($testDefaultNamespaceTokenIndex);
}
}
private function fixAssertTrueFalseInstanceof(Tokens $tokens, array $assertCall, int $testIndex): bool
{
if ($tokens[$testIndex]->equals('!')) {
$variableIndex = $tokens->getNextMeaningfulToken($testIndex);
$positive = false;
} else {
$variableIndex = $testIndex;
$positive = true;
}
if (!$tokens[$variableIndex]->isGivenKind(T_VARIABLE)) {
return false;
}
$instanceOfIndex = $tokens->getNextMeaningfulToken($variableIndex);
if (!$tokens[$instanceOfIndex]->isGivenKind(T_INSTANCEOF)) {
return false;
}
$classEndIndex = $instanceOfIndex;
$classPartTokens = [];
do {
$classEndIndex = $tokens->getNextMeaningfulToken($classEndIndex);
$classPartTokens[] = $tokens[$classEndIndex];
} while ($tokens[$classEndIndex]->isGivenKind([T_STRING, T_NS_SEPARATOR, T_VARIABLE]));
if ($tokens[$classEndIndex]->equalsAny([',', ')'])) {
array_pop($classPartTokens);
$isInstanceOfVar = reset($classPartTokens)->isGivenKind(T_VARIABLE);
$insertIndex = $testIndex - 1;
$newTokens = [];
foreach ($classPartTokens as $token) {
$newTokens[++$insertIndex] = clone $token;
}
if (!$isInstanceOfVar) {
$newTokens[++$insertIndex] = new Token([T_DOUBLE_COLON, '::']);
$newTokens[++$insertIndex] = new Token([CT::T_CLASS_CONSTANT, 'class']);
}
$newTokens[++$insertIndex] = new Token(',');
$newTokens[++$insertIndex] = new Token([T_WHITESPACE, ' ']);
$newTokens[++$insertIndex] = clone $tokens[$variableIndex];
for ($i = $classEndIndex - 1; $i >= $testIndex; --$i) {
if (!$tokens[$i]->isComment()) {
$tokens->clearTokenAndMergeSurroundingWhitespace($i);
}
}
$tokens->insertSlices($newTokens);
$tokens[$assertCall['index']] = new Token([T_STRING, $positive ? 'assertInstanceOf' : 'assertNotInstanceOf']);
}
return true;
}
private function fixAssertSameEquals(Tokens $tokens, array $assertCall): void
{
$expectedIndex = $tokens->getNextMeaningfulToken($assertCall['openBraceIndex']);
if ($tokens[$expectedIndex]->isGivenKind([T_VARIABLE])) {
if (!$tokens[$tokens->getNextMeaningfulToken($expectedIndex)]->equals(',')) {
return;
}
} elseif (!$tokens[$expectedIndex]->isGivenKind([T_LNUMBER, T_VARIABLE])) {
return;
}
$commaIndex = $tokens->getNextMeaningfulToken($expectedIndex);
if (!$tokens[$commaIndex]->equals(',')) {
return;
}
$countCallIndex = $tokens->getNextMeaningfulToken($commaIndex);
if ($tokens[$countCallIndex]->isGivenKind(T_NS_SEPARATOR)) {
$defaultNamespaceTokenIndex = $countCallIndex;
$countCallIndex = $tokens->getNextMeaningfulToken($countCallIndex);
} else {
$defaultNamespaceTokenIndex = null;
}
if (!$tokens[$countCallIndex]->isGivenKind(T_STRING)) {
return;
}
$lowerContent = strtolower($tokens[$countCallIndex]->getContent());
if ('count' !== $lowerContent && 'sizeof' !== $lowerContent) {
return;
}
$countCallOpenBraceIndex = $tokens->getNextMeaningfulToken($countCallIndex);
if (!$tokens[$countCallOpenBraceIndex]->equals('(')) {
return;
}
$countCallCloseBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $countCallOpenBraceIndex);
$afterCountCallCloseBraceIndex = $tokens->getNextMeaningfulToken($countCallCloseBraceIndex);
if (!$tokens[$afterCountCallCloseBraceIndex]->equalsAny([')', ','])) {
return;
}
$this->removeFunctionCall(
$tokens,
$defaultNamespaceTokenIndex,
$countCallIndex,
$countCallOpenBraceIndex,
$countCallCloseBraceIndex
);
$tokens[$assertCall['index']] = new Token([
T_STRING,
false === strpos($assertCall['loweredName'], 'not', 6) ? 'assertCount' : 'assertNotCount',
]);
}
private function getPreviousAssertCall(Tokens $tokens, int $startIndex, int $endIndex): iterable
{
$functionsAnalyzer = new FunctionsAnalyzer();
for ($index = $endIndex; $index > $startIndex; --$index) {
$index = $tokens->getPrevTokenOfKind($index, [[T_STRING]]);
if (null === $index) {
return;
}
$loweredContent = strtolower($tokens[$index]->getContent());
if (!str_starts_with($loweredContent, 'assert')) {
continue;
}
$openBraceIndex = $tokens->getNextMeaningfulToken($index);
if (!$tokens[$openBraceIndex]->equals('(')) {
continue;
}
if (!$functionsAnalyzer->isTheSameClassCall($tokens, $index)) {
continue;
}
yield [
'index' => $index,
'loweredName' => $loweredContent,
'openBraceIndex' => $openBraceIndex,
'closeBraceIndex' => $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openBraceIndex),
];
}
}
private function removeFunctionCall(Tokens $tokens, ?int $callNSIndex, int $callIndex, int $openIndex, int $closeIndex): void
{
$tokens->clearTokenAndMergeSurroundingWhitespace($callIndex);
if (null !== $callNSIndex) {
$tokens->clearTokenAndMergeSurroundingWhitespace($callNSIndex);
}
$tokens->clearTokenAndMergeSurroundingWhitespace($openIndex);
$commaIndex = $tokens->getPrevMeaningfulToken($closeIndex);
if ($tokens[$commaIndex]->equals(',')) {
$tokens->removeTrailingWhitespace($commaIndex);
$tokens->clearAt($commaIndex);
}
$tokens->clearTokenAndMergeSurroundingWhitespace($closeIndex);
}
private function swapArguments(Tokens $tokens, array $argumentsIndices): void
{
[$firstArgumentIndex, $secondArgumentIndex] = array_keys($argumentsIndices);
$firstArgumentEndIndex = $argumentsIndices[$firstArgumentIndex];
$secondArgumentEndIndex = $argumentsIndices[$secondArgumentIndex];
$firstClone = $this->cloneAndClearTokens($tokens, $firstArgumentIndex, $firstArgumentEndIndex);
$secondClone = $this->cloneAndClearTokens($tokens, $secondArgumentIndex, $secondArgumentEndIndex);
if (!$firstClone[0]->isWhitespace()) {
array_unshift($firstClone, new Token([T_WHITESPACE, ' ']));
}
$tokens->insertAt($secondArgumentIndex, $firstClone);
if ($secondClone[0]->isWhitespace()) {
array_shift($secondClone);
}
$tokens->insertAt($firstArgumentIndex, $secondClone);
}
private function cloneAndClearTokens(Tokens $tokens, int $start, int $end): array
{
$clone = [];
for ($i = $start; $i <= $end; ++$i) {
if ('' === $tokens[$i]->getContent()) {
continue;
}
$clone[] = clone $tokens[$i];
$tokens->clearAt($i);
}
return $clone;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\PhpUnit;
use PhpCsFixer\Fixer\AbstractPhpUnitFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpUnitConstructFixer extends AbstractPhpUnitFixer implements ConfigurableFixerInterface
{
private static array $assertionFixers = [
'assertSame' => 'fixAssertPositive',
'assertEquals' => 'fixAssertPositive',
'assertNotEquals' => 'fixAssertNegative',
'assertNotSame' => 'fixAssertNegative',
];
public function isRisky(): bool
{
return true;
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'PHPUnit assertion method calls like `->assertSame(true, $foo)` should be written with dedicated method like `->assertTrue($foo)`.',
[
new CodeSample(
'<?php
final class FooTest extends \PHPUnit_Framework_TestCase {
public function testSomething() {
$this->assertEquals(false, $b);
$this->assertSame(true, $a);
$this->assertNotEquals(null, $c);
$this->assertNotSame(null, $d);
}
}
'
),
new CodeSample(
'<?php
final class FooTest extends \PHPUnit_Framework_TestCase {
public function testSomething() {
$this->assertEquals(false, $b);
$this->assertSame(true, $a);
$this->assertNotEquals(null, $c);
$this->assertNotSame(null, $d);
}
}
',
['assertions' => ['assertSame', 'assertNotSame']]
),
],
null,
'Fixer could be risky if one is overriding PHPUnit\'s native methods.'
);
}
public function getPriority(): int
{
return -8;
}
protected function applyPhpUnitClassFix(Tokens $tokens, int $startIndex, int $endIndex): void
{
if (empty($this->configuration['assertions'])) {
return;
}
foreach ($this->configuration['assertions'] as $assertionMethod) {
$assertionFixer = self::$assertionFixers[$assertionMethod];
for ($index = $startIndex; $index < $endIndex; ++$index) {
$index = $this->{$assertionFixer}($tokens, $index, $assertionMethod);
if (null === $index) {
break;
}
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('assertions', 'List of assertion methods to fix.'))
->setAllowedTypes(['array'])
->setAllowedValues([new AllowedValueSubset(array_keys(self::$assertionFixers))])
->setDefault([
'assertEquals',
'assertSame',
'assertNotEquals',
'assertNotSame',
])
->getOption(),
]);
}
private function fixAssertNegative(Tokens $tokens, int $index, string $method): ?int
{
static $map = [
'false' => 'assertNotFalse',
'null' => 'assertNotNull',
'true' => 'assertNotTrue',
];
return $this->fixAssert($map, $tokens, $index, $method);
}
private function fixAssertPositive(Tokens $tokens, int $index, string $method): ?int
{
static $map = [
'false' => 'assertFalse',
'null' => 'assertNull',
'true' => 'assertTrue',
];
return $this->fixAssert($map, $tokens, $index, $method);
}
private function fixAssert(array $map, Tokens $tokens, int $index, string $method): ?int
{
$functionsAnalyzer = new FunctionsAnalyzer();
$sequence = $tokens->findSequence(
[
[T_STRING, $method],
'(',
],
$index
);
if (null === $sequence) {
return null;
}
$sequenceIndices = array_keys($sequence);
if (!$functionsAnalyzer->isTheSameClassCall($tokens, $sequenceIndices[0])) {
return null;
}
$sequenceIndices[2] = $tokens->getNextMeaningfulToken($sequenceIndices[1]);
$firstParameterToken = $tokens[$sequenceIndices[2]];
if (!$firstParameterToken->isNativeConstant()) {
return $sequenceIndices[2];
}
$sequenceIndices[3] = $tokens->getNextMeaningfulToken($sequenceIndices[2]);
if (!$tokens[$sequenceIndices[3]]->equals(',')) {
return $sequenceIndices[3];
}
$tokens[$sequenceIndices[0]] = new Token([T_STRING, $map[strtolower($firstParameterToken->getContent())]]);
$tokens->clearRange($sequenceIndices[2], $tokens->getNextNonWhitespace($sequenceIndices[3]) - 1);
return $sequenceIndices[3];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\PhpUnit;
use PhpCsFixer\Fixer\AbstractPhpUnitFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class PhpUnitSetUpTearDownVisibilityFixer extends AbstractPhpUnitFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Changes the visibility of the `setUp()` and `tearDown()` functions of PHPUnit to `protected`, to match the PHPUnit TestCase.',
[
new CodeSample(
'<?php
final class MyTest extends \PHPUnit_Framework_TestCase
{
private $hello;
public function setUp()
{
$this->hello = "hello";
}
public function tearDown()
{
$this->hello = null;
}
}
'
),
],
null,
'This fixer may change functions named `setUp()` or `tearDown()` outside of PHPUnit tests, '.
'when a class is wrongly seen as a PHPUnit test.'
);
}
public function isRisky(): bool
{
return true;
}
protected function applyPhpUnitClassFix(Tokens $tokens, int $startIndex, int $endIndex): void
{
$counter = 0;
$tokensAnalyzer = new TokensAnalyzer($tokens);
for ($i = $endIndex - 1; $i > $startIndex; --$i) {
if (2 === $counter) {
break;
}
if (!$this->isSetupOrTearDownMethod($tokens, $i)) {
continue;
}
++$counter;
$visibility = $tokensAnalyzer->getMethodAttributes($i)['visibility'];
if (T_PUBLIC === $visibility) {
$index = $tokens->getPrevTokenOfKind($i, [[T_PUBLIC]]);
$tokens[$index] = new Token([T_PROTECTED, 'protected']);
continue;
}
if (null === $visibility) {
$tokens->insertAt($i, [new Token([T_PROTECTED, 'protected']), new Token([T_WHITESPACE, ' '])]);
}
}
}
private function isSetupOrTearDownMethod(Tokens $tokens, int $index): bool
{
$tokensAnalyzer = new TokensAnalyzer($tokens);
$isMethod = $tokens[$index]->isGivenKind(T_FUNCTION) && !$tokensAnalyzer->isLambda($index);
if (!$isMethod) {
return false;
}
$functionNameIndex = $tokens->getNextMeaningfulToken($index);
$functionName = strtolower($tokens[$functionNameIndex]->getContent());
return 'setup' === $functionName || 'teardown' === $functionName;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\PhpUnit;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\DocBlock\Line;
use PhpCsFixer\Fixer\AbstractPhpUnitFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Analyzer\WhitespacesAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class PhpUnitTestAnnotationFixer extends AbstractPhpUnitFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
{
public function isRisky(): bool
{
return true;
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Adds or removes @test annotations from tests, following configuration.',
[
new CodeSample('<?php
class Test extends \\PhpUnit\\FrameWork\\TestCase
{
/**
* @test
*/
public function itDoesSomething() {} }'.$this->whitespacesConfig->getLineEnding()),
new CodeSample('<?php
class Test extends \\PhpUnit\\FrameWork\\TestCase
{
public function testItDoesSomething() {}}'.$this->whitespacesConfig->getLineEnding(), ['style' => 'annotation']),
],
null,
'This fixer may change the name of your tests, and could cause incompatibility with'.
' abstract classes or interfaces.'
);
}
public function getPriority(): int
{
return 10;
}
protected function applyPhpUnitClassFix(Tokens $tokens, int $startIndex, int $endIndex): void
{
if ('annotation' === $this->configuration['style']) {
$this->applyTestAnnotation($tokens, $startIndex, $endIndex);
} else {
$this->applyTestPrefix($tokens, $startIndex, $endIndex);
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('style', 'Whether to use the @test annotation or not.'))
->setAllowedValues(['prefix', 'annotation'])
->setDefault('prefix')
->getOption(),
]);
}
private function applyTestAnnotation(Tokens $tokens, int $startIndex, int $endIndex): void
{
for ($i = $endIndex - 1; $i > $startIndex; --$i) {
if (!$this->isTestMethod($tokens, $i)) {
continue;
}
$functionNameIndex = $tokens->getNextMeaningfulToken($i);
$functionName = $tokens[$functionNameIndex]->getContent();
if ($this->hasTestPrefix($functionName) && !$this->hasProperTestAnnotation($tokens, $i)) {
$newFunctionName = $this->removeTestPrefix($functionName);
$tokens[$functionNameIndex] = new Token([T_STRING, $newFunctionName]);
}
$docBlockIndex = $this->getDocBlockIndex($tokens, $i);
if ($this->isPHPDoc($tokens, $docBlockIndex)) {
$lines = $this->updateDocBlock($tokens, $docBlockIndex);
$lines = $this->addTestAnnotation($lines, $tokens, $docBlockIndex);
$lines = implode('', $lines);
$tokens[$docBlockIndex] = new Token([T_DOC_COMMENT, $lines]);
} else {
$this->createDocBlock($tokens, $docBlockIndex);
}
}
}
private function applyTestPrefix(Tokens $tokens, int $startIndex, int $endIndex): void
{
for ($i = $endIndex - 1; $i > $startIndex; --$i) {
if (!$this->isTestMethod($tokens, $i)) {
continue;
}
$docBlockIndex = $this->getDocBlockIndex($tokens, $i);
if (!$this->isPHPDoc($tokens, $docBlockIndex)) {
continue;
}
$lines = $this->updateDocBlock($tokens, $docBlockIndex);
$lines = implode('', $lines);
$tokens[$docBlockIndex] = new Token([T_DOC_COMMENT, $lines]);
$functionNameIndex = $tokens->getNextMeaningfulToken($i);
$functionName = $tokens[$functionNameIndex]->getContent();
if ($this->hasTestPrefix($functionName)) {
continue;
}
$newFunctionName = $this->addTestPrefix($functionName);
$tokens[$functionNameIndex] = new Token([T_STRING, $newFunctionName]);
}
}
private function isTestMethod(Tokens $tokens, int $index): bool
{
if (!$this->isMethod($tokens, $index)) {
return false;
}
$functionNameIndex = $tokens->getNextMeaningfulToken($index);
$functionName = $tokens[$functionNameIndex]->getContent();
if ($this->hasTestPrefix($functionName)) {
return true;
}
$docBlockIndex = $this->getDocBlockIndex($tokens, $index);
return
$this->isPHPDoc($tokens, $docBlockIndex)
&& str_contains($tokens[$docBlockIndex]->getContent(), '@test')
;
}
private function isMethod(Tokens $tokens, int $index): bool
{
$tokensAnalyzer = new TokensAnalyzer($tokens);
return $tokens[$index]->isGivenKind(T_FUNCTION) && !$tokensAnalyzer->isLambda($index);
}
private function hasTestPrefix(string $functionName): bool
{
return str_starts_with($functionName, 'test');
}
private function hasProperTestAnnotation(Tokens $tokens, int $index): bool
{
$docBlockIndex = $this->getDocBlockIndex($tokens, $index);
$doc = $tokens[$docBlockIndex]->getContent();
return 1 === Preg::match('/\*\s+@test\b/', $doc);
}
private function removeTestPrefix(string $functionName): string
{
$remainder = Preg::replace('/^test(?=[A-Z_])_?/', '', $functionName);
if ('' === $remainder) {
return $functionName;
}
return lcfirst($remainder);
}
private function addTestPrefix(string $functionName): string
{
return 'test'.ucfirst($functionName);
}
private function createDocBlock(Tokens $tokens, int $docBlockIndex): void
{
$lineEnd = $this->whitespacesConfig->getLineEnding();
$originalIndent = WhitespacesAnalyzer::detectIndent($tokens, $tokens->getNextNonWhitespace($docBlockIndex));
$toInsert = [
new Token([T_DOC_COMMENT, '/**'.$lineEnd."{$originalIndent} * @test".$lineEnd."{$originalIndent} */"]),
new Token([T_WHITESPACE, $lineEnd.$originalIndent]),
];
$index = $tokens->getNextMeaningfulToken($docBlockIndex);
$tokens->insertAt($index, $toInsert);
}
private function updateDocBlock(Tokens $tokens, int $docBlockIndex): array
{
$doc = new DocBlock($tokens[$docBlockIndex]->getContent());
$lines = $doc->getLines();
return $this->updateLines($lines, $tokens, $docBlockIndex);
}
private function updateLines(array $lines, Tokens $tokens, int $docBlockIndex): array
{
$needsAnnotation = 'annotation' === $this->configuration['style'];
$doc = new DocBlock($tokens[$docBlockIndex]->getContent());
for ($i = 0; $i < \count($lines); ++$i) {
if ($needsAnnotation && ($lines[$i]->isTheStart() && $lines[$i]->isTheEnd())) {
if (!$this->doesDocBlockContainTest($doc)) {
$lines = $this->splitUpDocBlock($lines, $tokens, $docBlockIndex);
return $this->updateLines($lines, $tokens, $docBlockIndex);
}
}
if (!$needsAnnotation
&& str_contains($lines[$i]->getContent(), ' @test')
&& !str_contains($lines[$i]->getContent(), '@testWith')
&& !str_contains($lines[$i]->getContent(), '@testdox')
) {
$lines[$i] = new Line(str_replace(' @test', '', $lines[$i]->getContent()));
}
if (!str_contains($lines[$i]->getContent(), '@depends')) {
continue;
}
$lines[$i] = $this->updateDependsAnnotation($lines[$i]);
}
return $lines;
}
private function splitUpDocBlock(array $lines, Tokens $tokens, int $docBlockIndex): array
{
$lineContent = $this->getSingleLineDocBlockEntry($lines);
$lineEnd = $this->whitespacesConfig->getLineEnding();
$originalIndent = WhitespacesAnalyzer::detectIndent($tokens, $tokens->getNextNonWhitespace($docBlockIndex));
return [
new Line('/**'.$lineEnd),
new Line($originalIndent.' * '.$lineContent.$lineEnd),
new Line($originalIndent.' */'),
];
}
private function getSingleLineDocBlockEntry(array $lines): string
{
$line = $lines[0];
$line = str_replace('*/', '', $line->getContent());
$line = trim($line);
$line = str_split($line);
$i = \count($line);
do {
--$i;
} while ('*' !== $line[$i] && '*' !== $line[$i - 1] && '/' !== $line[$i - 2]);
if (' ' === $line[$i]) {
++$i;
}
$line = \array_slice($line, $i);
return implode('', $line);
}
private function updateDependsAnnotation(Line $line): Line
{
if ('annotation' === $this->configuration['style']) {
return $this->removeTestPrefixFromDependsAnnotation($line);
}
return $this->addTestPrefixToDependsAnnotation($line);
}
private function removeTestPrefixFromDependsAnnotation(Line $line): Line
{
$line = str_split($line->getContent());
$dependsIndex = $this->findWhereDependsFunctionNameStarts($line);
$dependsFunctionName = implode('', \array_slice($line, $dependsIndex));
if ($this->hasTestPrefix($dependsFunctionName)) {
$dependsFunctionName = $this->removeTestPrefix($dependsFunctionName);
}
array_splice($line, $dependsIndex);
return new Line(implode('', $line).$dependsFunctionName);
}
private function addTestPrefixToDependsAnnotation(Line $line): Line
{
$line = str_split($line->getContent());
$dependsIndex = $this->findWhereDependsFunctionNameStarts($line);
$dependsFunctionName = implode('', \array_slice($line, $dependsIndex));
if (!$this->hasTestPrefix($dependsFunctionName)) {
$dependsFunctionName = $this->addTestPrefix($dependsFunctionName);
}
array_splice($line, $dependsIndex);
return new Line(implode('', $line).$dependsFunctionName);
}
private function findWhereDependsFunctionNameStarts(array $line): int
{
$counter = \count($line);
do {
--$counter;
} while (' ' !== $line[$counter]);
return $counter + 1;
}
private function addTestAnnotation(array $lines, Tokens $tokens, int $docBlockIndex): array
{
$doc = new DocBlock($tokens[$docBlockIndex]->getContent());
if (!$this->doesDocBlockContainTest($doc)) {
$originalIndent = WhitespacesAnalyzer::detectIndent($tokens, $docBlockIndex);
$lineEnd = $this->whitespacesConfig->getLineEnding();
array_splice($lines, -1, 0, $originalIndent.' *'.$lineEnd.$originalIndent.' * @test'.$lineEnd);
}
return $lines;
}
private function doesDocBlockContainTest(DocBlock $doc): bool
{
return 0 !== \count($doc->getAnnotationsOfType('test'));
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\PhpUnit;
use PhpCsFixer\DocBlock\Annotation;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\DocBlock\Line;
use PhpCsFixer\Fixer\AbstractPhpUnitFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\WhitespacesAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpUnitSizeClassFixer extends AbstractPhpUnitFixer implements WhitespacesAwareFixerInterface, ConfigurableFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'All PHPUnit test cases should have `@small`, `@medium` or `@large` annotation to enable run time limits.',
[
new CodeSample("<?php\nclass MyTest extends TestCase {}\n"),
new CodeSample("<?php\nclass MyTest extends TestCase {}\n", ['group' => 'medium']),
],
'The special groups [small, medium, large] provides a way to identify tests that are taking long to be executed.'
);
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('group', 'Define a specific group to be used in case no group is already in use'))
->setAllowedValues(['small', 'medium', 'large'])
->setDefault('small')
->getOption(),
]);
}
protected function applyPhpUnitClassFix(Tokens $tokens, int $startIndex, int $endIndex): void
{
$classIndex = $tokens->getPrevTokenOfKind($startIndex, [[T_CLASS]]);
if ($this->isAbstractClass($tokens, $classIndex)) {
return;
}
$docBlockIndex = $this->getDocBlockIndex($tokens, $classIndex);
if ($this->isPHPDoc($tokens, $docBlockIndex)) {
$this->updateDocBlockIfNeeded($tokens, $docBlockIndex);
} else {
$this->createDocBlock($tokens, $docBlockIndex);
}
}
private function isAbstractClass(Tokens $tokens, int $i): bool
{
$typeIndex = $tokens->getPrevMeaningfulToken($i);
return $tokens[$typeIndex]->isGivenKind(T_ABSTRACT);
}
private function createDocBlock(Tokens $tokens, int $docBlockIndex): void
{
$lineEnd = $this->whitespacesConfig->getLineEnding();
$originalIndent = WhitespacesAnalyzer::detectIndent($tokens, $tokens->getNextNonWhitespace($docBlockIndex));
$group = $this->configuration['group'];
$toInsert = [
new Token([T_DOC_COMMENT, '/**'.$lineEnd."{$originalIndent} * @".$group.$lineEnd."{$originalIndent} */"]),
new Token([T_WHITESPACE, $lineEnd.$originalIndent]),
];
$index = $tokens->getNextMeaningfulToken($docBlockIndex);
$tokens->insertAt($index, $toInsert);
}
private function updateDocBlockIfNeeded(Tokens $tokens, int $docBlockIndex): void
{
$doc = new DocBlock($tokens[$docBlockIndex]->getContent());
if (0 !== \count($this->filterDocBlock($doc))) {
return;
}
$doc = $this->makeDocBlockMultiLineIfNeeded($doc, $tokens, $docBlockIndex);
$lines = $this->addSizeAnnotation($doc, $tokens, $docBlockIndex);
$lines = implode('', $lines);
$tokens[$docBlockIndex] = new Token([T_DOC_COMMENT, $lines]);
}
private function addSizeAnnotation(DocBlock $docBlock, Tokens $tokens, int $docBlockIndex): array
{
$lines = $docBlock->getLines();
$originalIndent = WhitespacesAnalyzer::detectIndent($tokens, $docBlockIndex);
$lineEnd = $this->whitespacesConfig->getLineEnding();
$group = $this->configuration['group'];
array_splice($lines, -1, 0, $originalIndent.' *'.$lineEnd.$originalIndent.' * @'.$group.$lineEnd);
return $lines;
}
private function makeDocBlockMultiLineIfNeeded(DocBlock $doc, Tokens $tokens, int $docBlockIndex): DocBlock
{
$lines = $doc->getLines();
if (1 === \count($lines) && 0 === \count($this->filterDocBlock($doc))) {
$lines = $this->splitUpDocBlock($lines, $tokens, $docBlockIndex);
return new DocBlock(implode('', $lines));
}
return $doc;
}
private function splitUpDocBlock(array $lines, Tokens $tokens, int $docBlockIndex): array
{
$lineContent = $this->getSingleLineDocBlockEntry($lines[0]);
$lineEnd = $this->whitespacesConfig->getLineEnding();
$originalIndent = WhitespacesAnalyzer::detectIndent($tokens, $tokens->getNextNonWhitespace($docBlockIndex));
return [
new Line('/**'.$lineEnd),
new Line($originalIndent.' * '.$lineContent.$lineEnd),
new Line($originalIndent.' */'),
];
}
private function getSingleLineDocBlockEntry(Line $line): string
{
$line = $line->getContent();
$line = str_replace('*/', '', $line);
$line = trim($line);
$line = str_split($line);
$i = \count($line);
do {
--$i;
} while ('*' !== $line[$i] && '*' !== $line[$i - 1] && '/' !== $line[$i - 2]);
if (' ' === $line[$i]) {
++$i;
}
$line = \array_slice($line, $i);
return implode('', $line);
}
private function filterDocBlock(DocBlock $doc): array
{
return array_filter([
$doc->getAnnotationsOfType('small'),
$doc->getAnnotationsOfType('large'),
$doc->getAnnotationsOfType('medium'),
]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\PhpUnit;
use PhpCsFixer\DocBlock\Annotation;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\Fixer\AbstractPhpUnitFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Analyzer\WhitespacesAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class PhpUnitNoExpectationAnnotationFixer extends AbstractPhpUnitFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
{
private $fixMessageRegExp;
public function configure(array $configuration): void
{
parent::configure($configuration);
$this->fixMessageRegExp = PhpUnitTargetVersion::fulfills($this->configuration['target'], PhpUnitTargetVersion::VERSION_4_3);
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Usages of `@expectedException*` annotations MUST be replaced by `->setExpectedException*` methods.',
[
new CodeSample(
'<?php
final class MyTest extends \PHPUnit_Framework_TestCase
{
/**
* @expectedException FooException
* @expectedExceptionMessageRegExp /foo.*$/
* @expectedExceptionCode 123
*/
function testAaa()
{
aaa();
}
}
'
),
new CodeSample(
'<?php
final class MyTest extends \PHPUnit_Framework_TestCase
{
/**
* @expectedException FooException
* @expectedExceptionCode 123
*/
function testBbb()
{
bbb();
}
/**
* @expectedException FooException
* @expectedExceptionMessageRegExp /foo.*$/
*/
function testCcc()
{
ccc();
}
}
',
['target' => PhpUnitTargetVersion::VERSION_3_2]
),
],
null,
'Risky when PHPUnit classes are overridden or not accessible, or when project has PHPUnit incompatibilities.'
);
}
public function getPriority(): int
{
return 10;
}
public function isRisky(): bool
{
return true;
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('target', 'Target version of PHPUnit.'))
->setAllowedTypes(['string'])
->setAllowedValues([PhpUnitTargetVersion::VERSION_3_2, PhpUnitTargetVersion::VERSION_4_3, PhpUnitTargetVersion::VERSION_NEWEST])
->setDefault(PhpUnitTargetVersion::VERSION_NEWEST)
->getOption(),
(new FixerOptionBuilder('use_class_const', 'Use ::class notation.'))
->setAllowedTypes(['bool'])
->setDefault(true)
->getOption(),
]);
}
protected function applyPhpUnitClassFix(Tokens $tokens, int $startIndex, int $endIndex): void
{
$tokensAnalyzer = new TokensAnalyzer($tokens);
for ($i = $endIndex - 1; $i > $startIndex; --$i) {
if (!$tokens[$i]->isGivenKind(T_FUNCTION) || $tokensAnalyzer->isLambda($i)) {
continue;
}
$functionIndex = $i;
$docBlockIndex = $i;
$braceIndex = $tokens->getNextTokenOfKind($functionIndex, [';', '{']);
if (!$tokens[$braceIndex]->equals('{')) {
continue;
}
do {
$docBlockIndex = $tokens->getPrevNonWhitespace($docBlockIndex);
} while ($tokens[$docBlockIndex]->isGivenKind([T_PUBLIC, T_PROTECTED, T_PRIVATE, T_FINAL, T_ABSTRACT, T_COMMENT]));
if (!$tokens[$docBlockIndex]->isGivenKind(T_DOC_COMMENT)) {
continue;
}
$doc = new DocBlock($tokens[$docBlockIndex]->getContent());
$annotations = [];
foreach ($doc->getAnnotationsOfType([
'expectedException',
'expectedExceptionCode',
'expectedExceptionMessage',
'expectedExceptionMessageRegExp',
]) as $annotation) {
$tag = $annotation->getTag()->getName();
$content = $this->extractContentFromAnnotation($annotation);
$annotations[$tag] = $content;
$annotation->remove();
}
if (!isset($annotations['expectedException'])) {
continue;
}
if (!$this->fixMessageRegExp && isset($annotations['expectedExceptionMessageRegExp'])) {
continue;
}
$originalIndent = WhitespacesAnalyzer::detectIndent($tokens, $docBlockIndex);
$paramList = $this->annotationsToParamList($annotations);
$newMethodsCode = '<?php $this->'
.(isset($annotations['expectedExceptionMessageRegExp']) ? 'setExpectedExceptionRegExp' : 'setExpectedException')
.'('
.implode(', ', $paramList)
.');';
$newMethods = Tokens::fromCode($newMethodsCode);
$newMethods[0] = new Token([
T_WHITESPACE,
$this->whitespacesConfig->getLineEnding().$originalIndent.$this->whitespacesConfig->getIndent(),
]);
$docContent = $doc->getContent();
if ('' === $docContent) {
$docContent = '/** */';
}
$tokens[$docBlockIndex] = new Token([T_DOC_COMMENT, $docContent]);
$tokens->insertAt($braceIndex + 1, $newMethods);
$whitespaceIndex = $braceIndex + $newMethods->getSize() + 1;
$tokens[$whitespaceIndex] = new Token([
T_WHITESPACE,
$this->whitespacesConfig->getLineEnding().$tokens[$whitespaceIndex]->getContent(),
]);
$i = $docBlockIndex;
}
}
private function extractContentFromAnnotation(Annotation $annotation): string
{
$tag = $annotation->getTag()->getName();
if (1 !== Preg::match('/@'.$tag.'\s+(.+)$/s', $annotation->getContent(), $matches)) {
return '';
}
$content = Preg::replace('/\*+\/$/', '', $matches[1]);
if (Preg::match('/\R/u', $content)) {
$content = Preg::replace('/\s*\R+\s*\*\s*/u', ' ', $content);
}
return rtrim($content);
}
private function annotationsToParamList(array $annotations): array
{
$params = [];
$exceptionClass = ltrim($annotations['expectedException'], '\\');
if (str_contains($exceptionClass, '*')) {
$exceptionClass = substr($exceptionClass, 0, strpos($exceptionClass, '*'));
}
$exceptionClass = trim($exceptionClass);
if (true === $this->configuration['use_class_const']) {
$params[] = "\\{$exceptionClass}::class";
} else {
$params[] = "'{$exceptionClass}'";
}
if (isset($annotations['expectedExceptionMessage'])) {
$params[] = var_export($annotations['expectedExceptionMessage'], true);
} elseif (isset($annotations['expectedExceptionMessageRegExp'])) {
$params[] = var_export($annotations['expectedExceptionMessageRegExp'], true);
} elseif (isset($annotations['expectedExceptionCode'])) {
$params[] = 'null';
}
if (isset($annotations['expectedExceptionCode'])) {
$params[] = $annotations['expectedExceptionCode'];
}
return $params;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\PhpUnit;
use PhpCsFixer\Fixer\AbstractPhpUnitFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class PhpUnitDedicateAssertInternalTypeFixer extends AbstractPhpUnitFixer implements ConfigurableFixerInterface
{
private array $typeToDedicatedAssertMap = [
'array' => 'assertIsArray',
'boolean' => 'assertIsBool',
'bool' => 'assertIsBool',
'double' => 'assertIsFloat',
'float' => 'assertIsFloat',
'integer' => 'assertIsInt',
'int' => 'assertIsInt',
'null' => 'assertNull',
'numeric' => 'assertIsNumeric',
'object' => 'assertIsObject',
'real' => 'assertIsFloat',
'resource' => 'assertIsResource',
'string' => 'assertIsString',
'scalar' => 'assertIsScalar',
'callable' => 'assertIsCallable',
'iterable' => 'assertIsIterable',
];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'PHPUnit assertions like `assertIsArray` should be used over `assertInternalType`.',
[
new CodeSample(
'<?php
final class MyTest extends \PHPUnit\Framework\TestCase
{
public function testMe()
{
$this->assertInternalType("array", $var);
$this->assertInternalType("boolean", $var);
}
}
'
),
new CodeSample(
'<?php
final class MyTest extends \PHPUnit\Framework\TestCase
{
public function testMe()
{
$this->assertInternalType("array", $var);
$this->assertInternalType("boolean", $var);
}
}
',
['target' => PhpUnitTargetVersion::VERSION_7_5]
),
],
null,
'Risky when PHPUnit methods are overridden or when project has PHPUnit incompatibilities.'
);
}
public function isRisky(): bool
{
return true;
}
public function getPriority(): int
{
return -16;
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('target', 'Target version of PHPUnit.'))
->setAllowedTypes(['string'])
->setAllowedValues([PhpUnitTargetVersion::VERSION_7_5, PhpUnitTargetVersion::VERSION_NEWEST])
->setDefault(PhpUnitTargetVersion::VERSION_NEWEST)
->getOption(),
]);
}
protected function applyPhpUnitClassFix(Tokens $tokens, int $startIndex, int $endIndex): void
{
$anonymousClassIndices = [];
$tokenAnalyzer = new TokensAnalyzer($tokens);
for ($index = $startIndex; $index < $endIndex; ++$index) {
if (!$tokens[$index]->isGivenKind(T_CLASS) || !$tokenAnalyzer->isAnonymousClass($index)) {
continue;
}
$openingBraceIndex = $tokens->getNextTokenOfKind($index, ['{']);
$closingBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $openingBraceIndex);
$anonymousClassIndices[$closingBraceIndex] = $openingBraceIndex;
}
for ($index = $endIndex - 1; $index > $startIndex; --$index) {
if (isset($anonymousClassIndices[$index])) {
$index = $anonymousClassIndices[$index];
continue;
}
if (!$tokens[$index]->isGivenKind(T_STRING)) {
continue;
}
$functionName = strtolower($tokens[$index]->getContent());
if ('assertinternaltype' !== $functionName && 'assertnotinternaltype' !== $functionName) {
continue;
}
$bracketTokenIndex = $tokens->getNextMeaningfulToken($index);
if (!$tokens[$bracketTokenIndex]->equals('(')) {
continue;
}
$expectedTypeTokenIndex = $tokens->getNextMeaningfulToken($bracketTokenIndex);
$expectedTypeToken = $tokens[$expectedTypeTokenIndex];
if (!$expectedTypeToken->isGivenKind(T_CONSTANT_ENCAPSED_STRING)) {
continue;
}
$expectedType = trim($expectedTypeToken->getContent(), '\'"');
if (!isset($this->typeToDedicatedAssertMap[$expectedType])) {
continue;
}
$commaTokenIndex = $tokens->getNextMeaningfulToken($expectedTypeTokenIndex);
if (!$tokens[$commaTokenIndex]->equals(',')) {
continue;
}
$newAssertion = $this->typeToDedicatedAssertMap[$expectedType];
if ('assertnotinternaltype' === $functionName) {
$newAssertion = str_replace('Is', 'IsNot', $newAssertion);
$newAssertion = str_replace('Null', 'NotNull', $newAssertion);
}
$nextMeaningfulTokenIndex = $tokens->getNextMeaningfulToken($commaTokenIndex);
$tokens->overrideRange($index, $nextMeaningfulTokenIndex - 1, [
new Token([T_STRING, $newAssertion]),
new Token('('),
]);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\PhpUnit;
use PhpCsFixer\Fixer\AbstractPhpUnitFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpUnitMockFixer extends AbstractPhpUnitFixer implements ConfigurableFixerInterface
{
private $fixCreatePartialMock;
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Usages of `->getMock` and `->getMockWithoutInvokingTheOriginalConstructor` methods MUST be replaced by `->createMock` or `->createPartialMock` methods.',
[
new CodeSample(
'<?php
final class MyTest extends \PHPUnit_Framework_TestCase
{
public function testFoo()
{
$mock = $this->getMockWithoutInvokingTheOriginalConstructor("Foo");
$mock1 = $this->getMock("Foo");
$mock1 = $this->getMock("Bar", ["aaa"]);
$mock1 = $this->getMock("Baz", ["aaa"], ["argument"]); // version with more than 2 params is not supported
}
}
'
),
new CodeSample(
'<?php
final class MyTest extends \PHPUnit_Framework_TestCase
{
public function testFoo()
{
$mock1 = $this->getMock("Foo");
$mock1 = $this->getMock("Bar", ["aaa"]); // version with multiple params is not supported
}
}
',
['target' => PhpUnitTargetVersion::VERSION_5_4]
),
],
null,
'Risky when PHPUnit classes are overridden or not accessible, or when project has PHPUnit incompatibilities.'
);
}
public function isRisky(): bool
{
return true;
}
public function configure(array $configuration): void
{
parent::configure($configuration);
$this->fixCreatePartialMock = PhpUnitTargetVersion::fulfills($this->configuration['target'], PhpUnitTargetVersion::VERSION_5_5);
}
protected function applyPhpUnitClassFix(Tokens $tokens, int $startIndex, int $endIndex): void
{
$argumentsAnalyzer = new ArgumentsAnalyzer();
for ($index = $startIndex; $index < $endIndex; ++$index) {
if (!$tokens[$index]->isObjectOperator()) {
continue;
}
$index = $tokens->getNextMeaningfulToken($index);
if ($tokens[$index]->equals([T_STRING, 'getMockWithoutInvokingTheOriginalConstructor'], false)) {
$tokens[$index] = new Token([T_STRING, 'createMock']);
} elseif ($tokens[$index]->equals([T_STRING, 'getMock'], false)) {
$openingParenthesis = $tokens->getNextMeaningfulToken($index);
$closingParenthesis = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openingParenthesis);
$argumentsCount = $argumentsAnalyzer->countArguments($tokens, $openingParenthesis, $closingParenthesis);
if (1 === $argumentsCount) {
$tokens[$index] = new Token([T_STRING, 'createMock']);
} elseif (2 === $argumentsCount && true === $this->fixCreatePartialMock) {
$tokens[$index] = new Token([T_STRING, 'createPartialMock']);
}
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('target', 'Target version of PHPUnit.'))
->setAllowedTypes(['string'])
->setAllowedValues([PhpUnitTargetVersion::VERSION_5_4, PhpUnitTargetVersion::VERSION_5_5, PhpUnitTargetVersion::VERSION_NEWEST])
->setDefault(PhpUnitTargetVersion::VERSION_NEWEST)
->getOption(),
]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\PhpUnit;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\DocBlock\Line;
use PhpCsFixer\Fixer\AbstractPhpUnitFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\WhitespacesAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpUnitInternalClassFixer extends AbstractPhpUnitFixer implements WhitespacesAwareFixerInterface, ConfigurableFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'All PHPUnit test classes should be marked as internal.',
[
new CodeSample("<?php\nclass MyTest extends TestCase {}\n"),
new CodeSample(
"<?php\nclass MyTest extends TestCase {}\nfinal class FinalTest extends TestCase {}\nabstract class AbstractTest extends TestCase {}\n",
['types' => ['final']]
),
]
);
}
public function getPriority(): int
{
return 68;
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
$types = ['normal', 'final', 'abstract'];
return new FixerConfigurationResolver([
(new FixerOptionBuilder('types', 'What types of classes to mark as internal'))
->setAllowedValues([new AllowedValueSubset($types)])
->setAllowedTypes(['array'])
->setDefault(['normal', 'final'])
->getOption(),
]);
}
protected function applyPhpUnitClassFix(Tokens $tokens, int $startIndex, int $endIndex): void
{
$classIndex = $tokens->getPrevTokenOfKind($startIndex, [[T_CLASS]]);
if (!$this->isAllowedByConfiguration($tokens, $classIndex)) {
return;
}
$docBlockIndex = $this->getDocBlockIndex($tokens, $classIndex);
if ($this->isPHPDoc($tokens, $docBlockIndex)) {
$this->updateDocBlockIfNeeded($tokens, $docBlockIndex);
} else {
$this->createDocBlock($tokens, $docBlockIndex);
}
}
private function isAllowedByConfiguration(Tokens $tokens, int $i): bool
{
$typeIndex = $tokens->getPrevMeaningfulToken($i);
if ($tokens[$typeIndex]->isGivenKind(T_FINAL)) {
return \in_array('final', $this->configuration['types'], true);
}
if ($tokens[$typeIndex]->isGivenKind(T_ABSTRACT)) {
return \in_array('abstract', $this->configuration['types'], true);
}
return \in_array('normal', $this->configuration['types'], true);
}
private function createDocBlock(Tokens $tokens, int $docBlockIndex): void
{
$lineEnd = $this->whitespacesConfig->getLineEnding();
$originalIndent = WhitespacesAnalyzer::detectIndent($tokens, $tokens->getNextNonWhitespace($docBlockIndex));
$toInsert = [
new Token([T_DOC_COMMENT, '/**'.$lineEnd."{$originalIndent} * @internal".$lineEnd."{$originalIndent} */"]),
new Token([T_WHITESPACE, $lineEnd.$originalIndent]),
];
$index = $tokens->getNextMeaningfulToken($docBlockIndex);
$tokens->insertAt($index, $toInsert);
}
private function updateDocBlockIfNeeded(Tokens $tokens, int $docBlockIndex): void
{
$doc = new DocBlock($tokens[$docBlockIndex]->getContent());
if (!empty($doc->getAnnotationsOfType('internal'))) {
return;
}
$doc = $this->makeDocBlockMultiLineIfNeeded($doc, $tokens, $docBlockIndex);
$lines = $this->addInternalAnnotation($doc, $tokens, $docBlockIndex);
$lines = implode('', $lines);
$tokens[$docBlockIndex] = new Token([T_DOC_COMMENT, $lines]);
}
private function addInternalAnnotation(DocBlock $docBlock, Tokens $tokens, int $docBlockIndex): array
{
$lines = $docBlock->getLines();
$originalIndent = WhitespacesAnalyzer::detectIndent($tokens, $docBlockIndex);
$lineEnd = $this->whitespacesConfig->getLineEnding();
array_splice($lines, -1, 0, $originalIndent.' *'.$lineEnd.$originalIndent.' * @internal'.$lineEnd);
return $lines;
}
private function makeDocBlockMultiLineIfNeeded(DocBlock $doc, Tokens $tokens, int $docBlockIndex): DocBlock
{
$lines = $doc->getLines();
if (1 === \count($lines) && empty($doc->getAnnotationsOfType('internal'))) {
$indent = WhitespacesAnalyzer::detectIndent($tokens, $tokens->getNextNonWhitespace($docBlockIndex));
$doc->makeMultiLine($indent, $this->whitespacesConfig->getLineEnding());
return $doc;
}
return $doc;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\PhpUnit;
use PhpCsFixer\Fixer\AbstractPhpUnitFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\WhitespacesAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpUnitExpectationFixer extends AbstractPhpUnitFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
{
private array $methodMap = [];
public function configure(array $configuration): void
{
parent::configure($configuration);
$this->methodMap = [
'setExpectedException' => 'expectExceptionMessage',
];
if (PhpUnitTargetVersion::fulfills($this->configuration['target'], PhpUnitTargetVersion::VERSION_5_6)) {
$this->methodMap['setExpectedExceptionRegExp'] = 'expectExceptionMessageRegExp';
}
if (PhpUnitTargetVersion::fulfills($this->configuration['target'], PhpUnitTargetVersion::VERSION_8_4)) {
$this->methodMap['setExpectedExceptionRegExp'] = 'expectExceptionMessageMatches';
$this->methodMap['expectExceptionMessageRegExp'] = 'expectExceptionMessageMatches';
}
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Usages of `->setExpectedException*` methods MUST be replaced by `->expectException*` methods.',
[
new CodeSample(
'<?php
final class MyTest extends \PHPUnit_Framework_TestCase
{
public function testFoo()
{
$this->setExpectedException("RuntimeException", "Msg", 123);
foo();
}
public function testBar()
{
$this->setExpectedExceptionRegExp("RuntimeException", "/Msg.*/", 123);
bar();
}
}
'
),
new CodeSample(
'<?php
final class MyTest extends \PHPUnit_Framework_TestCase
{
public function testFoo()
{
$this->setExpectedException("RuntimeException", null, 123);
foo();
}
public function testBar()
{
$this->setExpectedExceptionRegExp("RuntimeException", "/Msg.*/", 123);
bar();
}
}
',
['target' => PhpUnitTargetVersion::VERSION_8_4]
),
new CodeSample(
'<?php
final class MyTest extends \PHPUnit_Framework_TestCase
{
public function testFoo()
{
$this->setExpectedException("RuntimeException", null, 123);
foo();
}
public function testBar()
{
$this->setExpectedExceptionRegExp("RuntimeException", "/Msg.*/", 123);
bar();
}
}
',
['target' => PhpUnitTargetVersion::VERSION_5_6]
),
new CodeSample(
'<?php
final class MyTest extends \PHPUnit_Framework_TestCase
{
public function testFoo()
{
$this->setExpectedException("RuntimeException", "Msg", 123);
foo();
}
public function testBar()
{
$this->setExpectedExceptionRegExp("RuntimeException", "/Msg.*/", 123);
bar();
}
}
',
['target' => PhpUnitTargetVersion::VERSION_5_2]
),
],
null,
'Risky when PHPUnit classes are overridden or not accessible, or when project has PHPUnit incompatibilities.'
);
}
public function getPriority(): int
{
return 0;
}
public function isRisky(): bool
{
return true;
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('target', 'Target version of PHPUnit.'))
->setAllowedTypes(['string'])
->setAllowedValues([PhpUnitTargetVersion::VERSION_5_2, PhpUnitTargetVersion::VERSION_5_6, PhpUnitTargetVersion::VERSION_8_4, PhpUnitTargetVersion::VERSION_NEWEST])
->setDefault(PhpUnitTargetVersion::VERSION_NEWEST)
->getOption(),
]);
}
protected function applyPhpUnitClassFix(Tokens $tokens, int $startIndex, int $endIndex): void
{
foreach (Token::getObjectOperatorKinds() as $objectOperator) {
$this->applyPhpUnitClassFixWithObjectOperator($tokens, $startIndex, $endIndex, $objectOperator);
}
}
private function applyPhpUnitClassFixWithObjectOperator(Tokens $tokens, int $startIndex, int $endIndex, int $objectOperator): void
{
$argumentsAnalyzer = new ArgumentsAnalyzer();
$oldMethodSequence = [
[T_VARIABLE, '$this'],
[$objectOperator],
[T_STRING],
];
for ($index = $startIndex; $startIndex < $endIndex; ++$index) {
$match = $tokens->findSequence($oldMethodSequence, $index);
if (null === $match) {
return;
}
[$thisIndex, , $index] = array_keys($match);
if (!isset($this->methodMap[$tokens[$index]->getContent()])) {
continue;
}
$openIndex = $tokens->getNextTokenOfKind($index, ['(']);
$closeIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openIndex);
$commaIndex = $tokens->getPrevMeaningfulToken($closeIndex);
if ($tokens[$commaIndex]->equals(',')) {
$tokens->removeTrailingWhitespace($commaIndex);
$tokens->clearAt($commaIndex);
}
$arguments = $argumentsAnalyzer->getArguments($tokens, $openIndex, $closeIndex);
$argumentsCnt = \count($arguments);
$argumentsReplacements = ['expectException', $this->methodMap[$tokens[$index]->getContent()], 'expectExceptionCode'];
$indent = $this->whitespacesConfig->getLineEnding().WhitespacesAnalyzer::detectIndent($tokens, $thisIndex);
$isMultilineWhitespace = false;
for ($cnt = $argumentsCnt - 1; $cnt >= 1; --$cnt) {
$argStart = array_keys($arguments)[$cnt];
$argBefore = $tokens->getPrevMeaningfulToken($argStart);
if ('expectExceptionMessage' === $argumentsReplacements[$cnt]) {
$paramIndicatorIndex = $tokens->getNextMeaningfulToken($argBefore);
$afterParamIndicatorIndex = $tokens->getNextMeaningfulToken($paramIndicatorIndex);
if (
$tokens[$paramIndicatorIndex]->equals([T_STRING, 'null'], false)
&& $tokens[$afterParamIndicatorIndex]->equals(')')
) {
if ($tokens[$argBefore + 1]->isWhitespace()) {
$tokens->clearTokenAndMergeSurroundingWhitespace($argBefore + 1);
}
$tokens->clearTokenAndMergeSurroundingWhitespace($argBefore);
$tokens->clearTokenAndMergeSurroundingWhitespace($paramIndicatorIndex);
continue;
}
}
$isMultilineWhitespace = $isMultilineWhitespace || ($tokens[$argStart]->isWhitespace() && !$tokens[$argStart]->isWhitespace(" \t"));
$tokensOverrideArgStart = [
new Token([T_WHITESPACE, $indent]),
new Token([T_VARIABLE, '$this']),
new Token([T_OBJECT_OPERATOR, '->']),
new Token([T_STRING, $argumentsReplacements[$cnt]]),
new Token('('),
];
$tokensOverrideArgBefore = [
new Token(')'),
new Token(';'),
];
if ($isMultilineWhitespace) {
$tokensOverrideArgStart[] = new Token([T_WHITESPACE, $indent.$this->whitespacesConfig->getIndent()]);
array_unshift($tokensOverrideArgBefore, new Token([T_WHITESPACE, $indent]));
}
if ($tokens[$argStart]->isWhitespace()) {
$tokens->overrideRange($argStart, $argStart, $tokensOverrideArgStart);
} else {
$tokens->insertAt($argStart, $tokensOverrideArgStart);
}
$tokens->overrideRange($argBefore, $argBefore, $tokensOverrideArgBefore);
}
$methodName = 'expectException';
if ('expectExceptionMessageRegExp' === $tokens[$index]->getContent()) {
$methodName = $this->methodMap[$tokens[$index]->getContent()];
}
$tokens[$index] = new Token([T_STRING, $methodName]);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\PhpUnit;
use PhpCsFixer\Fixer\AbstractPhpUnitFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpUnitFqcnAnnotationFixer extends AbstractPhpUnitFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'PHPUnit annotations should be a FQCNs including a root namespace.',
[new CodeSample(
'<?php
final class MyTest extends \PHPUnit_Framework_TestCase
{
/**
* @expectedException InvalidArgumentException
* @covers Project\NameSpace\Something
* @coversDefaultClass Project\Default
* @uses Project\Test\Util
*/
public function testSomeTest()
{
}
}
'
)]
);
}
public function getPriority(): int
{
return -9;
}
protected function applyPhpUnitClassFix(Tokens $tokens, int $startIndex, int $endIndex): void
{
$prevDocCommentIndex = $tokens->getPrevTokenOfKind($startIndex, [[T_DOC_COMMENT]]);
if (null !== $prevDocCommentIndex) {
$startIndex = $prevDocCommentIndex;
}
$this->fixPhpUnitClass($tokens, $startIndex, $endIndex);
}
private function fixPhpUnitClass(Tokens $tokens, int $startIndex, int $endIndex): void
{
for ($index = $startIndex; $index < $endIndex; ++$index) {
if ($tokens[$index]->isGivenKind(T_DOC_COMMENT)) {
$tokens[$index] = new Token([T_DOC_COMMENT, Preg::replace(
'~^(\s*\*\s*@(?:expectedException|covers|coversDefaultClass|uses)\h+)(?!(?:self|static)::)(\w.*)$~m',
'$1\\\\$2',
$tokens[$index]->getContent()
)]);
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\PhpUnit;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpUnitNamespacedFixer extends AbstractFixer implements ConfigurableFixerInterface
{
private $originalClassRegEx;
private $classMap;
public function getDefinition(): FixerDefinitionInterface
{
$codeSample = '<?php
final class MyTest extends \PHPUnit_Framework_TestCase
{
public function testSomething()
{
PHPUnit_Framework_Assert::assertTrue(true);
}
}
';
return new FixerDefinition(
'PHPUnit classes MUST be used in namespaced version, e.g. `\PHPUnit\Framework\TestCase` instead of `\PHPUnit_Framework_TestCase`.',
[
new CodeSample($codeSample),
new CodeSample($codeSample, ['target' => PhpUnitTargetVersion::VERSION_4_8]),
],
"PHPUnit v6 has finally fully switched to namespaces.\n"
."You could start preparing the upgrade by switching from non-namespaced TestCase to namespaced one.\n"
.'Forward compatibility layer (`\PHPUnit\Framework\TestCase` class) was backported to PHPUnit v4.8.35 and PHPUnit v5.4.0.'."\n"
.'Extended forward compatibility layer (`PHPUnit\Framework\Assert`, `PHPUnit\Framework\BaseTestListener`, `PHPUnit\Framework\TestListener` classes) was introduced in v5.7.0.'."\n",
'Risky when PHPUnit classes are overridden or not accessible, or when project has PHPUnit incompatibilities.'
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_STRING);
}
public function isRisky(): bool
{
return true;
}
public function configure(array $configuration): void
{
parent::configure($configuration);
if (PhpUnitTargetVersion::fulfills($this->configuration['target'], PhpUnitTargetVersion::VERSION_6_0)) {
$this->originalClassRegEx = '/^PHPUnit_\w+$/i';
$this->classMap = [
'PHPUnit_Extensions_PhptTestCase' => 'PHPUnit\Runner\PhptTestCase',
'PHPUnit_Framework_Constraint' => 'PHPUnit\Framework\Constraint\Constraint',
'PHPUnit_Framework_Constraint_StringMatches' => 'PHPUnit\Framework\Constraint\StringMatchesFormatDescription',
'PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider' => 'PHPUnit\Framework\Constraint\JsonMatchesErrorMessageProvider',
'PHPUnit_Framework_Constraint_PCREMatch' => 'PHPUnit\Framework\Constraint\RegularExpression',
'PHPUnit_Framework_Constraint_ExceptionMessageRegExp' => 'PHPUnit\Framework\Constraint\ExceptionMessageRegularExpression',
'PHPUnit_Framework_Constraint_And' => 'PHPUnit\Framework\Constraint\LogicalAnd',
'PHPUnit_Framework_Constraint_Or' => 'PHPUnit\Framework\Constraint\LogicalOr',
'PHPUnit_Framework_Constraint_Not' => 'PHPUnit\Framework\Constraint\LogicalNot',
'PHPUnit_Framework_Constraint_Xor' => 'PHPUnit\Framework\Constraint\LogicalXor',
'PHPUnit_Framework_Error' => 'PHPUnit\Framework\Error\Error',
'PHPUnit_Framework_TestSuite_DataProvider' => 'PHPUnit\Framework\DataProviderTestSuite',
'PHPUnit_Framework_MockObject_Invocation_Static' => 'PHPUnit\Framework\MockObject\Invocation\StaticInvocation',
'PHPUnit_Framework_MockObject_Invocation_Object' => 'PHPUnit\Framework\MockObject\Invocation\ObjectInvocation',
'PHPUnit_Framework_MockObject_Stub_Return' => 'PHPUnit\Framework\MockObject\Stub\ReturnStub',
'PHPUnit_Runner_Filter_Group_Exclude' => 'PHPUnit\Runner\Filter\ExcludeGroupFilterIterator',
'PHPUnit_Runner_Filter_Group_Include' => 'PHPUnit\Runner\Filter\IncludeGroupFilterIterator',
'PHPUnit_Runner_Filter_Test' => 'PHPUnit\Runner\Filter\NameFilterIterator',
'PHPUnit_Util_PHP' => 'PHPUnit\Util\PHP\AbstractPhpProcess',
'PHPUnit_Util_PHP_Default' => 'PHPUnit\Util\PHP\DefaultPhpProcess',
'PHPUnit_Util_PHP_Windows' => 'PHPUnit\Util\PHP\WindowsPhpProcess',
'PHPUnit_Util_Regex' => 'PHPUnit\Util\RegularExpression',
'PHPUnit_Util_TestDox_ResultPrinter_XML' => 'PHPUnit\Util\TestDox\XmlResultPrinter',
'PHPUnit_Util_TestDox_ResultPrinter_HTML' => 'PHPUnit\Util\TestDox\HtmlResultPrinter',
'PHPUnit_Util_TestDox_ResultPrinter_Text' => 'PHPUnit\Util\TestDox\TextResultPrinter',
'PHPUnit_Util_TestSuiteIterator' => 'PHPUnit\Framework\TestSuiteIterator',
'PHPUnit_Util_XML' => 'PHPUnit\Util\Xml',
];
} elseif (PhpUnitTargetVersion::fulfills($this->configuration['target'], PhpUnitTargetVersion::VERSION_5_7)) {
$this->originalClassRegEx = '/^PHPUnit_Framework_TestCase|PHPUnit_Framework_Assert|PHPUnit_Framework_BaseTestListener|PHPUnit_Framework_TestListener$/i';
$this->classMap = [];
} else {
$this->originalClassRegEx = '/^PHPUnit_Framework_TestCase$/i';
$this->classMap = [];
}
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$importedOriginalClassesMap = [];
$currIndex = 0;
while (true) {
$currIndex = $tokens->getNextTokenOfKind($currIndex, [[T_STRING]]);
if (null === $currIndex) {
break;
}
$prevIndex = $tokens->getPrevMeaningfulToken($currIndex);
if ($tokens[$prevIndex]->isGivenKind([T_CONST, T_DOUBLE_COLON])) {
continue;
}
$originalClass = $tokens[$currIndex]->getContent();
if (1 !== Preg::match($this->originalClassRegEx, $originalClass)) {
++$currIndex;
continue;
}
$substituteTokens = $this->generateReplacement($originalClass);
$tokens->clearAt($currIndex);
$tokens->insertAt(
$currIndex,
isset($importedOriginalClassesMap[$originalClass]) ? $substituteTokens[$substituteTokens->getSize() - 1] : $substituteTokens
);
$prevIndex = $tokens->getPrevMeaningfulToken($currIndex);
if ($tokens[$prevIndex]->isGivenKind(T_USE)) {
$importedOriginalClassesMap[$originalClass] = true;
} elseif ($tokens[$prevIndex]->isGivenKind(T_NS_SEPARATOR)) {
$prevIndex = $tokens->getPrevMeaningfulToken($prevIndex);
if ($tokens[$prevIndex]->isGivenKind(T_USE)) {
$importedOriginalClassesMap[$originalClass] = true;
}
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('target', 'Target version of PHPUnit.'))
->setAllowedTypes(['string'])
->setAllowedValues([PhpUnitTargetVersion::VERSION_4_8, PhpUnitTargetVersion::VERSION_5_7, PhpUnitTargetVersion::VERSION_6_0, PhpUnitTargetVersion::VERSION_NEWEST])
->setDefault(PhpUnitTargetVersion::VERSION_NEWEST)
->getOption(),
]);
}
private function generateReplacement(string $originalClassName): Tokens
{
$delimiter = '_';
$string = $originalClassName;
if (isset($this->classMap[$originalClassName])) {
$delimiter = '\\';
$string = $this->classMap[$originalClassName];
}
$parts = explode($delimiter, $string);
$tokensArray = [];
while (!empty($parts)) {
$tokensArray[] = new Token([T_STRING, array_shift($parts)]);
if (!empty($parts)) {
$tokensArray[] = new Token([T_NS_SEPARATOR, '\\']);
}
}
return Tokens::fromArray($tokensArray);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\PhpUnit;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\DocBlock\Line;
use PhpCsFixer\Fixer\AbstractPhpUnitFixer;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpUnitTestClassRequiresCoversFixer extends AbstractPhpUnitFixer implements WhitespacesAwareFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Adds a default `@coversNothing` annotation to PHPUnit test classes that have no `@covers*` annotation.',
[
new CodeSample(
'<?php
final class MyTest extends \PHPUnit_Framework_TestCase
{
public function testSomeTest()
{
$this->assertSame(a(), b());
}
}
'
),
]
);
}
protected function applyPhpUnitClassFix(Tokens $tokens, int $startIndex, int $endIndex): void
{
$classIndex = $tokens->getPrevTokenOfKind($startIndex, [[T_CLASS]]);
$prevIndex = $tokens->getPrevMeaningfulToken($classIndex);
if ($tokens[$prevIndex]->isGivenKind(T_ABSTRACT)) {
return;
}
$index = $tokens[$prevIndex]->isGivenKind(T_FINAL) ? $prevIndex : $classIndex;
$indent = $tokens[$index - 1]->isGivenKind(T_WHITESPACE)
? Preg::replace('/^.*\R*/', '', $tokens[$index - 1]->getContent())
: '';
$prevIndex = $tokens->getPrevNonWhitespace($index);
if ($tokens[$prevIndex]->isGivenKind(T_DOC_COMMENT)) {
$docIndex = $prevIndex;
$docContent = $tokens[$docIndex]->getContent();
if (!str_contains($docContent, "\n")) {
return;
}
$doc = new DocBlock($docContent);
if (0 !== \count($doc->getAnnotationsOfType([
'covers',
'coversDefaultClass',
'coversNothing',
]))) {
return;
}
} else {
$docIndex = $index;
$tokens->insertAt($docIndex, [
new Token([T_DOC_COMMENT, sprintf('/**%s%s */', $this->whitespacesConfig->getLineEnding(), $indent)]),
new Token([T_WHITESPACE, sprintf('%s%s', $this->whitespacesConfig->getLineEnding(), $indent)]),
]);
if (!$tokens[$docIndex - 1]->isGivenKind(T_WHITESPACE)) {
$extraNewLines = $this->whitespacesConfig->getLineEnding();
if (!$tokens[$docIndex - 1]->isGivenKind(T_OPEN_TAG)) {
$extraNewLines .= $this->whitespacesConfig->getLineEnding();
}
$tokens->insertAt($docIndex, [
new Token([T_WHITESPACE, $extraNewLines.$indent]),
]);
++$docIndex;
}
$doc = new DocBlock($tokens[$docIndex]->getContent());
}
$lines = $doc->getLines();
array_splice(
$lines,
\count($lines) - 1,
0,
[
new Line(sprintf(
'%s * @coversNothing%s',
$indent,
$this->whitespacesConfig->getLineEnding()
)),
]
);
$tokens[$docIndex] = new Token([T_DOC_COMMENT, implode('', $lines)]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\PhpUnit;
use PhpCsFixer\Fixer\AbstractPhpUnitFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
final class PhpUnitTestCaseStaticMethodCallsFixer extends AbstractPhpUnitFixer implements ConfigurableFixerInterface
{
public const CALL_TYPE_THIS = 'this';
public const CALL_TYPE_SELF = 'self';
public const CALL_TYPE_STATIC = 'static';
private array $allowedValues = [
self::CALL_TYPE_THIS => true,
self::CALL_TYPE_SELF => true,
self::CALL_TYPE_STATIC => true,
];
private array $staticMethods = [
'anything' => true,
'arrayHasKey' => true,
'assertArrayHasKey' => true,
'assertArrayNotHasKey' => true,
'assertArraySubset' => true,
'assertAttributeContains' => true,
'assertAttributeContainsOnly' => true,
'assertAttributeCount' => true,
'assertAttributeEmpty' => true,
'assertAttributeEquals' => true,
'assertAttributeGreaterThan' => true,
'assertAttributeGreaterThanOrEqual' => true,
'assertAttributeInstanceOf' => true,
'assertAttributeInternalType' => true,
'assertAttributeLessThan' => true,
'assertAttributeLessThanOrEqual' => true,
'assertAttributeNotContains' => true,
'assertAttributeNotContainsOnly' => true,
'assertAttributeNotCount' => true,
'assertAttributeNotEmpty' => true,
'assertAttributeNotEquals' => true,
'assertAttributeNotInstanceOf' => true,
'assertAttributeNotInternalType' => true,
'assertAttributeNotSame' => true,
'assertAttributeSame' => true,
'assertClassHasAttribute' => true,
'assertClassHasStaticAttribute' => true,
'assertClassNotHasAttribute' => true,
'assertClassNotHasStaticAttribute' => true,
'assertContains' => true,
'assertContainsEquals' => true,
'assertContainsOnly' => true,
'assertContainsOnlyInstancesOf' => true,
'assertCount' => true,
'assertDirectoryDoesNotExist' => true,
'assertDirectoryExists' => true,
'assertDirectoryIsNotReadable' => true,
'assertDirectoryIsNotWritable' => true,
'assertDirectoryIsReadable' => true,
'assertDirectoryIsWritable' => true,
'assertDirectoryNotExists' => true,
'assertDirectoryNotIsReadable' => true,
'assertDirectoryNotIsWritable' => true,
'assertDoesNotMatchRegularExpression' => true,
'assertEmpty' => true,
'assertEqualXMLStructure' => true,
'assertEquals' => true,
'assertEqualsCanonicalizing' => true,
'assertEqualsIgnoringCase' => true,
'assertEqualsWithDelta' => true,
'assertFalse' => true,
'assertFileDoesNotExist' => true,
'assertFileEquals' => true,
'assertFileEqualsCanonicalizing' => true,
'assertFileEqualsIgnoringCase' => true,
'assertFileExists' => true,
'assertFileIsNotReadable' => true,
'assertFileIsNotWritable' => true,
'assertFileIsReadable' => true,
'assertFileIsWritable' => true,
'assertFileNotEquals' => true,
'assertFileNotEqualsCanonicalizing' => true,
'assertFileNotEqualsIgnoringCase' => true,
'assertFileNotExists' => true,
'assertFileNotIsReadable' => true,
'assertFileNotIsWritable' => true,
'assertFinite' => true,
'assertGreaterThan' => true,
'assertGreaterThanOrEqual' => true,
'assertInfinite' => true,
'assertInstanceOf' => true,
'assertInternalType' => true,
'assertIsArray' => true,
'assertIsBool' => true,
'assertIsCallable' => true,
'assertIsClosedResource' => true,
'assertIsFloat' => true,
'assertIsInt' => true,
'assertIsIterable' => true,
'assertIsNotArray' => true,
'assertIsNotBool' => true,
'assertIsNotCallable' => true,
'assertIsNotClosedResource' => true,
'assertIsNotFloat' => true,
'assertIsNotInt' => true,
'assertIsNotIterable' => true,
'assertIsNotNumeric' => true,
'assertIsNotObject' => true,
'assertIsNotReadable' => true,
'assertIsNotResource' => true,
'assertIsNotScalar' => true,
'assertIsNotString' => true,
'assertIsNotWritable' => true,
'assertIsNumeric' => true,
'assertIsObject' => true,
'assertIsReadable' => true,
'assertIsResource' => true,
'assertIsScalar' => true,
'assertIsString' => true,
'assertIsWritable' => true,
'assertJson' => true,
'assertJsonFileEqualsJsonFile' => true,
'assertJsonFileNotEqualsJsonFile' => true,
'assertJsonStringEqualsJsonFile' => true,
'assertJsonStringEqualsJsonString' => true,
'assertJsonStringNotEqualsJsonFile' => true,
'assertJsonStringNotEqualsJsonString' => true,
'assertLessThan' => true,
'assertLessThanOrEqual' => true,
'assertMatchesRegularExpression' => true,
'assertNan' => true,
'assertNotContains' => true,
'assertNotContainsEquals' => true,
'assertNotContainsOnly' => true,
'assertNotCount' => true,
'assertNotEmpty' => true,
'assertNotEquals' => true,
'assertNotEqualsCanonicalizing' => true,
'assertNotEqualsIgnoringCase' => true,
'assertNotEqualsWithDelta' => true,
'assertNotFalse' => true,
'assertNotInstanceOf' => true,
'assertNotInternalType' => true,
'assertNotIsReadable' => true,
'assertNotIsWritable' => true,
'assertNotNull' => true,
'assertNotRegExp' => true,
'assertNotSame' => true,
'assertNotSameSize' => true,
'assertNotTrue' => true,
'assertNull' => true,
'assertObjectEquals' => true,
'assertObjectHasAttribute' => true,
'assertObjectNotHasAttribute' => true,
'assertRegExp' => true,
'assertSame' => true,
'assertSameSize' => true,
'assertStringContainsString' => true,
'assertStringContainsStringIgnoringCase' => true,
'assertStringEndsNotWith' => true,
'assertStringEndsWith' => true,
'assertStringEqualsFile' => true,
'assertStringEqualsFileCanonicalizing' => true,
'assertStringEqualsFileIgnoringCase' => true,
'assertStringMatchesFormat' => true,
'assertStringMatchesFormatFile' => true,
'assertStringNotContainsString' => true,
'assertStringNotContainsStringIgnoringCase' => true,
'assertStringNotEqualsFile' => true,
'assertStringNotEqualsFileCanonicalizing' => true,
'assertStringNotEqualsFileIgnoringCase' => true,
'assertStringNotMatchesFormat' => true,
'assertStringNotMatchesFormatFile' => true,
'assertStringStartsNotWith' => true,
'assertStringStartsWith' => true,
'assertThat' => true,
'assertTrue' => true,
'assertXmlFileEqualsXmlFile' => true,
'assertXmlFileNotEqualsXmlFile' => true,
'assertXmlStringEqualsXmlFile' => true,
'assertXmlStringEqualsXmlString' => true,
'assertXmlStringNotEqualsXmlFile' => true,
'assertXmlStringNotEqualsXmlString' => true,
'attribute' => true,
'attributeEqualTo' => true,
'callback' => true,
'classHasAttribute' => true,
'classHasStaticAttribute' => true,
'contains' => true,
'containsEqual' => true,
'containsIdentical' => true,
'containsOnly' => true,
'containsOnlyInstancesOf' => true,
'countOf' => true,
'directoryExists' => true,
'equalTo' => true,
'equalToCanonicalizing' => true,
'equalToIgnoringCase' => true,
'equalToWithDelta' => true,
'fail' => true,
'fileExists' => true,
'getCount' => true,
'getObjectAttribute' => true,
'getStaticAttribute' => true,
'greaterThan' => true,
'greaterThanOrEqual' => true,
'identicalTo' => true,
'isEmpty' => true,
'isFalse' => true,
'isFinite' => true,
'isInfinite' => true,
'isInstanceOf' => true,
'isJson' => true,
'isNan' => true,
'isNull' => true,
'isReadable' => true,
'isTrue' => true,
'isType' => true,
'isWritable' => true,
'lessThan' => true,
'lessThanOrEqual' => true,
'logicalAnd' => true,
'logicalNot' => true,
'logicalOr' => true,
'logicalXor' => true,
'markTestIncomplete' => true,
'markTestSkipped' => true,
'matches' => true,
'matchesRegularExpression' => true,
'objectEquals' => true,
'objectHasAttribute' => true,
'readAttribute' => true,
'resetCount' => true,
'stringContains' => true,
'stringEndsWith' => true,
'stringStartsWith' => true,
'any' => true,
'at' => true,
'atLeast' => true,
'atLeastOnce' => true,
'atMost' => true,
'exactly' => true,
'never' => true,
'once' => true,
'onConsecutiveCalls' => true,
'returnArgument' => true,
'returnCallback' => true,
'returnSelf' => true,
'returnValue' => true,
'returnValueMap' => true,
'setUpBeforeClass' => true,
'tearDownAfterClass' => true,
'throwException' => true,
];
private array $conversionMap = [
self::CALL_TYPE_THIS => [[T_OBJECT_OPERATOR, '->'], [T_VARIABLE, '$this']],
self::CALL_TYPE_SELF => [[T_DOUBLE_COLON, '::'], [T_STRING, 'self']],
self::CALL_TYPE_STATIC => [[T_DOUBLE_COLON, '::'], [T_STATIC, 'static']],
];
public function getDefinition(): FixerDefinitionInterface
{
$codeSample = '<?php
final class MyTest extends \PHPUnit_Framework_TestCase
{
public function testMe()
{
$this->assertSame(1, 2);
self::assertSame(1, 2);
static::assertSame(1, 2);
}
}
';
return new FixerDefinition(
'Calls to `PHPUnit\Framework\TestCase` static methods must all be of the same type, either `$this->`, `self::` or `static::`.',
[
new CodeSample($codeSample),
new CodeSample($codeSample, ['call_type' => self::CALL_TYPE_THIS]),
],
null,
'Risky when PHPUnit methods are overridden or not accessible, or when project has PHPUnit incompatibilities.'
);
}
public function getPriority(): int
{
return 0;
}
public function isRisky(): bool
{
return true;
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
$thisFixer = $this;
return new FixerConfigurationResolver([
(new FixerOptionBuilder('call_type', 'The call type to use for referring to PHPUnit methods.'))
->setAllowedTypes(['string'])
->setAllowedValues(array_keys($this->allowedValues))
->setDefault('static')
->getOption(),
(new FixerOptionBuilder('methods', 'Dictionary of `method` => `call_type` values that differ from the default strategy.'))
->setAllowedTypes(['array'])
->setAllowedValues([static function (array $option) use ($thisFixer): bool {
foreach ($option as $method => $value) {
if (!isset($thisFixer->staticMethods[$method])) {
throw new InvalidOptionsException(
sprintf(
'Unexpected "methods" key, expected any of "%s", got "%s".',
implode('", "', array_keys($thisFixer->staticMethods)),
\gettype($method).'#'.$method
)
);
}
if (!isset($thisFixer->allowedValues[$value])) {
throw new InvalidOptionsException(
sprintf(
'Unexpected value for method "%s", expected any of "%s", got "%s".',
$method,
implode('", "', array_keys($thisFixer->allowedValues)),
\is_object($value) ? \get_class($value) : (null === $value ? 'null' : \gettype($value).'#'.$value)
)
);
}
}
return true;
}])
->setDefault([])
->getOption(),
]);
}
protected function applyPhpUnitClassFix(Tokens $tokens, int $startIndex, int $endIndex): void
{
$analyzer = new TokensAnalyzer($tokens);
for ($index = $startIndex; $index < $endIndex; ++$index) {
if ($tokens[$index]->isGivenKind(T_CLASS)) {
$index = $this->findEndOfNextBlock($tokens, $index);
continue;
}
$callType = $this->configuration['call_type'];
if ($tokens[$index]->isGivenKind(T_FUNCTION)) {
if ($analyzer->isLambda($index)) {
$index = $this->findEndOfNextBlock($tokens, $index);
continue;
}
if ('this' === $callType) {
$attributes = $analyzer->getMethodAttributes($index);
if (false !== $attributes['static']) {
$index = $this->findEndOfNextBlock($tokens, $index);
continue;
}
}
}
if (!$tokens[$index]->isGivenKind(T_STRING) || !isset($this->staticMethods[$tokens[$index]->getContent()])) {
continue;
}
$nextIndex = $tokens->getNextMeaningfulToken($index);
if (!$tokens[$nextIndex]->equals('(')) {
$index = $nextIndex;
continue;
}
if ($tokens[$tokens->getNextMeaningfulToken($nextIndex)]->isGivenKind(CT::T_FIRST_CLASS_CALLABLE)) {
continue;
}
$methodName = $tokens[$index]->getContent();
if (isset($this->configuration['methods'][$methodName])) {
$callType = $this->configuration['methods'][$methodName];
}
$operatorIndex = $tokens->getPrevMeaningfulToken($index);
$referenceIndex = $tokens->getPrevMeaningfulToken($operatorIndex);
if (!$this->needsConversion($tokens, $index, $referenceIndex, $callType)) {
continue;
}
$tokens[$operatorIndex] = new Token($this->conversionMap[$callType][0]);
$tokens[$referenceIndex] = new Token($this->conversionMap[$callType][1]);
}
}
private function needsConversion(Tokens $tokens, int $index, int $referenceIndex, string $callType): bool
{
$functionsAnalyzer = new FunctionsAnalyzer();
return $functionsAnalyzer->isTheSameClassCall($tokens, $index)
&& !$tokens[$referenceIndex]->equals($this->conversionMap[$callType][1], false);
}
private function findEndOfNextBlock(Tokens $tokens, int $index): int
{
$nextIndex = $tokens->getNextTokenOfKind($index, [';', '{']);
return $tokens[$nextIndex]->equals('{')
? $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $nextIndex)
: $nextIndex;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\PhpUnit;
use PhpCsFixer\Fixer\AbstractPhpUnitFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpUnitMockShortWillReturnFixer extends AbstractPhpUnitFixer
{
private const RETURN_METHODS_MAP = [
'returnargument' => 'willReturnArgument',
'returncallback' => 'willReturnCallback',
'returnself' => 'willReturnSelf',
'returnvalue' => 'willReturn',
'returnvaluemap' => 'willReturnMap',
];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Usage of PHPUnit\'s mock e.g. `->will($this->returnValue(..))` must be replaced by its shorter equivalent such as `->willReturn(...)`.',
[
new CodeSample('<?php
final class MyTest extends \PHPUnit_Framework_TestCase
{
public function testSomeTest()
{
$someMock = $this->createMock(Some::class);
$someMock->method("some")->will($this->returnSelf());
$someMock->method("some")->will($this->returnValue("example"));
$someMock->method("some")->will($this->returnArgument(2));
$someMock->method("some")->will($this->returnCallback("str_rot13"));
$someMock->method("some")->will($this->returnValueMap(["a","b","c"]));
}
}
'),
],
null,
'Risky when PHPUnit classes are overridden or not accessible, or when project has PHPUnit incompatibilities.'
);
}
public function isRisky(): bool
{
return true;
}
protected function applyPhpUnitClassFix(Tokens $tokens, int $startIndex, int $endIndex): void
{
$functionsAnalyzer = new FunctionsAnalyzer();
for ($index = $startIndex; $index < $endIndex; ++$index) {
if (!$tokens[$index]->isObjectOperator()) {
continue;
}
$functionToReplaceIndex = $tokens->getNextMeaningfulToken($index);
if (!$tokens[$functionToReplaceIndex]->equals([T_STRING, 'will'], false)) {
continue;
}
$functionToReplaceOpeningBraceIndex = $tokens->getNextMeaningfulToken($functionToReplaceIndex);
if (!$tokens[$functionToReplaceOpeningBraceIndex]->equals('(')) {
continue;
}
$classReferenceIndex = $tokens->getNextMeaningfulToken($functionToReplaceOpeningBraceIndex);
$objectOperatorIndex = $tokens->getNextMeaningfulToken($classReferenceIndex);
$functionToRemoveIndex = $tokens->getNextMeaningfulToken($objectOperatorIndex);
if (!$functionsAnalyzer->isTheSameClassCall($tokens, $functionToRemoveIndex)) {
continue;
}
if (!\array_key_exists(strtolower($tokens[$functionToRemoveIndex]->getContent()), self::RETURN_METHODS_MAP)) {
continue;
}
$openingBraceIndex = $tokens->getNextMeaningfulToken($functionToRemoveIndex);
if (!$tokens[$openingBraceIndex]->equals('(')) {
continue;
}
if ($tokens[$tokens->getNextMeaningfulToken($openingBraceIndex)]->isGivenKind(CT::T_FIRST_CLASS_CALLABLE)) {
continue;
}
$closingBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openingBraceIndex);
$tokens[$functionToReplaceIndex] = new Token([T_STRING, self::RETURN_METHODS_MAP[strtolower($tokens[$functionToRemoveIndex]->getContent())]]);
$tokens->clearTokenAndMergeSurroundingWhitespace($classReferenceIndex);
$tokens->clearTokenAndMergeSurroundingWhitespace($objectOperatorIndex);
$tokens->clearTokenAndMergeSurroundingWhitespace($functionToRemoveIndex);
$tokens->clearTokenAndMergeSurroundingWhitespace($openingBraceIndex);
$tokens->clearTokenAndMergeSurroundingWhitespace($closingBraceIndex);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\PhpUnit;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\DocBlock\Line;
use PhpCsFixer\Fixer\AbstractPhpUnitFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
use PhpCsFixer\Utils;
final class PhpUnitMethodCasingFixer extends AbstractPhpUnitFixer implements ConfigurableFixerInterface
{
public const CAMEL_CASE = 'camel_case';
public const SNAKE_CASE = 'snake_case';
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Enforce camel (or snake) case for PHPUnit test methods, following configuration.',
[
new CodeSample(
'<?php
class MyTest extends \\PhpUnit\\FrameWork\\TestCase
{
public function test_my_code() {}
}
'
),
new CodeSample(
'<?php
class MyTest extends \\PhpUnit\\FrameWork\\TestCase
{
public function testMyCode() {}
}
',
['case' => self::SNAKE_CASE]
),
]
);
}
public function getPriority(): int
{
return 0;
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('case', 'Apply camel or snake case to test methods'))
->setAllowedValues([self::CAMEL_CASE, self::SNAKE_CASE])
->setDefault(self::CAMEL_CASE)
->getOption(),
]);
}
protected function applyPhpUnitClassFix(Tokens $tokens, int $startIndex, int $endIndex): void
{
for ($index = $endIndex - 1; $index > $startIndex; --$index) {
if (!$this->isTestMethod($tokens, $index)) {
continue;
}
$functionNameIndex = $tokens->getNextMeaningfulToken($index);
$functionName = $tokens[$functionNameIndex]->getContent();
$newFunctionName = $this->updateMethodCasing($functionName);
if ($newFunctionName !== $functionName) {
$tokens[$functionNameIndex] = new Token([T_STRING, $newFunctionName]);
}
$docBlockIndex = $this->getDocBlockIndex($tokens, $index);
if ($this->isPHPDoc($tokens, $docBlockIndex)) {
$this->updateDocBlock($tokens, $docBlockIndex);
}
}
}
private function updateMethodCasing(string $functionName): string
{
$parts = explode('::', $functionName);
$functionNamePart = array_pop($parts);
if (self::CAMEL_CASE === $this->configuration['case']) {
$newFunctionNamePart = $functionNamePart;
$newFunctionNamePart = ucwords($newFunctionNamePart, '_');
$newFunctionNamePart = str_replace('_', '', $newFunctionNamePart);
$newFunctionNamePart = lcfirst($newFunctionNamePart);
} else {
$newFunctionNamePart = Utils::camelCaseToUnderscore($functionNamePart);
}
$parts[] = $newFunctionNamePart;
return implode('::', $parts);
}
private function isTestMethod(Tokens $tokens, int $index): bool
{
if (!$this->isMethod($tokens, $index)) {
return false;
}
$functionNameIndex = $tokens->getNextMeaningfulToken($index);
$functionName = $tokens[$functionNameIndex]->getContent();
if (str_starts_with($functionName, 'test')) {
return true;
}
$docBlockIndex = $this->getDocBlockIndex($tokens, $index);
return
$this->isPHPDoc($tokens, $docBlockIndex)
&& str_contains($tokens[$docBlockIndex]->getContent(), '@test')
;
}
private function isMethod(Tokens $tokens, int $index): bool
{
$tokensAnalyzer = new TokensAnalyzer($tokens);
return $tokens[$index]->isGivenKind(T_FUNCTION) && !$tokensAnalyzer->isLambda($index);
}
private function updateDocBlock(Tokens $tokens, int $docBlockIndex): void
{
$doc = new DocBlock($tokens[$docBlockIndex]->getContent());
$lines = $doc->getLines();
$docBlockNeedsUpdate = false;
for ($inc = 0; $inc < \count($lines); ++$inc) {
$lineContent = $lines[$inc]->getContent();
if (!str_contains($lineContent, '@depends')) {
continue;
}
$newLineContent = Preg::replaceCallback('/(@depends\s+)(.+)(\b)/', function (array $matches): string {
return sprintf(
'%s%s%s',
$matches[1],
$this->updateMethodCasing($matches[2]),
$matches[3]
);
}, $lineContent);
if ($newLineContent !== $lineContent) {
$lines[$inc] = new Line($newLineContent);
$docBlockNeedsUpdate = true;
}
}
if ($docBlockNeedsUpdate) {
$lines = implode('', $lines);
$tokens[$docBlockIndex] = new Token([T_DOC_COMMENT, $lines]);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer;
use PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
interface ConfigurableFixerInterface extends FixerInterface
{
public function configure(array $configuration): void;
public function getConfigurationDefinition(): FixerConfigurationResolverInterface;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Whitespace;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Tokens;
final class CompactNullableTypehintFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Remove extra spaces in a nullable typehint.',
[
new CodeSample(
"<?php\nfunction sample(? string \$str): ? string\n{}\n"
),
],
'Rule is applied only in a PHP 7.1+ environment.'
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(CT::T_NULLABLE_TYPE);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
static $typehintKinds = [
CT::T_ARRAY_TYPEHINT,
T_CALLABLE,
T_NS_SEPARATOR,
T_STRING,
];
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
if (!$tokens[$index]->isGivenKind(CT::T_NULLABLE_TYPE)) {
continue;
}
if (
$tokens[$index + 1]->isWhitespace()
&& $tokens[$index + 2]->isGivenKind($typehintKinds)
) {
$tokens->removeTrailingWhitespace($index);
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Whitespace;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class LineEndingFixer extends AbstractFixer implements WhitespacesAwareFixerInterface
{
public function isCandidate(Tokens $tokens): bool
{
return true;
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'All PHP files must use same line ending.',
[
new CodeSample(
"<?php \$b = \" \$a \r\n 123\"; \$a = <<<TEST\r\nAAAAA \r\n |\r\nTEST;\n"
),
]
);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$ending = $this->whitespacesConfig->getLineEnding();
for ($index = 0, $count = \count($tokens); $index < $count; ++$index) {
$token = $tokens[$index];
if ($token->isGivenKind(T_ENCAPSED_AND_WHITESPACE)) {
if ($tokens[$tokens->getNextMeaningfulToken($index)]->isGivenKind(T_END_HEREDOC)) {
$tokens[$index] = new Token([
$token->getId(),
Preg::replace(
'#\R#',
$ending,
$token->getContent()
),
]);
}
continue;
}
if ($token->isGivenKind([T_CLOSE_TAG, T_COMMENT, T_DOC_COMMENT, T_OPEN_TAG, T_START_HEREDOC, T_WHITESPACE])) {
$tokens[$index] = new Token([
$token->getId(),
Preg::replace(
'#\R#',
$ending,
$token->getContent()
),
]);
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Whitespace;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Tokens;
final class SingleBlankLineAtEofFixer extends AbstractFixer implements WhitespacesAwareFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'A PHP file without end tag must always end with a single empty line feed.',
[
new CodeSample("<?php\n\$a = 1;"),
new CodeSample("<?php\n\$a = 1;\n\n"),
]
);
}
public function getPriority(): int
{
return -50;
}
public function isCandidate(Tokens $tokens): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$count = $tokens->count();
if ($count > 0 && !$tokens[$count - 1]->isGivenKind([T_INLINE_HTML, T_CLOSE_TAG, T_OPEN_TAG])) {
$tokens->ensureWhitespaceAtIndex($count - 1, 1, $this->whitespacesConfig->getLineEnding());
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Whitespace;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class IndentationTypeFixer extends AbstractFixer implements WhitespacesAwareFixerInterface
{
private $indent;
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Code MUST use configured indentation type.',
[
new CodeSample("<?php\n\nif (true) {\n\techo 'Hello!';\n}\n"),
]
);
}
public function getPriority(): int
{
return 50;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_COMMENT, T_DOC_COMMENT, T_WHITESPACE]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$this->indent = $this->whitespacesConfig->getIndent();
foreach ($tokens as $index => $token) {
if ($token->isComment()) {
$tokens[$index] = $this->fixIndentInComment($tokens, $index);
continue;
}
if ($token->isWhitespace()) {
$tokens[$index] = $this->fixIndentToken($tokens, $index);
continue;
}
}
}
private function fixIndentInComment(Tokens $tokens, int $index): Token
{
$content = Preg::replace('/^(?:(?<! ) {1,3})?\t/m', '\1 ', $tokens[$index]->getContent(), -1, $count);
while (0 !== $count) {
$content = Preg::replace('/^(\ +)?\t/m', '\1 ', $content, -1, $count);
}
$indent = $this->indent;
$content = Preg::replaceCallback('/^(?: )+/m', function (array $matches) use ($indent): string {
return $this->getExpectedIndent($matches[0], $indent);
}, $content);
return new Token([$tokens[$index]->getId(), $content]);
}
private function fixIndentToken(Tokens $tokens, int $index): Token
{
$content = $tokens[$index]->getContent();
$previousTokenHasTrailingLinebreak = false;
if (str_contains($tokens[$index - 1]->getContent(), "\n")) {
$content = "\n".$content;
$previousTokenHasTrailingLinebreak = true;
}
$indent = $this->indent;
$newContent = Preg::replaceCallback(
'/(\R)(\h+)/',
function (array $matches) use ($indent): string {
$content = Preg::replace('/(?:(?<! ) {1,3})?\t/', ' ', $matches[2]);
return $matches[1].$this->getExpectedIndent($content, $indent);
},
$content
);
if ($previousTokenHasTrailingLinebreak) {
$newContent = substr($newContent, 1);
}
return new Token([T_WHITESPACE, $newContent]);
}
private function getExpectedIndent(string $content, string $indent): string
{
if ("\t" === $indent) {
$content = str_replace(' ', $indent, $content);
}
return $content;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Whitespace;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\Indentation;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class ArrayIndentationFixer extends AbstractFixer implements WhitespacesAwareFixerInterface
{
use Indentation;
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Each element of an array must be indented exactly once.',
[
new CodeSample("<?php\n\$foo = [\n 'bar' => [\n 'baz' => true,\n ],\n];\n"),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_ARRAY, CT::T_ARRAY_SQUARE_BRACE_OPEN]);
}
public function getPriority(): int
{
return 29;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$lastIndent = '';
$scopes = [];
$previousLineInitialIndent = '';
$previousLineNewIndent = '';
foreach ($tokens as $index => $token) {
$currentScope = [] !== $scopes ? \count($scopes) - 1 : null;
if ($token->isComment()) {
continue;
}
if (
$token->isGivenKind(CT::T_ARRAY_SQUARE_BRACE_OPEN)
|| ($token->equals('(') && $tokens[$tokens->getPrevMeaningfulToken($index)]->isGivenKind(T_ARRAY))
) {
$endIndex = $tokens->findBlockEnd(
$token->equals('(') ? Tokens::BLOCK_TYPE_PARENTHESIS_BRACE : Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE,
$index
);
$scopes[] = [
'type' => 'array',
'end_index' => $endIndex,
'initial_indent' => $lastIndent,
];
continue;
}
if ($this->isNewLineToken($tokens, $index)) {
$lastIndent = $this->extractIndent($this->computeNewLineContent($tokens, $index));
}
if (null === $currentScope) {
continue;
}
if ($token->isWhitespace()) {
if (!Preg::match('/\R/', $token->getContent())) {
continue;
}
if ('array' === $scopes[$currentScope]['type']) {
$indent = false;
for ($searchEndIndex = $index + 1; $searchEndIndex < $scopes[$currentScope]['end_index']; ++$searchEndIndex) {
$searchEndToken = $tokens[$searchEndIndex];
if (
(!$searchEndToken->isWhitespace() && !$searchEndToken->isComment())
|| ($searchEndToken->isWhitespace() && Preg::match('/\R/', $searchEndToken->getContent()))
) {
$indent = true;
break;
}
}
$content = Preg::replace(
'/(\R+)\h*$/',
'$1'.$scopes[$currentScope]['initial_indent'].($indent ? $this->whitespacesConfig->getIndent() : ''),
$token->getContent()
);
$previousLineInitialIndent = $this->extractIndent($token->getContent());
$previousLineNewIndent = $this->extractIndent($content);
} else {
$content = Preg::replace(
'/(\R)'.preg_quote($scopes[$currentScope]['initial_indent'], '/').'(\h*)$/',
'$1'.$scopes[$currentScope]['new_indent'].'$2',
$token->getContent()
);
}
$tokens[$index] = new Token([T_WHITESPACE, $content]);
$lastIndent = $this->extractIndent($content);
continue;
}
if ($index === $scopes[$currentScope]['end_index']) {
while ([] !== $scopes && $index === $scopes[$currentScope]['end_index']) {
array_pop($scopes);
--$currentScope;
}
continue;
}
if ($token->equals(',')) {
continue;
}
if ('expression' !== $scopes[$currentScope]['type']) {
$endIndex = $this->findExpressionEndIndex($tokens, $index, $scopes[$currentScope]['end_index']);
if ($endIndex === $index) {
continue;
}
$scopes[] = [
'type' => 'expression',
'end_index' => $endIndex,
'initial_indent' => $previousLineInitialIndent,
'new_indent' => $previousLineNewIndent,
];
}
}
}
private function findExpressionEndIndex(Tokens $tokens, int $index, int $parentScopeEndIndex): int
{
$endIndex = null;
for ($searchEndIndex = $index + 1; $searchEndIndex < $parentScopeEndIndex; ++$searchEndIndex) {
$searchEndToken = $tokens[$searchEndIndex];
if ($searchEndToken->equalsAny(['(', '{']) || $searchEndToken->isGivenKind(CT::T_ARRAY_SQUARE_BRACE_OPEN)) {
$type = Tokens::detectBlockType($searchEndToken);
$searchEndIndex = $tokens->findBlockEnd(
$type['type'],
$searchEndIndex
);
continue;
}
if ($searchEndToken->equals(',')) {
$endIndex = $tokens->getPrevMeaningfulToken($searchEndIndex);
break;
}
}
return $endIndex ?? $tokens->getPrevMeaningfulToken($parentScopeEndIndex);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Whitespace;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Tokens;
final class NoSpacesAroundOffsetFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'There MUST NOT be spaces around offset braces.',
[
new CodeSample("<?php\n\$sample = \$b [ 'a' ] [ 'b' ];\n"),
new CodeSample("<?php\n\$sample = \$b [ 'a' ] [ 'b' ];\n", ['positions' => ['inside']]),
new CodeSample("<?php\n\$sample = \$b [ 'a' ] [ 'b' ];\n", ['positions' => ['outside']]),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound(['[', CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->equalsAny(['[', [CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN]])) {
continue;
}
if (\in_array('inside', $this->configuration['positions'], true)) {
if ($token->equals('[')) {
$endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_INDEX_SQUARE_BRACE, $index);
} else {
$endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ARRAY_INDEX_CURLY_BRACE, $index);
}
if ($tokens[$index + 1]->isWhitespace(" \t")) {
$tokens->clearAt($index + 1);
}
if ($tokens[$endIndex - 1]->isWhitespace(" \t")) {
$tokens->clearAt($endIndex - 1);
}
}
if (\in_array('outside', $this->configuration['positions'], true)) {
$prevNonWhitespaceIndex = $tokens->getPrevNonWhitespace($index);
if ($tokens[$prevNonWhitespaceIndex]->isComment()) {
continue;
}
$tokens->removeLeadingWhitespace($index);
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
$values = ['inside', 'outside'];
return new FixerConfigurationResolver([
(new FixerOptionBuilder('positions', 'Whether spacing should be fixed inside and/or outside the offset braces.'))
->setAllowedTypes(['array'])
->setAllowedValues([new AllowedValueSubset($values)])
->setDefault($values)
->getOption(),
]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Whitespace;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Tokens;
final class NoWhitespaceInBlankLineFixer extends AbstractFixer implements WhitespacesAwareFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Remove trailing whitespace at the end of blank lines.',
[new CodeSample("<?php\n \n\$a = 1;\n")]
);
}
public function getPriority(): int
{
return -19;
}
public function isCandidate(Tokens $tokens): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($i = 1, $count = \count($tokens); $i < $count; ++$i) {
if ($tokens[$i]->isWhitespace()) {
$this->fixWhitespaceToken($tokens, $i);
}
}
}
private function fixWhitespaceToken(Tokens $tokens, int $index): void
{
$content = $tokens[$index]->getContent();
$lines = Preg::split("/(\r\n|\n)/", $content);
$lineCount = \count($lines);
if (
$lineCount > 2
|| ($lineCount > 0 && (!isset($tokens[$index + 1]) || $tokens[$index - 1]->isGivenKind(T_OPEN_TAG)))
) {
$lMax = isset($tokens[$index + 1]) ? $lineCount - 1 : $lineCount;
$lStart = 1;
if ($tokens[$index - 1]->isGivenKind(T_OPEN_TAG) && "\n" === substr($tokens[$index - 1]->getContent(), -1)) {
$lStart = 0;
}
for ($l = $lStart; $l < $lMax; ++$l) {
$lines[$l] = Preg::replace('/^\h+$/', '', $lines[$l]);
}
$content = implode($this->whitespacesConfig->getLineEnding(), $lines);
$tokens->ensureWhitespaceAtIndex($index, 0, $content);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Whitespace;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class BlankLineBeforeStatementFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
{
private static array $tokenMap = [
'break' => T_BREAK,
'case' => T_CASE,
'continue' => T_CONTINUE,
'declare' => T_DECLARE,
'default' => T_DEFAULT,
'do' => T_DO,
'exit' => T_EXIT,
'for' => T_FOR,
'foreach' => T_FOREACH,
'goto' => T_GOTO,
'if' => T_IF,
'include' => T_INCLUDE,
'include_once' => T_INCLUDE_ONCE,
'phpdoc' => T_DOC_COMMENT,
'require' => T_REQUIRE,
'require_once' => T_REQUIRE_ONCE,
'return' => T_RETURN,
'switch' => T_SWITCH,
'throw' => T_THROW,
'try' => T_TRY,
'while' => T_WHILE,
'yield' => T_YIELD,
'yield_from' => T_YIELD_FROM,
];
private array $fixTokenMap = [];
public function configure(array $configuration): void
{
parent::configure($configuration);
$this->fixTokenMap = [];
foreach ($this->configuration['statements'] as $key) {
$this->fixTokenMap[$key] = self::$tokenMap[$key];
}
$this->fixTokenMap = array_values($this->fixTokenMap);
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'An empty line feed must precede any configured statement.',
[
new CodeSample(
'<?php
function A() {
echo 1;
return 1;
}
'
),
new CodeSample(
'<?php
switch ($foo) {
case 42:
$bar->process();
break;
case 44:
break;
}
',
[
'statements' => ['break'],
]
),
new CodeSample(
'<?php
foreach ($foo as $bar) {
if ($bar->isTired()) {
$bar->sleep();
continue;
}
}
',
[
'statements' => ['continue'],
]
),
new CodeSample(
'<?php
$i = 0;
do {
echo $i;
} while ($i > 0);
',
[
'statements' => ['do'],
]
),
new CodeSample(
'<?php
if ($foo === false) {
exit(0);
} else {
$bar = 9000;
exit(1);
}
',
[
'statements' => ['exit'],
]
),
new CodeSample(
'<?php
a:
if ($foo === false) {
goto a;
} else {
$bar = 9000;
goto b;
}
',
[
'statements' => ['goto'],
]
),
new CodeSample(
'<?php
$a = 9000;
if (true) {
$foo = $bar;
}
',
[
'statements' => ['if'],
]
),
new CodeSample(
'<?php
if (true) {
$foo = $bar;
return;
}
',
[
'statements' => ['return'],
]
),
new CodeSample(
'<?php
$a = 9000;
switch ($a) {
case 42:
break;
}
',
[
'statements' => ['switch'],
]
),
new CodeSample(
'<?php
if (null === $a) {
$foo->bar();
throw new \UnexpectedValueException("A cannot be null.");
}
',
[
'statements' => ['throw'],
]
),
new CodeSample(
'<?php
$a = 9000;
try {
$foo->bar();
} catch (\Exception $exception) {
$a = -1;
}
',
[
'statements' => ['try'],
]
),
new CodeSample(
'<?php
if (true) {
$foo = $bar;
yield $foo;
}
',
[
'statements' => ['yield'],
]
),
]
);
}
public function getPriority(): int
{
return -21;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound($this->fixTokenMap);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$analyzer = new TokensAnalyzer($tokens);
for ($index = $tokens->count() - 1; $index > 0; --$index) {
$token = $tokens[$index];
if (!$token->isGivenKind($this->fixTokenMap)) {
continue;
}
if ($token->isGivenKind(T_WHILE) && $analyzer->isWhilePartOfDoWhile($index)) {
continue;
}
$prevNonWhitespace = $tokens->getPrevNonWhitespace($index);
if ($this->shouldAddBlankLine($tokens, $prevNonWhitespace)) {
$this->insertBlankLine($tokens, $index);
}
$index = $prevNonWhitespace;
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('statements', 'List of statements which must be preceded by an empty line.'))
->setAllowedTypes(['array'])
->setAllowedValues([new AllowedValueSubset(array_keys(self::$tokenMap))])
->setDefault([
'break',
'continue',
'declare',
'return',
'throw',
'try',
])
->getOption(),
]);
}
private function shouldAddBlankLine(Tokens $tokens, int $prevNonWhitespace): bool
{
$prevNonWhitespaceToken = $tokens[$prevNonWhitespace];
if ($prevNonWhitespaceToken->isComment()) {
for ($j = $prevNonWhitespace - 1; $j >= 0; --$j) {
if (str_contains($tokens[$j]->getContent(), "\n")) {
return false;
}
if ($tokens[$j]->isWhitespace() || $tokens[$j]->isComment()) {
continue;
}
return $tokens[$j]->equalsAny([';', '}']);
}
}
return $prevNonWhitespaceToken->equalsAny([';', '}']);
}
private function insertBlankLine(Tokens $tokens, int $index): void
{
$prevIndex = $index - 1;
$prevToken = $tokens[$prevIndex];
$lineEnding = $this->whitespacesConfig->getLineEnding();
if ($prevToken->isWhitespace()) {
$newlinesCount = substr_count($prevToken->getContent(), "\n");
if (0 === $newlinesCount) {
$tokens[$prevIndex] = new Token([T_WHITESPACE, rtrim($prevToken->getContent(), " \t").$lineEnding.$lineEnding]);
} elseif (1 === $newlinesCount) {
$tokens[$prevIndex] = new Token([T_WHITESPACE, $lineEnding.$prevToken->getContent()]);
}
} else {
$tokens->insertAt($index, new Token([T_WHITESPACE, $lineEnding.$lineEnding]));
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Whitespace;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\FixerDefinition\VersionSpecification;
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Analyzer\WhitespacesAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class HeredocIndentationFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Heredoc/nowdoc content must be properly indented. Requires PHP >= 7.3.',
[
new VersionSpecificCodeSample(
<<<'SAMPLE'
<?php
$a = <<<EOD
abc
def
EOD;
SAMPLE
,
new VersionSpecification(70300)
),
new VersionSpecificCodeSample(
<<<'SAMPLE'
<?php
$a = <<<'EOD'
abc
def
EOD;
SAMPLE
,
new VersionSpecification(70300)
),
new VersionSpecificCodeSample(
<<<'SAMPLE'
<?php
$a = <<<'EOD'
abc
def
EOD;
SAMPLE
,
new VersionSpecification(70300),
['indentation' => 'same_as_start']
),
]
);
}
public function getPriority(): int
{
return -26;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_START_HEREDOC);
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('indentation', 'Whether the indentation should be the same as in the start token line or one level more.'))
->setAllowedValues(['start_plus_one', 'same_as_start'])
->setDefault('start_plus_one')
->getOption(),
]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = \count($tokens) - 1; 0 <= $index; --$index) {
if (!$tokens[$index]->isGivenKind(T_END_HEREDOC)) {
continue;
}
$end = $index;
$index = $tokens->getPrevTokenOfKind($index, [[T_START_HEREDOC]]);
$this->fixIndentation($tokens, $index, $end);
}
}
private function fixIndentation(Tokens $tokens, int $start, int $end): void
{
$indent = WhitespacesAnalyzer::detectIndent($tokens, $start);
if ('start_plus_one' === $this->configuration['indentation']) {
$indent .= $this->whitespacesConfig->getIndent();
}
Preg::match('/^\h*/', $tokens[$end]->getContent(), $matches);
$currentIndent = $matches[0];
$currentIndentLength = \strlen($currentIndent);
$content = $indent.substr($tokens[$end]->getContent(), $currentIndentLength);
$tokens[$end] = new Token([T_END_HEREDOC, $content]);
if ($end === $start + 1) {
return;
}
for ($index = $end - 1, $last = true; $index > $start; --$index, $last = false) {
if (!$tokens[$index]->isGivenKind([T_ENCAPSED_AND_WHITESPACE, T_WHITESPACE])) {
continue;
}
$content = $tokens[$index]->getContent();
if ('' !== $currentIndent) {
$content = Preg::replace('/(?<=\v)(?!'.$currentIndent.')\h+/', '', $content);
}
$regexEnd = $last && !$currentIndent ? '(?!\v|$)' : '(?!\v)';
$content = Preg::replace('/(?<=\v)'.$currentIndent.$regexEnd.'/', $indent, $content);
$tokens[$index] = new Token([$tokens[$index]->getId(), $content]);
}
++$index;
if (!$tokens[$index]->isGivenKind(T_ENCAPSED_AND_WHITESPACE)) {
$tokens->insertAt($index, new Token([T_ENCAPSED_AND_WHITESPACE, $indent]));
return;
}
$content = $tokens[$index]->getContent();
if (!\in_array($content[0], ["\r", "\n"], true) && (!$currentIndent || str_starts_with($content, $currentIndent))) {
$content = $indent.substr($content, $currentIndentLength);
} elseif ($currentIndent) {
$content = Preg::replace('/^(?!'.$currentIndent.')\h+/', '', $content);
}
$tokens[$index] = new Token([T_ENCAPSED_AND_WHITESPACE, $content]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Whitespace;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\FixerDefinition\VersionSpecification;
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class TypesSpacesFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public function configure(array $configuration): void
{
parent::configure($configuration);
if (!isset($this->configuration['space_multiple_catch'])) {
$this->configuration['space_multiple_catch'] = $this->configuration['space'];
}
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'A single space or none should be around union type and intersection type operators.',
[
new CodeSample(
"<?php\ntry\n{\n new Foo();\n} catch (ErrorA | ErrorB \$e) {\necho'error';}\n"
),
new CodeSample(
"<?php\ntry\n{\n new Foo();\n} catch (ErrorA|ErrorB \$e) {\necho'error';}\n",
['space' => 'single']
),
new VersionSpecificCodeSample(
"<?php\nfunction foo(int | string \$x)\n{\n}\n",
new VersionSpecification(80000)
),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([CT::T_TYPE_ALTERNATION, CT::T_TYPE_INTERSECTION]);
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('space', 'spacing to apply around union type and intersection type operators.'))
->setAllowedValues(['none', 'single'])
->setDefault('none')
->getOption(),
(new FixerOptionBuilder('space_multiple_catch', 'spacing to apply around type operator when catching exceptions of multiple types, use `null` to follow the value configured for `space`.'))
->setAllowedValues(['none', 'single', null])
->setDefault(null)
->getOption(),
]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$tokenCount = $tokens->count() - 1;
for ($index = 0; $index < $tokenCount; ++$index) {
if ($tokens[$index]->isGivenKind([CT::T_TYPE_ALTERNATION, CT::T_TYPE_INTERSECTION])) {
$tokenCount += $this->fixSpacing($tokens, $index, 'single' === $this->configuration['space']);
continue;
}
if ($tokens[$index]->isGivenKind(T_CATCH)) {
while (true) {
$index = $tokens->getNextTokenOfKind($index, [')', [CT::T_TYPE_ALTERNATION]]);
if ($tokens[$index]->equals(')')) {
break;
}
$tokenCount += $this->fixSpacing($tokens, $index, 'single' === $this->configuration['space_multiple_catch']);
}
}
}
}
private function fixSpacing(Tokens $tokens, int $index, bool $singleSpace): int
{
if (!$singleSpace) {
$this->ensureNoSpace($tokens, $index + 1);
$this->ensureNoSpace($tokens, $index - 1);
return 0;
}
$addedTokenCount = 0;
$addedTokenCount += $this->ensureSingleSpace($tokens, $index + 1, 0);
$addedTokenCount += $this->ensureSingleSpace($tokens, $index - 1, 1);
return $addedTokenCount;
}
private function ensureSingleSpace(Tokens $tokens, int $index, int $offset): int
{
if (!$tokens[$index]->isWhitespace()) {
$tokens->insertSlices([$index + $offset => new Token([T_WHITESPACE, ' '])]);
return 1;
}
if (' ' !== $tokens[$index]->getContent() && 1 !== Preg::match('/\R/', $tokens[$index]->getContent())) {
$tokens[$index] = new Token([T_WHITESPACE, ' ']);
}
return 0;
}
private function ensureNoSpace(Tokens $tokens, int $index): void
{
if ($tokens[$index]->isWhitespace() && 1 !== Preg::match('/\R/', $tokens[$index]->getContent())) {
$tokens->clearAt($index);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Whitespace;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class BlankLineBetweenImportGroupsFixer extends AbstractFixer implements WhitespacesAwareFixerInterface
{
private const IMPORT_TYPE_CLASS = 'class';
private const IMPORT_TYPE_CONST = 'const';
private const IMPORT_TYPE_FUNCTION = 'function';
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Putting blank lines between `use` statement groups.',
[
new CodeSample(
'<?php
use function AAC;
use const AAB;
use AAA;
'
),
new CodeSample(
'<?php
use const AAAA;
use const BBB;
use Bar;
use AAC;
use Acme;
use function CCC\AA;
use function DDD;
'
),
new CodeSample(
'<?php
use const BBB;
use const AAAA;
use Acme;
use AAC;
use Bar;
use function DDD;
use function CCC\AA;
'
),
new CodeSample(
'<?php
use const AAAA;
use const BBB;
use Acme;
use function DDD;
use AAC;
use function CCC\AA;
use Bar;
'
),
]
);
}
public function getPriority(): int
{
return -40;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_USE);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$tokensAnalyzer = new TokensAnalyzer($tokens);
$namespacesImports = $tokensAnalyzer->getImportUseIndexes(true);
foreach (array_reverse($namespacesImports) as $uses) {
$this->walkOverUses($tokens, $uses);
}
}
private function walkOverUses(Tokens $tokens, array $uses): void
{
$usesCount = \count($uses);
if ($usesCount < 2) {
return;
}
$previousType = null;
for ($i = $usesCount - 1; $i >= 0; --$i) {
$index = $uses[$i];
$startIndex = $tokens->getNextMeaningfulToken($index + 1);
$endIndex = $tokens->getNextTokenOfKind($startIndex, [';', [T_CLOSE_TAG]]);
if ($tokens[$startIndex]->isGivenKind(CT::T_CONST_IMPORT)) {
$type = self::IMPORT_TYPE_CONST;
} elseif ($tokens[$startIndex]->isGivenKind(CT::T_FUNCTION_IMPORT)) {
$type = self::IMPORT_TYPE_FUNCTION;
} else {
$type = self::IMPORT_TYPE_CLASS;
}
if (null !== $previousType && $type !== $previousType) {
$this->ensureLine($tokens, $endIndex + 1);
}
$previousType = $type;
}
}
private function ensureLine(Tokens $tokens, int $index): void
{
static $lineEnding;
if (null === $lineEnding) {
$lineEnding = $this->whitespacesConfig->getLineEnding();
$lineEnding .= $lineEnding;
}
$index = $this->getInsertIndex($tokens, $index);
if ($tokens[$index]->isWhitespace()) {
$tokens[$index] = new Token([T_WHITESPACE, $lineEnding]);
} else {
$tokens->insertSlices([$index + 1 => [new Token([T_WHITESPACE, $lineEnding])]]);
}
}
private function getInsertIndex(Tokens $tokens, int $index): int
{
$tokensCount = \count($tokens);
for (; $index < $tokensCount - 1; ++$index) {
if (!$tokens[$index]->isWhitespace() && !$tokens[$index]->isComment()) {
return $index - 1;
}
$content = $tokens[$index]->getContent();
if (str_contains($content, "\n")) {
return $index;
}
}
return $index;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Whitespace;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Tokens;
final class NoSpacesInsideParenthesisFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'There MUST NOT be a space after the opening parenthesis. There MUST NOT be a space before the closing parenthesis.',
[
new CodeSample("<?php\nif ( \$a ) {\n foo( );\n}\n"),
new CodeSample(
"<?php
function foo( \$bar, \$baz )
{
}\n"
),
]
);
}
public function getPriority(): int
{
return 2;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound('(');
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->equals('(')) {
continue;
}
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if (null !== $prevIndex && $tokens[$prevIndex]->isGivenKind(T_ARRAY)) {
continue;
}
$endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index);
if (!$tokens[$tokens->getNextNonWhitespace($index)]->isComment()) {
$this->removeSpaceAroundToken($tokens, $index + 1);
}
if (!$tokens[$tokens->getPrevMeaningfulToken($endIndex)]->equals(',')) {
$this->removeSpaceAroundToken($tokens, $endIndex - 1);
}
}
}
private function removeSpaceAroundToken(Tokens $tokens, int $index): void
{
$token = $tokens[$index];
if ($token->isWhitespace() && !str_contains($token->getContent(), "\n")) {
$tokens->clearAt($index);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Whitespace;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NoTrailingWhitespaceFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Remove trailing whitespace at the end of non-blank lines.',
[new CodeSample("<?php\n\$a = 1; \n")]
);
}
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = \count($tokens) - 1; $index >= 0; --$index) {
$token = $tokens[$index];
if (
$token->isGivenKind(T_OPEN_TAG)
&& $tokens->offsetExists($index + 1)
&& $tokens[$index + 1]->isWhitespace()
&& 1 === Preg::match('/(.*)\h$/', $token->getContent(), $openTagMatches)
&& 1 === Preg::match('/^(\R)(.*)$/s', $tokens[$index + 1]->getContent(), $whitespaceMatches)
) {
$tokens[$index] = new Token([T_OPEN_TAG, $openTagMatches[1].$whitespaceMatches[1]]);
$tokens->ensureWhitespaceAtIndex($index + 1, 0, $whitespaceMatches[2]);
continue;
}
if (!$token->isWhitespace()) {
continue;
}
$lines = Preg::split('/(\\R+)/', $token->getContent(), -1, PREG_SPLIT_DELIM_CAPTURE);
$linesSize = \count($lines);
if ($linesSize > 1 || !isset($tokens[$index + 1])) {
if (!$tokens[$index - 1]->isGivenKind(T_OPEN_TAG) || 1 !== Preg::match('/(.*)\R$/', $tokens[$index - 1]->getContent())) {
$lines[0] = rtrim($lines[0], " \t");
}
for ($i = 1; $i < $linesSize; ++$i) {
$trimmedLine = rtrim($lines[$i], " \t");
if ('' !== $trimmedLine) {
$lines[$i] = $trimmedLine;
}
}
$content = implode('', $lines);
if ('' !== $content) {
$tokens[$index] = new Token([$token->getId(), $content]);
} else {
$tokens->clearAt($index);
}
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Whitespace;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class MethodChainingIndentationFixer extends AbstractFixer implements WhitespacesAwareFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Method chaining MUST be properly indented. Method chaining with different levels of indentation is not supported.',
[new CodeSample("<?php\n\$user->setEmail('voff.web@gmail.com')\n ->setPassword('233434');\n")]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound(Token::getObjectOperatorKinds());
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$lineEnding = $this->whitespacesConfig->getLineEnding();
for ($index = 1, $count = \count($tokens); $index < $count; ++$index) {
if (!$tokens[$index]->isObjectOperator()) {
continue;
}
$endParenthesisIndex = $tokens->getNextTokenOfKind($index, ['(', ';', ',', [T_CLOSE_TAG]]);
if (null === $endParenthesisIndex || !$tokens[$endParenthesisIndex]->equals('(')) {
continue;
}
if ($this->canBeMovedToNextLine($index, $tokens)) {
$newline = new Token([T_WHITESPACE, $lineEnding]);
if ($tokens[$index - 1]->isWhitespace()) {
$tokens[$index - 1] = $newline;
} else {
$tokens->insertAt($index, $newline);
++$index;
++$endParenthesisIndex;
}
}
$currentIndent = $this->getIndentAt($tokens, $index - 1);
if (null === $currentIndent) {
continue;
}
$expectedIndent = $this->getExpectedIndentAt($tokens, $index);
if ($currentIndent !== $expectedIndent) {
$tokens[$index - 1] = new Token([T_WHITESPACE, $lineEnding.$expectedIndent]);
}
$endParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $endParenthesisIndex);
for ($searchIndex = $index + 1; $searchIndex < $endParenthesisIndex; ++$searchIndex) {
$searchToken = $tokens[$searchIndex];
if (!$searchToken->isWhitespace()) {
continue;
}
$content = $searchToken->getContent();
if (!Preg::match('/\R/', $content)) {
continue;
}
$content = Preg::replace(
'/(\R)'.$currentIndent.'(\h*)$/D',
'$1'.$expectedIndent.'$2',
$content
);
$tokens[$searchIndex] = new Token([$searchToken->getId(), $content]);
}
}
}
private function getExpectedIndentAt(Tokens $tokens, int $index): string
{
$index = $tokens->getPrevMeaningfulToken($index);
$indent = $this->whitespacesConfig->getIndent();
for ($i = $index; $i >= 0; --$i) {
if ($tokens[$i]->equals(')')) {
$i = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $i);
}
$currentIndent = $this->getIndentAt($tokens, $i);
if (null === $currentIndent) {
continue;
}
if ($this->currentLineRequiresExtraIndentLevel($tokens, $i, $index)) {
return $currentIndent.$indent;
}
return $currentIndent;
}
return $indent;
}
private function canBeMovedToNextLine(int $index, Tokens $tokens): bool
{
$prevMeaningful = $tokens->getPrevMeaningfulToken($index);
$hasCommentBefore = false;
for ($i = $index - 1; $i > $prevMeaningful; --$i) {
if ($tokens[$i]->isComment()) {
$hasCommentBefore = true;
continue;
}
if ($tokens[$i]->isWhitespace() && 1 === Preg::match('/\R/', $tokens[$i]->getContent())) {
return $hasCommentBefore;
}
}
return false;
}
private function getIndentAt(Tokens $tokens, int $index): ?string
{
if (1 === Preg::match('/\R{1}(\h*)$/', $this->getIndentContentAt($tokens, $index), $matches)) {
return $matches[1];
}
return null;
}
private function getIndentContentAt(Tokens $tokens, int $index): string
{
if (!$tokens[$index]->isGivenKind([T_WHITESPACE, T_INLINE_HTML])) {
return '';
}
$content = $tokens[$index]->getContent();
if ($tokens[$index]->isWhitespace() && $tokens[$index - 1]->isGivenKind(T_OPEN_TAG)) {
$content = $tokens[$index - 1]->getContent().$content;
}
if (Preg::match('/\R/', $content)) {
return $content;
}
return '';
}
private function currentLineRequiresExtraIndentLevel(Tokens $tokens, int $start, int $end): bool
{
$firstMeaningful = $tokens->getNextMeaningfulToken($start);
if ($tokens[$firstMeaningful]->isObjectOperator()) {
$thirdMeaningful = $tokens->getNextMeaningfulToken($tokens->getNextMeaningfulToken($firstMeaningful));
return
$tokens[$thirdMeaningful]->equals('(')
&& $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $thirdMeaningful) > $end
;
}
return
!$tokens[$end]->equals(')')
|| $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $end) >= $start
;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Whitespace;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
use PhpCsFixer\Utils;
final class NoExtraBlankLinesFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
{
private static array $availableTokens = [
'attribute',
'break',
'case',
'continue',
'curly_brace_block',
'default',
'extra',
'parenthesis_brace_block',
'return',
'square_brace_block',
'switch',
'throw',
'use',
'use_trait',
];
private array $tokenKindCallbackMap;
private array $tokenEqualsMap;
private Tokens $tokens;
private TokensAnalyzer $tokensAnalyzer;
public function configure(array $configuration): void
{
if (isset($configuration['tokens']) && \in_array('use_trait', $configuration['tokens'], true)) {
Utils::triggerDeprecation(new \RuntimeException('Option "tokens: use_trait" used in `no_extra_blank_lines` rule is deprecated, use the rule `class_attributes_separation` with `elements: trait_import` instead.'));
}
parent::configure($configuration);
$tokensConfiguration = $this->configuration['tokens'];
$this->tokenEqualsMap = [];
if (\in_array('curly_brace_block', $tokensConfiguration, true)) {
$this->tokenEqualsMap['{'] = 'fixStructureOpenCloseIfMultiLine';
}
if (\in_array('parenthesis_brace_block', $tokensConfiguration, true)) {
$this->tokenEqualsMap['('] = 'fixStructureOpenCloseIfMultiLine';
}
static $configMap = [
'attribute' => [CT::T_ATTRIBUTE_CLOSE, 'fixAfterToken'],
'break' => [T_BREAK, 'fixAfterToken'],
'case' => [T_CASE, 'fixAfterCaseToken'],
'continue' => [T_CONTINUE, 'fixAfterToken'],
'default' => [T_DEFAULT, 'fixAfterToken'],
'extra' => [T_WHITESPACE, 'removeMultipleBlankLines'],
'return' => [T_RETURN, 'fixAfterToken'],
'square_brace_block' => [CT::T_ARRAY_SQUARE_BRACE_OPEN, 'fixStructureOpenCloseIfMultiLine'],
'switch' => [T_SWITCH, 'fixAfterToken'],
'throw' => [T_THROW, 'fixAfterThrowToken'],
'use' => [T_USE, 'removeBetweenUse'],
'use_trait' => [CT::T_USE_TRAIT, 'removeBetweenUse'],
];
$this->tokenKindCallbackMap = [];
foreach ($tokensConfiguration as $config) {
if (isset($configMap[$config])) {
$this->tokenKindCallbackMap[$configMap[$config][0]] = $configMap[$config][1];
}
}
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Removes extra blank lines and/or blank lines following configuration.',
[
new CodeSample(
'<?php
$foo = array("foo");
$bar = "bar";
'
),
new CodeSample(
'<?php
switch ($foo) {
case 41:
echo "foo";
break;
case 42:
break;
}
',
['tokens' => ['break']]
),
new CodeSample(
'<?php
for ($i = 0; $i < 9000; ++$i) {
if (true) {
continue;
}
}
',
['tokens' => ['continue']]
),
new CodeSample(
'<?php
for ($i = 0; $i < 9000; ++$i) {
echo $i;
}
',
['tokens' => ['curly_brace_block']]
),
new CodeSample(
'<?php
$foo = array("foo");
$bar = "bar";
',
['tokens' => ['extra']]
),
new CodeSample(
'<?php
$foo = array(
"foo"
);
',
['tokens' => ['parenthesis_brace_block']]
),
new CodeSample(
'<?php
function foo($bar)
{
return $bar;
}
',
['tokens' => ['return']]
),
new CodeSample(
'<?php
$foo = [
"foo"
];
',
['tokens' => ['square_brace_block']]
),
new CodeSample(
'<?php
function foo($bar)
{
throw new \Exception("Hello!");
}
',
['tokens' => ['throw']]
),
new CodeSample(
'<?php
namespace Foo;
use Bar\Baz;
use Baz\Bar;
class Bar
{
}
',
['tokens' => ['use']]
),
new CodeSample(
'<?php
switch($a) {
case 1:
default:
echo 3;
}
',
['tokens' => ['switch', 'case', 'default']]
),
]
);
}
public function getPriority(): int
{
return -20;
}
public function isCandidate(Tokens $tokens): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$this->tokens = $tokens;
$this->tokensAnalyzer = new TokensAnalyzer($this->tokens);
for ($index = $tokens->getSize() - 1; $index > 0; --$index) {
$this->fixByToken($tokens[$index], $index);
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('tokens', 'List of tokens to fix.'))
->setAllowedTypes(['array'])
->setAllowedValues([new AllowedValueSubset(self::$availableTokens)])
->setDefault(['extra'])
->getOption(),
]);
}
private function fixByToken(Token $token, int $index): void
{
foreach ($this->tokenKindCallbackMap as $kind => $callback) {
if (!$token->isGivenKind($kind)) {
continue;
}
$this->{$callback}($index);
return;
}
foreach ($this->tokenEqualsMap as $equals => $callback) {
if (!$token->equals($equals)) {
continue;
}
$this->{$callback}($index);
return;
}
}
private function removeBetweenUse(int $index): void
{
$next = $this->tokens->getNextTokenOfKind($index, [';', [T_CLOSE_TAG]]);
if (null === $next || $this->tokens[$next]->isGivenKind(T_CLOSE_TAG)) {
return;
}
$nextUseCandidate = $this->tokens->getNextMeaningfulToken($next);
if (null === $nextUseCandidate || !$this->tokens[$nextUseCandidate]->isGivenKind($this->tokens[$index]->getId()) || !$this->containsLinebreak($index, $nextUseCandidate)) {
return;
}
$this->removeEmptyLinesAfterLineWithTokenAt($next);
}
private function removeMultipleBlankLines(int $index): void
{
$expected = $this->tokens[$index - 1]->isGivenKind(T_OPEN_TAG) && 1 === Preg::match('/\R$/', $this->tokens[$index - 1]->getContent()) ? 1 : 2;
$parts = Preg::split('/(.*\R)/', $this->tokens[$index]->getContent(), -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
$count = \count($parts);
if ($count > $expected) {
$this->tokens[$index] = new Token([T_WHITESPACE, implode('', \array_slice($parts, 0, $expected)).rtrim($parts[$count - 1], "\r\n")]);
}
}
private function fixAfterToken(int $index): void
{
for ($i = $index - 1; $i > 0; --$i) {
if ($this->tokens[$i]->isGivenKind(T_FUNCTION) && $this->tokensAnalyzer->isLambda($i)) {
return;
}
if ($this->tokens[$i]->isGivenKind(T_CLASS) && $this->tokensAnalyzer->isAnonymousClass($i)) {
return;
}
if ($this->tokens[$i]->isWhitespace() && str_contains($this->tokens[$i]->getContent(), "\n")) {
break;
}
}
$this->removeEmptyLinesAfterLineWithTokenAt($index);
}
private function fixAfterCaseToken(int $index): void
{
if (\defined('T_ENUM')) {
$enumSwitchIndex = $this->tokens->getPrevTokenOfKind($index, [[T_SWITCH], [T_ENUM]]);
if (!$this->tokens[$enumSwitchIndex]->isGivenKind(T_SWITCH)) {
return;
}
}
$this->removeEmptyLinesAfterLineWithTokenAt($index);
}
private function fixAfterThrowToken(int $index): void
{
if ($this->tokens[$this->tokens->getPrevMeaningfulToken($index)]->equalsAny([';', '{', '}', ':', [T_OPEN_TAG]])) {
$this->fixAfterToken($index);
}
}
private function fixStructureOpenCloseIfMultiLine(int $index): void
{
$blockTypeInfo = Tokens::detectBlockType($this->tokens[$index]);
$bodyEnd = $this->tokens->findBlockEnd($blockTypeInfo['type'], $index);
for ($i = $bodyEnd - 1; $i >= $index; --$i) {
if (str_contains($this->tokens[$i]->getContent(), "\n")) {
$this->removeEmptyLinesAfterLineWithTokenAt($i);
$this->removeEmptyLinesAfterLineWithTokenAt($index);
break;
}
}
}
private function removeEmptyLinesAfterLineWithTokenAt(int $index): void
{
$tokenCount = \count($this->tokens);
for ($end = $index; $end < $tokenCount; ++$end) {
if (
$this->tokens[$end]->equals('}')
|| str_contains($this->tokens[$end]->getContent(), "\n")
) {
break;
}
}
if ($end === $tokenCount) {
return;
}
$ending = $this->whitespacesConfig->getLineEnding();
for ($i = $end; $i < $tokenCount && $this->tokens[$i]->isWhitespace(); ++$i) {
$content = $this->tokens[$i]->getContent();
if (substr_count($content, "\n") < 1) {
continue;
}
$newContent = Preg::replace('/^.*\R(\h*)$/s', $ending.'$1', $content);
$this->tokens[$i] = new Token([T_WHITESPACE, $newContent]);
}
}
private function containsLinebreak(int $startIndex, int $endIndex): bool
{
for ($i = $endIndex; $i > $startIndex; --$i) {
if (Preg::match('/\R/', $this->tokens[$i]->getContent())) {
return true;
}
}
return false;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Whitespace;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\Indentation;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Analyzer\AlternativeSyntaxAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class StatementIndentationFixer extends AbstractFixer implements WhitespacesAwareFixerInterface
{
use Indentation;
private AlternativeSyntaxAnalyzer $alternativeSyntaxAnalyzer;
private bool $bracesFixerCompatibility;
public function __construct(bool $bracesFixerCompatibility = false)
{
parent::__construct();
$this->bracesFixerCompatibility = $bracesFixerCompatibility;
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Each statement must be indented.',
[
new CodeSample(
'<?php
if ($baz == true) {
echo "foo";
}
else {
echo "bar";
}
'
),
]
);
}
public function getPriority(): int
{
return parent::getPriority();
}
public function isCandidate(Tokens $tokens): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$this->alternativeSyntaxAnalyzer = new AlternativeSyntaxAnalyzer();
$blockSignatureFirstTokens = [
T_USE,
T_IF,
T_ELSE,
T_ELSEIF,
T_FOR,
T_FOREACH,
T_WHILE,
T_SWITCH,
T_CASE,
T_DEFAULT,
T_TRY,
T_FUNCTION,
T_CLASS,
T_INTERFACE,
T_TRAIT,
T_EXTENDS,
T_IMPLEMENTS,
];
if (\defined('T_MATCH')) {
$blockSignatureFirstTokens[] = T_MATCH;
}
$blockFirstTokens = ['{', [CT::T_DESTRUCTURING_SQUARE_BRACE_OPEN], [CT::T_USE_TRAIT], [CT::T_GROUP_IMPORT_BRACE_OPEN]];
if (\defined('T_ATTRIBUTE')) {
$blockFirstTokens[] = [T_ATTRIBUTE];
}
$endIndex = \count($tokens) - 1;
if ($tokens[$endIndex]->isWhitespace()) {
--$endIndex;
}
$lastIndent = $this->getLineIndentationWithBracesCompatibility(
$tokens,
0,
$this->extractIndent($this->computeNewLineContent($tokens, 0)),
);
$scopes = [
[
'type' => 'block',
'skip' => false,
'end_index' => $endIndex,
'end_index_inclusive' => true,
'initial_indent' => $lastIndent,
'is_indented_block' => false,
],
];
$previousLineInitialIndent = '';
$previousLineNewIndent = '';
$alternativeBlockStarts = [];
$caseBlockStarts = [];
foreach ($tokens as $index => $token) {
$currentScope = \count($scopes) - 1;
if (
$token->equalsAny($blockFirstTokens)
|| ($token->equals('(') && !$tokens[$tokens->getPrevMeaningfulToken($index)]->isGivenKind(T_ARRAY))
|| isset($alternativeBlockStarts[$index])
|| isset($caseBlockStarts[$index])
) {
$endIndexInclusive = true;
if ($token->isGivenKind([T_EXTENDS, T_IMPLEMENTS])) {
$endIndex = $tokens->getNextTokenOfKind($index, ['{']);
} elseif ($token->isGivenKind(CT::T_USE_TRAIT)) {
$endIndex = $tokens->getNextTokenOfKind($index, [';']);
} elseif ($token->equals(':')) {
if (isset($caseBlockStarts[$index])) {
[$endIndex, $endIndexInclusive] = $this->findCaseBlockEnd($tokens, $index);
} else {
$endIndex = $this->alternativeSyntaxAnalyzer->findAlternativeSyntaxBlockEnd($tokens, $alternativeBlockStarts[$index]);
}
} elseif ($token->isGivenKind(CT::T_DESTRUCTURING_SQUARE_BRACE_OPEN)) {
$endIndex = $tokens->getNextTokenOfKind($index, [[CT::T_DESTRUCTURING_SQUARE_BRACE_CLOSE]]);
} elseif ($token->isGivenKind(CT::T_GROUP_IMPORT_BRACE_OPEN)) {
$endIndex = $tokens->getNextTokenOfKind($index, [[CT::T_GROUP_IMPORT_BRACE_CLOSE]]);
} elseif ($token->equals('{')) {
$endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $index);
} elseif ($token->equals('(')) {
$endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index);
} else {
$endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ATTRIBUTE, $index);
}
if ('block_signature' === $scopes[$currentScope]['type']) {
$initialIndent = $scopes[$currentScope]['initial_indent'];
} else {
$initialIndent = $this->getLineIndentationWithBracesCompatibility($tokens, $index, $lastIndent);
}
$skip = false;
if ($this->bracesFixerCompatibility) {
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if (null !== $prevIndex) {
$prevIndex = $tokens->getPrevMeaningfulToken($prevIndex);
}
if (null !== $prevIndex && $tokens[$prevIndex]->isGivenKind([T_FUNCTION, T_FN])) {
$skip = true;
}
}
$scopes[] = [
'type' => 'block',
'skip' => $skip,
'end_index' => $endIndex,
'end_index_inclusive' => $endIndexInclusive,
'initial_indent' => $initialIndent,
'is_indented_block' => true,
];
++$currentScope;
while ($index >= $scopes[$currentScope]['end_index']) {
array_pop($scopes);
--$currentScope;
}
continue;
}
if ($token->isGivenKind($blockSignatureFirstTokens)) {
for ($endIndex = $index + 1, $max = \count($tokens); $endIndex < $max; ++$endIndex) {
if ($tokens[$endIndex]->equals('(')) {
$endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $endIndex);
continue;
}
if ($tokens[$endIndex]->equalsAny(['{', ';', [T_DOUBLE_ARROW], [T_IMPLEMENTS]])) {
break;
}
if ($tokens[$endIndex]->equals(':')) {
if ($token->isGivenKind([T_CASE, T_DEFAULT])) {
$caseBlockStarts[$endIndex] = $index;
} else {
$alternativeBlockStarts[$endIndex] = $index;
}
break;
}
}
$scopes[] = [
'type' => 'block_signature',
'skip' => false,
'end_index' => $endIndex,
'end_index_inclusive' => true,
'initial_indent' => $this->getLineIndentationWithBracesCompatibility($tokens, $index, $lastIndent),
'is_indented_block' => $token->isGivenKind([T_EXTENDS, T_IMPLEMENTS]),
];
continue;
}
if (
$token->isWhitespace()
|| ($index > 0 && $tokens[$index - 1]->isGivenKind(T_OPEN_TAG))
) {
$previousOpenTagContent = $tokens[$index - 1]->isGivenKind(T_OPEN_TAG)
? Preg::replace('/\S/', '', $tokens[$index - 1]->getContent())
: ''
;
$content = $previousOpenTagContent.($token->isWhitespace() ? $token->getContent() : '');
if (!Preg::match('/\R/', $content)) {
continue;
}
$nextToken = $tokens[$index + 1] ?? null;
if (
$this->bracesFixerCompatibility
&& null !== $nextToken
&& $nextToken->isComment()
&& !$this->isCommentWithFixableIndentation($tokens, $index + 1)
) {
continue;
}
if ('block' === $scopes[$currentScope]['type'] || 'block_signature' === $scopes[$currentScope]['type']) {
$indent = false;
if ($scopes[$currentScope]['is_indented_block']) {
$firstNonWhitespaceTokenIndex = null;
$nextNewlineIndex = null;
for ($searchIndex = $index + 1, $max = \count($tokens); $searchIndex < $max; ++$searchIndex) {
$searchToken = $tokens[$searchIndex];
if (!$searchToken->isWhitespace()) {
if (null === $firstNonWhitespaceTokenIndex) {
$firstNonWhitespaceTokenIndex = $searchIndex;
}
continue;
}
if (Preg::match('/\R/', $searchToken->getContent())) {
$nextNewlineIndex = $searchIndex;
break;
}
}
if (!$this->isCommentForControlSructureContinuation($tokens, $index + 1)) {
$endIndex = $scopes[$currentScope]['end_index'];
if (!$scopes[$currentScope]['end_index_inclusive']) {
++$endIndex;
}
if (
(null !== $firstNonWhitespaceTokenIndex && $firstNonWhitespaceTokenIndex < $endIndex)
|| (null !== $nextNewlineIndex && $nextNewlineIndex < $endIndex)
) {
$indent = true;
}
}
}
$previousLineInitialIndent = $this->extractIndent($content);
if ($scopes[$currentScope]['skip']) {
$whitespaces = $previousLineInitialIndent;
} else {
$whitespaces = $scopes[$currentScope]['initial_indent'].($indent ? $this->whitespacesConfig->getIndent() : '');
}
$content = Preg::replace(
'/(\R+)\h*$/',
'$1'.$whitespaces,
$content
);
$previousLineNewIndent = $this->extractIndent($content);
} else {
$content = Preg::replace(
'/(\R)'.$scopes[$currentScope]['initial_indent'].'(\h*)$/D',
'$1'.$scopes[$currentScope]['new_indent'].'$2',
$content
);
}
$lastIndent = $this->extractIndent($content);
if ('' !== $previousOpenTagContent) {
$content = Preg::replace("/^{$previousOpenTagContent}/", '', $content);
}
if ('' !== $content) {
$tokens->ensureWhitespaceAtIndex($index, 0, $content);
} elseif ($token->isWhitespace()) {
$tokens->clearAt($index);
}
if (null !== $nextToken && $nextToken->isComment()) {
$tokens[$index + 1] = new Token([
$nextToken->getId(),
Preg::replace(
'/(\R)'.preg_quote($previousLineInitialIndent, '/').'(\h*\S+.*)/',
'$1'.$previousLineNewIndent.'$2',
$nextToken->getContent()
),
]);
}
if ($token->isWhitespace()) {
continue;
}
}
if ($this->isNewLineToken($tokens, $index)) {
$lastIndent = $this->extractIndent($this->computeNewLineContent($tokens, $index));
}
while ($index >= $scopes[$currentScope]['end_index']) {
array_pop($scopes);
if ([] === $scopes) {
return;
}
--$currentScope;
}
if ($token->isComment() || $token->equalsAny([';', ',', '}', [T_OPEN_TAG], [T_CLOSE_TAG], [CT::T_ATTRIBUTE_CLOSE]])) {
continue;
}
if ('statement' !== $scopes[$currentScope]['type'] && 'block_signature' !== $scopes[$currentScope]['type']) {
$endIndex = $this->findStatementEndIndex($tokens, $index, $scopes[$currentScope]['end_index']);
if ($endIndex === $index) {
continue;
}
$scopes[] = [
'type' => 'statement',
'skip' => false,
'end_index' => $endIndex,
'end_index_inclusive' => false,
'initial_indent' => $previousLineInitialIndent,
'new_indent' => $previousLineNewIndent,
];
}
}
}
private function findStatementEndIndex(Tokens $tokens, int $index, int $parentScopeEndIndex): int
{
$endIndex = null;
for ($searchEndIndex = $index; $searchEndIndex <= $parentScopeEndIndex; ++$searchEndIndex) {
$searchEndToken = $tokens[$searchEndIndex];
if ($searchEndToken->equalsAny(['(', '{', [CT::T_ARRAY_SQUARE_BRACE_OPEN]])) {
if ($searchEndToken->equals('(')) {
$blockType = Tokens::BLOCK_TYPE_PARENTHESIS_BRACE;
} elseif ($searchEndToken->equals('{')) {
$blockType = Tokens::BLOCK_TYPE_CURLY_BRACE;
} else {
$blockType = Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE;
}
$searchEndIndex = $tokens->findBlockEnd($blockType, $searchEndIndex);
continue;
}
if ($searchEndToken->equalsAny([';', ',', '}', [T_CLOSE_TAG]])) {
$endIndex = $tokens->getPrevNonWhitespace($searchEndIndex);
break;
}
}
return $endIndex ?? $tokens->getPrevMeaningfulToken($parentScopeEndIndex);
}
private function findCaseBlockEnd(Tokens $tokens, int $index): array
{
for ($max = \count($tokens); $index < $max; ++$index) {
if ($tokens[$index]->isGivenKind(T_SWITCH)) {
$braceIndex = $tokens->getNextMeaningfulToken(
$tokens->findBlockEnd(
Tokens::BLOCK_TYPE_PARENTHESIS_BRACE,
$tokens->getNextMeaningfulToken($index)
)
);
if ($tokens[$braceIndex]->equals(':')) {
$index = $this->alternativeSyntaxAnalyzer->findAlternativeSyntaxBlockEnd($tokens, $index);
} else {
$index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $braceIndex);
}
continue;
}
if ($tokens[$index]->equals('{')) {
$index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $index);
continue;
}
if ($tokens[$index]->equalsAny([[T_CASE], [T_DEFAULT]])) {
return [$index, true];
}
if ($tokens[$index]->equalsAny(['}', [T_ENDSWITCH]])) {
return [$tokens->getPrevNonWhitespace($index), false];
}
}
throw new \LogicException('End of case block not found.');
}
private function getLineIndentationWithBracesCompatibility(Tokens $tokens, int $index, string $regularIndent): string
{
if (
$this->bracesFixerCompatibility
&& $tokens[$index]->isGivenKind(T_OPEN_TAG)
&& Preg::match('/\R/', $tokens[$index]->getContent())
&& isset($tokens[$index + 1])
&& $tokens[$index + 1]->isWhitespace()
&& Preg::match('/\h+$/D', $tokens[$index + 1]->getContent())
) {
return Preg::replace('/.*?(\h+)$/sD', '$1', $tokens[$index + 1]->getContent());
}
return $regularIndent;
}
private function isCommentForControlSructureContinuation(Tokens $tokens, int $index): bool
{
if (!isset($tokens[$index], $tokens[$index + 1])) {
return false;
}
if (!$tokens[$index]->isComment() || 1 !== Preg::match('~^(//|#)~', $tokens[$index]->getContent())) {
return false;
}
if (!$tokens[$index + 1]->isWhitespace() || 1 !== Preg::match('/\R/', $tokens[$index + 1]->getContent())) {
return false;
}
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if (null !== $prevIndex && $tokens[$prevIndex]->equals('{')) {
return false;
}
$index = $tokens->getNextMeaningfulToken($index + 1);
if (null === $index || !$tokens[$index]->equals('}')) {
return false;
}
$index = $tokens->getNextMeaningfulToken($index);
return null !== $index && $tokens[$index]->equalsAny([[T_ELSE], [T_ELSEIF], ',']);
}
private function isCommentWithFixableIndentation(Tokens $tokens, int $index): bool
{
if (!$tokens[$index]->isComment()) {
return false;
}
if (str_starts_with($tokens[$index]->getContent(), '/*')) {
return true;
}
$indent = preg_quote($this->whitespacesConfig->getIndent(), '~');
if (1 === Preg::match("~^(//|#)({$indent}.*)?$~", $tokens[$index]->getContent())) {
return false;
}
$firstCommentIndex = $index;
while (true) {
$i = $this->getSiblingContinuousSingleLineComment($tokens, $firstCommentIndex, false);
if (null === $i) {
break;
}
$firstCommentIndex = $i;
}
$lastCommentIndex = $index;
while (true) {
$i = $this->getSiblingContinuousSingleLineComment($tokens, $lastCommentIndex, true);
if (null === $i) {
break;
}
$lastCommentIndex = $i;
}
if ($firstCommentIndex === $lastCommentIndex) {
return true;
}
for ($i = $firstCommentIndex + 1; $i < $lastCommentIndex; ++$i) {
if (!$tokens[$i]->isWhitespace() && !$tokens[$i]->isComment()) {
return false;
}
}
return true;
}
private function getSiblingContinuousSingleLineComment(Tokens $tokens, int $index, bool $after): ?int
{
$siblingIndex = $index;
do {
if ($after) {
$siblingIndex = $tokens->getNextTokenOfKind($siblingIndex, [[T_COMMENT]]);
} else {
$siblingIndex = $tokens->getPrevTokenOfKind($siblingIndex, [[T_COMMENT]]);
}
if (null === $siblingIndex) {
return null;
}
} while (str_starts_with($tokens[$siblingIndex]->getContent(), '/*'));
$newLines = 0;
for ($i = min($siblingIndex, $index) + 1, $max = max($siblingIndex, $index); $i < $max; ++$i) {
if ($tokens[$i]->isWhitespace() && Preg::match('/\R/', $tokens[$i]->getContent())) {
if (1 === $newLines || Preg::match('/\R.*\R/', $tokens[$i]->getContent())) {
return null;
}
++$newLines;
}
}
return $siblingIndex;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Naming;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NoHomoglyphNamesFixer extends AbstractFixer
{
private static array $replacements = [
'O' => '0',
'' => '0',
'I' => '1',
'' => '1',
'' => '2',
'' => '3',
'' => '4',
'' => '5',
'' => '6',
'' => '7',
'' => '8',
'' => '9',
'Α' => 'A',
'А' => 'A',
'' => 'A',
'ʙ' => 'B',
'Β' => 'B',
'В' => 'B',
'' => 'B',
'Ϲ' => 'C',
'С' => 'C',
'' => 'C',
'' => 'C',
'' => 'D',
'' => 'D',
'Ε' => 'E',
'Е' => 'E',
'' => 'E',
'Ϝ' => 'F',
'' => 'F',
'ɢ' => 'G',
'Ԍ' => 'G',
'' => 'G',
'ʜ' => 'H',
'Η' => 'H',
'Н' => 'H',
'' => 'H',
'l' => 'I',
'Ι' => 'I',
'І' => 'I',
'' => 'I',
'' => 'I',
'Ј' => 'J',
'' => 'J',
'Κ' => 'K',
'К' => 'K',
'K' => 'K',
'' => 'K',
'ʟ' => 'L',
'' => 'L',
'' => 'L',
'Μ' => 'M',
'М' => 'M',
'' => 'M',
'' => 'M',
'ɴ' => 'N',
'Ν' => 'N',
'' => 'N',
'Ο' => 'O',
'О' => 'O',
'' => 'O',
'Ρ' => 'P',
'Р' => 'P',
'' => 'P',
'' => 'Q',
'ʀ' => 'R',
'' => 'R',
'Ѕ' => 'S',
'' => 'S',
'Τ' => 'T',
'Т' => 'T',
'' => 'T',
'' => 'U',
'Ѵ' => 'V',
'' => 'V',
'' => 'V',
'' => 'W',
'Χ' => 'X',
'Х' => 'X',
'' => 'X',
'' => 'X',
'ʏ' => 'Y',
'Υ' => 'Y',
'Ү' => 'Y',
'' => 'Y',
'Ζ' => 'Z',
'' => 'Z',
'_' => '_',
'ɑ' => 'a',
'а' => 'a',
'' => 'a',
'Ь' => 'b',
'' => 'b',
'ϲ' => 'c',
'с' => 'c',
'' => 'c',
'' => 'c',
'ԁ' => 'd',
'' => 'd',
'' => 'd',
'е' => 'e',
'' => 'e',
'' => 'f',
'ɡ' => 'g',
'' => 'g',
'һ' => 'h',
'' => 'h',
'ɩ' => 'i',
'і' => 'i',
'' => 'i',
'' => 'i',
'ј' => 'j',
'' => 'j',
'' => 'k',
'' => 'l',
'' => 'l',
'ⅿ' => 'm',
'' => 'm',
'' => 'n',
'ο' => 'o',
'о' => 'o',
'' => 'o',
'р' => 'p',
'' => 'p',
'' => 'q',
'' => 'r',
'ѕ' => 's',
'' => 's',
'' => 't',
'' => 'u',
'ν' => 'v',
'ѵ' => 'v',
'' => 'v',
'' => 'v',
'ѡ' => 'w',
'' => 'w',
'х' => 'x',
'' => 'x',
'' => 'x',
'у' => 'y',
'' => 'y',
'' => 'z',
];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Replace accidental usage of homoglyphs (non ascii characters) in names.',
[new CodeSample("<?php \$nаmе = 'wrong \"a\" character';\n")],
null,
'Renames classes and cannot rename the files. You might have string references to renamed code (`$$name`).'
);
}
public function isRisky(): bool
{
return true;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_VARIABLE, T_STRING]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind([T_VARIABLE, T_STRING])) {
continue;
}
$replaced = Preg::replaceCallback('/[^[:ascii:]]/u', static function (array $matches): string {
return self::$replacements[$matches[0]] ?? $matches[0];
}, $token->getContent(), -1, $count);
if ($count) {
$tokens->offsetSet($index, new Token([$token->getId(), $replaced]));
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Strict;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class DeclareStrictTypesFixer extends AbstractFixer implements WhitespacesAwareFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Force strict types declaration in all files. Requires PHP >= 7.0.',
[
new CodeSample(
"<?php\n"
),
],
null,
'Forcing strict types will stop non strict code from working.'
);
}
public function getPriority(): int
{
return 2;
}
public function isCandidate(Tokens $tokens): bool
{
return isset($tokens[0]) && $tokens[0]->isGivenKind(T_OPEN_TAG);
}
public function isRisky(): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$searchIndex = $tokens->getNextMeaningfulToken(0);
if (null === $searchIndex) {
$this->insertSequence($tokens);
return;
}
$sequenceLocation = $tokens->findSequence([[T_DECLARE, 'declare'], '(', [T_STRING, 'strict_types'], '=', [T_LNUMBER], ')'], $searchIndex, null, false);
if (null === $sequenceLocation) {
$this->insertSequence($tokens);
return;
}
$this->fixStrictTypesCasingAndValue($tokens, $sequenceLocation);
}
private function fixStrictTypesCasingAndValue(Tokens $tokens, array $sequence): void
{
foreach ($sequence as $index => $token) {
if ($token->isGivenKind(T_STRING)) {
$tokens[$index] = new Token([T_STRING, strtolower($token->getContent())]);
continue;
}
if ($token->isGivenKind(T_LNUMBER)) {
$tokens[$index] = new Token([T_LNUMBER, '1']);
break;
}
}
}
private function insertSequence(Tokens $tokens): void
{
$sequence = [
new Token([T_DECLARE, 'declare']),
new Token('('),
new Token([T_STRING, 'strict_types']),
new Token('='),
new Token([T_LNUMBER, '1']),
new Token(')'),
new Token(';'),
];
$endIndex = \count($sequence);
$tokens->insertAt(1, $sequence);
if (str_contains($tokens[0]->getContent(), "\n")) {
$tokens[0] = new Token([$tokens[0]->getId(), trim($tokens[0]->getContent()).' ']);
}
if ($endIndex === \count($tokens) - 1) {
return;
}
$lineEnding = $this->whitespacesConfig->getLineEnding();
if (!$tokens[1 + $endIndex]->isWhitespace()) {
$tokens->insertAt(1 + $endIndex, new Token([T_WHITESPACE, $lineEnding]));
return;
}
$content = $tokens[1 + $endIndex]->getContent();
$tokens[1 + $endIndex] = new Token([T_WHITESPACE, $lineEnding.ltrim($content, " \t")]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Strict;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class StrictComparisonFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Comparisons should be strict.',
[new CodeSample("<?php\n\$a = 1== \$b;\n")],
null,
'Changing comparisons to strict might change code behavior.'
);
}
public function getPriority(): int
{
return 38;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_IS_EQUAL, T_IS_NOT_EQUAL]);
}
public function isRisky(): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
static $map = [
T_IS_EQUAL => [
'id' => T_IS_IDENTICAL,
'content' => '===',
],
T_IS_NOT_EQUAL => [
'id' => T_IS_NOT_IDENTICAL,
'content' => '!==',
],
];
foreach ($tokens as $index => $token) {
$tokenId = $token->getId();
if (isset($map[$tokenId])) {
$tokens[$index] = new Token([$map[$tokenId]['id'], $map[$tokenId]['content']]);
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Strict;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class StrictParamFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Functions should be used with `$strict` param set to `true`.',
[new CodeSample("<?php\n\$a = array_keys(\$b);\n\$a = array_search(\$b, \$c);\n\$a = base64_decode(\$b);\n\$a = in_array(\$b, \$c);\n\$a = mb_detect_encoding(\$b, \$c);\n")],
'The functions "array_keys", "array_search", "base64_decode", "in_array" and "mb_detect_encoding" should be used with $strict param.',
'Risky when the fixed function is overridden or if the code relies on non-strict usage.'
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_STRING);
}
public function isRisky(): bool
{
return true;
}
public function getPriority(): int
{
return 31;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$functionsAnalyzer = new FunctionsAnalyzer();
static $map = null;
if (null === $map) {
$trueToken = new Token([T_STRING, 'true']);
$map = [
'array_keys' => [null, null, $trueToken],
'array_search' => [null, null, $trueToken],
'base64_decode' => [null, $trueToken],
'in_array' => [null, null, $trueToken],
'mb_detect_encoding' => [null, [new Token([T_STRING, 'mb_detect_order']), new Token('('), new Token(')')], $trueToken],
];
}
for ($index = $tokens->count() - 1; 0 <= $index; --$index) {
$token = $tokens[$index];
$nextIndex = $tokens->getNextMeaningfulToken($index);
if (null !== $nextIndex && !$tokens[$nextIndex]->equals('(')) {
continue;
}
$lowercaseContent = strtolower($token->getContent());
if (isset($map[$lowercaseContent]) && $functionsAnalyzer->isGlobalFunctionCall($tokens, $index)) {
$this->fixFunction($tokens, $index, $map[$lowercaseContent]);
}
}
}
private function fixFunction(Tokens $tokens, int $functionIndex, array $functionParams): void
{
$startBraceIndex = $tokens->getNextTokenOfKind($functionIndex, ['(']);
$endBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $startBraceIndex);
$paramsQuantity = 0;
$expectParam = true;
for ($index = $startBraceIndex + 1; $index < $endBraceIndex; ++$index) {
$token = $tokens[$index];
if ($expectParam && !$token->isWhitespace() && !$token->isComment()) {
++$paramsQuantity;
$expectParam = false;
}
if ($token->equals('(')) {
$index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index);
continue;
}
if ($token->isGivenKind(CT::T_ARRAY_SQUARE_BRACE_OPEN)) {
$index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, $index);
continue;
}
if ($token->equals(',')) {
$expectParam = true;
continue;
}
}
$functionParamsQuantity = \count($functionParams);
if ($paramsQuantity === $functionParamsQuantity) {
return;
}
$tokensToInsert = [];
for ($i = $paramsQuantity; $i < $functionParamsQuantity; ++$i) {
if (!$functionParams[$i]) {
return;
}
$tokensToInsert[] = new Token(',');
$tokensToInsert[] = new Token([T_WHITESPACE, ' ']);
if (!\is_array($functionParams[$i])) {
$tokensToInsert[] = clone $functionParams[$i];
continue;
}
foreach ($functionParams[$i] as $param) {
$tokensToInsert[] = clone $param;
}
}
$beforeEndBraceIndex = $tokens->getPrevMeaningfulToken($endBraceIndex);
if ($tokens[$beforeEndBraceIndex]->equals(',')) {
array_shift($tokensToInsert);
$tokensToInsert[] = new Token(',');
}
$tokens->insertAt($beforeEndBraceIndex + 1, $tokensToInsert);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Semicolon;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Tokens;
final class NoSinglelineWhitespaceBeforeSemicolonsFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Single-line whitespace before closing semicolon are prohibited.',
[new CodeSample("<?php \$this->foo() ;\n")]
);
}
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(';');
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->equals(';') || !$tokens[$index - 1]->isWhitespace(" \t")) {
continue;
}
if ($tokens[$index - 2]->equals(';')) {
$tokens->ensureWhitespaceAtIndex($index - 1, 0, ' ');
} elseif (!$tokens[$index - 2]->isComment()) {
$tokens->clearAt($index - 1);
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Semicolon;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class NoEmptyStatementFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Remove useless (semicolon) statements.',
[
new CodeSample("<?php \$a = 1;;\n"),
new CodeSample("<?php echo 1;2;\n"),
new CodeSample("<?php while(foo()){\n continue 1;\n}\n"),
]
);
}
public function getPriority(): int
{
return 40;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(';');
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = 0, $count = $tokens->count(); $index < $count; ++$index) {
if ($tokens[$index]->isGivenKind([T_BREAK, T_CONTINUE])) {
$index = $tokens->getNextMeaningfulToken($index);
if ($tokens[$index]->equals([T_LNUMBER, '1'])) {
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
}
continue;
}
if ($tokens[$index]->isGivenKind(T_FOR)) {
$index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $tokens->getNextMeaningfulToken($index)) + 1;
continue;
}
if (!$tokens[$index]->equals(';')) {
continue;
}
$previousMeaningfulIndex = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$previousMeaningfulIndex]->equalsAny(['{', ';', [T_OPEN_TAG]])) {
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
continue;
}
if ($tokens[$previousMeaningfulIndex]->equals('}')) {
$this->fixSemicolonAfterCurlyBraceClose($tokens, $index, $previousMeaningfulIndex);
continue;
}
$prePreviousMeaningfulIndex = $tokens->getPrevMeaningfulToken($previousMeaningfulIndex);
if (
$tokens[$prePreviousMeaningfulIndex]->equalsAny([';', '{', '}', [T_OPEN_TAG]])
&& $tokens[$previousMeaningfulIndex]->isGivenKind([T_CONSTANT_ENCAPSED_STRING, T_DNUMBER, T_LNUMBER, T_STRING, T_VARIABLE])
) {
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
$tokens->clearTokenAndMergeSurroundingWhitespace($previousMeaningfulIndex);
}
}
}
private function fixSemicolonAfterCurlyBraceClose(Tokens $tokens, int $index, int $curlyCloseIndex): void
{
static $beforeCurlyOpeningKinds = null;
if (null === $beforeCurlyOpeningKinds) {
$beforeCurlyOpeningKinds = [T_ELSE, T_FINALLY, T_NAMESPACE, T_OPEN_TAG];
}
$curlyOpeningIndex = $tokens->findBlockStart(Tokens::BLOCK_TYPE_CURLY_BRACE, $curlyCloseIndex);
$beforeCurlyOpeningIndex = $tokens->getPrevMeaningfulToken($curlyOpeningIndex);
if ($tokens[$beforeCurlyOpeningIndex]->isGivenKind($beforeCurlyOpeningKinds) || $tokens[$beforeCurlyOpeningIndex]->equalsAny([';', '{', '}'])) {
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
return;
}
if ($tokens[$beforeCurlyOpeningIndex]->isGivenKind(T_STRING)) {
$classyTestIndex = $tokens->getPrevMeaningfulToken($beforeCurlyOpeningIndex);
while ($tokens[$classyTestIndex]->equals(',') || $tokens[$classyTestIndex]->isGivenKind([T_STRING, T_NS_SEPARATOR, T_EXTENDS, T_IMPLEMENTS])) {
$classyTestIndex = $tokens->getPrevMeaningfulToken($classyTestIndex);
}
$tokensAnalyzer = new TokensAnalyzer($tokens);
if (
$tokens[$classyTestIndex]->isGivenKind(T_NAMESPACE)
|| ($tokens[$classyTestIndex]->isClassy() && !$tokensAnalyzer->isAnonymousClass($classyTestIndex))
) {
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
}
return;
}
if (!$tokens[$beforeCurlyOpeningIndex]->equals(')')) {
return;
}
$openingBraceIndex = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $beforeCurlyOpeningIndex);
$beforeOpeningBraceIndex = $tokens->getPrevMeaningfulToken($openingBraceIndex);
if ($tokens[$beforeOpeningBraceIndex]->isGivenKind([T_IF, T_ELSEIF, T_FOR, T_FOREACH, T_WHILE, T_SWITCH, T_CATCH, T_DECLARE])) {
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
return;
}
if ($tokens[$beforeOpeningBraceIndex]->isGivenKind(T_STRING)) {
$beforeStringIndex = $tokens->getPrevMeaningfulToken($beforeOpeningBraceIndex);
if ($tokens[$beforeStringIndex]->isGivenKind(T_FUNCTION)) {
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Semicolon;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class SpaceAfterSemicolonFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Fix whitespace after a semicolon.',
[
new CodeSample(
"<?php
sample(); \$test = 1;
sample();\$test = 2;
for ( ;;++\$sample) {
}\n"
),
new CodeSample("<?php\nfor (\$i = 0; ; ++\$i) {\n}\n", [
'remove_in_empty_for_expressions' => true,
]),
]
);
}
public function getPriority(): int
{
return -1;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(';');
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('remove_in_empty_for_expressions', 'Whether spaces should be removed for empty `for` expressions.'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$insideForParenthesesUntil = null;
for ($index = 0, $max = \count($tokens) - 1; $index < $max; ++$index) {
if (true === $this->configuration['remove_in_empty_for_expressions']) {
if ($tokens[$index]->isGivenKind(T_FOR)) {
$index = $tokens->getNextMeaningfulToken($index);
$insideForParenthesesUntil = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index);
continue;
}
if ($index === $insideForParenthesesUntil) {
$insideForParenthesesUntil = null;
continue;
}
}
if (!$tokens[$index]->equals(';')) {
continue;
}
if (!$tokens[$index + 1]->isWhitespace()) {
if (
!$tokens[$index + 1]->equalsAny([')', [T_INLINE_HTML]]) && (
false === $this->configuration['remove_in_empty_for_expressions']
|| !$tokens[$index + 1]->equals(';')
)
) {
$tokens->insertAt($index + 1, new Token([T_WHITESPACE, ' ']));
++$max;
}
continue;
}
if (
null !== $insideForParenthesesUntil
&& ($tokens[$index + 2]->equals(';') || $index + 2 === $insideForParenthesesUntil)
&& !Preg::match('/\R/', $tokens[$index + 1]->getContent())
) {
$tokens->clearAt($index + 1);
continue;
}
if (
isset($tokens[$index + 2])
&& !$tokens[$index + 1]->equals([T_WHITESPACE, ' '])
&& $tokens[$index + 1]->isWhitespace(" \t")
&& !$tokens[$index + 2]->isComment()
&& !$tokens[$index + 2]->equals(')')
) {
$tokens[$index + 1] = new Token([T_WHITESPACE, ' ']);
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Semicolon;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class MultilineWhitespaceBeforeSemicolonsFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
{
public const STRATEGY_NO_MULTI_LINE = 'no_multi_line';
public const STRATEGY_NEW_LINE_FOR_CHAINED_CALLS = 'new_line_for_chained_calls';
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Forbid multi-line whitespace before the closing semicolon or move the semicolon to the new line for chained calls.',
[
new CodeSample(
'<?php
function foo () {
return 1 + 2
;
}
'
),
new CodeSample(
'<?php
$this->method1()
->method2()
->method(3);
?>
',
['strategy' => self::STRATEGY_NEW_LINE_FOR_CHAINED_CALLS]
),
]
);
}
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(';');
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder(
'strategy',
'Forbid multi-line whitespace or move the semicolon to the new line for chained calls.'
))
->setAllowedValues([self::STRATEGY_NO_MULTI_LINE, self::STRATEGY_NEW_LINE_FOR_CHAINED_CALLS])
->setDefault(self::STRATEGY_NO_MULTI_LINE)
->getOption(),
]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
if (self::STRATEGY_NEW_LINE_FOR_CHAINED_CALLS === $this->configuration['strategy']) {
$this->applyChainedCallsFix($tokens);
return;
}
if (self::STRATEGY_NO_MULTI_LINE === $this->configuration['strategy']) {
$this->applyNoMultiLineFix($tokens);
}
}
private function applyNoMultiLineFix(Tokens $tokens): void
{
$lineEnding = $this->whitespacesConfig->getLineEnding();
foreach ($tokens as $index => $token) {
if (!$token->equals(';')) {
continue;
}
$previousIndex = $index - 1;
$previous = $tokens[$previousIndex];
if (!$previous->isWhitespace() || !str_contains($previous->getContent(), "\n")) {
continue;
}
$content = $previous->getContent();
if (str_starts_with($content, $lineEnding) && $tokens[$index - 2]->isComment()) {
$tokens->ensureWhitespaceAtIndex($previousIndex, 0, $lineEnding);
} else {
$tokens->clearAt($previousIndex);
}
}
}
private function applyChainedCallsFix(Tokens $tokens): void
{
for ($index = \count($tokens) - 1; $index >= 0; --$index) {
if (!$tokens[$index]->equals(';')) {
continue;
}
$indent = $this->findWhitespaceBeforeFirstCall($index - 1, $tokens);
if (null === $indent) {
continue;
}
$tokens->clearAt($index);
$index = $this->getNewLineIndex($index, $tokens);
$lineEnding = $this->whitespacesConfig->getLineEnding();
$newline = new Token([T_WHITESPACE, $lineEnding.$indent]);
$tokens->insertAt($index, [$newline, new Token(';')]);
}
}
private function getNewLineIndex(int $index, Tokens $tokens): int
{
$lineEnding = $this->whitespacesConfig->getLineEnding();
for ($index, $count = \count($tokens); $index < $count; ++$index) {
if (false !== strstr($tokens[$index]->getContent(), $lineEnding)) {
return $index;
}
}
return $index;
}
private function findWhitespaceBeforeFirstCall(int $index, Tokens $tokens): ?string
{
if (!$tokens[$index]->equals(')')) {
return null;
}
$openingBrackets = 1;
for (--$index; $index > 0; --$index) {
if ($tokens[$index]->equals(')')) {
++$openingBrackets;
continue;
}
if ($tokens[$index]->equals('(')) {
if (1 === $openingBrackets) {
break;
}
--$openingBrackets;
}
}
if (!$tokens[--$index]->isGivenKind(T_STRING)) {
return null;
}
if (!$tokens[--$index]->isObjectOperator() && !$tokens[$index]->isGivenKind(T_DOUBLE_COLON)) {
return null;
}
if (!$tokens[--$index]->isGivenKind(T_WHITESPACE)) {
return null;
}
$closingBrackets = 0;
for ($index; $index >= 0; --$index) {
if ($tokens[$index]->equals(')')) {
++$closingBrackets;
}
if ($tokens[$index]->equals('(')) {
--$closingBrackets;
}
if ($tokens[$index]->isGivenKind([T_VARIABLE, T_RETURN, T_STRING]) && 0 === $closingBrackets) {
if ($tokens[--$index]->isGivenKind(T_WHITESPACE)
|| $tokens[$index]->isGivenKind(T_OPEN_TAG)) {
return $this->getIndentAt($tokens, $index);
}
}
}
return null;
}
private function getIndentAt(Tokens $tokens, int $index): ?string
{
$content = '';
$lineEnding = $this->whitespacesConfig->getLineEnding();
for ($index; $index > 0; --$index) {
if (false !== strstr($tokens[$index]->getContent(), $lineEnding)) {
break;
}
}
if ($tokens[$index]->isWhitespace()) {
$content = $tokens[$index]->getContent();
--$index;
}
if ($tokens[$index]->isGivenKind(T_OPEN_TAG)) {
$content = $tokens[$index]->getContent().$content;
}
if (1 === Preg::match('/\R{1}(\h*)$/', $content, $matches)) {
return $matches[1];
}
return null;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Semicolon;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class SemicolonAfterInstructionFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Instructions must be terminated with a semicolon.',
[new CodeSample("<?php echo 1 ?>\n")]
);
}
public function getPriority(): int
{
return 2;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_CLOSE_TAG);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = \count($tokens) - 1; $index > 1; --$index) {
if (!$tokens[$index]->isGivenKind(T_CLOSE_TAG)) {
continue;
}
$prev = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$prev]->equalsAny([';', '{', '}', ':', [T_OPEN_TAG]])) {
continue;
}
$tokens->insertAt($prev + 1, new Token(';'));
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Indicator\PhpUnitTestCaseIndicator;
use PhpCsFixer\Tokenizer\Tokens;
abstract class AbstractPhpUnitFixer extends AbstractFixer
{
final public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAllTokenKindsFound([T_CLASS, T_STRING]);
}
final protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$phpUnitTestCaseIndicator = new PhpUnitTestCaseIndicator();
foreach ($phpUnitTestCaseIndicator->findPhpUnitClasses($tokens) as $indices) {
$this->applyPhpUnitClassFix($tokens, $indices[0], $indices[1]);
}
}
abstract protected function applyPhpUnitClassFix(Tokens $tokens, int $startIndex, int $endIndex): void;
final protected function getDocBlockIndex(Tokens $tokens, int $index): int
{
do {
$index = $tokens->getPrevNonWhitespace($index);
} while ($tokens[$index]->isGivenKind([T_PUBLIC, T_PROTECTED, T_PRIVATE, T_FINAL, T_ABSTRACT, T_COMMENT]));
return $index;
}
final protected function isPHPDoc(Tokens $tokens, int $index): bool
{
return $tokens[$index]->isGivenKind(T_DOC_COMMENT);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Import;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\TypeAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\NamespacesAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\NamespaceUsesAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class FullyQualifiedStrictTypesFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Transforms imported FQCN parameters and return types in function arguments to short version.',
[
new CodeSample(
'<?php
use Foo\Bar;
class SomeClass
{
public function doSomething(\Foo\Bar $foo)
{
}
}
'
),
new CodeSample(
'<?php
use Foo\Bar;
use Foo\Bar\Baz;
class SomeClass
{
public function doSomething(\Foo\Bar $foo): \Foo\Bar\Baz
{
}
}
'
),
]
);
}
public function getPriority(): int
{
return 7;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_FUNCTION);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$namespacesAnalyzer = new NamespacesAnalyzer();
$namespaceUsesAnalyzer = new NamespaceUsesAnalyzer();
$functionsAnalyzer = new FunctionsAnalyzer();
foreach ($namespacesAnalyzer->getDeclarations($tokens) as $namespace) {
$namespaceName = strtolower($namespace->getFullName());
$uses = [];
foreach ($namespaceUsesAnalyzer->getDeclarationsInNamespace($tokens, $namespace) as $use) {
$uses[strtolower(ltrim($use->getFullName(), '\\'))] = $use->getShortName();
}
for ($index = $namespace->getScopeStartIndex(); $index < $namespace->getScopeEndIndex(); ++$index) {
if ($tokens[$index]->isGivenKind(T_FUNCTION)) {
$this->fixFunction($functionsAnalyzer, $tokens, $index, $uses, $namespaceName);
}
}
}
}
private function fixFunction(FunctionsAnalyzer $functionsAnalyzer, Tokens $tokens, int $index, array $uses, string $namespaceName): void
{
$arguments = $functionsAnalyzer->getFunctionArguments($tokens, $index);
foreach ($arguments as $argument) {
if ($argument->hasTypeAnalysis()) {
$this->replaceByShortType($tokens, $argument->getTypeAnalysis(), $uses, $namespaceName);
}
}
$returnTypeAnalysis = $functionsAnalyzer->getFunctionReturnType($tokens, $index);
if (null !== $returnTypeAnalysis) {
$this->replaceByShortType($tokens, $returnTypeAnalysis, $uses, $namespaceName);
}
}
private function replaceByShortType(Tokens $tokens, TypeAnalysis $type, array $uses, string $namespaceName): void
{
if ($type->isReservedType()) {
return;
}
$typeStartIndex = $type->getStartIndex();
if ($tokens[$typeStartIndex]->isGivenKind(CT::T_NULLABLE_TYPE)) {
$typeStartIndex = $tokens->getNextMeaningfulToken($typeStartIndex);
}
$namespaceNameLength = \strlen($namespaceName);
$types = $this->getTypes($tokens, $typeStartIndex, $type->getEndIndex());
foreach ($types as $typeName => [$startIndex, $endIndex]) {
if (!str_starts_with($typeName, '\\')) {
continue;
}
$typeName = substr($typeName, 1);
$typeNameLower = strtolower($typeName);
if (isset($uses[$typeNameLower])) {
$tokens->overrideRange($startIndex, $endIndex, $this->namespacedStringToTokens($uses[$typeNameLower]));
} elseif ('' === $namespaceName) {
foreach ($uses as $useShortName) {
if (strtolower($useShortName) === $typeNameLower) {
continue 2;
}
}
$tokens->overrideRange($startIndex, $endIndex, $this->namespacedStringToTokens($typeName));
} elseif ($typeNameLower !== $namespaceName && str_starts_with($typeNameLower, $namespaceName)) {
$typeNameShort = substr($typeName, $namespaceNameLength + 1);
$tokens->overrideRange($startIndex, $endIndex, $this->namespacedStringToTokens($typeNameShort));
}
}
}
private function getTypes(Tokens $tokens, int $index, int $endIndex): iterable
{
$index = $typeStartIndex = $typeEndIndex = $tokens->getNextMeaningfulToken($index - 1);
$type = $tokens[$index]->getContent();
while (true) {
$index = $tokens->getNextMeaningfulToken($index);
if ($tokens[$index]->isGivenKind([CT::T_TYPE_ALTERNATION, CT::T_TYPE_INTERSECTION])) {
yield $type => [$typeStartIndex, $typeEndIndex];
$index = $typeStartIndex = $typeEndIndex = $tokens->getNextMeaningfulToken($index);
$type = $tokens[$index]->getContent();
continue;
}
if ($index > $endIndex || !$tokens[$index]->isGivenKind([T_STRING, T_NS_SEPARATOR])) {
yield $type => [$typeStartIndex, $typeEndIndex];
break;
}
$typeEndIndex = $index;
$type .= $tokens[$index]->getContent();
}
}
private function namespacedStringToTokens(string $input): array
{
$tokens = [];
$parts = explode('\\', $input);
foreach ($parts as $index => $part) {
$tokens[] = new Token([T_STRING, $part]);
if ($index !== \count($parts) - 1) {
$tokens[] = new Token([T_NS_SEPARATOR, '\\']);
}
}
return $tokens;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Import;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceUseAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\GotoLabelAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\NamespacesAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\NamespaceUsesAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class NoUnusedImportsFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Unused `use` statements must be removed.',
[new CodeSample("<?php\nuse \\DateTime;\nuse \\Exception;\n\nnew DateTime();\n")]
);
}
public function getPriority(): int
{
return -10;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_USE);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$useDeclarations = (new NamespaceUsesAnalyzer())->getDeclarationsFromTokens($tokens);
if (0 === \count($useDeclarations)) {
return;
}
foreach ((new NamespacesAnalyzer())->getDeclarations($tokens) as $namespace) {
$currentNamespaceUseDeclarations = [];
$currentNamespaceUseDeclarationIndices = [];
foreach ($useDeclarations as $useDeclaration) {
if ($useDeclaration->getStartIndex() >= $namespace->getScopeStartIndex() && $useDeclaration->getEndIndex() <= $namespace->getScopeEndIndex()) {
$currentNamespaceUseDeclarations[] = $useDeclaration;
$currentNamespaceUseDeclarationIndices[$useDeclaration->getStartIndex()] = $useDeclaration->getEndIndex();
}
}
foreach ($currentNamespaceUseDeclarations as $useDeclaration) {
if (!$this->isImportUsed($tokens, $namespace, $useDeclaration, $currentNamespaceUseDeclarationIndices)) {
$this->removeUseDeclaration($tokens, $useDeclaration);
}
}
$this->removeUsesInSameNamespace($tokens, $currentNamespaceUseDeclarations, $namespace);
}
}
private function isImportUsed(Tokens $tokens, NamespaceAnalysis $namespace, NamespaceUseAnalysis $import, array $ignoredIndices): bool
{
$analyzer = new TokensAnalyzer($tokens);
$gotoLabelAnalyzer = new GotoLabelAnalyzer();
$tokensNotBeforeFunctionCall = [T_NEW];
$attributeIsDefined = \defined('T_ATTRIBUTE');
if ($attributeIsDefined) {
$tokensNotBeforeFunctionCall[] = T_ATTRIBUTE;
}
$namespaceEndIndex = $namespace->getScopeEndIndex();
$inAttribute = false;
for ($index = $namespace->getScopeStartIndex(); $index <= $namespaceEndIndex; ++$index) {
$token = $tokens[$index];
if ($attributeIsDefined && $token->isGivenKind(T_ATTRIBUTE)) {
$inAttribute = true;
continue;
}
if ($attributeIsDefined && $token->isGivenKind(CT::T_ATTRIBUTE_CLOSE)) {
$inAttribute = false;
continue;
}
if (isset($ignoredIndices[$index])) {
$index = $ignoredIndices[$index];
continue;
}
if ($token->isGivenKind(T_STRING)) {
if (0 !== strcasecmp($import->getShortName(), $token->getContent())) {
continue;
}
if ($inAttribute) {
return true;
}
$prevMeaningfulToken = $tokens[$tokens->getPrevMeaningfulToken($index)];
if ($prevMeaningfulToken->isGivenKind(T_NAMESPACE)) {
$index = $tokens->getNextTokenOfKind($index, [';', '{', [T_CLOSE_TAG]]);
continue;
}
if (
$prevMeaningfulToken->isGivenKind([T_NS_SEPARATOR, T_FUNCTION, T_CONST, T_DOUBLE_COLON])
|| $prevMeaningfulToken->isObjectOperator()
) {
continue;
}
$nextMeaningfulIndex = $tokens->getNextMeaningfulToken($index);
if ($gotoLabelAnalyzer->belongsToGoToLabel($tokens, $nextMeaningfulIndex)) {
continue;
}
$nextMeaningfulToken = $tokens[$nextMeaningfulIndex];
if ($analyzer->isConstantInvocation($index)) {
$type = NamespaceUseAnalysis::TYPE_CONSTANT;
} elseif ($nextMeaningfulToken->equals('(') && !$prevMeaningfulToken->isGivenKind($tokensNotBeforeFunctionCall)) {
$type = NamespaceUseAnalysis::TYPE_FUNCTION;
} else {
$type = NamespaceUseAnalysis::TYPE_CLASS;
}
if ($import->getType() === $type) {
return true;
}
continue;
}
if ($token->isComment()
&& Preg::match(
'/(?<![[:alnum:]\$])(?<!\\\\)'.$import->getShortName().'(?![[:alnum:]])/i',
$token->getContent()
)
) {
return true;
}
}
return false;
}
private function removeUseDeclaration(Tokens $tokens, NamespaceUseAnalysis $useDeclaration): void
{
for ($index = $useDeclaration->getEndIndex() - 1; $index >= $useDeclaration->getStartIndex(); --$index) {
if ($tokens[$index]->isComment()) {
continue;
}
if (!$tokens[$index]->isWhitespace() || !str_contains($tokens[$index]->getContent(), "\n")) {
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
continue;
}
$prevIndex = $tokens->getPrevNonWhitespace($index);
if ($tokens[$prevIndex]->isComment()) {
$content = $tokens[$index]->getContent();
$tokens[$index] = new Token([T_WHITESPACE, substr($content, strrpos($content, "\n"))]);
} else {
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
}
}
if ($tokens[$useDeclaration->getEndIndex()]->equals(';')) {
$tokens->clearAt($useDeclaration->getEndIndex());
}
$prevIndex = $useDeclaration->getStartIndex() - 1;
$prevToken = $tokens[$prevIndex];
if ($prevToken->isWhitespace()) {
$content = rtrim($prevToken->getContent(), " \t");
$tokens->ensureWhitespaceAtIndex($prevIndex, 0, $content);
$prevToken = $tokens[$prevIndex];
}
if (!isset($tokens[$useDeclaration->getEndIndex() + 1])) {
return;
}
$nextIndex = $tokens->getNonEmptySibling($useDeclaration->getEndIndex(), 1);
if (null === $nextIndex) {
return;
}
$nextToken = $tokens[$nextIndex];
if ($nextToken->isWhitespace()) {
$content = Preg::replace(
"#^\r\n|^\n#",
'',
ltrim($nextToken->getContent(), " \t"),
1
);
$tokens->ensureWhitespaceAtIndex($nextIndex, 0, $content);
$nextToken = $tokens[$nextIndex];
}
if ($prevToken->isWhitespace() && $nextToken->isWhitespace()) {
$content = $prevToken->getContent().$nextToken->getContent();
$tokens->ensureWhitespaceAtIndex($nextIndex, 0, $content);
$tokens->clearAt($prevIndex);
}
}
private function removeUsesInSameNamespace(Tokens $tokens, array $useDeclarations, NamespaceAnalysis $namespaceDeclaration): void
{
$namespace = $namespaceDeclaration->getFullName();
$nsLength = \strlen($namespace.'\\');
foreach ($useDeclarations as $useDeclaration) {
if ($useDeclaration->isAliased()) {
continue;
}
$useDeclarationFullName = ltrim($useDeclaration->getFullName(), '\\');
if (!str_starts_with($useDeclarationFullName, $namespace.'\\')) {
continue;
}
$partName = substr($useDeclarationFullName, $nsLength);
if (!str_contains($partName, '\\')) {
$this->removeUseDeclaration($tokens, $useDeclaration);
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Import;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\DocBlock\Annotation;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceUseAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\ClassyAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\NamespacesAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\NamespaceUsesAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class GlobalNamespaceImportFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Imports or fully qualifies global classes/functions/constants.',
[
new CodeSample(
'<?php
namespace Foo;
$d = new \DateTimeImmutable();
'
),
new CodeSample(
'<?php
namespace Foo;
if (\count($x)) {
/** @var \DateTimeImmutable $d */
$d = new \DateTimeImmutable();
$p = \M_PI;
}
',
['import_classes' => true, 'import_constants' => true, 'import_functions' => true]
),
new CodeSample(
'<?php
namespace Foo;
use DateTimeImmutable;
use function count;
use const M_PI;
if (count($x)) {
/** @var DateTimeImmutable $d */
$d = new DateTimeImmutable();
$p = M_PI;
}
',
['import_classes' => false, 'import_constants' => false, 'import_functions' => false]
),
]
);
}
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_DOC_COMMENT, T_NS_SEPARATOR, T_USE])
&& $tokens->isTokenKindFound(T_NAMESPACE)
&& 1 === $tokens->countTokenKind(T_NAMESPACE)
&& $tokens->isMonolithicPhp();
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$namespaceAnalyses = (new NamespacesAnalyzer())->getDeclarations($tokens);
if (1 !== \count($namespaceAnalyses) || $namespaceAnalyses[0]->isGlobalNamespace()) {
return;
}
$useDeclarations = (new NamespaceUsesAnalyzer())->getDeclarationsFromTokens($tokens);
$newImports = [];
if (true === $this->configuration['import_constants']) {
$newImports['const'] = $this->importConstants($tokens, $useDeclarations);
} elseif (false === $this->configuration['import_constants']) {
$this->fullyQualifyConstants($tokens, $useDeclarations);
}
if (true === $this->configuration['import_functions']) {
$newImports['function'] = $this->importFunctions($tokens, $useDeclarations);
} elseif (false === $this->configuration['import_functions']) {
$this->fullyQualifyFunctions($tokens, $useDeclarations);
}
if (true === $this->configuration['import_classes']) {
$newImports['class'] = $this->importClasses($tokens, $useDeclarations);
} elseif (false === $this->configuration['import_classes']) {
$this->fullyQualifyClasses($tokens, $useDeclarations);
}
$newImports = array_filter($newImports);
if (\count($newImports) > 0) {
$this->insertImports($tokens, $newImports, $useDeclarations);
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('import_constants', 'Whether to import, not import or ignore global constants.'))
->setDefault(null)
->setAllowedValues([true, false, null])
->getOption(),
(new FixerOptionBuilder('import_functions', 'Whether to import, not import or ignore global functions.'))
->setDefault(null)
->setAllowedValues([true, false, null])
->getOption(),
(new FixerOptionBuilder('import_classes', 'Whether to import, not import or ignore global classes.'))
->setDefault(true)
->setAllowedValues([true, false, null])
->getOption(),
]);
}
private function importConstants(Tokens $tokens, array $useDeclarations): array
{
[$global, $other] = $this->filterUseDeclarations($useDeclarations, static function (NamespaceUseAnalysis $declaration): bool {
return $declaration->isConstant();
}, true);
for ($index = 0, $count = $tokens->count(); $index < $count; ++$index) {
$token = $tokens[$index];
if ($token->isClassy()) {
$index = $tokens->getNextTokenOfKind($index, ['{']);
$index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $index);
continue;
}
if (!$token->isGivenKind(T_CONST)) {
continue;
}
$index = $tokens->getNextMeaningfulToken($index);
$other[$tokens[$index]->getContent()] = true;
}
$analyzer = new TokensAnalyzer($tokens);
$indices = [];
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
$token = $tokens[$index];
if (!$token->isGivenKind(T_STRING)) {
continue;
}
$name = $token->getContent();
if (isset($other[$name])) {
continue;
}
if (!$analyzer->isConstantInvocation($index)) {
continue;
}
$nsSeparatorIndex = $tokens->getPrevMeaningfulToken($index);
if (!$tokens[$nsSeparatorIndex]->isGivenKind(T_NS_SEPARATOR)) {
if (!isset($global[$name])) {
$other[$name] = true;
}
continue;
}
$prevIndex = $tokens->getPrevMeaningfulToken($nsSeparatorIndex);
if ($tokens[$prevIndex]->isGivenKind([CT::T_NAMESPACE_OPERATOR, T_STRING])) {
continue;
}
$indices[] = $index;
}
return $this->prepareImports($tokens, $indices, $global, $other, true);
}
private function importFunctions(Tokens $tokens, array $useDeclarations): array
{
[$global, $other] = $this->filterUseDeclarations($useDeclarations, static function (NamespaceUseAnalysis $declaration): bool {
return $declaration->isFunction();
}, false);
foreach ($this->findFunctionDeclarations($tokens, 0, $tokens->count() - 1) as $name) {
$other[strtolower($name)] = true;
}
$analyzer = new FunctionsAnalyzer();
$indices = [];
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
$token = $tokens[$index];
if (!$token->isGivenKind(T_STRING)) {
continue;
}
$name = strtolower($token->getContent());
if (isset($other[$name])) {
continue;
}
if (!$analyzer->isGlobalFunctionCall($tokens, $index)) {
continue;
}
$nsSeparatorIndex = $tokens->getPrevMeaningfulToken($index);
if (!$tokens[$nsSeparatorIndex]->isGivenKind(T_NS_SEPARATOR)) {
if (!isset($global[$name])) {
$other[$name] = true;
}
continue;
}
$indices[] = $index;
}
return $this->prepareImports($tokens, $indices, $global, $other, false);
}
private function importClasses(Tokens $tokens, array $useDeclarations): array
{
[$global, $other] = $this->filterUseDeclarations($useDeclarations, static function (NamespaceUseAnalysis $declaration): bool {
return $declaration->isClass();
}, false);
$docBlocks = [];
for ($index = 0, $count = $tokens->count(); $index < $count; ++$index) {
$token = $tokens[$index];
if ($token->isGivenKind(T_DOC_COMMENT)) {
$docBlocks[$index] = new DocBlock($token->getContent());
$this->traverseDocBlockTypes($docBlocks[$index], static function (string $type) use ($global, &$other): void {
if (str_contains($type, '\\')) {
return;
}
$name = strtolower($type);
if (!isset($global[$name])) {
$other[$name] = true;
}
});
}
if (!$token->isClassy()) {
continue;
}
$index = $tokens->getNextMeaningfulToken($index);
if ($tokens[$index]->isGivenKind(T_STRING)) {
$other[strtolower($tokens[$index]->getContent())] = true;
}
}
$analyzer = new ClassyAnalyzer();
$indices = [];
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
$token = $tokens[$index];
if (!$token->isGivenKind(T_STRING)) {
continue;
}
$name = strtolower($token->getContent());
if (isset($other[$name])) {
continue;
}
if (!$analyzer->isClassyInvocation($tokens, $index)) {
continue;
}
$nsSeparatorIndex = $tokens->getPrevMeaningfulToken($index);
if (!$tokens[$nsSeparatorIndex]->isGivenKind(T_NS_SEPARATOR)) {
if (!isset($global[$name])) {
$other[$name] = true;
}
continue;
}
if ($tokens[$tokens->getPrevMeaningfulToken($nsSeparatorIndex)]->isGivenKind([CT::T_NAMESPACE_OPERATOR, T_STRING])) {
continue;
}
$indices[] = $index;
}
$imports = [];
foreach ($docBlocks as $index => $docBlock) {
$changed = $this->traverseDocBlockTypes($docBlock, static function (string $type) use ($global, $other, &$imports): string {
if ('\\' !== $type[0]) {
return $type;
}
$name = substr($type, 1);
$checkName = strtolower($name);
if (str_contains($checkName, '\\') || isset($other[$checkName])) {
return $type;
}
if (isset($global[$checkName])) {
return \is_string($global[$checkName]) ? $global[$checkName] : $name;
}
$imports[$checkName] = $name;
return $name;
});
if ($changed) {
$tokens[$index] = new Token([T_DOC_COMMENT, $docBlock->getContent()]);
}
}
return $imports + $this->prepareImports($tokens, $indices, $global, $other, false);
}
private function prepareImports(Tokens $tokens, array $indices, array $global, array $other, bool $caseSensitive): array
{
$imports = [];
foreach ($indices as $index) {
$name = $tokens[$index]->getContent();
$checkName = $caseSensitive ? $name : strtolower($name);
if (isset($other[$checkName])) {
continue;
}
if (!isset($global[$checkName])) {
$imports[$checkName] = $name;
} elseif (\is_string($global[$checkName])) {
$tokens[$index] = new Token([T_STRING, $global[$checkName]]);
}
$tokens->clearAt($tokens->getPrevMeaningfulToken($index));
}
return $imports;
}
private function insertImports(Tokens $tokens, array $imports, array $useDeclarations): void
{
if (\count($useDeclarations) > 0) {
$useDeclaration = end($useDeclarations);
$index = $useDeclaration->getEndIndex() + 1;
} else {
$namespace = (new NamespacesAnalyzer())->getDeclarations($tokens)[0];
$index = $namespace->getEndIndex() + 1;
}
$lineEnding = $this->whitespacesConfig->getLineEnding();
if (!$tokens[$index]->isWhitespace() || !str_contains($tokens[$index]->getContent(), "\n")) {
$tokens->insertAt($index, new Token([T_WHITESPACE, $lineEnding]));
}
foreach ($imports as $type => $typeImports) {
foreach ($typeImports as $name) {
$items = [
new Token([T_WHITESPACE, $lineEnding]),
new Token([T_USE, 'use']),
new Token([T_WHITESPACE, ' ']),
];
if ('const' === $type) {
$items[] = new Token([CT::T_CONST_IMPORT, 'const']);
$items[] = new Token([T_WHITESPACE, ' ']);
} elseif ('function' === $type) {
$items[] = new Token([CT::T_FUNCTION_IMPORT, 'function']);
$items[] = new Token([T_WHITESPACE, ' ']);
}
$items[] = new Token([T_STRING, $name]);
$items[] = new Token(';');
$tokens->insertAt($index, $items);
}
}
}
private function fullyQualifyConstants(Tokens $tokens, array $useDeclarations): void
{
if (!$tokens->isTokenKindFound(CT::T_CONST_IMPORT)) {
return;
}
[$global] = $this->filterUseDeclarations($useDeclarations, static function (NamespaceUseAnalysis $declaration): bool {
return $declaration->isConstant() && !$declaration->isAliased();
}, true);
if (!$global) {
return;
}
$analyzer = new TokensAnalyzer($tokens);
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
$token = $tokens[$index];
if (!$token->isGivenKind(T_STRING)) {
continue;
}
if (!isset($global[$token->getContent()])) {
continue;
}
if ($tokens[$tokens->getPrevMeaningfulToken($index)]->isGivenKind(T_NS_SEPARATOR)) {
continue;
}
if (!$analyzer->isConstantInvocation($index)) {
continue;
}
$tokens->insertAt($index, new Token([T_NS_SEPARATOR, '\\']));
}
}
private function fullyQualifyFunctions(Tokens $tokens, array $useDeclarations): void
{
if (!$tokens->isTokenKindFound(CT::T_FUNCTION_IMPORT)) {
return;
}
[$global] = $this->filterUseDeclarations($useDeclarations, static function (NamespaceUseAnalysis $declaration): bool {
return $declaration->isFunction() && !$declaration->isAliased();
}, false);
if (!$global) {
return;
}
$analyzer = new FunctionsAnalyzer();
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
$token = $tokens[$index];
if (!$token->isGivenKind(T_STRING)) {
continue;
}
if (!isset($global[strtolower($token->getContent())])) {
continue;
}
if ($tokens[$tokens->getPrevMeaningfulToken($index)]->isGivenKind(T_NS_SEPARATOR)) {
continue;
}
if (!$analyzer->isGlobalFunctionCall($tokens, $index)) {
continue;
}
$tokens->insertAt($index, new Token([T_NS_SEPARATOR, '\\']));
}
}
private function fullyQualifyClasses(Tokens $tokens, array $useDeclarations): void
{
if (!$tokens->isTokenKindFound(T_USE)) {
return;
}
[$global] = $this->filterUseDeclarations($useDeclarations, static function (NamespaceUseAnalysis $declaration): bool {
return $declaration->isClass() && !$declaration->isAliased();
}, false);
if (!$global) {
return;
}
$analyzer = new ClassyAnalyzer();
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
$token = $tokens[$index];
if ($token->isGivenKind(T_DOC_COMMENT)) {
$doc = new DocBlock($token->getContent());
$changed = $this->traverseDocBlockTypes($doc, static function (string $type) use ($global): string {
if (!isset($global[strtolower($type)])) {
return $type;
}
return '\\'.$type;
});
if ($changed) {
$tokens[$index] = new Token([T_DOC_COMMENT, $doc->getContent()]);
}
continue;
}
if (!$token->isGivenKind(T_STRING)) {
continue;
}
if (!isset($global[strtolower($token->getContent())])) {
continue;
}
if ($tokens[$tokens->getPrevMeaningfulToken($index)]->isGivenKind(T_NS_SEPARATOR)) {
continue;
}
if (!$analyzer->isClassyInvocation($tokens, $index)) {
continue;
}
$tokens->insertAt($index, new Token([T_NS_SEPARATOR, '\\']));
}
}
private function filterUseDeclarations(array $declarations, callable $callback, bool $caseSensitive): array
{
$global = [];
$other = [];
foreach ($declarations as $declaration) {
if (!$callback($declaration)) {
continue;
}
$fullName = ltrim($declaration->getFullName(), '\\');
if (str_contains($fullName, '\\')) {
$name = $caseSensitive ? $declaration->getShortName() : strtolower($declaration->getShortName());
$other[$name] = true;
continue;
}
$checkName = $caseSensitive ? $fullName : strtolower($fullName);
$alias = $declaration->getShortName();
$global[$checkName] = $alias === $fullName ? true : $alias;
}
return [$global, $other];
}
private function findFunctionDeclarations(Tokens $tokens, int $start, int $end): iterable
{
for ($index = $start; $index <= $end; ++$index) {
$token = $tokens[$index];
if ($token->isClassy()) {
$classStart = $tokens->getNextTokenOfKind($index, ['{']);
$classEnd = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $classStart);
for ($index = $classStart; $index <= $classEnd; ++$index) {
if (!$tokens[$index]->isGivenKind(T_FUNCTION)) {
continue;
}
$methodStart = $tokens->getNextTokenOfKind($index, ['{', ';']);
if ($tokens[$methodStart]->equals(';')) {
$index = $methodStart;
continue;
}
$methodEnd = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $methodStart);
foreach ($this->findFunctionDeclarations($tokens, $methodStart, $methodEnd) as $function) {
yield $function;
}
$index = $methodEnd;
}
continue;
}
if (!$token->isGivenKind(T_FUNCTION)) {
continue;
}
$index = $tokens->getNextMeaningfulToken($index);
if ($tokens[$index]->isGivenKind(CT::T_RETURN_REF)) {
$index = $tokens->getNextMeaningfulToken($index);
}
if ($tokens[$index]->isGivenKind(T_STRING)) {
yield $tokens[$index]->getContent();
}
}
}
private function traverseDocBlockTypes(DocBlock $doc, callable $callback): bool
{
$annotations = $doc->getAnnotationsOfType(Annotation::getTagsWithTypes());
if (0 === \count($annotations)) {
return false;
}
$changed = false;
foreach ($annotations as $annotation) {
$types = $new = $annotation->getTypes();
foreach ($types as $i => $fullType) {
$newFullType = $fullType;
Preg::matchAll('/[\\\\\w]+/', $fullType, $matches, PREG_OFFSET_CAPTURE);
foreach (array_reverse($matches[0]) as [$type, $offset]) {
$newType = $callback($type);
if (null !== $newType && $type !== $newType) {
$newFullType = substr_replace($newFullType, $newType, $offset, \strlen($type));
}
}
$new[$i] = $newFullType;
}
if ($types !== $new) {
$annotation->setTypes($new);
$changed = true;
}
}
return $changed;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Import;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
final class OrderedImportsFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
{
public const IMPORT_TYPE_CLASS = 'class';
public const IMPORT_TYPE_CONST = 'const';
public const IMPORT_TYPE_FUNCTION = 'function';
public const SORT_ALPHA = 'alpha';
public const SORT_LENGTH = 'length';
public const SORT_NONE = 'none';
private const SUPPORTED_SORT_TYPES = [self::IMPORT_TYPE_CLASS, self::IMPORT_TYPE_CONST, self::IMPORT_TYPE_FUNCTION];
private const SUPPORTED_SORT_ALGORITHMS = [self::SORT_ALPHA, self::SORT_LENGTH, self::SORT_NONE];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Ordering `use` statements.',
[
new CodeSample(
"<?php\nuse function AAC;\nuse const AAB;\nuse AAA;\n"
),
new CodeSample(
'<?php
use Acme\Bar;
use Bar1;
use Acme;
use Bar;
',
['sort_algorithm' => self::SORT_LENGTH]
),
new CodeSample(
'<?php
use const AAAA;
use const BBB;
use Bar;
use AAC;
use Acme;
use function CCC\AA;
use function DDD;
',
[
'sort_algorithm' => self::SORT_LENGTH,
'imports_order' => [
self::IMPORT_TYPE_CONST,
self::IMPORT_TYPE_CLASS,
self::IMPORT_TYPE_FUNCTION,
],
]
),
new CodeSample(
'<?php
use const BBB;
use const AAAA;
use Acme;
use AAC;
use Bar;
use function DDD;
use function CCC\AA;
',
[
'sort_algorithm' => self::SORT_ALPHA,
'imports_order' => [
self::IMPORT_TYPE_CONST,
self::IMPORT_TYPE_CLASS,
self::IMPORT_TYPE_FUNCTION,
],
]
),
new CodeSample(
'<?php
use const BBB;
use const AAAA;
use function DDD;
use function CCC\AA;
use Acme;
use AAC;
use Bar;
',
[
'sort_algorithm' => self::SORT_NONE,
'imports_order' => [
self::IMPORT_TYPE_CONST,
self::IMPORT_TYPE_CLASS,
self::IMPORT_TYPE_FUNCTION,
],
]
),
]
);
}
public function getPriority(): int
{
return -30;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_USE);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$tokensAnalyzer = new TokensAnalyzer($tokens);
$namespacesImports = $tokensAnalyzer->getImportUseIndexes(true);
if (0 === \count($namespacesImports)) {
return;
}
$usesOrder = [];
foreach ($namespacesImports as $uses) {
$usesOrder[] = $this->getNewOrder(array_reverse($uses), $tokens);
}
$usesOrder = array_replace(...$usesOrder);
$usesOrder = array_reverse($usesOrder, true);
$mapStartToEnd = [];
foreach ($usesOrder as $use) {
$mapStartToEnd[$use['startIndex']] = $use['endIndex'];
}
foreach ($usesOrder as $index => $use) {
$declarationTokens = Tokens::fromCode(
sprintf(
'<?php use %s%s;',
self::IMPORT_TYPE_CLASS === $use['importType'] ? '' : ' '.$use['importType'].' ',
$use['namespace']
)
);
$declarationTokens->clearRange(0, 2);
$declarationTokens->clearAt(\count($declarationTokens) - 1);
$declarationTokens->clearEmptyTokens();
$tokens->overrideRange($index, $mapStartToEnd[$index], $declarationTokens);
if ($use['group']) {
$prev = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$prev]->equals(',')) {
$tokens[$prev] = new Token(';');
$tokens->insertAt($prev + 1, new Token([T_USE, 'use']));
if (!$tokens[$prev + 2]->isWhitespace()) {
$tokens->insertAt($prev + 2, new Token([T_WHITESPACE, ' ']));
}
}
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
$supportedSortTypes = self::SUPPORTED_SORT_TYPES;
return new FixerConfigurationResolver([
(new FixerOptionBuilder('sort_algorithm', 'whether the statements should be sorted alphabetically or by length, or not sorted'))
->setAllowedValues(self::SUPPORTED_SORT_ALGORITHMS)
->setDefault(self::SORT_ALPHA)
->getOption(),
(new FixerOptionBuilder('imports_order', 'Defines the order of import types.'))
->setAllowedTypes(['array', 'null'])
->setAllowedValues([static function (?array $value) use ($supportedSortTypes): bool {
if (null !== $value) {
$missing = array_diff($supportedSortTypes, $value);
if (\count($missing) > 0) {
throw new InvalidOptionsException(sprintf(
'Missing sort %s "%s".',
1 === \count($missing) ? 'type' : 'types',
implode('", "', $missing)
));
}
$unknown = array_diff($value, $supportedSortTypes);
if (\count($unknown) > 0) {
throw new InvalidOptionsException(sprintf(
'Unknown sort %s "%s".',
1 === \count($unknown) ? 'type' : 'types',
implode('", "', $unknown)
));
}
}
return true;
}])
->setDefault(null)
->getOption(),
]);
}
private function sortAlphabetically(array $first, array $second): int
{
$firstNamespace = str_replace('\\', ' ', $this->prepareNamespace($first['namespace']));
$secondNamespace = str_replace('\\', ' ', $this->prepareNamespace($second['namespace']));
return strcasecmp($firstNamespace, $secondNamespace);
}
private function sortByLength(array $first, array $second): int
{
$firstNamespace = (self::IMPORT_TYPE_CLASS === $first['importType'] ? '' : $first['importType'].' ').$this->prepareNamespace($first['namespace']);
$secondNamespace = (self::IMPORT_TYPE_CLASS === $second['importType'] ? '' : $second['importType'].' ').$this->prepareNamespace($second['namespace']);
$firstNamespaceLength = \strlen($firstNamespace);
$secondNamespaceLength = \strlen($secondNamespace);
if ($firstNamespaceLength === $secondNamespaceLength) {
$sortResult = strcasecmp($firstNamespace, $secondNamespace);
} else {
$sortResult = $firstNamespaceLength > $secondNamespaceLength ? 1 : -1;
}
return $sortResult;
}
private function prepareNamespace(string $namespace): string
{
return trim(Preg::replace('%/\*(.*)\*/%s', '', $namespace));
}
private function getNewOrder(array $uses, Tokens $tokens): array
{
$indices = [];
$originalIndices = [];
$lineEnding = $this->whitespacesConfig->getLineEnding();
for ($i = \count($uses) - 1; $i >= 0; --$i) {
$index = $uses[$i];
$startIndex = $tokens->getTokenNotOfKindsSibling($index + 1, 1, [T_WHITESPACE]);
$endIndex = $tokens->getNextTokenOfKind($startIndex, [';', [T_CLOSE_TAG]]);
$previous = $tokens->getPrevMeaningfulToken($endIndex);
$group = $tokens[$previous]->isGivenKind(CT::T_GROUP_IMPORT_BRACE_CLOSE);
if ($tokens[$startIndex]->isGivenKind(CT::T_CONST_IMPORT)) {
$type = self::IMPORT_TYPE_CONST;
$index = $tokens->getNextNonWhitespace($startIndex);
} elseif ($tokens[$startIndex]->isGivenKind(CT::T_FUNCTION_IMPORT)) {
$type = self::IMPORT_TYPE_FUNCTION;
$index = $tokens->getNextNonWhitespace($startIndex);
} else {
$type = self::IMPORT_TYPE_CLASS;
$index = $startIndex;
}
$namespaceTokens = [];
while ($index <= $endIndex) {
$token = $tokens[$index];
if ($index === $endIndex || (!$group && $token->equals(','))) {
if ($group && self::SORT_NONE !== $this->configuration['sort_algorithm']) {
$namespaceTokensCount = \count($namespaceTokens) - 1;
$namespace = '';
for ($k = 0; $k < $namespaceTokensCount; ++$k) {
if ($namespaceTokens[$k]->isGivenKind(CT::T_GROUP_IMPORT_BRACE_OPEN)) {
$namespace .= '{';
break;
}
$namespace .= $namespaceTokens[$k]->getContent();
}
$parts = [];
$firstIndent = '';
$separator = ', ';
$lastIndent = '';
$hasGroupTrailingComma = false;
for ($k1 = $k + 1; $k1 < $namespaceTokensCount; ++$k1) {
$comment = '';
$namespacePart = '';
for ($k2 = $k1;; ++$k2) {
if ($namespaceTokens[$k2]->equalsAny([',', [CT::T_GROUP_IMPORT_BRACE_CLOSE]])) {
break;
}
if ($namespaceTokens[$k2]->isComment()) {
$comment .= $namespaceTokens[$k2]->getContent();
continue;
}
if (
'' === $firstIndent
&& $namespaceTokens[$k2]->isWhitespace()
&& str_contains($namespaceTokens[$k2]->getContent(), $lineEnding)
) {
$lastIndent = $lineEnding;
$firstIndent = $lineEnding.$this->whitespacesConfig->getIndent();
$separator = ','.$firstIndent;
}
$namespacePart .= $namespaceTokens[$k2]->getContent();
}
$namespacePart = trim($namespacePart);
if ('' === $namespacePart) {
$hasGroupTrailingComma = true;
continue;
}
$comment = trim($comment);
if ('' !== $comment) {
$namespacePart .= ' '.$comment;
}
$parts[] = $namespacePart;
$k1 = $k2;
}
$sortedParts = $parts;
sort($parts);
if ($sortedParts === $parts) {
$namespace = Tokens::fromArray($namespaceTokens)->generateCode();
} else {
$namespace .= $firstIndent.implode($separator, $parts).($hasGroupTrailingComma ? ',' : '').$lastIndent.'}';
}
} else {
$namespace = Tokens::fromArray($namespaceTokens)->generateCode();
}
$indices[$startIndex] = [
'namespace' => $namespace,
'startIndex' => $startIndex,
'endIndex' => $index - 1,
'importType' => $type,
'group' => $group,
];
$originalIndices[] = $startIndex;
if ($index === $endIndex) {
break;
}
$namespaceTokens = [];
$nextPartIndex = $tokens->getTokenNotOfKindSibling($index, 1, [',', [T_WHITESPACE]]);
$startIndex = $nextPartIndex;
$index = $nextPartIndex;
continue;
}
$namespaceTokens[] = $token;
++$index;
}
}
if (null !== $this->configuration['imports_order']) {
$groupedByTypes = [];
foreach ($indices as $startIndex => $item) {
$groupedByTypes[$item['importType']][$startIndex] = $item;
}
foreach ($groupedByTypes as $type => $groupIndices) {
$groupedByTypes[$type] = $this->sortByAlgorithm($groupIndices);
}
$sortedGroups = [];
foreach ($this->configuration['imports_order'] as $type) {
if (isset($groupedByTypes[$type]) && !empty($groupedByTypes[$type])) {
foreach ($groupedByTypes[$type] as $startIndex => $item) {
$sortedGroups[$startIndex] = $item;
}
}
}
$indices = $sortedGroups;
} else {
$indices = $this->sortByAlgorithm($indices);
}
$index = -1;
$usesOrder = [];
foreach ($indices as $v) {
$usesOrder[$originalIndices[++$index]] = $v;
}
return $usesOrder;
}
private function sortByAlgorithm(array $indices): array
{
if (self::SORT_ALPHA === $this->configuration['sort_algorithm']) {
uasort($indices, [$this, 'sortAlphabetically']);
} elseif (self::SORT_LENGTH === $this->configuration['sort_algorithm']) {
uasort($indices, [$this, 'sortByLength']);
}
return $indices;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Import;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Tokens;
final class NoUnneededImportAliasFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Imports should not be aliased as the same name.',
[new CodeSample("<?php\nuse A\\B\\Foo as Foo;\n")]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAllTokenKindsFound([T_USE, T_AS]);
}
public function getPriority(): int
{
return 1;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = \count($tokens) - 1; 0 <= $index; --$index) {
if (!$tokens[$index]->isGivenKind(T_AS)) {
continue;
}
$aliasIndex = $tokens->getNextMeaningfulToken($index);
if (!$tokens[$aliasIndex]->isGivenKind(T_STRING)) {
continue;
}
$importIndex = $tokens->getPrevMeaningfulToken($index);
if (!$tokens[$importIndex]->isGivenKind(T_STRING)) {
continue;
}
if ($tokens[$importIndex]->getContent() !== $tokens[$aliasIndex]->getContent()) {
continue;
}
do {
$importIndex = $tokens->getPrevMeaningfulToken($importIndex);
} while ($tokens[$importIndex]->isGivenKind([T_NS_SEPARATOR, T_STRING, T_AS]) || $tokens[$importIndex]->equals(','));
if ($tokens[$importIndex]->isGivenKind([CT::T_FUNCTION_IMPORT, CT::T_CONST_IMPORT])) {
$importIndex = $tokens->getPrevMeaningfulToken($importIndex);
}
if (!$tokens[$importIndex]->isGivenKind([T_USE, CT::T_GROUP_IMPORT_BRACE_OPEN])) {
continue;
}
$tokens->clearTokenAndMergeSurroundingWhitespace($aliasIndex);
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Import;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\WhitespacesAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class SingleImportPerStatementFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'There MUST be one use keyword per declaration.',
[
new CodeSample(
'<?php
use Foo, Sample, Sample\Sample as Sample2;
'
),
new CodeSample(
'<?php
use Space\Models\ {
TestModelA,
TestModelB,
TestModel,
};
',
['group_to_single_imports' => true]
),
]
);
}
public function getPriority(): int
{
return 1;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_USE);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$tokensAnalyzer = new TokensAnalyzer($tokens);
$fixGroups = $this->configuration['group_to_single_imports'];
foreach (array_reverse($tokensAnalyzer->getImportUseIndexes()) as $index) {
$endIndex = $tokens->getNextTokenOfKind($index, [';', [T_CLOSE_TAG]]);
$groupClose = $tokens->getPrevMeaningfulToken($endIndex);
if ($tokens[$groupClose]->isGivenKind(CT::T_GROUP_IMPORT_BRACE_CLOSE)) {
if ($fixGroups) {
$this->fixGroupUse($tokens, $index, $endIndex);
}
} else {
$this->fixMultipleUse($tokens, $index, $endIndex);
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('group_to_single_imports', 'Whether to change group imports into single imports.'))
->setAllowedTypes(['bool'])
->setDefault(true)
->getOption(),
]);
}
private function getGroupDeclaration(Tokens $tokens, int $index): array
{
$groupPrefix = '';
$comment = '';
$groupOpenIndex = null;
for ($i = $index + 1;; ++$i) {
if ($tokens[$i]->isGivenKind(CT::T_GROUP_IMPORT_BRACE_OPEN)) {
$groupOpenIndex = $i;
break;
}
if ($tokens[$i]->isComment()) {
$comment .= $tokens[$i]->getContent();
if (!$tokens[$i - 1]->isWhitespace() && !$tokens[$i + 1]->isWhitespace()) {
$groupPrefix .= ' ';
}
continue;
}
if ($tokens[$i]->isWhitespace()) {
$groupPrefix .= ' ';
continue;
}
$groupPrefix .= $tokens[$i]->getContent();
}
return [
rtrim($groupPrefix),
$groupOpenIndex,
$tokens->findBlockEnd(Tokens::BLOCK_TYPE_GROUP_IMPORT_BRACE, $groupOpenIndex),
$comment,
];
}
private function getGroupStatements(Tokens $tokens, string $groupPrefix, int $groupOpenIndex, int $groupCloseIndex, string $comment): array
{
$statements = [];
$statement = $groupPrefix;
for ($i = $groupOpenIndex + 1; $i <= $groupCloseIndex; ++$i) {
$token = $tokens[$i];
if ($token->equals(',') && $tokens[$tokens->getNextMeaningfulToken($i)]->isGivenKind(CT::T_GROUP_IMPORT_BRACE_CLOSE)) {
continue;
}
if ($token->equalsAny([',', [CT::T_GROUP_IMPORT_BRACE_CLOSE]])) {
$statements[] = 'use'.$statement.';';
$statement = $groupPrefix;
continue;
}
if ($token->isWhitespace()) {
$j = $tokens->getNextMeaningfulToken($i);
if ($tokens[$j]->isGivenKind(T_AS)) {
$statement .= ' as ';
$i += 2;
} elseif ($tokens[$j]->isGivenKind(CT::T_FUNCTION_IMPORT)) {
$statement = ' function'.$statement;
$i += 2;
} elseif ($tokens[$j]->isGivenKind(CT::T_CONST_IMPORT)) {
$statement = ' const'.$statement;
$i += 2;
}
if ($token->isWhitespace(" \t") || !str_starts_with($tokens[$i - 1]->getContent(), '//')) {
continue;
}
}
$statement .= $token->getContent();
}
if ('' !== $comment) {
$statements[0] .= ' '.$comment;
}
return $statements;
}
private function fixGroupUse(Tokens $tokens, int $index, int $endIndex): void
{
[$groupPrefix, $groupOpenIndex, $groupCloseIndex, $comment] = $this->getGroupDeclaration($tokens, $index);
$statements = $this->getGroupStatements($tokens, $groupPrefix, $groupOpenIndex, $groupCloseIndex, $comment);
if (\count($statements) < 2) {
return;
}
$tokens->clearRange($index, $groupCloseIndex);
if ($tokens[$endIndex]->equals(';')) {
$tokens->clearAt($endIndex);
}
$ending = $this->whitespacesConfig->getLineEnding();
$importTokens = Tokens::fromCode('<?php '.implode($ending, $statements));
$importTokens->clearAt(0);
$importTokens->clearEmptyTokens();
$tokens->insertAt($index, $importTokens);
}
private function fixMultipleUse(Tokens $tokens, int $index, int $endIndex): void
{
$nextTokenIndex = $tokens->getNextMeaningfulToken($index);
if ($tokens[$nextTokenIndex]->isGivenKind(CT::T_FUNCTION_IMPORT)) {
$leadingTokens = [
new Token([CT::T_FUNCTION_IMPORT, 'function']),
new Token([T_WHITESPACE, ' ']),
];
} elseif ($tokens[$nextTokenIndex]->isGivenKind(CT::T_CONST_IMPORT)) {
$leadingTokens = [
new Token([CT::T_CONST_IMPORT, 'const']),
new Token([T_WHITESPACE, ' ']),
];
} else {
$leadingTokens = [];
}
$ending = $this->whitespacesConfig->getLineEnding();
for ($i = $endIndex - 1; $i > $index; --$i) {
if (!$tokens[$i]->equals(',')) {
continue;
}
$tokens[$i] = new Token(';');
$i = $tokens->getNextMeaningfulToken($i);
$tokens->insertAt($i, new Token([T_USE, 'use']));
$tokens->insertAt($i + 1, new Token([T_WHITESPACE, ' ']));
foreach ($leadingTokens as $offset => $leadingToken) {
$tokens->insertAt($i + 2 + $offset, clone $leadingTokens[$offset]);
}
$indent = WhitespacesAnalyzer::detectIndent($tokens, $index);
if ($tokens[$i - 1]->isWhitespace()) {
$tokens[$i - 1] = new Token([T_WHITESPACE, $ending.$indent]);
} elseif (!str_contains($tokens[$i - 1]->getContent(), "\n")) {
$tokens->insertAt($i, new Token([T_WHITESPACE, $ending.$indent]));
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Import;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class NoLeadingImportSlashFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Remove leading slashes in `use` clauses.',
[new CodeSample("<?php\nnamespace Foo;\nuse \\Bar;\n")]
);
}
public function getPriority(): int
{
return -20;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_USE);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$tokensAnalyzer = new TokensAnalyzer($tokens);
$usesIndices = $tokensAnalyzer->getImportUseIndexes();
foreach ($usesIndices as $idx) {
$nextTokenIdx = $tokens->getNextMeaningfulToken($idx);
$nextToken = $tokens[$nextTokenIdx];
if ($nextToken->isGivenKind(T_NS_SEPARATOR)) {
$this->removeLeadingImportSlash($tokens, $nextTokenIdx);
} elseif ($nextToken->isGivenKind([CT::T_FUNCTION_IMPORT, CT::T_CONST_IMPORT])) {
$nextTokenIdx = $tokens->getNextMeaningfulToken($nextTokenIdx);
if ($tokens[$nextTokenIdx]->isGivenKind(T_NS_SEPARATOR)) {
$this->removeLeadingImportSlash($tokens, $nextTokenIdx);
}
}
}
}
private function removeLeadingImportSlash(Tokens $tokens, int $index): void
{
$previousIndex = $tokens->getPrevNonWhitespace($index);
if (
$previousIndex < $index - 1
|| $tokens[$previousIndex]->isComment()
) {
$tokens->clearAt($index);
return;
}
$tokens[$index] = new Token([T_WHITESPACE, ' ']);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Import;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceUseAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\NamespaceUsesAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class GroupImportFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'There MUST be group use for the same namespaces.',
[
new CodeSample(
"<?php\nuse Foo\\Bar;\nuse Foo\\Baz;\n"
),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_USE);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$useWithSameNamespaces = $this->getSameNamespaces($tokens);
if ([] === $useWithSameNamespaces) {
return;
}
$this->removeSingleUseStatements($useWithSameNamespaces, $tokens);
$this->addGroupUseStatements($useWithSameNamespaces, $tokens);
}
private function getSameNamespaces(Tokens $tokens): array
{
$useDeclarations = (new NamespaceUsesAnalyzer())->getDeclarationsFromTokens($tokens);
if (0 === \count($useDeclarations)) {
return [];
}
$allNamespaceAndType = array_map(
function (NamespaceUseAnalysis $useDeclaration): string {
return $this->getNamespaceNameWithSlash($useDeclaration).$useDeclaration->getType();
},
$useDeclarations
);
$sameNamespaces = array_filter(array_count_values($allNamespaceAndType), static function (int $count): bool {
return $count > 1;
});
$sameNamespaces = array_keys($sameNamespaces);
$sameNamespaceAnalysis = array_filter($useDeclarations, function (NamespaceUseAnalysis $useDeclaration) use ($sameNamespaces): bool {
$namespaceNameAndType = $this->getNamespaceNameWithSlash($useDeclaration).$useDeclaration->getType();
return \in_array($namespaceNameAndType, $sameNamespaces, true);
});
usort($sameNamespaceAnalysis, function (NamespaceUseAnalysis $a, NamespaceUseAnalysis $b): int {
$namespaceA = $this->getNamespaceNameWithSlash($a);
$namespaceB = $this->getNamespaceNameWithSlash($b);
return \strlen($namespaceA) - \strlen($namespaceB) ?: strcmp($a->getFullName(), $b->getFullName());
});
return $sameNamespaceAnalysis;
}
private function removeSingleUseStatements(array $statements, Tokens $tokens): void
{
foreach ($statements as $useDeclaration) {
$index = $useDeclaration->getStartIndex();
$endIndex = $useDeclaration->getEndIndex();
$useStatementTokens = [T_USE, T_WHITESPACE, T_STRING, T_NS_SEPARATOR, T_AS, CT::T_CONST_IMPORT, CT::T_FUNCTION_IMPORT];
while ($index !== $endIndex) {
if ($tokens[$index]->isGivenKind($useStatementTokens)) {
$tokens->clearAt($index);
}
++$index;
}
if (isset($tokens[$index]) && $tokens[$index]->equals(';')) {
$tokens->clearAt($index);
}
++$index;
if (isset($tokens[$index]) && $tokens[$index]->isGivenKind(T_WHITESPACE)) {
$tokens->clearAt($index);
}
}
}
private function addGroupUseStatements(array $statements, Tokens $tokens): void
{
$currentUseDeclaration = null;
$insertIndex = \array_slice($statements, -1)[0]->getEndIndex() + 1;
foreach ($statements as $index => $useDeclaration) {
if ($this->areDeclarationsDifferent($currentUseDeclaration, $useDeclaration)) {
$currentUseDeclaration = $useDeclaration;
$insertIndex += $this->createNewGroup(
$tokens,
$insertIndex,
$useDeclaration,
$this->getNamespaceNameWithSlash($currentUseDeclaration)
);
} else {
$newTokens = [
new Token(','),
new Token([T_WHITESPACE, ' ']),
];
if ($useDeclaration->isAliased()) {
$tokens->insertAt($insertIndex, $newTokens);
$insertIndex += \count($newTokens);
$newTokens = [];
$insertIndex += $this->insertToGroupUseWithAlias($tokens, $insertIndex, $useDeclaration);
}
$newTokens[] = new Token([T_STRING, $useDeclaration->getShortName()]);
if (!isset($statements[$index + 1]) || $this->areDeclarationsDifferent($currentUseDeclaration, $statements[$index + 1])) {
$newTokens[] = new Token([CT::T_GROUP_IMPORT_BRACE_CLOSE, '}']);
$newTokens[] = new Token(';');
$newTokens[] = new Token([T_WHITESPACE, "\n"]);
}
$tokens->insertAt($insertIndex, $newTokens);
$insertIndex += \count($newTokens);
}
}
}
private function getNamespaceNameWithSlash(NamespaceUseAnalysis $useDeclaration): string
{
$position = strrpos($useDeclaration->getFullName(), '\\');
if (false === $position || 0 === $position) {
return $useDeclaration->getFullName();
}
return substr($useDeclaration->getFullName(), 0, $position + 1);
}
private function insertToGroupUseWithAlias(Tokens $tokens, int $insertIndex, NamespaceUseAnalysis $useDeclaration): int
{
$newTokens = [
new Token([T_STRING, substr($useDeclaration->getFullName(), strripos($useDeclaration->getFullName(), '\\') + 1)]),
new Token([T_WHITESPACE, ' ']),
new Token([T_AS, 'as']),
new Token([T_WHITESPACE, ' ']),
];
$tokens->insertAt($insertIndex, $newTokens);
return \count($newTokens) + 1;
}
private function createNewGroup(Tokens $tokens, int $insertIndex, NamespaceUseAnalysis $useDeclaration, string $currentNamespace): int
{
$insertedTokens = 0;
if (\count($tokens) === $insertIndex) {
$tokens->setSize($insertIndex + 1);
}
$newTokens = [
new Token([T_USE, 'use']),
new Token([T_WHITESPACE, ' ']),
];
if ($useDeclaration->isFunction() || $useDeclaration->isConstant()) {
$importStatementParams = $useDeclaration->isFunction()
? [CT::T_FUNCTION_IMPORT, 'function']
: [CT::T_CONST_IMPORT, 'const'];
$newTokens[] = new Token($importStatementParams);
$newTokens[] = new Token([T_WHITESPACE, ' ']);
}
$namespaceParts = array_filter(explode('\\', $currentNamespace));
foreach ($namespaceParts as $part) {
$newTokens[] = new Token([T_STRING, $part]);
$newTokens[] = new Token([T_NS_SEPARATOR, '\\']);
}
$newTokens[] = new Token([CT::T_GROUP_IMPORT_BRACE_OPEN, '{']);
$newTokensCount = \count($newTokens);
$tokens->insertAt($insertIndex, $newTokens);
$insertedTokens += $newTokensCount;
$insertIndex += $newTokensCount;
if ($useDeclaration->isAliased()) {
$inserted = $this->insertToGroupUseWithAlias($tokens, $insertIndex + 1, $useDeclaration);
$insertedTokens += $inserted;
$insertIndex += $inserted;
}
$tokens->insertAt($insertIndex, new Token([T_STRING, $useDeclaration->getShortName()]));
++$insertedTokens;
return $insertedTokens;
}
private function areDeclarationsDifferent(?NamespaceUseAnalysis $analysis1, ?NamespaceUseAnalysis $analysis2): bool
{
if (null === $analysis1 || null === $analysis2) {
return true;
}
$namespaceName1 = $this->getNamespaceNameWithSlash($analysis1);
$namespaceName2 = $this->getNamespaceNameWithSlash($analysis2);
return $namespaceName1 !== $namespaceName2 || $analysis1->getType() !== $analysis2->getType();
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Import;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
use PhpCsFixer\Utils;
final class SingleLineAfterImportsFixer extends AbstractFixer implements WhitespacesAwareFixerInterface
{
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_USE);
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Each namespace use MUST go on its own line and there MUST be one blank line after the use statements block.',
[
new CodeSample(
'<?php
namespace Foo;
use Bar;
use Baz;
final class Example
{
}
'
),
new CodeSample(
'<?php
namespace Foo;
use Bar;
use Baz;
final class Example
{
}
'
),
]
);
}
public function getPriority(): int
{
return -11;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$ending = $this->whitespacesConfig->getLineEnding();
$tokensAnalyzer = new TokensAnalyzer($tokens);
$added = 0;
foreach ($tokensAnalyzer->getImportUseIndexes() as $index) {
$index += $added;
$indent = '';
if ($tokens[$index - 1]->isWhitespace(" \t") && $tokens[$index - 2]->isGivenKind(T_COMMENT)) {
$indent = $tokens[$index - 1]->getContent();
} elseif ($tokens[$index - 1]->isWhitespace()) {
$indent = Utils::calculateTrailingWhitespaceIndent($tokens[$index - 1]);
}
$semicolonIndex = $tokens->getNextTokenOfKind($index, [';', [T_CLOSE_TAG]]);
$insertIndex = $semicolonIndex;
if ($tokens[$semicolonIndex]->isGivenKind(T_CLOSE_TAG)) {
if ($tokens[$insertIndex - 1]->isWhitespace()) {
--$insertIndex;
}
$tokens->insertAt($insertIndex, new Token(';'));
++$added;
}
if ($semicolonIndex === \count($tokens) - 1) {
$tokens->insertAt($insertIndex + 1, new Token([T_WHITESPACE, $ending.$ending.$indent]));
++$added;
} else {
$newline = $ending;
$tokens[$semicolonIndex]->isGivenKind(T_CLOSE_TAG) ? --$insertIndex : ++$insertIndex;
if ($tokens[$insertIndex]->isWhitespace(" \t") && $tokens[$insertIndex + 1]->isComment()) {
++$insertIndex;
}
if ($tokens[$insertIndex]->isComment()) {
++$insertIndex;
}
$afterSemicolon = $tokens->getNextMeaningfulToken($semicolonIndex);
if (null === $afterSemicolon || !$tokens[$afterSemicolon]->isGivenKind(T_USE)) {
$newline .= $ending;
}
if ($tokens[$insertIndex]->isWhitespace()) {
$nextToken = $tokens[$insertIndex];
if (2 === substr_count($nextToken->getContent(), "\n")) {
continue;
}
$nextMeaningfulAfterUseIndex = $tokens->getNextMeaningfulToken($insertIndex);
if (null !== $nextMeaningfulAfterUseIndex && $tokens[$nextMeaningfulAfterUseIndex]->isGivenKind(T_USE)) {
if (substr_count($nextToken->getContent(), "\n") < 1) {
$tokens[$insertIndex] = new Token([T_WHITESPACE, $newline.$indent.ltrim($nextToken->getContent())]);
}
} else {
$tokens[$insertIndex] = new Token([T_WHITESPACE, $newline.$indent.ltrim($nextToken->getContent())]);
}
} else {
$tokens->insertAt($insertIndex, new Token([T_WHITESPACE, $newline.$indent]));
++$added;
}
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ArrayNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class ArraySyntaxFixer extends AbstractFixer implements ConfigurableFixerInterface
{
private $candidateTokenKind;
private $fixCallback;
public function configure(array $configuration): void
{
parent::configure($configuration);
$this->resolveCandidateTokenKind();
$this->resolveFixCallback();
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'PHP arrays should be declared using the configured syntax.',
[
new CodeSample(
"<?php\narray(1,2);\n"
),
new CodeSample(
"<?php\n[1,2];\n",
['syntax' => 'long']
),
]
);
}
public function getPriority(): int
{
return 1;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound($this->candidateTokenKind);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$callback = $this->fixCallback;
for ($index = $tokens->count() - 1; 0 <= $index; --$index) {
if ($tokens[$index]->isGivenKind($this->candidateTokenKind)) {
$this->{$callback}($tokens, $index);
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('syntax', 'Whether to use the `long` or `short` array syntax.'))
->setAllowedValues(['long', 'short'])
->setDefault('short')
->getOption(),
]);
}
private function fixToLongArraySyntax(Tokens $tokens, int $index): void
{
$closeIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, $index);
$tokens[$index] = new Token('(');
$tokens[$closeIndex] = new Token(')');
$tokens->insertAt($index, new Token([T_ARRAY, 'array']));
}
private function fixToShortArraySyntax(Tokens $tokens, int $index): void
{
$openIndex = $tokens->getNextTokenOfKind($index, ['(']);
$closeIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openIndex);
$tokens[$openIndex] = new Token([CT::T_ARRAY_SQUARE_BRACE_OPEN, '[']);
$tokens[$closeIndex] = new Token([CT::T_ARRAY_SQUARE_BRACE_CLOSE, ']']);
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
}
private function resolveFixCallback(): void
{
$this->fixCallback = sprintf('fixTo%sArraySyntax', ucfirst($this->configuration['syntax']));
}
private function resolveCandidateTokenKind(): void
{
$this->candidateTokenKind = 'long' === $this->configuration['syntax'] ? CT::T_ARRAY_SQUARE_BRACE_OPEN : T_ARRAY;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ArrayNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Tokens;
final class TrimArraySpacesFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Arrays should be formatted like function/method arguments, without leading or trailing single line space.',
[new CodeSample("<?php\n\$sample = array( );\n\$sample = array( 'a', 'b' );\n")]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_ARRAY, CT::T_ARRAY_SQUARE_BRACE_OPEN]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = 0, $c = $tokens->count(); $index < $c; ++$index) {
if ($tokens[$index]->isGivenKind([T_ARRAY, CT::T_ARRAY_SQUARE_BRACE_OPEN])) {
self::fixArray($tokens, $index);
}
}
}
private static function fixArray(Tokens $tokens, int $index): void
{
$startIndex = $index;
if ($tokens[$startIndex]->isGivenKind(T_ARRAY)) {
$startIndex = $tokens->getNextMeaningfulToken($startIndex);
$endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $startIndex);
} else {
$endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, $startIndex);
}
$nextIndex = $startIndex + 1;
$nextToken = $tokens[$nextIndex];
$nextNonWhitespaceIndex = $tokens->getNextNonWhitespace($startIndex);
$nextNonWhitespaceToken = $tokens[$nextNonWhitespaceIndex];
$tokenAfterNextNonWhitespaceToken = $tokens[$nextNonWhitespaceIndex + 1];
$prevIndex = $endIndex - 1;
$prevToken = $tokens[$prevIndex];
$prevNonWhitespaceIndex = $tokens->getPrevNonWhitespace($endIndex);
$prevNonWhitespaceToken = $tokens[$prevNonWhitespaceIndex];
if (
$nextToken->isWhitespace(" \t")
&& (
!$nextNonWhitespaceToken->isComment()
|| $nextNonWhitespaceIndex === $prevNonWhitespaceIndex
|| $tokenAfterNextNonWhitespaceToken->isWhitespace(" \t")
|| str_starts_with($nextNonWhitespaceToken->getContent(), '/*')
)
) {
$tokens->clearAt($nextIndex);
}
if (
$prevToken->isWhitespace(" \t")
&& !$prevNonWhitespaceToken->equals(',')
) {
$tokens->clearAt($prevIndex);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ArrayNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\FixerDefinition\VersionSpecification;
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Tokens;
final class NoWhitespaceBeforeCommaInArrayFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'In array declaration, there MUST NOT be a whitespace before each comma.',
[
new CodeSample("<?php \$x = array(1 , \"2\");\n"),
new VersionSpecificCodeSample(
<<<'PHP'
<?php
$x = [<<<EOD
foo
EOD
, 'bar'
];
PHP,
new VersionSpecification(70300),
['after_heredoc' => true]
),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_ARRAY, CT::T_ARRAY_SQUARE_BRACE_OPEN]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = $tokens->count() - 1; $index > 0; --$index) {
if ($tokens[$index]->isGivenKind([T_ARRAY, CT::T_ARRAY_SQUARE_BRACE_OPEN])) {
$this->fixSpacing($index, $tokens);
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('after_heredoc', 'Whether the whitespace between heredoc end and comma should be removed.'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
]);
}
private function fixSpacing(int $index, Tokens $tokens): void
{
if ($tokens[$index]->isGivenKind(CT::T_ARRAY_SQUARE_BRACE_OPEN)) {
$startIndex = $index;
$endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, $startIndex);
} else {
$startIndex = $tokens->getNextTokenOfKind($index, ['(']);
$endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $startIndex);
}
for ($i = $endIndex - 1; $i > $startIndex; --$i) {
$i = $this->skipNonArrayElements($i, $tokens);
$currentToken = $tokens[$i];
$prevIndex = $tokens->getPrevNonWhitespace($i - 1);
if (
$currentToken->equals(',') && !$tokens[$prevIndex]->isComment()
&& (true === $this->configuration['after_heredoc'] || !$tokens[$prevIndex]->isGivenKind(T_END_HEREDOC))
) {
$tokens->removeLeadingWhitespace($i);
}
}
}
private function skipNonArrayElements(int $index, Tokens $tokens): int
{
if ($tokens[$index]->equals('}')) {
return $tokens->findBlockStart(Tokens::BLOCK_TYPE_CURLY_BRACE, $index);
}
if ($tokens[$index]->equals(')')) {
$startIndex = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index);
$startIndex = $tokens->getPrevMeaningfulToken($startIndex);
if (!$tokens[$startIndex]->isGivenKind([T_ARRAY, CT::T_ARRAY_SQUARE_BRACE_OPEN])) {
return $startIndex;
}
}
if ($tokens[$index]->equals(',') && $this->commaIsPartOfImplementsList($index, $tokens)) {
--$index;
}
return $index;
}
private function commaIsPartOfImplementsList(int $index, Tokens $tokens): bool
{
do {
$index = $tokens->getPrevMeaningfulToken($index);
$current = $tokens[$index];
} while ($current->isGivenKind(T_STRING) || $current->equals(','));
return $current->isGivenKind(T_IMPLEMENTS);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ArrayNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NoMultilineWhitespaceAroundDoubleArrowFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Operator `=>` should not be surrounded by multi-line whitespaces.',
[new CodeSample("<?php\n\$a = array(1\n\n=> 2);\n")]
);
}
public function getPriority(): int
{
return 31;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOUBLE_ARROW);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_DOUBLE_ARROW)) {
continue;
}
if (!$tokens[$index - 2]->isComment() || str_starts_with($tokens[$index - 2]->getContent(), '/*')) {
$this->fixWhitespace($tokens, $index - 1);
}
if (!$tokens[$index + 2]->isComment()) {
$this->fixWhitespace($tokens, $index + 1);
}
}
}
private function fixWhitespace(Tokens $tokens, int $index): void
{
$token = $tokens[$index];
if ($token->isWhitespace() && !$token->isWhitespace(" \t")) {
$tokens[$index] = new Token([T_WHITESPACE, rtrim($token->getContent()).' ']);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ArrayNotation;
use PhpCsFixer\AbstractProxyFixer;
use PhpCsFixer\Fixer\Basic\NoTrailingCommaInSinglelineFixer;
use PhpCsFixer\Fixer\DeprecatedFixerInterface;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
final class NoTrailingCommaInSinglelineArrayFixer extends AbstractProxyFixer implements DeprecatedFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'PHP single-line arrays should not have trailing comma.',
[new CodeSample("<?php\n\$a = array('sample', );\n")]
);
}
public function getSuccessorsNames(): array
{
return array_keys($this->proxyFixers);
}
protected function createProxyFixers(): array
{
$fixer = new NoTrailingCommaInSinglelineFixer();
$fixer->configure(['elements' => ['array']]);
return [$fixer];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ArrayNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class WhitespaceAfterCommaInArrayFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'In array declaration, there MUST be a whitespace after each comma.',
[
new CodeSample("<?php\n\$sample = array(1,'a',\$b,);\n"),
new CodeSample("<?php\n\$sample = [1,2, 3, 4, 5];\n", ['ensure_single_space' => true]),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_ARRAY, CT::T_ARRAY_SQUARE_BRACE_OPEN]);
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('ensure_single_space', 'If there are only horizontal whitespaces after the comma then ensure it is a single space.'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$tokensToInsert = [];
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
if (!$tokens[$index]->isGivenKind([T_ARRAY, CT::T_ARRAY_SQUARE_BRACE_OPEN])) {
continue;
}
if ($tokens[$index]->isGivenKind(CT::T_ARRAY_SQUARE_BRACE_OPEN)) {
$startIndex = $index;
$endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, $startIndex);
} else {
$startIndex = $tokens->getNextTokenOfKind($index, ['(']);
$endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $startIndex);
}
for ($i = $endIndex - 1; $i > $startIndex; --$i) {
$i = $this->skipNonArrayElements($i, $tokens);
if (!$tokens[$i]->equals(',')) {
continue;
}
if (!$tokens[$i + 1]->isWhitespace()) {
$tokensToInsert[$i + 1] = new Token([T_WHITESPACE, ' ']);
} elseif (
$this->configuration['ensure_single_space']
&& ' ' !== $tokens[$i + 1]->getContent()
&& 1 === Preg::match('/^\h+$/', $tokens[$i + 1]->getContent())
&& (!$tokens[$i + 2]->isComment() || 1 === Preg::match('/^\h+$/', $tokens[$i + 3]->getContent()))
) {
$tokens[$i + 1] = new Token([T_WHITESPACE, ' ']);
}
}
}
if ([] !== $tokensToInsert) {
$tokens->insertSlices($tokensToInsert);
}
}
private function skipNonArrayElements(int $index, Tokens $tokens): int
{
if ($tokens[$index]->equals('}')) {
return $tokens->findBlockStart(Tokens::BLOCK_TYPE_CURLY_BRACE, $index);
}
if ($tokens[$index]->equals(')')) {
$startIndex = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index);
$startIndex = $tokens->getPrevMeaningfulToken($startIndex);
if (!$tokens[$startIndex]->isGivenKind([T_ARRAY, CT::T_ARRAY_SQUARE_BRACE_OPEN])) {
return $startIndex;
}
}
if ($tokens[$index]->equals(',') && $this->commaIsPartOfImplementsList($index, $tokens)) {
--$index;
}
return $index;
}
private function commaIsPartOfImplementsList(int $index, Tokens $tokens): bool
{
do {
$index = $tokens->getPrevMeaningfulToken($index);
$current = $tokens[$index];
} while ($current->isGivenKind(T_STRING) || $current->equals(','));
return $current->isGivenKind(T_IMPLEMENTS);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ArrayNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NormalizeIndexBraceFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Array index should always be written by using square braces.',
[new CodeSample("<?php\necho \$sample{\$index};\n")]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if ($token->isGivenKind(CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN)) {
$tokens[$index] = new Token('[');
} elseif ($token->isGivenKind(CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE)) {
$tokens[$index] = new Token(']');
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ListNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class ListSyntaxFixer extends AbstractFixer implements ConfigurableFixerInterface
{
private $candidateTokenKind;
public function configure(array $configuration): void
{
parent::configure($configuration);
$this->candidateTokenKind = 'long' === $this->configuration['syntax'] ? CT::T_DESTRUCTURING_SQUARE_BRACE_OPEN : T_LIST;
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'List (`array` destructuring) assignment should be declared using the configured syntax. Requires PHP >= 7.1.',
[
new CodeSample(
"<?php\nlist(\$sample) = \$array;\n"
),
new CodeSample(
"<?php\n[\$sample] = \$array;\n",
['syntax' => 'long']
),
]
);
}
public function getPriority(): int
{
return 1;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound($this->candidateTokenKind);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = $tokens->count() - 1; 0 <= $index; --$index) {
if ($tokens[$index]->isGivenKind($this->candidateTokenKind)) {
if (T_LIST === $this->candidateTokenKind) {
$this->fixToShortSyntax($tokens, $index);
} else {
$this->fixToLongSyntax($tokens, $index);
}
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('syntax', 'Whether to use the `long` or `short` `list` syntax.'))
->setAllowedValues(['long', 'short'])
->setDefault('short')
->getOption(),
]);
}
private function fixToLongSyntax(Tokens $tokens, int $index): void
{
static $typesOfInterest = [
[CT::T_DESTRUCTURING_SQUARE_BRACE_CLOSE],
'[',
];
$closeIndex = $tokens->getNextTokenOfKind($index, $typesOfInterest);
if (!$tokens[$closeIndex]->isGivenKind(CT::T_DESTRUCTURING_SQUARE_BRACE_CLOSE)) {
return;
}
$tokens[$index] = new Token('(');
$tokens[$closeIndex] = new Token(')');
$tokens->insertAt($index, new Token([T_LIST, 'list']));
}
private function fixToShortSyntax(Tokens $tokens, int $index): void
{
$openIndex = $tokens->getNextTokenOfKind($index, ['(']);
$closeIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openIndex);
$tokens[$openIndex] = new Token([CT::T_DESTRUCTURING_SQUARE_BRACE_OPEN, '[']);
$tokens[$closeIndex] = new Token([CT::T_DESTRUCTURING_SQUARE_BRACE_CLOSE, ']']);
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Operator;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class ConcatSpaceFixer extends AbstractFixer implements ConfigurableFixerInterface
{
private $fixCallback;
public function configure(array $configuration): void
{
parent::configure($configuration);
if ('one' === $this->configuration['spacing']) {
$this->fixCallback = 'fixConcatenationToSingleSpace';
} else {
$this->fixCallback = 'fixConcatenationToNoSpace';
}
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Concatenation should be spaced according configuration.',
[
new CodeSample(
"<?php\n\$foo = 'bar' . 3 . 'baz'.'qux';\n"
),
new CodeSample(
"<?php\n\$foo = 'bar' . 3 . 'baz'.'qux';\n",
['spacing' => 'none']
),
new CodeSample(
"<?php\n\$foo = 'bar' . 3 . 'baz'.'qux';\n",
['spacing' => 'one']
),
]
);
}
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound('.');
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$callBack = $this->fixCallback;
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
if ($tokens[$index]->equals('.')) {
$this->{$callBack}($tokens, $index);
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('spacing', 'Spacing to apply around concatenation operator.'))
->setAllowedValues(['one', 'none'])
->setDefault('none')
->getOption(),
]);
}
private function fixConcatenationToNoSpace(Tokens $tokens, int $index): void
{
$prevNonWhitespaceToken = $tokens[$tokens->getPrevNonWhitespace($index)];
if (!$prevNonWhitespaceToken->isGivenKind([T_LNUMBER, T_COMMENT, T_DOC_COMMENT]) || str_starts_with($prevNonWhitespaceToken->getContent(), '/*')) {
$tokens->removeLeadingWhitespace($index, " \t");
}
if (!$tokens[$tokens->getNextNonWhitespace($index)]->isGivenKind([T_LNUMBER, T_COMMENT, T_DOC_COMMENT])) {
$tokens->removeTrailingWhitespace($index, " \t");
}
}
private function fixConcatenationToSingleSpace(Tokens $tokens, int $index): void
{
$this->fixWhiteSpaceAroundConcatToken($tokens, $index, 1);
$this->fixWhiteSpaceAroundConcatToken($tokens, $index, -1);
}
private function fixWhiteSpaceAroundConcatToken(Tokens $tokens, int $index, int $offset): void
{
$offsetIndex = $index + $offset;
if (!$tokens[$offsetIndex]->isWhitespace()) {
$tokens->insertAt($index + (1 === $offset ?: 0), new Token([T_WHITESPACE, ' ']));
return;
}
if (str_contains($tokens[$offsetIndex]->getContent(), "\n")) {
return;
}
if ($tokens[$index + $offset * 2]->isComment()) {
return;
}
$tokens[$offsetIndex] = new Token([T_WHITESPACE, ' ']);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Operator;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Tokens;
final class NotOperatorWithSuccessorSpaceFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Logical NOT operators (`!`) should have one trailing whitespace.',
[new CodeSample(
'<?php
if (!$bar) {
echo "Help!";
}
'
)]
);
}
public function getPriority(): int
{
return -10;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound('!');
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
$token = $tokens[$index];
if ($token->equals('!')) {
$tokens->ensureWhitespaceAtIndex($index + 1, 0, ' ');
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Operator;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
final class BinaryOperatorSpacesFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public const SINGLE_SPACE = 'single_space';
public const NO_SPACE = 'no_space';
public const ALIGN = 'align';
public const ALIGN_SINGLE_SPACE = 'align_single_space';
public const ALIGN_SINGLE_SPACE_MINIMAL = 'align_single_space_minimal';
/**
@const
*/
public const ALIGN_PLACEHOLDER = "\x2 ALIGNABLE%d \x3";
private const SUPPORTED_OPERATORS = [
'=',
'*',
'/',
'%',
'<',
'>',
'|',
'^',
'+',
'-',
'&',
'&=',
'&&',
'||',
'.=',
'/=',
'=>',
'==',
'>=',
'===',
'!=',
'<>',
'!==',
'<=',
'and',
'or',
'xor',
'-=',
'%=',
'*=',
'|=',
'+=',
'<<',
'<<=',
'>>',
'>>=',
'^=',
'**',
'**=',
'<=>',
'??',
'??=',
];
private int $deepestLevel;
private int $currentLevel;
private static array $allowedValues = [
self::ALIGN,
self::ALIGN_SINGLE_SPACE,
self::ALIGN_SINGLE_SPACE_MINIMAL,
self::SINGLE_SPACE,
self::NO_SPACE,
null,
];
private TokensAnalyzer $tokensAnalyzer;
private array $alignOperatorTokens = [];
private array $operators = [];
public function configure(array $configuration): void
{
parent::configure($configuration);
$this->operators = $this->resolveOperatorsFromConfig();
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Binary operators should be surrounded by space as configured.',
[
new CodeSample(
"<?php\n\$a= 1 + \$b^ \$d !== \$e or \$f;\n"
),
new CodeSample(
'<?php
$aa= 1;
$b=2;
$c = $d xor $e;
$f -= 1;
',
['operators' => ['=' => 'align', 'xor' => null]]
),
new CodeSample(
'<?php
$a = $b +=$c;
$d = $ee+=$f;
$g = $b +=$c;
$h = $ee+=$f;
',
['operators' => ['+=' => 'align_single_space']]
),
new CodeSample(
'<?php
$a = $b===$c;
$d = $f === $g;
$h = $i=== $j;
',
['operators' => ['===' => 'align_single_space_minimal']]
),
new CodeSample(
'<?php
$foo = \json_encode($bar, JSON_PRESERVE_ZERO_FRACTION | JSON_PRETTY_PRINT);
',
['operators' => ['|' => 'no_space']]
),
new CodeSample(
'<?php
$array = [
"foo" => 1,
"baaaaaaaaaaar" => 11,
];
',
['operators' => ['=>' => 'single_space']]
),
new CodeSample(
'<?php
$array = [
"foo" => 12,
"baaaaaaaaaaar" => 13,
];
',
['operators' => ['=>' => 'align']]
),
new CodeSample(
'<?php
$array = [
"foo" => 12,
"baaaaaaaaaaar" => 13,
];
',
['operators' => ['=>' => 'align_single_space']]
),
new CodeSample(
'<?php
$array = [
"foo" => 12,
"baaaaaaaaaaar" => 13,
];
',
['operators' => ['=>' => 'align_single_space_minimal']]
),
]
);
}
public function getPriority(): int
{
return -32;
}
public function isCandidate(Tokens $tokens): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$this->tokensAnalyzer = new TokensAnalyzer($tokens);
for ($index = $tokens->count() - 2; $index > 0; --$index) {
if (!$this->tokensAnalyzer->isBinaryOperator($index)) {
continue;
}
if ('=' === $tokens[$index]->getContent()) {
$isDeclare = $this->isEqualPartOfDeclareStatement($tokens, $index);
if (false === $isDeclare) {
$this->fixWhiteSpaceAroundOperator($tokens, $index);
} else {
$index = $isDeclare;
}
} else {
$this->fixWhiteSpaceAroundOperator($tokens, $index);
}
--$index;
}
if (\count($this->alignOperatorTokens) > 0) {
$this->fixAlignment($tokens, $this->alignOperatorTokens);
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('default', 'Default fix strategy.'))
->setDefault(self::SINGLE_SPACE)
->setAllowedValues(self::$allowedValues)
->getOption(),
(new FixerOptionBuilder('operators', 'Dictionary of `binary operator` => `fix strategy` values that differ from the default strategy. Supported are: `'.implode('`, `', self::SUPPORTED_OPERATORS).'`'))
->setAllowedTypes(['array'])
->setAllowedValues([static function (array $option): bool {
foreach ($option as $operator => $value) {
if (!\in_array($operator, self::SUPPORTED_OPERATORS, true)) {
throw new InvalidOptionsException(
sprintf(
'Unexpected "operators" key, expected any of "%s", got "%s".',
implode('", "', self::SUPPORTED_OPERATORS),
\gettype($operator).'#'.$operator
)
);
}
if (!\in_array($value, self::$allowedValues, true)) {
throw new InvalidOptionsException(
sprintf(
'Unexpected value for operator "%s", expected any of "%s", got "%s".',
$operator,
implode('", "', self::$allowedValues),
\is_object($value) ? \get_class($value) : (null === $value ? 'null' : \gettype($value).'#'.$value)
)
);
}
}
return true;
}])
->setDefault([])
->getOption(),
]);
}
private function fixWhiteSpaceAroundOperator(Tokens $tokens, int $index): void
{
$tokenContent = strtolower($tokens[$index]->getContent());
if (!\array_key_exists($tokenContent, $this->operators)) {
return;
}
if (self::SINGLE_SPACE === $this->operators[$tokenContent]) {
$this->fixWhiteSpaceAroundOperatorToSingleSpace($tokens, $index);
return;
}
if (self::NO_SPACE === $this->operators[$tokenContent]) {
$this->fixWhiteSpaceAroundOperatorToNoSpace($tokens, $index);
return;
}
$this->alignOperatorTokens[$tokenContent] = $this->operators[$tokenContent];
if (self::ALIGN === $this->operators[$tokenContent]) {
return;
}
if ($tokens[$index + 1]->isWhitespace()) {
if (self::ALIGN_SINGLE_SPACE_MINIMAL === $this->operators[$tokenContent]) {
$tokens[$index + 1] = new Token([T_WHITESPACE, ' ']);
}
return;
}
$tokens->insertAt($index + 1, new Token([T_WHITESPACE, ' ']));
}
private function fixWhiteSpaceAroundOperatorToSingleSpace(Tokens $tokens, int $index): void
{
if ($tokens[$index + 1]->isWhitespace()) {
$content = $tokens[$index + 1]->getContent();
if (' ' !== $content && !str_contains($content, "\n") && !$tokens[$tokens->getNextNonWhitespace($index + 1)]->isComment()) {
$tokens[$index + 1] = new Token([T_WHITESPACE, ' ']);
}
} else {
$tokens->insertAt($index + 1, new Token([T_WHITESPACE, ' ']));
}
if ($tokens[$index - 1]->isWhitespace()) {
$content = $tokens[$index - 1]->getContent();
if (' ' !== $content && !str_contains($content, "\n") && !$tokens[$tokens->getPrevNonWhitespace($index - 1)]->isComment()) {
$tokens[$index - 1] = new Token([T_WHITESPACE, ' ']);
}
} else {
$tokens->insertAt($index, new Token([T_WHITESPACE, ' ']));
}
}
private function fixWhiteSpaceAroundOperatorToNoSpace(Tokens $tokens, int $index): void
{
if ($tokens[$index + 1]->isWhitespace()) {
$content = $tokens[$index + 1]->getContent();
if (!str_contains($content, "\n") && !$tokens[$tokens->getNextNonWhitespace($index + 1)]->isComment()) {
$tokens->clearAt($index + 1);
}
}
if ($tokens[$index - 1]->isWhitespace()) {
$content = $tokens[$index - 1]->getContent();
if (!str_contains($content, "\n") && !$tokens[$tokens->getPrevNonWhitespace($index - 1)]->isComment()) {
$tokens->clearAt($index - 1);
}
}
}
private function isEqualPartOfDeclareStatement(Tokens $tokens, int $index)
{
$prevMeaningfulIndex = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$prevMeaningfulIndex]->isGivenKind(T_STRING)) {
$prevMeaningfulIndex = $tokens->getPrevMeaningfulToken($prevMeaningfulIndex);
if ($tokens[$prevMeaningfulIndex]->equals('(')) {
$prevMeaningfulIndex = $tokens->getPrevMeaningfulToken($prevMeaningfulIndex);
if ($tokens[$prevMeaningfulIndex]->isGivenKind(T_DECLARE)) {
return $prevMeaningfulIndex;
}
}
}
return false;
}
private function resolveOperatorsFromConfig(): array
{
$operators = [];
if (null !== $this->configuration['default']) {
foreach (self::SUPPORTED_OPERATORS as $operator) {
$operators[$operator] = $this->configuration['default'];
}
}
foreach ($this->configuration['operators'] as $operator => $value) {
if (null === $value) {
unset($operators[$operator]);
} else {
$operators[$operator] = $value;
}
}
return $operators;
}
private function fixAlignment(Tokens $tokens, array $toAlign): void
{
$this->deepestLevel = 0;
$this->currentLevel = 0;
foreach ($toAlign as $tokenContent => $alignStrategy) {
$tokensClone = clone $tokens;
if ('=>' === $tokenContent) {
$this->injectAlignmentPlaceholdersForArrow($tokensClone, 0, \count($tokens));
} else {
$this->injectAlignmentPlaceholdersDefault($tokensClone, 0, \count($tokens), $tokenContent);
}
if (self::ALIGN_SINGLE_SPACE === $alignStrategy || self::ALIGN_SINGLE_SPACE_MINIMAL === $alignStrategy) {
if ('=>' === $tokenContent) {
for ($index = $tokens->count() - 2; $index > 0; --$index) {
if ($tokens[$index]->isGivenKind(T_DOUBLE_ARROW)) {
$this->fixWhiteSpaceBeforeOperator($tokensClone, $index, $alignStrategy);
}
}
} elseif ('=' === $tokenContent) {
for ($index = $tokens->count() - 2; $index > 0; --$index) {
if ('=' === $tokens[$index]->getContent() && !$this->isEqualPartOfDeclareStatement($tokens, $index) && $this->tokensAnalyzer->isBinaryOperator($index)) {
$this->fixWhiteSpaceBeforeOperator($tokensClone, $index, $alignStrategy);
}
}
} else {
for ($index = $tokens->count() - 2; $index > 0; --$index) {
$content = $tokens[$index]->getContent();
if (strtolower($content) === $tokenContent && $this->tokensAnalyzer->isBinaryOperator($index)) {
$this->fixWhiteSpaceBeforeOperator($tokensClone, $index, $alignStrategy);
}
}
}
}
$tokens->setCode($this->replacePlaceholders($tokensClone, $alignStrategy));
}
}
private function injectAlignmentPlaceholdersDefault(Tokens $tokens, int $startAt, int $endAt, string $tokenContent): void
{
$newLineFoundSinceLastPlaceholder = true;
for ($index = $startAt; $index < $endAt; ++$index) {
$token = $tokens[$index];
$content = $token->getContent();
if (str_contains($content, "\n")) {
$newLineFoundSinceLastPlaceholder = true;
}
if (
strtolower($content) === $tokenContent
&& $this->tokensAnalyzer->isBinaryOperator($index)
&& ('=' !== $content || !$this->isEqualPartOfDeclareStatement($tokens, $index))
&& $newLineFoundSinceLastPlaceholder
) {
$tokens[$index] = new Token(sprintf(self::ALIGN_PLACEHOLDER, $this->currentLevel).$content);
$newLineFoundSinceLastPlaceholder = false;
continue;
}
if ($token->isGivenKind(T_FN)) {
$from = $tokens->getNextMeaningfulToken($index);
$until = $this->getLastTokenIndexOfFn($tokens, $index);
$this->injectAlignmentPlaceholders($tokens, $from + 1, $until - 1, $tokenContent);
$index = $until;
continue;
}
if ($token->isGivenKind([T_FUNCTION, T_CLASS])) {
$index = $tokens->getNextTokenOfKind($index, ['{', ';', '(']);
if ($tokens[$index]->equals('(')) {
$index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index);
continue;
}
if ($tokens[$index]->equals(';')) {
continue;
}
$token = $tokens[$index];
}
if ($token->equals('{')) {
$until = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $index);
$this->injectAlignmentPlaceholders($tokens, $index + 1, $until - 1, $tokenContent);
$index = $until;
continue;
}
if ($token->equals('(')) {
$until = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index);
$this->injectAlignmentPlaceholders($tokens, $index + 1, $until - 1, $tokenContent);
$index = $until;
continue;
}
if ($token->equals('[')) {
$index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_INDEX_SQUARE_BRACE, $index);
continue;
}
if ($token->isGivenKind(CT::T_ARRAY_SQUARE_BRACE_OPEN)) {
$until = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, $index);
$this->injectAlignmentPlaceholders($tokens, $index + 1, $until - 1, $tokenContent);
$index = $until;
continue;
}
}
}
private function injectAlignmentPlaceholders(Tokens $tokens, int $from, int $until, string $tokenContent): void
{
if ($tokens->isPartialCodeMultiline($from, $until)) {
++$this->deepestLevel;
$currentLevel = $this->currentLevel;
$this->currentLevel = $this->deepestLevel;
$this->injectAlignmentPlaceholdersDefault($tokens, $from, $until, $tokenContent);
$this->currentLevel = $currentLevel;
}
}
private function injectAlignmentPlaceholdersForArrow(Tokens $tokens, int $startAt, int $endAt): void
{
$newLineFoundSinceLastPlaceholder = true;
for ($index = $startAt; $index < $endAt; ++$index) {
$token = $tokens[$index];
$content = $token->getContent();
if (str_contains($content, "\n")) {
$newLineFoundSinceLastPlaceholder = true;
}
if ($token->isGivenKind(T_FN)) {
$from = $tokens->getNextMeaningfulToken($index);
$until = $this->getLastTokenIndexOfFn($tokens, $index);
$this->injectArrayAlignmentPlaceholders($tokens, $from + 1, $until - 1);
$index = $until;
continue;
}
if ($token->isGivenKind(T_ARRAY)) {
$from = $tokens->getNextMeaningfulToken($index);
$until = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $from);
$index = $until;
$this->injectArrayAlignmentPlaceholders($tokens, $from + 1, $until - 1);
continue;
}
if ($token->isGivenKind(CT::T_ARRAY_SQUARE_BRACE_OPEN)) {
$from = $index;
$until = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, $from);
$index = $until;
$this->injectArrayAlignmentPlaceholders($tokens, $from + 1, $until - 1);
continue;
}
if ($token->isGivenKind(T_DOUBLE_ARROW) && $newLineFoundSinceLastPlaceholder) {
$tokenContent = sprintf(self::ALIGN_PLACEHOLDER, $this->currentLevel).$token->getContent();
$nextToken = $tokens[$index + 1];
if (!$nextToken->isWhitespace()) {
$tokenContent .= ' ';
} elseif ($nextToken->isWhitespace(" \t")) {
$tokens[$index + 1] = new Token([T_WHITESPACE, ' ']);
}
$tokens[$index] = new Token([T_DOUBLE_ARROW, $tokenContent]);
$newLineFoundSinceLastPlaceholder = false;
continue;
}
if ($token->equals(';')) {
++$this->deepestLevel;
++$this->currentLevel;
continue;
}
if ($token->equals(',')) {
for ($i = $index; $i < $endAt - 1; ++$i) {
if (str_contains($tokens[$i - 1]->getContent(), "\n")) {
$newLineFoundSinceLastPlaceholder = true;
break;
}
if ($tokens[$i + 1]->isGivenKind([T_ARRAY, CT::T_ARRAY_SQUARE_BRACE_OPEN])) {
$arrayStartIndex = $tokens[$i + 1]->isGivenKind(T_ARRAY)
? $tokens->getNextMeaningfulToken($i + 1)
: $i + 1
;
$blockType = Tokens::detectBlockType($tokens[$arrayStartIndex]);
$arrayEndIndex = $tokens->findBlockEnd($blockType['type'], $arrayStartIndex);
if ($tokens->isPartialCodeMultiline($arrayStartIndex, $arrayEndIndex)) {
break;
}
}
++$index;
}
}
if ($token->equals('{')) {
$until = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $index);
$this->injectArrayAlignmentPlaceholders($tokens, $index + 1, $until - 1);
$index = $until;
continue;
}
if ($token->equals('(')) {
$until = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index);
$this->injectArrayAlignmentPlaceholders($tokens, $index + 1, $until - 1);
$index = $until;
continue;
}
}
}
private function injectArrayAlignmentPlaceholders(Tokens $tokens, int $from, int $until): void
{
if ($tokens->isPartialCodeMultiline($from, $until)) {
++$this->deepestLevel;
$currentLevel = $this->currentLevel;
$this->currentLevel = $this->deepestLevel;
$this->injectAlignmentPlaceholdersForArrow($tokens, $from, $until);
$this->currentLevel = $currentLevel;
}
}
private function fixWhiteSpaceBeforeOperator(Tokens $tokens, int $index, string $alignStrategy): void
{
if (!$tokens[$index - 1]->isWhitespace()) {
$tokens->insertAt($index, new Token([T_WHITESPACE, ' ']));
return;
}
if (self::ALIGN_SINGLE_SPACE_MINIMAL !== $alignStrategy || $tokens[$tokens->getPrevNonWhitespace($index - 1)]->isComment()) {
return;
}
$content = $tokens[$index - 1]->getContent();
if (' ' !== $content && !str_contains($content, "\n")) {
$tokens[$index - 1] = new Token([T_WHITESPACE, ' ']);
}
}
private function replacePlaceholders(Tokens $tokens, string $alignStrategy): string
{
$tmpCode = $tokens->generateCode();
for ($j = 0; $j <= $this->deepestLevel; ++$j) {
$placeholder = sprintf(self::ALIGN_PLACEHOLDER, $j);
if (!str_contains($tmpCode, $placeholder)) {
continue;
}
$lines = explode("\n", $tmpCode);
$groups = [];
$groupIndex = 0;
$groups[$groupIndex] = [];
foreach ($lines as $index => $line) {
if (substr_count($line, $placeholder) > 0) {
$groups[$groupIndex][] = $index;
} else {
++$groupIndex;
$groups[$groupIndex] = [];
}
}
foreach ($groups as $group) {
if (\count($group) < 1) {
continue;
}
if (self::ALIGN !== $alignStrategy) {
foreach ($group as $index) {
$currentPosition = strpos($lines[$index], $placeholder);
$before = substr($lines[$index], 0, $currentPosition);
if (self::ALIGN_SINGLE_SPACE === $alignStrategy) {
if (!str_ends_with($before, ' ')) {
$before .= ' ';
}
} elseif (self::ALIGN_SINGLE_SPACE_MINIMAL === $alignStrategy) {
if (1 !== Preg::match('/^\h+$/', $before)) {
$before = rtrim($before).' ';
}
}
$lines[$index] = $before.substr($lines[$index], $currentPosition);
}
}
$rightmostSymbol = 0;
foreach ($group as $index) {
$rightmostSymbol = max($rightmostSymbol, mb_strpos($lines[$index], $placeholder));
}
foreach ($group as $index) {
$line = $lines[$index];
$currentSymbol = mb_strpos($line, $placeholder);
$delta = abs($rightmostSymbol - $currentSymbol);
if ($delta > 0) {
$line = str_replace($placeholder, str_repeat(' ', $delta).$placeholder, $line);
$lines[$index] = $line;
}
}
}
$tmpCode = str_replace($placeholder, '', implode("\n", $lines));
}
return $tmpCode;
}
private function getLastTokenIndexOfFn(Tokens $tokens, int $index): int
{
$index = $tokens->getNextTokenOfKind($index, [[T_DOUBLE_ARROW]]);
while (true) {
$index = $tokens->getNextMeaningfulToken($index);
if ($tokens[$index]->equalsAny([';', ',', [T_CLOSE_TAG]])) {
break;
}
$blockType = Tokens::detectBlockType($tokens[$index]);
if (null === $blockType) {
continue;
}
if ($blockType['isStart']) {
$index = $tokens->findBlockEnd($blockType['type'], $index);
continue;
}
break;
}
return $index;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Operator;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\AlternativeSyntaxAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\SwitchAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\ControlCaseStructuresAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\GotoLabelAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class TernaryOperatorSpacesFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Standardize spaces around ternary operator.',
[new CodeSample("<?php \$a = \$a ?1 :0;\n")]
);
}
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAllTokenKindsFound(['?', ':']);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$alternativeSyntaxAnalyzer = new AlternativeSyntaxAnalyzer();
$gotoLabelAnalyzer = new GotoLabelAnalyzer();
$ternaryOperatorIndices = [];
$excludedIndices = $this->getColonIndicesForSwitch($tokens);
foreach ($tokens as $index => $token) {
if (!$token->equalsAny(['?', ':'])) {
continue;
}
if (\in_array($index, $excludedIndices, true)) {
continue;
}
if ($alternativeSyntaxAnalyzer->belongsToAlternativeSyntax($tokens, $index)) {
continue;
}
if ($gotoLabelAnalyzer->belongsToGoToLabel($tokens, $index)) {
continue;
}
$ternaryOperatorIndices[] = $index;
}
foreach (array_reverse($ternaryOperatorIndices) as $index) {
$token = $tokens[$index];
if ($token->equals('?')) {
$nextNonWhitespaceIndex = $tokens->getNextNonWhitespace($index);
if ($tokens[$nextNonWhitespaceIndex]->equals(':')) {
$tokens->ensureWhitespaceAtIndex($index + 1, 0, '');
} else {
$this->ensureWhitespaceExistence($tokens, $index + 1, true);
}
$this->ensureWhitespaceExistence($tokens, $index - 1, false);
continue;
}
if ($token->equals(':')) {
$this->ensureWhitespaceExistence($tokens, $index + 1, true);
$prevNonWhitespaceToken = $tokens[$tokens->getPrevNonWhitespace($index)];
if (!$prevNonWhitespaceToken->equals('?')) {
$this->ensureWhitespaceExistence($tokens, $index - 1, false);
}
}
}
}
private function getColonIndicesForSwitch(Tokens $tokens): array
{
$colonIndices = [];
foreach (ControlCaseStructuresAnalyzer::findControlStructures($tokens, [T_SWITCH]) as $analysis) {
foreach ($analysis->getCases() as $case) {
$colonIndices[] = $case->getColonIndex();
}
$defaultAnalysis = $analysis->getDefaultAnalysis();
if (null !== $defaultAnalysis) {
$colonIndices[] = $defaultAnalysis->getColonIndex();
}
}
return $colonIndices;
}
private function ensureWhitespaceExistence(Tokens $tokens, int $index, bool $after): void
{
if ($tokens[$index]->isWhitespace()) {
if (
!str_contains($tokens[$index]->getContent(), "\n")
&& !$tokens[$index - 1]->isComment()
) {
$tokens[$index] = new Token([T_WHITESPACE, ' ']);
}
return;
}
$index += $after ? 0 : 1;
$tokens->insertAt($index, new Token([T_WHITESPACE, ' ']));
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Operator;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class TernaryToNullCoalescingFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Use `null` coalescing operator `??` where possible. Requires PHP >= 7.0.',
[
new CodeSample(
"<?php\n\$sample = isset(\$a) ? \$a : \$b;\n"
),
]
);
}
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_ISSET);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$issetIndices = array_keys($tokens->findGivenKind(T_ISSET));
while ($issetIndex = array_pop($issetIndices)) {
$this->fixIsset($tokens, $issetIndex);
}
}
private function fixIsset(Tokens $tokens, int $index): void
{
$prevTokenIndex = $tokens->getPrevMeaningfulToken($index);
if ($this->isHigherPrecedenceAssociativityOperator($tokens[$prevTokenIndex])) {
return;
}
$startBraceIndex = $tokens->getNextTokenOfKind($index, ['(']);
$endBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $startBraceIndex);
$ternaryQuestionMarkIndex = $tokens->getNextMeaningfulToken($endBraceIndex);
if (!$tokens[$ternaryQuestionMarkIndex]->equals('?')) {
return;
}
$issetTokens = $this->getMeaningfulSequence($tokens, $startBraceIndex, $endBraceIndex);
if ($this->hasChangingContent($issetTokens)) {
return;
}
$ternaryColonIndex = $tokens->getNextTokenOfKind($ternaryQuestionMarkIndex, [':']);
$ternaryFirstOperandTokens = $this->getMeaningfulSequence($tokens, $ternaryQuestionMarkIndex, $ternaryColonIndex);
if ($issetTokens->generateCode() !== $ternaryFirstOperandTokens->generateCode()) {
return;
}
$ternaryFirstOperandIndex = $tokens->getNextMeaningfulToken($ternaryQuestionMarkIndex);
$comments = [];
$commentStarted = false;
for ($loopIndex = $index; $loopIndex < $ternaryFirstOperandIndex; ++$loopIndex) {
if ($tokens[$loopIndex]->isComment()) {
$comments[] = $tokens[$loopIndex];
$commentStarted = true;
} elseif ($commentStarted) {
if ($tokens[$loopIndex]->isWhitespace()) {
$comments[] = $tokens[$loopIndex];
}
$commentStarted = false;
}
}
$tokens[$ternaryColonIndex] = new Token([T_COALESCE, '??']);
$tokens->overrideRange($index, $ternaryFirstOperandIndex - 1, $comments);
}
private function getMeaningfulSequence(Tokens $tokens, int $start, int $end): Tokens
{
$sequence = [];
$index = $start;
while ($index < $end) {
$index = $tokens->getNextMeaningfulToken($index);
if ($index >= $end || null === $index) {
break;
}
$sequence[] = $tokens[$index];
}
return Tokens::fromArray($sequence);
}
private function isHigherPrecedenceAssociativityOperator(Token $token): bool
{
static $operatorsPerId = [
T_ARRAY_CAST => true,
T_BOOLEAN_AND => true,
T_BOOLEAN_OR => true,
T_BOOL_CAST => true,
T_COALESCE => true,
T_DEC => true,
T_DOUBLE_CAST => true,
T_INC => true,
T_INT_CAST => true,
T_IS_EQUAL => true,
T_IS_GREATER_OR_EQUAL => true,
T_IS_IDENTICAL => true,
T_IS_NOT_EQUAL => true,
T_IS_NOT_IDENTICAL => true,
T_IS_SMALLER_OR_EQUAL => true,
T_OBJECT_CAST => true,
T_POW => true,
T_SL => true,
T_SPACESHIP => true,
T_SR => true,
T_STRING_CAST => true,
T_UNSET_CAST => true,
];
static $operatorsPerContent = [
'!',
'%',
'&',
'*',
'+',
'-',
'/',
':',
'^',
'|',
'~',
'.',
];
return isset($operatorsPerId[$token->getId()]) || $token->equalsAny($operatorsPerContent);
}
private function hasChangingContent(Tokens $tokens): bool
{
static $operatorsPerId = [
T_DEC,
T_INC,
T_YIELD,
T_YIELD_FROM,
];
foreach ($tokens as $token) {
if ($token->isGivenKind($operatorsPerId) || $token->equals('(')) {
return true;
}
}
return false;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Operator;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class UnaryOperatorSpacesFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Unary operators should be placed adjacent to their operands.',
[new CodeSample("<?php\n\$sample ++;\n-- \$sample;\n\$sample = ! ! \$a;\n\$sample = ~ \$c;\nfunction & foo(){}\n")]
);
}
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$tokensAnalyzer = new TokensAnalyzer($tokens);
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
if ($tokensAnalyzer->isUnarySuccessorOperator($index)) {
if (!$tokens[$tokens->getPrevNonWhitespace($index)]->isComment()) {
$tokens->removeLeadingWhitespace($index);
}
continue;
}
if ($tokensAnalyzer->isUnaryPredecessorOperator($index)) {
$tokens->removeTrailingWhitespace($index);
continue;
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Operator;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\RangeAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Tokens;
final class TernaryToElvisOperatorFixer extends AbstractFixer
{
private const VALID_BEFORE_ENDTYPES = [
'=',
[T_OPEN_TAG],
[T_OPEN_TAG_WITH_ECHO],
'(',
',',
';',
'[',
'{',
'}',
[CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN],
[T_AND_EQUAL],
[T_CONCAT_EQUAL],
[T_DIV_EQUAL],
[T_MINUS_EQUAL],
[T_MOD_EQUAL],
[T_MUL_EQUAL],
[T_OR_EQUAL],
[T_PLUS_EQUAL],
[T_POW_EQUAL],
[T_SL_EQUAL],
[T_SR_EQUAL],
[T_XOR_EQUAL],
];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Use the Elvis operator `?:` where possible.',
[
new CodeSample(
"<?php\n\$foo = \$foo ? \$foo : 1;\n"
),
new CodeSample(
"<?php \$foo = \$bar[a()] ? \$bar[a()] : 1; # \"risky\" sample, \"a()\" only gets called once after fixing\n"
),
],
null,
'Risky when relying on functions called on both sides of the `?` operator.'
);
}
public function getPriority(): int
{
return 1;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound('?');
}
public function isRisky(): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$blockEdgeDefinitions = Tokens::getBlockEdgeDefinitions();
for ($index = \count($tokens) - 5; $index > 1; --$index) {
if (!$tokens[$index]->equals('?')) {
continue;
}
$nextIndex = $tokens->getNextMeaningfulToken($index);
if ($tokens[$nextIndex]->equals(':')) {
continue;
}
$beforeOperator = $this->getBeforeOperator($tokens, $index, $blockEdgeDefinitions);
if (null === $beforeOperator) {
continue;
}
$afterOperator = $this->getAfterOperator($tokens, $index);
if (RangeAnalyzer::rangeEqualsRange($tokens, $beforeOperator, $afterOperator)) {
$this->clearMeaningfulFromRange($tokens, $afterOperator);
}
}
}
private function getBeforeOperator(Tokens $tokens, int $index, array $blockEdgeDefinitions): ?array
{
$index = $tokens->getPrevMeaningfulToken($index);
$before = ['end' => $index];
while (!$tokens[$index]->equalsAny(self::VALID_BEFORE_ENDTYPES)) {
if ($tokens[$index]->isGivenKind([T_INC, T_DEC])) {
return null;
}
$blockType = Tokens::detectBlockType($tokens[$index]);
if (null === $blockType || $blockType['isStart']) {
$before['start'] = $index;
$index = $tokens->getPrevMeaningfulToken($index);
continue;
}
$blockType = $blockEdgeDefinitions[$blockType['type']];
$openCount = 1;
do {
$index = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$index]->isGivenKind([T_INC, T_DEC])) {
return null;
}
if ($tokens[$index]->equals($blockType['start'])) {
++$openCount;
continue;
}
if ($tokens[$index]->equals($blockType['end'])) {
--$openCount;
}
} while (1 >= $openCount);
$before['start'] = $index;
$index = $tokens->getPrevMeaningfulToken($index);
}
if (!isset($before['start'])) {
return null;
}
return $before;
}
private function getAfterOperator(Tokens $tokens, int $index): array
{
$index = $tokens->getNextMeaningfulToken($index);
$after = ['start' => $index];
while (!$tokens[$index]->equals(':')) {
$blockType = Tokens::detectBlockType($tokens[$index]);
if (null !== $blockType) {
$index = $tokens->findBlockEnd($blockType['type'], $index);
}
$after['end'] = $index;
$index = $tokens->getNextMeaningfulToken($index);
}
return $after;
}
private function clearMeaningfulFromRange(Tokens $tokens, array $range): void
{
for ($i = $range['end']; $i >= $range['start']; $i = $tokens->getPrevMeaningfulToken($i)) {
$tokens->clearTokenAndMergeSurroundingWhitespace($i);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Operator;
use PhpCsFixer\Fixer\AbstractIncrementOperatorFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class IncrementStyleFixer extends AbstractIncrementOperatorFixer implements ConfigurableFixerInterface
{
public const STYLE_PRE = 'pre';
public const STYLE_POST = 'post';
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Pre- or post-increment and decrement operators should be used if possible.',
[
new CodeSample("<?php\n\$a++;\n\$b--;\n"),
new CodeSample(
"<?php\n++\$a;\n--\$b;\n",
['style' => self::STYLE_POST]
),
]
);
}
public function getPriority(): int
{
return 15;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_INC, T_DEC]);
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('style', 'Whether to use pre- or post-increment and decrement operators.'))
->setAllowedValues([self::STYLE_PRE, self::STYLE_POST])
->setDefault(self::STYLE_PRE)
->getOption(),
]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$tokensAnalyzer = new TokensAnalyzer($tokens);
for ($index = $tokens->count() - 1; 0 <= $index; --$index) {
$token = $tokens[$index];
if (!$token->isGivenKind([T_INC, T_DEC])) {
continue;
}
if (self::STYLE_PRE === $this->configuration['style'] && $tokensAnalyzer->isUnarySuccessorOperator($index)) {
$nextToken = $tokens[$tokens->getNextMeaningfulToken($index)];
if (!$nextToken->equalsAny([';', ')'])) {
continue;
}
$startIndex = $this->findStart($tokens, $index);
$prevToken = $tokens[$tokens->getPrevMeaningfulToken($startIndex)];
if ($prevToken->equalsAny([';', '{', '}', [T_OPEN_TAG], ')'])) {
$tokens->clearAt($index);
$tokens->insertAt($startIndex, clone $token);
}
} elseif (self::STYLE_POST === $this->configuration['style'] && $tokensAnalyzer->isUnaryPredecessorOperator($index)) {
$prevToken = $tokens[$tokens->getPrevMeaningfulToken($index)];
if (!$prevToken->equalsAny([';', '{', '}', [T_OPEN_TAG], ')'])) {
continue;
}
$endIndex = $this->findEnd($tokens, $index);
$nextToken = $tokens[$tokens->getNextMeaningfulToken($endIndex)];
if ($nextToken->equalsAny([';', ')'])) {
$tokens->clearAt($index);
$tokens->insertAt($tokens->getNextNonWhitespace($endIndex), clone $token);
}
}
}
}
private function findEnd(Tokens $tokens, int $index): int
{
$nextIndex = $tokens->getNextMeaningfulToken($index);
$nextToken = $tokens[$nextIndex];
while ($nextToken->equalsAny([
'$',
'(',
'[',
[CT::T_DYNAMIC_PROP_BRACE_OPEN],
[CT::T_DYNAMIC_VAR_BRACE_OPEN],
[CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN],
[T_NS_SEPARATOR],
[T_STATIC],
[T_STRING],
[T_VARIABLE],
])) {
$blockType = Tokens::detectBlockType($nextToken);
if (null !== $blockType) {
$nextIndex = $tokens->findBlockEnd($blockType['type'], $nextIndex);
}
$index = $nextIndex;
$nextIndex = $tokens->getNextMeaningfulToken($nextIndex);
$nextToken = $tokens[$nextIndex];
}
if ($nextToken->isObjectOperator()) {
return $this->findEnd($tokens, $nextIndex);
}
if ($nextToken->isGivenKind(T_PAAMAYIM_NEKUDOTAYIM)) {
return $this->findEnd($tokens, $tokens->getNextMeaningfulToken($nextIndex));
}
return $index;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Operator;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Analyzer\AlternativeSyntaxAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\SwitchAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\ControlCaseStructuresAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\GotoLabelAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\ReferenceAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class OperatorLinebreakFixer extends AbstractFixer implements ConfigurableFixerInterface
{
private const BOOLEAN_OPERATORS = [[T_BOOLEAN_AND], [T_BOOLEAN_OR], [T_LOGICAL_AND], [T_LOGICAL_OR], [T_LOGICAL_XOR]];
private string $position = 'beginning';
private array $operators = [];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Operators - when multiline - must always be at the beginning or at the end of the line.',
[
new CodeSample('<?php
function foo() {
return $bar ||
$baz;
}
'),
new CodeSample(
'<?php
function foo() {
return $bar
|| $baz;
}
',
['position' => 'end']
),
]
);
}
public function configure(array $configuration): void
{
parent::configure($configuration);
$this->position = $this->configuration['position'];
$this->operators = self::BOOLEAN_OPERATORS;
if (false === $this->configuration['only_booleans']) {
$this->operators = array_merge($this->operators, self::getNonBooleanOperators());
}
}
public function isCandidate(Tokens $tokens): bool
{
return true;
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('only_booleans', 'whether to limit operators to only boolean ones'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
(new FixerOptionBuilder('position', 'whether to place operators at the beginning or at the end of the line'))
->setAllowedValues(['beginning', 'end'])
->setDefault($this->position)
->getOption(),
]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$referenceAnalyzer = new ReferenceAnalyzer();
$gotoLabelAnalyzer = new GotoLabelAnalyzer();
$alternativeSyntaxAnalyzer = new AlternativeSyntaxAnalyzer();
$excludedIndices = $this->getExcludedIndices($tokens);
$index = $tokens->count();
while ($index > 1) {
--$index;
if (!$tokens[$index]->equalsAny($this->operators, false)) {
continue;
}
if ($gotoLabelAnalyzer->belongsToGoToLabel($tokens, $index)) {
continue;
}
if ($referenceAnalyzer->isReference($tokens, $index)) {
continue;
}
if ($alternativeSyntaxAnalyzer->belongsToAlternativeSyntax($tokens, $index)) {
continue;
}
if (\in_array($index, $excludedIndices, true)) {
continue;
}
$operatorIndices = [$index];
if ($tokens[$index]->equals(':')) {
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$prevIndex]->equals('?')) {
$operatorIndices = [$prevIndex, $index];
$index = $prevIndex;
}
}
$this->fixOperatorLinebreak($tokens, $operatorIndices);
}
}
private function getExcludedIndices(Tokens $tokens): array
{
$colonIndices = [];
foreach (ControlCaseStructuresAnalyzer::findControlStructures($tokens, [T_SWITCH]) as $analysis) {
foreach ($analysis->getCases() as $case) {
$colonIndices[] = $case->getColonIndex();
}
$defaultAnalysis = $analysis->getDefaultAnalysis();
if (null !== $defaultAnalysis) {
$colonIndices[] = $defaultAnalysis->getColonIndex();
}
}
return $colonIndices;
}
private function fixOperatorLinebreak(Tokens $tokens, array $operatorIndices): void
{
$prevIndex = $tokens->getPrevMeaningfulToken(min($operatorIndices));
$indexStart = $prevIndex + 1;
$nextIndex = $tokens->getNextMeaningfulToken(max($operatorIndices));
$indexEnd = $nextIndex - 1;
if (!$this->isMultiline($tokens, $indexStart, $indexEnd)) {
return;
}
if ('beginning' === $this->position) {
if (!$this->isMultiline($tokens, max($operatorIndices), $indexEnd)) {
return;
}
$this->fixMoveToTheBeginning($tokens, $operatorIndices);
return;
}
if (!$this->isMultiline($tokens, $indexStart, min($operatorIndices))) {
return;
}
$this->fixMoveToTheEnd($tokens, $operatorIndices);
}
private function fixMoveToTheBeginning(Tokens $tokens, array $operatorIndices): void
{
$prevIndex = $tokens->getNonEmptySibling(min($operatorIndices), -1);
$nextIndex = $tokens->getNextMeaningfulToken(max($operatorIndices));
for ($i = $nextIndex - 1; $i > max($operatorIndices); --$i) {
if ($tokens[$i]->isWhitespace() && 1 === Preg::match('/\R/u', $tokens[$i]->getContent())) {
$isWhitespaceBefore = $tokens[$prevIndex]->isWhitespace();
$inserts = $this->getReplacementsAndClear($tokens, $operatorIndices, -1);
if ($isWhitespaceBefore) {
$inserts[] = new Token([T_WHITESPACE, ' ']);
}
$tokens->insertAt($nextIndex, $inserts);
break;
}
}
}
private function fixMoveToTheEnd(Tokens $tokens, array $operatorIndices): void
{
$prevIndex = $tokens->getPrevMeaningfulToken(min($operatorIndices));
$nextIndex = $tokens->getNonEmptySibling(max($operatorIndices), 1);
for ($i = $prevIndex + 1; $i < max($operatorIndices); ++$i) {
if ($tokens[$i]->isWhitespace() && 1 === Preg::match('/\R/u', $tokens[$i]->getContent())) {
$isWhitespaceAfter = $tokens[$nextIndex]->isWhitespace();
$inserts = $this->getReplacementsAndClear($tokens, $operatorIndices, 1);
if ($isWhitespaceAfter) {
array_unshift($inserts, new Token([T_WHITESPACE, ' ']));
}
$tokens->insertAt($prevIndex + 1, $inserts);
break;
}
}
}
private function getReplacementsAndClear(Tokens $tokens, array $indices, int $direction): array
{
return array_map(
static function (int $index) use ($tokens, $direction): Token {
$clone = $tokens[$index];
if ($tokens[$index + $direction]->isWhitespace()) {
$tokens->clearAt($index + $direction);
}
$tokens->clearAt($index);
return $clone;
},
$indices
);
}
private function isMultiline(Tokens $tokens, int $indexStart, int $indexEnd): bool
{
for ($index = $indexStart; $index <= $indexEnd; ++$index) {
if (str_contains($tokens[$index]->getContent(), "\n")) {
return true;
}
}
return false;
}
private static function getNonBooleanOperators(): array
{
return array_merge(
[
'%', '&', '*', '+', '-', '.', '/', ':', '<', '=', '>', '?', '^', '|',
[T_AND_EQUAL], [T_CONCAT_EQUAL], [T_DIV_EQUAL], [T_DOUBLE_ARROW], [T_IS_EQUAL], [T_IS_GREATER_OR_EQUAL],
[T_IS_IDENTICAL], [T_IS_NOT_EQUAL], [T_IS_NOT_IDENTICAL], [T_IS_SMALLER_OR_EQUAL], [T_MINUS_EQUAL],
[T_MOD_EQUAL], [T_MUL_EQUAL], [T_OR_EQUAL], [T_PAAMAYIM_NEKUDOTAYIM], [T_PLUS_EQUAL], [T_POW],
[T_POW_EQUAL], [T_SL], [T_SL_EQUAL], [T_SR], [T_SR_EQUAL], [T_XOR_EQUAL],
[T_COALESCE], [T_SPACESHIP],
],
array_map(static fn (int $id): array => [$id], Token::getObjectOperatorKinds()),
);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Operator;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class LogicalOperatorsFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Use `&&` and `||` logical operators instead of `and` and `or`.',
[
new CodeSample(
'<?php
if ($a == "foo" and ($b == "bar" or $c == "baz")) {
}
'
),
],
null,
'Risky, because you must double-check if using and/or with lower precedence was intentional.'
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_LOGICAL_AND, T_LOGICAL_OR]);
}
public function isRisky(): bool
{
return true;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if ($token->isGivenKind(T_LOGICAL_AND)) {
$tokens[$index] = new Token([T_BOOLEAN_AND, '&&']);
} elseif ($token->isGivenKind(T_LOGICAL_OR)) {
$tokens[$index] = new Token([T_BOOLEAN_OR, '||']);
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Operator;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NewWithBracesFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'All instances created with `new` keyword must (not) be followed by braces.',
[
new CodeSample("<?php\n\n\$x = new X;\n\$y = new class {};\n"),
new CodeSample(
"<?php\n\n\$y = new class() {};\n",
['anonymous_class' => false]
),
new CodeSample(
"<?php\n\n\$x = new X();\n",
['named_class' => false]
),
]
);
}
public function getPriority(): int
{
return 37;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_NEW);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
static $nextTokenKinds = null;
if (null === $nextTokenKinds) {
$nextTokenKinds = [
'?',
';',
',',
'(',
')',
'[',
']',
':',
'<',
'>',
'+',
'-',
'*',
'/',
'%',
'&',
'^',
'|',
[T_CLASS],
[T_IS_SMALLER_OR_EQUAL],
[T_IS_GREATER_OR_EQUAL],
[T_IS_EQUAL],
[T_IS_NOT_EQUAL],
[T_IS_IDENTICAL],
[T_IS_NOT_IDENTICAL],
[T_CLOSE_TAG],
[T_LOGICAL_AND],
[T_LOGICAL_OR],
[T_LOGICAL_XOR],
[T_BOOLEAN_AND],
[T_BOOLEAN_OR],
[T_SL],
[T_SR],
[T_INSTANCEOF],
[T_AS],
[T_DOUBLE_ARROW],
[T_POW],
[T_SPACESHIP],
[CT::T_ARRAY_SQUARE_BRACE_OPEN],
[CT::T_ARRAY_SQUARE_BRACE_CLOSE],
[CT::T_BRACE_CLASS_INSTANTIATION_OPEN],
[CT::T_BRACE_CLASS_INSTANTIATION_CLOSE],
];
if (\defined('T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG')) {
$nextTokenKinds[] = [T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG];
$nextTokenKinds[] = [T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG];
}
}
for ($index = $tokens->count() - 3; $index > 0; --$index) {
if (!$tokens[$index]->isGivenKind(T_NEW)) {
continue;
}
$nextIndex = $tokens->getNextTokenOfKind($index, $nextTokenKinds);
if ($tokens[$nextIndex]->isGivenKind(T_CLASS)) {
$nextIndex = $tokens->getNextMeaningfulToken($nextIndex);
if ($this->configuration['anonymous_class']) {
$this->ensureBracesAt($tokens, $nextIndex);
} else {
$this->ensureNoBracesAt($tokens, $nextIndex);
}
continue;
}
while ($tokens[$nextIndex]->equals('[') || $tokens[$nextIndex]->isGivenKind(CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN)) {
$nextIndex = $tokens->findBlockEnd(Tokens::detectBlockType($tokens[$nextIndex])['type'], $nextIndex);
$nextIndex = $tokens->getNextMeaningfulToken($nextIndex);
}
if ($this->configuration['named_class']) {
$this->ensureBracesAt($tokens, $nextIndex);
} else {
$this->ensureNoBracesAt($tokens, $nextIndex);
}
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('named_class', 'Whether named classes should be followed by parentheses.'))
->setAllowedTypes(['bool'])
->setDefault(true)
->getOption(),
(new FixerOptionBuilder('anonymous_class', 'Whether anonymous classes should be followed by parentheses.'))
->setAllowedTypes(['bool'])
->setDefault(true)
->getOption(),
]);
}
private function ensureBracesAt(Tokens $tokens, int $index): void
{
$token = $tokens[$index];
if (!$token->equals('(') && !$token->isObjectOperator()) {
$tokens->insertAt(
$tokens->getPrevMeaningfulToken($index) + 1,
[new Token('('), new Token(')')]
);
}
}
private function ensureNoBracesAt(Tokens $tokens, int $index): void
{
if (!$tokens[$index]->equals('(')) {
return;
}
$closingIndex = $tokens->getNextMeaningfulToken($index);
if (!$tokens[$closingIndex]->equals(')')) {
return;
}
$tokens->clearTokenAndMergeSurroundingWhitespace($closingIndex);
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Operator;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\FixerDefinition\VersionSpecification;
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
use PhpCsFixer\Tokenizer\Analyzer\RangeAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class AssignNullCoalescingToCoalesceEqualFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Use the null coalescing assignment operator `??=` where possible.',
[
new VersionSpecificCodeSample(
"<?php\n\$foo = \$foo ?? 1;\n",
new VersionSpecification(70400)
),
]
);
}
public function getPriority(): int
{
return -1;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_COALESCE);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = \count($tokens) - 1; $index > 3; --$index) {
if (!$tokens[$index]->isGivenKind(T_COALESCE)) {
continue;
}
$nextIndex = $tokens->getNextTokenOfKind($index, ['?', ';', [T_CLOSE_TAG]]);
if ($tokens[$nextIndex]->equals('?')) {
continue;
}
$beforeRange = $this->getBeforeOperator($tokens, $index);
$equalsIndex = $tokens->getPrevMeaningfulToken($beforeRange['start']);
if (!$tokens[$equalsIndex]->equals('=')) {
continue;
}
$assignRange = $this->getBeforeOperator($tokens, $equalsIndex);
$beforeAssignmentIndex = $tokens->getPrevMeaningfulToken($assignRange['start']);
if (!$tokens[$beforeAssignmentIndex]->equalsAny([';', '{', '}', ')', '(', [T_OPEN_TAG]])) {
continue;
}
if (!RangeAnalyzer::rangeEqualsRange($tokens, $assignRange, $beforeRange)) {
continue;
}
$tokens[$equalsIndex] = new Token([T_COALESCE_EQUAL, '??=']);
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
$this->clearMeaningfulFromRange($tokens, $beforeRange);
foreach ([$equalsIndex, $assignRange['end']] as $i) {
$i = $tokens->getNonEmptySibling($i, 1);
if ($tokens[$i]->isWhitespace(" \t")) {
$tokens[$i] = new Token([T_WHITESPACE, ' ']);
} elseif (!$tokens[$i]->isWhitespace()) {
$tokens->insertAt($i, new Token([T_WHITESPACE, ' ']));
}
}
}
}
private function getBeforeOperator(Tokens $tokens, int $index): array
{
$controlStructureWithoutBracesTypes = [T_IF, T_ELSE, T_ELSEIF, T_FOR, T_FOREACH, T_WHILE];
$index = $tokens->getPrevMeaningfulToken($index);
$range = [
'start' => $index,
'end' => $index,
];
$previousIndex = $index;
$previousToken = $tokens[$previousIndex];
while ($previousToken->equalsAny([
'$',
']',
')',
[CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE],
[CT::T_DYNAMIC_PROP_BRACE_CLOSE],
[CT::T_DYNAMIC_VAR_BRACE_CLOSE],
[T_NS_SEPARATOR],
[T_STRING],
[T_VARIABLE],
])) {
$blockType = Tokens::detectBlockType($previousToken);
if (null !== $blockType) {
$blockStart = $tokens->findBlockStart($blockType['type'], $previousIndex);
if ($tokens[$previousIndex]->equals(')') && $tokens[$tokens->getPrevMeaningfulToken($blockStart)]->isGivenKind($controlStructureWithoutBracesTypes)) {
break;
}
$previousIndex = $blockStart;
}
$index = $previousIndex;
$previousIndex = $tokens->getPrevMeaningfulToken($previousIndex);
$previousToken = $tokens[$previousIndex];
}
if ($previousToken->isGivenKind(T_OBJECT_OPERATOR)) {
$index = $this->getBeforeOperator($tokens, $previousIndex)['start'];
} elseif ($previousToken->isGivenKind(T_PAAMAYIM_NEKUDOTAYIM)) {
$index = $this->getBeforeOperator($tokens, $tokens->getPrevMeaningfulToken($previousIndex))['start'];
}
$range['start'] = $index;
return $range;
}
private function clearMeaningfulFromRange(Tokens $tokens, array $range): void
{
for ($i = $range['end']; $i >= $range['start']; $i = $tokens->getPrevMeaningfulToken($i)) {
$tokens->clearTokenAndMergeSurroundingWhitespace($i);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Operator;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class ObjectOperatorWithoutWhitespaceFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'There should not be space before or after object operators `->` and `?->`.',
[new CodeSample("<?php \$a -> b;\n")]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound(Token::getObjectOperatorKinds());
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isObjectOperator()) {
continue;
}
if ($tokens[$index - 1]->isWhitespace(" \t") && !$tokens[$index - 2]->isComment()) {
$tokens->clearAt($index - 1);
}
if ($tokens[$index + 1]->isWhitespace(" \t") && !$tokens[$index + 2]->isComment()) {
$tokens->clearAt($index + 1);
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Operator;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\FixerDefinition\VersionSpecification;
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NoUselessNullsafeOperatorFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'There should not be useless `null-safe-operators` `?->` used.',
[
new VersionSpecificCodeSample(
'<?php
class Foo extends Bar
{
public function test() {
echo $this?->parentMethod();
}
}
',
new VersionSpecification(80000)
),
]
);
}
public function isCandidate(Tokens $tokens): bool
{
return \PHP_VERSION_ID >= 80000 && $tokens->isAllTokenKindsFound([T_VARIABLE, T_NULLSAFE_OBJECT_OPERATOR]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
if (!$tokens[$index]->isGivenKind(T_NULLSAFE_OBJECT_OPERATOR)) {
continue;
}
$nullsafeObjectOperatorIndex = $index;
$index = $tokens->getPrevMeaningfulToken($index);
if (!$tokens[$index]->isGivenKind(T_VARIABLE)) {
continue;
}
if ('$this' !== strtolower($tokens[$index]->getContent())) {
continue;
}
$tokens[$nullsafeObjectOperatorIndex] = new Token([T_OBJECT_OPERATOR, '->']);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Operator;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NoUselessConcatOperatorFixer extends AbstractFixer implements ConfigurableFixerInterface
{
private const STR_DOUBLE_QUOTE = 0;
private const STR_DOUBLE_QUOTE_VAR = 1;
private const STR_SINGLE_QUOTE = 2;
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'There should not be useless concat operations.',
[
new CodeSample("<?php\n\$a = 'a'.'b';\n"),
new CodeSample("<?php\n\$a = 'a'.\"b\";\n", ['juggle_simple_strings' => true]),
],
);
}
public function getPriority(): int
{
return 5;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound('.') && $tokens->isAnyTokenKindsFound([T_CONSTANT_ENCAPSED_STRING, '"']);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = $tokens->count() - 1; $index > 0; --$index) {
if (!$tokens[$index]->equals('.')) {
continue;
}
$nextMeaningfulTokenIndex = $tokens->getNextMeaningfulToken($index);
if ($this->containsLinebreak($tokens, $index, $nextMeaningfulTokenIndex)) {
continue;
}
$secondOperand = $this->getConcatOperandType($tokens, $nextMeaningfulTokenIndex, 1);
if (null === $secondOperand) {
continue;
}
$prevMeaningfulTokenIndex = $tokens->getPrevMeaningfulToken($index);
if ($this->containsLinebreak($tokens, $prevMeaningfulTokenIndex, $index)) {
continue;
}
$firstOperand = $this->getConcatOperandType($tokens, $prevMeaningfulTokenIndex, -1);
if (null === $firstOperand) {
continue;
}
$this->fixConcatOperation($tokens, $firstOperand, $index, $secondOperand);
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('juggle_simple_strings', 'Allow for simple string quote juggling if it results in more concat-operations merges.'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
]);
}
private function fixConcatOperation(Tokens $tokens, array $firstOperand, int $concatIndex, array $secondOperand): void
{
if (
(self::STR_DOUBLE_QUOTE === $firstOperand['type'] && self::STR_DOUBLE_QUOTE === $secondOperand['type'])
|| (self::STR_SINGLE_QUOTE === $firstOperand['type'] && self::STR_SINGLE_QUOTE === $secondOperand['type'])
) {
$this->mergeContantEscapedStringOperands($tokens, $firstOperand, $concatIndex, $secondOperand);
return;
}
if (self::STR_DOUBLE_QUOTE_VAR === $firstOperand['type'] && self::STR_DOUBLE_QUOTE_VAR === $secondOperand['type']) {
$this->mergeContantEscapedStringVarOperands($tokens, $firstOperand, $concatIndex, $secondOperand);
return;
}
$operands = [
[$firstOperand, $secondOperand],
[$secondOperand, $firstOperand],
];
foreach ($operands as $operandPair) {
[$operand1, $operand2] = $operandPair;
if (self::STR_DOUBLE_QUOTE_VAR === $operand1['type'] && self::STR_DOUBLE_QUOTE === $operand2['type']) {
$this->mergeContantEscapedStringVarOperands($tokens, $firstOperand, $concatIndex, $secondOperand);
return;
}
if (!$this->configuration['juggle_simple_strings']) {
continue;
}
if (self::STR_DOUBLE_QUOTE === $operand1['type'] && self::STR_SINGLE_QUOTE === $operand2['type']) {
$operantContent = $tokens[$operand2['start']]->getContent();
if ($this->isSimpleQuotedStringContent($operantContent)) {
$this->mergeContantEscapedStringOperands($tokens, $firstOperand, $concatIndex, $secondOperand);
}
return;
}
if (self::STR_DOUBLE_QUOTE_VAR === $operand1['type'] && self::STR_SINGLE_QUOTE === $operand2['type']) {
$operantContent = $tokens[$operand2['start']]->getContent();
if ($this->isSimpleQuotedStringContent($operantContent)) {
$this->mergeContantEscapedStringVarOperands($tokens, $firstOperand, $concatIndex, $secondOperand);
}
return;
}
}
}
private function getConcatOperandType(Tokens $tokens, int $index, int $direction): ?array
{
if ($tokens[$index]->isGivenKind(T_CONSTANT_ENCAPSED_STRING)) {
$firstChar = $tokens[$index]->getContent();
if ('b' === $firstChar[0] || 'B' === $firstChar[0]) {
return null;
}
return [
'start' => $index,
'end' => $index,
'type' => '"' === $firstChar[0] ? self::STR_DOUBLE_QUOTE : self::STR_SINGLE_QUOTE,
];
}
if ($tokens[$index]->equals('"')) {
$end = $tokens->getTokenOfKindSibling($index, $direction, ['"']);
return [
'start' => 1 === $direction ? $index : $end,
'end' => 1 === $direction ? $end : $index,
'type' => self::STR_DOUBLE_QUOTE_VAR,
];
}
return null;
}
private function mergeContantEscapedStringOperands(
Tokens $tokens,
array $firstOperand,
int $concatOperatorIndex,
array $secondOperand
): void {
$quote = self::STR_DOUBLE_QUOTE === $firstOperand['type'] || self::STR_DOUBLE_QUOTE === $secondOperand['type'] ? '"' : "'";
$firstOperandTokenContent = $tokens[$firstOperand['start']]->getContent();
$secondOperandTokenContent = $tokens[$secondOperand['start']]->getContent();
$tokens[$firstOperand['start']] = new Token(
[
T_CONSTANT_ENCAPSED_STRING,
$quote.substr($firstOperandTokenContent, 1, -1).substr($secondOperandTokenContent, 1, -1).$quote,
],
);
$tokens->clearTokenAndMergeSurroundingWhitespace($secondOperand['start']);
$this->clearConcatAndAround($tokens, $concatOperatorIndex);
}
private function mergeContantEscapedStringVarOperands(
Tokens $tokens,
array $firstOperand,
int $concatOperatorIndex,
array $secondOperand
): void {
$newContent = '';
foreach ([$firstOperand, $secondOperand] as $operant) {
$operandContent = '';
for ($i = $operant['start']; $i <= $operant['end'];) {
$operandContent .= $tokens[$i]->getContent();
$i = $tokens->getNextMeaningfulToken($i);
}
$newContent .= substr($operandContent, 1, -1);
}
for ($i = $secondOperand['end']; $i >= $secondOperand['start'];) {
$tokens->clearTokenAndMergeSurroundingWhitespace($i);
$i = $tokens->getPrevMeaningfulToken($i);
}
$this->clearConcatAndAround($tokens, $concatOperatorIndex);
for ($i = $firstOperand['end']; $i > $firstOperand['start'];) {
$tokens->clearTokenAndMergeSurroundingWhitespace($i);
$i = $tokens->getPrevMeaningfulToken($i);
}
$newTokens = Tokens::fromCode('<?php "'.$newContent.'";');
$newTokensCount = \count($newTokens);
$insertTokens = [];
for ($i = 1; $i < $newTokensCount - 1; ++$i) {
$insertTokens[] = $newTokens[$i];
}
$tokens->overrideRange($firstOperand['start'], $firstOperand['start'], $insertTokens);
}
private function clearConcatAndAround(Tokens $tokens, int $concatOperatorIndex): void
{
if ($tokens[$concatOperatorIndex + 1]->isWhitespace()) {
$tokens->clearTokenAndMergeSurroundingWhitespace($concatOperatorIndex + 1);
}
$tokens->clearTokenAndMergeSurroundingWhitespace($concatOperatorIndex);
if ($tokens[$concatOperatorIndex - 1]->isWhitespace()) {
$tokens->clearTokenAndMergeSurroundingWhitespace($concatOperatorIndex - 1);
}
}
private function isSimpleQuotedStringContent(string $candidate): bool
{
return 0 === Preg::match('#[\$"\'\\\]#', substr($candidate, 1, -1));
}
private function containsLinebreak(Tokens $tokens, int $startIndex, int $endIndex): bool
{
for ($i = $endIndex; $i > $startIndex; --$i) {
if (Preg::match('/\R/', $tokens[$i]->getContent())) {
return true;
}
}
return false;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Operator;
use PhpCsFixer\Fixer\AbstractIncrementOperatorFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class StandardizeIncrementFixer extends AbstractIncrementOperatorFixer
{
private const EXPRESSION_END_TOKENS = [
';',
')',
']',
',',
':',
[CT::T_DYNAMIC_PROP_BRACE_CLOSE],
[CT::T_DYNAMIC_VAR_BRACE_CLOSE],
[T_CLOSE_TAG],
];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Increment and decrement operators should be used if possible.',
[
new CodeSample("<?php\n\$i += 1;\n"),
new CodeSample("<?php\n\$i -= 1;\n"),
]
);
}
public function getPriority(): int
{
return 16;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_PLUS_EQUAL, T_MINUS_EQUAL]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = $tokens->count() - 1; $index > 0; --$index) {
$expressionEnd = $tokens[$index];
if (!$expressionEnd->equalsAny(self::EXPRESSION_END_TOKENS)) {
continue;
}
$numberIndex = $tokens->getPrevMeaningfulToken($index);
$number = $tokens[$numberIndex];
if (!$number->isGivenKind(T_LNUMBER) || '1' !== $number->getContent()) {
continue;
}
$operatorIndex = $tokens->getPrevMeaningfulToken($numberIndex);
$operator = $tokens[$operatorIndex];
if (!$operator->isGivenKind([T_PLUS_EQUAL, T_MINUS_EQUAL])) {
continue;
}
$startIndex = $this->findStart($tokens, $operatorIndex);
$this->clearRangeLeaveComments(
$tokens,
$tokens->getPrevMeaningfulToken($operatorIndex) + 1,
$numberIndex
);
$tokens->insertAt(
$startIndex,
new Token($operator->isGivenKind(T_PLUS_EQUAL) ? [T_INC, '++'] : [T_DEC, '--'])
);
}
}
private function clearRangeLeaveComments(Tokens $tokens, int $indexStart, int $indexEnd): void
{
for ($i = $indexStart; $i <= $indexEnd; ++$i) {
$token = $tokens[$i];
if ($token->isComment()) {
continue;
}
if ($token->isWhitespace("\n\r")) {
continue;
}
$tokens->clearAt($i);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Operator;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class StandardizeNotEqualsFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Replace all `<>` with `!=`.',
[new CodeSample("<?php\n\$a = \$b <> \$c;\n")]
);
}
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_IS_NOT_EQUAL);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if ($token->isGivenKind(T_IS_NOT_EQUAL)) {
$tokens[$index] = new Token([T_IS_NOT_EQUAL, '!=']);
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Operator;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NotOperatorWithSpaceFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Logical NOT operators (`!`) should have leading and trailing whitespaces.',
[new CodeSample(
'<?php
if (!$bar) {
echo "Help!";
}
'
)]
);
}
public function getPriority(): int
{
return -10;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound('!');
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
$token = $tokens[$index];
if ($token->equals('!')) {
if (!$tokens[$index + 1]->isWhitespace()) {
$tokens->insertAt($index + 1, new Token([T_WHITESPACE, ' ']));
}
if (!$tokens[$index - 1]->isWhitespace()) {
$tokens->insertAt($index, new Token([T_WHITESPACE, ' ']));
}
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\Operator;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Tokens;
final class NoSpaceAroundDoubleColonFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'There must be no space around double colons (also called Scope Resolution Operator or Paamayim Nekudotayim).',
[new CodeSample("\n<?php echo Foo\\Bar :: class;\n")]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOUBLE_COLON);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = \count($tokens) - 2; $index > 1; --$index) {
if ($tokens[$index]->isGivenKind(T_DOUBLE_COLON)) {
$this->removeSpace($tokens, $index, 1);
$this->removeSpace($tokens, $index, -1);
}
}
}
private function removeSpace(Tokens $tokens, int $index, int $direction): void
{
if (!$tokens[$index + $direction]->isWhitespace()) {
return;
}
if ($tokens[$tokens->getNonWhitespaceSibling($index, $direction)]->isComment()) {
return;
}
$tokens->clearAt($index + $direction);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Fixer\ConstantNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\NamespacesAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\NamespaceUsesAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
final class NativeConstantInvocationFixer extends AbstractFixer implements ConfigurableFixerInterface
{
private array $constantsToEscape = [];
private array $caseInsensitiveConstantsToEscape = [];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Add leading `\` before constant invocation of internal constant to speed up resolving. Constant name match is case-sensitive, except for `null`, `false` and `true`.',
[
new CodeSample("<?php var_dump(PHP_VERSION, M_PI, MY_CUSTOM_PI);\n"),
new CodeSample(
'<?php
namespace space1 {
echo PHP_VERSION;
}
namespace {
echo M_PI;
}
',
['scope' => 'namespaced']
),
new CodeSample(
"<?php var_dump(PHP_VERSION, M_PI, MY_CUSTOM_PI);\n",
[
'include' => [
'MY_CUSTOM_PI',
],
]
),
new CodeSample(
"<?php var_dump(PHP_VERSION, M_PI, MY_CUSTOM_PI);\n",
[
'fix_built_in' => false,
'include' => [
'MY_CUSTOM_PI',
],
]
),
new CodeSample(
"<?php var_dump(PHP_VERSION, M_PI, MY_CUSTOM_PI);\n",
[
'exclude' => [
'M_PI',
],
]
),
],
null,
'Risky when any of the constants are namespaced or overridden.'
);
}
public function getPriority(): int
{
return 10;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_STRING);
}
public function isRisky(): bool
{
return true;
}
public function configure(array $configuration): void
{
parent::configure($configuration);
$uniqueConfiguredExclude = array_unique($this->configuration['exclude']);
$constantsToEscape = array_values($this->configuration['include']);
if (true === $this->configuration['fix_built_in']) {
$getDefinedConstants = get_defined_constants(true);
unset($getDefinedConstants['user']);
foreach ($getDefinedConstants as $constants) {
$constantsToEscape = array_merge($constantsToEscape, array_keys($constants));
}
}
$constantsToEscape = array_diff(
array_unique($constantsToEscape),
$uniqueConfiguredExclude
);
static $caseInsensitiveConstants = ['null', 'false', 'true'];
$caseInsensitiveConstantsToEscape = [];
foreach ($constantsToEscape as $constantIndex => $constant) {
$loweredConstant = strtolower($constant);
if (\in_array($loweredConstant, $caseInsensitiveConstants, true)) {
$caseInsensitiveConstantsToEscape[] = $loweredConstant;
unset($constantsToEscape[$constantIndex]);
}
}
$caseInsensitiveConstantsToEscape = array_diff(
array_unique($caseInsensitiveConstantsToEscape),
array_map(
static fn (string $function): string => strtolower($function),
$uniqueConfiguredExclude,
),
);
$this->constantsToEscape = array_fill_keys($constantsToEscape, true);
ksort($this->constantsToEscape);
$this->caseInsensitiveConstantsToEscape = array_fill_keys($caseInsensitiveConstantsToEscape, true);
ksort($this->caseInsensitiveConstantsToEscape);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
if ('all' === $this->configuration['scope']) {
$this->fixConstantInvocations($tokens, 0, \count($tokens) - 1);
return;
}
$namespaces = (new NamespacesAnalyzer())->getDeclarations($tokens);
foreach (array_reverse($namespaces) as $namespace) {
if ($namespace->isGlobalNamespace()) {
continue;
}
$this->fixConstantInvocations($tokens, $namespace->getScopeStartIndex(), $namespace->getScopeEndIndex());
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
$constantChecker = static function (array $value): bool {
foreach ($value as $constantName) {
if (!\is_string($constantName) || '' === trim($constantName) || trim($constantName) !== $constantName) {
throw new InvalidOptionsException(sprintf(
'Each element must be a non-empty, trimmed string, got "%s" instead.',
get_debug_type($constantName)
));
}
}
return true;
};
return new FixerConfigurationResolver([
(new FixerOptionBuilder('fix_built_in', 'Whether to fix constants returned by `get_defined_constants`. User constants are not accounted in this list and must be specified in the include one.'))
->setAllowedTypes(['bool'])
->setDefault(true)
->getOption(),
(new FixerOptionBuilder('include', 'List of additional constants to fix.'))
->setAllowedTypes(['array'])
->setAllowedValues([$constantChecker])
->setDefault([])
->getOption(),
(new FixerOptionBuilder('exclude', 'List of constants to ignore.'))
->setAllowedTypes(['array'])
->setAllowedValues([$constantChecker])
->setDefault(['null', 'false', 'true'])
->getOption(),
(new FixerOptionBuilder('scope', 'Only fix constant invocations that are made within a namespace or fix all.'))
->setAllowedValues(['all', 'namespaced'])
->setDefault('all')
->getOption(),
(new FixerOptionBuilder('strict', 'Whether leading `\` of constant invocation not meant to have it should be removed.'))
->setAllowedTypes(['bool'])
->setDefault(true)
->getOption(),
]);
}
private function fixConstantInvocations(Tokens $tokens, int $startIndex, int $endIndex): void
{
$useDeclarations = (new NamespaceUsesAnalyzer())->getDeclarationsFromTokens($tokens);
$useConstantDeclarations = [];
foreach ($useDeclarations as $use) {
if ($use->isConstant()) {
$useConstantDeclarations[$use->getShortName()] = true;
}
}
$tokenAnalyzer = new TokensAnalyzer($tokens);
for ($index = $endIndex; $index > $startIndex; --$index) {
$token = $tokens[$index];
if (!$token->isGivenKind(T_STRING)) {
continue;
}
if (!$tokenAnalyzer->isConstantInvocation($index)) {
continue;
}
$tokenContent = $token->getContent();
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if (!isset($this->constantsToEscape[$tokenContent]) && !isset($this->caseInsensitiveConstantsToEscape[strtolower($tokenContent)])) {
if (false === $this->configuration['strict']) {
continue;
}
if (!$tokens[$prevIndex]->isGivenKind(T_NS_SEPARATOR)) {
continue;
}
$prevPrevIndex = $tokens->getPrevMeaningfulToken($prevIndex);
if ($tokens[$prevPrevIndex]->isGivenKind(T_STRING)) {
continue;
}
$tokens->clearTokenAndMergeSurroundingWhitespace($prevIndex);
continue;
}
if (isset($useConstantDeclarations[$tokenContent])) {
continue;
}
if ($tokens[$prevIndex]->isGivenKind(T_NS_SEPARATOR)) {
continue;
}
$tokens->insertAt($index, new Token([T_NS_SEPARATOR, '\\']));
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\ConfigurationException;
final class RequiredFixerConfigurationException extends InvalidFixerConfigurationException
{
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\ConfigurationException;
final class InvalidForEnvFixerConfigurationException extends InvalidFixerConfigurationException
{
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\ConfigurationException;
use PhpCsFixer\Console\Command\FixCommandExitStatusCalculator;
class InvalidConfigurationException extends \InvalidArgumentException
{
public function __construct(string $message, ?int $code = null, ?\Throwable $previous = null)
{
parent::__construct(
$message,
$code ?? FixCommandExitStatusCalculator::EXIT_STATUS_FLAG_HAS_INVALID_CONFIG,
$previous
);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\ConfigurationException;
use PhpCsFixer\Console\Command\FixCommandExitStatusCalculator;
class InvalidFixerConfigurationException extends InvalidConfigurationException
{
private string $fixerName;
public function __construct(string $fixerName, string $message, ?\Throwable $previous = null)
{
parent::__construct(
sprintf('[%s] %s', $fixerName, $message),
FixCommandExitStatusCalculator::EXIT_STATUS_FLAG_HAS_INVALID_FIXER_CONFIG,
$previous
);
$this->fixerName = $fixerName;
}
public function getFixerName(): string
{
return $this->fixerName;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Documentation;
use PhpCsFixer\Fixer\FixerInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Utils;
final class DocumentationLocator
{
private string $path;
public function __construct()
{
$this->path = \dirname(__DIR__, 2).'/doc';
}
public function getFixersDocumentationDirectoryPath(): string
{
return $this->path.'/rules';
}
public function getFixersDocumentationIndexFilePath(): string
{
return $this->getFixersDocumentationDirectoryPath().'/index.rst';
}
public function getFixerDocumentationFilePath(FixerInterface $fixer): string
{
return $this->getFixersDocumentationDirectoryPath().'/'.Preg::replaceCallback(
'/^.*\\\\(.+)\\\\(.+)Fixer$/',
static function (array $matches): string {
return Utils::camelCaseToUnderscore($matches[1]).'/'.Utils::camelCaseToUnderscore($matches[2]);
},
\get_class($fixer)
).'.rst';
}
public function getFixerDocumentationFileRelativePath(FixerInterface $fixer): string
{
return Preg::replace(
'#^'.preg_quote($this->getFixersDocumentationDirectoryPath(), '#').'/#',
'',
$this->getFixerDocumentationFilePath($fixer)
);
}
public function getRuleSetsDocumentationDirectoryPath(): string
{
return $this->path.'/ruleSets';
}
public function getRuleSetsDocumentationIndexFilePath(): string
{
return $this->getRuleSetsDocumentationDirectoryPath().'/index.rst';
}
public function getRuleSetsDocumentationFilePath(string $name): string
{
return $this->getRuleSetsDocumentationDirectoryPath().'/'.str_replace(':risky', 'Risky', ucfirst(substr($name, 1))).'.rst';
}
public function getListingFilePath(): string
{
return $this->path.'/list.rst';
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Documentation;
use PhpCsFixer\Console\Command\HelpCommand;
use PhpCsFixer\Fixer\FixerInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\RuleSet\RuleSetDescriptionInterface;
final class RuleSetDocumentationGenerator
{
private DocumentationLocator $locator;
public function __construct(DocumentationLocator $locator)
{
$this->locator = $locator;
}
public function generateRuleSetsDocumentation(RuleSetDescriptionInterface $definition, array $fixers): string
{
$fixerNames = [];
foreach ($fixers as $fixer) {
$fixerNames[$fixer->getName()] = $fixer;
}
$title = "Rule set ``{$definition->getName()}``";
$titleLine = str_repeat('=', \strlen($title));
$doc = "{$titleLine}\n{$title}\n{$titleLine}\n\n".$definition->getDescription();
if ($definition->isRisky()) {
$doc .= ' This set contains rules that are risky.';
}
$doc .= "\n\n";
$rules = $definition->getRules();
if (\count($rules) < 1) {
$doc .= 'This is an empty set.';
} else {
$doc .= "Rules\n-----\n";
foreach ($rules as $rule => $config) {
if (str_starts_with($rule, '@')) {
$ruleSetPath = $this->locator->getRuleSetsDocumentationFilePath($rule);
$ruleSetPath = substr($ruleSetPath, strrpos($ruleSetPath, '/'));
$doc .= "\n- `{$rule} <.{$ruleSetPath}>`_";
} else {
$path = Preg::replace(
'#^'.preg_quote($this->locator->getFixersDocumentationDirectoryPath(), '#').'/#',
'./../rules/',
$this->locator->getFixerDocumentationFilePath($fixerNames[$rule])
);
$doc .= "\n- `{$rule} <{$path}>`_";
}
if (!\is_bool($config)) {
$doc .= "\n config:\n ``".HelpCommand::toString($config).'``';
}
}
}
return $doc."\n";
}
public function generateRuleSetsDocumentationIndex(array $setDefinitions): string
{
$documentation = <<<'RST'
===========================
List of Available Rule sets
===========================
RST;
foreach ($setDefinitions as $name => $path) {
$path = substr($path, strrpos($path, '/'));
$documentation .= "\n- `{$name} <.{$path}>`_";
}
return $documentation."\n";
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Documentation;
use PhpCsFixer\Console\Command\HelpCommand;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\DeprecatedFixerInterface;
use PhpCsFixer\Fixer\FixerInterface;
use PhpCsFixer\FixerConfiguration\AliasedFixerOption;
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
use PhpCsFixer\FixerConfiguration\DeprecatedFixerOptionInterface;
use PhpCsFixer\RuleSet\RuleSet;
use PhpCsFixer\RuleSet\RuleSets;
use PhpCsFixer\Utils;
final class ListDocumentGenerator
{
private DocumentationLocator $locator;
public function __construct(DocumentationLocator $locator)
{
$this->locator = $locator;
}
public function generateListingDocumentation(array $fixers): string
{
usort(
$fixers,
static function (FixerInterface $fixer1, FixerInterface $fixer2): int {
return strnatcasecmp($fixer1->getName(), $fixer2->getName());
}
);
$documentation = <<<'RST'
=======================
List of Available Rules
=======================
RST;
foreach ($fixers as $fixer) {
$name = $fixer->getName();
$definition = $fixer->getDefinition();
$path = './rules/'.$this->locator->getFixerDocumentationFileRelativePath($fixer);
$documentation .= "\n- `{$name} <{$path}>`_\n";
$documentation .= "\n ".str_replace('`', '``', $definition->getSummary())."\n";
$description = $definition->getDescription();
if (null !== $description) {
$documentation .= "\n ".RstUtils::toRst($description, 3)."\n";
}
if ($fixer instanceof DeprecatedFixerInterface) {
$documentation .= "\n *warning deprecated*";
$alternatives = $fixer->getSuccessorsNames();
if (0 !== \count($alternatives)) {
$documentation .= RstUtils::toRst(sprintf(
' Use %s instead.',
Utils::naturalLanguageJoinWithBackticks($alternatives)
), 3);
}
$documentation .= "\n";
}
if ($fixer->isRisky()) {
$documentation .= "\n *warning risky* ".RstUtils::toRst($definition->getRiskyDescription(), 3)."\n";
}
if ($fixer instanceof ConfigurableFixerInterface) {
$documentation .= "\n Configuration options:\n";
$configurationDefinition = $fixer->getConfigurationDefinition();
foreach ($configurationDefinition->getOptions() as $option) {
$documentation .= "\n - | ``{$option->getName()}``";
$documentation .= "\n | {$option->getDescription()}";
if ($option instanceof DeprecatedFixerOptionInterface) {
$deprecationMessage = RstUtils::toRst($option->getDeprecationMessage(), 3);
$documentation .= "\n | warning:: This option is deprecated and will be removed on next major version. {$deprecationMessage}";
}
if ($option instanceof AliasedFixerOption) {
$documentation .= "\n | note:: The previous name of this option was ``{$option->getAlias()}`` but it is now deprecated and will be removed on next major version.";
}
$allowed = HelpCommand::getDisplayableAllowedValues($option);
if (null === $allowed) {
$allowedKind = 'Allowed types';
$allowed = array_map(
static fn ($value): string => '``'.$value.'``',
$option->getAllowedTypes(),
);
} else {
$allowedKind = 'Allowed values';
$allowed = array_map(static function ($value): string {
return $value instanceof AllowedValueSubset
? 'a subset of ``'.HelpCommand::toString($value->getAllowedValues()).'``'
: '``'.HelpCommand::toString($value).'``';
}, $allowed);
}
$allowed = implode(', ', $allowed);
$documentation .= "\n | {$allowedKind}: {$allowed}";
if ($option->hasDefault()) {
$default = HelpCommand::toString($option->getDefault());
$documentation .= "\n | Default value: ``{$default}``";
} else {
$documentation .= "\n | This option is required.";
}
}
$documentation .= "\n\n";
}
$ruleSetConfigs = [];
foreach (RuleSets::getSetDefinitionNames() as $set) {
$ruleSet = new RuleSet([$set => true]);
if ($ruleSet->hasRule($name)) {
$ruleSetConfigs[$set] = $ruleSet->getRuleConfiguration($name);
}
}
if ([] !== $ruleSetConfigs) {
$plural = 1 !== \count($ruleSetConfigs) ? 's' : '';
$documentation .= "\n Part of rule set{$plural} ";
foreach ($ruleSetConfigs as $set => $config) {
$ruleSetPath = $this->locator->getRuleSetsDocumentationFilePath($set);
$ruleSetPath = substr($ruleSetPath, strrpos($ruleSetPath, '/'));
$documentation .= "`{$set} <./ruleSets{$ruleSetPath}>`_ ";
}
$documentation = rtrim($documentation)."\n";
}
$reflectionObject = new \ReflectionObject($fixer);
$className = str_replace('\\', '\\\\', $reflectionObject->getName());
$fileName = $reflectionObject->getFileName();
$fileName = str_replace('\\', '/', $fileName);
$fileName = substr($fileName, strrpos($fileName, '/src/Fixer/') + 1);
$fileName = "`Source {$className} <./../{$fileName}>`_";
$documentation .= "\n ".$fileName;
}
return $documentation."\n";
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Documentation;
use PhpCsFixer\Preg;
final class RstUtils
{
private function __construct()
{
}
public static function toRst(string $string, int $indent = 0): string
{
$string = wordwrap(Preg::replace('/(?<!`)(`.*?`)(?!`)/', '`$1`', $string), 80 - $indent);
return 0 === $indent ? $string : self::indent($string, $indent);
}
public static function indent(string $string, int $indent): string
{
return Preg::replace('/(\n)(?!\n|$)/', '$1'.str_repeat(' ', $indent), $string);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Documentation;
use PhpCsFixer\Console\Command\HelpCommand;
use PhpCsFixer\Differ\FullDiffer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\DeprecatedFixerInterface;
use PhpCsFixer\Fixer\FixerInterface;
use PhpCsFixer\FixerConfiguration\AliasedFixerOption;
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
use PhpCsFixer\FixerConfiguration\DeprecatedFixerOptionInterface;
use PhpCsFixer\FixerDefinition\CodeSampleInterface;
use PhpCsFixer\FixerDefinition\FileSpecificCodeSampleInterface;
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSampleInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\RuleSet\RuleSet;
use PhpCsFixer\RuleSet\RuleSets;
use PhpCsFixer\StdinFileInfo;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Utils;
final class FixerDocumentGenerator
{
private DocumentationLocator $locator;
private FullDiffer $differ;
public function __construct(DocumentationLocator $locator)
{
$this->locator = $locator;
$this->differ = new FullDiffer();
}
public function generateFixerDocumentation(FixerInterface $fixer): string
{
$name = $fixer->getName();
$title = "Rule ``{$name}``";
$titleLine = str_repeat('=', \strlen($title));
$doc = "{$titleLine}\n{$title}\n{$titleLine}";
$definition = $fixer->getDefinition();
$doc .= "\n\n".RstUtils::toRst($definition->getSummary());
$description = $definition->getDescription();
if (null !== $description) {
$description = RstUtils::toRst($description);
$doc .= <<<RST
Description
-----------
{$description}
RST;
}
$deprecationDescription = '';
if ($fixer instanceof DeprecatedFixerInterface) {
$deprecationDescription = <<<'RST'
This rule is deprecated and will be removed on next major version
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RST;
$alternatives = $fixer->getSuccessorsNames();
if (0 !== \count($alternatives)) {
$deprecationDescription .= RstUtils::toRst(sprintf(
"\n\nYou should use %s instead.",
Utils::naturalLanguageJoinWithBackticks($alternatives)
), 0);
}
}
$riskyDescription = '';
$riskyDescriptionRaw = $definition->getRiskyDescription();
if (null !== $riskyDescriptionRaw) {
$riskyDescriptionRaw = RstUtils::toRst($riskyDescriptionRaw, 0);
$riskyDescription = <<<RST
Using this rule is risky
~~~~~~~~~~~~~~~~~~~~~~~~
{$riskyDescriptionRaw}
RST;
}
if ($deprecationDescription || $riskyDescription) {
$warningsHeader = 'Warning';
if ($deprecationDescription && $riskyDescription) {
$warningsHeader = 'Warnings';
}
$warningsHeaderLine = str_repeat('-', \strlen($warningsHeader));
$doc .= "\n\n".implode("\n", array_filter([
$warningsHeader,
$warningsHeaderLine,
$deprecationDescription,
$riskyDescription,
]));
}
if ($fixer instanceof ConfigurableFixerInterface) {
$doc .= <<<'RST'
Configuration
-------------
RST;
$configurationDefinition = $fixer->getConfigurationDefinition();
foreach ($configurationDefinition->getOptions() as $option) {
$optionInfo = "``{$option->getName()}``";
$optionInfo .= "\n".str_repeat('~', \strlen($optionInfo));
if ($option instanceof DeprecatedFixerOptionInterface) {
$deprecationMessage = RstUtils::toRst($option->getDeprecationMessage());
$optionInfo .= "\n\n.. warning:: This option is deprecated and will be removed on next major version. {$deprecationMessage}";
}
$optionInfo .= "\n\n".RstUtils::toRst($option->getDescription());
if ($option instanceof AliasedFixerOption) {
$optionInfo .= "\n\n.. note:: The previous name of this option was ``{$option->getAlias()}`` but it is now deprecated and will be removed on next major version.";
}
$allowed = HelpCommand::getDisplayableAllowedValues($option);
if (null === $allowed) {
$allowedKind = 'Allowed types';
$allowed = array_map(
static fn ($value): string => '``'.$value.'``',
$option->getAllowedTypes(),
);
} else {
$allowedKind = 'Allowed values';
$allowed = array_map(static function ($value): string {
return $value instanceof AllowedValueSubset
? 'a subset of ``'.HelpCommand::toString($value->getAllowedValues()).'``'
: '``'.HelpCommand::toString($value).'``';
}, $allowed);
}
$allowed = implode(', ', $allowed);
$optionInfo .= "\n\n{$allowedKind}: {$allowed}";
if ($option->hasDefault()) {
$default = HelpCommand::toString($option->getDefault());
$optionInfo .= "\n\nDefault value: ``{$default}``";
} else {
$optionInfo .= "\n\nThis option is required.";
}
$doc .= "\n\n{$optionInfo}";
}
}
$samples = $definition->getCodeSamples();
if (0 !== \count($samples)) {
$doc .= <<<'RST'
Examples
--------
RST;
foreach ($samples as $index => $sample) {
$title = sprintf('Example #%d', $index + 1);
$titleLine = str_repeat('~', \strlen($title));
$doc .= "\n\n{$title}\n{$titleLine}";
if ($fixer instanceof ConfigurableFixerInterface) {
if (null === $sample->getConfiguration()) {
$doc .= "\n\n*Default* configuration.";
} else {
$doc .= sprintf(
"\n\nWith configuration: ``%s``.",
HelpCommand::toString($sample->getConfiguration())
);
}
}
$doc .= "\n".$this->generateSampleDiff($fixer, $sample, $index + 1, $name);
}
}
$ruleSetConfigs = [];
foreach (RuleSets::getSetDefinitionNames() as $set) {
$ruleSet = new RuleSet([$set => true]);
if ($ruleSet->hasRule($name)) {
$ruleSetConfigs[$set] = $ruleSet->getRuleConfiguration($name);
}
}
if ([] !== $ruleSetConfigs) {
$plural = 1 !== \count($ruleSetConfigs) ? 's' : '';
$doc .= <<<RST
Rule sets
---------
The rule is part of the following rule set{$plural}:
RST;
foreach ($ruleSetConfigs as $set => $config) {
$ruleSetPath = $this->locator->getRuleSetsDocumentationFilePath($set);
$ruleSetPath = substr($ruleSetPath, strrpos($ruleSetPath, '/'));
$doc .= <<<RST
{$set}
Using the `{$set} <./../../ruleSets{$ruleSetPath}>`_ rule set will enable the ``{$name}`` rule
RST;
if (null !== $config) {
$doc .= " with the config below:\n\n ``".HelpCommand::toString($config).'``';
} elseif ($fixer instanceof ConfigurableFixerInterface) {
$doc .= ' with the default config.';
} else {
$doc .= '.';
}
}
}
return "{$doc}\n";
}
public function generateFixersDocumentationIndex(array $fixers): string
{
$overrideGroups = [
'PhpUnit' => 'PHPUnit',
'PhpTag' => 'PHP Tag',
'Phpdoc' => 'PHPDoc',
];
usort($fixers, static function (FixerInterface $a, FixerInterface $b): int {
return strcmp(\get_class($a), \get_class($b));
});
$documentation = <<<'RST'
=======================
List of Available Rules
=======================
RST;
$currentGroup = null;
foreach ($fixers as $fixer) {
$namespace = Preg::replace('/^.*\\\\(.+)\\\\.+Fixer$/', '$1', \get_class($fixer));
$group = $overrideGroups[$namespace] ?? Preg::replace('/(?<=[[:lower:]])(?=[[:upper:]])/', ' ', $namespace);
if ($group !== $currentGroup) {
$underline = str_repeat('-', \strlen($group));
$documentation .= "\n\n{$group}\n{$underline}\n";
$currentGroup = $group;
}
$path = './'.$this->locator->getFixerDocumentationFileRelativePath($fixer);
$attributes = [];
if ($fixer instanceof DeprecatedFixerInterface) {
$attributes[] = 'deprecated';
}
if ($fixer->isRisky()) {
$attributes[] = 'risky';
}
$attributes = 0 === \count($attributes)
? ''
: ' *('.implode(', ', $attributes).')*'
;
$summary = str_replace('`', '``', $fixer->getDefinition()->getSummary());
$documentation .= <<<RST
- `{$fixer->getName()} <{$path}>`_{$attributes}
{$summary}
RST;
}
return "{$documentation}\n";
}
private function generateSampleDiff(FixerInterface $fixer, CodeSampleInterface $sample, int $sampleNumber, string $ruleName): string
{
if ($sample instanceof VersionSpecificCodeSampleInterface && !$sample->isSuitableFor(\PHP_VERSION_ID)) {
$existingFile = @file_get_contents($this->locator->getFixerDocumentationFilePath($fixer));
if (false !== $existingFile) {
Preg::match("/\\RExample #{$sampleNumber}\\R.+?(?<diff>\\R\\.\\. code-block:: diff\\R\\R.*?)\\R(?:\\R\\S|$)/s", $existingFile, $matches);
if (isset($matches['diff'])) {
return $matches['diff'];
}
}
$error = <<<RST
.. error::
Cannot generate diff for code sample #{$sampleNumber} of rule {$ruleName}:
the sample is not suitable for current version of PHP (%s).
RST;
return sprintf($error, PHP_VERSION);
}
$old = $sample->getCode();
$tokens = Tokens::fromCode($old);
$file = $sample instanceof FileSpecificCodeSampleInterface
? $sample->getSplFileInfo()
: new StdinFileInfo()
;
if ($fixer instanceof ConfigurableFixerInterface) {
$fixer->configure($sample->getConfiguration() ?? []);
}
$fixer->fix($file, $tokens);
$diff = $this->differ->diff($old, $tokens->generateCode());
$diff = Preg::replace('/@@[ \+\-\d,]+@@\n/', '', $diff);
$diff = Preg::replace('/\r/', '^M', $diff);
$diff = Preg::replace('/^ $/m', '', $diff);
$diff = Preg::replace('/\n$/', '', $diff);
$diff = RstUtils::indent($diff, 3);
return <<<RST
.. code-block:: diff
{$diff}
RST;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet;
use Symfony\Component\Finder\Finder;
final class RuleSets
{
private static $setDefinitions;
public static function getSetDefinitions(): array
{
if (null === self::$setDefinitions) {
self::$setDefinitions = [];
foreach (Finder::create()->files()->in(__DIR__.'/Sets') as $file) {
$class = 'PhpCsFixer\RuleSet\Sets\\'.$file->getBasename('.php');
$set = new $class();
self::$setDefinitions[$set->getName()] = $set;
}
ksort(self::$setDefinitions);
}
return self::$setDefinitions;
}
public static function getSetDefinitionNames(): array
{
return array_keys(self::getSetDefinitions());
}
public static function getSetDefinition(string $name): RuleSetDescriptionInterface
{
$definitions = self::getSetDefinitions();
if (!isset($definitions[$name])) {
throw new \InvalidArgumentException(sprintf('Set "%s" does not exist.', $name));
}
return $definitions[$name];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet;
interface RuleSetInterface
{
public function __construct(array $set = []);
public function getRuleConfiguration(string $rule): ?array;
public function getRules(): array;
public function hasRule(string $rule): bool;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet;
interface RuleSetDescriptionInterface
{
public function getDescription(): string;
public function getName(): string;
public function getRules(): array;
public function isRisky(): bool;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet;
use PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException;
final class RuleSet implements RuleSetInterface
{
private array $rules;
public function __construct(array $set = [])
{
foreach ($set as $name => $value) {
if ('' === $name) {
throw new \InvalidArgumentException('Rule/set name must not be empty.');
}
if (\is_int($name)) {
throw new \InvalidArgumentException(sprintf('Missing value for "%s" rule/set.', $value));
}
if (!\is_bool($value) && !\is_array($value)) {
$message = str_starts_with($name, '@') ? 'Set must be enabled (true) or disabled (false). Other values are not allowed.' : 'Rule must be enabled (true), disabled (false) or configured (non-empty, assoc array). Other values are not allowed.';
if (null === $value) {
$message .= ' To disable the '.(str_starts_with($name, '@') ? 'set' : 'rule').', use "FALSE" instead of "NULL".';
}
throw new InvalidFixerConfigurationException($name, $message);
}
}
$this->resolveSet($set);
}
public function hasRule(string $rule): bool
{
return \array_key_exists($rule, $this->rules);
}
public function getRuleConfiguration(string $rule): ?array
{
if (!$this->hasRule($rule)) {
throw new \InvalidArgumentException(sprintf('Rule "%s" is not in the set.', $rule));
}
if (true === $this->rules[$rule]) {
return null;
}
return $this->rules[$rule];
}
public function getRules(): array
{
return $this->rules;
}
private function resolveSet(array $rules): void
{
$resolvedRules = [];
foreach ($rules as $name => $value) {
if (str_starts_with($name, '@')) {
if (!\is_bool($value)) {
throw new \UnexpectedValueException(sprintf('Nested rule set "%s" configuration must be a boolean.', $name));
}
$set = $this->resolveSubset($name, $value);
$resolvedRules = array_merge($resolvedRules, $set);
} else {
$resolvedRules[$name] = $value;
}
}
$resolvedRules = array_filter($resolvedRules);
$this->rules = $resolvedRules;
}
private function resolveSubset(string $setName, bool $setValue): array
{
$rules = RuleSets::getSetDefinition($setName)->getRules();
foreach ($rules as $name => $value) {
if (str_starts_with($name, '@')) {
$set = $this->resolveSubset($name, $setValue);
unset($rules[$name]);
$rules = array_merge($rules, $set);
} elseif (!$setValue) {
$rules[$name] = false;
} else {
$rules[$name] = $value;
}
}
return $rules;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\Fixer\PhpUnit\PhpUnitTargetVersion;
use PhpCsFixer\RuleSet\AbstractMigrationSetDescription;
final class PHPUnit32MigrationRiskySet extends AbstractMigrationSetDescription
{
public function getRules(): array
{
return [
'@PHPUnit30Migration:risky' => true,
'php_unit_no_expectation_annotation' => [
'target' => PhpUnitTargetVersion::VERSION_3_2,
],
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\RuleSet\AbstractMigrationSetDescription;
final class PHP80MigrationSet extends AbstractMigrationSetDescription
{
public function getRules(): array
{
return [
'@PHP74Migration' => true,
'clean_namespace' => true,
'no_unset_cast' => true,
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\RuleSet\AbstractRuleSetDescription;
final class SymfonyRiskySet extends AbstractRuleSetDescription
{
public function getRules(): array
{
return [
'@PHP56Migration:risky' => true,
'@PSR12:risky' => true,
'array_push' => true,
'combine_nested_dirname' => true,
'dir_constant' => true,
'ereg_to_preg' => true,
'error_suppression' => true,
'fopen_flag_order' => true,
'fopen_flags' => [
'b_mode' => false,
],
'function_to_constant' => true,
'implode_call' => true,
'is_null' => true,
'logical_operators' => true,
'modernize_types_casting' => true,
'native_constant_invocation' => true,
'native_function_invocation' => [
'include' => [
'@compiler_optimized',
],
'scope' => 'namespaced',
'strict' => true,
],
'no_alias_functions' => true,
'no_homoglyph_names' => true,
'no_php4_constructor' => true,
'no_unneeded_final_method' => true,
'no_unreachable_default_argument_value' => false,
'no_useless_sprintf' => true,
'non_printable_character' => true,
'ordered_traits' => true,
'php_unit_construct' => true,
'php_unit_mock_short_will_return' => true,
'php_unit_set_up_tear_down_visibility' => true,
'php_unit_test_annotation' => true,
'psr_autoloading' => true,
'self_accessor' => true,
'set_type_to_cast' => true,
'string_length_to_empty' => true,
'string_line_ending' => true,
'ternary_to_elvis_operator' => true,
];
}
public function getDescription(): string
{
return 'Rules that follow the official `Symfony Coding Standards <https://symfony.com/doc/current/contributing/code/standards.html>`_.';
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\RuleSet\AbstractRuleSetDescription;
final class PhpCsFixerSet extends AbstractRuleSetDescription
{
public function getRules(): array
{
return [
'@PER' => true,
'@Symfony' => true,
'align_multiline_comment' => true,
'array_indentation' => true,
'blank_line_before_statement' => [
'statements' => [
'break',
'case',
'continue',
'declare',
'default',
'exit',
'goto',
'include',
'include_once',
'phpdoc',
'require',
'require_once',
'return',
'switch',
'throw',
'try',
'yield',
'yield_from',
],
],
'combine_consecutive_issets' => true,
'combine_consecutive_unsets' => true,
'empty_loop_body' => true,
'escape_implicit_backslashes' => true,
'explicit_indirect_variable' => true,
'explicit_string_variable' => true,
'heredoc_to_nowdoc' => true,
'method_argument_space' => [
'on_multiline' => 'ensure_fully_multiline',
],
'method_chaining_indentation' => true,
'multiline_comment_opening_closing' => true,
'multiline_whitespace_before_semicolons' => [
'strategy' => 'new_line_for_chained_calls',
],
'no_extra_blank_lines' => [
'tokens' => [
'attribute',
'break',
'case',
'continue',
'curly_brace_block',
'default',
'extra',
'parenthesis_brace_block',
'return',
'square_brace_block',
'switch',
'throw',
'use',
],
],
'no_null_property_initialization' => true,
'no_superfluous_elseif' => true,
'no_unneeded_control_parentheses' => [
'statements' => [
'break',
'clone',
'continue',
'echo_print',
'negative_instanceof',
'others',
'return',
'switch_case',
'yield',
'yield_from',
],
],
'no_useless_else' => true,
'no_useless_return' => true,
'operator_linebreak' => [
'only_booleans' => true,
],
'ordered_class_elements' => true,
'php_unit_internal_class' => true,
'php_unit_test_class_requires_covers' => true,
'phpdoc_add_missing_param_annotation' => true,
'phpdoc_no_empty_return' => true,
'phpdoc_order_by_value' => true,
'phpdoc_types_order' => true,
'phpdoc_var_annotation_correct_order' => true,
'return_assignment' => true,
'single_line_comment_style' => true,
'single_line_throw' => false,
'whitespace_after_comma_in_array' => ['ensure_single_space' => true],
];
}
public function getDescription(): string
{
return 'Rule set as used by the PHP-CS-Fixer development team, highly opinionated.';
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\RuleSet\AbstractRuleSetDescription;
final class PSR2Set extends AbstractRuleSetDescription
{
public function getRules(): array
{
return [
'@PSR1' => true,
'blank_line_after_namespace' => true,
'braces' => true,
'class_definition' => true,
'constant_case' => true,
'elseif' => true,
'function_declaration' => true,
'indentation_type' => true,
'line_ending' => true,
'lowercase_keywords' => true,
'method_argument_space' => [
'on_multiline' => 'ensure_fully_multiline',
],
'no_break_comment' => true,
'no_closing_tag' => true,
'no_space_around_double_colon' => true,
'no_spaces_after_function_name' => true,
'no_spaces_inside_parenthesis' => true,
'no_trailing_whitespace' => true,
'no_trailing_whitespace_in_comment' => true,
'single_blank_line_at_eof' => true,
'single_class_element_per_statement' => [
'elements' => [
'property',
],
],
'single_import_per_statement' => true,
'single_line_after_imports' => true,
'switch_case_semicolon_to_colon' => true,
'switch_case_space' => true,
'visibility_required' => ['elements' => ['method', 'property']],
];
}
public function getDescription(): string
{
return 'Rules that follow `PSR-2 <https://www.php-fig.org/psr/psr-2/>`_ standard.';
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\RuleSet\AbstractMigrationSetDescription;
final class PHP71MigrationRiskySet extends AbstractMigrationSetDescription
{
public function getRules(): array
{
return [
'@PHP70Migration:risky' => true,
'void_return' => true,
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\Fixer\PhpUnit\PhpUnitTargetVersion;
use PhpCsFixer\RuleSet\AbstractMigrationSetDescription;
final class PHPUnit56MigrationRiskySet extends AbstractMigrationSetDescription
{
public function getRules(): array
{
return [
'@PHPUnit55Migration:risky' => true,
'php_unit_dedicate_assert' => [
'target' => PhpUnitTargetVersion::VERSION_5_6,
],
'php_unit_expectation' => [
'target' => PhpUnitTargetVersion::VERSION_5_6,
],
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\RuleSet\AbstractMigrationSetDescription;
final class PHP54MigrationSet extends AbstractMigrationSetDescription
{
public function getRules(): array
{
return [
'array_syntax' => true,
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\RuleSet\AbstractMigrationSetDescription;
final class PHP70MigrationSet extends AbstractMigrationSetDescription
{
public function getRules(): array
{
return [
'@PHP54Migration' => true,
'ternary_to_null_coalescing' => true,
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\RuleSet\AbstractMigrationSetDescription;
final class PHP74MigrationRiskySet extends AbstractMigrationSetDescription
{
public function getRules(): array
{
return [
'@PHP71Migration:risky' => true,
'implode_call' => true,
'no_alias_functions' => true,
'use_arrow_functions' => true,
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\RuleSet\AbstractRuleSetDescription;
final class SymfonySet extends AbstractRuleSetDescription
{
public function getRules(): array
{
return [
'@PSR12' => true,
'array_syntax' => true,
'backtick_to_shell_exec' => true,
'binary_operator_spaces' => true,
'blank_line_before_statement' => [
'statements' => [
'return',
],
],
'braces' => [
'allow_single_line_anonymous_class_with_empty_body' => true,
'allow_single_line_closure' => true,
],
'cast_spaces' => true,
'class_attributes_separation' => [
'elements' => [
'method' => 'one',
],
],
'class_definition' => [
'single_line' => true,
],
'class_reference_name_casing' => true,
'clean_namespace' => true,
'concat_space' => true,
'echo_tag_syntax' => true,
'empty_loop_body' => ['style' => 'braces'],
'empty_loop_condition' => true,
'fully_qualified_strict_types' => true,
'function_typehint_space' => true,
'general_phpdoc_tag_rename' => [
'replacements' => [
'inheritDocs' => 'inheritDoc',
],
],
'global_namespace_import' => [
'import_classes' => false,
'import_constants' => false,
'import_functions' => false,
],
'include' => true,
'increment_style' => true,
'integer_literal_case' => true,
'lambda_not_used_import' => true,
'linebreak_after_opening_tag' => true,
'magic_constant_casing' => true,
'magic_method_casing' => true,
'method_argument_space' => [
'on_multiline' => 'ignore',
],
'native_function_casing' => true,
'native_function_type_declaration_casing' => true,
'no_alias_language_construct_call' => true,
'no_alternative_syntax' => true,
'no_binary_string' => true,
'no_blank_lines_after_phpdoc' => true,
'no_empty_comment' => true,
'no_empty_phpdoc' => true,
'no_empty_statement' => true,
'no_extra_blank_lines' => [
'tokens' => [
'attribute',
'case',
'continue',
'curly_brace_block',
'default',
'extra',
'parenthesis_brace_block',
'square_brace_block',
'switch',
'throw',
'use',
],
],
'no_leading_namespace_whitespace' => true,
'no_mixed_echo_print' => true,
'no_multiline_whitespace_around_double_arrow' => true,
'no_short_bool_cast' => true,
'no_singleline_whitespace_before_semicolons' => true,
'no_spaces_around_offset' => true,
'no_superfluous_phpdoc_tags' => [
'allow_mixed' => true,
'allow_unused_params' => true,
],
'no_trailing_comma_in_singleline' => true,
'no_unneeded_control_parentheses' => [
'statements' => [
'break',
'clone',
'continue',
'echo_print',
'others',
'return',
'switch_case',
'yield',
'yield_from',
],
],
'no_unneeded_curly_braces' => [
'namespaces' => true,
],
'no_unneeded_import_alias' => true,
'no_unset_cast' => true,
'no_unused_imports' => true,
'no_useless_concat_operator' => true,
'no_useless_nullsafe_operator' => true,
'no_whitespace_before_comma_in_array' => true,
'normalize_index_brace' => true,
'object_operator_without_whitespace' => true,
'ordered_imports' => true,
'php_unit_fqcn_annotation' => true,
'php_unit_method_casing' => true,
'phpdoc_align' => true,
'phpdoc_annotation_without_dot' => true,
'phpdoc_indent' => true,
'phpdoc_inline_tag_normalizer' => true,
'phpdoc_no_access' => true,
'phpdoc_no_alias_tag' => true,
'phpdoc_no_package' => true,
'phpdoc_no_useless_inheritdoc' => true,
'phpdoc_order' => [
'order' => [
'param',
'return',
'throws',
],
],
'phpdoc_return_self_reference' => true,
'phpdoc_scalar' => true,
'phpdoc_separation' => true,
'phpdoc_single_line_var_spacing' => true,
'phpdoc_summary' => true,
'phpdoc_tag_type' => [
'tags' => [
'inheritDoc' => 'inline',
],
],
'phpdoc_to_comment' => true,
'phpdoc_trim' => true,
'phpdoc_trim_consecutive_blank_line_separation' => true,
'phpdoc_types' => true,
'phpdoc_types_order' => [
'null_adjustment' => 'always_last',
'sort_algorithm' => 'none',
],
'phpdoc_var_without_name' => true,
'protected_to_private' => true,
'semicolon_after_instruction' => true,
'simple_to_complex_string_variable' => true,
'single_class_element_per_statement' => true,
'single_import_per_statement' => true,
'single_line_comment_spacing' => true,
'single_line_comment_style' => [
'comment_types' => [
'hash',
],
],
'single_line_throw' => true,
'single_quote' => true,
'single_space_after_construct' => [
'constructs' => [
'abstract',
'as',
'attribute',
'break',
'case',
'catch',
'class',
'clone',
'comment',
'const',
'const_import',
'continue',
'do',
'echo',
'else',
'elseif',
'enum',
'extends',
'final',
'finally',
'for',
'foreach',
'function',
'function_import',
'global',
'goto',
'if',
'implements',
'include',
'include_once',
'instanceof',
'insteadof',
'interface',
'match',
'named_argument',
'namespace',
'new',
'open_tag_with_echo',
'php_doc',
'php_open',
'print',
'private',
'protected',
'public',
'readonly',
'require',
'require_once',
'return',
'static',
'switch',
'throw',
'trait',
'try',
'type_colon',
'use',
'use_lambda',
'use_trait',
'var',
'while',
'yield',
'yield_from',
],
],
'space_after_semicolon' => [
'remove_in_empty_for_expressions' => true,
],
'standardize_increment' => true,
'standardize_not_equals' => true,
'switch_continue_to_break' => true,
'trailing_comma_in_multiline' => true,
'trim_array_spaces' => true,
'types_spaces' => true,
'unary_operator_spaces' => true,
'whitespace_after_comma_in_array' => true,
'yoda_style' => true,
];
}
public function getDescription(): string
{
return 'Rules that follow the official `Symfony Coding Standards <https://symfony.com/doc/current/contributing/code/standards.html>`_.';
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\RuleSet\AbstractRuleSetDescription;
final class PSR12Set extends AbstractRuleSetDescription
{
public function getRules(): array
{
return [
'@PSR2' => true,
'blank_line_after_opening_tag' => true,
'blank_line_between_import_groups' => true,
'braces' => [
'allow_single_line_anonymous_class_with_empty_body' => true,
],
'class_definition' => [
'inline_constructor_arguments' => false,
'space_before_parenthesis' => true,
],
'compact_nullable_typehint' => true,
'declare_equal_normalize' => true,
'lowercase_cast' => true,
'lowercase_static_reference' => true,
'new_with_braces' => true,
'no_blank_lines_after_class_opening' => true,
'no_leading_import_slash' => true,
'no_whitespace_in_blank_line' => true,
'ordered_class_elements' => [
'order' => [
'use_trait',
],
],
'ordered_imports' => [
'imports_order' => [
'class',
'function',
'const',
],
'sort_algorithm' => 'none',
],
'return_type_declaration' => true,
'short_scalar_cast' => true,
'single_blank_line_before_namespace' => true,
'single_import_per_statement' => ['group_to_single_imports' => false],
'single_trait_insert_per_statement' => true,
'ternary_operator_spaces' => true,
'visibility_required' => true,
];
}
public function getDescription(): string
{
return 'Rules that follow `PSR-12 <https://www.php-fig.org/psr/psr-12/>`_ standard.';
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\RuleSet\AbstractMigrationSetDescription;
final class PHP71MigrationSet extends AbstractMigrationSetDescription
{
public function getRules(): array
{
return [
'@PHP70Migration' => true,
'list_syntax' => true,
'visibility_required' => true,
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\Fixer\PhpUnit\PhpUnitTargetVersion;
use PhpCsFixer\RuleSet\AbstractMigrationSetDescription;
final class PHPUnit84MigrationRiskySet extends AbstractMigrationSetDescription
{
public function getRules(): array
{
return [
'@PHPUnit60Migration:risky' => true,
'@PHPUnit75Migration:risky' => true,
'php_unit_expectation' => [
'target' => PhpUnitTargetVersion::VERSION_8_4,
],
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\RuleSet\AbstractMigrationSetDescription;
final class PHP81MigrationSet extends AbstractMigrationSetDescription
{
public function getRules(): array
{
return [
'@PHP80Migration' => true,
'octal_notation' => true,
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\Fixer\PhpUnit\PhpUnitTargetVersion;
use PhpCsFixer\RuleSet\AbstractMigrationSetDescription;
final class PHPUnit60MigrationRiskySet extends AbstractMigrationSetDescription
{
public function getRules(): array
{
return [
'@PHPUnit57Migration:risky' => true,
'php_unit_namespaced' => [
'target' => PhpUnitTargetVersion::VERSION_6_0,
],
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\Fixer\PhpUnit\PhpUnitTargetVersion;
use PhpCsFixer\RuleSet\AbstractMigrationSetDescription;
final class PHPUnit57MigrationRiskySet extends AbstractMigrationSetDescription
{
public function getRules(): array
{
return [
'@PHPUnit56Migration:risky' => true,
'php_unit_namespaced' => [
'target' => PhpUnitTargetVersion::VERSION_5_7,
],
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\RuleSet\AbstractMigrationSetDescription;
final class PHP70MigrationRiskySet extends AbstractMigrationSetDescription
{
public function getRules(): array
{
return [
'@PHP56Migration:risky' => true,
'combine_nested_dirname' => true,
'declare_strict_types' => true,
'non_printable_character' => true,
'random_api_migration' => [
'replacements' => [
'mt_rand' => 'random_int',
'rand' => 'random_int',
],
],
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\RuleSet\AbstractRuleSetDescription;
final class PERRiskySet extends AbstractRuleSetDescription
{
public function getRules(): array
{
return [
'@PSR12:risky' => true,
];
}
public function getDescription(): string
{
return 'Rules that follow `PER Coding Style <https://www.php-fig.org/per/coding-style/>`_.';
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\Fixer\PhpUnit\PhpUnitTargetVersion;
use PhpCsFixer\RuleSet\AbstractMigrationSetDescription;
final class PHPUnit52MigrationRiskySet extends AbstractMigrationSetDescription
{
public function getRules(): array
{
return [
'@PHPUnit50Migration:risky' => true,
'php_unit_expectation' => [
'target' => PhpUnitTargetVersion::VERSION_5_2,
],
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\RuleSet\AbstractRuleSetDescription;
final class DoctrineAnnotationSet extends AbstractRuleSetDescription
{
public function getRules(): array
{
return [
'doctrine_annotation_array_assignment' => [
'operator' => ':',
],
'doctrine_annotation_braces' => true,
'doctrine_annotation_indentation' => true,
'doctrine_annotation_spaces' => [
'before_array_assignments_colon' => false,
],
];
}
public function getDescription(): string
{
return 'Rules covering Doctrine annotations with configuration based on examples found in `Doctrine Annotation documentation <https://www.doctrine-project.org/projects/doctrine-annotations/en/latest/annotations.html>`_ and `Symfony documentation <https://symfony.com/doc/master/bundles/SensioFrameworkExtraBundle/annotations/routing.html>`_.';
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\RuleSet\AbstractRuleSetDescription;
final class PhpCsFixerRiskySet extends AbstractRuleSetDescription
{
public function getRules(): array
{
return [
'@PER:risky' => true,
'@Symfony:risky' => true,
'comment_to_phpdoc' => true,
'final_internal_class' => true,
'native_constant_invocation' => [
'fix_built_in' => false,
'include' => [
'DIRECTORY_SEPARATOR',
'PHP_INT_SIZE',
'PHP_SAPI',
'PHP_VERSION_ID',
],
'scope' => 'namespaced',
'strict' => true,
],
'no_alias_functions' => [
'sets' => [
'@all',
],
],
'no_unreachable_default_argument_value' => true,
'no_unset_on_property' => true,
'php_unit_strict' => true,
'php_unit_test_case_static_method_calls' => true,
'strict_comparison' => true,
'strict_param' => true,
];
}
public function getDescription(): string
{
return 'Rule set as used by the PHP-CS-Fixer development team, highly opinionated.';
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\RuleSet\AbstractMigrationSetDescription;
final class PHP73MigrationSet extends AbstractMigrationSetDescription
{
public function getRules(): array
{
return [
'@PHP71Migration' => true,
'heredoc_indentation' => true,
'method_argument_space' => ['after_heredoc' => true],
'no_whitespace_before_comma_in_array' => ['after_heredoc' => true],
'trailing_comma_in_multiline' => ['after_heredoc' => true],
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\RuleSet\AbstractMigrationSetDescription;
final class PHP80MigrationRiskySet extends AbstractMigrationSetDescription
{
public function getRules(): array
{
return [
'@PHP74Migration:risky' => true,
'get_class_to_class_keyword' => true,
'modernize_strpos' => true,
'no_alias_functions' => [
'sets' => [
'@all',
],
],
'no_php4_constructor' => true,
'no_unneeded_final_method' => true,
'no_unreachable_default_argument_value' => true,
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\Fixer\PhpUnit\PhpUnitTargetVersion;
use PhpCsFixer\RuleSet\AbstractMigrationSetDescription;
final class PHPUnit54MigrationRiskySet extends AbstractMigrationSetDescription
{
public function getRules(): array
{
return [
'@PHPUnit52Migration:risky' => true,
'php_unit_mock' => [
'target' => PhpUnitTargetVersion::VERSION_5_4,
],
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\Fixer\PhpUnit\PhpUnitTargetVersion;
use PhpCsFixer\RuleSet\AbstractMigrationSetDescription;
final class PHPUnit35MigrationRiskySet extends AbstractMigrationSetDescription
{
public function getRules(): array
{
return [
'@PHPUnit32Migration:risky' => true,
'php_unit_dedicate_assert' => [
'target' => PhpUnitTargetVersion::VERSION_3_5,
],
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\RuleSet\AbstractMigrationSetDescription;
final class PHP56MigrationRiskySet extends AbstractMigrationSetDescription
{
public function getRules(): array
{
return [
'pow_to_exponentiation' => true,
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\Fixer\PhpUnit\PhpUnitTargetVersion;
use PhpCsFixer\RuleSet\AbstractMigrationSetDescription;
final class PHPUnit30MigrationRiskySet extends AbstractMigrationSetDescription
{
public function getRules(): array
{
return [
'php_unit_dedicate_assert' => [
'target' => PhpUnitTargetVersion::VERSION_3_0,
],
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\Fixer\PhpUnit\PhpUnitTargetVersion;
use PhpCsFixer\RuleSet\AbstractMigrationSetDescription;
final class PHPUnit43MigrationRiskySet extends AbstractMigrationSetDescription
{
public function getRules(): array
{
return [
'@PHPUnit35Migration:risky' => true,
'php_unit_no_expectation_annotation' => [
'target' => PhpUnitTargetVersion::VERSION_4_3,
],
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\RuleSet\AbstractMigrationSetDescription;
final class PHP74MigrationSet extends AbstractMigrationSetDescription
{
public function getRules(): array
{
return [
'@PHP73Migration' => true,
'assign_null_coalescing_to_coalesce_equal' => true,
'normalize_index_brace' => true,
'short_scalar_cast' => true,
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\RuleSet\AbstractRuleSetDescription;
final class PSR1Set extends AbstractRuleSetDescription
{
public function getRules(): array
{
return [
'encoding' => true,
'full_opening_tag' => true,
];
}
public function getDescription(): string
{
return 'Rules that follow `PSR-1 <https://www.php-fig.org/psr/psr-1/>`_ standard.';
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\RuleSet\AbstractRuleSetDescription;
final class PSR12RiskySet extends AbstractRuleSetDescription
{
public function getRules(): array
{
return [
'no_trailing_whitespace_in_string' => true,
'no_unreachable_default_argument_value' => true,
];
}
public function getDescription(): string
{
return 'Rules that follow `PSR-12 <https://www.php-fig.org/psr/psr-12/>`_ standard.';
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\RuleSet\AbstractMigrationSetDescription;
final class PHP82MigrationSet extends AbstractMigrationSetDescription
{
public function getRules(): array
{
return [
'@PHP81Migration' => true,
'simple_to_complex_string_variable' => true,
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\Fixer\PhpUnit\PhpUnitTargetVersion;
use PhpCsFixer\RuleSet\AbstractMigrationSetDescription;
final class PHPUnit50MigrationRiskySet extends AbstractMigrationSetDescription
{
public function getRules(): array
{
return [
'@PHPUnit48Migration:risky' => true,
'php_unit_dedicate_assert' => [
'target' => PhpUnitTargetVersion::VERSION_5_0,
],
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\RuleSet\AbstractRuleSetDescription;
final class PERSet extends AbstractRuleSetDescription
{
public function getRules(): array
{
return [
'@PSR12' => true,
];
}
public function getDescription(): string
{
return 'Rules that follow `PER Coding Style <https://www.php-fig.org/per/coding-style/>`_.';
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\Fixer\PhpUnit\PhpUnitTargetVersion;
use PhpCsFixer\RuleSet\AbstractMigrationSetDescription;
final class PHPUnit55MigrationRiskySet extends AbstractMigrationSetDescription
{
public function getRules(): array
{
return [
'@PHPUnit54Migration:risky' => true,
'php_unit_mock' => [
'target' => PhpUnitTargetVersion::VERSION_5_5,
],
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\Fixer\PhpUnit\PhpUnitTargetVersion;
use PhpCsFixer\RuleSet\AbstractMigrationSetDescription;
final class PHPUnit75MigrationRiskySet extends AbstractMigrationSetDescription
{
public function getRules(): array
{
return [
'@PHPUnit60Migration:risky' => true,
'php_unit_dedicate_assert_internal_type' => [
'target' => PhpUnitTargetVersion::VERSION_7_5,
],
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet\Sets;
use PhpCsFixer\Fixer\PhpUnit\PhpUnitTargetVersion;
use PhpCsFixer\RuleSet\AbstractMigrationSetDescription;
final class PHPUnit48MigrationRiskySet extends AbstractMigrationSetDescription
{
public function getRules(): array
{
return [
'@PHPUnit43Migration:risky' => true,
'php_unit_namespaced' => [
'target' => PhpUnitTargetVersion::VERSION_4_8,
],
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet;
abstract class AbstractRuleSetDescription implements RuleSetDescriptionInterface
{
public function __construct()
{
}
public function getName(): string
{
$name = substr(static::class, 1 + strrpos(static::class, '\\'), -3);
return '@'.str_replace('Risky', ':risky', $name);
}
public function isRisky(): bool
{
return str_contains(static::class, 'Risky');
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\RuleSet;
use PhpCsFixer\Preg;
abstract class AbstractMigrationSetDescription extends AbstractRuleSetDescription
{
public function getDescription(): string
{
$name = $this->getName();
if (0 !== Preg::match('#^@PHPUnit([\d]{2})Migration.*$#', $name, $matches)) {
return sprintf('Rules to improve tests code for PHPUnit %d.%d compatibility.', $matches[1][0], $matches[1][1]);
}
if (0 !== Preg::match('#^@PHP([\d]{2})Migration.*$#', $name, $matches)) {
return sprintf('Rules to improve code for PHP %d.%d compatibility.', $matches[1][0], $matches[1][1]);
}
throw new \RuntimeException(sprintf('Cannot generate description for "%s" "%s".', static::class, $name));
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer;
use PhpCsFixer\Tokenizer\Tokens;
abstract class AbstractNoUselessElseFixer extends AbstractFixer
{
public function getPriority(): int
{
return 39;
}
protected function isSuperfluousElse(Tokens $tokens, int $index): bool
{
$previousBlockStart = $index;
do {
[$previousBlockStart, $previousBlockEnd] = $this->getPreviousBlock($tokens, $previousBlockStart);
$previous = $previousBlockEnd;
if ($tokens[$previous]->equals('}')) {
$previous = $tokens->getPrevMeaningfulToken($previous);
}
if (
!$tokens[$previous]->equals(';')
|| $tokens[$tokens->getPrevMeaningfulToken($previous)]->equals('{')
) {
return false;
}
$candidateIndex = $tokens->getPrevTokenOfKind(
$previous,
[
';',
[T_BREAK],
[T_CLOSE_TAG],
[T_CONTINUE],
[T_EXIT],
[T_GOTO],
[T_IF],
[T_RETURN],
[T_THROW],
]
);
if (null === $candidateIndex || $tokens[$candidateIndex]->equalsAny([';', [T_CLOSE_TAG], [T_IF]])) {
return false;
}
if ($tokens[$candidateIndex]->isGivenKind(T_THROW)) {
$previousIndex = $tokens->getPrevMeaningfulToken($candidateIndex);
if (!$tokens[$previousIndex]->equalsAny([';', '{'])) {
return false;
}
}
if ($this->isInConditional($tokens, $candidateIndex, $previousBlockStart)
|| $this->isInConditionWithoutBraces($tokens, $candidateIndex, $previousBlockStart)
) {
return false;
}
} while (!$tokens[$previousBlockStart]->isGivenKind(T_IF));
return true;
}
private function getPreviousBlock(Tokens $tokens, int $index): array
{
$close = $previous = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$close]->equals('}')) {
$previous = $tokens->findBlockStart(Tokens::BLOCK_TYPE_CURLY_BRACE, $close);
}
$open = $tokens->getPrevTokenOfKind($previous, [[T_IF], [T_ELSE], [T_ELSEIF]]);
if ($tokens[$open]->isGivenKind(T_IF)) {
$elseCandidate = $tokens->getPrevMeaningfulToken($open);
if ($tokens[$elseCandidate]->isGivenKind(T_ELSE)) {
$open = $elseCandidate;
}
}
return [$open, $close];
}
private function isInConditional(Tokens $tokens, int $index, int $lowerLimitIndex): bool
{
$candidateIndex = $tokens->getPrevTokenOfKind($index, [')', ';', ':']);
if ($tokens[$candidateIndex]->equals(':')) {
return true;
}
if (!$tokens[$candidateIndex]->equals(')')) {
return false;
}
$open = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $candidateIndex);
return $tokens->getPrevMeaningfulToken($open) > $lowerLimitIndex;
}
private function isInConditionWithoutBraces(Tokens $tokens, int $index, int $lowerLimitIndex): bool
{
do {
if ($tokens[$index]->isComment() || $tokens[$index]->isWhitespace()) {
$index = $tokens->getPrevMeaningfulToken($index);
}
$token = $tokens[$index];
if ($token->isGivenKind([T_IF, T_ELSEIF, T_ELSE])) {
return true;
}
if ($token->equals(';')) {
return false;
}
if ($token->equals('{')) {
$index = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$index]->isGivenKind(T_DO)) {
--$index;
continue;
}
if (!$tokens[$index]->equals(')')) {
return false;
}
$index = $tokens->findBlockStart(
Tokens::BLOCK_TYPE_PARENTHESIS_BRACE,
$index
);
$index = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$index]->isGivenKind([T_IF, T_ELSEIF])) {
return false;
}
} elseif ($token->equals(')')) {
$type = Tokens::detectBlockType($token);
$index = $tokens->findBlockStart(
$type['type'],
$index
);
$index = $tokens->getPrevMeaningfulToken($index);
} else {
--$index;
}
} while ($index > $lowerLimitIndex);
return false;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Indicator;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Tokens;
final class PhpUnitTestCaseIndicator
{
public function isPhpUnitClass(Tokens $tokens, int $index): bool
{
if (!$tokens[$index]->isGivenKind(T_CLASS)) {
throw new \LogicException(sprintf('No "T_CLASS" at given index %d, got "%s".', $index, $tokens[$index]->getName()));
}
$index = $tokens->getNextMeaningfulToken($index);
if (!$tokens[$index]->isGivenKind(T_STRING)) {
return false;
}
$extendsIndex = $tokens->getNextTokenOfKind($index, ['{', [T_EXTENDS]]);
if (!$tokens[$extendsIndex]->isGivenKind(T_EXTENDS)) {
return false;
}
if (0 !== Preg::match('/(?:Test|TestCase)$/', $tokens[$index]->getContent())) {
return true;
}
while (null !== $index = $tokens->getNextMeaningfulToken($index)) {
if ($tokens[$index]->equals('{')) {
break;
}
if (!$tokens[$index]->isGivenKind(T_STRING)) {
continue;
}
if (0 !== Preg::match('/(?:Test|TestCase)(?:Interface)?$/', $tokens[$index]->getContent())) {
return true;
}
}
return false;
}
public function findPhpUnitClasses(Tokens $tokens): \Generator
{
for ($index = $tokens->count() - 1; $index > 0; --$index) {
if (!$tokens[$index]->isGivenKind(T_CLASS) || !$this->isPhpUnitClass($tokens, $index)) {
continue;
}
$startIndex = $tokens->getNextTokenOfKind($index, ['{']);
if (null === $startIndex) {
return;
}
$endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $startIndex);
yield [$startIndex, $endIndex];
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Cache;
use Symfony\Component\Filesystem\Exception\IOException;
final class FileHandler implements FileHandlerInterface
{
private string $file;
public function __construct(string $file)
{
$this->file = $file;
}
public function getFile(): string
{
return $this->file;
}
public function read(): ?CacheInterface
{
if (!file_exists($this->file)) {
return null;
}
$content = file_get_contents($this->file);
try {
$cache = Cache::fromJson($content);
} catch (\InvalidArgumentException $exception) {
return null;
}
return $cache;
}
public function write(CacheInterface $cache): void
{
$content = $cache->toJson();
if (file_exists($this->file)) {
if (is_dir($this->file)) {
throw new IOException(
sprintf('Cannot write cache file "%s" as the location exists as directory.', realpath($this->file)),
0,
null,
$this->file
);
}
if (!is_writable($this->file)) {
throw new IOException(
sprintf('Cannot write to file "%s" as it is not writable.', realpath($this->file)),
0,
null,
$this->file
);
}
} else {
$dir = \dirname($this->file);
if (!is_dir($dir)) {
throw new IOException(
sprintf('Directory of cache file "%s" does not exists.', $this->file),
0,
null,
$this->file
);
}
@touch($this->file);
@chmod($this->file, 0666);
}
$bytesWritten = @file_put_contents($this->file, $content);
if (false === $bytesWritten) {
$error = error_get_last();
throw new IOException(
sprintf('Failed to write file "%s", "%s".', $this->file, $error['message'] ?? 'no reason available'),
0,
null,
$this->file
);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Cache;
final class FileCacheManager implements CacheManagerInterface
{
private FileHandlerInterface $handler;
private SignatureInterface $signature;
private bool $isDryRun;
private DirectoryInterface $cacheDirectory;
private $cache;
public function __construct(
FileHandlerInterface $handler,
SignatureInterface $signature,
bool $isDryRun = false,
?DirectoryInterface $cacheDirectory = null
) {
$this->handler = $handler;
$this->signature = $signature;
$this->isDryRun = $isDryRun;
$this->cacheDirectory = $cacheDirectory ?? new Directory('');
$this->readCache();
}
public function __destruct()
{
$this->writeCache();
}
public function __sleep(): array
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup(): void
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
public function needFixing(string $file, string $fileContent): bool
{
$file = $this->cacheDirectory->getRelativePathTo($file);
return !$this->cache->has($file) || $this->cache->get($file) !== $this->calcHash($fileContent);
}
public function setFile(string $file, string $fileContent): void
{
$file = $this->cacheDirectory->getRelativePathTo($file);
$hash = $this->calcHash($fileContent);
if ($this->isDryRun && $this->cache->has($file) && $this->cache->get($file) !== $hash) {
$this->cache->clear($file);
return;
}
$this->cache->set($file, $hash);
}
private function readCache(): void
{
$cache = $this->handler->read();
if (null === $cache || !$this->signature->equals($cache->getSignature())) {
$cache = new Cache($this->signature);
}
$this->cache = $cache;
}
private function writeCache(): void
{
$this->handler->write($this->cache);
}
private function calcHash(string $content): string
{
return md5($content);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Cache;
final class Cache implements CacheInterface
{
private SignatureInterface $signature;
private array $hashes = [];
public function __construct(SignatureInterface $signature)
{
$this->signature = $signature;
}
public function getSignature(): SignatureInterface
{
return $this->signature;
}
public function has(string $file): bool
{
return \array_key_exists($file, $this->hashes);
}
public function get(string $file): ?string
{
if (!$this->has($file)) {
return null;
}
return $this->hashes[$file];
}
public function set(string $file, string $hash): void
{
$this->hashes[$file] = $hash;
}
public function clear(string $file): void
{
unset($this->hashes[$file]);
}
public function toJson(): string
{
$json = json_encode([
'php' => $this->getSignature()->getPhpVersion(),
'version' => $this->getSignature()->getFixerVersion(),
'indent' => $this->getSignature()->getIndent(),
'lineEnding' => $this->getSignature()->getLineEnding(),
'rules' => $this->getSignature()->getRules(),
'hashes' => $this->hashes,
]);
if (JSON_ERROR_NONE !== json_last_error()) {
throw new \UnexpectedValueException(sprintf(
'Cannot encode cache signature to JSON, error: "%s". If you have non-UTF8 chars in your signature, like in license for `header_comment`, consider enabling `ext-mbstring` or install `symfony/polyfill-mbstring`.',
json_last_error_msg()
));
}
return $json;
}
public static function fromJson(string $json): self
{
$data = json_decode($json, true);
if (null === $data && JSON_ERROR_NONE !== json_last_error()) {
throw new \InvalidArgumentException(sprintf(
'Value needs to be a valid JSON string, got "%s", error: "%s".',
$json,
json_last_error_msg()
));
}
$requiredKeys = [
'php',
'version',
'indent',
'lineEnding',
'rules',
'hashes',
];
$missingKeys = array_diff_key(array_flip($requiredKeys), $data);
if (\count($missingKeys) > 0) {
throw new \InvalidArgumentException(sprintf(
'JSON data is missing keys "%s"',
implode('", "', $missingKeys)
));
}
$signature = new Signature(
$data['php'],
$data['version'],
$data['indent'],
$data['lineEnding'],
$data['rules']
);
$cache = new self($signature);
$cache->hashes = array_map(function ($v): string {
return \is_int($v) ? (string) $v : $v;
}, $data['hashes']);
return $cache;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Cache;
interface CacheInterface
{
public function getSignature(): SignatureInterface;
public function has(string $file): bool;
public function get(string $file): ?string;
public function set(string $file, string $hash): void;
public function clear(string $file): void;
public function toJson(): string;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Cache;
final class NullCacheManager implements CacheManagerInterface
{
public function needFixing(string $file, string $fileContent): bool
{
return true;
}
public function setFile(string $file, string $fileContent): void
{
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Cache;
interface CacheManagerInterface
{
public function needFixing(string $file, string $fileContent): bool;
public function setFile(string $file, string $fileContent): void;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Cache;
interface FileHandlerInterface
{
public function getFile(): string;
public function read(): ?CacheInterface;
public function write(CacheInterface $cache): void;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Cache;
final class Directory implements DirectoryInterface
{
private string $directoryName;
public function __construct(string $directoryName)
{
$this->directoryName = $directoryName;
}
public function getRelativePathTo(string $file): string
{
$file = $this->normalizePath($file);
if (
'' === $this->directoryName
|| 0 !== stripos($file, $this->directoryName.\DIRECTORY_SEPARATOR)
) {
return $file;
}
return substr($file, \strlen($this->directoryName) + 1);
}
private function normalizePath(string $path): string
{
return str_replace(['\\', '/'], \DIRECTORY_SEPARATOR, $path);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Cache;
interface SignatureInterface
{
public function getPhpVersion(): string;
public function getFixerVersion(): string;
public function getIndent(): string;
public function getLineEnding(): string;
public function getRules(): array;
public function equals(self $signature): bool;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Cache;
interface DirectoryInterface
{
public function getRelativePathTo(string $file): string;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Cache;
final class Signature implements SignatureInterface
{
private string $phpVersion;
private string $fixerVersion;
private string $indent;
private string $lineEnding;
private array $rules;
public function __construct(string $phpVersion, string $fixerVersion, string $indent, string $lineEnding, array $rules)
{
$this->phpVersion = $phpVersion;
$this->fixerVersion = $fixerVersion;
$this->indent = $indent;
$this->lineEnding = $lineEnding;
$this->rules = self::makeJsonEncodable($rules);
}
public function getPhpVersion(): string
{
return $this->phpVersion;
}
public function getFixerVersion(): string
{
return $this->fixerVersion;
}
public function getIndent(): string
{
return $this->indent;
}
public function getLineEnding(): string
{
return $this->lineEnding;
}
public function getRules(): array
{
return $this->rules;
}
public function equals(SignatureInterface $signature): bool
{
return $this->phpVersion === $signature->getPhpVersion()
&& $this->fixerVersion === $signature->getFixerVersion()
&& $this->indent === $signature->getIndent()
&& $this->lineEnding === $signature->getLineEnding()
&& $this->rules === $signature->getRules();
}
private static function makeJsonEncodable(array $data): array
{
array_walk_recursive($data, static function (&$item): void {
if (\is_string($item) && !mb_detect_encoding($item, 'utf-8', true)) {
$item = base64_encode($item);
}
});
return $data;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Linter;
use Symfony\Component\Process\Process;
final class ProcessLintingResult implements LintingResultInterface
{
private Process $process;
private ?string $path;
private ?bool $isSuccessful = null;
public function __construct(Process $process, ?string $path = null)
{
$this->process = $process;
$this->path = $path;
}
public function check(): void
{
if (!$this->isSuccessful()) {
throw new LintingException($this->getProcessErrorMessage(), $this->process->getExitCode());
}
}
private function getProcessErrorMessage(): string
{
$output = strtok(ltrim($this->process->getErrorOutput() ?: $this->process->getOutput()), "\n");
if (false === $output) {
return 'Fatal error: Unable to lint file.';
}
if (null !== $this->path) {
$needle = sprintf('in %s ', $this->path);
$pos = strrpos($output, $needle);
if (false !== $pos) {
$output = sprintf('%s%s', substr($output, 0, $pos), substr($output, $pos + \strlen($needle)));
}
}
$prefix = substr($output, 0, 18);
if ('PHP Parse error: ' === $prefix) {
return sprintf('Parse error: %s.', substr($output, 18));
}
if ('PHP Fatal error: ' === $prefix) {
return sprintf('Fatal error: %s.', substr($output, 18));
}
return sprintf('%s.', $output);
}
private function isSuccessful(): bool
{
if (null === $this->isSuccessful) {
$this->process->wait();
$this->isSuccessful = $this->process->isSuccessful();
}
return $this->isSuccessful;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Linter;
interface LintingResultInterface
{
public function check(): void;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Linter;
use PhpCsFixer\FileReader;
use PhpCsFixer\FileRemoval;
use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Process\PhpExecutableFinder;
use Symfony\Component\Process\Process;
final class ProcessLinter implements LinterInterface
{
private FileRemoval $fileRemoval;
private ProcessLinterProcessBuilder $processBuilder;
private $temporaryFile;
public function __construct(?string $executable = null)
{
if (null === $executable) {
$executableFinder = new PhpExecutableFinder();
$executable = $executableFinder->find(false);
if (false === $executable) {
throw new UnavailableLinterException('Cannot find PHP executable.');
}
if ('phpdbg' === \PHP_SAPI) {
if (!str_contains($executable, 'phpdbg')) {
throw new UnavailableLinterException('Automatically found PHP executable is non-standard phpdbg. Could not find proper PHP executable.');
}
$executable = str_replace('phpdbg', 'php', $executable);
if (!is_executable($executable)) {
throw new UnavailableLinterException('Automatically found PHP executable is phpdbg. Could not find proper PHP executable.');
}
}
}
$this->processBuilder = new ProcessLinterProcessBuilder($executable);
$this->fileRemoval = new FileRemoval();
}
public function __destruct()
{
if (null !== $this->temporaryFile) {
$this->fileRemoval->delete($this->temporaryFile);
}
}
public function __sleep(): array
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup(): void
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
public function isAsync(): bool
{
return true;
}
public function lintFile(string $path): LintingResultInterface
{
return new ProcessLintingResult($this->createProcessForFile($path), $path);
}
public function lintSource(string $source): LintingResultInterface
{
return new ProcessLintingResult($this->createProcessForSource($source), $this->temporaryFile);
}
private function createProcessForFile(string $path): Process
{
if (!is_file($path)) {
return $this->createProcessForSource(FileReader::createSingleton()->read($path));
}
$process = $this->processBuilder->build($path);
$process->setTimeout(10);
$process->start();
return $process;
}
private function createProcessForSource(string $source): Process
{
if (null === $this->temporaryFile) {
$this->temporaryFile = tempnam(sys_get_temp_dir(), 'cs_fixer_tmp_');
$this->fileRemoval->observe($this->temporaryFile);
}
if (false === @file_put_contents($this->temporaryFile, $source)) {
throw new IOException(sprintf('Failed to write file "%s".', $this->temporaryFile), 0, null, $this->temporaryFile);
}
return $this->createProcessForFile($this->temporaryFile);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Linter;
final class TokenizerLintingResult implements LintingResultInterface
{
private ?\Error $error;
public function __construct(?\Error $error = null)
{
$this->error = $error;
}
public function check(): void
{
if (null !== $this->error) {
throw new LintingException(
sprintf('%s: %s on line %d.', $this->getMessagePrefix(), $this->error->getMessage(), $this->error->getLine()),
$this->error->getCode(),
$this->error
);
}
}
private function getMessagePrefix(): string
{
return $this->error instanceof \ParseError ? 'Parse error' : 'Fatal error';
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Linter;
class UnavailableLinterException extends \RuntimeException
{
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Linter;
class LintingException extends \RuntimeException
{
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Linter;
use Symfony\Component\Process\Process;
final class ProcessLinterProcessBuilder
{
private string $executable;
public function __construct(string $executable)
{
$this->executable = $executable;
}
public function build(string $path): Process
{
return new Process([
$this->executable,
'-l',
$path,
]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Linter;
interface LinterInterface
{
public function isAsync(): bool;
public function lintFile(string $path): LintingResultInterface;
public function lintSource(string $source): LintingResultInterface;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Linter;
final class Linter implements LinterInterface
{
private LinterInterface $subLinter;
public function __construct()
{
$this->subLinter = new TokenizerLinter();
}
public function isAsync(): bool
{
return $this->subLinter->isAsync();
}
public function lintFile(string $path): LintingResultInterface
{
return $this->subLinter->lintFile($path);
}
public function lintSource(string $source): LintingResultInterface
{
return $this->subLinter->lintSource($source);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Linter;
use PhpCsFixer\FileReader;
use PhpCsFixer\Tokenizer\CodeHasher;
use PhpCsFixer\Tokenizer\Tokens;
final class TokenizerLinter implements LinterInterface
{
public function isAsync(): bool
{
return false;
}
public function lintFile(string $path): LintingResultInterface
{
return $this->lintSource(FileReader::createSingleton()->read($path));
}
public function lintSource(string $source): LintingResultInterface
{
try {
$codeHash = CodeHasher::calculateCodeHash($source);
Tokens::clearCache($codeHash);
Tokens::fromCode($source);
return new TokenizerLintingResult();
} catch (\ParseError|\CompileError $e) {
return new TokenizerLintingResult($e);
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Linter;
final class CachingLinter implements LinterInterface
{
private LinterInterface $sublinter;
private array $cache = [];
public function __construct(LinterInterface $linter)
{
$this->sublinter = $linter;
}
public function isAsync(): bool
{
return $this->sublinter->isAsync();
}
public function lintFile(string $path): LintingResultInterface
{
$checksum = md5(file_get_contents($path));
if (!isset($this->cache[$checksum])) {
$this->cache[$checksum] = $this->sublinter->lintFile($path);
}
return $this->cache[$checksum];
}
public function lintSource(string $source): LintingResultInterface
{
$checksum = md5($source);
if (!isset($this->cache[$checksum])) {
$this->cache[$checksum] = $this->sublinter->lintSource($source);
}
return $this->cache[$checksum];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer;
final class PregException extends \RuntimeException
{
}
<?php
declare(strict_types=1);
namespace PhpCsFixer;
use PhpCsFixer\Fixer\FixerInterface;
interface ConfigInterface
{
public function getCacheFile(): ?string;
public function getCustomFixers(): array;
public function getFinder(): iterable;
public function getFormat(): string;
public function getHideProgress(): bool;
public function getIndent(): string;
public function getLineEnding(): string;
public function getName(): string;
public function getPhpExecutable(): ?string;
public function getRiskyAllowed(): bool;
public function getRules(): array;
public function getUsingCache(): bool;
public function registerCustomFixers(iterable $fixers): self;
public function setCacheFile(string $cacheFile): self;
public function setFinder(iterable $finder): self;
public function setFormat(string $format): self;
public function setHideProgress(bool $hideProgress): self;
public function setIndent(string $indent): self;
public function setLineEnding(string $lineEnding): self;
public function setPhpExecutable(?string $phpExecutable): self;
public function setRiskyAllowed(bool $isRiskyAllowed): self;
public function setRules(array $rules): self;
public function setUsingCache(bool $usingCache): self;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer;
final class FileReader
{
private $stdinContent;
public static function createSingleton(): self
{
static $instance = null;
if (!$instance) {
$instance = new self();
}
return $instance;
}
public function read(string $filePath): string
{
if ('php://stdin' === $filePath) {
if (null === $this->stdinContent) {
$this->stdinContent = $this->readRaw($filePath);
}
return $this->stdinContent;
}
return $this->readRaw($filePath);
}
private function readRaw(string $realPath): string
{
$content = @file_get_contents($realPath);
if (false === $content) {
$error = error_get_last();
throw new \RuntimeException(sprintf(
'Failed to read content from "%s".%s',
$realPath,
$error ? ' '.$error['message'] : ''
));
}
return $content;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer;
use PhpCsFixer\DocBlock\Annotation;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
abstract class AbstractPhpdocTypesFixer extends AbstractFixer
{
protected array $tags;
public function __construct()
{
parent::__construct();
$this->tags = Annotation::getTagsWithTypes();
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOC_COMMENT);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(T_DOC_COMMENT)) {
continue;
}
$doc = new DocBlock($token->getContent());
$annotations = $doc->getAnnotationsOfType($this->tags);
if (0 === \count($annotations)) {
continue;
}
foreach ($annotations as $annotation) {
$this->fixTypes($annotation);
}
$tokens[$index] = new Token([T_DOC_COMMENT, $doc->getContent()]);
}
}
abstract protected function normalize(string $type): string;
private function fixTypes(Annotation $annotation): void
{
$types = $annotation->getTypes();
$new = $this->normalizeTypes($types);
if ($types !== $new) {
$annotation->setTypes($new);
}
}
private function normalizeTypes(array $types): array
{
foreach ($types as $index => $type) {
$types[$index] = $this->normalizeType($type);
}
return $types;
}
private function normalizeType(string $type): string
{
return str_ends_with($type, '[]')
? $this->normalizeType(substr($type, 0, -2)).'[]'
: $this->normalize($type)
;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer;
use PhpCsFixer\Tokenizer\Analyzer\AttributeAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\GotoLabelAnalyzer;
final class TokensAnalyzer
{
private Tokens $tokens;
private ?GotoLabelAnalyzer $gotoLabelAnalyzer = null;
public function __construct(Tokens $tokens)
{
$this->tokens = $tokens;
}
public function getClassyElements(): array
{
$elements = [];
for ($index = 1, $count = \count($this->tokens) - 2; $index < $count; ++$index) {
if ($this->tokens[$index]->isClassy()) {
[$index, $newElements] = $this->findClassyElements($index, $index);
$elements += $newElements;
}
}
ksort($elements);
return $elements;
}
public function getImportUseIndexes(bool $perNamespace = false): array
{
$tokens = $this->tokens;
$uses = [];
$namespaceIndex = 0;
for ($index = 0, $limit = $tokens->count(); $index < $limit; ++$index) {
$token = $tokens[$index];
if ($token->isGivenKind(T_NAMESPACE)) {
$nextTokenIndex = $tokens->getNextTokenOfKind($index, [';', '{']);
$nextToken = $tokens[$nextTokenIndex];
if ($nextToken->equals('{')) {
$index = $nextTokenIndex;
}
if ($perNamespace) {
++$namespaceIndex;
}
continue;
}
if ($token->isGivenKind(T_USE)) {
$uses[$namespaceIndex][] = $index;
}
}
if (!$perNamespace && isset($uses[$namespaceIndex])) {
return $uses[$namespaceIndex];
}
return $uses;
}
public function isArray(int $index): bool
{
return $this->tokens[$index]->isGivenKind([T_ARRAY, CT::T_ARRAY_SQUARE_BRACE_OPEN]);
}
public function isArrayMultiLine(int $index): bool
{
if (!$this->isArray($index)) {
throw new \InvalidArgumentException(sprintf('Not an array at given index %d.', $index));
}
$tokens = $this->tokens;
if ($tokens[$index]->isGivenKind(T_ARRAY)) {
$index = $tokens->getNextMeaningfulToken($index);
}
return $this->isBlockMultiline($tokens, $index);
}
public function isBlockMultiline(Tokens $tokens, int $index): bool
{
$blockType = Tokens::detectBlockType($tokens[$index]);
if (null === $blockType || !$blockType['isStart']) {
throw new \InvalidArgumentException(sprintf('Not an block start at given index %d.', $index));
}
$endIndex = $tokens->findBlockEnd($blockType['type'], $index);
for (++$index; $index < $endIndex; ++$index) {
$token = $tokens[$index];
$blockType = Tokens::detectBlockType($token);
if (null !== $blockType && $blockType['isStart']) {
$index = $tokens->findBlockEnd($blockType['type'], $index);
continue;
}
if (
$token->isWhitespace()
&& !$tokens[$index - 1]->isGivenKind(T_END_HEREDOC)
&& str_contains($token->getContent(), "\n")
) {
return true;
}
}
return false;
}
public function getMethodAttributes(int $index): array
{
$tokens = $this->tokens;
$token = $tokens[$index];
if (!$token->isGivenKind(T_FUNCTION)) {
throw new \LogicException(sprintf('No T_FUNCTION at given index %d, got "%s".', $index, $token->getName()));
}
$attributes = [
'visibility' => null,
'static' => false,
'abstract' => false,
'final' => false,
];
for ($i = $index; $i >= 0; --$i) {
$tokenIndex = $tokens->getPrevMeaningfulToken($i);
$i = $tokenIndex;
$token = $tokens[$tokenIndex];
if ($token->isGivenKind(T_STATIC)) {
$attributes['static'] = true;
continue;
}
if ($token->isGivenKind(T_FINAL)) {
$attributes['final'] = true;
continue;
}
if ($token->isGivenKind(T_ABSTRACT)) {
$attributes['abstract'] = true;
continue;
}
if ($token->isGivenKind(T_PRIVATE)) {
$attributes['visibility'] = T_PRIVATE;
continue;
}
if ($token->isGivenKind(T_PROTECTED)) {
$attributes['visibility'] = T_PROTECTED;
continue;
}
if ($token->isGivenKind(T_PUBLIC)) {
$attributes['visibility'] = T_PUBLIC;
continue;
}
break;
}
return $attributes;
}
public function isAnonymousClass(int $index): bool
{
if (!$this->tokens[$index]->isClassy()) {
throw new \LogicException(sprintf('No classy token at given index %d.', $index));
}
if (!$this->tokens[$index]->isGivenKind(T_CLASS)) {
return false;
}
$index = $this->tokens->getPrevMeaningfulToken($index);
while ($this->tokens[$index]->isGivenKind(CT::T_ATTRIBUTE_CLOSE)) {
$index = $this->tokens->findBlockStart(Tokens::BLOCK_TYPE_ATTRIBUTE, $index);
$index = $this->tokens->getPrevMeaningfulToken($index);
}
return $this->tokens[$index]->isGivenKind(T_NEW);
}
public function isLambda(int $index): bool
{
if (!$this->tokens[$index]->isGivenKind([T_FUNCTION, T_FN])) {
throw new \LogicException(sprintf('No T_FUNCTION or T_FN at given index %d, got "%s".', $index, $this->tokens[$index]->getName()));
}
$startParenthesisIndex = $this->tokens->getNextMeaningfulToken($index);
$startParenthesisToken = $this->tokens[$startParenthesisIndex];
if ($startParenthesisToken->isGivenKind(CT::T_RETURN_REF)) {
$startParenthesisIndex = $this->tokens->getNextMeaningfulToken($startParenthesisIndex);
$startParenthesisToken = $this->tokens[$startParenthesisIndex];
}
return $startParenthesisToken->equals('(');
}
public function isConstantInvocation(int $index): bool
{
if (!$this->tokens[$index]->isGivenKind(T_STRING)) {
throw new \LogicException(sprintf('No T_STRING at given index %d, got "%s".', $index, $this->tokens[$index]->getName()));
}
$nextIndex = $this->tokens->getNextMeaningfulToken($index);
if (
$this->tokens[$nextIndex]->equalsAny(['(', '{'])
|| $this->tokens[$nextIndex]->isGivenKind([T_AS, T_DOUBLE_COLON, T_ELLIPSIS, T_NS_SEPARATOR, CT::T_RETURN_REF, CT::T_TYPE_ALTERNATION, CT::T_TYPE_INTERSECTION, T_VARIABLE])
) {
return false;
}
$prevIndex = $this->tokens->getPrevMeaningfulToken($index);
if ($this->tokens[$prevIndex]->isGivenKind([T_AS, T_CLASS, T_CONST, T_DOUBLE_COLON, T_FUNCTION, T_GOTO, CT::T_GROUP_IMPORT_BRACE_OPEN, T_INTERFACE, T_TRAIT, CT::T_TYPE_COLON, CT::T_TYPE_ALTERNATION, CT::T_TYPE_INTERSECTION]) || $this->tokens[$prevIndex]->isObjectOperator()) {
return false;
}
while ($this->tokens[$prevIndex]->isGivenKind([CT::T_NAMESPACE_OPERATOR, T_NS_SEPARATOR, T_STRING])) {
$prevIndex = $this->tokens->getPrevMeaningfulToken($prevIndex);
}
if ($this->tokens[$prevIndex]->isGivenKind([CT::T_CONST_IMPORT, T_EXTENDS, CT::T_FUNCTION_IMPORT, T_IMPLEMENTS, T_INSTANCEOF, T_INSTEADOF, T_NAMESPACE, T_NEW, CT::T_NULLABLE_TYPE, CT::T_TYPE_COLON, T_USE, CT::T_USE_TRAIT])) {
return false;
}
if ($this->tokens[$nextIndex]->equals('&') && $this->tokens[$this->tokens->getNextMeaningfulToken($nextIndex)]->isGivenKind(T_VARIABLE)) {
$checkIndex = $this->tokens->getPrevTokenOfKind($prevIndex, [';', '{', '}', [T_FUNCTION], [T_OPEN_TAG], [T_OPEN_TAG_WITH_ECHO]]);
if ($this->tokens[$checkIndex]->isGivenKind(T_FUNCTION)) {
return false;
}
}
if ($this->tokens[$prevIndex]->equals(',')) {
$checkIndex = $prevIndex;
while ($this->tokens[$checkIndex]->equalsAny([',', [T_AS], [CT::T_NAMESPACE_OPERATOR], [T_NS_SEPARATOR], [T_STRING]])) {
$checkIndex = $this->tokens->getPrevMeaningfulToken($checkIndex);
}
if ($this->tokens[$checkIndex]->isGivenKind([T_EXTENDS, CT::T_GROUP_IMPORT_BRACE_OPEN, T_IMPLEMENTS, T_USE, CT::T_USE_TRAIT])) {
return false;
}
}
if ($this->tokens[$prevIndex]->equals('[') && $this->tokens[$nextIndex]->equals(']')) {
$checkToken = $this->tokens[$this->tokens->getNextMeaningfulToken($nextIndex)];
if ($checkToken->equals('"') || $checkToken->isGivenKind([T_CURLY_OPEN, T_DOLLAR_OPEN_CURLY_BRACES, T_ENCAPSED_AND_WHITESPACE, T_VARIABLE])) {
return false;
}
}
if (AttributeAnalyzer::isAttribute($this->tokens, $index)) {
return false;
}
if ($this->tokens[$nextIndex]->equals(':')) {
if (null === $this->gotoLabelAnalyzer) {
$this->gotoLabelAnalyzer = new GotoLabelAnalyzer();
}
if ($this->gotoLabelAnalyzer->belongsToGoToLabel($this->tokens, $nextIndex)) {
return false;
}
}
while ($this->tokens[$prevIndex]->isGivenKind([CT::T_NAMESPACE_OPERATOR, T_NS_SEPARATOR, T_STRING, CT::T_TYPE_ALTERNATION])) {
$prevIndex = $this->tokens->getPrevMeaningfulToken($prevIndex);
}
if ($this->tokens[$prevIndex]->equals('(')) {
$prevPrevIndex = $this->tokens->getPrevMeaningfulToken($prevIndex);
if ($this->tokens[$prevPrevIndex]->isGivenKind(T_CATCH)) {
return false;
}
}
return true;
}
public function isUnarySuccessorOperator(int $index): bool
{
static $allowedPrevToken = [
']',
[T_STRING],
[T_VARIABLE],
[CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE],
[CT::T_DYNAMIC_PROP_BRACE_CLOSE],
[CT::T_DYNAMIC_VAR_BRACE_CLOSE],
];
$tokens = $this->tokens;
$token = $tokens[$index];
if (!$token->isGivenKind([T_INC, T_DEC])) {
return false;
}
$prevToken = $tokens[$tokens->getPrevMeaningfulToken($index)];
return $prevToken->equalsAny($allowedPrevToken);
}
public function isUnaryPredecessorOperator(int $index): bool
{
static $potentialSuccessorOperator = [T_INC, T_DEC];
static $potentialBinaryOperator = ['+', '-', '&', [CT::T_RETURN_REF]];
static $otherOperators;
if (null === $otherOperators) {
$otherOperators = ['!', '~', '@', [T_ELLIPSIS]];
}
static $disallowedPrevTokens;
if (null === $disallowedPrevTokens) {
$disallowedPrevTokens = [
']',
'}',
')',
'"',
'`',
[CT::T_ARRAY_SQUARE_BRACE_CLOSE],
[CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE],
[CT::T_DYNAMIC_PROP_BRACE_CLOSE],
[CT::T_DYNAMIC_VAR_BRACE_CLOSE],
[T_CLASS_C],
[T_CONSTANT_ENCAPSED_STRING],
[T_DEC],
[T_DIR],
[T_DNUMBER],
[T_FILE],
[T_FUNC_C],
[T_INC],
[T_LINE],
[T_LNUMBER],
[T_METHOD_C],
[T_NS_C],
[T_STRING],
[T_TRAIT_C],
[T_VARIABLE],
];
}
$tokens = $this->tokens;
$token = $tokens[$index];
if ($token->isGivenKind($potentialSuccessorOperator)) {
return !$this->isUnarySuccessorOperator($index);
}
if ($token->equalsAny($otherOperators)) {
return true;
}
if (!$token->equalsAny($potentialBinaryOperator)) {
return false;
}
$prevToken = $tokens[$tokens->getPrevMeaningfulToken($index)];
if (!$prevToken->equalsAny($disallowedPrevTokens)) {
return true;
}
if (!$token->equals('&') || !$prevToken->isGivenKind(T_STRING)) {
return false;
}
static $searchTokens = [
';',
'{',
'}',
[T_FUNCTION],
[T_OPEN_TAG],
[T_OPEN_TAG_WITH_ECHO],
];
$prevToken = $tokens[$tokens->getPrevTokenOfKind($index, $searchTokens)];
return $prevToken->isGivenKind(T_FUNCTION);
}
public function isBinaryOperator(int $index): bool
{
static $nonArrayOperators = [
'=' => true,
'*' => true,
'/' => true,
'%' => true,
'<' => true,
'>' => true,
'|' => true,
'^' => true,
'.' => true,
];
static $potentialUnaryNonArrayOperators = [
'+' => true,
'-' => true,
'&' => true,
];
static $arrayOperators;
if (null === $arrayOperators) {
$arrayOperators = [
T_AND_EQUAL => true,
T_BOOLEAN_AND => true,
T_BOOLEAN_OR => true,
T_CONCAT_EQUAL => true,
T_DIV_EQUAL => true,
T_DOUBLE_ARROW => true,
T_IS_EQUAL => true,
T_IS_GREATER_OR_EQUAL => true,
T_IS_IDENTICAL => true,
T_IS_NOT_EQUAL => true,
T_IS_NOT_IDENTICAL => true,
T_IS_SMALLER_OR_EQUAL => true,
T_LOGICAL_AND => true,
T_LOGICAL_OR => true,
T_LOGICAL_XOR => true,
T_MINUS_EQUAL => true,
T_MOD_EQUAL => true,
T_MUL_EQUAL => true,
T_OR_EQUAL => true,
T_PLUS_EQUAL => true,
T_POW => true,
T_POW_EQUAL => true,
T_SL => true,
T_SL_EQUAL => true,
T_SR => true,
T_SR_EQUAL => true,
T_XOR_EQUAL => true,
T_SPACESHIP => true,
T_COALESCE => true,
T_COALESCE_EQUAL => true,
];
}
$tokens = $this->tokens;
$token = $tokens[$index];
if ($token->isGivenKind([T_INLINE_HTML, T_ENCAPSED_AND_WHITESPACE, CT::T_TYPE_INTERSECTION])) {
return false;
}
if (isset($potentialUnaryNonArrayOperators[$token->getContent()])) {
return !$this->isUnaryPredecessorOperator($index);
}
if ($token->isArray()) {
return isset($arrayOperators[$token->getId()]);
}
if (isset($nonArrayOperators[$token->getContent()])) {
return true;
}
return false;
}
public function isWhilePartOfDoWhile(int $index): bool
{
$tokens = $this->tokens;
$token = $tokens[$index];
if (!$token->isGivenKind(T_WHILE)) {
throw new \LogicException(sprintf('No T_WHILE at given index %d, got "%s".', $index, $token->getName()));
}
$endIndex = $tokens->getPrevMeaningfulToken($index);
if (!$tokens[$endIndex]->equals('}')) {
return false;
}
$startIndex = $tokens->findBlockStart(Tokens::BLOCK_TYPE_CURLY_BRACE, $endIndex);
$beforeStartIndex = $tokens->getPrevMeaningfulToken($startIndex);
return $tokens[$beforeStartIndex]->isGivenKind(T_DO);
}
public function isSuperGlobal(int $index): bool
{
static $superNames = [
'$_COOKIE' => true,
'$_ENV' => true,
'$_FILES' => true,
'$_GET' => true,
'$_POST' => true,
'$_REQUEST' => true,
'$_SERVER' => true,
'$_SESSION' => true,
'$GLOBALS' => true,
];
$token = $this->tokens[$index];
if (!$token->isGivenKind(T_VARIABLE)) {
return false;
}
return isset($superNames[strtoupper($token->getContent())]);
}
private function findClassyElements(int $classIndex, int $index): array
{
$elements = [];
$curlyBracesLevel = 0;
$bracesLevel = 0;
++$index;
for ($count = \count($this->tokens); $index < $count; ++$index) {
$token = $this->tokens[$index];
if ($token->isGivenKind(T_ENCAPSED_AND_WHITESPACE)) {
continue;
}
if ($token->isGivenKind(T_CLASS)) {
$nestedClassIndex = $index;
$index = $this->tokens->getNextMeaningfulToken($index);
if ($this->tokens[$index]->equals('(')) {
++$index;
for ($nestedBracesLevel = 1; $index < $count; ++$index) {
$token = $this->tokens[$index];
if ($token->equals('(')) {
++$nestedBracesLevel;
continue;
}
if ($token->equals(')')) {
--$nestedBracesLevel;
if (0 === $nestedBracesLevel) {
[$index, $newElements] = $this->findClassyElements($nestedClassIndex, $index);
$elements += $newElements;
break;
}
continue;
}
if ($token->isGivenKind(T_CLASS)) {
[$index, $newElements] = $this->findClassyElements($index, $index);
$elements += $newElements;
}
}
} else {
[$index, $newElements] = $this->findClassyElements($nestedClassIndex, $nestedClassIndex);
$elements += $newElements;
}
continue;
}
if ($token->equals('(')) {
++$bracesLevel;
continue;
}
if ($token->equals(')')) {
--$bracesLevel;
continue;
}
if ($token->equals('{')) {
++$curlyBracesLevel;
continue;
}
if ($token->equals('}')) {
--$curlyBracesLevel;
if (0 === $curlyBracesLevel) {
break;
}
continue;
}
if (1 !== $curlyBracesLevel || !$token->isArray()) {
continue;
}
if (0 === $bracesLevel && $token->isGivenKind(T_VARIABLE)) {
$elements[$index] = [
'classIndex' => $classIndex,
'token' => $token,
'type' => 'property',
];
continue;
}
if ($token->isGivenKind(T_FUNCTION)) {
$elements[$index] = [
'classIndex' => $classIndex,
'token' => $token,
'type' => 'method',
];
} elseif ($token->isGivenKind(T_CONST)) {
$elements[$index] = [
'classIndex' => $classIndex,
'token' => $token,
'type' => 'const',
];
} elseif ($token->isGivenKind(CT::T_USE_TRAIT)) {
$elements[$index] = [
'classIndex' => $classIndex,
'token' => $token,
'type' => 'trait_import',
];
} elseif ($token->isGivenKind(T_CASE)) {
$elements[$index] = [
'classIndex' => $classIndex,
'token' => $token,
'type' => 'case',
];
}
}
return [$index, $elements];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer;
use PhpCsFixer\Preg;
/**
@extends
*/
class Tokens extends \SplFixedArray
{
public const BLOCK_TYPE_PARENTHESIS_BRACE = 1;
public const BLOCK_TYPE_CURLY_BRACE = 2;
public const BLOCK_TYPE_INDEX_SQUARE_BRACE = 3;
public const BLOCK_TYPE_ARRAY_SQUARE_BRACE = 4;
public const BLOCK_TYPE_DYNAMIC_PROP_BRACE = 5;
public const BLOCK_TYPE_DYNAMIC_VAR_BRACE = 6;
public const BLOCK_TYPE_ARRAY_INDEX_CURLY_BRACE = 7;
public const BLOCK_TYPE_GROUP_IMPORT_BRACE = 8;
public const BLOCK_TYPE_DESTRUCTURING_SQUARE_BRACE = 9;
public const BLOCK_TYPE_BRACE_CLASS_INSTANTIATION = 10;
public const BLOCK_TYPE_ATTRIBUTE = 11;
private static array $cache = [];
private array $blockStartCache = [];
private array $blockEndCache = [];
private ?string $codeHash = null;
private bool $changed = false;
private array $foundTokenKinds = [];
public function __clone()
{
foreach ($this as $key => $val) {
$this[$key] = clone $val;
}
}
public static function clearCache(?string $key = null): void
{
if (null === $key) {
self::$cache = [];
return;
}
unset(self::$cache[$key]);
}
public static function detectBlockType(Token $token): ?array
{
foreach (self::getBlockEdgeDefinitions() as $type => $definition) {
if ($token->equals($definition['start'])) {
return ['type' => $type, 'isStart' => true];
}
if ($token->equals($definition['end'])) {
return ['type' => $type, 'isStart' => false];
}
}
return null;
}
public static function fromArray($array, $saveIndices = null): self
{
$tokens = new self(\count($array));
if ($saveIndices ?? true) {
foreach ($array as $key => $val) {
$tokens[$key] = $val;
}
} else {
$index = 0;
foreach ($array as $val) {
$tokens[$index++] = $val;
}
}
$tokens->generateCode();
$tokens->clearChanged();
return $tokens;
}
public static function fromCode(string $code): self
{
$codeHash = self::calculateCodeHash($code);
if (self::hasCache($codeHash)) {
$tokens = self::getCache($codeHash);
$tokens->generateCode();
if ($codeHash === $tokens->codeHash) {
$tokens->clearEmptyTokens();
$tokens->clearChanged();
return $tokens;
}
}
$tokens = new self();
$tokens->setCode($code);
$tokens->clearChanged();
return $tokens;
}
public static function getBlockEdgeDefinitions(): array
{
$definitions = [
self::BLOCK_TYPE_CURLY_BRACE => [
'start' => '{',
'end' => '}',
],
self::BLOCK_TYPE_PARENTHESIS_BRACE => [
'start' => '(',
'end' => ')',
],
self::BLOCK_TYPE_INDEX_SQUARE_BRACE => [
'start' => '[',
'end' => ']',
],
self::BLOCK_TYPE_ARRAY_SQUARE_BRACE => [
'start' => [CT::T_ARRAY_SQUARE_BRACE_OPEN, '['],
'end' => [CT::T_ARRAY_SQUARE_BRACE_CLOSE, ']'],
],
self::BLOCK_TYPE_DYNAMIC_PROP_BRACE => [
'start' => [CT::T_DYNAMIC_PROP_BRACE_OPEN, '{'],
'end' => [CT::T_DYNAMIC_PROP_BRACE_CLOSE, '}'],
],
self::BLOCK_TYPE_DYNAMIC_VAR_BRACE => [
'start' => [CT::T_DYNAMIC_VAR_BRACE_OPEN, '{'],
'end' => [CT::T_DYNAMIC_VAR_BRACE_CLOSE, '}'],
],
self::BLOCK_TYPE_ARRAY_INDEX_CURLY_BRACE => [
'start' => [CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN, '{'],
'end' => [CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE, '}'],
],
self::BLOCK_TYPE_GROUP_IMPORT_BRACE => [
'start' => [CT::T_GROUP_IMPORT_BRACE_OPEN, '{'],
'end' => [CT::T_GROUP_IMPORT_BRACE_CLOSE, '}'],
],
self::BLOCK_TYPE_DESTRUCTURING_SQUARE_BRACE => [
'start' => [CT::T_DESTRUCTURING_SQUARE_BRACE_OPEN, '['],
'end' => [CT::T_DESTRUCTURING_SQUARE_BRACE_CLOSE, ']'],
],
self::BLOCK_TYPE_BRACE_CLASS_INSTANTIATION => [
'start' => [CT::T_BRACE_CLASS_INSTANTIATION_OPEN, '('],
'end' => [CT::T_BRACE_CLASS_INSTANTIATION_CLOSE, ')'],
],
];
if (\defined('T_ATTRIBUTE')) {
$definitions[self::BLOCK_TYPE_ATTRIBUTE] = [
'start' => [T_ATTRIBUTE, '#['],
'end' => [CT::T_ATTRIBUTE_CLOSE, ']'],
];
}
return $definitions;
}
public function setSize($size): bool
{
if ($this->getSize() !== $size) {
$this->changed = true;
return parent::setSize($size);
}
return true;
}
public function offsetUnset($index): void
{
$this->changed = true;
$this->unregisterFoundToken($this[$index]);
parent::offsetUnset($index);
}
public function offsetSet($index, $newval): void
{
$this->blockStartCache = [];
$this->blockEndCache = [];
if (!isset($this[$index]) || !$this[$index]->equals($newval)) {
$this->changed = true;
if (isset($this[$index])) {
$this->unregisterFoundToken($this[$index]);
}
$this->registerFoundToken($newval);
}
parent::offsetSet($index, $newval);
}
public function clearChanged(): void
{
$this->changed = false;
}
public function clearEmptyTokens(): void
{
$limit = $this->count();
for ($index = 0; $index < $limit; ++$index) {
if ($this->isEmptyAt($index)) {
break;
}
}
if ($limit === $index) {
return;
}
for ($count = $index; $index < $limit; ++$index) {
if (!$this->isEmptyAt($index)) {
parent::offsetSet($count++, $this[$index]);
}
}
$this->blockStartCache = [];
$this->blockEndCache = [];
$this->setSize($count);
}
public function ensureWhitespaceAtIndex(int $index, int $indexOffset, string $whitespace): bool
{
$removeLastCommentLine = static function (self $tokens, int $index, int $indexOffset, string $whitespace): string {
$token = $tokens[$index];
if (1 === $indexOffset && $token->isGivenKind(T_OPEN_TAG)) {
if (str_starts_with($whitespace, "\r\n")) {
$tokens[$index] = new Token([T_OPEN_TAG, rtrim($token->getContent())."\r\n"]);
return \strlen($whitespace) > 2
? substr($whitespace, 2)
: ''
;
}
$tokens[$index] = new Token([T_OPEN_TAG, rtrim($token->getContent()).$whitespace[0]]);
return \strlen($whitespace) > 1
? substr($whitespace, 1)
: ''
;
}
return $whitespace;
};
if ($this[$index]->isWhitespace()) {
$whitespace = $removeLastCommentLine($this, $index - 1, $indexOffset, $whitespace);
if ('' === $whitespace) {
$this->clearAt($index);
} else {
$this[$index] = new Token([T_WHITESPACE, $whitespace]);
}
return false;
}
$whitespace = $removeLastCommentLine($this, $index, $indexOffset, $whitespace);
if ('' === $whitespace) {
return false;
}
$this->insertAt(
$index + $indexOffset,
[new Token([T_WHITESPACE, $whitespace])]
);
return true;
}
public function findBlockEnd(int $type, int $searchIndex): int
{
return $this->findOppositeBlockEdge($type, $searchIndex, true);
}
public function findBlockStart(int $type, int $searchIndex): int
{
return $this->findOppositeBlockEdge($type, $searchIndex, false);
}
public function findGivenKind($possibleKind, int $start = 0, ?int $end = null): array
{
if (null === $end) {
$end = $this->count();
}
$elements = [];
$possibleKinds = (array) $possibleKind;
foreach ($possibleKinds as $kind) {
$elements[$kind] = [];
}
$possibleKinds = array_filter($possibleKinds, fn ($kind): bool => $this->isTokenKindFound($kind));
if (\count($possibleKinds) > 0) {
for ($i = $start; $i < $end; ++$i) {
$token = $this[$i];
if ($token->isGivenKind($possibleKinds)) {
$elements[$token->getId()][$i] = $token;
}
}
}
return \is_array($possibleKind) ? $elements : $elements[$possibleKind];
}
public function generateCode(): string
{
$code = $this->generatePartialCode(0, \count($this) - 1);
$this->changeCodeHash(self::calculateCodeHash($code));
return $code;
}
public function generatePartialCode(int $start, int $end): string
{
$code = '';
for ($i = $start; $i <= $end; ++$i) {
$code .= $this[$i]->getContent();
}
return $code;
}
public function getCodeHash(): string
{
return $this->codeHash;
}
public function getNextNonWhitespace(int $index, ?string $whitespaces = null): ?int
{
return $this->getNonWhitespaceSibling($index, 1, $whitespaces);
}
public function getNextTokenOfKind(int $index, array $tokens = [], bool $caseSensitive = true): ?int
{
return $this->getTokenOfKindSibling($index, 1, $tokens, $caseSensitive);
}
public function getNonWhitespaceSibling(int $index, int $direction, ?string $whitespaces = null): ?int
{
while (true) {
$index += $direction;
if (!$this->offsetExists($index)) {
return null;
}
if (!$this[$index]->isWhitespace($whitespaces)) {
return $index;
}
}
}
public function getPrevNonWhitespace(int $index, ?string $whitespaces = null): ?int
{
return $this->getNonWhitespaceSibling($index, -1, $whitespaces);
}
public function getPrevTokenOfKind(int $index, array $tokens = [], bool $caseSensitive = true): ?int
{
return $this->getTokenOfKindSibling($index, -1, $tokens, $caseSensitive);
}
public function getTokenOfKindSibling(int $index, int $direction, array $tokens = [], bool $caseSensitive = true): ?int
{
$tokens = array_filter($tokens, function ($token): bool {
return $this->isTokenKindFound($this->extractTokenKind($token));
});
if (0 === \count($tokens)) {
return null;
}
while (true) {
$index += $direction;
if (!$this->offsetExists($index)) {
return null;
}
if ($this[$index]->equalsAny($tokens, $caseSensitive)) {
return $index;
}
}
}
public function getTokenNotOfKindSibling(int $index, int $direction, array $tokens = []): ?int
{
return $this->getTokenNotOfKind(
$index,
$direction,
fn (int $a): bool => $this[$a]->equalsAny($tokens),
);
}
public function getTokenNotOfKindsSibling(int $index, int $direction, array $kinds = []): ?int
{
return $this->getTokenNotOfKind(
$index,
$direction,
fn (int $index): bool => $this[$index]->isGivenKind($kinds),
);
}
public function getMeaningfulTokenSibling(int $index, int $direction): ?int
{
return $this->getTokenNotOfKindsSibling(
$index,
$direction,
[T_WHITESPACE, T_COMMENT, T_DOC_COMMENT]
);
}
public function getNonEmptySibling(int $index, int $direction): ?int
{
while (true) {
$index += $direction;
if (!$this->offsetExists($index)) {
return null;
}
if (!$this->isEmptyAt($index)) {
return $index;
}
}
}
public function getNextMeaningfulToken(int $index): ?int
{
return $this->getMeaningfulTokenSibling($index, 1);
}
public function getPrevMeaningfulToken(int $index): ?int
{
return $this->getMeaningfulTokenSibling($index, -1);
}
public function findSequence(array $sequence, int $start = 0, ?int $end = null, $caseSensitive = true): ?array
{
$sequenceCount = \count($sequence);
if (0 === $sequenceCount) {
throw new \InvalidArgumentException('Invalid sequence.');
}
$end = null === $end ? \count($this) - 1 : min($end, \count($this) - 1);
if ($start + $sequenceCount - 1 > $end) {
return null;
}
$nonMeaningFullKind = [T_COMMENT, T_DOC_COMMENT, T_WHITESPACE];
foreach ($sequence as $key => $token) {
if (!$token instanceof Token) {
if (\is_array($token) && !isset($token[1])) {
$token[1] = 'DUMMY';
}
$token = new Token($token);
}
if ($token->isGivenKind($nonMeaningFullKind)) {
throw new \InvalidArgumentException(sprintf('Non-meaningful token at position: "%s".', $key));
}
if ('' === $token->getContent()) {
throw new \InvalidArgumentException(sprintf('Non-meaningful (empty) token at position: "%s".', $key));
}
}
foreach ($sequence as $token) {
if (!$this->isTokenKindFound($this->extractTokenKind($token))) {
return null;
}
}
$key = key($sequence);
$firstCs = Token::isKeyCaseSensitive($caseSensitive, $key);
$firstToken = $sequence[$key];
unset($sequence[$key]);
$index = $start - 1;
while ($index <= $end) {
$index = $this->getNextTokenOfKind($index, [$firstToken], $firstCs);
if (null === $index || $index > $end) {
return null;
}
$result = [$index => $this[$index]];
$currIdx = $index;
foreach ($sequence as $key => $token) {
$currIdx = $this->getNextMeaningfulToken($currIdx);
if (null === $currIdx || $currIdx > $end) {
return null;
}
if (!$this[$currIdx]->equals($token, Token::isKeyCaseSensitive($caseSensitive, $key))) {
continue 2;
}
$result[$currIdx] = $this[$currIdx];
}
if (\count($sequence) < \count($result)) {
return $result;
}
}
return null;
}
public function insertAt(int $index, $items): void
{
$this->insertSlices([$index => $items]);
}
public function insertSlices(array $slices): void
{
$itemsCount = 0;
foreach ($slices as $slice) {
$itemsCount += \is_array($slice) || $slice instanceof self ? \count($slice) : 1;
}
if (0 === $itemsCount) {
return;
}
$oldSize = \count($this);
$this->changed = true;
$this->blockStartCache = [];
$this->blockEndCache = [];
$this->setSize($oldSize + $itemsCount);
krsort($slices);
$farthestSliceIndex = key($slices);
if (!\is_int($farthestSliceIndex) || $farthestSliceIndex > $oldSize) {
throw new \OutOfBoundsException(sprintf('Cannot insert index "%s" outside of collection.', $farthestSliceIndex));
}
$previousSliceIndex = $oldSize;
foreach ($slices as $index => $slice) {
if (!\is_int($index) || $index < 0) {
throw new \OutOfBoundsException(sprintf('Invalid index "%s".', $index));
}
$slice = \is_array($slice) || $slice instanceof self ? $slice : [$slice];
$sliceCount = \count($slice);
for ($i = $previousSliceIndex - 1; $i >= $index; --$i) {
parent::offsetSet($i + $itemsCount, parent::offsetGet($i));
}
$previousSliceIndex = $index;
$itemsCount -= $sliceCount;
foreach ($slice as $indexItem => $item) {
if ('' === $item->getContent()) {
throw new \InvalidArgumentException('Must not add empty token to collection.');
}
$this->registerFoundToken($item);
parent::offsetSet($index + $itemsCount + $indexItem, $item);
}
}
}
public function isChanged(): bool
{
return $this->changed;
}
public function isEmptyAt(int $index): bool
{
$token = $this[$index];
return null === $token->getId() && '' === $token->getContent();
}
public function clearAt(int $index): void
{
$this[$index] = new Token('');
}
public function overrideRange(int $indexStart, int $indexEnd, iterable $items): void
{
$indexToChange = $indexEnd - $indexStart + 1;
$itemsCount = \count($items);
if ($itemsCount > $indexToChange) {
$placeholders = [];
while ($itemsCount > $indexToChange) {
$placeholders[] = new Token('__PLACEHOLDER__');
++$indexToChange;
}
$this->insertAt($indexEnd + 1, $placeholders);
}
foreach ($items as $itemIndex => $item) {
$this[$indexStart + $itemIndex] = $item;
}
if ($itemsCount < $indexToChange) {
$this->clearRange($indexStart + $itemsCount, $indexEnd);
}
}
public function removeLeadingWhitespace(int $index, ?string $whitespaces = null): void
{
$this->removeWhitespaceSafely($index, -1, $whitespaces);
}
public function removeTrailingWhitespace(int $index, ?string $whitespaces = null): void
{
$this->removeWhitespaceSafely($index, 1, $whitespaces);
}
public function setCode(string $code): void
{
if ($code === $this->generateCode()) {
return;
}
$this->setSize(0);
$tokens = token_get_all($code, TOKEN_PARSE);
$this->setSize(\count($tokens));
foreach ($tokens as $index => $token) {
$this[$index] = new Token($token);
}
$this->applyTransformers();
$this->foundTokenKinds = [];
foreach ($this as $token) {
$this->registerFoundToken($token);
}
if (\PHP_VERSION_ID < 80000) {
$this->rewind();
}
$this->changeCodeHash(self::calculateCodeHash($code));
$this->changed = true;
}
public function toJson(): string
{
$output = new \SplFixedArray(\count($this));
foreach ($this as $index => $token) {
$output[$index] = $token->toArray();
}
if (\PHP_VERSION_ID < 80000) {
$this->rewind();
}
return json_encode($output, JSON_PRETTY_PRINT | JSON_NUMERIC_CHECK);
}
public function isAllTokenKindsFound(array $tokenKinds): bool
{
foreach ($tokenKinds as $tokenKind) {
if (empty($this->foundTokenKinds[$tokenKind])) {
return false;
}
}
return true;
}
public function isAnyTokenKindsFound(array $tokenKinds): bool
{
foreach ($tokenKinds as $tokenKind) {
if (!empty($this->foundTokenKinds[$tokenKind])) {
return true;
}
}
return false;
}
public function isTokenKindFound($tokenKind): bool
{
return !empty($this->foundTokenKinds[$tokenKind]);
}
public function countTokenKind($tokenKind): int
{
return $this->foundTokenKinds[$tokenKind] ?? 0;
}
public function clearRange(int $indexStart, int $indexEnd): void
{
for ($i = $indexStart; $i <= $indexEnd; ++$i) {
$this->clearAt($i);
}
}
public function isMonolithicPhp(): bool
{
if (0 === $this->count()) {
return false;
}
if ($this->countTokenKind(T_INLINE_HTML) > 1) {
return false;
}
if (1 === $this->countTokenKind(T_INLINE_HTML)) {
return 1 === Preg::match('/^#!.+$/', $this[0]->getContent());
}
return 1 === ($this->countTokenKind(T_OPEN_TAG) + $this->countTokenKind(T_OPEN_TAG_WITH_ECHO));
}
public function isPartialCodeMultiline(int $start, int $end): bool
{
for ($i = $start; $i <= $end; ++$i) {
if (str_contains($this[$i]->getContent(), "\n")) {
return true;
}
}
return false;
}
public function hasAlternativeSyntax(): bool
{
return $this->isAnyTokenKindsFound([
T_ENDDECLARE,
T_ENDFOR,
T_ENDFOREACH,
T_ENDIF,
T_ENDSWITCH,
T_ENDWHILE,
]);
}
public function clearTokenAndMergeSurroundingWhitespace(int $index): void
{
$count = \count($this);
$this->clearAt($index);
if ($index === $count - 1) {
return;
}
$nextIndex = $this->getNonEmptySibling($index, 1);
if (null === $nextIndex || !$this[$nextIndex]->isWhitespace()) {
return;
}
$prevIndex = $this->getNonEmptySibling($index, -1);
if ($this[$prevIndex]->isWhitespace()) {
$this[$prevIndex] = new Token([T_WHITESPACE, $this[$prevIndex]->getContent().$this[$nextIndex]->getContent()]);
} elseif ($this->isEmptyAt($prevIndex + 1)) {
$this[$prevIndex + 1] = new Token([T_WHITESPACE, $this[$nextIndex]->getContent()]);
}
$this->clearAt($nextIndex);
}
protected function applyTransformers(): void
{
$transformers = Transformers::createSingleton();
$transformers->transform($this);
}
private function removeWhitespaceSafely(int $index, int $direction, ?string $whitespaces = null): void
{
$whitespaceIndex = $this->getNonEmptySibling($index, $direction);
if (isset($this[$whitespaceIndex]) && $this[$whitespaceIndex]->isWhitespace()) {
$newContent = '';
$tokenToCheck = $this[$whitespaceIndex];
if (isset($this[$whitespaceIndex - 1]) && $this[$whitespaceIndex - 1]->isComment() && !str_starts_with($this[$whitespaceIndex - 1]->getContent(), '/*')) {
[, $newContent, $whitespacesToCheck] = Preg::split('/^(\R)/', $this[$whitespaceIndex]->getContent(), -1, PREG_SPLIT_DELIM_CAPTURE);
if ('' === $whitespacesToCheck) {
return;
}
$tokenToCheck = new Token([T_WHITESPACE, $whitespacesToCheck]);
}
if (!$tokenToCheck->isWhitespace($whitespaces)) {
return;
}
if ('' === $newContent) {
$this->clearAt($whitespaceIndex);
} else {
$this[$whitespaceIndex] = new Token([T_WHITESPACE, $newContent]);
}
}
}
private function findOppositeBlockEdge(int $type, int $searchIndex, bool $findEnd): int
{
$blockEdgeDefinitions = self::getBlockEdgeDefinitions();
if (!isset($blockEdgeDefinitions[$type])) {
throw new \InvalidArgumentException(sprintf('Invalid param type: "%s".', $type));
}
if ($findEnd && isset($this->blockStartCache[$searchIndex])) {
return $this->blockStartCache[$searchIndex];
}
if (!$findEnd && isset($this->blockEndCache[$searchIndex])) {
return $this->blockEndCache[$searchIndex];
}
$startEdge = $blockEdgeDefinitions[$type]['start'];
$endEdge = $blockEdgeDefinitions[$type]['end'];
$startIndex = $searchIndex;
$endIndex = $this->count() - 1;
$indexOffset = 1;
if (!$findEnd) {
[$startEdge, $endEdge] = [$endEdge, $startEdge];
$indexOffset = -1;
$endIndex = 0;
}
if (!$this[$startIndex]->equals($startEdge)) {
throw new \InvalidArgumentException(sprintf('Invalid param $startIndex - not a proper block "%s".', $findEnd ? 'start' : 'end'));
}
$blockLevel = 0;
for ($index = $startIndex; $index !== $endIndex; $index += $indexOffset) {
$token = $this[$index];
if ($token->equals($startEdge)) {
++$blockLevel;
continue;
}
if ($token->equals($endEdge)) {
--$blockLevel;
if (0 === $blockLevel) {
break;
}
}
}
if (!$this[$index]->equals($endEdge)) {
throw new \UnexpectedValueException(sprintf('Missing block "%s".', $findEnd ? 'end' : 'start'));
}
if ($startIndex < $index) {
$this->blockStartCache[$startIndex] = $index;
$this->blockEndCache[$index] = $startIndex;
} else {
$this->blockStartCache[$index] = $startIndex;
$this->blockEndCache[$startIndex] = $index;
}
return $index;
}
private static function calculateCodeHash(string $code): string
{
return CodeHasher::calculateCodeHash($code);
}
private static function getCache(string $key): self
{
if (!self::hasCache($key)) {
throw new \OutOfBoundsException(sprintf('Unknown cache key: "%s".', $key));
}
return self::$cache[$key];
}
private static function hasCache(string $key): bool
{
return isset(self::$cache[$key]);
}
private static function setCache(string $key, self $value): void
{
self::$cache[$key] = $value;
}
private function changeCodeHash(string $codeHash): void
{
if (null !== $this->codeHash) {
self::clearCache($this->codeHash);
}
$this->codeHash = $codeHash;
self::setCache($this->codeHash, $this);
}
private function registerFoundToken($token): void
{
$tokenKind = $token instanceof Token
? ($token->isArray() ? $token->getId() : $token->getContent())
: (\is_array($token) ? $token[0] : $token)
;
$this->foundTokenKinds[$tokenKind] ??= 0;
++$this->foundTokenKinds[$tokenKind];
}
private function unregisterFoundToken($token): void
{
$tokenKind = $token instanceof Token
? ($token->isArray() ? $token->getId() : $token->getContent())
: (\is_array($token) ? $token[0] : $token)
;
if (!isset($this->foundTokenKinds[$tokenKind])) {
return;
}
--$this->foundTokenKinds[$tokenKind];
}
private function extractTokenKind($token)
{
return $token instanceof Token
? ($token->isArray() ? $token->getId() : $token->getContent())
: (\is_array($token) ? $token[0] : $token)
;
}
private function getTokenNotOfKind(int $index, int $direction, callable $filter): ?int
{
while (true) {
$index += $direction;
if (!$this->offsetExists($index)) {
return null;
}
if ($this->isEmptyAt($index) || $filter($index)) {
continue;
}
return $index;
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer;
final class Token
{
private string $content;
private ?int $id = null;
private bool $isArray;
private bool $changed = false;
public function __construct($token)
{
if (\is_array($token)) {
if (!\is_int($token[0])) {
throw new \InvalidArgumentException(sprintf(
'Id must be an int, got "%s".',
get_debug_type($token[0])
));
}
if (!\is_string($token[1])) {
throw new \InvalidArgumentException(sprintf(
'Content must be a string, got "%s".',
get_debug_type($token[1])
));
}
if ('' === $token[1]) {
throw new \InvalidArgumentException('Cannot set empty content for id-based Token.');
}
$this->isArray = true;
$this->id = $token[0];
$this->content = $token[1];
} elseif (\is_string($token)) {
$this->isArray = false;
$this->content = $token;
} else {
throw new \InvalidArgumentException(sprintf('Cannot recognize input value as valid Token prototype, got "%s".', get_debug_type($token)));
}
}
public static function getCastTokenKinds(): array
{
static $castTokens = [T_ARRAY_CAST, T_BOOL_CAST, T_DOUBLE_CAST, T_INT_CAST, T_OBJECT_CAST, T_STRING_CAST, T_UNSET_CAST];
return $castTokens;
}
public static function getClassyTokenKinds(): array
{
static $classTokens;
if (null === $classTokens) {
$classTokens = [T_CLASS, T_TRAIT, T_INTERFACE];
if (\defined('T_ENUM')) {
$classTokens[] = T_ENUM;
}
}
return $classTokens;
}
public static function getObjectOperatorKinds(): array
{
static $objectOperators = null;
if (null === $objectOperators) {
$objectOperators = [T_OBJECT_OPERATOR];
if (\defined('T_NULLSAFE_OBJECT_OPERATOR')) {
$objectOperators[] = T_NULLSAFE_OBJECT_OPERATOR;
}
}
return $objectOperators;
}
public function equals($other, bool $caseSensitive = true): bool
{
if (\defined('T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG')) {
if ('&' === $other) {
return '&' === $this->content && (null === $this->id || $this->isGivenKind([T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG, T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG]));
}
if (null === $this->id && '&' === $this->content) {
return $other instanceof self && '&' === $other->content && (null === $other->id || $other->isGivenKind([T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG, T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG]));
}
}
if ($other instanceof self) {
if (!$other->isArray) {
$otherPrototype = $other->content;
} else {
$otherPrototype = [
$other->id,
$other->content,
];
}
} else {
$otherPrototype = $other;
}
if ($this->isArray !== \is_array($otherPrototype)) {
return false;
}
if (!$this->isArray) {
return $this->content === $otherPrototype;
}
if ($this->id !== $otherPrototype[0]) {
return false;
}
if (isset($otherPrototype[1])) {
if ($caseSensitive) {
if ($this->content !== $otherPrototype[1]) {
return false;
}
} elseif (0 !== strcasecmp($this->content, $otherPrototype[1])) {
return false;
}
}
unset($otherPrototype[0], $otherPrototype[1]);
/**
@phpstan-ignore-next-line
*/
return empty($otherPrototype);
}
public function equalsAny(array $others, bool $caseSensitive = true): bool
{
foreach ($others as $other) {
if ($this->equals($other, $caseSensitive)) {
return true;
}
}
return false;
}
public static function isKeyCaseSensitive($caseSensitive, int $key): bool
{
if (\is_array($caseSensitive)) {
return $caseSensitive[$key] ?? true;
}
return $caseSensitive;
}
public function getPrototype()
{
if (!$this->isArray) {
return $this->content;
}
return [
$this->id,
$this->content,
];
}
public function getContent(): string
{
return $this->content;
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
if (null === $this->id) {
return null;
}
return self::getNameForId($this->id);
}
public static function getNameForId(int $id): ?string
{
if (CT::has($id)) {
return CT::getName($id);
}
$name = token_name($id);
return 'UNKNOWN' === $name ? null : $name;
}
public static function getKeywords(): array
{
static $keywords = null;
if (null === $keywords) {
$keywords = self::getTokenKindsForNames(['T_ABSTRACT', 'T_ARRAY', 'T_AS', 'T_BREAK', 'T_CALLABLE', 'T_CASE',
'T_CATCH', 'T_CLASS', 'T_CLONE', 'T_CONST', 'T_CONTINUE', 'T_DECLARE', 'T_DEFAULT', 'T_DO',
'T_ECHO', 'T_ELSE', 'T_ELSEIF', 'T_EMPTY', 'T_ENDDECLARE', 'T_ENDFOR', 'T_ENDFOREACH',
'T_ENDIF', 'T_ENDSWITCH', 'T_ENDWHILE', 'T_EVAL', 'T_EXIT', 'T_EXTENDS', 'T_FINAL',
'T_FINALLY', 'T_FN', 'T_FOR', 'T_FOREACH', 'T_FUNCTION', 'T_GLOBAL', 'T_GOTO', 'T_HALT_COMPILER',
'T_IF', 'T_IMPLEMENTS', 'T_INCLUDE', 'T_INCLUDE_ONCE', 'T_INSTANCEOF', 'T_INSTEADOF',
'T_INTERFACE', 'T_ISSET', 'T_LIST', 'T_LOGICAL_AND', 'T_LOGICAL_OR', 'T_LOGICAL_XOR',
'T_NAMESPACE', 'T_MATCH', 'T_NEW', 'T_PRINT', 'T_PRIVATE', 'T_PROTECTED', 'T_PUBLIC', 'T_REQUIRE',
'T_REQUIRE_ONCE', 'T_RETURN', 'T_STATIC', 'T_SWITCH', 'T_THROW', 'T_TRAIT', 'T_TRY',
'T_UNSET', 'T_USE', 'T_VAR', 'T_WHILE', 'T_YIELD', 'T_YIELD_FROM', 'T_READONLY', 'T_ENUM',
]) + [
CT::T_ARRAY_TYPEHINT => CT::T_ARRAY_TYPEHINT,
CT::T_CLASS_CONSTANT => CT::T_CLASS_CONSTANT,
CT::T_CONST_IMPORT => CT::T_CONST_IMPORT,
CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE => CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE,
CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED => CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED,
CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC => CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC,
CT::T_FUNCTION_IMPORT => CT::T_FUNCTION_IMPORT,
CT::T_NAMESPACE_OPERATOR => CT::T_NAMESPACE_OPERATOR,
CT::T_USE_LAMBDA => CT::T_USE_LAMBDA,
CT::T_USE_TRAIT => CT::T_USE_TRAIT,
];
}
return $keywords;
}
public static function getMagicConstants(): array
{
static $magicConstants = null;
if (null === $magicConstants) {
$magicConstants = self::getTokenKindsForNames(['T_CLASS_C', 'T_DIR', 'T_FILE', 'T_FUNC_C', 'T_LINE', 'T_METHOD_C', 'T_NS_C', 'T_TRAIT_C']);
}
return $magicConstants;
}
public function isArray(): bool
{
return $this->isArray;
}
public function isCast(): bool
{
return $this->isGivenKind(self::getCastTokenKinds());
}
public function isClassy(): bool
{
return $this->isGivenKind(self::getClassyTokenKinds());
}
public function isComment(): bool
{
static $commentTokens = [T_COMMENT, T_DOC_COMMENT];
return $this->isGivenKind($commentTokens);
}
public function isObjectOperator(): bool
{
return $this->isGivenKind(self::getObjectOperatorKinds());
}
public function isGivenKind($possibleKind): bool
{
return $this->isArray && (\is_array($possibleKind) ? \in_array($this->id, $possibleKind, true) : $this->id === $possibleKind);
}
public function isKeyword(): bool
{
$keywords = static::getKeywords();
return $this->isArray && isset($keywords[$this->id]);
}
public function isNativeConstant(): bool
{
static $nativeConstantStrings = ['true', 'false', 'null'];
return $this->isArray && \in_array(strtolower($this->content), $nativeConstantStrings, true);
}
public function isMagicConstant(): bool
{
$magicConstants = static::getMagicConstants();
return $this->isArray && isset($magicConstants[$this->id]);
}
public function isWhitespace(?string $whitespaces = " \t\n\r\0\x0B"): bool
{
if (null === $whitespaces) {
$whitespaces = " \t\n\r\0\x0B";
}
if ($this->isArray && !$this->isGivenKind(T_WHITESPACE)) {
return false;
}
return '' === trim($this->content, $whitespaces);
}
public function toArray(): array
{
return [
'id' => $this->id,
'name' => $this->getName(),
'content' => $this->content,
'isArray' => $this->isArray,
'changed' => $this->changed,
];
}
public function toJson(): string
{
$jsonResult = json_encode($this->toArray(), JSON_PRETTY_PRINT | JSON_NUMERIC_CHECK);
if (JSON_ERROR_NONE !== json_last_error()) {
$jsonResult = json_encode(
[
'errorDescription' => 'Cannot encode Tokens to JSON.',
'rawErrorMessage' => json_last_error_msg(),
],
JSON_PRETTY_PRINT | JSON_NUMERIC_CHECK
);
}
return $jsonResult;
}
private static function getTokenKindsForNames(array $tokenNames): array
{
$keywords = [];
foreach ($tokenNames as $keywordName) {
if (\defined($keywordName)) {
$keyword = \constant($keywordName);
$keywords[$keyword] = $keyword;
}
}
return $keywords;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer;
final class CodeHasher
{
private function __construct()
{
}
public static function calculateCodeHash(string $code): string
{
return md5($code);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NameQualifiedTransformer extends AbstractTransformer
{
public function getPriority(): int
{
return 1;
}
public function getRequiredPhpVersionId(): int
{
return 80000;
}
public function process(Tokens $tokens, Token $token, int $index): void
{
if ($token->isGivenKind([T_NAME_QUALIFIED, T_NAME_FULLY_QUALIFIED])) {
$this->transformQualified($tokens, $token, $index);
} elseif ($token->isGivenKind(T_NAME_RELATIVE)) {
$this->transformRelative($tokens, $token, $index);
}
}
public function getCustomTokens(): array
{
return [];
}
private function transformQualified(Tokens $tokens, Token $token, int $index): void
{
$parts = explode('\\', $token->getContent());
$newTokens = [];
if ('' === $parts[0]) {
$newTokens[] = new Token([T_NS_SEPARATOR, '\\']);
array_shift($parts);
}
foreach ($parts as $part) {
$newTokens[] = new Token([T_STRING, $part]);
$newTokens[] = new Token([T_NS_SEPARATOR, '\\']);
}
array_pop($newTokens);
$tokens->overrideRange($index, $index, $newTokens);
}
private function transformRelative(Tokens $tokens, Token $token, int $index): void
{
$parts = explode('\\', $token->getContent());
$newTokens = [
new Token([T_NAMESPACE, array_shift($parts)]),
new Token([T_NS_SEPARATOR, '\\']),
];
foreach ($parts as $part) {
$newTokens[] = new Token([T_STRING, $part]);
$newTokens[] = new Token([T_NS_SEPARATOR, '\\']);
}
array_pop($newTokens);
$tokens->overrideRange($index, $index, $newTokens);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class ArrayTypehintTransformer extends AbstractTransformer
{
public function getRequiredPhpVersionId(): int
{
return 50000;
}
public function process(Tokens $tokens, Token $token, int $index): void
{
if (!$token->isGivenKind(T_ARRAY)) {
return;
}
$nextIndex = $tokens->getNextMeaningfulToken($index);
$nextToken = $tokens[$nextIndex];
if (!$nextToken->equals('(')) {
$tokens[$index] = new Token([CT::T_ARRAY_TYPEHINT, $token->getContent()]);
}
}
public function getCustomTokens(): array
{
return [CT::T_ARRAY_TYPEHINT];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class ImportTransformer extends AbstractTransformer
{
public function getPriority(): int
{
return -1;
}
public function getRequiredPhpVersionId(): int
{
return 50600;
}
public function process(Tokens $tokens, Token $token, int $index): void
{
if (!$token->isGivenKind([T_CONST, T_FUNCTION])) {
return;
}
$prevToken = $tokens[$tokens->getPrevMeaningfulToken($index)];
if (!$prevToken->isGivenKind(T_USE)) {
$nextToken = $tokens[$tokens->getNextTokenOfKind($index, ['=', '(', [CT::T_RETURN_REF], [CT::T_GROUP_IMPORT_BRACE_CLOSE]])];
if (!$nextToken->isGivenKind(CT::T_GROUP_IMPORT_BRACE_CLOSE)) {
return;
}
}
$tokens[$index] = new Token([
$token->isGivenKind(T_FUNCTION) ? CT::T_FUNCTION_IMPORT : CT::T_CONST_IMPORT,
$token->getContent(),
]);
}
public function getCustomTokens(): array
{
return [CT::T_CONST_IMPORT, CT::T_FUNCTION_IMPORT];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class SquareBraceTransformer extends AbstractTransformer
{
public function getPriority(): int
{
return -1;
}
public function getRequiredPhpVersionId(): int
{
return 50000;
}
public function process(Tokens $tokens, Token $token, int $index): void
{
if ($this->isArrayDestructing($tokens, $index)) {
$this->transformIntoDestructuringSquareBrace($tokens, $index);
return;
}
if ($this->isShortArray($tokens, $index)) {
$this->transformIntoArraySquareBrace($tokens, $index);
}
}
public function getCustomTokens(): array
{
return [
CT::T_ARRAY_SQUARE_BRACE_OPEN,
CT::T_ARRAY_SQUARE_BRACE_CLOSE,
CT::T_DESTRUCTURING_SQUARE_BRACE_OPEN,
CT::T_DESTRUCTURING_SQUARE_BRACE_CLOSE,
];
}
private function transformIntoArraySquareBrace(Tokens $tokens, int $index): void
{
$endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_INDEX_SQUARE_BRACE, $index);
$tokens[$index] = new Token([CT::T_ARRAY_SQUARE_BRACE_OPEN, '[']);
$tokens[$endIndex] = new Token([CT::T_ARRAY_SQUARE_BRACE_CLOSE, ']']);
}
private function transformIntoDestructuringSquareBrace(Tokens $tokens, int $index): void
{
$endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_INDEX_SQUARE_BRACE, $index);
$tokens[$index] = new Token([CT::T_DESTRUCTURING_SQUARE_BRACE_OPEN, '[']);
$tokens[$endIndex] = new Token([CT::T_DESTRUCTURING_SQUARE_BRACE_CLOSE, ']']);
$previousMeaningfulIndex = $index;
$index = $tokens->getNextMeaningfulToken($index);
while ($index < $endIndex) {
if ($tokens[$index]->equals('[') && $tokens[$previousMeaningfulIndex]->equalsAny([[CT::T_DESTRUCTURING_SQUARE_BRACE_OPEN], ','])) {
$tokens[$tokens->findBlockEnd(Tokens::BLOCK_TYPE_INDEX_SQUARE_BRACE, $index)] = new Token([CT::T_DESTRUCTURING_SQUARE_BRACE_CLOSE, ']']);
$tokens[$index] = new Token([CT::T_DESTRUCTURING_SQUARE_BRACE_OPEN, '[']);
}
$previousMeaningfulIndex = $index;
$index = $tokens->getNextMeaningfulToken($index);
}
}
private function isShortArray(Tokens $tokens, int $index): bool
{
if (!$tokens[$index]->equals('[')) {
return false;
}
static $disallowedPrevTokens = [
')',
']',
'}',
'"',
[T_CONSTANT_ENCAPSED_STRING],
[T_STRING],
[T_STRING_VARNAME],
[T_VARIABLE],
[CT::T_ARRAY_SQUARE_BRACE_CLOSE],
[CT::T_DYNAMIC_PROP_BRACE_CLOSE],
[CT::T_DYNAMIC_VAR_BRACE_CLOSE],
[CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE],
];
$prevToken = $tokens[$tokens->getPrevMeaningfulToken($index)];
if ($prevToken->equalsAny($disallowedPrevTokens)) {
return false;
}
$nextToken = $tokens[$tokens->getNextMeaningfulToken($index)];
if ($nextToken->equals(']')) {
return true;
}
return !$this->isArrayDestructing($tokens, $index);
}
private function isArrayDestructing(Tokens $tokens, int $index): bool
{
if (!$tokens[$index]->equals('[')) {
return false;
}
static $disallowedPrevTokens = [
')',
']',
'"',
[T_CONSTANT_ENCAPSED_STRING],
[T_STRING],
[T_STRING_VARNAME],
[T_VARIABLE],
[CT::T_ARRAY_SQUARE_BRACE_CLOSE],
[CT::T_DYNAMIC_PROP_BRACE_CLOSE],
[CT::T_DYNAMIC_VAR_BRACE_CLOSE],
[CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE],
];
$prevIndex = $tokens->getPrevMeaningfulToken($index);
$prevToken = $tokens[$prevIndex];
if ($prevToken->equalsAny($disallowedPrevTokens)) {
return false;
}
if ($prevToken->isGivenKind(T_AS)) {
return true;
}
if ($prevToken->isGivenKind(T_DOUBLE_ARROW)) {
$variableIndex = $tokens->getPrevMeaningfulToken($prevIndex);
if (!$tokens[$variableIndex]->isGivenKind(T_VARIABLE)) {
return false;
}
$prevVariableIndex = $tokens->getPrevMeaningfulToken($variableIndex);
if ($tokens[$prevVariableIndex]->isGivenKind(T_AS)) {
return true;
}
}
$type = Tokens::detectBlockType($tokens[$index]);
$end = $tokens->findBlockEnd($type['type'], $index);
$nextToken = $tokens[$tokens->getNextMeaningfulToken($end)];
return $nextToken->equals('=');
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class TypeColonTransformer extends AbstractTransformer
{
public function getPriority(): int
{
return -10;
}
public function getRequiredPhpVersionId(): int
{
return 70000;
}
public function process(Tokens $tokens, Token $token, int $index): void
{
if (!$token->equals(':')) {
return;
}
$endIndex = $tokens->getPrevMeaningfulToken($index);
if (
\defined('T_ENUM')
&& $tokens[$tokens->getPrevMeaningfulToken($endIndex)]->isGivenKind(T_ENUM)
) {
$tokens[$index] = new Token([CT::T_TYPE_COLON, ':']);
return;
}
if (!$tokens[$endIndex]->equals(')')) {
return;
}
$startIndex = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $endIndex);
$prevIndex = $tokens->getPrevMeaningfulToken($startIndex);
$prevToken = $tokens[$prevIndex];
if ($prevToken->isGivenKind(T_STRING)) {
$prevIndex = $tokens->getPrevMeaningfulToken($prevIndex);
$prevToken = $tokens[$prevIndex];
}
if ($prevToken->isGivenKind([T_FUNCTION, CT::T_RETURN_REF, CT::T_USE_LAMBDA, T_FN])) {
$tokens[$index] = new Token([CT::T_TYPE_COLON, ':']);
}
}
public function getCustomTokens(): array
{
return [CT::T_TYPE_COLON];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class ReturnRefTransformer extends AbstractTransformer
{
public function getRequiredPhpVersionId(): int
{
return 50000;
}
public function process(Tokens $tokens, Token $token, int $index): void
{
if ($token->equals('&') && $tokens[$tokens->getPrevMeaningfulToken($index)]->isGivenKind([T_FUNCTION, T_FN])) {
$tokens[$index] = new Token([CT::T_RETURN_REF, '&']);
}
}
public function getCustomTokens(): array
{
return [CT::T_RETURN_REF];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class WhitespacyCommentTransformer extends AbstractTransformer
{
public function getRequiredPhpVersionId(): int
{
return 50000;
}
public function process(Tokens $tokens, Token $token, int $index): void
{
if (!$token->isComment()) {
return;
}
$content = $token->getContent();
$trimmedContent = rtrim($content);
if ($content === $trimmedContent) {
return;
}
$whitespaces = substr($content, \strlen($trimmedContent));
$tokens[$index] = new Token([$token->getId(), $trimmedContent]);
if (isset($tokens[$index + 1]) && $tokens[$index + 1]->isWhitespace()) {
$tokens[$index + 1] = new Token([T_WHITESPACE, $whitespaces.$tokens[$index + 1]->getContent()]);
} else {
$tokens->insertAt($index + 1, new Token([T_WHITESPACE, $whitespaces]));
}
}
public function getCustomTokens(): array
{
return [];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class FirstClassCallableTransformer extends AbstractTransformer
{
public function getRequiredPhpVersionId(): int
{
return 80100;
}
public function process(Tokens $tokens, Token $token, int $index): void
{
if (
$token->isGivenKind(T_ELLIPSIS)
&& $tokens[$tokens->getPrevMeaningfulToken($index)]->equals('(')
&& $tokens[$tokens->getNextMeaningfulToken($index)]->equals(')')
) {
$tokens[$index] = new Token([CT::T_FIRST_CLASS_CALLABLE, '...']);
}
}
public function getCustomTokens(): array
{
return [
CT::T_FIRST_CLASS_CALLABLE,
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NamespaceOperatorTransformer extends AbstractTransformer
{
public function getRequiredPhpVersionId(): int
{
return 50300;
}
public function process(Tokens $tokens, Token $token, int $index): void
{
if (!$token->isGivenKind(T_NAMESPACE)) {
return;
}
$nextIndex = $tokens->getNextMeaningfulToken($index);
if ($tokens[$nextIndex]->isGivenKind(T_NS_SEPARATOR)) {
$tokens[$index] = new Token([CT::T_NAMESPACE_OPERATOR, $token->getContent()]);
}
}
public function getCustomTokens(): array
{
return [CT::T_NAMESPACE_OPERATOR];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NullableTypeTransformer extends AbstractTransformer
{
public function getPriority(): int
{
return -20;
}
public function getRequiredPhpVersionId(): int
{
return 70100;
}
public function process(Tokens $tokens, Token $token, int $index): void
{
if (!$token->equals('?')) {
return;
}
static $types;
if (null === $types) {
$types = [
'(',
',',
[CT::T_TYPE_COLON],
[CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC],
[CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED],
[CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE],
[CT::T_ATTRIBUTE_CLOSE],
[T_PRIVATE],
[T_PROTECTED],
[T_PUBLIC],
[T_VAR],
[T_STATIC],
];
if (\defined('T_READONLY')) {
$types[] = [T_READONLY];
}
}
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$prevIndex]->equalsAny($types)) {
$tokens[$index] = new Token([CT::T_NULLABLE_TYPE, '?']);
}
}
public function getCustomTokens(): array
{
return [CT::T_NULLABLE_TYPE];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTypeTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class TypeIntersectionTransformer extends AbstractTypeTransformer
{
public function getPriority(): int
{
return -15;
}
public function getRequiredPhpVersionId(): int
{
return 80100;
}
public function process(Tokens $tokens, Token $token, int $index): void
{
$this->doProcess($tokens, $index, [T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG, '&']);
}
public function getCustomTokens(): array
{
return [CT::T_TYPE_INTERSECTION];
}
protected function replaceToken(Tokens $tokens, int $index): void
{
$tokens[$index] = new Token([CT::T_TYPE_INTERSECTION, '&']);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class CurlyBraceTransformer extends AbstractTransformer
{
public function getRequiredPhpVersionId(): int
{
return 50000;
}
public function process(Tokens $tokens, Token $token, int $index): void
{
$this->transformIntoCurlyCloseBrace($tokens, $token, $index);
$this->transformIntoDollarCloseBrace($tokens, $token, $index);
$this->transformIntoDynamicPropBraces($tokens, $token, $index);
$this->transformIntoDynamicVarBraces($tokens, $token, $index);
$this->transformIntoCurlyIndexBraces($tokens, $token, $index);
$this->transformIntoGroupUseBraces($tokens, $token, $index);
}
public function getCustomTokens(): array
{
return [
CT::T_CURLY_CLOSE,
CT::T_DOLLAR_CLOSE_CURLY_BRACES,
CT::T_DYNAMIC_PROP_BRACE_OPEN,
CT::T_DYNAMIC_PROP_BRACE_CLOSE,
CT::T_DYNAMIC_VAR_BRACE_OPEN,
CT::T_DYNAMIC_VAR_BRACE_CLOSE,
CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN,
CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE,
CT::T_GROUP_IMPORT_BRACE_OPEN,
CT::T_GROUP_IMPORT_BRACE_CLOSE,
];
}
private function transformIntoCurlyCloseBrace(Tokens $tokens, Token $token, int $index): void
{
if (!$token->isGivenKind(T_CURLY_OPEN)) {
return;
}
$level = 1;
do {
++$index;
if ($tokens[$index]->equals('{') || $tokens[$index]->isGivenKind(T_CURLY_OPEN)) {
++$level;
} elseif ($tokens[$index]->equals('}')) {
--$level;
}
} while (0 < $level);
$tokens[$index] = new Token([CT::T_CURLY_CLOSE, '}']);
}
private function transformIntoDollarCloseBrace(Tokens $tokens, Token $token, int $index): void
{
if ($token->isGivenKind(T_DOLLAR_OPEN_CURLY_BRACES)) {
$nextIndex = $tokens->getNextTokenOfKind($index, ['}']);
$tokens[$nextIndex] = new Token([CT::T_DOLLAR_CLOSE_CURLY_BRACES, '}']);
}
}
private function transformIntoDynamicPropBraces(Tokens $tokens, Token $token, int $index): void
{
if (!$token->isObjectOperator()) {
return;
}
if (!$tokens[$index + 1]->equals('{')) {
return;
}
$openIndex = $index + 1;
$closeIndex = $this->naivelyFindCurlyBlockEnd($tokens, $openIndex);
$tokens[$openIndex] = new Token([CT::T_DYNAMIC_PROP_BRACE_OPEN, '{']);
$tokens[$closeIndex] = new Token([CT::T_DYNAMIC_PROP_BRACE_CLOSE, '}']);
}
private function transformIntoDynamicVarBraces(Tokens $tokens, Token $token, int $index): void
{
if (!$token->equals('$')) {
return;
}
$openIndex = $tokens->getNextMeaningfulToken($index);
if (null === $openIndex) {
return;
}
$openToken = $tokens[$openIndex];
if (!$openToken->equals('{')) {
return;
}
$closeIndex = $this->naivelyFindCurlyBlockEnd($tokens, $openIndex);
$tokens[$openIndex] = new Token([CT::T_DYNAMIC_VAR_BRACE_OPEN, '{']);
$tokens[$closeIndex] = new Token([CT::T_DYNAMIC_VAR_BRACE_CLOSE, '}']);
}
private function transformIntoCurlyIndexBraces(Tokens $tokens, Token $token, int $index): void
{
if (!$token->equals('{')) {
return;
}
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if (!$tokens[$prevIndex]->equalsAny([
[T_STRING],
[T_VARIABLE],
[CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE],
']',
')',
])) {
return;
}
if (
$tokens[$prevIndex]->isGivenKind(T_STRING)
&& !$tokens[$tokens->getPrevMeaningfulToken($prevIndex)]->isObjectOperator()
) {
return;
}
if (
$tokens[$prevIndex]->equals(')')
&& !$tokens[$tokens->getPrevMeaningfulToken(
$tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $prevIndex)
)]->isGivenKind(T_ARRAY)
) {
return;
}
$closeIndex = $this->naivelyFindCurlyBlockEnd($tokens, $index);
$tokens[$index] = new Token([CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN, '{']);
$tokens[$closeIndex] = new Token([CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE, '}']);
}
private function transformIntoGroupUseBraces(Tokens $tokens, Token $token, int $index): void
{
if (!$token->equals('{')) {
return;
}
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if (!$tokens[$prevIndex]->isGivenKind(T_NS_SEPARATOR)) {
return;
}
$closeIndex = $this->naivelyFindCurlyBlockEnd($tokens, $index);
$tokens[$index] = new Token([CT::T_GROUP_IMPORT_BRACE_OPEN, '{']);
$tokens[$closeIndex] = new Token([CT::T_GROUP_IMPORT_BRACE_CLOSE, '}']);
}
private function naivelyFindCurlyBlockEnd(Tokens $tokens, int $startIndex): int
{
if (!$tokens->offsetExists($startIndex)) {
throw new \OutOfBoundsException(sprintf('Unavailable index: "%s".', $startIndex));
}
if ('{' !== $tokens[$startIndex]->getContent()) {
throw new \InvalidArgumentException(sprintf('Wrong start index: "%s".', $startIndex));
}
$blockLevel = 1;
$endIndex = $tokens->count() - 1;
for ($index = $startIndex + 1; $index !== $endIndex; ++$index) {
$token = $tokens[$index];
if ('{' === $token->getContent()) {
++$blockLevel;
continue;
}
if ('}' === $token->getContent()) {
--$blockLevel;
if (0 === $blockLevel) {
if (!$token->equals('}')) {
throw new \UnexpectedValueException(sprintf('Detected block end for index: "%s" was already transformed into other token type: "%s".', $startIndex, $token->getName()));
}
return $index;
}
}
}
throw new \UnexpectedValueException(sprintf('Missing block end for index: "%s".', $startIndex));
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTypeTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class TypeAlternationTransformer extends AbstractTypeTransformer
{
public function getPriority(): int
{
return -15;
}
public function getRequiredPhpVersionId(): int
{
return 70100;
}
public function process(Tokens $tokens, Token $token, int $index): void
{
$this->doProcess($tokens, $index, '|');
}
public function getCustomTokens(): array
{
return [CT::T_TYPE_ALTERNATION];
}
protected function replaceToken(Tokens $tokens, int $index): void
{
$tokens[$index] = new Token([CT::T_TYPE_ALTERNATION, '|']);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class AttributeTransformer extends AbstractTransformer
{
public function getPriority(): int
{
return 200;
}
public function getRequiredPhpVersionId(): int
{
return 80000;
}
public function process(Tokens $tokens, Token $token, int $index): void
{
if (!$tokens[$index]->isGivenKind(T_ATTRIBUTE)) {
return;
}
$level = 1;
do {
++$index;
if ($tokens[$index]->equals('[')) {
++$level;
} elseif ($tokens[$index]->equals(']')) {
--$level;
}
} while (0 < $level);
$tokens[$index] = new Token([CT::T_ATTRIBUTE_CLOSE, ']']);
}
public function getCustomTokens(): array
{
return [
CT::T_ATTRIBUTE_CLOSE,
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class BraceClassInstantiationTransformer extends AbstractTransformer
{
public function getPriority(): int
{
return -2;
}
public function getRequiredPhpVersionId(): int
{
return 50000;
}
public function process(Tokens $tokens, Token $token, int $index): void
{
if (!$tokens[$index]->equals('(') || !$tokens[$tokens->getNextMeaningfulToken($index)]->isGivenKind(T_NEW)) {
return;
}
if ($tokens[$tokens->getPrevMeaningfulToken($index)]->equalsAny([
']',
[CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE],
[CT::T_ARRAY_SQUARE_BRACE_CLOSE],
[T_ARRAY],
[T_CLASS],
[T_ELSEIF],
[T_FOR],
[T_FOREACH],
[T_IF],
[T_STATIC],
[T_STRING],
[T_SWITCH],
[T_VARIABLE],
[T_WHILE],
])) {
return;
}
$closeIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index);
$tokens[$index] = new Token([CT::T_BRACE_CLASS_INSTANTIATION_OPEN, '(']);
$tokens[$closeIndex] = new Token([CT::T_BRACE_CLASS_INSTANTIATION_CLOSE, ')']);
}
public function getCustomTokens(): array
{
return [CT::T_BRACE_CLASS_INSTANTIATION_OPEN, CT::T_BRACE_CLASS_INSTANTIATION_CLOSE];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class ClassConstantTransformer extends AbstractTransformer
{
public function getRequiredPhpVersionId(): int
{
return 50500;
}
public function process(Tokens $tokens, Token $token, int $index): void
{
if (!$token->equalsAny([
[T_CLASS, 'class'],
[T_STRING, 'class'],
], false)) {
return;
}
$prevIndex = $tokens->getPrevMeaningfulToken($index);
$prevToken = $tokens[$prevIndex];
if ($prevToken->isGivenKind(T_DOUBLE_COLON)) {
$tokens[$index] = new Token([CT::T_CLASS_CONSTANT, $token->getContent()]);
}
}
public function getCustomTokens(): array
{
return [CT::T_CLASS_CONSTANT];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class UseTransformer extends AbstractTransformer
{
public function getPriority(): int
{
return -5;
}
public function getRequiredPhpVersionId(): int
{
return 50300;
}
public function process(Tokens $tokens, Token $token, int $index): void
{
if ($token->isGivenKind(T_USE) && $this->isUseForLambda($tokens, $index)) {
$tokens[$index] = new Token([CT::T_USE_LAMBDA, $token->getContent()]);
return;
}
$classTypes = [T_TRAIT];
if (\defined('T_ENUM')) {
$classTypes[] = T_ENUM;
}
if ($token->isGivenKind(T_CLASS)) {
if ($tokens[$tokens->getPrevMeaningfulToken($index)]->isGivenKind(T_DOUBLE_COLON)) {
return;
}
} elseif (!$token->isGivenKind($classTypes)) {
return;
}
$index = $tokens->getNextTokenOfKind($index, ['{']);
$innerLimit = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $index);
while ($index < $innerLimit) {
$token = $tokens[++$index];
if (!$token->isGivenKind(T_USE)) {
continue;
}
if ($this->isUseForLambda($tokens, $index)) {
$tokens[$index] = new Token([CT::T_USE_LAMBDA, $token->getContent()]);
} else {
$tokens[$index] = new Token([CT::T_USE_TRAIT, $token->getContent()]);
}
}
}
public function getCustomTokens(): array
{
return [CT::T_USE_TRAIT, CT::T_USE_LAMBDA];
}
private function isUseForLambda(Tokens $tokens, int $index): bool
{
$nextToken = $tokens[$tokens->getNextMeaningfulToken($index)];
return $nextToken->equals('(');
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class NamedArgumentTransformer extends AbstractTransformer
{
public function getPriority(): int
{
return -15;
}
public function getRequiredPhpVersionId(): int
{
return 80000;
}
public function process(Tokens $tokens, Token $token, int $index): void
{
if (!$tokens[$index]->equals(':')) {
return;
}
$stringIndex = $tokens->getPrevMeaningfulToken($index);
if (!$tokens[$stringIndex]->isGivenKind(T_STRING)) {
return;
}
$preStringIndex = $tokens->getPrevMeaningfulToken($stringIndex);
if (!$tokens[$preStringIndex]->equalsAny([',', '('])) {
return;
}
$tokens[$stringIndex] = new Token([CT::T_NAMED_ARGUMENT_NAME, $tokens[$stringIndex]->getContent()]);
$tokens[$index] = new Token([CT::T_NAMED_ARGUMENT_COLON, ':']);
}
public function getCustomTokens(): array
{
return [
CT::T_NAMED_ARGUMENT_COLON,
CT::T_NAMED_ARGUMENT_NAME,
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class ConstructorPromotionTransformer extends AbstractTransformer
{
public function getRequiredPhpVersionId(): int
{
return 80000;
}
public function process(Tokens $tokens, Token $token, int $index): void
{
if (!$tokens[$index]->isGivenKind(T_FUNCTION)) {
return;
}
$index = $tokens->getNextMeaningfulToken($index);
if (!$tokens[$index]->isGivenKind(T_STRING) || '__construct' !== strtolower($tokens[$index]->getContent())) {
return;
}
$openIndex = $tokens->getNextMeaningfulToken($index);
$closeIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openIndex);
for ($index = $openIndex; $index < $closeIndex; ++$index) {
if ($tokens[$index]->isGivenKind(T_PUBLIC)) {
$tokens[$index] = new Token([CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC, $tokens[$index]->getContent()]);
} elseif ($tokens[$index]->isGivenKind(T_PROTECTED)) {
$tokens[$index] = new Token([CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED, $tokens[$index]->getContent()]);
} elseif ($tokens[$index]->isGivenKind(T_PRIVATE)) {
$tokens[$index] = new Token([CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE, $tokens[$index]->getContent()]);
}
}
}
public function getCustomTokens(): array
{
return [
CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC,
CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED,
CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE,
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer;
abstract class AbstractTypeTransformer extends AbstractTransformer
{
protected function doProcess(Tokens $tokens, int $index, $originalToken): void
{
if (!$tokens[$index]->equals($originalToken)) {
return;
}
$prevIndex = $this->getPreviousTokenCandidate($tokens, $index);
$prevToken = $tokens[$prevIndex];
if ($prevToken->isGivenKind([
CT::T_TYPE_COLON,
CT::T_TYPE_ALTERNATION,
CT::T_TYPE_INTERSECTION,
T_STATIC, T_VAR, T_PUBLIC, T_PROTECTED, T_PRIVATE,
CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE, CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED, CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC,
])) {
$this->replaceToken($tokens, $index);
return;
}
if (\defined('T_READONLY') && $prevToken->isGivenKind(T_READONLY)) {
$this->replaceToken($tokens, $index);
return;
}
if (!$prevToken->equalsAny(['(', ','])) {
return;
}
$prevPrevTokenIndex = $tokens->getPrevMeaningfulToken($prevIndex);
if ($tokens[$prevPrevTokenIndex]->isGivenKind(T_CATCH)) {
$this->replaceToken($tokens, $index);
return;
}
$functionKinds = [[T_FUNCTION], [T_FN]];
$functionIndex = $tokens->getPrevTokenOfKind($prevIndex, $functionKinds);
if (null === $functionIndex) {
return;
}
$braceOpenIndex = $tokens->getNextTokenOfKind($functionIndex, ['(']);
$braceCloseIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $braceOpenIndex);
if ($braceCloseIndex < $index) {
return;
}
$this->replaceToken($tokens, $index);
}
abstract protected function replaceToken(Tokens $tokens, int $index): void;
private function getPreviousTokenCandidate(Tokens $tokens, int $index): int
{
$candidateIndex = $tokens->getTokenNotOfKindsSibling($index, -1, [T_CALLABLE, T_NS_SEPARATOR, T_STRING, CT::T_ARRAY_TYPEHINT, T_WHITESPACE, T_COMMENT, T_DOC_COMMENT]);
return $tokens[$candidateIndex]->isGivenKind(CT::T_ATTRIBUTE_CLOSE)
? $this->getPreviousTokenCandidate($tokens, $tokens->getPrevTokenOfKind($index, [[T_ATTRIBUTE]]))
: $candidateIndex
;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;
final class Transformers
{
private array $items = [];
private function __construct()
{
$this->registerBuiltInTransformers();
usort($this->items, static function (TransformerInterface $a, TransformerInterface $b): int {
return $b->getPriority() <=> $a->getPriority();
});
}
public static function createSingleton(): self
{
static $instance = null;
if (!$instance) {
$instance = new self();
}
return $instance;
}
public function transform(Tokens $tokens): void
{
foreach ($this->items as $transformer) {
foreach ($tokens as $index => $token) {
$transformer->process($tokens, $token, $index);
}
}
}
private function registerTransformer(TransformerInterface $transformer): void
{
if (\PHP_VERSION_ID >= $transformer->getRequiredPhpVersionId()) {
$this->items[] = $transformer;
}
}
private function registerBuiltInTransformers(): void
{
static $registered = false;
if ($registered) {
return;
}
$registered = true;
foreach ($this->findBuiltInTransformers() as $transformer) {
$this->registerTransformer($transformer);
}
}
private function findBuiltInTransformers(): iterable
{
foreach (Finder::create()->files()->in(__DIR__.'/Transformer') as $file) {
$relativeNamespace = $file->getRelativePath();
$class = __NAMESPACE__.'\\Transformer\\'.($relativeNamespace ? $relativeNamespace.'\\' : '').$file->getBasename('.php');
yield new $class();
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer;
interface TransformerInterface
{
public function getCustomTokens(): array;
public function getName(): string;
public function getPriority(): int;
public function getRequiredPhpVersionId(): int;
public function process(Tokens $tokens, Token $token, int $index): void;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer;
use PhpCsFixer\Utils;
abstract class AbstractTransformer implements TransformerInterface
{
public function getName(): string
{
$nameParts = explode('\\', static::class);
$name = substr(end($nameParts), 0, -\strlen('Transformer'));
return Utils::camelCaseToUnderscore($name);
}
public function getPriority(): int
{
return 0;
}
abstract public function getCustomTokens(): array;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Analyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Tokens;
final class ReferenceAnalyzer
{
public function isReference(Tokens $tokens, int $index): bool
{
if ($tokens[$index]->isGivenKind(CT::T_RETURN_REF)) {
return true;
}
if (!$tokens[$index]->equals('&')) {
return false;
}
$index = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$index]->equalsAny(['=', [T_AS], [T_CALLABLE], [T_DOUBLE_ARROW], [CT::T_ARRAY_TYPEHINT]])) {
return true;
}
if ($tokens[$index]->isGivenKind(T_STRING)) {
$index = $tokens->getPrevMeaningfulToken($index);
}
return $tokens[$index]->equalsAny(['(', ',', [T_NS_SEPARATOR], [CT::T_NULLABLE_TYPE]]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Analyzer\Analysis;
final class MatchAnalysis extends AbstractControlCaseStructuresAnalysis
{
private ?DefaultAnalysis $defaultAnalysis;
public function __construct(int $index, int $open, int $close, ?DefaultAnalysis $defaultAnalysis)
{
parent::__construct($index, $open, $close);
$this->defaultAnalysis = $defaultAnalysis;
}
public function getDefaultAnalysis(): ?DefaultAnalysis
{
return $this->defaultAnalysis;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Analyzer\Analysis;
final class SwitchAnalysis extends AbstractControlCaseStructuresAnalysis
{
private array $cases;
private ?DefaultAnalysis $defaultAnalysis;
public function __construct(int $index, int $open, int $close, array $cases, ?DefaultAnalysis $defaultAnalysis)
{
parent::__construct($index, $open, $close);
$this->cases = $cases;
$this->defaultAnalysis = $defaultAnalysis;
}
public function getCases(): array
{
return $this->cases;
}
public function getDefaultAnalysis(): ?DefaultAnalysis
{
return $this->defaultAnalysis;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Analyzer\Analysis;
final class NamespaceUseAnalysis implements StartEndTokenAwareAnalysis
{
public const TYPE_CLASS = 1;
public const TYPE_FUNCTION = 2;
public const TYPE_CONSTANT = 3;
private string $fullName;
private string $shortName;
private bool $isAliased;
private int $startIndex;
private int $endIndex;
private int $type;
public function __construct(string $fullName, string $shortName, bool $isAliased, int $startIndex, int $endIndex, int $type)
{
$this->fullName = $fullName;
$this->shortName = $shortName;
$this->isAliased = $isAliased;
$this->startIndex = $startIndex;
$this->endIndex = $endIndex;
$this->type = $type;
}
public function getFullName(): string
{
return $this->fullName;
}
public function getShortName(): string
{
return $this->shortName;
}
public function isAliased(): bool
{
return $this->isAliased;
}
public function getStartIndex(): int
{
return $this->startIndex;
}
public function getEndIndex(): int
{
return $this->endIndex;
}
public function getType(): int
{
return $this->type;
}
public function isClass(): bool
{
return self::TYPE_CLASS === $this->type;
}
public function isFunction(): bool
{
return self::TYPE_FUNCTION === $this->type;
}
public function isConstant(): bool
{
return self::TYPE_CONSTANT === $this->type;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Analyzer\Analysis;
abstract class AbstractControlCaseStructuresAnalysis
{
private int $index;
private int $open;
private int $close;
public function __construct(int $index, int $open, int $close)
{
$this->index = $index;
$this->open = $open;
$this->close = $close;
}
public function getIndex(): int
{
return $this->index;
}
public function getOpenIndex(): int
{
return $this->open;
}
public function getCloseIndex(): int
{
return $this->close;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Analyzer\Analysis;
interface StartEndTokenAwareAnalysis
{
public function getStartIndex(): int;
public function getEndIndex(): int;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Analyzer\Analysis;
final class TypeAnalysis implements StartEndTokenAwareAnalysis
{
private static array $reservedTypes = [
'array',
'bool',
'callable',
'float',
'int',
'iterable',
'mixed',
'never',
'numeric',
'object',
'resource',
'self',
'string',
'void',
];
private string $name;
private int $startIndex;
private int $endIndex;
private bool $nullable;
public function __construct(string $name, int $startIndex, int $endIndex)
{
$this->name = $name;
$this->nullable = false;
if (str_starts_with($name, '?')) {
$this->name = substr($name, 1);
$this->nullable = true;
}
$this->startIndex = $startIndex;
$this->endIndex = $endIndex;
}
public function getName(): string
{
return $this->name;
}
public function getStartIndex(): int
{
return $this->startIndex;
}
public function getEndIndex(): int
{
return $this->endIndex;
}
public function isReservedType(): bool
{
return \in_array($this->name, self::$reservedTypes, true);
}
public function isNullable(): bool
{
return $this->nullable;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Analyzer\Analysis;
final class DefaultAnalysis
{
private int $index;
private int $colonIndex;
public function __construct(int $index, int $colonIndex)
{
$this->index = $index;
$this->colonIndex = $colonIndex;
}
public function getIndex(): int
{
return $this->index;
}
public function getColonIndex(): int
{
return $this->colonIndex;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Analyzer\Analysis;
final class NamespaceAnalysis implements StartEndTokenAwareAnalysis
{
private string $fullName;
private string $shortName;
private int $startIndex;
private int $endIndex;
private int $scopeStartIndex;
private int $scopeEndIndex;
public function __construct(string $fullName, string $shortName, int $startIndex, int $endIndex, int $scopeStartIndex, int $scopeEndIndex)
{
$this->fullName = $fullName;
$this->shortName = $shortName;
$this->startIndex = $startIndex;
$this->endIndex = $endIndex;
$this->scopeStartIndex = $scopeStartIndex;
$this->scopeEndIndex = $scopeEndIndex;
}
public function getFullName(): string
{
return $this->fullName;
}
public function getShortName(): string
{
return $this->shortName;
}
public function getStartIndex(): int
{
return $this->startIndex;
}
public function getEndIndex(): int
{
return $this->endIndex;
}
public function getScopeStartIndex(): int
{
return $this->scopeStartIndex;
}
public function getScopeEndIndex(): int
{
return $this->scopeEndIndex;
}
public function isGlobalNamespace(): bool
{
return '' === $this->getFullName();
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Analyzer\Analysis;
final class EnumAnalysis extends AbstractControlCaseStructuresAnalysis
{
private array $cases;
public function __construct(int $index, int $open, int $close, array $cases)
{
parent::__construct($index, $open, $close);
$this->cases = $cases;
}
public function getCases(): array
{
return $this->cases;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Analyzer\Analysis;
final class CaseAnalysis
{
private int $index;
private int $colonIndex;
public function __construct(int $index, int $colonIndex)
{
$this->index = $index;
$this->colonIndex = $colonIndex;
}
public function getIndex(): int
{
return $this->index;
}
public function getColonIndex(): int
{
return $this->colonIndex;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Analyzer\Analysis;
final class ArgumentAnalysis
{
private string $name;
private int $nameIndex;
private ?string $default;
private ?TypeAnalysis $typeAnalysis;
public function __construct(string $name, int $nameIndex, ?string $default, ?TypeAnalysis $typeAnalysis = null)
{
$this->name = $name;
$this->nameIndex = $nameIndex;
$this->default = $default ?: null;
$this->typeAnalysis = $typeAnalysis ?: null;
}
public function getDefault(): ?string
{
return $this->default;
}
public function hasDefault(): bool
{
return null !== $this->default;
}
public function getName(): string
{
return $this->name;
}
public function getNameIndex(): int
{
return $this->nameIndex;
}
public function getTypeAnalysis(): ?TypeAnalysis
{
return $this->typeAnalysis;
}
public function hasTypeAnalysis(): bool
{
return null !== $this->typeAnalysis;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Analyzer;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\AbstractControlCaseStructuresAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\CaseAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\DefaultAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\EnumAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\MatchAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\SwitchAnalysis;
use PhpCsFixer\Tokenizer\Tokens;
final class ControlCaseStructuresAnalyzer
{
public static function findControlStructures(Tokens $tokens, array $types): \Generator
{
if (\count($types) < 1) {
return;
}
$typesWithCaseOrDefault = self::getTypesWithCaseOrDefault();
foreach ($types as $type) {
if (!\in_array($type, $typesWithCaseOrDefault, true)) {
throw new \InvalidArgumentException(sprintf('Unexpected type "%d".', $type));
}
}
if (!$tokens->isAnyTokenKindsFound($types)) {
return;
}
$depth = -1;
$stack = [];
$isTypeOfInterest = false;
foreach ($tokens as $index => $token) {
if ($token->isGivenKind($typesWithCaseOrDefault)) {
++$depth;
$stack[$depth] = [
'kind' => $token->getId(),
'index' => $index,
'brace_count' => 0,
'cases' => [],
'default' => null,
'alternative_syntax' => false,
];
$isTypeOfInterest = \in_array($stack[$depth]['kind'], $types, true);
if ($token->isGivenKind(T_SWITCH)) {
$index = $tokens->getNextMeaningfulToken($index);
$index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index);
$stack[$depth]['open'] = $tokens->getNextMeaningfulToken($index);
$stack[$depth]['alternative_syntax'] = $tokens[$stack[$depth]['open']]->equals(':');
} elseif (\defined('T_MATCH') && $token->isGivenKind(T_MATCH)) {
$index = $tokens->getNextMeaningfulToken($index);
$index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index);
$stack[$depth]['open'] = $tokens->getNextMeaningfulToken($index);
} elseif (\defined('T_ENUM') && $token->isGivenKind(T_ENUM)) {
$stack[$depth]['open'] = $tokens->getNextTokenOfKind($index, ['{']);
}
continue;
}
if ($depth < 0) {
continue;
}
if ($token->equals('{')) {
++$stack[$depth]['brace_count'];
continue;
}
if ($token->equals('}')) {
--$stack[$depth]['brace_count'];
if (0 === $stack[$depth]['brace_count']) {
if ($stack[$depth]['alternative_syntax']) {
continue;
}
if ($isTypeOfInterest) {
$stack[$depth]['end'] = $index;
yield $stack[$depth]['index'] => self::buildControlCaseStructureAnalysis($stack[$depth]);
}
array_pop($stack);
--$depth;
if ($depth < -1) {
throw new \RuntimeException('Analysis depth count failure.');
}
if (isset($stack[$depth]['kind'])) {
$isTypeOfInterest = \in_array($stack[$depth]['kind'], $types, true);
}
}
continue;
}
if ($tokens[$index]->isGivenKind(T_ENDSWITCH)) {
if (!$stack[$depth]['alternative_syntax']) {
throw new \RuntimeException('Analysis syntax failure, unexpected "T_ENDSWITCH".');
}
if (T_SWITCH !== $stack[$depth]['kind']) {
throw new \RuntimeException('Analysis type failure, unexpected "T_ENDSWITCH".');
}
if (0 !== $stack[$depth]['brace_count']) {
throw new \RuntimeException('Analysis count failure, unexpected "T_ENDSWITCH".');
}
$index = $tokens->getNextTokenOfKind($index, [';', [T_CLOSE_TAG]]);
if ($isTypeOfInterest) {
$stack[$depth]['end'] = $index;
yield $stack[$depth]['index'] => self::buildControlCaseStructureAnalysis($stack[$depth]);
}
array_pop($stack);
--$depth;
if ($depth < -1) {
throw new \RuntimeException('Analysis depth count failure ("T_ENDSWITCH").');
}
if (isset($stack[$depth]['kind'])) {
$isTypeOfInterest = \in_array($stack[$depth]['kind'], $types, true);
}
}
if (!$isTypeOfInterest) {
continue;
}
if ($token->isGivenKind(T_CASE)) {
$stack[$depth]['cases'][] = ['index' => $index, 'open' => self::findCaseOpen($tokens, $stack[$depth]['kind'], $index)];
} elseif ($token->isGivenKind(T_DEFAULT)) {
if (null !== $stack[$depth]['default']) {
throw new \RuntimeException('Analysis multiple "default" found.');
}
$stack[$depth]['default'] = ['index' => $index, 'open' => self::findDefaultOpen($tokens, $stack[$depth]['kind'], $index)];
}
}
}
private static function buildControlCaseStructureAnalysis(array $analysis): AbstractControlCaseStructuresAnalysis
{
$default = null === $analysis['default']
? null
: new DefaultAnalysis($analysis['default']['index'], $analysis['default']['open'])
;
$cases = [];
foreach ($analysis['cases'] as $case) {
$cases[$case['index']] = new CaseAnalysis($case['index'], $case['open']);
}
sort($cases);
if (T_SWITCH === $analysis['kind']) {
return new SwitchAnalysis(
$analysis['index'],
$analysis['open'],
$analysis['end'],
$cases,
$default
);
}
if (\defined('T_ENUM') && T_ENUM === $analysis['kind']) {
return new EnumAnalysis(
$analysis['index'],
$analysis['open'],
$analysis['end'],
$cases
);
}
if (\defined('T_MATCH') && T_MATCH === $analysis['kind']) {
return new MatchAnalysis(
$analysis['index'],
$analysis['open'],
$analysis['end'],
$default
);
}
throw new \InvalidArgumentException(sprintf('Unexpected type "%d".', $analysis['kind']));
}
private static function findCaseOpen(Tokens $tokens, int $kind, int $index): int
{
if (T_SWITCH === $kind) {
$ternariesCount = 0;
do {
if ($tokens[$index]->equalsAny(['(', '{'])) {
$type = Tokens::detectBlockType($tokens[$index]);
$index = $tokens->findBlockEnd($type['type'], $index);
continue;
}
if ($tokens[$index]->equals('?')) {
++$ternariesCount;
continue;
}
if ($tokens[$index]->equalsAny([':', ';'])) {
if (0 === $ternariesCount) {
break;
}
--$ternariesCount;
}
} while (++$index);
return $index;
}
if (\defined('T_ENUM') && T_ENUM === $kind) {
return $tokens->getNextTokenOfKind($index, ['=', ';']);
}
throw new \InvalidArgumentException(sprintf('Unexpected case for type "%d".', $kind));
}
private static function findDefaultOpen(Tokens $tokens, int $kind, int $index): int
{
if (T_SWITCH === $kind) {
return $tokens->getNextTokenOfKind($index, [':', ';']);
}
if (\defined('T_MATCH') && T_MATCH === $kind) {
return $tokens->getNextTokenOfKind($index, [[T_DOUBLE_ARROW]]);
}
throw new \InvalidArgumentException(sprintf('Unexpected default for type "%d".', $kind));
}
private static function getTypesWithCaseOrDefault(): array
{
$supportedTypes = [T_SWITCH];
if (\defined('T_MATCH')) {
$supportedTypes[] = T_MATCH;
}
if (\defined('T_ENUM')) {
$supportedTypes[] = T_ENUM;
}
return $supportedTypes;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Analyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class BlocksAnalyzer
{
public function isBlock(Tokens $tokens, ?int $openIndex, ?int $closeIndex): bool
{
if (null === $openIndex || null === $closeIndex) {
return false;
}
if (!$tokens->offsetExists($openIndex)) {
return false;
}
if (!$tokens->offsetExists($closeIndex)) {
return false;
}
$blockType = $this->getBlockType($tokens[$openIndex]);
if (null === $blockType) {
return false;
}
return $closeIndex === $tokens->findBlockEnd($blockType, $openIndex);
}
private function getBlockType(Token $token): ?int
{
foreach (Tokens::getBlockEdgeDefinitions() as $blockType => $definition) {
if ($token->equals($definition['start'])) {
return $blockType;
}
}
return null;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Analyzer;
use PhpCsFixer\Tokenizer\Tokens;
final class GotoLabelAnalyzer
{
public function belongsToGoToLabel(Tokens $tokens, int $index): bool
{
if (!$tokens[$index]->equals(':')) {
return false;
}
$prevMeaningfulTokenIndex = $tokens->getPrevMeaningfulToken($index);
if (!$tokens[$prevMeaningfulTokenIndex]->isGivenKind(T_STRING)) {
return false;
}
$prevMeaningfulTokenIndex = $tokens->getPrevMeaningfulToken($prevMeaningfulTokenIndex);
return $tokens[$prevMeaningfulTokenIndex]->equalsAny([':', ';', '{', '}', [T_OPEN_TAG]]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Analyzer;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\ArgumentAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceUseAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\TypeAnalysis;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class FunctionsAnalyzer
{
private array $functionsAnalysis = ['tokens' => '', 'imports' => [], 'declarations' => []];
public function isGlobalFunctionCall(Tokens $tokens, int $index): bool
{
if (!$tokens[$index]->isGivenKind(T_STRING)) {
return false;
}
$nextIndex = $tokens->getNextMeaningfulToken($index);
if (!$tokens[$nextIndex]->equals('(')) {
return false;
}
$previousIsNamespaceSeparator = false;
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$prevIndex]->isGivenKind(T_NS_SEPARATOR)) {
$previousIsNamespaceSeparator = true;
$prevIndex = $tokens->getPrevMeaningfulToken($prevIndex);
}
$possibleKind = array_merge([T_DOUBLE_COLON, T_FUNCTION, CT::T_NAMESPACE_OPERATOR, T_NEW, CT::T_RETURN_REF, T_STRING], Token::getObjectOperatorKinds());
if (\defined('T_ATTRIBUTE')) {
$possibleKind[] = T_ATTRIBUTE;
}
if ($tokens[$prevIndex]->isGivenKind($possibleKind)) {
return false;
}
if ($previousIsNamespaceSeparator) {
return true;
}
if ($tokens[$tokens->getNextMeaningfulToken($nextIndex)]->isGivenKind(CT::T_FIRST_CLASS_CALLABLE)) {
return false;
}
if ($tokens->isChanged() || $tokens->getCodeHash() !== $this->functionsAnalysis['tokens']) {
$this->buildFunctionsAnalysis($tokens);
}
$namespaceAnalyzer = new NamespacesAnalyzer();
$declarations = $namespaceAnalyzer->getDeclarations($tokens);
$scopeStartIndex = 0;
$scopeEndIndex = \count($tokens) - 1;
$inGlobalNamespace = false;
foreach ($declarations as $declaration) {
$scopeStartIndex = $declaration->getScopeStartIndex();
$scopeEndIndex = $declaration->getScopeEndIndex();
if ($index >= $scopeStartIndex && $index <= $scopeEndIndex) {
$inGlobalNamespace = $declaration->isGlobalNamespace();
break;
}
}
$call = strtolower($tokens[$index]->getContent());
if (!$inGlobalNamespace) {
foreach ($this->functionsAnalysis['declarations'] as $functionNameIndex) {
if ($functionNameIndex < $scopeStartIndex || $functionNameIndex > $scopeEndIndex) {
continue;
}
if (strtolower($tokens[$functionNameIndex]->getContent()) === $call) {
return false;
}
}
}
foreach ($this->functionsAnalysis['imports'] as $functionUse) {
if ($functionUse->getStartIndex() < $scopeStartIndex || $functionUse->getEndIndex() > $scopeEndIndex) {
continue;
}
if ($call !== strtolower($functionUse->getShortName())) {
continue;
}
return $functionUse->getShortName() === ltrim($functionUse->getFullName(), '\\');
}
if (AttributeAnalyzer::isAttribute($tokens, $index)) {
return false;
}
return true;
}
public function getFunctionArguments(Tokens $tokens, int $functionIndex): array
{
$argumentsStart = $tokens->getNextTokenOfKind($functionIndex, ['(']);
$argumentsEnd = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $argumentsStart);
$argumentAnalyzer = new ArgumentsAnalyzer();
$arguments = [];
foreach ($argumentAnalyzer->getArguments($tokens, $argumentsStart, $argumentsEnd) as $start => $end) {
$argumentInfo = $argumentAnalyzer->getArgumentInfo($tokens, $start, $end);
$arguments[$argumentInfo->getName()] = $argumentInfo;
}
return $arguments;
}
public function getFunctionReturnType(Tokens $tokens, int $methodIndex): ?TypeAnalysis
{
$argumentsStart = $tokens->getNextTokenOfKind($methodIndex, ['(']);
$argumentsEnd = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $argumentsStart);
$typeColonIndex = $tokens->getNextMeaningfulToken($argumentsEnd);
if (!$tokens[$typeColonIndex]->isGivenKind(CT::T_TYPE_COLON)) {
return null;
}
$type = '';
$typeStartIndex = $tokens->getNextMeaningfulToken($typeColonIndex);
$typeEndIndex = $typeStartIndex;
$functionBodyStart = $tokens->getNextTokenOfKind($typeColonIndex, ['{', ';', [T_DOUBLE_ARROW]]);
for ($i = $typeStartIndex; $i < $functionBodyStart; ++$i) {
if ($tokens[$i]->isWhitespace() || $tokens[$i]->isComment()) {
continue;
}
$type .= $tokens[$i]->getContent();
$typeEndIndex = $i;
}
return new TypeAnalysis($type, $typeStartIndex, $typeEndIndex);
}
public function isTheSameClassCall(Tokens $tokens, int $index): bool
{
if (!$tokens->offsetExists($index)) {
return false;
}
$operatorIndex = $tokens->getPrevMeaningfulToken($index);
if (null === $operatorIndex) {
return false;
}
if (!$tokens[$operatorIndex]->isObjectOperator() && !$tokens[$operatorIndex]->isGivenKind(T_DOUBLE_COLON)) {
return false;
}
$referenceIndex = $tokens->getPrevMeaningfulToken($operatorIndex);
if (null === $referenceIndex) {
return false;
}
return $tokens[$referenceIndex]->equalsAny([[T_VARIABLE, '$this'], [T_STRING, 'self'], [T_STATIC, 'static']], false);
}
private function buildFunctionsAnalysis(Tokens $tokens): void
{
$this->functionsAnalysis = [
'tokens' => $tokens->getCodeHash(),
'imports' => [],
'declarations' => [],
];
if ($tokens->isTokenKindFound(T_FUNCTION)) {
$end = \count($tokens);
for ($i = 0; $i < $end; ++$i) {
if ($tokens[$i]->isGivenKind(Token::getClassyTokenKinds())) {
$i = $tokens->getNextTokenOfKind($i, ['(', '{']);
if ($tokens[$i]->equals('(')) {
$i = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $i);
$i = $tokens->getNextTokenOfKind($i, ['{']);
}
$i = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $i);
continue;
}
if (!$tokens[$i]->isGivenKind(T_FUNCTION)) {
continue;
}
$i = $tokens->getNextMeaningfulToken($i);
if ($tokens[$i]->isGivenKind(CT::T_RETURN_REF)) {
$i = $tokens->getNextMeaningfulToken($i);
}
if (!$tokens[$i]->isGivenKind(T_STRING)) {
continue;
}
$this->functionsAnalysis['declarations'][] = $i;
}
}
$namespaceUsesAnalyzer = new NamespaceUsesAnalyzer();
if ($tokens->isTokenKindFound(CT::T_FUNCTION_IMPORT)) {
$declarations = $namespaceUsesAnalyzer->getDeclarationsFromTokens($tokens);
foreach ($declarations as $declaration) {
if ($declaration->isFunction()) {
$this->functionsAnalysis['imports'][] = $declaration;
}
}
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Analyzer;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceAnalysis;
use PhpCsFixer\Tokenizer\Tokens;
final class NamespacesAnalyzer
{
public function getDeclarations(Tokens $tokens): array
{
$namespaces = [];
for ($index = 1, $count = \count($tokens); $index < $count; ++$index) {
$token = $tokens[$index];
if (!$token->isGivenKind(T_NAMESPACE)) {
continue;
}
$declarationEndIndex = $tokens->getNextTokenOfKind($index, [';', '{']);
$namespace = trim($tokens->generatePartialCode($index + 1, $declarationEndIndex - 1));
$declarationParts = explode('\\', $namespace);
$shortName = end($declarationParts);
if ($tokens[$declarationEndIndex]->equals('{')) {
$scopeEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $declarationEndIndex);
} else {
$scopeEndIndex = $tokens->getNextTokenOfKind($declarationEndIndex, [[T_NAMESPACE]]);
if (null === $scopeEndIndex) {
$scopeEndIndex = \count($tokens);
}
--$scopeEndIndex;
}
$namespaces[] = new NamespaceAnalysis(
$namespace,
$shortName,
$index,
$declarationEndIndex,
$index,
$scopeEndIndex
);
$index = $scopeEndIndex;
}
if (0 === \count($namespaces)) {
$namespaces[] = new NamespaceAnalysis('', '', 0, 0, 0, \count($tokens) - 1);
}
return $namespaces;
}
public function getNamespaceAt(Tokens $tokens, int $index): NamespaceAnalysis
{
if (!$tokens->offsetExists($index)) {
throw new \InvalidArgumentException(sprintf('Token index %d does not exist.', $index));
}
foreach ($this->getDeclarations($tokens) as $namespace) {
if ($namespace->getScopeStartIndex() <= $index && $namespace->getScopeEndIndex() >= $index) {
return $namespace;
}
}
throw new \LogicException(sprintf('Unable to get the namespace at index %d.', $index));
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Analyzer;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\ArgumentAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\TypeAnalysis;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Tokens;
final class ArgumentsAnalyzer
{
public function countArguments(Tokens $tokens, int $openParenthesis, int $closeParenthesis): int
{
return \count($this->getArguments($tokens, $openParenthesis, $closeParenthesis));
}
public function getArguments(Tokens $tokens, int $openParenthesis, int $closeParenthesis): array
{
$arguments = [];
$firstSensibleToken = $tokens->getNextMeaningfulToken($openParenthesis);
if ($tokens[$firstSensibleToken]->equals(')')) {
return $arguments;
}
$paramContentIndex = $openParenthesis + 1;
$argumentsStart = $paramContentIndex;
for (; $paramContentIndex < $closeParenthesis; ++$paramContentIndex) {
$token = $tokens[$paramContentIndex];
$blockDefinitionProbe = Tokens::detectBlockType($token);
if (null !== $blockDefinitionProbe && true === $blockDefinitionProbe['isStart']) {
$paramContentIndex = $tokens->findBlockEnd($blockDefinitionProbe['type'], $paramContentIndex);
continue;
}
if ($token->equals(',')) {
if ($tokens->getNextMeaningfulToken($paramContentIndex) === $closeParenthesis) {
break;
}
$arguments[$argumentsStart] = $paramContentIndex - 1;
$argumentsStart = $paramContentIndex + 1;
}
}
$arguments[$argumentsStart] = $paramContentIndex - 1;
return $arguments;
}
public function getArgumentInfo(Tokens $tokens, int $argumentStart, int $argumentEnd): ArgumentAnalysis
{
static $skipTypes = null;
if (null === $skipTypes) {
$skipTypes = [T_ELLIPSIS, CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC, CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED, CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE];
if (\defined('T_READONLY')) {
$skipTypes[] = T_READONLY;
}
}
$info = [
'default' => null,
'name' => null,
'name_index' => null,
'type' => null,
'type_index_start' => null,
'type_index_end' => null,
];
$sawName = false;
for ($index = $argumentStart; $index <= $argumentEnd; ++$index) {
$token = $tokens[$index];
if (\defined('T_ATTRIBUTE') && $token->isGivenKind(T_ATTRIBUTE)) {
$index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ATTRIBUTE, $index);
continue;
}
if (
$token->isComment()
|| $token->isWhitespace()
|| $token->isGivenKind($skipTypes)
|| $token->equals('&')
) {
continue;
}
if ($token->isGivenKind(T_VARIABLE)) {
$sawName = true;
$info['name_index'] = $index;
$info['name'] = $token->getContent();
continue;
}
if ($token->equals('=')) {
continue;
}
if ($sawName) {
$info['default'] .= $token->getContent();
} else {
$info['type_index_start'] = ($info['type_index_start'] > 0) ? $info['type_index_start'] : $index;
$info['type_index_end'] = $index;
$info['type'] .= $token->getContent();
}
}
return new ArgumentAnalysis(
$info['name'],
$info['name_index'],
$info['default'],
$info['type'] ? new TypeAnalysis($info['type'], $info['type_index_start'], $info['type_index_end']) : null
);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Analyzer;
use PhpCsFixer\Tokenizer\Tokens;
final class AlternativeSyntaxAnalyzer
{
private const ALTERNATIVE_SYNTAX_BLOCK_EDGES = [
T_IF => [T_ENDIF, T_ELSE, T_ELSEIF],
T_ELSE => [T_ENDIF],
T_ELSEIF => [T_ENDIF, T_ELSE, T_ELSEIF],
T_FOR => [T_ENDFOR],
T_FOREACH => [T_ENDFOREACH],
T_WHILE => [T_ENDWHILE],
T_SWITCH => [T_ENDSWITCH],
];
public function belongsToAlternativeSyntax(Tokens $tokens, int $index): bool
{
if (!$tokens[$index]->equals(':')) {
return false;
}
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$prevIndex]->isGivenKind(T_ELSE)) {
return true;
}
if (!$tokens[$prevIndex]->equals(')')) {
return false;
}
$openParenthesisIndex = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $prevIndex);
$beforeOpenParenthesisIndex = $tokens->getPrevMeaningfulToken($openParenthesisIndex);
return $tokens[$beforeOpenParenthesisIndex]->isGivenKind([
T_DECLARE,
T_ELSEIF,
T_FOR,
T_FOREACH,
T_IF,
T_SWITCH,
T_WHILE,
]);
}
public function findAlternativeSyntaxBlockEnd(Tokens $tokens, int $index): int
{
if (!isset($tokens[$index])) {
throw new \InvalidArgumentException("There is no token at index {$index}.");
}
if (!$this->isStartOfAlternativeSyntaxBlock($tokens, $index)) {
throw new \InvalidArgumentException("Token at index {$index} is not the start of an alternative syntax block.");
}
$startTokenKind = $tokens[$index]->getId();
$endTokenKinds = self::ALTERNATIVE_SYNTAX_BLOCK_EDGES[$startTokenKind];
$findKinds = [[$startTokenKind]];
foreach ($endTokenKinds as $endTokenKind) {
$findKinds[] = [$endTokenKind];
}
while (true) {
$index = $tokens->getNextTokenOfKind($index, $findKinds);
if ($tokens[$index]->isGivenKind($endTokenKinds)) {
return $index;
}
if ($this->isStartOfAlternativeSyntaxBlock($tokens, $index)) {
$index = $this->findAlternativeSyntaxBlockEnd($tokens, $index);
}
}
}
private function isStartOfAlternativeSyntaxBlock(Tokens $tokens, int $index): bool
{
$map = self::ALTERNATIVE_SYNTAX_BLOCK_EDGES;
$startTokenKind = $tokens[$index]->getId();
if (null === $startTokenKind || !isset($map[$startTokenKind])) {
return false;
}
$index = $tokens->getNextMeaningfulToken($index);
if ($tokens[$index]->equals('(')) {
$index = $tokens->getNextMeaningfulToken(
$tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index)
);
}
return $tokens[$index]->equals(':');
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Analyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Tokens;
final class RangeAnalyzer
{
private function __construct()
{
}
public static function rangeEqualsRange(Tokens $tokens, array $range1, array $range2): bool
{
$leftStart = $range1['start'];
$leftEnd = $range1['end'];
if ($tokens[$leftStart]->isGivenKind([T_WHITESPACE, T_COMMENT, T_DOC_COMMENT])) {
$leftStart = $tokens->getNextMeaningfulToken($leftStart);
}
while ($tokens[$leftStart]->equals('(') && $tokens[$leftEnd]->equals(')')) {
$leftStart = $tokens->getNextMeaningfulToken($leftStart);
$leftEnd = $tokens->getPrevMeaningfulToken($leftEnd);
}
$rightStart = $range2['start'];
$rightEnd = $range2['end'];
if ($tokens[$rightStart]->isGivenKind([T_WHITESPACE, T_COMMENT, T_DOC_COMMENT])) {
$rightStart = $tokens->getNextMeaningfulToken($rightStart);
}
while ($tokens[$rightStart]->equals('(') && $tokens[$rightEnd]->equals(')')) {
$rightStart = $tokens->getNextMeaningfulToken($rightStart);
$rightEnd = $tokens->getPrevMeaningfulToken($rightEnd);
}
$arrayOpenTypes = ['[', [CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN]];
$arrayCloseTypes = [']', [CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE]];
while (true) {
$leftToken = $tokens[$leftStart];
$rightToken = $tokens[$rightStart];
if (
!$leftToken->equals($rightToken)
&& !($leftToken->equalsAny($arrayOpenTypes) && $rightToken->equalsAny($arrayOpenTypes))
&& !($leftToken->equalsAny($arrayCloseTypes) && $rightToken->equalsAny($arrayCloseTypes))
) {
return false;
}
$leftStart = $tokens->getNextMeaningfulToken($leftStart);
$rightStart = $tokens->getNextMeaningfulToken($rightStart);
$reachedLeftEnd = null === $leftStart || $leftStart > $leftEnd;
$reachedRightEnd = null === $rightStart || $rightStart > $rightEnd;
if (!$reachedLeftEnd && !$reachedRightEnd) {
continue;
}
return $reachedLeftEnd && $reachedRightEnd;
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Analyzer;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceUseAnalysis;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
final class NamespaceUsesAnalyzer
{
public function getDeclarationsFromTokens(Tokens $tokens): array
{
$tokenAnalyzer = new TokensAnalyzer($tokens);
$useIndices = $tokenAnalyzer->getImportUseIndexes();
return $this->getDeclarations($tokens, $useIndices);
}
public function getDeclarationsInNamespace(Tokens $tokens, NamespaceAnalysis $namespace): array
{
$namespaceUses = [];
foreach ($this->getDeclarationsFromTokens($tokens) as $namespaceUse) {
if ($namespaceUse->getStartIndex() >= $namespace->getScopeStartIndex() && $namespaceUse->getStartIndex() <= $namespace->getScopeEndIndex()) {
$namespaceUses[] = $namespaceUse;
}
}
return $namespaceUses;
}
private function getDeclarations(Tokens $tokens, array $useIndices): array
{
$uses = [];
foreach ($useIndices as $index) {
$endIndex = $tokens->getNextTokenOfKind($index, [';', [T_CLOSE_TAG]]);
$analysis = $this->parseDeclaration($tokens, $index, $endIndex);
if (null !== $analysis) {
$uses[] = $analysis;
}
}
return $uses;
}
private function parseDeclaration(Tokens $tokens, int $startIndex, int $endIndex): ?NamespaceUseAnalysis
{
$fullName = $shortName = '';
$aliased = false;
$type = NamespaceUseAnalysis::TYPE_CLASS;
for ($i = $startIndex; $i <= $endIndex; ++$i) {
$token = $tokens[$i];
if ($token->equals(',') || $token->isGivenKind(CT::T_GROUP_IMPORT_BRACE_CLOSE)) {
return null;
}
if ($token->isGivenKind(CT::T_FUNCTION_IMPORT)) {
$type = NamespaceUseAnalysis::TYPE_FUNCTION;
} elseif ($token->isGivenKind(CT::T_CONST_IMPORT)) {
$type = NamespaceUseAnalysis::TYPE_CONSTANT;
}
if ($token->isWhitespace() || $token->isComment() || $token->isGivenKind(T_USE)) {
continue;
}
if ($token->isGivenKind(T_STRING)) {
$shortName = $token->getContent();
if (!$aliased) {
$fullName .= $shortName;
}
} elseif ($token->isGivenKind(T_NS_SEPARATOR)) {
$fullName .= $token->getContent();
} elseif ($token->isGivenKind(T_AS)) {
$aliased = true;
}
}
return new NamespaceUseAnalysis(
trim($fullName),
$shortName,
$aliased,
$startIndex,
$endIndex,
$type
);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Analyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Tokens;
final class ClassyAnalyzer
{
public function isClassyInvocation(Tokens $tokens, int $index): bool
{
$token = $tokens[$index];
if (!$token->isGivenKind(T_STRING)) {
throw new \LogicException(sprintf('No T_STRING at given index %d, got "%s".', $index, $tokens[$index]->getName()));
}
if (\in_array(strtolower($token->getContent()), ['bool', 'float', 'int', 'iterable', 'object', 'parent', 'self', 'string', 'void', 'null', 'false', 'never'], true)) {
return false;
}
$next = $tokens->getNextMeaningfulToken($index);
$nextToken = $tokens[$next];
if ($nextToken->isGivenKind(T_NS_SEPARATOR)) {
return false;
}
if ($nextToken->isGivenKind([T_DOUBLE_COLON, T_ELLIPSIS, CT::T_TYPE_ALTERNATION, CT::T_TYPE_INTERSECTION, T_VARIABLE])) {
return true;
}
$prev = $tokens->getPrevMeaningfulToken($index);
while ($tokens[$prev]->isGivenKind([CT::T_NAMESPACE_OPERATOR, T_NS_SEPARATOR, T_STRING])) {
$prev = $tokens->getPrevMeaningfulToken($prev);
}
$prevToken = $tokens[$prev];
if ($prevToken->isGivenKind([T_EXTENDS, T_INSTANCEOF, T_INSTEADOF, T_IMPLEMENTS, T_NEW, CT::T_NULLABLE_TYPE, CT::T_TYPE_ALTERNATION, CT::T_TYPE_INTERSECTION, CT::T_TYPE_COLON, CT::T_USE_TRAIT])) {
return true;
}
if (AttributeAnalyzer::isAttribute($tokens, $index)) {
return true;
}
if ($nextToken->equals('&') && $tokens[$tokens->getNextMeaningfulToken($next)]->isGivenKind(T_VARIABLE)) {
$checkIndex = $tokens->getPrevTokenOfKind($prev + 1, [';', '{', '}', [T_FUNCTION], [T_OPEN_TAG], [T_OPEN_TAG_WITH_ECHO]]);
return $tokens[$checkIndex]->isGivenKind(T_FUNCTION);
}
if (!$prevToken->equals(',')) {
return false;
}
do {
$prev = $tokens->getPrevMeaningfulToken($prev);
} while ($tokens[$prev]->equalsAny([',', [T_NS_SEPARATOR], [T_STRING], [CT::T_NAMESPACE_OPERATOR]]));
return $tokens[$prev]->isGivenKind([T_IMPLEMENTS, CT::T_USE_TRAIT]);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Analyzer;
use PhpCsFixer\Tokenizer\Tokens;
final class WhitespacesAnalyzer
{
public static function detectIndent(Tokens $tokens, int $index): string
{
while (true) {
$whitespaceIndex = $tokens->getPrevTokenOfKind($index, [[T_WHITESPACE]]);
if (null === $whitespaceIndex) {
return '';
}
$whitespaceToken = $tokens[$whitespaceIndex];
if (str_contains($whitespaceToken->getContent(), "\n")) {
break;
}
$prevToken = $tokens[$whitespaceIndex - 1];
if ($prevToken->isGivenKind([T_OPEN_TAG, T_COMMENT]) && "\n" === substr($prevToken->getContent(), -1)) {
break;
}
$index = $whitespaceIndex;
}
$explodedContent = explode("\n", $whitespaceToken->getContent());
return end($explodedContent);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Analyzer;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class CommentsAnalyzer
{
private const TYPE_HASH = 1;
private const TYPE_DOUBLE_SLASH = 2;
private const TYPE_SLASH_ASTERISK = 3;
public function isHeaderComment(Tokens $tokens, int $index): bool
{
if (!$tokens[$index]->isGivenKind([T_COMMENT, T_DOC_COMMENT])) {
throw new \InvalidArgumentException('Given index must point to a comment.');
}
if (null === $tokens->getNextMeaningfulToken($index)) {
return false;
}
$prevIndex = $tokens->getPrevNonWhitespace($index);
if ($tokens[$prevIndex]->equals(';')) {
$braceCloseIndex = $tokens->getPrevMeaningfulToken($prevIndex);
if (!$tokens[$braceCloseIndex]->equals(')')) {
return false;
}
$braceOpenIndex = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $braceCloseIndex);
$declareIndex = $tokens->getPrevMeaningfulToken($braceOpenIndex);
if (!$tokens[$declareIndex]->isGivenKind(T_DECLARE)) {
return false;
}
$prevIndex = $tokens->getPrevNonWhitespace($declareIndex);
}
return $tokens[$prevIndex]->isGivenKind(T_OPEN_TAG);
}
public function isBeforeStructuralElement(Tokens $tokens, int $index): bool
{
$token = $tokens[$index];
if (!$token->isGivenKind([T_COMMENT, T_DOC_COMMENT])) {
throw new \InvalidArgumentException('Given index must point to a comment.');
}
$nextIndex = $index;
do {
$nextIndex = $tokens->getNextMeaningfulToken($nextIndex);
if (\defined('T_ATTRIBUTE')) {
while (null !== $nextIndex && $tokens[$nextIndex]->isGivenKind(T_ATTRIBUTE)) {
$nextIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ATTRIBUTE, $nextIndex);
$nextIndex = $tokens->getNextMeaningfulToken($nextIndex);
}
}
} while (null !== $nextIndex && $tokens[$nextIndex]->equals('('));
if (null === $nextIndex || $tokens[$nextIndex]->equals('}')) {
return false;
}
if ($this->isStructuralElement($tokens, $nextIndex)) {
return true;
}
if ($this->isValidControl($tokens, $token, $nextIndex)) {
return true;
}
if ($this->isValidVariable($tokens, $nextIndex)) {
return true;
}
if ($this->isValidLanguageConstruct($tokens, $token, $nextIndex)) {
return true;
}
if ($tokens[$nextIndex]->isGivenKind(CT::T_USE_TRAIT)) {
return true;
}
return false;
}
public function getCommentBlockIndices(Tokens $tokens, int $index): array
{
if (!$tokens[$index]->isGivenKind(T_COMMENT)) {
throw new \InvalidArgumentException('Given index must point to a comment.');
}
$commentType = $this->getCommentType($tokens[$index]->getContent());
$indices = [$index];
if (self::TYPE_SLASH_ASTERISK === $commentType) {
return $indices;
}
$count = \count($tokens);
++$index;
for (; $index < $count; ++$index) {
if ($tokens[$index]->isComment()) {
if ($commentType === $this->getCommentType($tokens[$index]->getContent())) {
$indices[] = $index;
continue;
}
break;
}
if (!$tokens[$index]->isWhitespace() || $this->getLineBreakCount($tokens, $index, $index + 1) > 1) {
break;
}
}
return $indices;
}
private function isStructuralElement(Tokens $tokens, int $index): bool
{
static $skip;
if (null === $skip) {
$skip = [
T_PRIVATE,
T_PROTECTED,
T_PUBLIC,
T_VAR,
T_FUNCTION,
T_ABSTRACT,
T_CONST,
T_NAMESPACE,
T_REQUIRE,
T_REQUIRE_ONCE,
T_INCLUDE,
T_INCLUDE_ONCE,
T_FINAL,
CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC,
CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED,
CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE,
];
if (\defined('T_READONLY')) {
$skip[] = T_READONLY;
}
}
$token = $tokens[$index];
if ($token->isClassy() || $token->isGivenKind($skip)) {
return true;
}
if ($token->isGivenKind(T_STATIC)) {
return !$tokens[$tokens->getNextMeaningfulToken($index)]->isGivenKind(T_DOUBLE_COLON);
}
return false;
}
private function isValidControl(Tokens $tokens, Token $docsToken, int $controlIndex): bool
{
static $controlStructures = [
T_FOR,
T_FOREACH,
T_IF,
T_SWITCH,
T_WHILE,
];
if (!$tokens[$controlIndex]->isGivenKind($controlStructures)) {
return false;
}
$index = $tokens->getNextMeaningfulToken($controlIndex);
$endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index);
$docsContent = $docsToken->getContent();
for ($index = $index + 1; $index < $endIndex; ++$index) {
$token = $tokens[$index];
if (
$token->isGivenKind(T_VARIABLE)
&& str_contains($docsContent, $token->getContent())
) {
return true;
}
}
return false;
}
private function isValidLanguageConstruct(Tokens $tokens, Token $docsToken, int $languageConstructIndex): bool
{
static $languageStructures = [
T_LIST,
T_PRINT,
T_ECHO,
CT::T_DESTRUCTURING_SQUARE_BRACE_OPEN,
];
if (!$tokens[$languageConstructIndex]->isGivenKind($languageStructures)) {
return false;
}
$endKind = $tokens[$languageConstructIndex]->isGivenKind(CT::T_DESTRUCTURING_SQUARE_BRACE_OPEN)
? [CT::T_DESTRUCTURING_SQUARE_BRACE_CLOSE]
: ')';
$endIndex = $tokens->getNextTokenOfKind($languageConstructIndex, [$endKind]);
$docsContent = $docsToken->getContent();
for ($index = $languageConstructIndex + 1; $index < $endIndex; ++$index) {
$token = $tokens[$index];
if ($token->isGivenKind(T_VARIABLE) && str_contains($docsContent, $token->getContent())) {
return true;
}
}
return false;
}
private function isValidVariable(Tokens $tokens, int $index): bool
{
if (!$tokens[$index]->isGivenKind(T_VARIABLE)) {
return false;
}
$nextIndex = $tokens->getNextMeaningfulToken($index);
return $tokens[$nextIndex]->equals('=');
}
private function getCommentType(string $content): int
{
if (str_starts_with($content, '#')) {
return self::TYPE_HASH;
}
if ('*' === $content[1]) {
return self::TYPE_SLASH_ASTERISK;
}
return self::TYPE_DOUBLE_SLASH;
}
private function getLineBreakCount(Tokens $tokens, int $whiteStart, int $whiteEnd): int
{
$lineCount = 0;
for ($i = $whiteStart; $i < $whiteEnd; ++$i) {
$lineCount += Preg::matchAll('/\R/u', $tokens[$i]->getContent());
}
return $lineCount;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer\Analyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Tokens;
final class AttributeAnalyzer
{
private const TOKEN_KINDS_NOT_ALLOWED_IN_ATTRIBUTE = [
';',
'{',
[T_ATTRIBUTE],
[T_FUNCTION],
[T_OPEN_TAG],
[T_OPEN_TAG_WITH_ECHO],
[T_PRIVATE],
[T_PROTECTED],
[T_PUBLIC],
[T_RETURN],
[T_VARIABLE],
[CT::T_ATTRIBUTE_CLOSE],
];
public static function isAttribute(Tokens $tokens, int $index): bool
{
if (
!\defined('T_ATTRIBUTE')
|| !$tokens[$index]->isGivenKind(T_STRING)
|| !$tokens->isAnyTokenKindsFound([T_ATTRIBUTE])
) {
return false;
}
$attributeStartIndex = $tokens->getPrevTokenOfKind($index, self::TOKEN_KINDS_NOT_ALLOWED_IN_ATTRIBUTE);
if (!$tokens[$attributeStartIndex]->isGivenKind(T_ATTRIBUTE)) {
return false;
}
$count = 0;
for ($i = $attributeStartIndex + 1; $i < $index; ++$i) {
if ($tokens[$i]->equals('(')) {
++$count;
} elseif ($tokens[$i]->equals(')')) {
--$count;
}
}
return 0 === $count;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Tokenizer;
final class CT
{
public const T_ARRAY_INDEX_CURLY_BRACE_CLOSE = 10001;
public const T_ARRAY_INDEX_CURLY_BRACE_OPEN = 10002;
public const T_ARRAY_SQUARE_BRACE_CLOSE = 10003;
public const T_ARRAY_SQUARE_BRACE_OPEN = 10004;
public const T_ARRAY_TYPEHINT = 10005;
public const T_BRACE_CLASS_INSTANTIATION_CLOSE = 10006;
public const T_BRACE_CLASS_INSTANTIATION_OPEN = 10007;
public const T_CLASS_CONSTANT = 10008;
public const T_CONST_IMPORT = 10009;
public const T_CURLY_CLOSE = 10010;
public const T_DESTRUCTURING_SQUARE_BRACE_CLOSE = 10011;
public const T_DESTRUCTURING_SQUARE_BRACE_OPEN = 10012;
public const T_DOLLAR_CLOSE_CURLY_BRACES = 10013;
public const T_DYNAMIC_PROP_BRACE_CLOSE = 10014;
public const T_DYNAMIC_PROP_BRACE_OPEN = 10015;
public const T_DYNAMIC_VAR_BRACE_CLOSE = 10016;
public const T_DYNAMIC_VAR_BRACE_OPEN = 10017;
public const T_FUNCTION_IMPORT = 10018;
public const T_GROUP_IMPORT_BRACE_CLOSE = 10019;
public const T_GROUP_IMPORT_BRACE_OPEN = 10020;
public const T_NAMESPACE_OPERATOR = 10021;
public const T_NULLABLE_TYPE = 10022;
public const T_RETURN_REF = 10023;
public const T_TYPE_ALTERNATION = 10024;
public const T_TYPE_COLON = 10025;
public const T_USE_LAMBDA = 10026;
public const T_USE_TRAIT = 10027;
public const T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC = 10028;
public const T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED = 10029;
public const T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE = 10030;
public const T_ATTRIBUTE_CLOSE = 10031;
public const T_NAMED_ARGUMENT_NAME = 10032;
public const T_NAMED_ARGUMENT_COLON = 10033;
public const T_FIRST_CLASS_CALLABLE = 10034;
public const T_TYPE_INTERSECTION = 10035;
private function __construct()
{
}
public static function getName(int $value): string
{
if (!self::has($value)) {
throw new \InvalidArgumentException(sprintf('No custom token was found for "%s".', $value));
}
$tokens = self::getMapById();
return 'CT::'.$tokens[$value];
}
public static function has(int $value): bool
{
$tokens = self::getMapById();
return isset($tokens[$value]);
}
private static function getMapById(): array
{
static $constants;
if (null === $constants) {
$reflection = new \ReflectionClass(__CLASS__);
$constants = array_flip($reflection->getConstants());
}
return $constants;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer;
use PhpCsFixer\Doctrine\Annotation\Tokens as DoctrineAnnotationTokens;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
abstract class AbstractDoctrineAnnotationFixer extends AbstractFixer implements ConfigurableFixerInterface
{
private array $classyElements;
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_DOC_COMMENT);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$analyzer = new TokensAnalyzer($tokens);
$this->classyElements = $analyzer->getClassyElements();
foreach ($tokens->findGivenKind(T_DOC_COMMENT) as $index => $docCommentToken) {
if (!$this->nextElementAcceptsDoctrineAnnotations($tokens, $index)) {
continue;
}
$doctrineAnnotationTokens = DoctrineAnnotationTokens::createFromDocComment(
$docCommentToken,
$this->configuration['ignored_tags']
);
$this->fixAnnotations($doctrineAnnotationTokens);
$tokens[$index] = new Token([T_DOC_COMMENT, $doctrineAnnotationTokens->getCode()]);
}
}
abstract protected function fixAnnotations(DoctrineAnnotationTokens $doctrineAnnotationTokens): void;
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('ignored_tags', 'List of tags that must not be treated as Doctrine Annotations.'))
->setAllowedTypes(['array'])
->setAllowedValues([static function (array $values): bool {
foreach ($values as $value) {
if (!\is_string($value)) {
return false;
}
}
return true;
}])
->setDefault([
'abstract',
'access',
'code',
'deprec',
'encode',
'exception',
'final',
'ingroup',
'inheritdoc',
'inheritDoc',
'magic',
'name',
'toc',
'tutorial',
'private',
'static',
'staticvar',
'staticVar',
'throw',
'api',
'author',
'category',
'copyright',
'deprecated',
'example',
'filesource',
'global',
'ignore',
'internal',
'license',
'link',
'method',
'package',
'param',
'property',
'property-read',
'property-write',
'return',
'see',
'since',
'source',
'subpackage',
'throws',
'todo',
'TODO',
'usedBy',
'uses',
'var',
'version',
'after',
'afterClass',
'backupGlobals',
'backupStaticAttributes',
'before',
'beforeClass',
'codeCoverageIgnore',
'codeCoverageIgnoreStart',
'codeCoverageIgnoreEnd',
'covers',
'coversDefaultClass',
'coversNothing',
'dataProvider',
'depends',
'expectedException',
'expectedExceptionCode',
'expectedExceptionMessage',
'expectedExceptionMessageRegExp',
'group',
'large',
'medium',
'preserveGlobalState',
'requires',
'runTestsInSeparateProcesses',
'runInSeparateProcess',
'small',
'test',
'testdox',
'ticket',
'uses',
'SuppressWarnings',
'noinspection',
'package_version',
'enduml',
'startuml',
'psalm',
'phpstan',
'template',
'fix',
'FIXME',
'fixme',
'override',
])
->getOption(),
]);
}
private function nextElementAcceptsDoctrineAnnotations(Tokens $tokens, int $index): bool
{
do {
$index = $tokens->getNextMeaningfulToken($index);
if (null === $index) {
return false;
}
} while ($tokens[$index]->isGivenKind([T_ABSTRACT, T_FINAL]));
if ($tokens[$index]->isGivenKind(T_CLASS)) {
return true;
}
$modifierKinds = [T_PUBLIC, T_PROTECTED, T_PRIVATE, T_FINAL, T_ABSTRACT, T_NS_SEPARATOR, T_STRING, CT::T_NULLABLE_TYPE];
if (\defined('T_READONLY')) {
$modifierKinds[] = T_READONLY;
}
while ($tokens[$index]->isGivenKind($modifierKinds)) {
$index = $tokens->getNextMeaningfulToken($index);
}
if (!isset($this->classyElements[$index])) {
return false;
}
return $tokens[$this->classyElements[$index]['classIndex']]->isGivenKind(T_CLASS);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Runner;
use PhpCsFixer\Cache\CacheManagerInterface;
use PhpCsFixer\FileReader;
use PhpCsFixer\FixerFileProcessedEvent;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Contracts\EventDispatcher\Event;
/**
@extends
*/
final class FileFilterIterator extends \FilterIterator
{
private ?EventDispatcherInterface $eventDispatcher;
private CacheManagerInterface $cacheManager;
private array $visitedElements = [];
public function __construct(
\Traversable $iterator,
?EventDispatcherInterface $eventDispatcher,
CacheManagerInterface $cacheManager
) {
if (!$iterator instanceof \Iterator) {
$iterator = new \IteratorIterator($iterator);
}
parent::__construct($iterator);
$this->eventDispatcher = $eventDispatcher;
$this->cacheManager = $cacheManager;
}
public function accept(): bool
{
$file = $this->current();
if (!$file instanceof \SplFileInfo) {
throw new \RuntimeException(
sprintf(
'Expected instance of "\SplFileInfo", got "%s".',
get_debug_type($file)
)
);
}
$path = $file->isLink() ? $file->getPathname() : $file->getRealPath();
if (isset($this->visitedElements[$path])) {
return false;
}
$this->visitedElements[$path] = true;
if (!$file->isFile() || $file->isLink()) {
return false;
}
$content = FileReader::createSingleton()->read($path);
if (
'' === $content
|| !$this->cacheManager->needFixing($file->getPathname(), $content)
) {
$this->dispatchEvent(
FixerFileProcessedEvent::NAME,
new FixerFileProcessedEvent(FixerFileProcessedEvent::STATUS_SKIPPED)
);
return false;
}
return true;
}
private function dispatchEvent(string $name, Event $event): void
{
if (null === $this->eventDispatcher) {
return;
}
$this->eventDispatcher->dispatch($event, $name);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Runner;
use PhpCsFixer\Linter\LinterInterface;
use PhpCsFixer\Linter\LintingResultInterface;
/**
@extends
*/
final class FileLintingIterator extends \IteratorIterator
{
private $currentResult;
private LinterInterface $linter;
public function __construct(\Iterator $iterator, LinterInterface $linter)
{
parent::__construct($iterator);
$this->linter = $linter;
}
public function currentLintingResult(): ?LintingResultInterface
{
return $this->currentResult;
}
public function next(): void
{
parent::next();
$this->currentResult = $this->valid() ? $this->handleItem($this->current()) : null;
}
public function rewind(): void
{
parent::rewind();
$this->currentResult = $this->valid() ? $this->handleItem($this->current()) : null;
}
private function handleItem(\SplFileInfo $file): LintingResultInterface
{
return $this->linter->lintFile($file->getRealPath());
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Runner;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Cache\CacheManagerInterface;
use PhpCsFixer\Cache\Directory;
use PhpCsFixer\Cache\DirectoryInterface;
use PhpCsFixer\Differ\DifferInterface;
use PhpCsFixer\Error\Error;
use PhpCsFixer\Error\ErrorsManager;
use PhpCsFixer\FileReader;
use PhpCsFixer\Fixer\FixerInterface;
use PhpCsFixer\FixerFileProcessedEvent;
use PhpCsFixer\Linter\LinterInterface;
use PhpCsFixer\Linter\LintingException;
use PhpCsFixer\Linter\LintingResultInterface;
use PhpCsFixer\Tokenizer\Tokens;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Contracts\EventDispatcher\Event;
final class Runner
{
private DifferInterface $differ;
private ?DirectoryInterface $directory;
private ?EventDispatcherInterface $eventDispatcher;
private ErrorsManager $errorsManager;
private CacheManagerInterface $cacheManager;
private bool $isDryRun;
private LinterInterface $linter;
private $finder;
private array $fixers;
private bool $stopOnViolation;
public function __construct(
\Traversable $finder,
array $fixers,
DifferInterface $differ,
?EventDispatcherInterface $eventDispatcher,
ErrorsManager $errorsManager,
LinterInterface $linter,
bool $isDryRun,
CacheManagerInterface $cacheManager,
?DirectoryInterface $directory = null,
bool $stopOnViolation = false
) {
$this->finder = $finder;
$this->fixers = $fixers;
$this->differ = $differ;
$this->eventDispatcher = $eventDispatcher;
$this->errorsManager = $errorsManager;
$this->linter = $linter;
$this->isDryRun = $isDryRun;
$this->cacheManager = $cacheManager;
$this->directory = $directory ?: new Directory('');
$this->stopOnViolation = $stopOnViolation;
}
public function fix(): array
{
$changed = [];
$finder = $this->finder;
$finderIterator = $finder instanceof \IteratorAggregate ? $finder->getIterator() : $finder;
$fileFilteredFileIterator = new FileFilterIterator(
$finderIterator,
$this->eventDispatcher,
$this->cacheManager
);
$collection = $this->linter->isAsync()
? new FileCachingLintingIterator($fileFilteredFileIterator, $this->linter)
: new FileLintingIterator($fileFilteredFileIterator, $this->linter);
foreach ($collection as $file) {
$fixInfo = $this->fixFile($file, $collection->currentLintingResult());
Tokens::clearCache();
if (null !== $fixInfo) {
$name = $this->directory->getRelativePathTo($file->__toString());
$changed[$name] = $fixInfo;
if ($this->stopOnViolation) {
break;
}
}
}
return $changed;
}
private function fixFile(\SplFileInfo $file, LintingResultInterface $lintingResult): ?array
{
$name = $file->getPathname();
try {
$lintingResult->check();
} catch (LintingException $e) {
$this->dispatchEvent(
FixerFileProcessedEvent::NAME,
new FixerFileProcessedEvent(FixerFileProcessedEvent::STATUS_INVALID)
);
$this->errorsManager->report(new Error(Error::TYPE_INVALID, $name, $e));
return null;
}
$old = FileReader::createSingleton()->read($file->getRealPath());
$tokens = Tokens::fromCode($old);
$oldHash = $tokens->getCodeHash();
$newHash = $oldHash;
$new = $old;
$appliedFixers = [];
try {
foreach ($this->fixers as $fixer) {
if (
!$fixer instanceof AbstractFixer
&& (!$fixer->supports($file) || !$fixer->isCandidate($tokens))
) {
continue;
}
$fixer->fix($file, $tokens);
if ($tokens->isChanged()) {
$tokens->clearEmptyTokens();
$tokens->clearChanged();
$appliedFixers[] = $fixer->getName();
}
}
} catch (\ParseError $e) {
$this->dispatchEvent(
FixerFileProcessedEvent::NAME,
new FixerFileProcessedEvent(FixerFileProcessedEvent::STATUS_LINT)
);
$this->errorsManager->report(new Error(Error::TYPE_LINT, $name, $e));
return null;
} catch (\Throwable $e) {
$this->processException($name, $e);
return null;
}
$fixInfo = null;
if (!empty($appliedFixers)) {
$new = $tokens->generateCode();
$newHash = $tokens->getCodeHash();
}
if ($oldHash !== $newHash) {
$fixInfo = [
'appliedFixers' => $appliedFixers,
'diff' => $this->differ->diff($old, $new, $file),
];
try {
$this->linter->lintSource($new)->check();
} catch (LintingException $e) {
$this->dispatchEvent(
FixerFileProcessedEvent::NAME,
new FixerFileProcessedEvent(FixerFileProcessedEvent::STATUS_LINT)
);
$this->errorsManager->report(new Error(Error::TYPE_LINT, $name, $e, $fixInfo['appliedFixers'], $fixInfo['diff']));
return null;
}
if (!$this->isDryRun) {
$fileName = $file->getRealPath();
if (!file_exists($fileName)) {
throw new IOException(
sprintf('Failed to write file "%s" (no longer) exists.', $file->getPathname()),
0,
null,
$file->getPathname()
);
}
if (is_dir($fileName)) {
throw new IOException(
sprintf('Cannot write file "%s" as the location exists as directory.', $fileName),
0,
null,
$fileName
);
}
if (!is_writable($fileName)) {
throw new IOException(
sprintf('Cannot write to file "%s" as it is not writable.', $fileName),
0,
null,
$fileName
);
}
if (false === @file_put_contents($fileName, $new)) {
$error = error_get_last();
throw new IOException(
sprintf('Failed to write file "%s", "%s".', $fileName, $error ? $error['message'] : 'no reason available'),
0,
null,
$fileName
);
}
}
}
$this->cacheManager->setFile($name, $new);
$this->dispatchEvent(
FixerFileProcessedEvent::NAME,
new FixerFileProcessedEvent($fixInfo ? FixerFileProcessedEvent::STATUS_FIXED : FixerFileProcessedEvent::STATUS_NO_CHANGES)
);
return $fixInfo;
}
private function processException(string $name, \Throwable $e): void
{
$this->dispatchEvent(
FixerFileProcessedEvent::NAME,
new FixerFileProcessedEvent(FixerFileProcessedEvent::STATUS_EXCEPTION)
);
$this->errorsManager->report(new Error(Error::TYPE_EXCEPTION, $name, $e));
}
private function dispatchEvent(string $name, Event $event): void
{
if (null === $this->eventDispatcher) {
return;
}
$this->eventDispatcher->dispatch($event, $name);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Runner;
use PhpCsFixer\Linter\LinterInterface;
use PhpCsFixer\Linter\LintingResultInterface;
/**
@extends
*/
final class FileCachingLintingIterator extends \CachingIterator
{
private LinterInterface $linter;
private $currentResult;
private $nextResult;
public function __construct(\Iterator $iterator, LinterInterface $linter)
{
parent::__construct($iterator);
$this->linter = $linter;
}
public function currentLintingResult(): LintingResultInterface
{
return $this->currentResult;
}
public function next(): void
{
parent::next();
$this->currentResult = $this->nextResult;
if ($this->hasNext()) {
$this->nextResult = $this->handleItem($this->getInnerIterator()->current());
}
}
public function rewind(): void
{
parent::rewind();
if ($this->valid()) {
$this->currentResult = $this->handleItem($this->current());
}
if ($this->hasNext()) {
$this->nextResult = $this->handleItem($this->getInnerIterator()->current());
}
}
private function handleItem(\SplFileInfo $file): LintingResultInterface
{
return $this->linter->lintFile($file->getRealPath());
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\FixerConfiguration;
final class AllowedValueSubset
{
private array $allowedValues;
public function __construct(array $allowedValues)
{
$this->allowedValues = $allowedValues;
sort($this->allowedValues, SORT_FLAG_CASE | SORT_STRING);
}
public function __invoke($values): bool
{
if (!\is_array($values)) {
return false;
}
foreach ($values as $value) {
if (!\in_array($value, $this->allowedValues, true)) {
return false;
}
}
return true;
}
public function getAllowedValues(): array
{
return $this->allowedValues;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\FixerConfiguration;
use PhpCsFixer\Utils;
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
use Symfony\Component\OptionsResolver\OptionsResolver;
final class FixerConfigurationResolver implements FixerConfigurationResolverInterface
{
private array $options = [];
private array $registeredNames = [];
public function __construct(iterable $options)
{
foreach ($options as $option) {
$this->addOption($option);
}
if (0 === \count($this->registeredNames)) {
throw new \LogicException('Options cannot be empty.');
}
}
public function getOptions(): array
{
return $this->options;
}
public function resolve(array $configuration): array
{
$resolver = new OptionsResolver();
foreach ($this->options as $option) {
$name = $option->getName();
if ($option instanceof AliasedFixerOption) {
$alias = $option->getAlias();
if (\array_key_exists($alias, $configuration)) {
if (\array_key_exists($name, $configuration)) {
throw new InvalidOptionsException(sprintf('Aliased option "%s"/"%s" is passed multiple times.', $name, $alias));
}
Utils::triggerDeprecation(new \RuntimeException(sprintf(
'Option "%s" is deprecated, use "%s" instead.',
$alias,
$name
)));
$configuration[$name] = $configuration[$alias];
unset($configuration[$alias]);
}
}
if ($option->hasDefault()) {
$resolver->setDefault($name, $option->getDefault());
} else {
$resolver->setRequired($name);
}
$allowedValues = $option->getAllowedValues();
if (null !== $allowedValues) {
foreach ($allowedValues as &$allowedValue) {
if (\is_object($allowedValue) && \is_callable($allowedValue)) {
$allowedValue = static function ( $values) use ($allowedValue) {
return $allowedValue($values);
};
}
}
$resolver->setAllowedValues($name, $allowedValues);
}
$allowedTypes = $option->getAllowedTypes();
if (null !== $allowedTypes) {
$resolver->setAllowedTypes($name, $allowedTypes);
}
$normalizer = $option->getNormalizer();
if (null !== $normalizer) {
$resolver->setNormalizer($name, $normalizer);
}
}
return $resolver->resolve($configuration);
}
private function addOption(FixerOptionInterface $option): void
{
$name = $option->getName();
if (\in_array($name, $this->registeredNames, true)) {
throw new \LogicException(sprintf('The "%s" option is defined multiple times.', $name));
}
$this->options[] = $option;
$this->registeredNames[] = $name;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\FixerConfiguration;
interface DeprecatedFixerOptionInterface extends FixerOptionInterface
{
public function getDeprecationMessage(): string;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\FixerConfiguration;
final class FixerOption implements FixerOptionInterface
{
private string $name;
private string $description;
private bool $isRequired;
private $default;
private $allowedTypes;
private $allowedValues;
private $normalizer;
public function __construct(
string $name,
string $description,
bool $isRequired = true,
$default = null,
?array $allowedTypes = null,
?array $allowedValues = null,
?\Closure $normalizer = null
) {
if ($isRequired && null !== $default) {
throw new \LogicException('Required options cannot have a default value.');
}
if (null !== $allowedValues) {
foreach ($allowedValues as &$allowedValue) {
if ($allowedValue instanceof \Closure) {
$allowedValue = $this->unbind($allowedValue);
}
}
}
$this->name = $name;
$this->description = $description;
$this->isRequired = $isRequired;
$this->default = $default;
$this->allowedTypes = $allowedTypes;
$this->allowedValues = $allowedValues;
if (null !== $normalizer) {
$this->normalizer = $this->unbind($normalizer);
}
}
public function getName(): string
{
return $this->name;
}
public function getDescription(): string
{
return $this->description;
}
public function hasDefault(): bool
{
return !$this->isRequired;
}
public function getDefault()
{
if (!$this->hasDefault()) {
throw new \LogicException('No default value defined.');
}
return $this->default;
}
public function getAllowedTypes(): ?array
{
return $this->allowedTypes;
}
public function getAllowedValues(): ?array
{
return $this->allowedValues;
}
public function getNormalizer(): ?\Closure
{
return $this->normalizer;
}
private function unbind(\Closure $closure): \Closure
{
return $closure->bindTo(null);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\FixerConfiguration;
interface FixerOptionInterface
{
public function getName(): string;
public function getDescription(): string;
public function hasDefault(): bool;
public function getDefault();
public function getAllowedTypes(): ?array;
public function getAllowedValues(): ?array;
public function getNormalizer(): ?\Closure;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\FixerConfiguration;
final class FixerOptionBuilder
{
private string $name;
private string $description;
private $default;
private bool $isRequired = true;
private $allowedTypes;
private $allowedValues;
private $normalizer;
private $deprecationMessage;
public function __construct(string $name, string $description)
{
$this->name = $name;
$this->description = $description;
}
public function setDefault($default): self
{
$this->default = $default;
$this->isRequired = false;
return $this;
}
public function setAllowedTypes(array $allowedTypes): self
{
$this->allowedTypes = $allowedTypes;
return $this;
}
public function setAllowedValues(array $allowedValues): self
{
$this->allowedValues = $allowedValues;
return $this;
}
public function setNormalizer(\Closure $normalizer): self
{
$this->normalizer = $normalizer;
return $this;
}
public function setDeprecationMessage(?string $deprecationMessage): self
{
$this->deprecationMessage = $deprecationMessage;
return $this;
}
public function getOption(): FixerOptionInterface
{
$option = new FixerOption(
$this->name,
$this->description,
$this->isRequired,
$this->default,
$this->allowedTypes,
$this->allowedValues,
$this->normalizer
);
if (null !== $this->deprecationMessage) {
$option = new DeprecatedFixerOption($option, $this->deprecationMessage);
}
return $option;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\FixerConfiguration;
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
final class InvalidOptionsForEnvException extends InvalidOptionsException
{
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\FixerConfiguration;
final class AliasedFixerOption implements FixerOptionInterface
{
private FixerOptionInterface $fixerOption;
private string $alias;
public function __construct(FixerOptionInterface $fixerOption, string $alias)
{
$this->fixerOption = $fixerOption;
$this->alias = $alias;
}
public function getAlias(): string
{
return $this->alias;
}
public function getName(): string
{
return $this->fixerOption->getName();
}
public function getDescription(): string
{
return $this->fixerOption->getDescription();
}
public function hasDefault(): bool
{
return $this->fixerOption->hasDefault();
}
public function getDefault()
{
return $this->fixerOption->getDefault();
}
public function getAllowedTypes(): ?array
{
return $this->fixerOption->getAllowedTypes();
}
public function getAllowedValues(): ?array
{
return $this->fixerOption->getAllowedValues();
}
public function getNormalizer(): ?\Closure
{
return $this->fixerOption->getNormalizer();
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\FixerConfiguration;
final class AliasedFixerOptionBuilder
{
private FixerOptionBuilder $optionBuilder;
private string $alias;
public function __construct(FixerOptionBuilder $optionBuilder, string $alias)
{
$this->optionBuilder = $optionBuilder;
$this->alias = $alias;
}
public function setDefault($default): self
{
$this->optionBuilder->setDefault($default);
return $this;
}
public function setAllowedTypes(array $allowedTypes): self
{
$this->optionBuilder->setAllowedTypes($allowedTypes);
return $this;
}
public function setAllowedValues(array $allowedValues): self
{
$this->optionBuilder->setAllowedValues($allowedValues);
return $this;
}
public function setNormalizer(\Closure $normalizer): self
{
$this->optionBuilder->setNormalizer($normalizer);
return $this;
}
public function getOption(): AliasedFixerOption
{
return new AliasedFixerOption(
$this->optionBuilder->getOption(),
$this->alias
);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\FixerConfiguration;
interface FixerConfigurationResolverInterface
{
public function getOptions(): array;
public function resolve(array $configuration): array;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\FixerConfiguration;
final class DeprecatedFixerOption implements DeprecatedFixerOptionInterface
{
private FixerOptionInterface $option;
private string $deprecationMessage;
public function __construct(FixerOptionInterface $option, string $deprecationMessage)
{
$this->option = $option;
$this->deprecationMessage = $deprecationMessage;
}
public function getName(): string
{
return $this->option->getName();
}
public function getDescription(): string
{
return $this->option->getDescription();
}
public function hasDefault(): bool
{
return $this->option->hasDefault();
}
public function getDefault()
{
return $this->option->getDefault();
}
public function getAllowedTypes(): ?array
{
return $this->option->getAllowedTypes();
}
public function getAllowedValues(): ?array
{
return $this->option->getAllowedValues();
}
public function getNormalizer(): ?\Closure
{
return $this->option->getNormalizer();
}
public function getDeprecationMessage(): string
{
return $this->deprecationMessage;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer;
use Symfony\Contracts\EventDispatcher\Event;
final class FixerFileProcessedEvent extends Event
{
public const NAME = 'fixer.file_processed';
public const STATUS_INVALID = 1;
public const STATUS_SKIPPED = 2;
public const STATUS_NO_CHANGES = 3;
public const STATUS_FIXED = 4;
public const STATUS_EXCEPTION = 5;
public const STATUS_LINT = 6;
private int $status;
public function __construct(int $status)
{
$this->status = $status;
}
public function getStatus(): int
{
return $this->status;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
abstract class AbstractLinesBeforeNamespaceFixer extends AbstractFixer implements WhitespacesAwareFixerInterface
{
protected function fixLinesBeforeNamespace(Tokens $tokens, int $index, int $expectedMin, int $expectedMax): void
{
$openingTokenIndex = null;
$precedingNewlines = 0;
$newlineInOpening = false;
$openingToken = null;
for ($i = 1; $i <= 2; ++$i) {
if (isset($tokens[$index - $i])) {
$token = $tokens[$index - $i];
if ($token->isGivenKind(T_OPEN_TAG)) {
$openingToken = $token;
$openingTokenIndex = $index - $i;
$newlineInOpening = str_contains($token->getContent(), "\n");
if ($newlineInOpening) {
++$precedingNewlines;
}
break;
}
if (false === $token->isGivenKind(T_WHITESPACE)) {
break;
}
$precedingNewlines += substr_count($token->getContent(), "\n");
}
}
if ($precedingNewlines >= $expectedMin && $precedingNewlines <= $expectedMax) {
return;
}
$previousIndex = $index - 1;
$previous = $tokens[$previousIndex];
if (0 === $expectedMax) {
if ($previous->isWhitespace()) {
$tokens->clearAt($previousIndex);
}
if ($newlineInOpening) {
$tokens[$openingTokenIndex] = new Token([T_OPEN_TAG, rtrim($openingToken->getContent()).' ']);
}
return;
}
$lineEnding = $this->whitespacesConfig->getLineEnding();
$newlinesForWhitespaceToken = $expectedMax;
if (null !== $openingToken) {
$content = rtrim($openingToken->getContent());
$newContent = $content.$lineEnding;
$tokens[$openingTokenIndex] = new Token([T_OPEN_TAG, $newContent]);
--$newlinesForWhitespaceToken;
}
if (0 === $newlinesForWhitespaceToken) {
if ($previous->isWhitespace()) {
$tokens->clearAt($previousIndex);
}
return;
}
if ($previous->isWhitespace()) {
$tokens[$previousIndex] = new Token([T_WHITESPACE, str_repeat($lineEnding, $newlinesForWhitespaceToken).substr($previous->getContent(), strrpos($previous->getContent(), "\n") + 1)]);
} else {
$tokens->insertAt($index, new Token([T_WHITESPACE, str_repeat($lineEnding, $newlinesForWhitespaceToken)]));
}
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer;
use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
use PhpCsFixer\Tokenizer\Tokens;
abstract class AbstractFopenFlagFixer extends AbstractFunctionReferenceFixer
{
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAllTokenKindsFound([T_STRING, T_CONSTANT_ENCAPSED_STRING]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$argumentsAnalyzer = new ArgumentsAnalyzer();
$index = 0;
$end = $tokens->count() - 1;
while (true) {
$candidate = $this->find('fopen', $tokens, $index, $end);
if (null === $candidate) {
break;
}
$index = $candidate[1];
$arguments = $argumentsAnalyzer->getArguments(
$tokens,
$index,
$candidate[2]
);
$argumentsCount = \count($arguments);
if ($argumentsCount < 2 || $argumentsCount > 4) {
continue;
}
$argumentStartIndex = array_keys($arguments)[1];
$this->fixFopenFlagToken(
$tokens,
$argumentStartIndex,
$arguments[$argumentStartIndex]
);
}
}
abstract protected function fixFopenFlagToken(Tokens $tokens, int $argumentStartIndex, int $argumentEndIndex): void;
protected function isValidModeString(string $mode): bool
{
$modeLength = \strlen($mode);
if ($modeLength < 1 || $modeLength > 13) {
return false;
}
$validFlags = [
'a' => true,
'b' => true,
'c' => true,
'e' => true,
'r' => true,
't' => true,
'w' => true,
'x' => true,
];
if (!isset($validFlags[$mode[0]])) {
return false;
}
unset($validFlags[$mode[0]]);
for ($i = 1; $i < $modeLength; ++$i) {
if (isset($validFlags[$mode[$i]])) {
unset($validFlags[$mode[$i]]);
continue;
}
if ('+' !== $mode[$i]
|| (
'a' !== $mode[$i - 1]
&& 'c' !== $mode[$i - 1]
&& 'r' !== $mode[$i - 1]
&& 'w' !== $mode[$i - 1]
&& 'x' !== $mode[$i - 1]
)
) {
return false;
}
}
return true;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer;
final class PharChecker implements PharCheckerInterface
{
public function checkFileValidity(string $filename): ?string
{
try {
$phar = new \Phar($filename);
unset($phar);
} catch (\Exception $e) {
if (!$e instanceof \UnexpectedValueException && !$e instanceof \PharException) {
throw $e;
}
return 'Failed to create Phar instance. '.$e->getMessage();
}
return null;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Differ;
use SebastianBergmann\Diff\Differ;
use SebastianBergmann\Diff\Output\StrictUnifiedDiffOutputBuilder;
final class FullDiffer implements DifferInterface
{
private Differ $differ;
public function __construct()
{
$this->differ = new Differ(new StrictUnifiedDiffOutputBuilder([
'collapseRanges' => false,
'commonLineThreshold' => 100,
'contextLines' => 100,
'fromFile' => 'Original',
'toFile' => 'New',
]));
}
public function diff(string $old, string $new, ?\SplFileInfo $file = null): string
{
return $this->differ->diff($old, $new);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Differ;
use PhpCsFixer\Preg;
use Symfony\Component\Console\Formatter\OutputFormatter;
final class DiffConsoleFormatter
{
private bool $isDecoratedOutput;
private string $template;
public function __construct(bool $isDecoratedOutput, string $template = '%s')
{
$this->isDecoratedOutput = $isDecoratedOutput;
$this->template = $template;
}
public function format(string $diff, string $lineTemplate = '%s'): string
{
$isDecorated = $this->isDecoratedOutput;
$template = $isDecorated
? $this->template
: Preg::replace('/<[^<>]+>/', '', $this->template)
;
return sprintf(
$template,
implode(
PHP_EOL,
array_map(
static function (string $line) use ($isDecorated, $lineTemplate): string {
if ($isDecorated) {
$count = 0;
$line = Preg::replaceCallback(
'/^([+\-@].*)/',
static function (array $matches): string {
if ('+' === $matches[0][0]) {
$colour = 'green';
} elseif ('-' === $matches[0][0]) {
$colour = 'red';
} else {
$colour = 'cyan';
}
return sprintf('<fg=%s>%s</fg=%s>', $colour, OutputFormatter::escape($matches[0]), $colour);
},
$line,
1,
$count
);
if (0 === $count) {
$line = OutputFormatter::escape($line);
}
}
return sprintf($lineTemplate, $line);
},
Preg::split('#\R#u', $diff)
)
)
);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Differ;
interface DifferInterface
{
public function diff(string $old, string $new, ?\SplFileInfo $file = null): string;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Differ;
final class NullDiffer implements DifferInterface
{
public function diff(string $old, string $new, ?\SplFileInfo $file = null): string
{
return '';
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Differ;
use PhpCsFixer\Preg;
use SebastianBergmann\Diff\Differ;
use SebastianBergmann\Diff\Output\StrictUnifiedDiffOutputBuilder;
final class UnifiedDiffer implements DifferInterface
{
public function diff(string $old, string $new, ?\SplFileInfo $file = null): string
{
if (null === $file) {
$options = [
'fromFile' => 'Original',
'toFile' => 'New',
];
} else {
$filePath = $file->getRealPath();
if (1 === Preg::match('/\s/', $filePath)) {
$filePath = '"'.$filePath.'"';
}
$options = [
'fromFile' => $filePath,
'toFile' => $filePath,
];
}
$differ = new Differ(new StrictUnifiedDiffOutputBuilder($options));
return $differ->diff($old, $new);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer;
final class FixerNameValidator
{
public function isValid(string $name, bool $isCustom): bool
{
if (!$isCustom) {
return 1 === Preg::match('/^[a-z][a-z0-9_]*$/', $name);
}
return 1 === Preg::match('/^[A-Z][a-zA-Z0-9]*\/[a-z][a-z0-9_]*$/', $name);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer;
use PhpCsFixer\Fixer\FixerInterface;
class Config implements ConfigInterface
{
private string $cacheFile = '.php-cs-fixer.cache';
private array $customFixers = [];
private ?iterable $finder = null;
private string $format = 'txt';
private bool $hideProgress = false;
private string $indent = ' ';
private bool $isRiskyAllowed = false;
private string $lineEnding = "\n";
private string $name;
private $phpExecutable;
/**
@TODO:
*/
private array $rules = ['@PSR12' => true];
private bool $usingCache = true;
public function __construct(string $name = 'default')
{
$this->name = $name;
}
public function getCacheFile(): string
{
return $this->cacheFile;
}
public function getCustomFixers(): array
{
return $this->customFixers;
}
public function getFinder(): iterable
{
if (null === $this->finder) {
$this->finder = new Finder();
}
return $this->finder;
}
public function getFormat(): string
{
return $this->format;
}
public function getHideProgress(): bool
{
return $this->hideProgress;
}
public function getIndent(): string
{
return $this->indent;
}
public function getLineEnding(): string
{
return $this->lineEnding;
}
public function getName(): string
{
return $this->name;
}
public function getPhpExecutable(): ?string
{
return $this->phpExecutable;
}
public function getRiskyAllowed(): bool
{
return $this->isRiskyAllowed;
}
public function getRules(): array
{
return $this->rules;
}
public function getUsingCache(): bool
{
return $this->usingCache;
}
public function registerCustomFixers(iterable $fixers): ConfigInterface
{
foreach ($fixers as $fixer) {
$this->addCustomFixer($fixer);
}
return $this;
}
public function setCacheFile(string $cacheFile): ConfigInterface
{
$this->cacheFile = $cacheFile;
return $this;
}
public function setFinder(iterable $finder): ConfigInterface
{
$this->finder = $finder;
return $this;
}
public function setFormat(string $format): ConfigInterface
{
$this->format = $format;
return $this;
}
public function setHideProgress(bool $hideProgress): ConfigInterface
{
$this->hideProgress = $hideProgress;
return $this;
}
public function setIndent(string $indent): ConfigInterface
{
$this->indent = $indent;
return $this;
}
public function setLineEnding(string $lineEnding): ConfigInterface
{
$this->lineEnding = $lineEnding;
return $this;
}
public function setPhpExecutable(?string $phpExecutable): ConfigInterface
{
$this->phpExecutable = $phpExecutable;
return $this;
}
public function setRiskyAllowed(bool $isRiskyAllowed): ConfigInterface
{
$this->isRiskyAllowed = $isRiskyAllowed;
return $this;
}
public function setRules(array $rules): ConfigInterface
{
$this->rules = $rules;
return $this;
}
public function setUsingCache(bool $usingCache): ConfigInterface
{
$this->usingCache = $usingCache;
return $this;
}
private function addCustomFixer(FixerInterface $fixer): void
{
$this->customFixers[] = $fixer;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Doctrine\Annotation;
use Doctrine\Common\Annotations\DocLexer;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token as PhpToken;
/**
@extends
*/
final class Tokens extends \SplFixedArray
{
public static function createFromDocComment(PhpToken $input, array $ignoredTags = []): self
{
if (!$input->isGivenKind(T_DOC_COMMENT)) {
throw new \InvalidArgumentException('Input must be a T_DOC_COMMENT token.');
}
$tokens = [];
$content = $input->getContent();
$ignoredTextPosition = 0;
$currentPosition = 0;
$token = null;
while (false !== $nextAtPosition = strpos($content, '@', $currentPosition)) {
if (0 !== $nextAtPosition && !Preg::match('/\s/', $content[$nextAtPosition - 1])) {
$currentPosition = $nextAtPosition + 1;
continue;
}
$lexer = new DocLexer();
$lexer->setInput(substr($content, $nextAtPosition));
$scannedTokens = [];
$index = 0;
$nbScannedTokensToUse = 0;
$nbScopes = 0;
while (null !== $token = $lexer->peek()) {
if (0 === $index && DocLexer::T_AT !== $token['type']) {
break;
}
if (1 === $index) {
if (DocLexer::T_IDENTIFIER !== $token['type'] || \in_array($token['value'], $ignoredTags, true)) {
break;
}
$nbScannedTokensToUse = 2;
}
if ($index >= 2 && 0 === $nbScopes && !\in_array($token['type'], [DocLexer::T_NONE, DocLexer::T_OPEN_PARENTHESIS], true)) {
break;
}
$scannedTokens[] = $token;
if (DocLexer::T_OPEN_PARENTHESIS === $token['type']) {
++$nbScopes;
} elseif (DocLexer::T_CLOSE_PARENTHESIS === $token['type']) {
if (0 === --$nbScopes) {
$nbScannedTokensToUse = \count($scannedTokens);
break;
}
}
++$index;
}
if (0 !== $nbScopes) {
break;
}
if (0 !== $nbScannedTokensToUse) {
$ignoredTextLength = $nextAtPosition - $ignoredTextPosition;
if (0 !== $ignoredTextLength) {
$tokens[] = new Token(DocLexer::T_NONE, substr($content, $ignoredTextPosition, $ignoredTextLength));
}
$lastTokenEndIndex = 0;
foreach (\array_slice($scannedTokens, 0, $nbScannedTokensToUse) as $token) {
if (DocLexer::T_STRING === $token['type']) {
$token['value'] = '"'.str_replace('"', '""', $token['value']).'"';
}
$missingTextLength = $token['position'] - $lastTokenEndIndex;
if ($missingTextLength > 0) {
$tokens[] = new Token(DocLexer::T_NONE, substr(
$content,
$nextAtPosition + $lastTokenEndIndex,
$missingTextLength
));
}
$tokens[] = new Token($token['type'], $token['value']);
$lastTokenEndIndex = $token['position'] + \strlen($token['value']);
}
$currentPosition = $ignoredTextPosition = $nextAtPosition + $token['position'] + \strlen($token['value']);
} else {
$currentPosition = $nextAtPosition + 1;
}
}
if ($ignoredTextPosition < \strlen($content)) {
$tokens[] = new Token(DocLexer::T_NONE, substr($content, $ignoredTextPosition));
}
return self::fromArray($tokens);
}
public static function fromArray($array, $saveIndices = null): self
{
$tokens = new self(\count($array));
if (null === $saveIndices || $saveIndices) {
foreach ($array as $key => $val) {
$tokens[$key] = $val;
}
} else {
$index = 0;
foreach ($array as $val) {
$tokens[$index++] = $val;
}
}
return $tokens;
}
public function getNextMeaningfulToken(int $index): ?int
{
return $this->getMeaningfulTokenSibling($index, 1);
}
public function getPreviousMeaningfulToken(int $index): ?int
{
return $this->getMeaningfulTokenSibling($index, -1);
}
public function getAnnotationEnd(int $index): ?int
{
$currentIndex = null;
if (isset($this[$index + 2])) {
if ($this[$index + 2]->isType(DocLexer::T_OPEN_PARENTHESIS)) {
$currentIndex = $index + 2;
} elseif (
isset($this[$index + 3])
&& $this[$index + 2]->isType(DocLexer::T_NONE)
&& $this[$index + 3]->isType(DocLexer::T_OPEN_PARENTHESIS)
&& Preg::match('/^(\R\s*\*\s*)*\s*$/', $this[$index + 2]->getContent())
) {
$currentIndex = $index + 3;
}
}
if (null !== $currentIndex) {
$level = 0;
for ($max = \count($this); $currentIndex < $max; ++$currentIndex) {
if ($this[$currentIndex]->isType(DocLexer::T_OPEN_PARENTHESIS)) {
++$level;
} elseif ($this[$currentIndex]->isType(DocLexer::T_CLOSE_PARENTHESIS)) {
--$level;
}
if (0 === $level) {
return $currentIndex;
}
}
return null;
}
return $index + 1;
}
public function getCode(): string
{
$code = '';
foreach ($this as $token) {
$code .= $token->getContent();
}
return $code;
}
public function insertAt(int $index, Token $token): void
{
$this->setSize($this->getSize() + 1);
for ($i = $this->getSize() - 1; $i > $index; --$i) {
$this[$i] = $this[$i - 1] ?? new Token();
}
$this[$index] = $token;
}
public function offsetSet($index, $token): void
{
if (null === $token) {
throw new \InvalidArgumentException('Token must be an instance of PhpCsFixer\\Doctrine\\Annotation\\Token, "null" given.');
}
if (!$token instanceof Token) {
$type = \gettype($token);
if ('object' === $type) {
$type = \get_class($token);
}
throw new \InvalidArgumentException(sprintf('Token must be an instance of PhpCsFixer\\Doctrine\\Annotation\\Token, "%s" given.', $type));
}
parent::offsetSet($index, $token);
}
public function offsetUnset($index): void
{
if (!isset($this[$index])) {
throw new \OutOfBoundsException(sprintf('Index "%s" is invalid or does not exist.', $index));
}
$max = \count($this) - 1;
while ($index < $max) {
$this[$index] = $this[$index + 1];
++$index;
}
parent::offsetUnset($index);
$this->setSize($max);
}
private function getMeaningfulTokenSibling(int $index, int $direction): ?int
{
while (true) {
$index += $direction;
if (!$this->offsetExists($index)) {
break;
}
if (!$this[$index]->isType(DocLexer::T_NONE)) {
return $index;
}
}
return null;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Doctrine\Annotation;
use Doctrine\Common\Annotations\DocLexer;
final class Token
{
private int $type;
private string $content;
public function __construct(int $type = DocLexer::T_NONE, string $content = '')
{
$this->type = $type;
$this->content = $content;
}
public function getType(): int
{
return $this->type;
}
public function setType(int $type): void
{
$this->type = $type;
}
public function getContent(): string
{
return $this->content;
}
public function setContent(string $content): void
{
$this->content = $content;
}
public function isType($types): bool
{
if (!\is_array($types)) {
$types = [$types];
}
return \in_array($this->getType(), $types, true);
}
public function clear(): void
{
$this->setContent('');
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer;
final class WordMatcher
{
private array $candidates;
public function __construct(array $candidates)
{
$this->candidates = $candidates;
}
public function match(string $needle): ?string
{
$word = null;
$distance = ceil(\strlen($needle) * 0.35);
foreach ($this->candidates as $candidate) {
$candidateDistance = levenshtein($needle, $candidate);
if ($candidateDistance < $distance) {
$word = $candidate;
$distance = $candidateDistance;
}
}
return $word;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer;
use PhpCsFixer\Fixer\FixerInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\Tokenizer\Tokens;
abstract class AbstractProxyFixer extends AbstractFixer
{
protected array $proxyFixers = [];
public function __construct()
{
foreach (Utils::sortFixers($this->createProxyFixers()) as $proxyFixer) {
$this->proxyFixers[$proxyFixer->getName()] = $proxyFixer;
}
parent::__construct();
}
public function isCandidate(Tokens $tokens): bool
{
foreach ($this->proxyFixers as $fixer) {
if ($fixer->isCandidate($tokens)) {
return true;
}
}
return false;
}
public function isRisky(): bool
{
foreach ($this->proxyFixers as $fixer) {
if ($fixer->isRisky()) {
return true;
}
}
return false;
}
public function getPriority(): int
{
if (\count($this->proxyFixers) > 1) {
throw new \LogicException('You need to override this method to provide the priority of combined fixers.');
}
return reset($this->proxyFixers)->getPriority();
}
public function supports(\SplFileInfo $file): bool
{
foreach ($this->proxyFixers as $fixer) {
if ($fixer->supports($file)) {
return true;
}
}
return false;
}
public function setWhitespacesConfig(WhitespacesFixerConfig $config): void
{
parent::setWhitespacesConfig($config);
foreach ($this->proxyFixers as $fixer) {
if ($fixer instanceof WhitespacesAwareFixerInterface) {
$fixer->setWhitespacesConfig($config);
}
}
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($this->proxyFixers as $fixer) {
$fixer->fix($file, $tokens);
}
}
abstract protected function createProxyFixers(): array;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer;
final class StdinFileInfo extends \SplFileInfo
{
public function __construct()
{
}
public function __toString(): string
{
return $this->getRealPath();
}
public function getRealPath(): string
{
return 'php://stdin';
}
public function getATime(): int
{
return 0;
}
public function getBasename($suffix = null): string
{
return $this->getFilename();
}
public function getCTime(): int
{
return 0;
}
public function getExtension(): string
{
return '.php';
}
public function getFileInfo($className = null): \SplFileInfo
{
throw new \BadMethodCallException(sprintf('Method "%s" is not implemented.', __METHOD__));
}
public function getFilename(): string
{
return 'stdin.php';
}
public function getGroup(): int
{
return 0;
}
public function getInode(): int
{
return 0;
}
public function getLinkTarget(): string
{
return '';
}
public function getMTime(): int
{
return 0;
}
public function getOwner(): int
{
return 0;
}
public function getPath(): string
{
return '';
}
public function getPathInfo($className = null): \SplFileInfo
{
throw new \BadMethodCallException(sprintf('Method "%s" is not implemented.', __METHOD__));
}
public function getPathname(): string
{
return $this->getFilename();
}
public function getPerms(): int
{
return 0;
}
public function getSize(): int
{
return 0;
}
public function getType(): string
{
return 'file';
}
public function isDir(): bool
{
return false;
}
public function isExecutable(): bool
{
return false;
}
public function isFile(): bool
{
return true;
}
public function isLink(): bool
{
return false;
}
public function isReadable(): bool
{
return true;
}
public function isWritable(): bool
{
return false;
}
public function openFile($openMode = 'r', $useIncludePath = false, $context = null): \SplFileObject
{
throw new \BadMethodCallException(sprintf('Method "%s" is not implemented.', __METHOD__));
}
public function setFileClass($className = null): void
{
}
public function setInfoClass($className = null): void
{
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\DocBlock;
use PhpCsFixer\Preg;
final class Tag
{
public const PSR_STANDARD_TAGS = [
'api', 'author', 'category', 'copyright', 'deprecated', 'example',
'global', 'internal', 'license', 'link', 'method', 'package', 'param',
'property', 'property-read', 'property-write', 'return', 'see',
'since', 'subpackage', 'throws', 'todo', 'uses', 'var', 'version',
];
private Line $line;
private ?string $name = null;
public function __construct(Line $line)
{
$this->line = $line;
}
public function getName(): string
{
if (null === $this->name) {
Preg::matchAll('/@[a-zA-Z0-9_-]+(?=\s|$)/', $this->line->getContent(), $matches);
if (isset($matches[0][0])) {
$this->name = ltrim($matches[0][0], '@');
} else {
$this->name = 'other';
}
}
return $this->name;
}
public function setName(string $name): void
{
$current = $this->getName();
if ('other' === $current) {
throw new \RuntimeException('Cannot set name on unknown tag.');
}
$this->line->setContent(Preg::replace("/@{$current}/", "@{$name}", $this->line->getContent(), 1));
$this->name = $name;
}
public function valid(): bool
{
return \in_array($this->getName(), self::PSR_STANDARD_TAGS, true);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\DocBlock;
final class TagComparator
{
public const DEFAULT_GROUPS = [
['deprecated', 'link', 'see', 'since'],
['author', 'copyright', 'license'],
['category', 'package', 'subpackage'],
['property', 'property-read', 'property-write'],
];
public static function shouldBeTogether(Tag $first, Tag $second, array $groups = self::DEFAULT_GROUPS): bool
{
$firstName = $first->getName();
$secondName = $second->getName();
if ($firstName === $secondName) {
return true;
}
foreach ($groups as $group) {
if (\in_array($firstName, $group, true) && \in_array($secondName, $group, true)) {
return true;
}
}
return false;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\DocBlock;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceUseAnalysis;
final class Annotation
{
private static array $tags = [
'method',
'param',
'property',
'property-read',
'property-write',
'return',
'throws',
'type',
'var',
];
private array $lines;
private $start;
private $end;
private $tag;
private $typesContent;
private $types;
private $namespace;
private array $namespaceUses;
public function __construct(array $lines, $namespace = null, array $namespaceUses = [])
{
$this->lines = array_values($lines);
$this->namespace = $namespace;
$this->namespaceUses = $namespaceUses;
$keys = array_keys($lines);
$this->start = $keys[0];
$this->end = end($keys);
}
public function __toString(): string
{
return $this->getContent();
}
public static function getTagsWithTypes(): array
{
return self::$tags;
}
public function getStart(): int
{
return $this->start;
}
public function getEnd(): int
{
return $this->end;
}
public function getTag(): Tag
{
if (null === $this->tag) {
$this->tag = new Tag($this->lines[0]);
}
return $this->tag;
}
public function getTypeExpression(): TypeExpression
{
return new TypeExpression($this->getTypesContent(), $this->namespace, $this->namespaceUses);
}
public function getVariableName()
{
$type = preg_quote($this->getTypesContent(), '/');
$regex = "/@{$this->tag->getName()}\\s+({$type}\\s*)?(&\\s*)?(\\.{3}\\s*)?(?<variable>\\$.+?)(?:[\\s*]|$)/";
if (Preg::match($regex, $this->lines[0]->getContent(), $matches)) {
return $matches['variable'];
}
return null;
}
public function getTypes(): array
{
if (null === $this->types) {
$this->types = $this->getTypeExpression()->getTypes();
}
return $this->types;
}
public function setTypes(array $types): void
{
$pattern = '/'.preg_quote($this->getTypesContent(), '/').'/';
$this->lines[0]->setContent(Preg::replace($pattern, implode($this->getTypeExpression()->getTypesGlue(), $types), $this->lines[0]->getContent(), 1));
$this->clearCache();
}
public function getNormalizedTypes(): array
{
$normalized = array_map(static function (string $type): string {
return strtolower($type);
}, $this->getTypes());
sort($normalized);
return $normalized;
}
public function remove(): void
{
foreach ($this->lines as $line) {
if ($line->isTheStart() && $line->isTheEnd()) {
$line->remove();
} elseif ($line->isTheStart()) {
$content = Preg::replace('#(\s*/\*\*).*#', '$1', $line->getContent());
$line->setContent($content);
} elseif ($line->isTheEnd()) {
$content = Preg::replace('#(\s*)\S.*(\*/.*)#', '$1$2', $line->getContent());
$line->setContent($content);
} else {
$line->remove();
}
}
$this->clearCache();
}
public function getContent(): string
{
return implode('', $this->lines);
}
public function supportTypes(): bool
{
return \in_array($this->getTag()->getName(), self::$tags, true);
}
private function getTypesContent(): string
{
if (null === $this->typesContent) {
$name = $this->getTag()->getName();
if (!$this->supportTypes()) {
throw new \RuntimeException('This tag does not support types.');
}
$matchingResult = Preg::match(
'{^(?:\s*\*|/\*\*)\s*@'.$name.'\s+'.TypeExpression::REGEX_TYPES.'(?:(?:[*\h\v]|\&[\.\$]).*)?\r?$}isx',
$this->lines[0]->getContent(),
$matches
);
$this->typesContent = 1 === $matchingResult
? $matches['types']
: '';
}
return $this->typesContent;
}
private function clearCache(): void
{
$this->types = null;
$this->typesContent = null;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\DocBlock;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceUseAnalysis;
use PhpCsFixer\Utils;
final class TypeExpression
{
public const REGEX_TYPES = '
(?<types> # several types separated by `|` or `&`
(?<type> # single type
(?<nullable>\??)
(?:
(?<object_like_array>
(?<object_like_array_start>array\h*\{)
(?<object_like_array_keys>
(?<object_like_array_key>
\h*[^?:\h]+\h*\??\h*:\h*(?&types)
)
(?:\h*,(?&object_like_array_key))*
)
\h*\}
)
|
(?<callable> # callable syntax, e.g. `callable(string): bool`
(?<callable_start>(?:callable|Closure)\h*\(\h*)
(?<callable_arguments>
(?&types)
(?:
\h*,\h*
(?&types)
)*
)?
\h*\)
(?:
\h*\:\h*
(?<callable_return>(?&types))
)?
)
|
(?<generic> # generic syntax, e.g.: `array<int, \Foo\Bar>`
(?<generic_start>
(?&name)+
\h*<\h*
)
(?<generic_types>
(?&types)
(?:
\h*,\h*
(?&types)
)*
)
\h*>
)
|
(?<class_constant> # class constants with optional wildcard, e.g.: `Foo::*`, `Foo::CONST_A`, `FOO::CONST_*`
(?&name)::(\*|\w+\*?)
)
|
(?<array> # array expression, e.g.: `string[]`, `string[][]`
(?&name)(\[\])+
)
|
(?<constant> # single constant value (case insensitive), e.g.: 1, `\'a\'`
(?i)
null | true | false
| -?(?:\d+(?:\.\d*)?|\.\d+) # all sorts of numbers with or without minus, e.g.: 1, 1.1, 1., .1, -1
| \'[^\']+?\' | "[^"]+?"
| [@$]?(?:this | self | static)
(?-i)
)
|
(?<name> # single type, e.g.: `null`, `int`, `\Foo\Bar`
[\\\\\w-]++
)
)
)
(?:
\h*(?<glue>[|&])\h*
(?&type)
)*
)
';
private string $value;
private bool $isUnionType = false;
private array $innerTypeExpressions = [];
private string $typesGlue = '|';
private ?NamespaceAnalysis $namespace;
private array $namespaceUses;
public function __construct(string $value, ?NamespaceAnalysis $namespace, array $namespaceUses)
{
$this->value = $value;
$this->namespace = $namespace;
$this->namespaceUses = $namespaceUses;
$this->parse();
}
public function toString(): string
{
return $this->value;
}
public function getTypes(): array
{
if ($this->isUnionType) {
return array_map(
static fn (array $type) => $type['expression']->toString(),
$this->innerTypeExpressions,
);
}
return [$this->value];
}
public function sortTypes(callable $compareCallback): void
{
foreach (array_reverse($this->innerTypeExpressions) as [
'start_index' => $startIndex,
'expression' => $inner,
]) {
$initialValueLength = \strlen($inner->toString());
$inner->sortTypes($compareCallback);
$this->value = substr_replace(
$this->value,
$inner->toString(),
$startIndex,
$initialValueLength
);
}
if ($this->isUnionType) {
$this->innerTypeExpressions = Utils::stableSort(
$this->innerTypeExpressions,
static fn (array $type): self => $type['expression'],
$compareCallback,
);
$this->value = implode($this->getTypesGlue(), $this->getTypes());
}
}
public function getTypesGlue(): string
{
return $this->typesGlue;
}
public function getCommonType(): ?string
{
$aliases = $this->getAliases();
$mainType = null;
foreach ($this->getTypes() as $type) {
if ('null' === $type) {
continue;
}
if (isset($aliases[$type])) {
$type = $aliases[$type];
} elseif (1 === Preg::match('/\[\]$/', $type)) {
$type = 'array';
} elseif (1 === Preg::match('/^(.+?)</', $type, $matches)) {
$type = $matches[1];
}
if (null === $mainType || $type === $mainType) {
$mainType = $type;
continue;
}
$mainType = $this->getParentType($type, $mainType);
if (null === $mainType) {
return null;
}
}
return $mainType;
}
public function allowsNull(): bool
{
foreach ($this->getTypes() as $type) {
if (\in_array($type, ['null', 'mixed'], true)) {
return true;
}
}
return false;
}
private function parse(): void
{
$value = $this->value;
Preg::match(
'{^'.self::REGEX_TYPES.'$}x',
$value,
$matches
);
if ([] === $matches) {
return;
}
$this->typesGlue = $matches['glue'] ?? $this->typesGlue;
$index = '' !== $matches['nullable'] ? 1 : 0;
if ($matches['type'] !== $matches['types']) {
$this->isUnionType = true;
while (true) {
$innerType = $matches['type'];
$newValue = Preg::replace(
'/^'.preg_quote($innerType, '/').'(\h*[|&]\h*)?/',
'',
$value
);
$this->innerTypeExpressions[] = [
'start_index' => $index,
'expression' => $this->inner($innerType),
];
if ('' === $newValue) {
return;
}
$index += \strlen($value) - \strlen($newValue);
$value = $newValue;
Preg::match(
'{^'.self::REGEX_TYPES.'$}x',
$value,
$matches
);
}
}
if ('' !== ($matches['generic'] ?? '')) {
$this->parseCommaSeparatedInnerTypes(
$index + \strlen($matches['generic_start']),
$matches['generic_types']
);
return;
}
if ('' !== ($matches['callable'] ?? '')) {
$this->parseCommaSeparatedInnerTypes(
$index + \strlen($matches['callable_start']),
$matches['callable_arguments'] ?? ''
);
$return = $matches['callable_return'] ?? null;
if (null !== $return) {
$this->innerTypeExpressions[] = [
'start_index' => \strlen($this->value) - \strlen($matches['callable_return']),
'expression' => $this->inner($matches['callable_return']),
];
}
return;
}
if ('' !== ($matches['object_like_array'] ?? '')) {
$this->parseObjectLikeArrayKeys(
$index + \strlen($matches['object_like_array_start']),
$matches['object_like_array_keys']
);
}
}
private function parseCommaSeparatedInnerTypes(int $startIndex, string $value): void
{
while ('' !== $value) {
Preg::match(
'{^'.self::REGEX_TYPES.'\h*(?:,|$)}x',
$value,
$matches
);
$this->innerTypeExpressions[] = [
'start_index' => $startIndex,
'expression' => $this->inner($matches['types']),
];
$newValue = Preg::replace(
'/^'.preg_quote($matches['types'], '/').'(\h*\,\h*)?/',
'',
$value
);
$startIndex += \strlen($value) - \strlen($newValue);
$value = $newValue;
}
}
private function parseObjectLikeArrayKeys(int $startIndex, string $value): void
{
while ('' !== $value) {
Preg::match(
'{(?<_start>^.+?:\h*)'.self::REGEX_TYPES.'\h*(?:,|$)}x',
$value,
$matches
);
$this->innerTypeExpressions[] = [
'start_index' => $startIndex + \strlen($matches['_start']),
'expression' => $this->inner($matches['types']),
];
$newValue = Preg::replace(
'/^.+?:\h*'.preg_quote($matches['types'], '/').'(\h*\,\h*)?/',
'',
$value
);
$startIndex += \strlen($value) - \strlen($newValue);
$value = $newValue;
}
}
private function inner(string $value): self
{
return new self($value, $this->namespace, $this->namespaceUses);
}
private function getParentType(string $type1, string $type2): ?string
{
$types = [
$this->normalize($type1),
$this->normalize($type2),
];
natcasesort($types);
$types = implode('|', $types);
$parents = [
'array|Traversable' => 'iterable',
'array|iterable' => 'iterable',
'iterable|Traversable' => 'iterable',
'self|static' => 'self',
];
return $parents[$types] ?? null;
}
private function normalize(string $type): string
{
$aliases = $this->getAliases();
if (isset($aliases[$type])) {
return $aliases[$type];
}
if (\in_array($type, [
'array',
'bool',
'callable',
'float',
'int',
'iterable',
'mixed',
'never',
'null',
'object',
'resource',
'string',
'void',
], true)) {
return $type;
}
if (1 === Preg::match('/\[\]$/', $type)) {
return 'array';
}
if (1 === Preg::match('/^(.+?)</', $type, $matches)) {
return $matches[1];
}
if (str_starts_with($type, '\\')) {
return substr($type, 1);
}
foreach ($this->namespaceUses as $namespaceUse) {
if ($namespaceUse->getShortName() === $type) {
return $namespaceUse->getFullName();
}
}
if (null === $this->namespace || $this->namespace->isGlobalNamespace()) {
return $type;
}
return "{$this->namespace->getFullName()}\\{$type}";
}
private function getAliases(): array
{
return [
'boolean' => 'bool',
'callback' => 'callable',
'double' => 'float',
'false' => 'bool',
'integer' => 'int',
'real' => 'float',
'true' => 'bool',
];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\DocBlock;
final class ShortDescription
{
private DocBlock $doc;
public function __construct(DocBlock $doc)
{
$this->doc = $doc;
}
public function getEnd(): ?int
{
$reachedContent = false;
foreach ($this->doc->getLines() as $index => $line) {
if ($reachedContent && ($line->containsATag() || !$line->containsUsefulContent())) {
return $index - 1;
}
if ($line->containsATag()) {
return null;
}
if ($line->containsUsefulContent()) {
$reachedContent = true;
}
}
return null;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\DocBlock;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceUseAnalysis;
final class DocBlock
{
private array $lines = [];
private ?array $annotations = null;
private ?NamespaceAnalysis $namespace;
private array $namespaceUses;
public function __construct(string $content, ?NamespaceAnalysis $namespace = null, array $namespaceUses = [])
{
foreach (Preg::split('/([^\n\r]+\R*)/', $content, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) as $line) {
$this->lines[] = new Line($line);
}
$this->namespace = $namespace;
$this->namespaceUses = $namespaceUses;
}
public function __toString(): string
{
return $this->getContent();
}
public function getLines(): array
{
return $this->lines;
}
public function getLine(int $pos): ?Line
{
return $this->lines[$pos] ?? null;
}
public function getAnnotations(): array
{
if (null !== $this->annotations) {
return $this->annotations;
}
$this->annotations = [];
$total = \count($this->lines);
for ($index = 0; $index < $total; ++$index) {
if ($this->lines[$index]->containsATag()) {
$lines = \array_slice($this->lines, $index, $this->findAnnotationLength($index), true);
$annotation = new Annotation($lines, $this->namespace, $this->namespaceUses);
$index = $annotation->getEnd();
$this->annotations[] = $annotation;
}
}
return $this->annotations;
}
public function isMultiLine(): bool
{
return 1 !== \count($this->lines);
}
public function makeMultiLine(string $indent, string $lineEnd): void
{
if ($this->isMultiLine()) {
return;
}
$lineContent = $this->getSingleLineDocBlockEntry($this->lines[0]);
if ('' === $lineContent) {
$this->lines = [
new Line('/**'.$lineEnd),
new Line($indent.' *'.$lineEnd),
new Line($indent.' */'),
];
return;
}
$this->lines = [
new Line('/**'.$lineEnd),
new Line($indent.' * '.$lineContent.$lineEnd),
new Line($indent.' */'),
];
}
public function makeSingleLine(): void
{
if (!$this->isMultiLine()) {
return;
}
$usefulLines = array_filter(
$this->lines,
static function (Line $line): bool {
return $line->containsUsefulContent();
}
);
if (1 < \count($usefulLines)) {
return;
}
$lineContent = '';
if (\count($usefulLines) > 0) {
$lineContent = $this->getSingleLineDocBlockEntry(array_shift($usefulLines));
}
$this->lines = [new Line('/** '.$lineContent.' */')];
}
public function getAnnotation(int $pos): ?Annotation
{
$annotations = $this->getAnnotations();
return $annotations[$pos] ?? null;
}
public function getAnnotationsOfType($types): array
{
$typesToSearchFor = (array) $types;
$annotations = [];
foreach ($this->getAnnotations() as $annotation) {
$tagName = $annotation->getTag()->getName();
if (\in_array($tagName, $typesToSearchFor, true)) {
$annotations[] = $annotation;
}
}
return $annotations;
}
public function getContent(): string
{
return implode('', $this->lines);
}
private function findAnnotationLength(int $start): int
{
$index = $start;
while ($line = $this->getLine(++$index)) {
if ($line->containsATag()) {
break;
}
if (!$line->containsUsefulContent()) {
$next = $this->getLine($index + 1);
if (null === $next || !$next->containsUsefulContent() || $next->containsATag()) {
break;
}
}
}
return $index - $start;
}
private function getSingleLineDocBlockEntry(Line $line): string
{
$lineString = $line->getContent();
if ('' === $lineString) {
return $lineString;
}
$lineString = str_replace('*/', '', $lineString);
$lineString = trim($lineString);
if (str_starts_with($lineString, '/**')) {
$lineString = substr($lineString, 3);
} elseif (str_starts_with($lineString, '*')) {
$lineString = substr($lineString, 1);
}
return trim($lineString);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\DocBlock;
use PhpCsFixer\Preg;
final class Line
{
private string $content;
public function __construct(string $content)
{
$this->content = $content;
}
public function __toString(): string
{
return $this->content;
}
public function getContent(): string
{
return $this->content;
}
public function containsUsefulContent(): bool
{
return 0 !== Preg::match('/\\*\s*\S+/', $this->content) && '' !== trim(str_replace(['/', '*'], ' ', $this->content));
}
public function containsATag(): bool
{
return 0 !== Preg::match('/\\*\s*@/', $this->content);
}
public function isTheStart(): bool
{
return str_contains($this->content, '/**');
}
public function isTheEnd(): bool
{
return str_contains($this->content, '*/');
}
public function setContent(string $content): void
{
$this->content = $content;
}
public function remove(): void
{
$this->content = '';
}
public function addBlank(): void
{
$matched = Preg::match('/^(\h*\*)[^\r\n]*(\r?\n)$/', $this->content, $matches);
if (1 !== $matched) {
return;
}
$this->content .= $matches[1].$matches[2];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\FixerDefinition;
final class CodeSample implements CodeSampleInterface
{
private string $code;
private ?array $configuration;
public function __construct(string $code, ?array $configuration = null)
{
$this->code = $code;
$this->configuration = $configuration;
}
public function getCode(): string
{
return $this->code;
}
public function getConfiguration(): ?array
{
return $this->configuration;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\FixerDefinition;
interface CodeSampleInterface
{
public function getCode(): string;
public function getConfiguration(): ?array;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\FixerDefinition;
interface VersionSpecificCodeSampleInterface extends CodeSampleInterface
{
public function isSuitableFor(int $version): bool;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\FixerDefinition;
final class FixerDefinition implements FixerDefinitionInterface
{
private string $summary;
private array $codeSamples;
private ?string $description;
private ?string $riskyDescription;
public function __construct(
string $summary,
array $codeSamples,
?string $description = null,
?string $riskyDescription = null
) {
$this->summary = $summary;
$this->codeSamples = $codeSamples;
$this->description = $description;
$this->riskyDescription = $riskyDescription;
}
public function getSummary(): string
{
return $this->summary;
}
public function getDescription(): ?string
{
return $this->description;
}
public function getRiskyDescription(): ?string
{
return $this->riskyDescription;
}
public function getCodeSamples(): array
{
return $this->codeSamples;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\FixerDefinition;
final class FileSpecificCodeSample implements FileSpecificCodeSampleInterface
{
private CodeSampleInterface $codeSample;
private \SplFileInfo $splFileInfo;
public function __construct(
string $code,
\SplFileInfo $splFileInfo,
?array $configuration = null
) {
$this->codeSample = new CodeSample($code, $configuration);
$this->splFileInfo = $splFileInfo;
}
public function getCode(): string
{
return $this->codeSample->getCode();
}
public function getConfiguration(): ?array
{
return $this->codeSample->getConfiguration();
}
public function getSplFileInfo(): \SplFileInfo
{
return $this->splFileInfo;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\FixerDefinition;
interface VersionSpecificationInterface
{
public function isSatisfiedBy(int $version): bool;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\FixerDefinition;
interface FixerDefinitionInterface
{
public function getSummary(): string;
public function getDescription(): ?string;
public function getRiskyDescription(): ?string;
public function getCodeSamples(): array;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\FixerDefinition;
final class VersionSpecificCodeSample implements VersionSpecificCodeSampleInterface
{
private CodeSampleInterface $codeSample;
private VersionSpecificationInterface $versionSpecification;
public function __construct(
string $code,
VersionSpecificationInterface $versionSpecification,
?array $configuration = null
) {
$this->codeSample = new CodeSample($code, $configuration);
$this->versionSpecification = $versionSpecification;
}
public function getCode(): string
{
return $this->codeSample->getCode();
}
public function getConfiguration(): ?array
{
return $this->codeSample->getConfiguration();
}
public function isSuitableFor(int $version): bool
{
return $this->versionSpecification->isSatisfiedBy($version);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\FixerDefinition;
final class VersionSpecification implements VersionSpecificationInterface
{
private ?int $minimum;
private ?int $maximum;
public function __construct(?int $minimum = null, ?int $maximum = null)
{
if (null === $minimum && null === $maximum) {
throw new \InvalidArgumentException('Minimum or maximum need to be specified.');
}
if (null !== $minimum && 1 > $minimum) {
throw new \InvalidArgumentException('Minimum needs to be either null or an integer greater than 0.');
}
if (null !== $maximum) {
if (1 > $maximum) {
throw new \InvalidArgumentException('Maximum needs to be either null or an integer greater than 0.');
}
if (null !== $minimum && $maximum < $minimum) {
throw new \InvalidArgumentException('Maximum should not be lower than the minimum.');
}
}
$this->minimum = $minimum;
$this->maximum = $maximum;
}
public function isSatisfiedBy(int $version): bool
{
if (null !== $this->minimum && $version < $this->minimum) {
return false;
}
if (null !== $this->maximum && $version > $this->maximum) {
return false;
}
return true;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\FixerDefinition;
interface FileSpecificCodeSampleInterface extends CodeSampleInterface
{
public function getSplFileInfo(): \SplFileInfo;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer;
use PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\FixerInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\RuleSet\RuleSetInterface;
use Symfony\Component\Finder\Finder as SymfonyFinder;
use Symfony\Component\Finder\SplFileInfo;
final class FixerFactory
{
private FixerNameValidator $nameValidator;
private array $fixers = [];
private array $fixersByName = [];
public function __construct()
{
$this->nameValidator = new FixerNameValidator();
}
public function setWhitespacesConfig(WhitespacesFixerConfig $config): self
{
foreach ($this->fixers as $fixer) {
if ($fixer instanceof WhitespacesAwareFixerInterface) {
$fixer->setWhitespacesConfig($config);
}
}
return $this;
}
public function getFixers(): array
{
$this->fixers = Utils::sortFixers($this->fixers);
return $this->fixers;
}
public function registerBuiltInFixers(): self
{
static $builtInFixers = null;
if (null === $builtInFixers) {
$builtInFixers = [];
foreach (SymfonyFinder::create()->files()->in(__DIR__.'/Fixer')->name('*Fixer.php')->depth(1) as $file) {
$relativeNamespace = $file->getRelativePath();
$fixerClass = 'PhpCsFixer\\Fixer\\'.($relativeNamespace ? $relativeNamespace.'\\' : '').$file->getBasename('.php');
$builtInFixers[] = $fixerClass;
}
}
foreach ($builtInFixers as $class) {
$this->registerFixer(new $class(), false);
}
return $this;
}
public function registerCustomFixers(iterable $fixers): self
{
foreach ($fixers as $fixer) {
$this->registerFixer($fixer, true);
}
return $this;
}
public function registerFixer(FixerInterface $fixer, bool $isCustom): self
{
$name = $fixer->getName();
if (isset($this->fixersByName[$name])) {
throw new \UnexpectedValueException(sprintf('Fixer named "%s" is already registered.', $name));
}
if (!$this->nameValidator->isValid($name, $isCustom)) {
throw new \UnexpectedValueException(sprintf('Fixer named "%s" has invalid name.', $name));
}
$this->fixers[] = $fixer;
$this->fixersByName[$name] = $fixer;
return $this;
}
public function useRuleSet(RuleSetInterface $ruleSet): self
{
$fixers = [];
$fixersByName = [];
$fixerConflicts = [];
$fixerNames = array_keys($ruleSet->getRules());
foreach ($fixerNames as $name) {
if (!\array_key_exists($name, $this->fixersByName)) {
throw new \UnexpectedValueException(sprintf('Rule "%s" does not exist.', $name));
}
$fixer = $this->fixersByName[$name];
$config = $ruleSet->getRuleConfiguration($name);
if (null !== $config) {
if ($fixer instanceof ConfigurableFixerInterface) {
if (\count($config) < 1) {
throw new InvalidFixerConfigurationException($fixer->getName(), 'Configuration must be an array and may not be empty.');
}
$fixer->configure($config);
} else {
throw new InvalidFixerConfigurationException($fixer->getName(), 'Is not configurable.');
}
}
$fixers[] = $fixer;
$fixersByName[$name] = $fixer;
$conflicts = array_intersect($this->getFixersConflicts($fixer), $fixerNames);
if (\count($conflicts) > 0) {
$fixerConflicts[$name] = $conflicts;
}
}
if (\count($fixerConflicts) > 0) {
throw new \UnexpectedValueException($this->generateConflictMessage($fixerConflicts));
}
$this->fixers = $fixers;
$this->fixersByName = $fixersByName;
return $this;
}
public function hasRule(string $name): bool
{
return isset($this->fixersByName[$name]);
}
private function getFixersConflicts(FixerInterface $fixer): ?array
{
static $conflictMap = [
'no_blank_lines_before_namespace' => ['single_blank_line_before_namespace'],
'single_import_per_statement' => ['group_import'],
];
$fixerName = $fixer->getName();
return \array_key_exists($fixerName, $conflictMap) ? $conflictMap[$fixerName] : [];
}
private function generateConflictMessage(array $fixerConflicts): string
{
$message = 'Rule contains conflicting fixers:';
$report = [];
foreach ($fixerConflicts as $fixer => $fixers) {
$report[$fixer] = array_filter(
$fixers,
static function (string $candidate) use ($report, $fixer): bool {
return !\array_key_exists($candidate, $report) || !\in_array($fixer, $report[$candidate], true);
}
);
if (\count($report[$fixer]) > 0) {
$message .= sprintf("\n- \"%s\" with \"%s\"", $fixer, implode('", "', $report[$fixer]));
}
}
return $message;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer;
use PhpCsFixer\Fixer\FixerInterface;
use PhpCsFixer\Tokenizer\Token;
final class Utils
{
private static array $deprecations = [];
private function __construct()
{
}
public static function camelCaseToUnderscore(string $string): string
{
return mb_strtolower(Preg::replace('/(?<!^)((?=[\p{Lu}][^\p{Lu}])|(?<![\p{Lu}])(?=[\p{Lu}]))/', '_', $string));
}
public static function calculateTrailingWhitespaceIndent(Token $token): string
{
if (!$token->isWhitespace()) {
throw new \InvalidArgumentException(sprintf('The given token must be whitespace, got "%s".', $token->getName()));
}
$str = strrchr(
str_replace(["\r\n", "\r"], "\n", $token->getContent()),
"\n"
);
if (false === $str) {
return '';
}
return ltrim($str, "\n");
}
public static function stableSort(array $elements, callable $getComparedValue, callable $compareValues): array
{
array_walk($elements, static function (&$element, int $index) use ($getComparedValue): void {
$element = [$element, $index, $getComparedValue($element)];
});
usort($elements, static function ($a, $b) use ($compareValues): int {
$comparison = $compareValues($a[2], $b[2]);
if (0 !== $comparison) {
return $comparison;
}
return $a[1] <=> $b[1];
});
return array_map(static function (array $item) {
return $item[0];
}, $elements);
}
public static function sortFixers(array $fixers): array
{
return self::stableSort(
$fixers,
static function (FixerInterface $fixer): int {
return $fixer->getPriority();
},
static function (int $a, int $b): int {
return $b <=> $a;
}
);
}
public static function naturalLanguageJoinWithBackticks(array $names): string
{
if (0 === \count($names)) {
throw new \InvalidArgumentException('Array of names cannot be empty.');
}
$names = array_map(static function (string $name): string {
return sprintf('`%s`', $name);
}, $names);
$last = array_pop($names);
if (\count($names) > 0) {
return implode(', ', $names).' and '.$last;
}
return $last;
}
public static function triggerDeprecation(\Exception $futureException): void
{
if (getenv('PHP_CS_FIXER_FUTURE_MODE')) {
throw new \RuntimeException(
'Your are using something deprecated, see previous exception. Aborting execution because `PHP_CS_FIXER_FUTURE_MODE` environment variable is set.',
0,
$futureException
);
}
$message = $futureException->getMessage();
self::$deprecations[$message] = true;
@trigger_error($message, E_USER_DEPRECATED);
}
public static function getTriggeredDeprecations(): array
{
$triggeredDeprecations = array_keys(self::$deprecations);
sort($triggeredDeprecations);
return $triggeredDeprecations;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer;
interface ToolInfoInterface
{
public function getComposerInstallationDetails(): array;
public function getComposerVersion(): string;
public function getVersion(): string;
public function isInstalledAsPhar(): bool;
public function isInstalledByComposer(): bool;
public function getPharDownloadUri(string $version): string;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer;
use PhpCsFixer\DocBlock\Annotation;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\Tokenizer\Analyzer\NamespacesAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\NamespaceUsesAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
abstract class AbstractPhpdocToTypeDeclarationFixer extends AbstractFixer implements ConfigurableFixerInterface
{
private const CLASS_REGEX = '/^\\\\?[a-zA-Z_\\x7f-\\xff](?:\\\\?[a-zA-Z0-9_\\x7f-\\xff]+)*$/';
private array $versionSpecificTypes = [
'void' => 70100,
'iterable' => 70100,
'object' => 70200,
'mixed' => 80000,
];
private array $scalarTypes = [
'bool' => true,
'float' => true,
'int' => true,
'string' => true,
];
private static array $syntaxValidationCache = [];
public function isRisky(): bool
{
return true;
}
abstract protected function isSkippedType(string $type): bool;
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('scalar_types', 'Fix also scalar types; may have unexpected behaviour due to PHP bad type coercion system.'))
->setAllowedTypes(['bool'])
->setDefault(true)
->getOption(),
]);
}
protected function findFunctionDocComment(Tokens $tokens, int $index): ?int
{
do {
$index = $tokens->getPrevNonWhitespace($index);
} while ($tokens[$index]->isGivenKind([
T_COMMENT,
T_ABSTRACT,
T_FINAL,
T_PRIVATE,
T_PROTECTED,
T_PUBLIC,
T_STATIC,
]));
if ($tokens[$index]->isGivenKind(T_DOC_COMMENT)) {
return $index;
}
return null;
}
protected function getAnnotationsFromDocComment(string $name, Tokens $tokens, int $docCommentIndex): array
{
$namespacesAnalyzer = new NamespacesAnalyzer();
$namespace = $namespacesAnalyzer->getNamespaceAt($tokens, $docCommentIndex);
$namespaceUsesAnalyzer = new NamespaceUsesAnalyzer();
$namespaceUses = $namespaceUsesAnalyzer->getDeclarationsInNamespace($tokens, $namespace);
$doc = new DocBlock(
$tokens[$docCommentIndex]->getContent(),
$namespace,
$namespaceUses
);
return $doc->getAnnotationsOfType($name);
}
protected function createTypeDeclarationTokens(string $type, bool $isNullable): array
{
static $specialTypes = [
'array' => [CT::T_ARRAY_TYPEHINT, 'array'],
'callable' => [T_CALLABLE, 'callable'],
'static' => [T_STATIC, 'static'],
];
$newTokens = [];
if (true === $isNullable && 'mixed' !== $type) {
$newTokens[] = new Token([CT::T_NULLABLE_TYPE, '?']);
}
if (isset($specialTypes[$type])) {
$newTokens[] = new Token($specialTypes[$type]);
} else {
$typeUnqualified = ltrim($type, '\\');
if (isset($this->scalarTypes[$typeUnqualified]) || isset($this->versionSpecificTypes[$typeUnqualified])) {
$newTokens[] = new Token([T_STRING, $typeUnqualified]);
} else {
foreach (explode('\\', $type) as $nsIndex => $value) {
if (0 === $nsIndex && '' === $value) {
continue;
}
if (0 < $nsIndex) {
$newTokens[] = new Token([T_NS_SEPARATOR, '\\']);
}
$newTokens[] = new Token([T_STRING, $value]);
}
}
}
return $newTokens;
}
protected function getCommonTypeFromAnnotation(Annotation $annotation, bool $isReturnType): ?array
{
$typesExpression = $annotation->getTypeExpression();
$commonType = $typesExpression->getCommonType();
$isNullable = $typesExpression->allowsNull();
if (null === $commonType) {
return null;
}
if ($isNullable && 'void' === $commonType) {
return null;
}
if ('static' === $commonType && (!$isReturnType || \PHP_VERSION_ID < 80000)) {
$commonType = 'self';
}
if ($this->isSkippedType($commonType)) {
return null;
}
if (isset($this->versionSpecificTypes[$commonType]) && \PHP_VERSION_ID < $this->versionSpecificTypes[$commonType]) {
return null;
}
if (isset($this->scalarTypes[$commonType])) {
if (false === $this->configuration['scalar_types']) {
return null;
}
} elseif (1 !== Preg::match(self::CLASS_REGEX, $commonType)) {
return null;
}
return [$commonType, $isNullable];
}
final protected function isValidSyntax(string $code): bool
{
if (!isset(self::$syntaxValidationCache[$code])) {
try {
Tokens::fromCode($code);
self::$syntaxValidationCache[$code] = true;
} catch (\ParseError $e) {
self::$syntaxValidationCache[$code] = false;
}
}
return self::$syntaxValidationCache[$code];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer;
final class WhitespacesFixerConfig
{
private string $indent;
private string $lineEnding;
public function __construct(string $indent = ' ', string $lineEnding = "\n")
{
if (!\in_array($indent, [' ', ' ', "\t"], true)) {
throw new \InvalidArgumentException('Invalid "indent" param, expected tab or two or four spaces.');
}
if (!\in_array($lineEnding, ["\n", "\r\n"], true)) {
throw new \InvalidArgumentException('Invalid "lineEnding" param, expected "\n" or "\r\n".');
}
$this->indent = $indent;
$this->lineEnding = $lineEnding;
}
public function getIndent(): string
{
return $this->indent;
}
public function getLineEnding(): string
{
return $this->lineEnding;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer;
use PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException;
use PhpCsFixer\ConfigurationException\InvalidForEnvFixerConfigurationException;
use PhpCsFixer\ConfigurationException\RequiredFixerConfigurationException;
use PhpCsFixer\Console\Application;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\FixerInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\DeprecatedFixerOption;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\InvalidOptionsForEnvException;
use PhpCsFixer\Tokenizer\Tokens;
use Symfony\Component\OptionsResolver\Exception\ExceptionInterface;
use Symfony\Component\OptionsResolver\Exception\MissingOptionsException;
abstract class AbstractFixer implements FixerInterface
{
protected $configuration;
protected $whitespacesConfig;
private $configurationDefinition;
public function __construct()
{
if ($this instanceof ConfigurableFixerInterface) {
try {
$this->configure([]);
} catch (RequiredFixerConfigurationException $e) {
}
}
if ($this instanceof WhitespacesAwareFixerInterface) {
$this->whitespacesConfig = $this->getDefaultWhitespacesFixerConfig();
}
}
final public function fix(\SplFileInfo $file, Tokens $tokens): void
{
if ($this instanceof ConfigurableFixerInterface && null === $this->configuration) {
throw new RequiredFixerConfigurationException($this->getName(), 'Configuration is required.');
}
if (0 < $tokens->count() && $this->isCandidate($tokens) && $this->supports($file)) {
$this->applyFix($file, $tokens);
}
}
public function isRisky(): bool
{
return false;
}
public function getName(): string
{
$nameParts = explode('\\', static::class);
$name = substr(end($nameParts), 0, -\strlen('Fixer'));
return Utils::camelCaseToUnderscore($name);
}
public function getPriority(): int
{
return 0;
}
public function supports(\SplFileInfo $file): bool
{
return true;
}
public function configure(array $configuration): void
{
if (!$this instanceof ConfigurableFixerInterface) {
throw new \LogicException('Cannot configure using Abstract parent, child not implementing "PhpCsFixer\Fixer\ConfigurableFixerInterface".');
}
foreach ($this->getConfigurationDefinition()->getOptions() as $option) {
if (!$option instanceof DeprecatedFixerOption) {
continue;
}
$name = $option->getName();
if (\array_key_exists($name, $configuration)) {
Utils::triggerDeprecation(new \InvalidArgumentException(sprintf(
'Option "%s" for rule "%s" is deprecated and will be removed in version %d.0. %s',
$name,
$this->getName(),
Application::getMajorVersion() + 1,
str_replace('`', '"', $option->getDeprecationMessage())
)));
}
}
try {
$this->configuration = $this->getConfigurationDefinition()->resolve($configuration);
} catch (MissingOptionsException $exception) {
throw new RequiredFixerConfigurationException(
$this->getName(),
sprintf('Missing required configuration: %s', $exception->getMessage()),
$exception
);
} catch (InvalidOptionsForEnvException $exception) {
throw new InvalidForEnvFixerConfigurationException(
$this->getName(),
sprintf('Invalid configuration for env: %s', $exception->getMessage()),
$exception
);
} catch (ExceptionInterface $exception) {
throw new InvalidFixerConfigurationException(
$this->getName(),
sprintf('Invalid configuration: %s', $exception->getMessage()),
$exception
);
}
}
public function getConfigurationDefinition(): FixerConfigurationResolverInterface
{
if (!$this instanceof ConfigurableFixerInterface) {
throw new \LogicException(sprintf('Cannot get configuration definition using Abstract parent, child "%s" not implementing "PhpCsFixer\Fixer\ConfigurableFixerInterface".', static::class));
}
if (null === $this->configurationDefinition) {
$this->configurationDefinition = $this->createConfigurationDefinition();
}
return $this->configurationDefinition;
}
public function setWhitespacesConfig(WhitespacesFixerConfig $config): void
{
if (!$this instanceof WhitespacesAwareFixerInterface) {
throw new \LogicException('Cannot run method for class not implementing "PhpCsFixer\Fixer\WhitespacesAwareFixerInterface".');
}
$this->whitespacesConfig = $config;
}
abstract protected function applyFix(\SplFileInfo $file, Tokens $tokens): void;
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
if (!$this instanceof ConfigurableFixerInterface) {
throw new \LogicException('Cannot create configuration definition using Abstract parent, child not implementing "PhpCsFixer\Fixer\ConfigurableFixerInterface".');
}
throw new \LogicException('Not implemented.');
}
private function getDefaultWhitespacesFixerConfig(): WhitespacesFixerConfig
{
static $defaultWhitespacesFixerConfig = null;
if (null === $defaultWhitespacesFixerConfig) {
$defaultWhitespacesFixerConfig = new WhitespacesFixerConfig(' ', "\n");
}
return $defaultWhitespacesFixerConfig;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer;
final class Preg
{
public static function match(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): int
{
$result = @preg_match(self::addUtf8Modifier($pattern), $subject, $matches, $flags, $offset);
if (false !== $result && PREG_NO_ERROR === preg_last_error()) {
return $result;
}
$result = @preg_match(self::removeUtf8Modifier($pattern), $subject, $matches, $flags, $offset);
if (false !== $result && PREG_NO_ERROR === preg_last_error()) {
return $result;
}
throw self::newPregException(preg_last_error(), __METHOD__, (array) $pattern);
}
public static function matchAll(string $pattern, string $subject, ?array &$matches = null, int $flags = PREG_PATTERN_ORDER, int $offset = 0): int
{
$result = @preg_match_all(self::addUtf8Modifier($pattern), $subject, $matches, $flags, $offset);
if (false !== $result && PREG_NO_ERROR === preg_last_error()) {
return $result;
}
$result = @preg_match_all(self::removeUtf8Modifier($pattern), $subject, $matches, $flags, $offset);
if (false !== $result && PREG_NO_ERROR === preg_last_error()) {
return $result;
}
throw self::newPregException(preg_last_error(), __METHOD__, (array) $pattern);
}
public static function replace(string $pattern, string $replacement, $subject, int $limit = -1, ?int &$count = null): string
{
$result = @preg_replace(self::addUtf8Modifier($pattern), $replacement, $subject, $limit, $count);
if (null !== $result && PREG_NO_ERROR === preg_last_error()) {
return $result;
}
$result = @preg_replace(self::removeUtf8Modifier($pattern), $replacement, $subject, $limit, $count);
if (null !== $result && PREG_NO_ERROR === preg_last_error()) {
return $result;
}
throw self::newPregException(preg_last_error(), __METHOD__, (array) $pattern);
}
public static function replaceCallback(string $pattern, callable $callback, string $subject, int $limit = -1, ?int &$count = null): string
{
$result = @preg_replace_callback(self::addUtf8Modifier($pattern), $callback, $subject, $limit, $count);
if (null !== $result && PREG_NO_ERROR === preg_last_error()) {
return $result;
}
$result = @preg_replace_callback(self::removeUtf8Modifier($pattern), $callback, $subject, $limit, $count);
if (null !== $result && PREG_NO_ERROR === preg_last_error()) {
return $result;
}
throw self::newPregException(preg_last_error(), __METHOD__, (array) $pattern);
}
public static function split(string $pattern, string $subject, int $limit = -1, int $flags = 0): array
{
$result = @preg_split(self::addUtf8Modifier($pattern), $subject, $limit, $flags);
if (false !== $result && PREG_NO_ERROR === preg_last_error()) {
return $result;
}
$result = @preg_split(self::removeUtf8Modifier($pattern), $subject, $limit, $flags);
if (false !== $result && PREG_NO_ERROR === preg_last_error()) {
return $result;
}
throw self::newPregException(preg_last_error(), __METHOD__, (array) $pattern);
}
private static function addUtf8Modifier($pattern)
{
if (\is_array($pattern)) {
return array_map(__METHOD__, $pattern);
}
return $pattern.'u';
}
private static function removeUtf8Modifier($pattern)
{
if (\is_array($pattern)) {
return array_map(__METHOD__, $pattern);
}
if ('' === $pattern) {
return '';
}
$delimiter = $pattern[0];
$endDelimiterPosition = strrpos($pattern, $delimiter);
return substr($pattern, 0, $endDelimiterPosition).str_replace('u', '', substr($pattern, $endDelimiterPosition));
}
private static function newPregException(int $error, string $method, array $patterns): PregException
{
foreach ($patterns as $pattern) {
$last = error_get_last();
$result = @preg_match($pattern, '');
if (false !== $result) {
continue;
}
$code = preg_last_error();
$next = error_get_last();
if ($last !== $next) {
$message = sprintf(
'(code: %d) %s',
$code,
preg_replace('~preg_[a-z_]+[()]{2}: ~', '', $next['message'])
);
} else {
$message = sprintf('(code: %d)', $code);
}
return new PregException(
sprintf('%s(): Invalid PCRE pattern "%s": %s (version: %s)', $method, $pattern, $message, PCRE_VERSION),
$code
);
}
return new PregException(sprintf('Error occurred when calling %s.', $method), $error);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Error;
final class Error
{
public const TYPE_INVALID = 1;
public const TYPE_EXCEPTION = 2;
public const TYPE_LINT = 3;
private int $type;
private string $filePath;
private ?\Throwable $source;
private array $appliedFixers;
private ?string $diff;
public function __construct(int $type, string $filePath, ?\Throwable $source = null, array $appliedFixers = [], ?string $diff = null)
{
$this->type = $type;
$this->filePath = $filePath;
$this->source = $source;
$this->appliedFixers = $appliedFixers;
$this->diff = $diff;
}
public function getFilePath(): string
{
return $this->filePath;
}
public function getSource(): ?\Throwable
{
return $this->source;
}
public function getType(): int
{
return $this->type;
}
public function getAppliedFixers(): array
{
return $this->appliedFixers;
}
public function getDiff(): ?string
{
return $this->diff;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Error;
final class ErrorsManager
{
private array $errors = [];
public function getInvalidErrors(): array
{
return array_filter($this->errors, static function (Error $error): bool {
return Error::TYPE_INVALID === $error->getType();
});
}
public function getExceptionErrors(): array
{
return array_filter($this->errors, static function (Error $error): bool {
return Error::TYPE_EXCEPTION === $error->getType();
});
}
public function getLintErrors(): array
{
return array_filter($this->errors, static function (Error $error): bool {
return Error::TYPE_LINT === $error->getType();
});
}
public function isEmpty(): bool
{
return empty($this->errors);
}
public function report(Error $error): void
{
$this->errors[] = $error;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\Tokens;
abstract class AbstractFunctionReferenceFixer extends AbstractFixer
{
private $functionsAnalyzer;
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_STRING);
}
public function isRisky(): bool
{
return true;
}
protected function find(string $functionNameToSearch, Tokens $tokens, int $start = 0, ?int $end = null): ?array
{
if (null === $this->functionsAnalyzer) {
$this->functionsAnalyzer = new FunctionsAnalyzer();
}
$end ??= $tokens->count();
$candidateSequence = [[T_STRING, $functionNameToSearch], '('];
$matches = $tokens->findSequence($candidateSequence, $start, $end, false);
if (null === $matches) {
return null;
}
[$functionName, $openParenthesis] = array_keys($matches);
if (!$this->functionsAnalyzer->isGlobalFunctionCall($tokens, $functionName)) {
return $this->find($functionNameToSearch, $tokens, $openParenthesis, $end);
}
return [$functionName, $openParenthesis, $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openParenthesis)];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer;
use PhpCsFixer\Console\Application;
final class ToolInfo implements ToolInfoInterface
{
public const COMPOSER_PACKAGE_NAME = 'friendsofphp/php-cs-fixer';
public const COMPOSER_LEGACY_PACKAGE_NAME = 'fabpot/php-cs-fixer';
private $composerInstallationDetails;
private $isInstalledByComposer;
public function getComposerInstallationDetails(): array
{
if (!$this->isInstalledByComposer()) {
throw new \LogicException('Cannot get composer version for tool not installed by composer.');
}
if (null === $this->composerInstallationDetails) {
$composerInstalled = json_decode(file_get_contents($this->getComposerInstalledFile()), true);
$packages = $composerInstalled['packages'] ?? $composerInstalled;
foreach ($packages as $package) {
if (\in_array($package['name'], [self::COMPOSER_PACKAGE_NAME, self::COMPOSER_LEGACY_PACKAGE_NAME], true)) {
$this->composerInstallationDetails = $package;
break;
}
}
}
return $this->composerInstallationDetails;
}
public function getComposerVersion(): string
{
$package = $this->getComposerInstallationDetails();
$versionSuffix = '';
if (isset($package['dist']['reference'])) {
$versionSuffix = '#'.$package['dist']['reference'];
}
return $package['version'].$versionSuffix;
}
public function getVersion(): string
{
if ($this->isInstalledByComposer()) {
return Application::VERSION.':'.$this->getComposerVersion();
}
return Application::VERSION;
}
public function isInstalledAsPhar(): bool
{
return str_starts_with(__DIR__, 'phar://');
}
public function isInstalledByComposer(): bool
{
if (null === $this->isInstalledByComposer) {
$this->isInstalledByComposer = !$this->isInstalledAsPhar() && file_exists($this->getComposerInstalledFile());
}
return $this->isInstalledByComposer;
}
public function getPharDownloadUri(string $version): string
{
return sprintf(
'https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/releases/download/%s/php-cs-fixer.phar',
$version
);
}
private function getComposerInstalledFile(): string
{
return __DIR__.'/../../../composer/installed.json';
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console;
use PhpCsFixer\Console\Command\DescribeCommand;
use PhpCsFixer\Console\Command\FixCommand;
use PhpCsFixer\Console\Command\HelpCommand;
use PhpCsFixer\Console\Command\ListFilesCommand;
use PhpCsFixer\Console\Command\ListSetsCommand;
use PhpCsFixer\Console\Command\SelfUpdateCommand;
use PhpCsFixer\Console\SelfUpdate\GithubClient;
use PhpCsFixer\Console\SelfUpdate\NewVersionChecker;
use PhpCsFixer\PharChecker;
use PhpCsFixer\ToolInfo;
use PhpCsFixer\Utils;
use Symfony\Component\Console\Application as BaseApplication;
use Symfony\Component\Console\Command\ListCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
final class Application extends BaseApplication
{
public const VERSION = '3.13.0';
public const VERSION_CODENAME = 'Oliva';
private ToolInfo $toolInfo;
public function __construct()
{
parent::__construct('PHP CS Fixer', self::VERSION);
$this->toolInfo = new ToolInfo();
$this->add(new DescribeCommand());
$this->add(new FixCommand($this->toolInfo));
$this->add(new ListFilesCommand($this->toolInfo));
$this->add(new ListSetsCommand());
$this->add(new SelfUpdateCommand(
new NewVersionChecker(new GithubClient()),
$this->toolInfo,
new PharChecker()
));
}
public static function getMajorVersion(): int
{
return (int) explode('.', self::VERSION)[0];
}
public function doRun(InputInterface $input, OutputInterface $output): int
{
$stdErr = $output instanceof ConsoleOutputInterface
? $output->getErrorOutput()
: ($input->hasParameterOption('--format', true) && 'txt' !== $input->getParameterOption('--format', null, true) ? null : $output)
;
if (null !== $stdErr) {
$warningsDetector = new WarningsDetector($this->toolInfo);
$warningsDetector->detectOldVendor();
$warningsDetector->detectOldMajor();
$warnings = $warningsDetector->getWarnings();
if (\count($warnings) > 0) {
foreach ($warnings as $warning) {
$stdErr->writeln(sprintf($stdErr->isDecorated() ? '<bg=yellow;fg=black;>%s</>' : '%s', $warning));
}
$stdErr->writeln('');
}
}
$result = parent::doRun($input, $output);
if (
null !== $stdErr
&& $output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE
) {
$triggeredDeprecations = Utils::getTriggeredDeprecations();
if (\count($triggeredDeprecations) > 0) {
$stdErr->writeln('');
$stdErr->writeln($stdErr->isDecorated() ? '<bg=yellow;fg=black;>Detected deprecations in use:</>' : 'Detected deprecations in use:');
foreach ($triggeredDeprecations as $deprecation) {
$stdErr->writeln(sprintf('- %s', $deprecation));
}
}
}
return $result;
}
public function getLongVersion(): string
{
$commit = 'a6232229a8309e8811dc751c28b91cb34b2943e1';
$versionCommit = '';
if ('@'.'git-commit@' !== $commit) { /**
@phpstan-ignore-line */
$versionCommit = substr($commit, 0, 7);
}
return implode('', [
parent::getLongVersion(),
$versionCommit ? sprintf(' <info>(%s)</info>', $versionCommit) : '',
self::VERSION_CODENAME ? sprintf(' <info>%s</info>', self::VERSION_CODENAME) : '',
' by <comment>Fabien Potencier</comment> and <comment>Dariusz Ruminski</comment>.',
"\nPHP runtime: <info>".PHP_VERSION.'</info>',
]);
}
protected function getDefaultCommands(): array
{
return [new HelpCommand(), new ListCommand()];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\SelfUpdate;
interface NewVersionCheckerInterface
{
public function getLatestVersion(): string;
public function getLatestVersionOfMajor(int $majorVersion): ?string;
public function compareVersions(string $versionA, string $versionB): int;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\SelfUpdate;
interface GithubClientInterface
{
public function getTags(): array;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\SelfUpdate;
final class GithubClient implements GithubClientInterface
{
public function getTags(): array
{
$url = 'https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/tags';
$result = @file_get_contents(
$url,
false,
stream_context_create([
'http' => [
'header' => 'User-Agent: PHP-CS-Fixer/PHP-CS-Fixer',
],
])
);
if (false === $result) {
throw new \RuntimeException(sprintf('Failed to load tags at "%s".', $url));
}
$result = json_decode($result, true);
if (JSON_ERROR_NONE !== json_last_error()) {
throw new \RuntimeException(sprintf(
'Failed to read response from "%s" as JSON: %s.',
$url,
json_last_error_msg()
));
}
return $result;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\SelfUpdate;
use Composer\Semver\Comparator;
use Composer\Semver\Semver;
use Composer\Semver\VersionParser;
final class NewVersionChecker implements NewVersionCheckerInterface
{
private GithubClientInterface $githubClient;
private VersionParser $versionParser;
private $availableVersions;
public function __construct(GithubClientInterface $githubClient)
{
$this->githubClient = $githubClient;
$this->versionParser = new VersionParser();
}
public function getLatestVersion(): string
{
$this->retrieveAvailableVersions();
return $this->availableVersions[0];
}
public function getLatestVersionOfMajor(int $majorVersion): ?string
{
$this->retrieveAvailableVersions();
$semverConstraint = '^'.$majorVersion;
foreach ($this->availableVersions as $availableVersion) {
if (Semver::satisfies($availableVersion, $semverConstraint)) {
return $availableVersion;
}
}
return null;
}
public function compareVersions(string $versionA, string $versionB): int
{
$versionA = $this->versionParser->normalize($versionA);
$versionB = $this->versionParser->normalize($versionB);
if (Comparator::lessThan($versionA, $versionB)) {
return -1;
}
if (Comparator::greaterThan($versionA, $versionB)) {
return 1;
}
return 0;
}
private function retrieveAvailableVersions(): void
{
if (null !== $this->availableVersions) {
return;
}
foreach ($this->githubClient->getTags() as $tag) {
$version = $tag['name'];
try {
$this->versionParser->normalize($version);
if ('stable' === VersionParser::parseStability($version)) {
$this->availableVersions[] = $version;
}
} catch (\UnexpectedValueException $exception) {
}
}
$this->availableVersions = Semver::rsort($this->availableVersions);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console;
use PhpCsFixer\ToolInfo;
use PhpCsFixer\ToolInfoInterface;
final class WarningsDetector
{
private ToolInfoInterface $toolInfo;
private array $warnings = [];
public function __construct(ToolInfoInterface $toolInfo)
{
$this->toolInfo = $toolInfo;
}
public function detectOldMajor(): void
{
}
public function detectOldVendor(): void
{
if ($this->toolInfo->isInstalledByComposer()) {
$details = $this->toolInfo->getComposerInstallationDetails();
if (ToolInfo::COMPOSER_LEGACY_PACKAGE_NAME === $details['name']) {
$this->warnings[] = sprintf(
'You are running PHP CS Fixer installed with old vendor `%s`. Please update to `%s`.',
ToolInfo::COMPOSER_LEGACY_PACKAGE_NAME,
ToolInfo::COMPOSER_PACKAGE_NAME
);
}
}
}
public function getWarnings(): array
{
if (0 === \count($this->warnings)) {
return [];
}
return array_unique(array_merge(
$this->warnings,
['If you need help while solving warnings, ask at https://gitter.im/PHP-CS-Fixer, we will help you!']
));
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\Output;
use PhpCsFixer\FixerFileProcessedEvent;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
final class ProcessOutput implements ProcessOutputInterface
{
private static array $eventStatusMap = [
FixerFileProcessedEvent::STATUS_NO_CHANGES => ['symbol' => '.', 'format' => '%s', 'description' => 'no changes'],
FixerFileProcessedEvent::STATUS_FIXED => ['symbol' => 'F', 'format' => '<fg=green>%s</fg=green>', 'description' => 'fixed'],
FixerFileProcessedEvent::STATUS_SKIPPED => ['symbol' => 'S', 'format' => '<fg=cyan>%s</fg=cyan>', 'description' => 'skipped (cached or empty file)'],
FixerFileProcessedEvent::STATUS_INVALID => ['symbol' => 'I', 'format' => '<bg=red>%s</bg=red>', 'description' => 'invalid file syntax (file ignored)'],
FixerFileProcessedEvent::STATUS_EXCEPTION => ['symbol' => 'E', 'format' => '<bg=red>%s</bg=red>', 'description' => 'error'],
FixerFileProcessedEvent::STATUS_LINT => ['symbol' => 'E', 'format' => '<bg=red>%s</bg=red>', 'description' => 'error'],
];
private OutputInterface $output;
private EventDispatcherInterface $eventDispatcher;
private int $files;
private int $processedFiles = 0;
private $symbolsPerLine;
public function __construct(OutputInterface $output, EventDispatcherInterface $dispatcher, int $width, int $nbFiles)
{
$this->output = $output;
$this->eventDispatcher = $dispatcher;
$this->eventDispatcher->addListener(FixerFileProcessedEvent::NAME, [$this, 'onFixerFileProcessed']);
$this->files = $nbFiles;
$this->symbolsPerLine = max(1, $width - \strlen((string) $this->files) * 2 - 11);
}
public function __destruct()
{
$this->eventDispatcher->removeListener(FixerFileProcessedEvent::NAME, [$this, 'onFixerFileProcessed']);
}
public function __sleep(): array
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup(): void
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
public function onFixerFileProcessed(FixerFileProcessedEvent $event): void
{
$status = self::$eventStatusMap[$event->getStatus()];
$this->output->write($this->output->isDecorated() ? sprintf($status['format'], $status['symbol']) : $status['symbol']);
++$this->processedFiles;
$symbolsOnCurrentLine = $this->processedFiles % $this->symbolsPerLine;
$isLast = $this->processedFiles === $this->files;
if (0 === $symbolsOnCurrentLine || $isLast) {
$this->output->write(sprintf(
'%s %'.\strlen((string) $this->files).'d / %d (%3d%%)',
$isLast && 0 !== $symbolsOnCurrentLine ? str_repeat(' ', $this->symbolsPerLine - $symbolsOnCurrentLine) : '',
$this->processedFiles,
$this->files,
round($this->processedFiles / $this->files * 100)
));
if (!$isLast) {
$this->output->writeln('');
}
}
}
public function printLegend(): void
{
$symbols = [];
foreach (self::$eventStatusMap as $status) {
$symbol = $status['symbol'];
if ('' === $symbol || isset($symbols[$symbol])) {
continue;
}
$symbols[$symbol] = sprintf('%s-%s', $this->output->isDecorated() ? sprintf($status['format'], $symbol) : $symbol, $status['description']);
}
$this->output->write(sprintf("\nLegend: %s\n", implode(', ', $symbols)));
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\Output;
use PhpCsFixer\Differ\DiffConsoleFormatter;
use PhpCsFixer\Error\Error;
use PhpCsFixer\Linter\LintingException;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Output\OutputInterface;
final class ErrorOutput
{
private OutputInterface $output;
private $isDecorated;
public function __construct(OutputInterface $output)
{
$this->output = $output;
$this->isDecorated = $output->isDecorated();
}
public function listErrors(string $process, array $errors): void
{
$this->output->writeln(['', sprintf(
'Files that were not fixed due to errors reported during %s:',
$process
)]);
$showDetails = $this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERY_VERBOSE;
$showTrace = $this->output->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG;
foreach ($errors as $i => $error) {
$this->output->writeln(sprintf('%4d) %s', $i + 1, $error->getFilePath()));
$e = $error->getSource();
if (!$showDetails || null === $e) {
continue;
}
$class = sprintf('[%s]', \get_class($e));
$message = $e->getMessage();
$code = $e->getCode();
if (0 !== $code) {
$message .= " ({$code})";
}
$length = max(\strlen($class), \strlen($message));
$lines = [
'',
$class,
$message,
'',
];
$this->output->writeln('');
foreach ($lines as $line) {
if (\strlen($line) < $length) {
$line .= str_repeat(' ', $length - \strlen($line));
}
$this->output->writeln(sprintf(' <error> %s </error>', $this->prepareOutput($line)));
}
if ($showTrace && !$e instanceof LintingException) {
$this->output->writeln('');
$stackTrace = $e->getTrace();
foreach ($stackTrace as $trace) {
if (isset($trace['class']) && \Symfony\Component\Console\Command\Command::class === $trace['class'] && 'run' === $trace['function']) {
$this->output->writeln(' [ ... ]');
break;
}
$this->outputTrace($trace);
}
}
if (Error::TYPE_LINT === $error->getType() && 0 < \count($error->getAppliedFixers())) {
$this->output->writeln('');
$this->output->writeln(sprintf(' Applied fixers: <comment>%s</comment>', implode(', ', $error->getAppliedFixers())));
$diff = $error->getDiff();
if (!empty($diff)) {
$diffFormatter = new DiffConsoleFormatter(
$this->isDecorated,
sprintf(
'<comment> ---------- begin diff ----------</comment>%s%%s%s<comment> ----------- end diff -----------</comment>',
PHP_EOL,
PHP_EOL
)
);
$this->output->writeln($diffFormatter->format($diff));
}
}
}
}
private function outputTrace(array $trace): void
{
if (isset($trace['class'], $trace['type'], $trace['function'])) {
$this->output->writeln(sprintf(
' <comment>%s</comment>%s<comment>%s()</comment>',
$this->prepareOutput($trace['class']),
$this->prepareOutput($trace['type']),
$this->prepareOutput($trace['function'])
));
} elseif (isset($trace['function'])) {
$this->output->writeln(sprintf(' <comment>%s()</comment>', $this->prepareOutput($trace['function'])));
}
if (isset($trace['file'])) {
$this->output->writeln(sprintf(' in <info>%s</info> at line <info>%d</info>', $this->prepareOutput($trace['file']), $trace['line']));
}
}
private function prepareOutput(string $string): string
{
return $this->isDecorated
? OutputFormatter::escape($string)
: $string
;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\Output;
interface ProcessOutputInterface
{
public function printLegend(): void;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\Output;
final class NullOutput implements ProcessOutputInterface
{
public function printLegend(): void
{
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console;
use PhpCsFixer\Cache\CacheManagerInterface;
use PhpCsFixer\Cache\Directory;
use PhpCsFixer\Cache\DirectoryInterface;
use PhpCsFixer\Cache\FileCacheManager;
use PhpCsFixer\Cache\FileHandler;
use PhpCsFixer\Cache\NullCacheManager;
use PhpCsFixer\Cache\Signature;
use PhpCsFixer\ConfigInterface;
use PhpCsFixer\ConfigurationException\InvalidConfigurationException;
use PhpCsFixer\Console\Command\HelpCommand;
use PhpCsFixer\Console\Report\FixReport\ReporterFactory;
use PhpCsFixer\Console\Report\FixReport\ReporterInterface;
use PhpCsFixer\Differ\DifferInterface;
use PhpCsFixer\Differ\NullDiffer;
use PhpCsFixer\Differ\UnifiedDiffer;
use PhpCsFixer\Finder;
use PhpCsFixer\Fixer\DeprecatedFixerInterface;
use PhpCsFixer\Fixer\FixerInterface;
use PhpCsFixer\FixerFactory;
use PhpCsFixer\Linter\Linter;
use PhpCsFixer\Linter\LinterInterface;
use PhpCsFixer\RuleSet\RuleSet;
use PhpCsFixer\RuleSet\RuleSetInterface;
use PhpCsFixer\StdinFileInfo;
use PhpCsFixer\ToolInfoInterface;
use PhpCsFixer\Utils;
use PhpCsFixer\WhitespacesFixerConfig;
use PhpCsFixer\WordMatcher;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Finder\Finder as SymfonyFinder;
final class ConfigurationResolver
{
public const PATH_MODE_OVERRIDE = 'override';
public const PATH_MODE_INTERSECTION = 'intersection';
private $allowRisky;
private $config;
private $configFile;
private string $cwd;
private ConfigInterface $defaultConfig;
private $reporter;
private $isStdIn;
private $isDryRun;
private $fixers;
private $configFinderIsOverridden;
private ToolInfoInterface $toolInfo;
private array $options = [
'allow-risky' => null,
'cache-file' => null,
'config' => null,
'diff' => null,
'dry-run' => null,
'format' => null,
'path' => [],
'path-mode' => self::PATH_MODE_OVERRIDE,
'rules' => null,
'show-progress' => null,
'stop-on-violation' => null,
'using-cache' => null,
'verbosity' => null,
];
private $cacheFile;
private $cacheManager;
private $differ;
private $directory;
private ?iterable $finder = null;
private ?string $format = null;
private $linter;
private ?array $path = null;
private $progress;
private $ruleSet;
private $usingCache;
private $fixerFactory;
public function __construct(
ConfigInterface $config,
array $options,
string $cwd,
ToolInfoInterface $toolInfo
) {
$this->defaultConfig = $config;
$this->cwd = $cwd;
$this->toolInfo = $toolInfo;
foreach ($options as $name => $value) {
$this->setOption($name, $value);
}
}
public function getCacheFile(): ?string
{
if (!$this->getUsingCache()) {
return null;
}
if (null === $this->cacheFile) {
if (null === $this->options['cache-file']) {
$this->cacheFile = $this->getConfig()->getCacheFile();
} else {
$this->cacheFile = $this->options['cache-file'];
}
}
return $this->cacheFile;
}
public function getCacheManager(): CacheManagerInterface
{
if (null === $this->cacheManager) {
$cacheFile = $this->getCacheFile();
if (null === $cacheFile) {
$this->cacheManager = new NullCacheManager();
} else {
$this->cacheManager = new FileCacheManager(
new FileHandler($cacheFile),
new Signature(
PHP_VERSION,
$this->toolInfo->getVersion(),
$this->getConfig()->getIndent(),
$this->getConfig()->getLineEnding(),
$this->getRules()
),
$this->isDryRun(),
$this->getDirectory()
);
}
}
return $this->cacheManager;
}
public function getConfig(): ConfigInterface
{
if (null === $this->config) {
foreach ($this->computeConfigFiles() as $configFile) {
if (!file_exists($configFile)) {
continue;
}
$configFileBasename = basename($configFile);
$deprecatedConfigs = [
'.php_cs' => '.php-cs-fixer.php',
'.php_cs.dist' => '.php-cs-fixer.dist.php',
];
if (isset($deprecatedConfigs[$configFileBasename])) {
throw new InvalidConfigurationException("Configuration file `{$configFileBasename}` is outdated, rename to `{$deprecatedConfigs[$configFileBasename]}`.");
}
$this->config = self::separatedContextLessInclude($configFile);
$this->configFile = $configFile;
break;
}
if (null === $this->config) {
$this->config = $this->defaultConfig;
}
}
return $this->config;
}
public function getConfigFile(): ?string
{
if (null === $this->configFile) {
$this->getConfig();
}
return $this->configFile;
}
public function getDiffer(): DifferInterface
{
if (null === $this->differ) {
if ($this->options['diff']) {
$this->differ = new UnifiedDiffer();
} else {
$this->differ = new NullDiffer();
}
}
return $this->differ;
}
public function getDirectory(): DirectoryInterface
{
if (null === $this->directory) {
$path = $this->getCacheFile();
if (null === $path) {
$absolutePath = $this->cwd;
} else {
$filesystem = new Filesystem();
$absolutePath = $filesystem->isAbsolutePath($path)
? $path
: $this->cwd.\DIRECTORY_SEPARATOR.$path;
}
$this->directory = new Directory(\dirname($absolutePath));
}
return $this->directory;
}
public function getFixers(): array
{
if (null === $this->fixers) {
$this->fixers = $this->createFixerFactory()
->useRuleSet($this->getRuleSet())
->setWhitespacesConfig(new WhitespacesFixerConfig($this->config->getIndent(), $this->config->getLineEnding()))
->getFixers()
;
if (false === $this->getRiskyAllowed()) {
$riskyFixers = array_map(
static function (FixerInterface $fixer): string {
return $fixer->getName();
},
array_filter(
$this->fixers,
static function (FixerInterface $fixer): bool {
return $fixer->isRisky();
}
)
);
if (\count($riskyFixers) > 0) {
throw new InvalidConfigurationException(sprintf('The rules contain risky fixers ("%s"), but they are not allowed to run. Perhaps you forget to use --allow-risky=yes option?', implode('", "', $riskyFixers)));
}
}
}
return $this->fixers;
}
public function getLinter(): LinterInterface
{
if (null === $this->linter) {
$this->linter = new Linter();
}
return $this->linter;
}
public function getPath(): array
{
if (null === $this->path) {
$filesystem = new Filesystem();
$cwd = $this->cwd;
if (1 === \count($this->options['path']) && '-' === $this->options['path'][0]) {
$this->path = $this->options['path'];
} else {
$this->path = array_map(
static function (string $rawPath) use ($cwd, $filesystem): string {
$path = trim($rawPath);
if ('' === $path) {
throw new InvalidConfigurationException("Invalid path: \"{$rawPath}\".");
}
$absolutePath = $filesystem->isAbsolutePath($path)
? $path
: $cwd.\DIRECTORY_SEPARATOR.$path;
if (!file_exists($absolutePath)) {
throw new InvalidConfigurationException(sprintf(
'The path "%s" is not readable.',
$path
));
}
return $absolutePath;
},
$this->options['path']
);
}
}
return $this->path;
}
public function getProgress(): string
{
if (null === $this->progress) {
if (OutputInterface::VERBOSITY_VERBOSE <= $this->options['verbosity'] && 'txt' === $this->getFormat()) {
$progressType = $this->options['show-progress'];
$progressTypes = ['none', 'dots'];
if (null === $progressType) {
$progressType = $this->getConfig()->getHideProgress() ? 'none' : 'dots';
} elseif (!\in_array($progressType, $progressTypes, true)) {
throw new InvalidConfigurationException(sprintf(
'The progress type "%s" is not defined, supported are "%s".',
$progressType,
implode('", "', $progressTypes)
));
}
$this->progress = $progressType;
} else {
$this->progress = 'none';
}
}
return $this->progress;
}
public function getReporter(): ReporterInterface
{
if (null === $this->reporter) {
$reporterFactory = new ReporterFactory();
$reporterFactory->registerBuiltInReporters();
$format = $this->getFormat();
try {
$this->reporter = $reporterFactory->getReporter($format);
} catch (\UnexpectedValueException $e) {
$formats = $reporterFactory->getFormats();
sort($formats);
throw new InvalidConfigurationException(sprintf('The format "%s" is not defined, supported are "%s".', $format, implode('", "', $formats)));
}
}
return $this->reporter;
}
public function getRiskyAllowed(): bool
{
if (null === $this->allowRisky) {
if (null === $this->options['allow-risky']) {
$this->allowRisky = $this->getConfig()->getRiskyAllowed();
} else {
$this->allowRisky = $this->resolveOptionBooleanValue('allow-risky');
}
}
return $this->allowRisky;
}
public function getRules(): array
{
return $this->getRuleSet()->getRules();
}
public function getUsingCache(): bool
{
if (null === $this->usingCache) {
if (null === $this->options['using-cache']) {
$this->usingCache = $this->getConfig()->getUsingCache();
} else {
$this->usingCache = $this->resolveOptionBooleanValue('using-cache');
}
}
$this->usingCache = $this->usingCache && ($this->toolInfo->isInstalledAsPhar() || $this->toolInfo->isInstalledByComposer());
return $this->usingCache;
}
public function getFinder(): iterable
{
if (null === $this->finder) {
$this->finder = $this->resolveFinder();
}
return $this->finder;
}
public function isDryRun(): bool
{
if (null === $this->isDryRun) {
if ($this->isStdIn()) {
$this->isDryRun = true;
} else {
$this->isDryRun = $this->options['dry-run'];
}
}
return $this->isDryRun;
}
public function shouldStopOnViolation(): bool
{
return $this->options['stop-on-violation'];
}
public function configFinderIsOverridden(): bool
{
if (null === $this->configFinderIsOverridden) {
$this->resolveFinder();
}
return $this->configFinderIsOverridden;
}
private function computeConfigFiles(): array
{
$configFile = $this->options['config'];
if (null !== $configFile) {
if (false === file_exists($configFile) || false === is_readable($configFile)) {
throw new InvalidConfigurationException(sprintf('Cannot read config file "%s".', $configFile));
}
return [$configFile];
}
$path = $this->getPath();
if ($this->isStdIn() || 0 === \count($path)) {
$configDir = $this->cwd;
} elseif (1 < \count($path)) {
throw new InvalidConfigurationException('For multiple paths config parameter is required.');
} elseif (!is_file($path[0])) {
$configDir = $path[0];
} else {
$dirName = pathinfo($path[0], PATHINFO_DIRNAME);
$configDir = $dirName ?: $path[0];
}
$candidates = [
$configDir.\DIRECTORY_SEPARATOR.'.php-cs-fixer.php',
$configDir.\DIRECTORY_SEPARATOR.'.php-cs-fixer.dist.php',
$configDir.\DIRECTORY_SEPARATOR.'.php_cs',
$configDir.\DIRECTORY_SEPARATOR.'.php_cs.dist',
];
if ($configDir !== $this->cwd) {
$candidates[] = $this->cwd.\DIRECTORY_SEPARATOR.'.php-cs-fixer.php';
$candidates[] = $this->cwd.\DIRECTORY_SEPARATOR.'.php-cs-fixer.dist.php';
$candidates[] = $this->cwd.\DIRECTORY_SEPARATOR.'.php_cs';
$candidates[] = $this->cwd.\DIRECTORY_SEPARATOR.'.php_cs.dist';
}
return $candidates;
}
private function createFixerFactory(): FixerFactory
{
if (null === $this->fixerFactory) {
$fixerFactory = new FixerFactory();
$fixerFactory->registerBuiltInFixers();
$fixerFactory->registerCustomFixers($this->getConfig()->getCustomFixers());
$this->fixerFactory = $fixerFactory;
}
return $this->fixerFactory;
}
private function getFormat(): string
{
if (null === $this->format) {
$this->format = $this->options['format'] ?? $this->getConfig()->getFormat();
}
return $this->format;
}
private function getRuleSet(): RuleSetInterface
{
if (null === $this->ruleSet) {
$rules = $this->parseRules();
$this->validateRules($rules);
$this->ruleSet = new RuleSet($rules);
}
return $this->ruleSet;
}
private function isStdIn(): bool
{
if (null === $this->isStdIn) {
$this->isStdIn = 1 === \count($this->options['path']) && '-' === $this->options['path'][0];
}
return $this->isStdIn;
}
/**
@template
*/
private function iterableToTraversable(iterable $iterable): \Traversable
{
return \is_array($iterable) ? new \ArrayIterator($iterable) : $iterable;
}
private function parseRules(): array
{
if (null === $this->options['rules']) {
return $this->getConfig()->getRules();
}
$rules = trim($this->options['rules']);
if ('' === $rules) {
throw new InvalidConfigurationException('Empty rules value is not allowed.');
}
if (str_starts_with($rules, '{')) {
$rules = json_decode($rules, true);
if (JSON_ERROR_NONE !== json_last_error()) {
throw new InvalidConfigurationException(sprintf('Invalid JSON rules input: "%s".', json_last_error_msg()));
}
return $rules;
}
$rules = [];
foreach (explode(',', $this->options['rules']) as $rule) {
$rule = trim($rule);
if ('' === $rule) {
throw new InvalidConfigurationException('Empty rule name is not allowed.');
}
if (str_starts_with($rule, '-')) {
$rules[substr($rule, 1)] = false;
} else {
$rules[$rule] = true;
}
}
return $rules;
}
private function validateRules(array $rules): void
{
$ruleSet = [];
foreach ($rules as $key => $value) {
if (\is_int($key)) {
throw new InvalidConfigurationException(sprintf('Missing value for "%s" rule/set.', $value));
}
$ruleSet[$key] = true;
}
$ruleSet = new RuleSet($ruleSet);
$configuredFixers = array_keys($ruleSet->getRules());
$fixers = $this->createFixerFactory()->getFixers();
$availableFixers = array_map(static fn (FixerInterface $fixer): string => $fixer->getName(), $fixers);
$unknownFixers = array_diff($configuredFixers, $availableFixers);
if (\count($unknownFixers) > 0) {
$renamedRules = [
'blank_line_before_return' => [
'new_name' => 'blank_line_before_statement',
'config' => ['statements' => ['return']],
],
'final_static_access' => [
'new_name' => 'self_static_accessor',
],
'hash_to_slash_comment' => [
'new_name' => 'single_line_comment_style',
'config' => ['comment_types' => ['hash']],
],
'lowercase_constants' => [
'new_name' => 'constant_case',
'config' => ['case' => 'lower'],
],
'no_extra_consecutive_blank_lines' => [
'new_name' => 'no_extra_blank_lines',
],
'no_multiline_whitespace_before_semicolons' => [
'new_name' => 'multiline_whitespace_before_semicolons',
],
'no_short_echo_tag' => [
'new_name' => 'echo_tag_syntax',
'config' => ['format' => 'long'],
],
'php_unit_ordered_covers' => [
'new_name' => 'phpdoc_order_by_value',
'config' => ['annotations' => ['covers']],
],
'phpdoc_inline_tag' => [
'new_name' => 'general_phpdoc_tag_rename, phpdoc_inline_tag_normalizer and phpdoc_tag_type',
],
'pre_increment' => [
'new_name' => 'increment_style',
'config' => ['style' => 'pre'],
],
'psr0' => [
'new_name' => 'psr_autoloading',
'config' => ['dir' => 'x'],
],
'psr4' => [
'new_name' => 'psr_autoloading',
],
'silenced_deprecation_error' => [
'new_name' => 'error_suppression',
],
'trailing_comma_in_multiline_array' => [
'new_name' => 'trailing_comma_in_multiline',
'config' => ['elements' => ['arrays']],
],
];
$message = 'The rules contain unknown fixers: ';
$hasOldRule = false;
foreach ($unknownFixers as $unknownFixer) {
if (isset($renamedRules[$unknownFixer])) {
$hasOldRule = true;
$message .= sprintf(
'"%s" is renamed (did you mean "%s"?%s), ',
$unknownFixer,
$renamedRules[$unknownFixer]['new_name'],
isset($renamedRules[$unknownFixer]['config']) ? ' (note: use configuration "'.HelpCommand::toString($renamedRules[$unknownFixer]['config']).'")' : ''
);
} else {
$matcher = new WordMatcher($availableFixers);
$alternative = $matcher->match($unknownFixer);
$message .= sprintf(
'"%s"%s, ',
$unknownFixer,
null === $alternative ? '' : ' (did you mean "'.$alternative.'"?)'
);
}
}
$message = substr($message, 0, -2).'.';
if ($hasOldRule) {
$message .= "\nFor more info about updating see: https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/blob/v3.0.0/UPGRADE-v3.md#renamed-ruless.";
}
throw new InvalidConfigurationException($message);
}
foreach ($fixers as $fixer) {
$fixerName = $fixer->getName();
if (isset($rules[$fixerName]) && $fixer instanceof DeprecatedFixerInterface) {
$successors = $fixer->getSuccessorsNames();
$messageEnd = [] === $successors
? sprintf(' and will be removed in version %d.0.', Application::getMajorVersion() + 1)
: sprintf('. Use %s instead.', str_replace('`', '"', Utils::naturalLanguageJoinWithBackticks($successors)));
Utils::triggerDeprecation(new \RuntimeException("Rule \"{$fixerName}\" is deprecated{$messageEnd}"));
}
}
}
private function resolveFinder(): iterable
{
$this->configFinderIsOverridden = false;
if ($this->isStdIn()) {
return new \ArrayIterator([new StdinFileInfo()]);
}
$modes = [self::PATH_MODE_OVERRIDE, self::PATH_MODE_INTERSECTION];
if (!\in_array(
$this->options['path-mode'],
$modes,
true
)) {
throw new InvalidConfigurationException(sprintf(
'The path-mode "%s" is not defined, supported are "%s".',
$this->options['path-mode'],
implode('", "', $modes)
));
}
$isIntersectionPathMode = self::PATH_MODE_INTERSECTION === $this->options['path-mode'];
$paths = array_filter(array_map(
static function (string $path) {
return realpath($path);
},
$this->getPath()
));
if (0 === \count($paths)) {
if ($isIntersectionPathMode) {
return new \ArrayIterator([]);
}
return $this->iterableToTraversable($this->getConfig()->getFinder());
}
$pathsByType = [
'file' => [],
'dir' => [],
];
foreach ($paths as $path) {
if (is_file($path)) {
$pathsByType['file'][] = $path;
} else {
$pathsByType['dir'][] = $path.\DIRECTORY_SEPARATOR;
}
}
$nestedFinder = null;
$currentFinder = $this->iterableToTraversable($this->getConfig()->getFinder());
try {
$nestedFinder = $currentFinder instanceof \IteratorAggregate ? $currentFinder->getIterator() : $currentFinder;
} catch (\Exception $e) {
}
if ($isIntersectionPathMode) {
if (null === $nestedFinder) {
throw new InvalidConfigurationException(
'Cannot create intersection with not-fully defined Finder in configuration file.'
);
}
return new \CallbackFilterIterator(
new \IteratorIterator($nestedFinder),
static function (\SplFileInfo $current) use ($pathsByType): bool {
$currentRealPath = $current->getRealPath();
if (\in_array($currentRealPath, $pathsByType['file'], true)) {
return true;
}
foreach ($pathsByType['dir'] as $path) {
if (str_starts_with($currentRealPath, $path)) {
return true;
}
}
return false;
}
);
}
if (null !== $this->getConfigFile() && null !== $nestedFinder) {
$this->configFinderIsOverridden = true;
}
if ($currentFinder instanceof SymfonyFinder && null === $nestedFinder) {
return $currentFinder->in($pathsByType['dir'])->append($pathsByType['file']);
}
return Finder::create()->in($pathsByType['dir'])->append($pathsByType['file']);
}
private function setOption(string $name, $value): void
{
if (!\array_key_exists($name, $this->options)) {
throw new InvalidConfigurationException(sprintf('Unknown option name: "%s".', $name));
}
$this->options[$name] = $value;
}
private function resolveOptionBooleanValue(string $optionName): bool
{
$value = $this->options[$optionName];
if (!\is_string($value)) {
throw new InvalidConfigurationException(sprintf('Expected boolean or string value for option "%s".', $optionName));
}
if ('yes' === $value) {
return true;
}
if ('no' === $value) {
return false;
}
throw new InvalidConfigurationException(sprintf('Expected "yes" or "no" for option "%s", got "%s".', $optionName, $value));
}
private static function separatedContextLessInclude(string $path): ConfigInterface
{
$config = include $path;
if (!$config instanceof ConfigInterface) {
throw new InvalidConfigurationException(sprintf('The config file: "%s" does not return a "PhpCsFixer\ConfigInterface" instance. Got: "%s".', $path, \is_object($config) ? \get_class($config) : \gettype($config)));
}
return $config;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\Report\ListSetsReport;
use PhpCsFixer\RuleSet\RuleSetDescriptionInterface;
final class ReportSummary
{
private array $sets;
public function __construct(array $sets)
{
$this->sets = $sets;
}
public function getSets(): array
{
return $this->sets;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\Report\ListSetsReport;
use PhpCsFixer\RuleSet\RuleSetDescriptionInterface;
final class TextReporter implements ReporterInterface
{
public function getFormat(): string
{
return 'txt';
}
public function generate(ReportSummary $reportSummary): string
{
$sets = $reportSummary->getSets();
usort($sets, static function (RuleSetDescriptionInterface $a, RuleSetDescriptionInterface $b): int {
return strcmp($a->getName(), $b->getName());
});
$output = '';
foreach ($sets as $i => $set) {
$output .= sprintf('%2d) %s', $i + 1, $set->getName()).PHP_EOL.' '.$set->getDescription().PHP_EOL;
if ($set->isRisky()) {
$output .= ' Set contains risky rules.'.PHP_EOL;
}
}
return $output;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\Report\ListSetsReport;
use PhpCsFixer\RuleSet\RuleSetDescriptionInterface;
final class JsonReporter implements ReporterInterface
{
public function getFormat(): string
{
return 'json';
}
public function generate(ReportSummary $reportSummary): string
{
$sets = $reportSummary->getSets();
usort($sets, static function (RuleSetDescriptionInterface $a, RuleSetDescriptionInterface $b): int {
return strcmp($a->getName(), $b->getName());
});
$json = ['sets' => []];
foreach ($sets as $set) {
$setName = $set->getName();
$json['sets'][$setName] = [
'description' => $set->getDescription(),
'isRisky' => $set->isRisky(),
'name' => $setName,
];
}
return json_encode($json, JSON_PRETTY_PRINT);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\Report\ListSetsReport;
use Symfony\Component\Finder\Finder as SymfonyFinder;
final class ReporterFactory
{
private array $reporters = [];
public function registerBuiltInReporters(): self
{
static $builtInReporters;
if (null === $builtInReporters) {
$builtInReporters = [];
foreach (SymfonyFinder::create()->files()->name('*Reporter.php')->in(__DIR__) as $file) {
$relativeNamespace = $file->getRelativePath();
$builtInReporters[] = sprintf(
'%s\\%s%s',
__NAMESPACE__,
$relativeNamespace ? $relativeNamespace.'\\' : '',
$file->getBasename('.php')
);
}
}
foreach ($builtInReporters as $reporterClass) {
$this->registerReporter(new $reporterClass());
}
return $this;
}
public function registerReporter(ReporterInterface $reporter): self
{
$format = $reporter->getFormat();
if (isset($this->reporters[$format])) {
throw new \UnexpectedValueException(sprintf('Reporter for format "%s" is already registered.', $format));
}
$this->reporters[$format] = $reporter;
return $this;
}
public function getFormats(): array
{
$formats = array_keys($this->reporters);
sort($formats);
return $formats;
}
public function getReporter(string $format): ReporterInterface
{
if (!isset($this->reporters[$format])) {
throw new \UnexpectedValueException(sprintf('Reporter for format "%s" is not registered.', $format));
}
return $this->reporters[$format];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\Report\ListSetsReport;
interface ReporterInterface
{
public function getFormat(): string;
public function generate(ReportSummary $reportSummary): string;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\Report\FixReport;
use PhpCsFixer\Preg;
use Symfony\Component\Console\Formatter\OutputFormatter;
final class JunitReporter implements ReporterInterface
{
public function getFormat(): string
{
return 'junit';
}
public function generate(ReportSummary $reportSummary): string
{
if (!\extension_loaded('dom')) {
throw new \RuntimeException('Cannot generate report! `ext-dom` is not available!');
}
$dom = new \DOMDocument('1.0', 'UTF-8');
$testsuites = $dom->appendChild($dom->createElement('testsuites'));
$testsuite = $testsuites->appendChild($dom->createElement('testsuite'));
$testsuite->setAttribute('name', 'PHP CS Fixer');
if (\count($reportSummary->getChanged()) > 0) {
$this->createFailedTestCases($dom, $testsuite, $reportSummary);
} else {
$this->createSuccessTestCase($dom, $testsuite);
}
if ($reportSummary->getTime() > 0) {
$testsuite->setAttribute(
'time',
sprintf(
'%.3f',
$reportSummary->getTime() / 1000
)
);
}
$dom->formatOutput = true;
return $reportSummary->isDecoratedOutput() ? OutputFormatter::escape($dom->saveXML()) : $dom->saveXML();
}
private function createSuccessTestCase(\DOMDocument $dom, \DOMElement $testsuite): void
{
$testcase = $dom->createElement('testcase');
$testcase->setAttribute('name', 'All OK');
$testcase->setAttribute('assertions', '1');
$testsuite->appendChild($testcase);
$testsuite->setAttribute('tests', '1');
$testsuite->setAttribute('assertions', '1');
$testsuite->setAttribute('failures', '0');
$testsuite->setAttribute('errors', '0');
}
private function createFailedTestCases(\DOMDocument $dom, \DOMElement $testsuite, ReportSummary $reportSummary): void
{
$assertionsCount = 0;
foreach ($reportSummary->getChanged() as $file => $fixResult) {
$testcase = $this->createFailedTestCase(
$dom,
$file,
$fixResult,
$reportSummary->shouldAddAppliedFixers()
);
$testsuite->appendChild($testcase);
$assertionsCount += (int) $testcase->getAttribute('assertions');
}
$testsuite->setAttribute('tests', (string) \count($reportSummary->getChanged()));
$testsuite->setAttribute('assertions', (string) $assertionsCount);
$testsuite->setAttribute('failures', (string) $assertionsCount);
$testsuite->setAttribute('errors', '0');
}
private function createFailedTestCase(\DOMDocument $dom, string $file, array $fixResult, bool $shouldAddAppliedFixers): \DOMElement
{
$appliedFixersCount = \count($fixResult['appliedFixers']);
$testName = str_replace('.', '_DOT_', Preg::replace('@\.'.pathinfo($file, PATHINFO_EXTENSION).'$@', '', $file));
$testcase = $dom->createElement('testcase');
$testcase->setAttribute('name', $testName);
$testcase->setAttribute('file', $file);
$testcase->setAttribute('assertions', (string) $appliedFixersCount);
$failure = $dom->createElement('failure');
$failure->setAttribute('type', 'code_style');
$testcase->appendChild($failure);
if ($shouldAddAppliedFixers) {
$failureContent = "applied fixers:\n---------------\n";
foreach ($fixResult['appliedFixers'] as $appliedFixer) {
$failureContent .= "* {$appliedFixer}\n";
}
} else {
$failureContent = "Wrong code style\n";
}
if ('' !== $fixResult['diff']) {
$failureContent .= "\nDiff:\n---------------\n\n".$fixResult['diff'];
}
$failure->appendChild($dom->createCDATASection(trim($failureContent)));
return $testcase;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\Report\FixReport;
use Symfony\Component\Console\Formatter\OutputFormatter;
final class XmlReporter implements ReporterInterface
{
public function getFormat(): string
{
return 'xml';
}
public function generate(ReportSummary $reportSummary): string
{
if (!\extension_loaded('dom')) {
throw new \RuntimeException('Cannot generate report! `ext-dom` is not available!');
}
$dom = new \DOMDocument('1.0', 'UTF-8');
$root = $dom->createElement('report');
$dom->appendChild($root);
$filesXML = $dom->createElement('files');
$root->appendChild($filesXML);
$i = 1;
foreach ($reportSummary->getChanged() as $file => $fixResult) {
$fileXML = $dom->createElement('file');
$fileXML->setAttribute('id', (string) $i++);
$fileXML->setAttribute('name', $file);
$filesXML->appendChild($fileXML);
if ($reportSummary->shouldAddAppliedFixers()) {
$fileXML->appendChild(
$this->createAppliedFixersElement($dom, $fixResult['appliedFixers']),
);
}
if ('' !== $fixResult['diff']) {
$fileXML->appendChild($this->createDiffElement($dom, $fixResult['diff']));
}
}
if (0 !== $reportSummary->getTime()) {
$root->appendChild($this->createTimeElement($reportSummary->getTime(), $dom));
}
if (0 !== $reportSummary->getMemory()) {
$root->appendChild($this->createMemoryElement($reportSummary->getMemory(), $dom));
}
$dom->formatOutput = true;
return $reportSummary->isDecoratedOutput() ? OutputFormatter::escape($dom->saveXML()) : $dom->saveXML();
}
private function createAppliedFixersElement(\DOMDocument $dom, array $appliedFixers): \DOMElement
{
$appliedFixersXML = $dom->createElement('applied_fixers');
foreach ($appliedFixers as $appliedFixer) {
$appliedFixerXML = $dom->createElement('applied_fixer');
$appliedFixerXML->setAttribute('name', $appliedFixer);
$appliedFixersXML->appendChild($appliedFixerXML);
}
return $appliedFixersXML;
}
private function createDiffElement(\DOMDocument $dom, string $diff): \DOMElement
{
$diffXML = $dom->createElement('diff');
$diffXML->appendChild($dom->createCDATASection($diff));
return $diffXML;
}
private function createTimeElement(float $time, \DOMDocument $dom): \DOMElement
{
$time = round($time / 1000, 3);
$timeXML = $dom->createElement('time');
$timeXML->setAttribute('unit', 's');
$timeTotalXML = $dom->createElement('total');
$timeTotalXML->setAttribute('value', (string) $time);
$timeXML->appendChild($timeTotalXML);
return $timeXML;
}
private function createMemoryElement(float $memory, \DOMDocument $dom): \DOMElement
{
$memory = round($memory / 1024 / 1024, 3);
$memoryXML = $dom->createElement('memory');
$memoryXML->setAttribute('value', (string) $memory);
$memoryXML->setAttribute('unit', 'MB');
return $memoryXML;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\Report\FixReport;
final class ReportSummary
{
private array $changed;
private int $time;
private int $memory;
private bool $addAppliedFixers;
private bool $isDryRun;
private bool $isDecoratedOutput;
public function __construct(
array $changed,
int $time,
int $memory,
bool $addAppliedFixers,
bool $isDryRun,
bool $isDecoratedOutput
) {
$this->changed = $changed;
$this->time = $time;
$this->memory = $memory;
$this->addAppliedFixers = $addAppliedFixers;
$this->isDryRun = $isDryRun;
$this->isDecoratedOutput = $isDecoratedOutput;
}
public function isDecoratedOutput(): bool
{
return $this->isDecoratedOutput;
}
public function isDryRun(): bool
{
return $this->isDryRun;
}
public function getChanged(): array
{
return $this->changed;
}
public function getMemory(): int
{
return $this->memory;
}
public function getTime(): int
{
return $this->time;
}
public function shouldAddAppliedFixers(): bool
{
return $this->addAppliedFixers;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\Report\FixReport;
use Symfony\Component\Console\Formatter\OutputFormatter;
final class GitlabReporter implements ReporterInterface
{
public function getFormat(): string
{
return 'gitlab';
}
public function generate(ReportSummary $reportSummary): string
{
$report = [];
foreach ($reportSummary->getChanged() as $fileName => $change) {
foreach ($change['appliedFixers'] as $fixerName) {
$report[] = [
'description' => $fixerName,
'fingerprint' => md5($fileName.$fixerName),
'severity' => 'minor',
'location' => [
'path' => $fileName,
'lines' => [
'begin' => 0,
],
],
];
}
}
$jsonString = json_encode($report);
return $reportSummary->isDecoratedOutput() ? OutputFormatter::escape($jsonString) : $jsonString;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\Report\FixReport;
use Symfony\Component\Console\Formatter\OutputFormatter;
final class CheckstyleReporter implements ReporterInterface
{
public function getFormat(): string
{
return 'checkstyle';
}
public function generate(ReportSummary $reportSummary): string
{
if (!\extension_loaded('dom')) {
throw new \RuntimeException('Cannot generate report! `ext-dom` is not available!');
}
$dom = new \DOMDocument('1.0', 'UTF-8');
$checkstyles = $dom->appendChild($dom->createElement('checkstyle'));
foreach ($reportSummary->getChanged() as $filePath => $fixResult) {
$file = $checkstyles->appendChild($dom->createElement('file'));
$file->setAttribute('name', $filePath);
foreach ($fixResult['appliedFixers'] as $appliedFixer) {
$error = $this->createError($dom, $appliedFixer);
$file->appendChild($error);
}
}
$dom->formatOutput = true;
return $reportSummary->isDecoratedOutput() ? OutputFormatter::escape($dom->saveXML()) : $dom->saveXML();
}
private function createError(\DOMDocument $dom, string $appliedFixer): \DOMElement
{
$error = $dom->createElement('error');
$error->setAttribute('severity', 'warning');
$error->setAttribute('source', 'PHP-CS-Fixer.'.$appliedFixer);
$error->setAttribute('message', 'Found violation(s) of type: '.$appliedFixer);
return $error;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\Report\FixReport;
use PhpCsFixer\Differ\DiffConsoleFormatter;
final class TextReporter implements ReporterInterface
{
public function getFormat(): string
{
return 'txt';
}
public function generate(ReportSummary $reportSummary): string
{
$output = '';
$i = 0;
foreach ($reportSummary->getChanged() as $file => $fixResult) {
++$i;
$output .= sprintf('%4d) %s', $i, $file);
if ($reportSummary->shouldAddAppliedFixers()) {
$output .= $this->getAppliedFixers(
$reportSummary->isDecoratedOutput(),
$fixResult['appliedFixers'],
);
}
$output .= $this->getDiff($reportSummary->isDecoratedOutput(), $fixResult['diff']);
$output .= PHP_EOL;
}
return $output.$this->getFooter($reportSummary->getTime(), $reportSummary->getMemory(), $reportSummary->isDryRun());
}
private function getAppliedFixers(bool $isDecoratedOutput, array $appliedFixers): string
{
return sprintf(
$isDecoratedOutput ? ' (<comment>%s</comment>)' : ' (%s)',
implode(', ', $appliedFixers)
);
}
private function getDiff(bool $isDecoratedOutput, string $diff): string
{
if ('' === $diff) {
return '';
}
$diffFormatter = new DiffConsoleFormatter($isDecoratedOutput, sprintf(
'<comment> ---------- begin diff ----------</comment>%s%%s%s<comment> ----------- end diff -----------</comment>',
PHP_EOL,
PHP_EOL
));
return PHP_EOL.$diffFormatter->format($diff).PHP_EOL;
}
private function getFooter(int $time, int $memory, bool $isDryRun): string
{
if (0 === $time || 0 === $memory) {
return '';
}
return PHP_EOL.sprintf(
'%s all files in %.3f seconds, %.3f MB memory used'.PHP_EOL,
$isDryRun ? 'Checked' : 'Fixed',
$time / 1000,
$memory / 1024 / 1024
);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\Report\FixReport;
use Symfony\Component\Console\Formatter\OutputFormatter;
final class JsonReporter implements ReporterInterface
{
public function getFormat(): string
{
return 'json';
}
public function generate(ReportSummary $reportSummary): string
{
$jsonFiles = [];
foreach ($reportSummary->getChanged() as $file => $fixResult) {
$jsonFile = ['name' => $file];
if ($reportSummary->shouldAddAppliedFixers()) {
$jsonFile['appliedFixers'] = $fixResult['appliedFixers'];
}
if ('' !== $fixResult['diff']) {
$jsonFile['diff'] = $fixResult['diff'];
}
$jsonFiles[] = $jsonFile;
}
$json = [
'files' => $jsonFiles,
'time' => [
'total' => round($reportSummary->getTime() / 1000, 3),
],
'memory' => round($reportSummary->getMemory() / 1024 / 1024, 3),
];
$json = json_encode($json);
return $reportSummary->isDecoratedOutput() ? OutputFormatter::escape($json) : $json;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\Report\FixReport;
use Symfony\Component\Finder\Finder as SymfonyFinder;
final class ReporterFactory
{
private array $reporters = [];
public function registerBuiltInReporters(): self
{
static $builtInReporters;
if (null === $builtInReporters) {
$builtInReporters = [];
foreach (SymfonyFinder::create()->files()->name('*Reporter.php')->in(__DIR__) as $file) {
$relativeNamespace = $file->getRelativePath();
$builtInReporters[] = sprintf(
'%s\\%s%s',
__NAMESPACE__,
$relativeNamespace ? $relativeNamespace.'\\' : '',
$file->getBasename('.php')
);
}
}
foreach ($builtInReporters as $reporterClass) {
$this->registerReporter(new $reporterClass());
}
return $this;
}
public function registerReporter(ReporterInterface $reporter): self
{
$format = $reporter->getFormat();
if (isset($this->reporters[$format])) {
throw new \UnexpectedValueException(sprintf('Reporter for format "%s" is already registered.', $format));
}
$this->reporters[$format] = $reporter;
return $this;
}
public function getFormats(): array
{
$formats = array_keys($this->reporters);
sort($formats);
return $formats;
}
public function getReporter(string $format): ReporterInterface
{
if (!isset($this->reporters[$format])) {
throw new \UnexpectedValueException(sprintf('Reporter for format "%s" is not registered.', $format));
}
return $this->reporters[$format];
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\Report\FixReport;
interface ReporterInterface
{
public function getFormat(): string;
public function generate(ReportSummary $reportSummary): string;
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\Command;
final class DescribeNameNotFoundException extends \InvalidArgumentException
{
private string $name;
private string $type;
public function __construct(string $name, string $type)
{
$this->name = $name;
$this->type = $type;
parent::__construct();
}
public function getName(): string
{
return $this->name;
}
public function getType(): string
{
return $this->type;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\Command;
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
use PhpCsFixer\FixerConfiguration\FixerOptionInterface;
use PhpCsFixer\Preg;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\HelpCommand as BaseHelpCommand;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
#[AsCommand(name: 'help')]
final class HelpCommand extends BaseHelpCommand
{
protected static $defaultName = 'help';
public static function toString($value): string
{
return \is_array($value)
? static::arrayToString($value)
: static::scalarToString($value)
;
}
public static function getDisplayableAllowedValues(FixerOptionInterface $option): ?array
{
$allowed = $option->getAllowedValues();
if (null !== $allowed) {
$allowed = array_filter($allowed, static function ($value): bool {
return !$value instanceof \Closure;
});
usort($allowed, static function ($valueA, $valueB): int {
if ($valueA instanceof AllowedValueSubset) {
return -1;
}
if ($valueB instanceof AllowedValueSubset) {
return 1;
}
return strcasecmp(
self::toString($valueA),
self::toString($valueB)
);
});
if (0 === \count($allowed)) {
$allowed = null;
}
}
return $allowed;
}
protected function initialize(InputInterface $input, OutputInterface $output): void
{
$output->getFormatter()->setStyle('url', new OutputFormatterStyle('blue'));
}
private static function scalarToString($value): string
{
$str = var_export($value, true);
return Preg::replace('/\bNULL\b/', 'null', $str);
}
private static function arrayToString(array $value): string
{
if (0 === \count($value)) {
return '[]';
}
$isHash = !array_is_list($value);
$str = '[';
foreach ($value as $k => $v) {
if ($isHash) {
$str .= static::scalarToString($k).' => ';
}
$str .= \is_array($v)
? static::arrayToString($v).', '
: static::scalarToString($v).', '
;
}
return substr($str, 0, -2).']';
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\Command;
final class FixCommandExitStatusCalculator
{
public const EXIT_STATUS_FLAG_HAS_INVALID_FILES = 4;
public const EXIT_STATUS_FLAG_HAS_CHANGED_FILES = 8;
public const EXIT_STATUS_FLAG_HAS_INVALID_CONFIG = 16;
public const EXIT_STATUS_FLAG_HAS_INVALID_FIXER_CONFIG = 32;
public const EXIT_STATUS_FLAG_EXCEPTION_IN_APP = 64;
public function calculate(bool $isDryRun, bool $hasChangedFiles, bool $hasInvalidErrors, bool $hasExceptionErrors, bool $hasLintErrorsAfterFixing): int
{
$exitStatus = 0;
if ($isDryRun) {
if ($hasChangedFiles) {
$exitStatus |= self::EXIT_STATUS_FLAG_HAS_CHANGED_FILES;
}
if ($hasInvalidErrors) {
$exitStatus |= self::EXIT_STATUS_FLAG_HAS_INVALID_FILES;
}
}
if ($hasExceptionErrors || $hasLintErrorsAfterFixing) {
$exitStatus |= self::EXIT_STATUS_FLAG_EXCEPTION_IN_APP;
}
return $exitStatus;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\Command;
use PhpCsFixer\Config;
use PhpCsFixer\ConfigInterface;
use PhpCsFixer\ConfigurationException\InvalidConfigurationException;
use PhpCsFixer\Console\ConfigurationResolver;
use PhpCsFixer\Console\Output\ErrorOutput;
use PhpCsFixer\Console\Output\NullOutput;
use PhpCsFixer\Console\Output\ProcessOutput;
use PhpCsFixer\Console\Report\FixReport\ReportSummary;
use PhpCsFixer\Error\ErrorsManager;
use PhpCsFixer\Runner\Runner;
use PhpCsFixer\ToolInfoInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Terminal;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Stopwatch\Stopwatch;
#[AsCommand(name: 'fix')]
final class FixCommand extends Command
{
protected static $defaultName = 'fix';
private EventDispatcherInterface $eventDispatcher;
private ErrorsManager $errorsManager;
private Stopwatch $stopwatch;
private ConfigInterface $defaultConfig;
private ToolInfoInterface $toolInfo;
public function __construct(ToolInfoInterface $toolInfo)
{
parent::__construct();
$this->eventDispatcher = new EventDispatcher();
$this->errorsManager = new ErrorsManager();
$this->stopwatch = new Stopwatch();
$this->defaultConfig = new Config();
$this->toolInfo = $toolInfo;
}
public function getHelp(): string
{
return <<<'EOF'
The <info>%command.name%</info> command tries to fix as much coding standards
problems as possible on a given file or files in a given directory and its subdirectories:
<info>$ php %command.full_name% /path/to/dir</info>
<info>$ php %command.full_name% /path/to/file</info>
By default <comment>--path-mode</comment> is set to `override`, which means, that if you specify the path to a file or a directory via
command arguments, then the paths provided to a `Finder` in config file will be ignored. You can use <comment>--path-mode=intersection</comment>
to merge paths from the config file and from the argument:
<info>$ php %command.full_name% --path-mode=intersection /path/to/dir</info>
The <comment>--format</comment> option for the output format. Supported formats are `txt` (default one), `json`, `xml`, `checkstyle`, `junit` and `gitlab`.
NOTE: the output for the following formats are generated in accordance with schemas
* `checkstyle` follows the common `"checkstyle" XML schema </doc/schemas/fix/checkstyle.xsd>`_
* `json` follows the `own JSON schema </doc/schemas/fix/schema.json>`_
* `junit` follows the `JUnit XML schema from Jenkins </doc/schemas/fix/junit-10.xsd>`_
* `xml` follows the `own XML schema </doc/schemas/fix/xml.xsd>`_
The <comment>--quiet</comment> Do not output any message.
The <comment>--verbose</comment> option will show the applied rules. When using the `txt` format it will also display progress notifications.
NOTE: if there is an error like "errors reported during linting after fixing", you can use this to be even more verbose for debugging purpose
* `-v`: verbose
* `-vv`: very verbose
* `-vvv`: debug
The <comment>--rules</comment> option limits the rules to apply to the
project:
EOF. <<<'EOF'
<info>$ php %command.full_name% /path/to/project --rules=@PSR12</info>
By default the PSR-12 rules are used.
The <comment>--rules</comment> option lets you choose the exact rules to
apply (the rule names must be separated by a comma):
<info>$ php %command.full_name% /path/to/dir --rules=line_ending,full_opening_tag,indentation_type</info>
You can also exclude the rules you don't want by placing a dash in front of the rule name, if this is more convenient,
using <comment>-name_of_fixer</comment>:
<info>$ php %command.full_name% /path/to/dir --rules=-full_opening_tag,-indentation_type</info>
When using combinations of exact and exclude rules, applying exact rules along with above excluded results:
<info>$ php %command.full_name% /path/to/project --rules=@Symfony,-@PSR1,-blank_line_before_statement,strict_comparison</info>
Complete configuration for rules can be supplied using a `json` formatted string.
<info>$ php %command.full_name% /path/to/project --rules='{"concat_space": {"spacing": "none"}}'</info>
The <comment>--dry-run</comment> flag will run the fixer without making changes to your files.
The <comment>--diff</comment> flag can be used to let the fixer output all the changes it makes.
The <comment>--allow-risky</comment> option (pass `yes` or `no`) allows you to set whether risky rules may run. Default value is taken from config file.
A rule is considered risky if it could change code behaviour. By default no risky rules are run.
The <comment>--stop-on-violation</comment> flag stops the execution upon first file that needs to be fixed.
The <comment>--show-progress</comment> option allows you to choose the way process progress is rendered:
* <comment>none</comment>: disables progress output;
* <comment>dots</comment>: multiline progress output with number of files and percentage on each line.
If the option is not provided, it defaults to <comment>dots</comment> unless a config file that disables output is used, in which case it defaults to <comment>none</comment>. This option has no effect if the verbosity of the command is less than <comment>verbose</comment>.
<info>$ php %command.full_name% --verbose --show-progress=dots</info>
By using <command>--using-cache</command> option with `yes` or `no` you can set if the caching
mechanism should be used.
The command can also read from standard input, in which case it won't
automatically fix anything:
<info>$ cat foo.php | php %command.full_name% --diff -</info>
Finally, if you don't need BC kept on CLI level, you might use `PHP_CS_FIXER_FUTURE_MODE` to start using options that
would be default in next MAJOR release and to forbid using deprecated configuration:
<info>$ PHP_CS_FIXER_FUTURE_MODE=1 php %command.full_name% -v --diff</info>
Exit code
---------
Exit code of the fix command is built using following bit flags:
* 0 - OK.
* 1 - General error (or PHP minimal requirement not matched).
* 4 - Some files have invalid syntax (only in dry-run mode).
* 8 - Some files need fixing (only in dry-run mode).
* 16 - Configuration error of the application.
* 32 - Configuration error of a Fixer.
* 64 - Exception raised within the application.
EOF
;
}
protected function configure(): void
{
$this
->setDefinition(
[
new InputArgument('path', InputArgument::IS_ARRAY, 'The path.'),
new InputOption('path-mode', '', InputOption::VALUE_REQUIRED, 'Specify path mode (can be override or intersection).', ConfigurationResolver::PATH_MODE_OVERRIDE),
new InputOption('allow-risky', '', InputOption::VALUE_REQUIRED, 'Are risky fixers allowed (can be yes or no).'),
new InputOption('config', '', InputOption::VALUE_REQUIRED, 'The path to a .php-cs-fixer.php file.'),
new InputOption('dry-run', '', InputOption::VALUE_NONE, 'Only shows which files would have been modified.'),
new InputOption('rules', '', InputOption::VALUE_REQUIRED, 'The rules.'),
new InputOption('using-cache', '', InputOption::VALUE_REQUIRED, 'Does cache should be used (can be yes or no).'),
new InputOption('cache-file', '', InputOption::VALUE_REQUIRED, 'The path to the cache file.'),
new InputOption('diff', '', InputOption::VALUE_NONE, 'Also produce diff for each file.'),
new InputOption('format', '', InputOption::VALUE_REQUIRED, 'To output results in other formats.'),
new InputOption('stop-on-violation', '', InputOption::VALUE_NONE, 'Stop execution on first violation.'),
new InputOption('show-progress', '', InputOption::VALUE_REQUIRED, 'Type of progress indicator (none, dots).'),
]
)
->setDescription('Fixes a directory or a file.')
;
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$verbosity = $output->getVerbosity();
$passedConfig = $input->getOption('config');
$passedRules = $input->getOption('rules');
if (null !== $passedConfig && null !== $passedRules) {
throw new InvalidConfigurationException('Passing both `--config` and `--rules` options is not allowed.');
}
$resolver = new ConfigurationResolver(
$this->defaultConfig,
[
'allow-risky' => $input->getOption('allow-risky'),
'config' => $passedConfig,
'dry-run' => $input->getOption('dry-run'),
'rules' => $passedRules,
'path' => $input->getArgument('path'),
'path-mode' => $input->getOption('path-mode'),
'using-cache' => $input->getOption('using-cache'),
'cache-file' => $input->getOption('cache-file'),
'format' => $input->getOption('format'),
'diff' => $input->getOption('diff'),
'stop-on-violation' => $input->getOption('stop-on-violation'),
'verbosity' => $verbosity,
'show-progress' => $input->getOption('show-progress'),
],
getcwd(),
$this->toolInfo
);
$reporter = $resolver->getReporter();
$stdErr = $output instanceof ConsoleOutputInterface
? $output->getErrorOutput()
: ('txt' === $reporter->getFormat() ? $output : null)
;
if (null !== $stdErr) {
if (OutputInterface::VERBOSITY_VERBOSE <= $verbosity) {
$stdErr->writeln($this->getApplication()->getLongVersion());
}
$configFile = $resolver->getConfigFile();
$stdErr->writeln(sprintf('Loaded config <comment>%s</comment>%s.', $resolver->getConfig()->getName(), null === $configFile ? '' : ' from "'.$configFile.'"'));
if ($resolver->getUsingCache()) {
$cacheFile = $resolver->getCacheFile();
if (is_file($cacheFile)) {
$stdErr->writeln(sprintf('Using cache file "%s".', $cacheFile));
}
}
}
$progressType = $resolver->getProgress();
$finder = $resolver->getFinder();
if (null !== $stdErr && $resolver->configFinderIsOverridden()) {
$stdErr->writeln(
sprintf($stdErr->isDecorated() ? '<bg=yellow;fg=black;>%s</>' : '%s', 'Paths from configuration file have been overridden by paths provided as command arguments.')
);
}
if ('none' === $progressType || null === $stdErr) {
$progressOutput = new NullOutput();
} else {
$finder = new \ArrayIterator(iterator_to_array($finder));
$progressOutput = new ProcessOutput(
$stdErr,
$this->eventDispatcher,
(new Terminal())->getWidth(),
\count($finder)
);
}
$runner = new Runner(
$finder,
$resolver->getFixers(),
$resolver->getDiffer(),
'none' !== $progressType ? $this->eventDispatcher : null,
$this->errorsManager,
$resolver->getLinter(),
$resolver->isDryRun(),
$resolver->getCacheManager(),
$resolver->getDirectory(),
$resolver->shouldStopOnViolation()
);
$this->stopwatch->start('fixFiles');
$changed = $runner->fix();
$this->stopwatch->stop('fixFiles');
$progressOutput->printLegend();
$fixEvent = $this->stopwatch->getEvent('fixFiles');
$reportSummary = new ReportSummary(
$changed,
$fixEvent->getDuration(),
$fixEvent->getMemory(),
OutputInterface::VERBOSITY_VERBOSE <= $verbosity,
$resolver->isDryRun(),
$output->isDecorated()
);
$output->isDecorated()
? $output->write($reporter->generate($reportSummary))
: $output->write($reporter->generate($reportSummary), false, OutputInterface::OUTPUT_RAW)
;
$invalidErrors = $this->errorsManager->getInvalidErrors();
$exceptionErrors = $this->errorsManager->getExceptionErrors();
$lintErrors = $this->errorsManager->getLintErrors();
if (null !== $stdErr) {
$errorOutput = new ErrorOutput($stdErr);
if (\count($invalidErrors) > 0) {
$errorOutput->listErrors('linting before fixing', $invalidErrors);
}
if (\count($exceptionErrors) > 0) {
$errorOutput->listErrors('fixing', $exceptionErrors);
}
if (\count($lintErrors) > 0) {
$errorOutput->listErrors('linting after fixing', $lintErrors);
}
}
$exitStatusCalculator = new FixCommandExitStatusCalculator();
return $exitStatusCalculator->calculate(
$resolver->isDryRun(),
\count($changed) > 0,
\count($invalidErrors) > 0,
\count($exceptionErrors) > 0,
\count($lintErrors) > 0
);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\Command;
use PhpCsFixer\Config;
use PhpCsFixer\ConfigInterface;
use PhpCsFixer\Console\ConfigurationResolver;
use PhpCsFixer\ToolInfoInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
#[AsCommand(name: 'list-files')]
final class ListFilesCommand extends Command
{
protected static $defaultName = 'list-files';
private ConfigInterface $defaultConfig;
private ToolInfoInterface $toolInfo;
public function __construct(ToolInfoInterface $toolInfo)
{
parent::__construct();
$this->defaultConfig = new Config();
$this->toolInfo = $toolInfo;
}
protected function configure(): void
{
$this
->setDefinition(
[
new InputOption('config', '', InputOption::VALUE_REQUIRED, 'The path to a .php-cs-fixer.php file.'),
]
)
->setDescription('List all files being fixed by the given config.')
;
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$passedConfig = $input->getOption('config');
$cwd = getcwd();
$resolver = new ConfigurationResolver(
$this->defaultConfig,
[
'config' => $passedConfig,
],
$cwd,
$this->toolInfo
);
$finder = $resolver->getFinder();
foreach ($finder as $file) {
if ($file->isFile()) {
$relativePath = str_replace($cwd, '.', $file->getRealPath());
$relativePath = str_replace('/', \DIRECTORY_SEPARATOR, $relativePath);
$output->writeln(escapeshellarg($relativePath));
}
}
return 0;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\Command;
use PhpCsFixer\Console\SelfUpdate\NewVersionCheckerInterface;
use PhpCsFixer\PharCheckerInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\ToolInfoInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
#[AsCommand(name: 'self-update')]
final class SelfUpdateCommand extends Command
{
protected static $defaultName = 'self-update';
private NewVersionCheckerInterface $versionChecker;
private ToolInfoInterface $toolInfo;
private PharCheckerInterface $pharChecker;
public function __construct(
NewVersionCheckerInterface $versionChecker,
ToolInfoInterface $toolInfo,
PharCheckerInterface $pharChecker
) {
parent::__construct();
$this->versionChecker = $versionChecker;
$this->toolInfo = $toolInfo;
$this->pharChecker = $pharChecker;
}
protected function configure(): void
{
$this
->setAliases(['selfupdate'])
->setDefinition(
[
new InputOption('--force', '-f', InputOption::VALUE_NONE, 'Force update to next major version if available.'),
]
)
->setDescription('Update php-cs-fixer.phar to the latest stable version.')
->setHelp(
<<<'EOT'
The <info>%command.name%</info> command replace your php-cs-fixer.phar by the
latest version released on:
<comment>https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/releases</comment>
<info>$ php php-cs-fixer.phar %command.name%</info>
EOT
)
;
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity() && $output instanceof ConsoleOutputInterface) {
$stdErr = $output->getErrorOutput();
$stdErr->writeln($this->getApplication()->getLongVersion());
}
if (!$this->toolInfo->isInstalledAsPhar()) {
$output->writeln('<error>Self-update is available only for PHAR version.</error>');
return 1;
}
$currentVersion = $this->getApplication()->getVersion();
Preg::match('/^v?(?<major>\d+)\./', $currentVersion, $matches);
$currentMajor = (int) $matches['major'];
try {
$latestVersion = $this->versionChecker->getLatestVersion();
$latestVersionOfCurrentMajor = $this->versionChecker->getLatestVersionOfMajor($currentMajor);
} catch (\Exception $exception) {
$output->writeln(sprintf(
'<error>Unable to determine newest version: %s</error>',
$exception->getMessage()
));
return 1;
}
if (1 !== $this->versionChecker->compareVersions($latestVersion, $currentVersion)) {
$output->writeln('<info>PHP CS Fixer is already up-to-date.</info>');
return 0;
}
$remoteTag = $latestVersion;
if (
0 !== $this->versionChecker->compareVersions($latestVersionOfCurrentMajor, $latestVersion)
&& true !== $input->getOption('force')
) {
$output->writeln(sprintf('<info>A new major version of PHP CS Fixer is available</info> (<comment>%s</comment>)', $latestVersion));
$output->writeln(sprintf('<info>Before upgrading please read</info> https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/blob/%s/UPGRADE-v%s.md', $latestVersion, $currentMajor + 1));
$output->writeln('<info>If you are ready to upgrade run this command with</info> <comment>-f</comment>');
$output->writeln('<info>Checking for new minor/patch version...</info>');
if (1 !== $this->versionChecker->compareVersions($latestVersionOfCurrentMajor, $currentVersion)) {
$output->writeln('<info>No minor update for PHP CS Fixer.</info>');
return 0;
}
$remoteTag = $latestVersionOfCurrentMajor;
}
$localFilename = realpath($_SERVER['argv'][0]) ?: $_SERVER['argv'][0];
if (!is_writable($localFilename)) {
$output->writeln(sprintf('<error>No permission to update</error> "%s" <error>file.</error>', $localFilename));
return 1;
}
$tempFilename = \dirname($localFilename).'/'.basename($localFilename, '.phar').'-tmp.phar';
$remoteFilename = $this->toolInfo->getPharDownloadUri($remoteTag);
if (false === @copy($remoteFilename, $tempFilename)) {
$output->writeln(sprintf('<error>Unable to download new version</error> %s <error>from the server.</error>', $remoteTag));
return 1;
}
chmod($tempFilename, 0777 & ~umask());
$pharInvalidityReason = $this->pharChecker->checkFileValidity($tempFilename);
if (null !== $pharInvalidityReason) {
unlink($tempFilename);
$output->writeln(sprintf('<error>The download of</error> %s <error>is corrupt (%s).</error>', $remoteTag, $pharInvalidityReason));
$output->writeln('<error>Please re-run the "self-update" command to try again.</error>');
return 1;
}
rename($tempFilename, $localFilename);
$output->writeln(sprintf('<info>PHP CS Fixer updated</info> (<comment>%s</comment> -> <comment>%s</comment>)', $currentVersion, $remoteTag));
return 0;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\Command;
use PhpCsFixer\Documentation\DocumentationLocator;
use PhpCsFixer\Documentation\FixerDocumentGenerator;
use PhpCsFixer\Documentation\ListDocumentGenerator;
use PhpCsFixer\Documentation\RuleSetDocumentationGenerator;
use PhpCsFixer\FixerFactory;
use PhpCsFixer\RuleSet\RuleSets;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;
#[AsCommand(name: 'documentation')]
final class DocumentationCommand extends Command
{
protected static $defaultName = 'documentation';
protected function configure(): void
{
$this
->setAliases(['doc'])
->setDescription('Dumps the documentation of the project into its "/doc" directory.')
;
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$filesystem = new Filesystem();
$locator = new DocumentationLocator();
$fixerFactory = new FixerFactory();
$fixerFactory->registerBuiltInFixers();
$fixers = $fixerFactory->getFixers();
$setDefinitions = RuleSets::getSetDefinitions();
$fixerDocumentGenerator = new FixerDocumentGenerator($locator);
$ruleSetDocumentationGenerator = new RuleSetDocumentationGenerator($locator);
$listDocumentGenerator = new ListDocumentGenerator($locator);
$docForFixerRelativePaths = [];
foreach ($fixers as $fixer) {
$docForFixerRelativePaths[] = $locator->getFixerDocumentationFileRelativePath($fixer);
$filesystem->dumpFile(
$locator->getFixerDocumentationFilePath($fixer),
$fixerDocumentGenerator->generateFixerDocumentation($fixer)
);
}
foreach (
(new Finder())->files()
->in($locator->getFixersDocumentationDirectoryPath())
->notPath($docForFixerRelativePaths) as $file
) {
$filesystem->remove($file->getPathname());
}
$filesystem->dumpFile(
$locator->getFixersDocumentationIndexFilePath(),
$fixerDocumentGenerator->generateFixersDocumentationIndex($fixers)
);
foreach ((new Finder())->files()->in($locator->getRuleSetsDocumentationDirectoryPath()) as $file) {
$filesystem->remove($file->getPathname());
}
$paths = [];
foreach ($setDefinitions as $name => $definition) {
$path = $locator->getRuleSetsDocumentationFilePath($name);
$paths[$name] = $path;
$filesystem->dumpFile($path, $ruleSetDocumentationGenerator->generateRuleSetsDocumentation($definition, $fixers));
}
$filesystem->dumpFile(
$locator->getRuleSetsDocumentationIndexFilePath(),
$ruleSetDocumentationGenerator->generateRuleSetsDocumentationIndex($paths)
);
$filesystem->dumpFile(
$locator->getListingFilePath(),
$listDocumentGenerator->generateListingDocumentation($fixers)
);
$output->writeln('Docs updated.');
return 0;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\Command;
use PhpCsFixer\ConfigurationException\InvalidConfigurationException;
use PhpCsFixer\Console\Report\ListSetsReport\ReporterFactory;
use PhpCsFixer\Console\Report\ListSetsReport\ReporterInterface;
use PhpCsFixer\Console\Report\ListSetsReport\ReportSummary;
use PhpCsFixer\Console\Report\ListSetsReport\TextReporter;
use PhpCsFixer\RuleSet\RuleSets;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
#[AsCommand(name: 'list-sets')]
final class ListSetsCommand extends Command
{
protected static $defaultName = 'list-sets';
protected function configure(): void
{
$this
->setDefinition(
[
new InputOption('format', '', InputOption::VALUE_REQUIRED, 'To output results in other formats.', (new TextReporter())->getFormat()),
]
)
->setDescription('List all available RuleSets.')
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$reporter = $this->resolveReporterWithFactory(
$input->getOption('format'),
new ReporterFactory()
);
$reportSummary = new ReportSummary(
array_values(RuleSets::getSetDefinitions())
);
$report = $reporter->generate($reportSummary);
$output->isDecorated()
? $output->write(OutputFormatter::escape($report))
: $output->write($report, false, OutputInterface::OUTPUT_RAW)
;
return 0;
}
private function resolveReporterWithFactory(string $format, ReporterFactory $factory): ReporterInterface
{
try {
$factory->registerBuiltInReporters();
$reporter = $factory->getReporter($format);
} catch (\UnexpectedValueException $e) {
$formats = $factory->getFormats();
sort($formats);
throw new InvalidConfigurationException(sprintf('The format "%s" is not defined, supported are "%s".', $format, implode('", "', $formats)));
}
return $reporter;
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer\Console\Command;
use PhpCsFixer\Differ\DiffConsoleFormatter;
use PhpCsFixer\Differ\FullDiffer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\DeprecatedFixerInterface;
use PhpCsFixer\Fixer\FixerInterface;
use PhpCsFixer\FixerConfiguration\AliasedFixerOption;
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
use PhpCsFixer\FixerConfiguration\DeprecatedFixerOption;
use PhpCsFixer\FixerDefinition\CodeSampleInterface;
use PhpCsFixer\FixerDefinition\FileSpecificCodeSampleInterface;
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSampleInterface;
use PhpCsFixer\FixerFactory;
use PhpCsFixer\Preg;
use PhpCsFixer\RuleSet\RuleSets;
use PhpCsFixer\StdinFileInfo;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Utils;
use PhpCsFixer\WordMatcher;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
#[AsCommand(name: 'describe')]
final class DescribeCommand extends Command
{
protected static $defaultName = 'describe';
private $setNames;
private FixerFactory $fixerFactory;
private $fixers;
public function __construct(?FixerFactory $fixerFactory = null)
{
parent::__construct();
if (null === $fixerFactory) {
$fixerFactory = new FixerFactory();
$fixerFactory->registerBuiltInFixers();
}
$this->fixerFactory = $fixerFactory;
}
protected function configure(): void
{
$this
->setDefinition(
[
new InputArgument('name', InputArgument::REQUIRED, 'Name of rule / set.'),
]
)
->setDescription('Describe rule / ruleset.')
;
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity() && $output instanceof ConsoleOutputInterface) {
$stdErr = $output->getErrorOutput();
$stdErr->writeln($this->getApplication()->getLongVersion());
}
$name = $input->getArgument('name');
try {
if (str_starts_with($name, '@')) {
$this->describeSet($output, $name);
return 0;
}
$this->describeRule($output, $name);
} catch (DescribeNameNotFoundException $e) {
$matcher = new WordMatcher(
'set' === $e->getType() ? $this->getSetNames() : array_keys($this->getFixers())
);
$alternative = $matcher->match($name);
$this->describeList($output, $e->getType());
throw new \InvalidArgumentException(sprintf(
'%s "%s" not found.%s',
ucfirst($e->getType()),
$name,
null === $alternative ? '' : ' Did you mean "'.$alternative.'"?'
));
}
return 0;
}
private function describeRule(OutputInterface $output, string $name): void
{
$fixers = $this->getFixers();
if (!isset($fixers[$name])) {
throw new DescribeNameNotFoundException($name, 'rule');
}
$fixer = $fixers[$name];
$definition = $fixer->getDefinition();
$summary = $definition->getSummary();
if ($fixer instanceof DeprecatedFixerInterface) {
$successors = $fixer->getSuccessorsNames();
$message = [] === $successors
? 'will be removed on next major version'
: sprintf('use %s instead', Utils::naturalLanguageJoinWithBackticks($successors));
$message = Preg::replace('/(`.+?`)/', '<info>$1</info>', $message);
$summary .= sprintf(' <error>DEPRECATED</error>: %s.', $message);
}
$output->writeln(sprintf('<info>Description of</info> %s <info>rule</info>.', $name));
if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
$output->writeln(sprintf('Fixer class: <comment>%s</comment>.', \get_class($fixer)));
}
$output->writeln($summary);
$description = $definition->getDescription();
if (null !== $description) {
$output->writeln($description);
}
$output->writeln('');
if ($fixer->isRisky()) {
$output->writeln('<error>Fixer applying this rule is risky.</error>');
$riskyDescription = $definition->getRiskyDescription();
if (null !== $riskyDescription) {
$output->writeln($riskyDescription);
}
$output->writeln('');
}
if ($fixer instanceof ConfigurableFixerInterface) {
$configurationDefinition = $fixer->getConfigurationDefinition();
$options = $configurationDefinition->getOptions();
$output->writeln(sprintf('Fixer is configurable using following option%s:', 1 === \count($options) ? '' : 's'));
foreach ($options as $option) {
$line = '* <info>'.OutputFormatter::escape($option->getName()).'</info>';
$allowed = HelpCommand::getDisplayableAllowedValues($option);
if (null === $allowed) {
$allowed = array_map(
static fn (string $type): string => '<comment>'.$type.'</comment>',
$option->getAllowedTypes(),
);
} else {
$allowed = array_map(static function ($value): string {
return $value instanceof AllowedValueSubset
? 'a subset of <comment>'.HelpCommand::toString($value->getAllowedValues()).'</comment>'
: '<comment>'.HelpCommand::toString($value).'</comment>';
}, $allowed);
}
$line .= ' ('.implode(', ', $allowed).')';
$description = Preg::replace('/(`.+?`)/', '<info>$1</info>', OutputFormatter::escape($option->getDescription()));
$line .= ': '.lcfirst(Preg::replace('/\.$/', '', $description)).'; ';
if ($option->hasDefault()) {
$line .= sprintf(
'defaults to <comment>%s</comment>',
HelpCommand::toString($option->getDefault())
);
} else {
$line .= '<comment>required</comment>';
}
if ($option instanceof DeprecatedFixerOption) {
$line .= '. <error>DEPRECATED</error>: '.Preg::replace(
'/(`.+?`)/',
'<info>$1</info>',
OutputFormatter::escape(lcfirst($option->getDeprecationMessage()))
);
}
if ($option instanceof AliasedFixerOption) {
$line .= '; <error>DEPRECATED</error> alias: <comment>'.$option->getAlias().'</comment>';
}
$output->writeln($line);
}
$output->writeln('');
}
$codeSamples = array_filter($definition->getCodeSamples(), static function (CodeSampleInterface $codeSample): bool {
if ($codeSample instanceof VersionSpecificCodeSampleInterface) {
return $codeSample->isSuitableFor(\PHP_VERSION_ID);
}
return true;
});
if (0 === \count($codeSamples)) {
$output->writeln([
'Fixing examples cannot be demonstrated on the current PHP version.',
'',
]);
} else {
$output->writeln('Fixing examples:');
$differ = new FullDiffer();
$diffFormatter = new DiffConsoleFormatter(
$output->isDecorated(),
sprintf(
'<comment> ---------- begin diff ----------</comment>%s%%s%s<comment> ----------- end diff -----------</comment>',
PHP_EOL,
PHP_EOL
)
);
foreach ($codeSamples as $index => $codeSample) {
$old = $codeSample->getCode();
$tokens = Tokens::fromCode($old);
$configuration = $codeSample->getConfiguration();
if ($fixer instanceof ConfigurableFixerInterface) {
$fixer->configure($configuration ?? []);
}
$file = $codeSample instanceof FileSpecificCodeSampleInterface
? $codeSample->getSplFileInfo()
: new StdinFileInfo();
$fixer->fix($file, $tokens);
$diff = $differ->diff($old, $tokens->generateCode());
if ($fixer instanceof ConfigurableFixerInterface) {
if (null === $configuration) {
$output->writeln(sprintf(' * Example #%d. Fixing with the <comment>default</comment> configuration.', $index + 1));
} else {
$output->writeln(sprintf(' * Example #%d. Fixing with configuration: <comment>%s</comment>.', $index + 1, HelpCommand::toString($codeSample->getConfiguration())));
}
} else {
$output->writeln(sprintf(' * Example #%d.', $index + 1));
}
$output->writeln([$diffFormatter->format($diff, ' %s'), '']);
}
}
}
private function describeSet(OutputInterface $output, string $name): void
{
if (!\in_array($name, $this->getSetNames(), true)) {
throw new DescribeNameNotFoundException($name, 'set');
}
$ruleSetDefinitions = RuleSets::getSetDefinitions();
$fixers = $this->getFixers();
$output->writeln(sprintf('<info>Description of the</info> %s <info>set.</info>', $ruleSetDefinitions[$name]->getName()));
$output->writeln($this->replaceRstLinks($ruleSetDefinitions[$name]->getDescription()));
if ($ruleSetDefinitions[$name]->isRisky()) {
$output->writeln('This set contains <error>risky</error> rules.');
}
$output->writeln('');
$help = '';
foreach ($ruleSetDefinitions[$name]->getRules() as $rule => $config) {
if (str_starts_with($rule, '@')) {
$set = $ruleSetDefinitions[$rule];
$help .= sprintf(
" * <info>%s</info>%s\n | %s\n\n",
$rule,
$set->isRisky() ? ' <error>risky</error>' : '',
$this->replaceRstLinks($set->getDescription())
);
continue;
}
$fixer = $fixers[$rule];
$definition = $fixer->getDefinition();
$help .= sprintf(
" * <info>%s</info>%s\n | %s\n%s\n",
$rule,
$fixer->isRisky() ? ' <error>risky</error>' : '',
$definition->getSummary(),
true !== $config ? sprintf(" <comment>| Configuration: %s</comment>\n", HelpCommand::toString($config)) : ''
);
}
$output->write($help);
}
private function getFixers(): array
{
if (null !== $this->fixers) {
return $this->fixers;
}
$fixers = [];
foreach ($this->fixerFactory->getFixers() as $fixer) {
$fixers[$fixer->getName()] = $fixer;
}
$this->fixers = $fixers;
ksort($this->fixers);
return $this->fixers;
}
private function getSetNames(): array
{
if (null !== $this->setNames) {
return $this->setNames;
}
$this->setNames = RuleSets::getSetDefinitionNames();
return $this->setNames;
}
private function describeList(OutputInterface $output, string $type): void
{
if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERY_VERBOSE) {
$describe = [
'sets' => $this->getSetNames(),
'rules' => $this->getFixers(),
];
} elseif ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
$describe = 'set' === $type ? ['sets' => $this->getSetNames()] : ['rules' => $this->getFixers()];
} else {
return;
}
foreach ($describe as $list => $items) {
$output->writeln(sprintf('<comment>Defined %s:</comment>', $list));
foreach ($items as $name => $item) {
$output->writeln(sprintf('* <info>%s</info>', \is_string($name) ? $name : $item));
}
}
}
private function replaceRstLinks(string $content): string
{
return Preg::replaceCallback(
'/(`[^<]+<[^>]+>`_)/',
static function (array $matches) {
return Preg::replaceCallback(
'/`(.*)<(.*)>`_/',
static function (array $matches): string {
return $matches[1].'('.$matches[2].')';
},
$matches[1]
);
},
$content
);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer;
final class FileRemoval
{
private array $files = [];
public function __construct()
{
register_shutdown_function([$this, 'clean']);
}
public function __destruct()
{
$this->clean();
}
public function __sleep(): array
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup(): void
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
public function observe(string $path): void
{
$this->files[$path] = true;
}
public function delete(string $path): void
{
if (isset($this->files[$path])) {
unset($this->files[$path]);
}
$this->unlink($path);
}
public function clean(): void
{
foreach ($this->files as $file => $value) {
$this->unlink($file);
}
$this->files = [];
}
private function unlink(string $path): void
{
@unlink($path);
}
}
<?php
declare(strict_types=1);
namespace PhpCsFixer;
interface PharCheckerInterface
{
public function checkFileValidity(string $filename): ?string;
}
d<D2>+ <0A><><EC><A2>*<2A><>'vj<76><6A>V<DA>x<9E>GBMB