public function setCommand($path, $value, $json = false)
    {
        if (!$this->configurationService->localIsActive($path)) {
            $this->outputLine('<warning>It seems that configuration for path "%s" is overridden.</warning>', [$path]);
            $this->outputLine('<warning>Writing the new value might have no effect.</warning>');
        }

        $encodedValue = $value;
        if ($json) {
            $encodedValue = @json_decode($value, true);
        }

        if ($encodedValue === null && strtolower($value) !== 'null') {
            $this->outputLine('<error>Could not decode value "%s" as json.</error>', [$value]);
            $this->quit(2);
        }

        $setWasAllowed = $this->configurationService->setLocal($path, $encodedValue);
        $isApplied = $this->configurationService->hasLocal($path);

        if (!$setWasAllowed) {
            $this->outputLine('<warning>Could not set value "%s" for configuration path "%s".</warning>', [$value, $path]);
            $this->outputLine('<warning>Possible reasons: configuration path is not allowed, configuration is not writable or type of value does not match given type.</warning>', [$value, $path]);
            $this->quit(1);
        }
        if ($isApplied) {
            $this->outputLine('<info>Successfully set value for path "%s".</info>', [$path]);
        } else {
            $this->outputLine('<warning>Value "%s" for configuration path "%s" seems not applied.</warning>', [$value, $path]);
            $this->outputLine('<warning>Possible reasons: changed value in AdditionalConfiguration.php or extension ext_localconf.php</warning>');
        }
    }

Set configuration value

Set system configuration option value by path.

Examples:
%command.full_name% SYS/fileCreateMask 0664
%command.full_name% EXTCONF/processor_enabled true --json
%command.full_name% EXTCONF/lang/availableLanguages '["de", "fr"]' --json

@param string $path Path to system configuration
@param string $value Value for system configuration
@param bool $json Treat value as JSON (also makes it possible to force datatypes for value)


public function afterInstallation($keyOfInstalledExtension)
    {
        if (self::EXTKEY !== $keyOfInstalledExtension) {
            return;
        }
        $scriptName = $this->isWindowsOs() ? 'Scripts/' . Application::COMMAND_NAME . '.bat' : Application::COMMAND_NAME;
        $success = $this->safeCopy(PATH_site . self::BINARY_PATH . $scriptName, PATH_site . basename($scriptName));
        if (!$success) {
            self::addFlashMessage(sprintf(self::COPY_FAILED_MESSAGE, $scriptName), sprintf(self::COPY_FAILED_MESSAGE_TITLE, $scriptName, PATH_site), AbstractMessage::WARNING);
        } else {
            self::addFlashMessage(sprintf(self::COPY_SUCCESS_MESSAGE, $scriptName));
        }
    }

public function afterInstallation ( $ keyOfInstalledExtension ) { if ( self :: EXTKEY !== $ keyOfInstalledExtension ) { return ; } $ scriptName = $ this -> isWindowsOs ( ) ? 'Scripts/' . Application :: COMMAND_NAME . '.bat' : Application :: COMMAND_NAME ; $ success = $ this -> safeCopy ( PATH_site . self :: BINARY_PATH . $ scriptName , PATH_site . basename ( $ scriptName ) ) ; if ( ! $ success ) { self :: addFlashMessage ( sprintf ( self :: COPY_FAILED_MESSAGE , $ scriptName ) , sprintf ( self :: COPY_FAILED_MESSAGE_TITLE , $ scriptName , PATH_site ) , AbstractMessage :: WARNING ) ; } else { self :: addFlashMessage ( sprintf ( self :: COPY_SUCCESS_MESSAGE , $ scriptName ) ) ; } }

Actions to take after extension has been installed

@param string $keyOfInstalledExtension


protected function addFlashMessage($messageBody, $messageTitle = '', $severity = AbstractMessage::OK, $storeInSession = true)
    {
        if (PHP_SAPI === 'cli') {
            return;
        }
        if (!is_string($messageBody)) {
            throw new \InvalidArgumentException('The message body must be of type string, "' . gettype($messageBody) . '" given.', 1418250286);
        }
        $flashMessage = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Messaging\FlashMessage::class, $messageBody, $messageTitle, $severity, $storeInSession);
        $queue = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Messaging\FlashMessageQueue::class, self::EM_FLASH_MESSAGE_QUEUE_ID);
        $queue->enqueue($flashMessage);
    }

Creates a Message object and adds it to the FlashMessageQueue.

@param string $messageBody The message
@param string $messageTitle Optional message title
@param int $severity Optional severity, must be one of \TYPO3\CMS\Core\Messaging\FlashMessage constants
@param bool $storeInSession Optional, defines whether the message should be stored in the session (default) or not
@throws \TYPO3\CMS\Core\Exception
@throws \InvalidArgumentException if the message body is no string
@return void

Creates a Message object and adds it to the FlashMessageQueue .


private function safeCopy($fullSourcePath, $fullTargetPath, $relativeWebDir = '')
    {
        if (file_exists($fullTargetPath)) {
            if (!is_file($fullTargetPath)) {
                // Seems to be a directory: ignore
                return false;
            }
            if (!self::isTypo3CmsBinary($fullTargetPath)) {
                // File is there but does not seem to be a previous version of our script: better ignore
                return false;
            }
        }
        if ($this->isWindowsOs()) {
            $success = @copy($fullSourcePath, $fullTargetPath);
        } else {
            $proxyFileContent = file_get_contents($fullSourcePath);
            $proxyFileContent = str_replace(
                'require __DIR__ . \'/Scripts/typo3-console.php\';',
                '// In non Composer mode we\'re copied into TYPO3 web root
require __DIR__ . \'/typo3conf/ext/typo3_console/Scripts/typo3-console.php\';',
                $proxyFileContent
            );
            $success = file_put_contents($fullTargetPath, $proxyFileContent);
        }

        if ($success && !$this->isWindowsOs()) {
            $success = @chmod($fullTargetPath, 0755);
        }
        if ($success) {
            $success = @file_put_contents(
                $fullTargetPath,
                str_replace(
                    '{$relative-web-dir}',
                    $relativeWebDir,
                    file_get_contents($fullTargetPath)
                )
            );
        }

        return $success;
    }

Copy typo3 console binary to root directory taking several possible situations into account

@param string $fullSourcePath Path to the script that should be copied (depending on OS)
@param string $fullTargetPath Target path to which the script should be copied to
@param string $relativeWebDir Relative path to the web directory (which equals the TYPO3 root directory currently)
@return bool
@internal


public function generateXsdCommand($phpNamespace, $xsdNamespace = null, $targetFile = null)
    {
        if ($xsdNamespace === null) {
            $phpNamespace = rtrim($phpNamespace, '_\\');
            if (strpos($phpNamespace, '\\') === false) {
                $search = ['Tx_', '_'];
                $replace = ['', '/'];
            } else {
                $search = '\\';
                $replace = '/';
            }
            $xsdNamespace = sprintf('http://typo3.org/ns/%s', str_replace($search, $replace, $phpNamespace));
        }
        $xsdSchema = '';
        try {
            $xsdSchema = $this->xsdGenerator->generateXsd($phpNamespace, $xsdNamespace);
        } catch (Service\Exception $exception) {
            $this->outputLine('An error occurred while trying to generate the XSD schema:');
            $this->outputLine('%s', [$exception->getMessage()]);
            $this->quit(1);
        }
        if ($targetFile === null) {
            echo $xsdSchema;
        } else {
            file_put_contents($targetFile, $xsdSchema);
        }
    }

Generate Fluid ViewHelper XSD Schema

Generates Schema documentation (XSD) for your ViewHelpers, preparing the
file to be placed online and used by any XSD-aware editor.
After creating the XSD file, reference it in your IDE and import the namespace
in your Fluid template by adding the xmlns:* attribute(s):

@param string $phpNamespace Namespace of the Fluid ViewHelpers without leading backslash (for example 'TYPO3\Fluid\ViewHelpers' or 'Tx_News_ViewHelpers'). NOTE: Quote and/or escape this argument as needed to avoid backslashes from being interpreted!
@param string $xsdNamespace Unique target namespace used in the XSD schema (for example "http://yourdomain.org/ns/viewhelpers"). Defaults to "http://typo3.org/ns/<php namespace>".
@param string $targetFile File path and name of the generated XSD schema. If not specified the schema will be output to standard output.
@return void


private static function initializePackageManagement(Bootstrap $bootstrap)
    {
        $packageManager = CompatibilityScripts::createPackageManager();
        $bootstrap->setEarlyInstance(PackageManager::class, $packageManager);
        GeneralUtility::setSingletonInstance(PackageManager::class, $packageManager);
        ExtensionManagementUtility::setPackageManager($packageManager);
        $packageManager->init();
    }

private static function initializePackageManagement ( Bootstrap $ bootstrap ) { $ packageManager = CompatibilityScripts :: createPackageManager ( ) ; $ bootstrap -> setEarlyInstance ( PackageManager :: class , $ packageManager ) ; GeneralUtility :: setSingletonInstance ( PackageManager :: class , $ packageManager ) ; ExtensionManagementUtility :: setPackageManager ( $ packageManager ) ; $ packageManager -> init ( ) ; }

Initializes the package system and loads the package configuration and settings
provided by the packages.

@return void

Initializes the package system and loads the package configuration and settings provided by the packages .


public static function overrideImplementation($originalClassName, $overrideClassName)
    {
        self::registerImplementation($originalClassName, $overrideClassName);
        $GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][$originalClassName]['className'] = $overrideClassName;
        class_alias($overrideClassName, $originalClassName);
    }

public static function overrideImplementation ( $ originalClassName , $ overrideClassName ) { self :: registerImplementation ( $ originalClassName , $ overrideClassName ) ; $ GLOBALS [ 'TYPO3_CONF_VARS' ] [ 'SYS' ] [ 'Objects' ] [ $ originalClassName ] [ 'className' ] = $ overrideClassName ; class_alias ( $ overrideClassName , $ originalClassName ) ; }

Tell Extbase, TYPO3 and PHP that we have another implementation

@param string $originalClassName
@param string $overrideClassName

Tell Extbase TYPO3 and PHP that we have another implementation


private static function registerImplementation($className, $alternativeClassName)
    {
        /** @var $extbaseObjectContainer \TYPO3\CMS\Extbase\Object\Container\Container */
        $extbaseObjectContainer = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\Container\Container::class);
        $extbaseObjectContainer->registerImplementation($className, $alternativeClassName);
    }

Tell Extbase about this implementation

@param string $className
@param string $alternativeClassName


public function setLocal($path, $value, $targetType = '')
    {
        try {
            $value = $this->convertToTargetType($path, $value, $targetType);

            return $this->configurationManager->setLocalConfigurationValueByPath($path, $value);
        } catch (TypesAreNotConvertibleException $e) {
            return false;
        }
    }

public function setLocal ( $ path , $ value , $ targetType = '' ) { try { $ value = $ this -> convertToTargetType ( $ path , $ value , $ targetType ) ; return $ this -> configurationManager -> setLocalConfigurationValueByPath ( $ path , $ value ) ; } catch ( TypesAreNotConvertibleException $ e ) { return false ; } }

Sets a value in LocalConfiguration.php

But only if types are compatible and local config is active

@param string $path
@param mixed $value
@param string $targetType
@return bool

Sets a value in LocalConfiguration . php


public function convertToTargetType($path, $value, $targetType = '')
    {
        $targetType = $targetType ?: $this->getType($path);
        $actualType = gettype($value);
        if ($actualType !== $targetType && $targetType !== 'NULL') {
            if ($this->isTypeConvertible($targetType, $actualType)) {
                switch ($targetType) {
                    case 'integer':
                        $value = (int)$value;
                        break;
                    case 'float':
                    case 'double':
                        $value = (float)$value;
                        break;
                    case 'boolean':
                        $value = (bool)$value;
                        break;
                    case 'string':
                        $value = (string)$value;
                        break;
                    default:
                        // We don't know any type conversion, so we better exit
                        throw new TypesAreNotConvertibleException(sprintf('Unknown target type "%s"', $targetType), 1477778705);
                }
            } else {
                // We cannot convert from or to non scalar types, so we better exit
                throw new TypesAreNotConvertibleException(sprintf('Cannot convert type from "%s" to "%s"', $actualType, $targetType), 1477778754);
            }
        }

        return $value;
    }

Convert a value to the type belonging to the given path

@param string $path
@param string $value
@param string $targetType
@throws TypesAreNotConvertibleException
@return bool|float|int|string


private function getType($path)
    {
        $value = null;
        if ($this->hasActive($path)) {
            $value = $this->getActive($path);
        }
        if ($this->hasLocal($path)) {
            $value = $this->getLocal($path);
        }
        if ($this->hasDefault($path)) {
            $value = $this->getDefault($path);
        }

        return gettype($value);
    }

private function getType ( $ path ) { $ value = null ; if ( $ this -> hasActive ( $ path ) ) { $ value = $ this -> getActive ( $ path ) ; } if ( $ this -> hasLocal ( $ path ) ) { $ value = $ this -> getLocal ( $ path ) ; } if ( $ this -> hasDefault ( $ path ) ) { $ value = $ this -> getDefault ( $ path ) ; } return gettype ( $ value ) ; }

Returns the type of a value in given config path

@param string $path
@return string


private function isTypeConvertible($targetType, $actualType)
    {
        if (in_array($targetType, ['array', 'object', 'resource'], true)) {
            return false;
        }
        if (in_array($actualType, ['array', 'object', 'resource'], true)) {
            return false;
        }

        return true;
    }

private function isTypeConvertible ( $ targetType , $ actualType ) { if ( in_array ( $ targetType , [ 'array' , 'object' , 'resource' ] , true ) ) { return false ; } if ( in_array ( $ actualType , [ 'array' , 'object' , 'resource' ] , true ) ) { return false ; } return true ; }

Checks if target and actual type is scalar

@param string $targetType
@param string $actualType
@return bool


public function isCommandAvailable(Command $command): bool
    {
        if (!$this->isFullyCapable()
            && in_array($command->getName(), [
                // Although these commands are technically available
                // they call other hidden commands in sub processes
                // that need all capabilities. Therefore we disable these commands here.
                // This can be removed, once they implement Symfony commands directly.
                'upgrade:all',
                'upgrade:list',
                'upgrade:wizard',
            ], true)
        ) {
            return false;
        }
        if ($command->getName() === 'cache:flushcomplete') {
            return true;
        }

        return $this->runLevel->isCommandAvailable($command->getName());
    }

Checks if the given command can be executed in current application state

@param Command $command
@return bool


protected function configure()
    {
        $this->setDescription($this->commandDefinition->getShortDescription());
        $this->setHelp($this->commandDefinition->getDescription());
        $strict = $this->commandDefinition->shouldValidateInputStrict();
        if (!$strict) {
            $this->ignoreValidationErrors();
        }
        $this->setDefinition($this->commandDefinition->getInputDefinitions());
    }

protected function configure ( ) { $ this -> setDescription ( $ this -> commandDefinition -> getShortDescription ( ) ) ; $ this -> setHelp ( $ this -> commandDefinition -> getDescription ( ) ) ; $ strict = $ this -> commandDefinition -> shouldValidateInputStrict ( ) ; if ( ! $ strict ) { $ this -> ignoreValidationErrors ( ) ; } $ this -> setDefinition ( $ this -> commandDefinition -> getInputDefinitions ( ) ) ; }

Extbase has its own validation logic, so it is disabled in this place

Extbase has its own validation logic so it is disabled in this place


protected function execute(InputInterface $input, OutputInterface $output)
    {
        // @deprecated in 5.0 will be removed in 6.0
        $givenCommandName = $input->getArgument('command');
        $debugOutput = $output;
        if ($output instanceof ConsoleOutput) {
            $debugOutput = $output->getErrorOutput();
        }
        if ($givenCommandName === $this->getAliases()[0]) {
            $debugOutput->writeln('<warning>Specifying the full command name is deprecated.</warning>');
            $debugOutput->writeln(sprintf('<warning>Please use "%s" as command name instead.</warning>', $this->getName()));
        }
        if ($this->isLateCommand && $output->isVerbose()) {
            $debugOutput->writeln('<warning>Registering commands via $GLOBALS[\'TYPO3_CONF_VARS\'][\'SC_OPTIONS\'][\'extbase\'][\'commandControllers\'] is deprecated and will be removed with 6.0. Register Symfony commands in Configuration/Commands.php instead.</warning>');
        }
        $response = (new RequestHandler())->handle($this->commandDefinition, $input, $output);

        return $response->getExitCode();
    }

Executes the command to find any Extbase command

@param InputInterface $input
@param OutputInterface $output
@return null|int null or 0 if everything went fine, or an error code


private function ensureRequiredEnvironment()
    {
        if (!in_array(PHP_SAPI, ['cli', 'phpdbg'], true) || !isset($_SERVER['argc'], $_SERVER['argv'])) {
            echo 'The command line must be executed with a cli PHP binary! The current PHP sapi type is "' . PHP_SAPI . '".' . PHP_EOL;
            exit(1);
        }
        if (ini_get('memory_limit') !== '-1') {
            @ini_set('memory_limit', '-1');
        }
        if (ini_get('max_execution_time') !== '0') {
            @ini_set('max_execution_time', '0');
        }
    }

Checks PHP sapi type and sets required PHP options


public static function initializeCompatibilityLayer(ClassLoader $classLoader)
    {
        $typo3Branch = '95';
        if (method_exists(Bootstrap::class, 'setCacheHashOptions')) {
            $typo3Branch = '87';
        }
        if ($typo3Branch === '95') {
            return;
        }
        $classLoader = self::$nonComposerCompatClassLoader ?? $classLoader;
        $compatibilityNamespace = 'Helhum\\Typo3Console\\TYPO3v' . $typo3Branch . '\\';
        spl_autoload_register(function ($className) use ($classLoader, $compatibilityNamespace) {
            if (strpos($className, 'Helhum\\Typo3Console\\') !== 0) {
                // We don't care about classes that are not within our namespace
                return;
            }
            $compatibilityClassName = str_replace('Helhum\\Typo3Console\\', $compatibilityNamespace, $className);
            if ($file = $classLoader->findFile($compatibilityClassName)) {
                require $file;
                class_alias($compatibilityClassName, $className);
            }
        }, true, true);
    }

If detected TYPO3 version does not match the main supported version,
overlay compatibility classes for the detected branch, by registering
an autoloader and aliasing the compatibility class with the original class name.

@param ClassLoader $classLoader
@internal

If detected TYPO3 version does not match the main supported version overlay compatibility classes for the detected branch by registering an autoloader and aliasing the compatibility class with the original class name .


public function handle(InputInterface $input): int
    {
        $this->initialize();

        $commandCollection = new CommandCollection(
            $this->runLevel,
            new CommandConfiguration(GeneralUtility::makeInstance(PackageManager::class))
        );

        $application = new Application($this->runLevel, CompatibilityScripts::isComposerMode());
        $application->setCommandLoader($commandCollection);

        // Try to resolve short command names and aliases
        $givenCommandName = $input->getFirstArgument() ?: 'list';
        $commandNameCandidate = $commandCollection->find($givenCommandName);
        if ($this->runLevel->isCommandAvailable($commandNameCandidate)) {
            $this->runLevel->runSequenceForCommand($commandNameCandidate);
            // @deprecated will be removed once command controller support is removed
            if ($this->runLevel->getRunLevelForCommand($commandNameCandidate) !== RunLevel::LEVEL_ESSENTIAL) {
                $commandCollection->addCommandControllerCommands($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['extbase']['commandControllers'] ?? []);
                $commandName = $commandCollection->find($givenCommandName);
                if ($commandNameCandidate !== $commandName) {
                    // Mitigate #779 and #778 at least when command controller commands conflict with non low level
                    // previously registered commands
                    $this->runLevel->runSequenceForCommand($commandName);
                }
            }
        }

        return $application->run($input);
    }

Handle the given command input and return the exit code of the called command

@param InputInterface $input
@throws Exception
@throws InvalidArgumentException
@return int


public function render(\Throwable $exception, OutputInterface $output, Application $application = null)
    {
        if (getenv('TYPO3_CONSOLE_SUB_PROCESS')) {
            $output->write(\json_encode($this->serializeException($exception)), false, OutputInterface::VERBOSITY_QUIET);

            return;
        }
        $output->writeln('', OutputInterface::VERBOSITY_QUIET);
        do {
            $this->outputException($exception, $output);
            if ($output->isVerbose()) {
                $this->outputCode($exception, $output);
                $this->outputCommand($exception, $output);
                $this->outputTrace($exception, $output);
                $output->writeln('');
            }
            $exception = $exception->getPrevious();
            if ($exception) {
                $output->writeln('<comment>Caused by:</comment>', OutputInterface::VERBOSITY_QUIET);
            }
        } while ($exception);

        $this->outputSynopsis($output, $application);
    }

public
function
render
(
\
Throwable
$
exception
,
OutputInterface
$
output
,
Application
$
application
=
)
{
if
(
getenv
(
'TYPO3_CONSOLE_SUB_PROCESS'
)
)
{
$
output
->
write
(
\
json_encode
(
$
this
->
serializeException
(
$
exception
)
)
,
false
,
OutputInterface
::
VERBOSITY_QUIET
)
;
return
;
}
$
output
->
writeln
(
''
,
OutputInterface
::
VERBOSITY_QUIET
)
;
do
{
$
this
->
outputException
(
$
exception
,
$
output
)
;
if
(
$
output
->
isVerbose
(
)
)
{
$
this
->
outputCode
(
$
exception
,
$
output
)
;
$
this
->
outputCommand
(
$
exception
,
$
output
)
;
$
this
->
outputTrace
(
$
exception
,
$
output
)
;
$
output
->
writeln
(
''
)
;
}
$
exception
=
$
exception
->
getPrevious
(
)
;
if
(
$
exception
)
{
$
output
->
writeln
(
'<comment>Caused by:</comment>'
,
OutputInterface
::
VERBOSITY_QUIET
)
;
}
}
while
(
$
exception
)
;
$
this
->
outputSynopsis
(
$
output
,
$
application
)
;
}

Renders Exception with trace and nested exceptions with trace.

@param \Throwable $exception
@param OutputInterface $output
@param Application|null $application

Renders Exception with trace and nested exceptions with trace .


private function outputException(\Throwable $exception, OutputInterface $output)
    {
        $exceptionClass = get_class($exception);
        if ($exception instanceof SubProcessException) {
            $exceptionClass = $exception->getPreviousExceptionClass();
        }

        $title = sprintf('[ %s ]', $exceptionClass);

        $messageLength = Helper::strlen($title);
        $maxWidth = $this->terminal->getWidth() ? $this->terminal->getWidth() - 1 : PHP_INT_MAX;

        $lines = [];
        foreach (preg_split('/\r?\n/', trim($exception->getMessage())) as $line) {
            foreach ($this->splitStringByWidth($line, $maxWidth - 4) as $splitLine) {
                $lines[] = $splitLine;
                $messageLength = max(Helper::strlen($splitLine), $messageLength);
            }
        }

        $messages = [];
        $messages[] = $emptyLine = $this->padMessage('', $messageLength);
        $messages[] = $this->padMessage($title, $messageLength);
        foreach ($lines as $line) {
            $messages[] = $this->padMessage(OutputFormatter::escape($line), $messageLength);
        }
        $messages[] = $emptyLine;
        $messages[] = '';
        $output->writeln($messages, OutputInterface::VERBOSITY_QUIET);
    }

private
function
outputException
(
\
Throwable
$
exception
,
OutputInterface
$
output
)
{
$
exceptionClass
=
get_class
(
$
exception
)
;
if
(
$
exception
instanceof
SubProcessException
)
{
$
exceptionClass
=
$
exception
->
getPreviousExceptionClass
(
)
;
}
$
title
=
sprintf
(
'[ %s ]'
,
$
exceptionClass
)
;
$
messageLength
=
Helper
::
strlen
(
$
title
)
;
$
maxWidth
=
$
this
->
terminal
->
getWidth
(
)
$
this
->
terminal
->
getWidth
(
)
1
:
PHP_INT_MAX
;
$
lines
=
[
]
;
foreach
(
preg_split
(
'/\r?\n/'
,
trim
(
$
exception
->
getMessage
(
)
)
)
as
$
line
)
{
foreach
(
$
this
->
splitStringByWidth
(
$
line
,
$
maxWidth
4
)
as
$
splitLine
)
{
$
lines
[
]
=
$
splitLine
;
$
messageLength
=
max
(
Helper
::
strlen
(
$
splitLine
)
,
$
messageLength
)
;
}
}
$
messages
=
[
]
;
$
messages
[
]
=
$
emptyLine
=
$
this
->
padMessage
(
''
,
$
messageLength
)
;
$
messages
[
]
=
$
this
->
padMessage
(
$
title
,
$
messageLength
)
;
foreach
(
$
lines
as
$
line
)
{
$
messages
[
]
=
$
this
->
padMessage
(
OutputFormatter
::
escape
(
$
line
)
,
$
messageLength
)
;
}
$
messages
[
]
=
$
emptyLine
;
$
messages
[
]
=
''
;
$
output
->
writeln
(
$
messages
,
OutputInterface
::
VERBOSITY_QUIET
)
;
}

Output formatted exception.

@param \Throwable $exception
@param OutputInterface $output


private function outputTrace(\Throwable $exception, OutputInterface $output)
    {
        $output->writeln('<comment>Exception trace:</comment>');
        $backtraceSteps = $this->getTrace($exception);
        foreach ($backtraceSteps as $index => $step) {
            $traceLine = '#' . $index . ' ';
            if (isset($backtraceSteps[$index]['class'])) {
                $traceLine .= $backtraceSteps[$index]['class'];
            }
            if (isset($backtraceSteps[$index]['function'])) {
                $traceLine .= (isset($backtraceSteps[$index]['class']) ? $backtraceSteps[$index]['type'] : '') . $backtraceSteps[$index]['function'] . '()';
            }
            $output->writeln(sprintf('<info>%s</info>', $traceLine));
            if (isset($backtraceSteps[$index]['file'])) {
                $output->writeln('   ' . $this->getPossibleShortenedFileName($backtraceSteps[$index]['file']) . (isset($backtraceSteps[$index]['line']) ? ':' . $backtraceSteps[$index]['line'] : ''));
            }
        }
    }

private
function
outputTrace
(
\
Throwable
$
exception
,
OutputInterface
$
output
)
{
$
output
->
writeln
(
'<comment>Exception trace:</comment>'
)
;
$
backtraceSteps
=
$
this
->
getTrace
(
$
exception
)
;
foreach
(
$
backtraceSteps
as
$
index
=>
$
step
)
{
$
traceLine
=
'#'
.
$
index
.
' '
;
if
(
isset
(
$
backtraceSteps
[
$
index
]
[
'class'
]
)
)
{
$
traceLine
.=
$
backtraceSteps
[
$
index
]
[
'class'
]
;
}
if
(
isset
(
$
backtraceSteps
[
$
index
]
[
'function'
]
)
)
{
$
traceLine
.=
(
isset
(
$
backtraceSteps
[
$
index
]
[
'class'
]
)
$
backtraceSteps
[
$
index
]
[
'type'
]
:
''
)
.
$
backtraceSteps
[
$
index
]
[
'function'
]
.
'()'
;
}
$
output
->
writeln
(
sprintf
(
'<info>%s</info>'
,
$
traceLine
)
)
;
if
(
isset
(
$
backtraceSteps
[
$
index
]
[
'file'
]
)
)
{
$
output
->
writeln
(
'   '
.
$
this
->
getPossibleShortenedFileName
(
$
backtraceSteps
[
$
index
]
[
'file'
]
)
.
(
isset
(
$
backtraceSteps
[
$
index
]
[
'line'
]
)
':'
.
$
backtraceSteps
[
$
index
]
[
'line'
]
:
''
)
)
;
}
}
}

Output formatted trace.

@param \Throwable $exception


private function getPossibleShortenedFileName($fileName): string
    {
        $pathPrefixes = [];
        if (getenv('TYPO3_PATH_COMPOSER_ROOT')) {
            $pathPrefixes = [getenv('TYPO3_PATH_COMPOSER_ROOT') . '/'];
        }
        $pathPrefixes[] = PATH_site;
        $fileName = str_replace($pathPrefixes, '', $fileName);
        $pathPosition = strpos($fileName, 'typo3conf/ext/');
        $pathAndFilename = ($pathPosition !== false) ? substr($fileName, $pathPosition) : $fileName;
        $pathPosition = strpos($pathAndFilename, 'typo3/sysext/');

        return ($pathPosition !== false) ? substr($pathAndFilename, $pathPosition) : $pathAndFilename;
    }

private function getPossibleShortenedFileName ( $ fileName ) : string { $ pathPrefixes = [ ] ; if ( getenv ( 'TYPO3_PATH_COMPOSER_ROOT' ) ) { $ pathPrefixes = [ getenv ( 'TYPO3_PATH_COMPOSER_ROOT' ) . '/' ] ; } $ pathPrefixes [ ] = PATH_site ; $ fileName = str_replace ( $ pathPrefixes , '' , $ fileName ) ; $ pathPosition = strpos ( $ fileName , 'typo3conf/ext/' ) ; $ pathAndFilename = ( $ pathPosition !== false ) ? substr ( $ fileName , $ pathPosition ) : $ fileName ; $ pathPosition = strpos ( $ pathAndFilename , 'typo3/sysext/' ) ; return ( $ pathPosition !== false ) ? substr ( $ pathAndFilename , $ pathPosition ) : $ pathAndFilename ; }

Shorten file name if inside extension or core extension.

@param string $fileName
@return string

Shorten file name if inside extension or core extension .


public function errorCommand(\TYPO3\CMS\Extbase\Mvc\Exception\CommandException $exception)
    {
        $this->outputLine('<error>%s</error>', [$exception->getMessage()]);
        if ($exception instanceof \TYPO3\CMS\Extbase\Mvc\Exception\AmbiguousCommandIdentifierException) {
            $this->outputLine('Please specify the complete command identifier. Matched commands:');
            foreach ($exception->getMatchingCommands() as $matchingCommand) {
                $this->outputLine('    %s', [$matchingCommand->getCommandIdentifier()]);
            }
        }
        $this->outputLine();
        $this->outputLine('See <info>list</info> for an overview of all available commands');
        $this->outputLine('or <info>help</info> <command> for a detailed description of the corresponding command.');
        $this->quit(1);
    }

Displays an error message

@internal
@param \TYPO3\CMS\Extbase\Mvc\Exception\CommandException $exception
@return void


public function importCommand($interactive = false, string $connection = 'Default')
    {
        $availableConnectionNames = $this->connectionConfiguration->getAvailableConnectionNames('mysql');
        if (empty($availableConnectionNames) || !in_array($connection, $availableConnectionNames, true)) {
            $this->output('<error>No suitable MySQL connection found for import.</error>');
            $this->quit(2);
        }

        $mysqlCommand = new MysqlCommand($this->connectionConfiguration->build($connection), [], $this->output->getSymfonyConsoleOutput());
        $exitCode = $mysqlCommand->mysql(
            $interactive ? [] : ['--skip-column-names'],
            STDIN,
            null,
            $interactive
        );
        $this->quit($exitCode);
    }

Import mysql queries from stdin

This means that this can not only be used to pass insert statements,
it but works as well to pass SELECT statements to it.
The mysql binary must be available in the path for this command to work.
This obviously only works when MySQL is used as DBMS.

Example (import): ssh remote.server '/path/to/typo3cms database:export' | %command.full_name%
Example (select): echo 'SELECT username from be_users WHERE admin=1;' | %command.full_name%
Example (interactive): %command.full_name% --interactive

@param bool $interactive Open an interactive mysql shell using the TYPO3 connection settings.
@param string $connection TYPO3 database connection name
@throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException


public function executeInSubProcess($command, array $arguments = [], array &$messages = [])
    {
        $messages = $this->ensureUpgradeIsPossible();

        return @unserialize($this->commandDispatcher->executeCommand('upgrade:subprocess', [$command, serialize($arguments)]));
    }

public function executeInSubProcess ( $ command , array $ arguments = [ ] , array & $ messages = [ ] ) { $ messages = $ this -> ensureUpgradeIsPossible ( ) ; return @ unserialize ( $ this -> commandDispatcher -> executeCommand ( 'upgrade:subprocess' , [ $ command , serialize ( $ arguments ) ] ) ) ; }

Execute the command in a sub process,
but execute some automated migration steps beforehand

@param string $command
@param array $arguments
@param array &$messages
@throws FailedSubProcessCommandException
@throws \UnexpectedValueException
@throws \RuntimeException
@return mixed

Execute the command in a sub process but execute some automated migration steps beforehand


public function renderSchemaResult(ConsoleOutput $output, SchemaUpdateResultRenderer $schemaUpdateResultRenderer = null)
    {
        if (!isset($this->results['renderSchemaResult'])) {
            return;
        }
        $result = reset($this->results['renderSchemaResult']);
        if ($result->hasPerformedUpdates()) {
            $schemaUpdateResultRenderer = $schemaUpdateResultRenderer ?: new SchemaUpdateResultRenderer();
            $output->outputLine('<info>The following database schema updates were performed:</info>');
            $schemaUpdateResultRenderer->render($result, $output, true);
        } else {
            $output->outputLine(
                '<info>No schema updates were performed for update types:%s</info>',
                [
                    PHP_EOL . '"' . implode('", "', SchemaUpdateType::expandSchemaUpdateTypes(['safe'])) . '"',
                ]
            );
        }
    }

Output schema result
@param ConsoleOutput $output


public function renderImportedStaticDataResult(ConsoleOutput $output)
    {
        if (!isset($this->results['renderImportedStaticDataResult'])) {
            return;
        }
        foreach ($this->results['renderImportedStaticDataResult'] as $pathToStaticSqlFile) {
            // Output content of $pathToStaticSqlFile at cli context
            $absolutePath = PATH_site . $pathToStaticSqlFile;
            if (file_exists($absolutePath)) {
                $output->outputFormatted('<info>Import content of file "%s" into database.</info>', [$pathToStaticSqlFile]);
                $output->outputFormatted(file_get_contents($absolutePath), [], 2);
            }
        }
    }

Output at cli context imported static database content.

@param ConsoleOutput $output

Output at cli context imported static database content .


public function updateSchema(array $schemaUpdateTypes, $dryRun = false)
    {
        $updateStatements = [
            SchemaUpdateType::GROUP_SAFE => $this->schemaUpdate->getSafeUpdates(),
            SchemaUpdateType::GROUP_DESTRUCTIVE => $this->schemaUpdate->getDestructiveUpdates(),
        ];

        $updateResult = new SchemaUpdateResult();

        foreach ($schemaUpdateTypes as $schemaUpdateType) {
            foreach ($schemaUpdateType->getStatementTypes() as $statementType => $statementGroup) {
                if (isset($updateStatements[$statementGroup][$statementType])) {
                    $statements = $updateStatements[$statementGroup][$statementType];
                    if (empty($statements)) {
                        continue;
                    }
                    if ($dryRun) {
                        $updateResult->addPerformedUpdates($schemaUpdateType, $statements);
                    } else {
                        $result = $this->schemaUpdate->migrate(
                            $statements,
                            // Generate a map of statements as keys and true as values
                            array_combine(array_keys($statements), array_fill(0, count($statements), true))
                        );
                        if (empty($result)) {
                            $updateResult->addPerformedUpdates($schemaUpdateType, $statements);
                        } else {
                            $updateResult->addErrors($schemaUpdateType, $result, $statements);
                        }
                    }
                }
            }
        }

        $this->emitDatabaseUpdateSignal($updateResult);

        return $updateResult;
    }

Perform necessary database schema migrations

@param SchemaUpdateType[] $schemaUpdateTypes List of permitted schema update types
@param bool $dryRun If true, the database operations are not performed
@return SchemaUpdateResult Result of the schema update


private function buildSequence(string $runLevel): Sequence
    {
        if (is_callable([$this, $runLevel])) {
            return $this->{$runLevel}($runLevel);
        }
        throw new InvalidArgumentException('Invalid run level "' . $runLevel . '"', 1402075492);
    }

private
function
buildSequence
(
string
$
runLevel
)
:
Sequence
{
if
(
is_callable
(
[
$
this
,
$
runLevel
]
)
)
{
return
$
this
->
{
$
runLevel
}
(
$
runLevel
)
;
}
throw
new
InvalidArgumentException
(
'Invalid run level "'
.
$
runLevel
.
'"'
,
1402075492
)
;
}

Builds the sequence for the given run level

@param string $runLevel
@throws InvalidArgumentException
@return Sequence


private function buildEssentialSequence(string $identifier): Sequence
    {
        $sequence = new Sequence($identifier);
        $this->addStep($sequence, 'helhum.typo3console:coreconfiguration');
        $this->addStep($sequence, 'helhum.typo3console:providecleanclassimplementations');
        $this->addStep($sequence, 'helhum.typo3console:disabledcaching');
        $this->addStep($sequence, 'helhum.typo3console:errorhandling');

        return $sequence;
    }

Essential steps for a minimal usable system

@param string $identifier
@return Sequence


private function buildBasicRuntimeSequence(string $identifier = self::LEVEL_MINIMAL): Sequence
    {
        $sequence = $this->buildEssentialSequence($identifier);

        $this->addStep($sequence, 'helhum.typo3console:extensionconfiguration');

        return $sequence;
    }

System with complete configuration, but no database

@param string $identifier
@return Sequence

System with complete configuration but no database


public function flushGroupsCommand(array $groups)
    {
        try {
            $this->cacheService->flushGroups($groups);
            $this->outputLine('Flushed all caches for group(s): "' . implode('","', $groups) . '".');
        } catch (NoSuchCacheGroupException $e) {
            $this->outputLine($e->getMessage());
            $this->quit(1);
        }
    }

public
function
flushGroupsCommand
(
array
$
groups
)
{
try
{
$
this
->
cacheService
->
flushGroups
(
$
groups
)
;
$
this
->
outputLine
(
'Flushed all caches for group(s): "'
.
implode
(
'","'
,
$
groups
)
.
'".'
)
;
}
catch
(
NoSuchCacheGroupException
$
e
)
{
$
this
->
outputLine
(
$
e
->
getMessage
(
)
)
;
$
this
->
quit
(
1
)
;
}
}

Flush all caches in specified groups

Flushes all caches in specified groups.
Valid group names are by default:

- lowlevel
- pages
- system

Example: %command.full_name% pages,all

@param array $groups An array of names (specified as comma separated values) of cache groups to flush


public function flushTagsCommand(array $tags, array $groups = null)
    {
        try {
            $this->cacheService->flushByTagsAndGroups($tags, $groups);
            if ($groups === null) {
                $this->outputLine('Flushed caches by tags "' . implode('","', $tags) . '".');
            } else {
                $this->outputLine('Flushed caches by tags "' . implode('","', $tags) . '" in groups: "' . implode('","', $groups) . '".');
            }
        } catch (NoSuchCacheGroupException $e) {
            $this->outputLine($e->getMessage());
            $this->quit(1);
        }
    }

public
function
flushTagsCommand
(
array
$
tags
,
array
$
groups
=
)
{
try
{
$
this
->
cacheService
->
flushByTagsAndGroups
(
$
tags
,
$
groups
)
;
if
(
$
groups
===
)
{
$
this
->
outputLine
(
'Flushed caches by tags "'
.
implode
(
'","'
,
$
tags
)
.
'".'
)
;
}
else
{
$
this
->
outputLine
(
'Flushed caches by tags "'
.
implode
(
'","'
,
$
tags
)
.
'" in groups: "'
.
implode
(
'","'
,
$
groups
)
.
'".'
)
;
}
}
catch
(
NoSuchCacheGroupException
$
e
)
{
$
this
->
outputLine
(
$
e
->
getMessage
(
)
)
;
$
this
->
quit
(
1
)
;
}
}

Flush cache by tags

Flushes caches by tags, optionally only caches in specified groups.

Example: %command.full_name% news_123 --groups pages,all

@param array $tags Array of tags (specified as comma separated values) to flush.
@param array $groups Optional array of groups (specified as comma separated values) for which to flush tags. If no group is specified, caches of all groups are flushed.


public function listGroupsCommand()
    {
        $groups = $this->cacheService->getValidCacheGroups();
        sort($groups);

        switch (count($groups)) {
            case 0:
                $this->outputLine('No cache groups are registered.');
                break;
            case 1:
                $this->outputLine('The following cache group is registered: "' . implode('", "', $groups) . '".');
                break;
            default:
                $this->outputLine('The following cache groups are registered: "' . implode('", "', $groups) . '".');
                break;
        }
    }

List cache groups

Lists all registered cache groups.


private function hydrate(): void
    {
        if (null !== $this->values) {
            return;
        }

        $values = \call_user_func_array($this->callable, $this->callableArgs);

        if (false === \is_array($values)) {
            throw new InvalidArgumentException(
                'Dictionary callable must return an array or an instance of ArrayAccess.'
            );
        }

        $this->values = $values;
    }


private function trace(): void
    {
        $this->collector->addDictionary(
            $this->dictionary->getName(),
            $this->dictionary->getKeys(),
            array_values($this->dictionary->getValues())
        );
    }

private function trace ( ) : void { $ this -> collector -> addDictionary ( $ this -> dictionary -> getName ( ) , $ this -> dictionary -> getKeys ( ) , array_values ( $ this -> dictionary -> getValues ( ) ) ) ; }


private function retrieveFilesNumber($path, $regex)
    {
        $files = new Finder();
        $files->in($path)->files()->depth(0)->name($regex);

        return iterator_count($files);
    }

private function retrieveFilesNumber ( $ path , $ regex ) { $ files = new Finder ( ) ; $ files -> in ( $ path ) -> files ( ) -> depth ( 0 ) -> name ( $ regex ) ; return iterator_count ( $ files ) ; }

Tree Iterator.

@param $path
@param $regex

@return int


public function toJSON($data, $options = 0)
    {
        // Because of PHP Version 5.3, we cannot use JSON_UNESCAPED_SLASHES option
        // Instead we would use the str_replace command for now.
        // TODO: Replace this code with return json_encode($this->toArray(), $options | 64); once we support PHP >= 5.4
        if (version_compare(phpversion(), '5.4.0', '>=') === true) {
            return json_encode($data, $options | 64);
        }
        return str_replace('\\/', '/', json_encode($data, $options));
    }

Returns object JSON representation required by PayPal.
The PayPal REST API requires the use of JSON_UNESCAPED_SLASHES.

Adapted from the official PayPal REST API PHP SDK.
(https://github.com/paypal/PayPal-PHP-SDK/blob/master/lib/PayPal/Common/PayPalModel.php)

@param int $options http://php.net/manual/en/json.constants.php
@return string

Returns object JSON representation required by PayPal . The PayPal REST API requires the use of JSON_UNESCAPED_SLASHES .


public function getDescription()
    {
        $id = $this->getTransactionId();
        $desc = parent::getDescription();

        if (empty($id)) {
            return $desc;
        } elseif (empty($desc)) {
            return $id;
        } else {
            return "$id : $desc";
        }
    }

public
function
getDescription
(
)
{
$
id
=
$
this
->
getTransactionId
(
)
;
$
desc
=
parent
::
getDescription
(
)
;
if
(
empty
(
$
id
)
)
{
return
$
desc
;
}
elseif
(
empty
(
$
desc
)
)
{
return
$
id
;
}
else
{
return
"$id : $desc"
;
}
}

Get transaction description.

The REST API does not currently have support for passing an invoice number
or transaction ID.

@return string


public function getToken($createIfNeeded = true)
    {
        if ($createIfNeeded && !$this->hasToken()) {
            $response = $this->createToken()->send();
            if ($response->isSuccessful()) {
                $data = $response->getData();
                if (isset($data['access_token'])) {
                    $this->setToken($data['access_token']);
                    $this->setTokenExpires(time() + $data['expires_in']);
                }
            }
        }

        return $this->getParameter('token');
    }

public function getToken ( $ createIfNeeded = true ) { if ( $ createIfNeeded && ! $ this -> hasToken ( ) ) { $ response = $ this -> createToken ( ) -> send ( ) ; if ( $ response -> isSuccessful ( ) ) { $ data = $ response -> getData ( ) ; if ( isset ( $ data [ 'access_token' ] ) ) { $ this -> setToken ( $ data [ 'access_token' ] ) ; $ this -> setTokenExpires ( time ( ) + $ data [ 'expires_in' ] ) ; } } } return $ this -> getParameter ( 'token' ) ; }

Get OAuth 2.0 access token.

@param bool $createIfNeeded [optional] - If there is not an active token present, should we create one?
@return string


public function hasToken()
    {
        $token = $this->getParameter('token');

        $expires = $this->getTokenExpires();
        if (!empty($expires) && !is_numeric($expires)) {
            $expires = strtotime($expires);
        }

        return !empty($token) && time() < $expires;
    }

public function hasToken ( ) { $ token = $ this -> getParameter ( 'token' ) ; $ expires = $ this -> getTokenExpires ( ) ; if ( ! empty ( $ expires ) && ! is_numeric ( $ expires ) ) { $ expires = strtotime ( $ expires ) ; } return ! empty ( $ token ) && time ( ) < $ expires ; }

Is there a bearer token and is it still valid?

@return bool


public function setStartTime($value)
    {
        if ($value instanceof \DateTime) {
            $value->setTimezone(new \DateTimeZone('UTC'));
            $value = $value->format('Y-m-d\TH:i:s\Z');
        }
        return $this->setParameter('startTime', $value);
    }

public function setStartTime ( $ value ) { if ( $ value instanceof \ DateTime ) { $ value -> setTimezone ( new \ DateTimeZone ( 'UTC' ) ) ; $ value = $ value -> format ( 'Y-m-d\TH:i:s\Z' ) ; } return $ this -> setParameter ( 'startTime' , $ value ) ; }

Set the request startTime

@param string|\DateTime $value
@return AbstractRestRequest provides a fluent interface.


public function setEndTime($value)
    {
        if ($value instanceof \DateTime) {
            $value->setTimezone(new \DateTimeZone('UTC'));
            $value = $value->format('Y-m-d\TH:i:s\Z');
        }
        return $this->setParameter('endTime', $value);
    }

public function setEndTime ( $ value ) { if ( $ value instanceof \ DateTime ) { $ value -> setTimezone ( new \ DateTimeZone ( 'UTC' ) ) ; $ value = $ value -> format ( 'Y-m-d\TH:i:s\Z' ) ; } return $ this -> setParameter ( 'endTime' , $ value ) ; }

Set the request endTime

@param string|\DateTime $value
@return AbstractRestRequest provides a fluent interface.


public function getAll($type) {
        $all = [];
        foreach ($this->issues as $version => $issues) {
            if (isset($issues[$type])) {
                foreach ($issues[$type] as $issue_name => $issue_value) {
                    if (is_int($issue_name)) {
                        /**
                         * If issue does not have a key (for example, function does not have a replacement), it sets key to that function.
                         * That because scanner looks for methods/functions/variables in keys, not in values.
                         * Exception: if issue with type `function_usage` does not have a key, it just adds with numeric key.
                         */
                        if ($type !== 'functions_usage')
                            $all[$issue_value] = array($issue_value, $version);
                        else
                            $all[] = array($issue_value, $version);
                    } else
                        $all[$issue_name] = array($issue_value, $version);
                }
            }
        }
        return $all;
    }

Get all issues of specific type.
@param $type
@return array


public function run()
    {
        try {
            $this->setTarget();
            $this->setMaxSize();
            $this->setExcludeList();
            $this->setSkipChecks();
            $this->setFileExtensions();

            $this->scanFiles();
            $this->printReport();
            $this->printMemoryUsage();
            if ($this->hasIssue)
                exit(1);
        } catch (Exception $e) {
            $this->exitWithError($e->getMessage(), 128);
        }
    }

public function run ( ) { try { $ this -> setTarget ( ) ; $ this -> setMaxSize ( ) ; $ this -> setExcludeList ( ) ; $ this -> setSkipChecks ( ) ; $ this -> setFileExtensions ( ) ; $ this -> scanFiles ( ) ; $ this -> printReport ( ) ; $ this -> printMemoryUsage ( ) ; if ( $ this -> hasIssue ) exit ( 1 ) ; } catch ( Exception $ e ) { $ this -> exitWithError ( $ e -> getMessage ( ) , 128 ) ; } }


public function normalizeAndTruncatePath($path, $maxLength) {
        $truncated = 1;
        $path_parts = explode('/', str_replace('\\', '/', $path));
        $total_parts = count($path_parts);

        while (strlen($path) > $maxLength) {
            if (($truncated + 1) === $total_parts) break;
            $part_to_modify = $total_parts - 1 - $truncated;
            $chars_to_truncate = min(strlen($path_parts[$part_to_modify]) - 1, strlen($path) - $maxLength);
            if ((strlen($path) - $maxLength + 2) < strlen($path_parts[$part_to_modify]))
                $chars_to_truncate += 2;

            $path_parts[$part_to_modify] = substr($path_parts[$part_to_modify], 0, -$chars_to_truncate).'..';
            $path = implode('/', $path_parts);
            $truncated++;
        }

        return $path;
    }

public function normalizeAndTruncatePath ( $ path , $ maxLength ) { $ truncated = 1 ; $ path_parts = explode ( '/' , str_replace ( '\\' , '/' , $ path ) ) ; $ total_parts = count ( $ path_parts ) ; while ( strlen ( $ path ) > $ maxLength ) { if ( ( $ truncated + 1 ) === $ total_parts ) break ; $ part_to_modify = $ total_parts - 1 - $ truncated ; $ chars_to_truncate = min ( strlen ( $ path_parts [ $ part_to_modify ] ) - 1 , strlen ( $ path ) - $ maxLength ) ; if ( ( strlen ( $ path ) - $ maxLength + 2 ) < strlen ( $ path_parts [ $ part_to_modify ] ) ) $ chars_to_truncate += 2 ; $ path_parts [ $ part_to_modify ] = substr ( $ path_parts [ $ part_to_modify ] , 0 , - $ chars_to_truncate ) . '..' ; $ path = implode ( '/' , $ path_parts ) ; $ truncated ++ ; } return $ path ; }

Simplifies path to fit in specific width
@param string $path
@param integer $maxLength
@return string


public function exitWithError($message, $code = 128)
    {
        fwrite(STDERR, TerminalInfo::colorize($message, TerminalInfo::RED_BACKGROUND).PHP_EOL);
        exit($code);
    }

public function exitWithError ( $ message , $ code = 128 ) { fwrite ( STDERR , TerminalInfo :: colorize ( $ message , TerminalInfo :: RED_BACKGROUND ) . PHP_EOL ) ; exit ( $ code ) ; }

Shows error and terminates script
@param string $message
@param int $code


public static function divideByComma(array $tokens) {
        $delimited = [];
        $comma = 0;
        foreach ($tokens as $token) {
            if ($token == ',') $comma++;
            else $delimited[$comma][] = $token;
        }
        return $delimited;
    }

public static function divideByComma ( array $ tokens ) { $ delimited = [ ] ; $ comma = 0 ; foreach ( $ tokens as $ token ) { if ( $ token == ',' ) $ comma ++ ; else $ delimited [ $ comma ] [ ] = $ token ; } return $ delimited ; }

Divide first level of tokens hierarchy by comma
@param array $tokens
@return array


public static function trimSpaces(array $tokens) {
        $trimmed = [];
        foreach ($tokens as $token) {
            if (is_array($token)) {
                if ($token[0] == T_WHITESPACE)
                    continue;
                else
                    $trimmed[] = self::trimSpaces($token);
            }
            else
                $trimmed[] = $token;
        }
        return $trimmed;
    }

public static function trimSpaces ( array $ tokens ) { $ trimmed = [ ] ; foreach ( $ tokens as $ token ) { if ( is_array ( $ token ) ) { if ( $ token [ 0 ] == T_WHITESPACE ) continue ; else $ trimmed [ ] = self :: trimSpaces ( $ token ) ; } else $ trimmed [ ] = $ token ; } return $ trimmed ; }

Removes all T_WHITESPACE tokens from tokens hierarchy
@param array $tokens
@return array


protected static function callFunctionUsageChecker($checker, $functionName, array $callTokens)
    {
        require_once dirname(dirname(__FILE__)).'/data/'.$checker.'.php';
        $checker = __NAMESPACE__ . '\\' . $checker;
        $result = $checker($callTokens, $functionName);
        return $result;
    }

protected static function callFunctionUsageChecker ( $ checker , $ functionName , array $ callTokens ) { require_once dirname ( dirname ( __FILE__ ) ) . '/data/' . $ checker . '.php' ; $ checker = __NAMESPACE__ . '\\' . $ checker ; $ result = $ checker ( $ callTokens , $ functionName ) ; return $ result ; }

Calls function-usage checker
@param string $checker
@param string $functionName
@param array $callTokens
@return boolean


public function add($version, $type, $text, $replacement, $file, $line) {
        if ($this->removablePath !== null && strncasecmp($file, $this->removablePath, strlen($this->removablePath)) === 0)
            $file = substr($file, strlen($this->removablePath));
        $this->records[$version][] = [$type, $text, $replacement, $file, $line];
    }

public function add ( $ version , $ type , $ text , $ replacement , $ file , $ line ) { if ( $ this -> removablePath !== null && strncasecmp ( $ file , $ this -> removablePath , strlen ( $ this -> removablePath ) ) === 0 ) $ file = substr ( $ file , strlen ( $ this -> removablePath ) ) ; $ this -> records [ $ version ] [ ] = [ $ type , $ text , $ replacement , $ file , $ line ] ; }

Adds issue to the report
@param string $version PHP version
@param string $type Issue type
@param string $text Issue description
@param string|null $replacement Possible replacement
@param string $file File in which issue present
@param integer $line Line of file
@return void


static public function isColorsCapable()
    {
        if (self::$colorsCapability === null) {
            if (!static::isUnixPlatform())
                self::$colorsCapability = false;
            else if (!static::isInteractive())
                self::$colorsCapability = false;
            else {
                $tput_presence = static::exec('which tput');
                if (strlen(trim($tput_presence[0])) === 0)
                    self::$colorsCapability = false;
                else {
                    $tput_colors = static::exec('tput colors');
                    self::$colorsCapability = (int)$tput_colors[0] > 0;
                }
            }
        }
        return self::$colorsCapability;
    }

static
public
function
isColorsCapable
(
)
{
if
(
self
::
$
colorsCapability
===
)
{
if
(
!
static
::
isUnixPlatform
(
)
)
self
::
$
colorsCapability
=
false
;
else
if
(
!
static
::
isInteractive
(
)
)
self
::
$
colorsCapability
=
false
;
else
{
$
tput_presence
=
static
::
exec
(
'which tput'
)
;
if
(
strlen
(
trim
(
$
tput_presence
[
0
]
)
)
===
0
)
self
::
$
colorsCapability
=
false
;
else
{
$
tput_colors
=
static
::
exec
(
'tput colors'
)
;
self
::
$
colorsCapability
=
(
int
)
$
tput_colors
[
0
]
>
0
;
}
}
}
return
self
::
$
colorsCapability
;
}

Checks that terminal supports colors
@return bool


static protected function getWindowsTerminalSize() {
        $output = self::exec('mode', $returnCode);
        if ($returnCode !== 0)
            return [25, 80];

        foreach ($output as $i => $line) {
            if (strpos($line, ' CON') !== false) {
                $sizes = [$output[$i + 2], $output[$i + 3]];
                break;
            }
        }
        if (!isset($sizes))
            return [25, 80];

        return array_map(function($val) { list(, $val) = explode(':', $val); return trim($val); }, $sizes);
    }

static
protected
function
getWindowsTerminalSize
(
)
{
$
output
=
self
::
exec
(
'mode'
,
$
returnCode
)
;
if
(
$
returnCode
!==
0
)
return
[
25
,
80
]
;
foreach
(
$
output
as
$
i
=>
$
line
)
{
if
(
strpos
(
$
line
,
' CON'
)
!==
false
)
{
$
sizes
=
[
$
output
[
$
i
+
2
]
,
$
output
[
$
i
+
3
]
]
;
break
;
}
}
if
(
!
isset
(
$
sizes
)
)
return
[
25
,
80
]
;
return
array_map
(
function
(
$
val
)
{
list
(
,
$
val
)
=
explode
(
':'
,
$
val
)
;
return
trim
(
$
val
)
;
}
,
$
sizes
)
;
}

Returns size of terminal on Windows
@return array


private function attemptAutoloadForIdentifier(Identifier $identifier) : ?string
    {
        if ($identifier->isClass()) {
            return $this->locateClassByName($identifier->getName());
        }

        if ($identifier->isFunction()) {
            return $this->locateFunctionByName($identifier->getName());
        }

        return null;
    }

private function attemptAutoloadForIdentifier ( Identifier $ identifier ) : ? string { if ( $ identifier -> isClass ( ) ) { return $ this -> locateClassByName ( $ identifier -> getName ( ) ) ; } if ( $ identifier -> isFunction ( ) ) { return $ this -> locateFunctionByName ( $ identifier -> getName ( ) ) ; } return null ; }

Attempts to locate the specified identifier.

@throws ReflectionException

Attempts to locate the specified identifier .


private function locateClassByName(string $className) : ?string
    {
        if (class_exists($className, false) || interface_exists($className, false) || trait_exists($className, false)) {
            $filename = (new ReflectionClass($className))->getFileName();

            if (! is_string($filename)) {
                return null;
            }

            return $filename;
        }

        self::$autoloadLocatedFile = null;
        self::$currentAstLocator   = $this->astLocator; // passing the locator on to the implicitly instantiated `self`
        $previousErrorHandler      = set_error_handler(static function () : void {
        });
        stream_wrapper_unregister('file');
        stream_wrapper_register('file', self::class);
        class_exists($className);
        stream_wrapper_restore('file');
        set_error_handler($previousErrorHandler);

        return self::$autoloadLocatedFile;
    }

Attempt to locate a class by name.

If class already exists, simply use internal reflection API to get the
filename and store it.

If class does not exist, we make an assumption that whatever autoloaders
that are registered will be loading a file. We then override the file://
protocol stream wrapper to "capture" the filename we expect the class to
be in, and then restore it. Note that class_exists will cause an error
that it cannot find the file, so we squelch the errors by overriding the
error handler temporarily.

@throws ReflectionException


private function locateFunctionByName(string $functionName) : ?string
    {
        if (! function_exists($functionName)) {
            return null;
        }

        $reflection         = new ReflectionFunction($functionName);
        $reflectionFileName = $reflection->getFileName();

        if (! is_string($reflectionFileName)) {
            return null;
        }

        return $reflectionFileName;
    }

private function locateFunctionByName ( string $ functionName ) : ? string { if ( ! function_exists ( $ functionName ) ) { return null ; } $ reflection = new ReflectionFunction ( $ functionName ) ; $ reflectionFileName = $ reflection -> getFileName ( ) ; if ( ! is_string ( $ reflectionFileName ) ) { return null ; } return $ reflectionFileName ; }

We can only load functions if they already exist, because PHP does not
have function autoloading. Therefore if it exists, we simply use the
internal reflection API to find the filename. If it doesn't we can do
nothing so throw an exception.

@throws ReflectionException

We can only load functions if they already exist because PHP does not have function autoloading . Therefore if it exists we simply use the internal reflection API to find the filename . If it doesn t we can do nothing so throw an exception .


public function stream_open($path, $mode, $options, &$opened_path) : bool
    {
        self::$autoloadLocatedFile = $path;

        return false;
    }

public function stream_open ( $ path , $ mode , $ options , & $ opened_path ) : bool { self :: $ autoloadLocatedFile = $ path ; return false ; }

Our wrapper simply records which file we tried to load and returns
boolean false indicating failure.

@see https://php.net/manual/en/class.streamwrapper.php
@see https://php.net/manual/en/streamwrapper.stream-open.php

@param string $path
@param string $mode
@param int    $options
@param string $opened_path

@phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint

Our wrapper simply records which file we tried to load and returns boolean false indicating failure .


public function url_stat($path, $flags)
    {
        stream_wrapper_restore('file');

        if ($flags & STREAM_URL_STAT_QUIET) {
            set_error_handler(static function () {
                // Use native error handler
                return false;
            });
            $result = @stat($path);
            restore_error_handler();
        } else {
            $result = stat($path);
        }

        stream_wrapper_unregister('file');
        stream_wrapper_register('file', self::class);

        return $result;
    }

public
function
url_stat
(
$
path
,
$
flags
)
{
stream_wrapper_restore
(
'file'
)
;
if
(
$
flags
&
STREAM_URL_STAT_QUIET
)
{
set_error_handler
(
static
function
(
)
{
// Use native error handler
return
false
;
}
)
;
$
result
=
@
stat
(
$
path
)
;
restore_error_handler
(
)
;
}
else
{
$
result
=
stat
(
$
path
)
;
}
stream_wrapper_unregister
(
'file'
)
;
stream_wrapper_register
(
'file'
,
self
::
class
)
;
return
$
result
;
}

url_stat is triggered by calls like "file_exists". The call to "file_exists" must not be overloaded.
This function restores the original "file" stream, issues a call to "stat" to get the real results,
and then re-registers the AutoloadSourceLocator stream wrapper.

@see https://php.net/manual/en/class.streamwrapper.php
@see https://php.net/manual/en/streamwrapper.url-stat.php

@param string $path
@param int    $flags

@return mixed[]|bool

@phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint

url_stat is triggered by calls like file_exists . The call to file_exists must not be overloaded . This function restores the original file stream issues a call to stat to get the real results and then re - registers the AutoloadSourceLocator stream wrapper .


private function compileConstFetch(Node\Expr\ConstFetch $constNode, CompilerContext $context)
    {
        $firstName = reset($constNode->name->parts);
        switch ($firstName) {
            case 'null':
                return null;
            case 'false':
                return false;
            case 'true':
                return true;
            default:
                if (! defined($firstName)) {
                    throw Exception\UnableToCompileNode::becauseOfNotFoundConstantReference($context, $constNode);
                }

                return constant($firstName);
        }
    }

private function compileConstFetch ( Node \ Expr \ ConstFetch $ constNode , CompilerContext $ context ) { $ firstName = reset ( $ constNode -> name -> parts ) ; switch ( $ firstName ) { case 'null' : return null ; case 'false' : return false ; case 'true' : return true ; default : if ( ! defined ( $ firstName ) ) { throw Exception \ UnableToCompileNode :: becauseOfNotFoundConstantReference ( $ context , $ constNode ) ; } return constant ( $ firstName ) ; } }

Compile constant expressions

@return bool|mixed|null

@throws Exception\UnableToCompileNode


private function compileClassConstFetch(Node\Expr\ClassConstFetch $node, CompilerContext $context)
    {
        /** @var Node\Identifier $node->name */
        $nodeName = $node->name->name;
        /** @var Node\Name $node->class */
        $className = $node->class->toString();

        if ($nodeName === 'class') {
            return $className;
        }

        /** @var ReflectionClass|null $classInfo */
        $classInfo = null;

        if ($className === 'self' || $className === 'static') {
            $classInfo = $this->getConstantDeclaringClass($nodeName, $context->getSelf());
        }

        if ($classInfo === null) {
            /** @var ReflectionClass $classInfo */
            $classInfo = $context->getReflector()->reflect($className);
        }

        $reflectionConstant = $classInfo->getReflectionConstant($nodeName);

        if (! $reflectionConstant instanceof ReflectionClassConstant) {
            throw Exception\UnableToCompileNode::becauseOfNotFoundClassConstantReference($context, $classInfo, $node);
        }

        return $this->__invoke(
            $reflectionConstant->getAst()->consts[$reflectionConstant->getPositionInAst()]->value,
            new CompilerContext($context->getReflector(), $classInfo)
        );
    }

Compile class constants

@return string|int|float|bool|mixed[]|null

@throws IdentifierNotFound
@throws Exception\UnableToCompileNode If a referenced constant could not be located on the expected referenced class.


private function computeReflections(string $filename) : array
    {
        $singleFileSourceLocator = new SingleFileSourceLocator($filename, $this->astLocator);
        $reflector               = new ClassReflector(new AggregateSourceLocator([$singleFileSourceLocator, $this->sourceLocator]));

        return array_merge(
            $singleFileSourceLocator->locateIdentifiersByType($reflector, new IdentifierType(IdentifierType::IDENTIFIER_CLASS)),
            $singleFileSourceLocator->locateIdentifiersByType($reflector, new IdentifierType(IdentifierType::IDENTIFIER_FUNCTION))
        );
    }

private function computeReflections ( string $ filename ) : array { $ singleFileSourceLocator = new SingleFileSourceLocator ( $ filename , $ this -> astLocator ) ; $ reflector = new ClassReflector ( new AggregateSourceLocator ( [ $ singleFileSourceLocator , $ this -> sourceLocator ] ) ) ; return array_merge ( $ singleFileSourceLocator -> locateIdentifiersByType ( $ reflector , new IdentifierType ( IdentifierType :: IDENTIFIER_CLASS ) ) , $ singleFileSourceLocator -> locateIdentifiersByType ( $ reflector , new IdentifierType ( IdentifierType :: IDENTIFIER_FUNCTION ) ) ) ; }

Find all class and function reflections in the specified file

@return Reflection[]

@throws ParseToAstFailure
@throws InvalidFileLocation


private function containsLine($reflection, int $lineNumber) : bool
    {
        if (! method_exists($reflection, 'getStartLine')) {
            throw new InvalidArgumentException('Reflection does not have getStartLine method');
        }

        if (! method_exists($reflection, 'getEndLine')) {
            throw new InvalidArgumentException('Reflection does not have getEndLine method');
        }

        return $lineNumber >= $reflection->getStartLine() && $lineNumber <= $reflection->getEndLine();
    }

Check to see if the line is within the boundaries of the reflection specified.

@param ReflectionMethod|ReflectionClass|ReflectionFunction|Reflection $reflection

@throws InvalidArgumentException

Check to see if the line is within the boundaries of the reflection specified .


public function getAllClasses() : array
    {
        /** @var ReflectionClass[] $allClasses */
        $allClasses = $this->sourceLocator->locateIdentifiersByType(
            $this,
            new IdentifierType(IdentifierType::IDENTIFIER_CLASS)
        );

        return $allClasses;
    }

Get all the classes available in the scope specified by the SourceLocator.

@return ReflectionClass[]

Get all the classes available in the scope specified by the SourceLocator .


public static function createFromName(string $className, string $propertyName) : self
    {
        return ReflectionClass::createFromName($className)->getProperty($propertyName);
    }

public static function createFromName ( string $ className , string $ propertyName ) : self { return ReflectionClass :: createFromName ( $ className ) -> getProperty ( $ propertyName ) ; }

Create a reflection of a class's property by its name

Create a reflection of a class s property by its name


public static function createFromInstance($instance, string $propertyName) : self
    {
        return ReflectionClass::createFromInstance($instance)->getProperty($propertyName);
    }

public static function createFromInstance ( $ instance , string $ propertyName ) : self { return ReflectionClass :: createFromInstance ( $ instance ) -> getProperty ( $ propertyName ) ; }

Create a reflection of an instance's property by its name

@param object $instance

@throws InvalidArgumentException
@throws ReflectionException
@throws IdentifierNotFound

Create a reflection of an instance s property by its name


public function getDocBlockTypeStrings() : array
    {
        $stringTypes = [];

        foreach ($this->getDocBlockTypes() as $type) {
            $stringTypes[] = (string) $type;
        }

        return $stringTypes;
    }

public function getDocBlockTypeStrings ( ) : array { $ stringTypes = [ ] ; foreach ( $ this -> getDocBlockTypes ( ) as $ type ) { $ stringTypes [ ] = ( string ) $ type ; } return $ stringTypes ; }

Get the DocBlock type hints as an array of strings.

@return string[]

Get the DocBlock type hints as an array of strings .


public function findReflectionsOfType(
        Reflector $reflector,
        LocatedSource $locatedSource,
        IdentifierType $identifierType
    ) : array {
        try {
            return $this->findReflectionsInTree->__invoke(
                $reflector,
                $this->parser->parse($locatedSource->getSource()),
                $identifierType,
                $locatedSource
            );
        } catch (Throwable $exception) {
            throw Exception\ParseToAstFailure::fromLocatedSource($locatedSource, $exception);
        }
    }

public function findReflectionsOfType ( Reflector $ reflector , LocatedSource $ locatedSource , IdentifierType $ identifierType ) : array { try { return $ this -> findReflectionsInTree -> __invoke ( $ reflector , $ this -> parser -> parse ( $ locatedSource -> getSource ( ) ) , $ identifierType , $ locatedSource ) ; } catch ( Throwable $ exception ) { throw Exception \ ParseToAstFailure :: fromLocatedSource ( $ locatedSource , $ exception ) ; } }

Get an array of reflections found in some code.

@return Reflection[]

@throws Exception\ParseToAstFailure

Get an array of reflections found in some code .


private function findInArray(array $reflections, Identifier $identifier) : Reflection
    {
        $identifierName = strtolower($identifier->getName());

        foreach ($reflections as $reflection) {
            if (strtolower($reflection->getName()) === $identifierName) {
                return $reflection;
            }
        }

        throw IdentifierNotFound::fromIdentifier($identifier);
    }

private function findInArray ( array $ reflections , Identifier $ identifier ) : Reflection { $ identifierName = strtolower ( $ identifier -> getName ( ) ) ; foreach ( $ reflections as $ reflection ) { if ( strtolower ( $ reflection -> getName ( ) ) === $ identifierName ) { return $ reflection ; } } throw IdentifierNotFound :: fromIdentifier ( $ identifier ) ; }

Given an array of Reflections, try to find the identifier.

@param Reflection[] $reflections

@throws IdentifierNotFound

Given an array of Reflections try to find the identifier .


public static function createFromClassNameAndMethod(
        string $className,
        string $methodName,
        string $parameterName
    ) : self {
        return ReflectionClass::createFromName($className)
            ->getMethod($methodName)
            ->getParameter($parameterName);
    }

public static function createFromClassNameAndMethod ( string $ className , string $ methodName , string $ parameterName ) : self { return ReflectionClass :: createFromName ( $ className ) -> getMethod ( $ methodName ) -> getParameter ( $ parameterName ) ; }

Create a reflection of a parameter using a class name

@throws OutOfBoundsException


public static function createFromClassInstanceAndMethod(
        $instance,
        string $methodName,
        string $parameterName
    ) : self {
        return ReflectionClass::createFromInstance($instance)
            ->getMethod($methodName)
            ->getParameter($parameterName);
    }

public static function createFromClassInstanceAndMethod ( $ instance , string $ methodName , string $ parameterName ) : self { return ReflectionClass :: createFromInstance ( $ instance ) -> getMethod ( $ methodName ) -> getParameter ( $ parameterName ) ; }

Create a reflection of a parameter using an instance

@param object $instance

@throws OutOfBoundsException


public static function createFromClosure(Closure $closure, string $parameterName) : ReflectionParameter
    {
        return ReflectionFunction::createFromClosure($closure)
            ->getParameter($parameterName);
    }

public static function createFromClosure ( Closure $ closure , string $ parameterName ) : ReflectionParameter { return ReflectionFunction :: createFromClosure ( $ closure ) -> getParameter ( $ parameterName ) ; }

Create a reflection of a parameter using a closure


public function allowsNull() : bool
    {
        if (! $this->hasType()) {
            return true;
        }

        if ($this->node->type instanceof NullableType) {
            return true;
        }

        if (! $this->isDefaultValueAvailable()) {
            return false;
        }

        return $this->getDefaultValue() === null;
    }

public function allowsNull ( ) : bool { if ( ! $ this -> hasType ( ) ) { return true ; } if ( $ this -> node -> type instanceof NullableType ) { return true ; } if ( ! $ this -> isDefaultValueAvailable ( ) ) { return false ; } return $ this -> getDefaultValue ( ) === null ; }

Does this method allow null for a parameter?


public function getType() : ?ReflectionType
    {
        $type = $this->node->type;

        if ($type === null) {
            return null;
        }

        if ($type instanceof NullableType) {
            $type = $type->type;
        }

        return ReflectionType::createFromTypeAndReflector((string) $type, $this->allowsNull(), $this->reflector);
    }

public function getType ( ) : ? ReflectionType { $ type = $ this -> node -> type ; if ( $ type === null ) { return null ; } if ( $ type instanceof NullableType ) { $ type = $ type -> type ; } return ReflectionType :: createFromTypeAndReflector ( ( string ) $ type , $ this -> allowsNull ( ) , $ this -> reflector ) ; }

Get the ReflectionType instance representing the type declaration for
this parameter

(note: this has nothing to do with DocBlocks).


public function setType(string $newParameterType) : void
    {
        $this->node->type = new Node\Name($newParameterType);
    }

public function setType ( string $ newParameterType ) : void { $ this -> node -> type = new Node \ Name ( $ newParameterType ) ; }


public function getAllFunctions() : array
    {
        /** @var ReflectionFunction[] $allFunctions */
        $allFunctions = $this->sourceLocator->locateIdentifiersByType(
            $this,
            new IdentifierType(IdentifierType::IDENTIFIER_FUNCTION)
        );

        return $allFunctions;
    }

Get all the functions available in the scope specified by the SourceLocator.

@return ReflectionFunction[]

Get all the functions available in the scope specified by the SourceLocator .


public static function createFromName(string $className, string $methodName) : self
    {
        return ReflectionClass::createFromName($className)->getMethod($methodName);
    }

public static function createFromName ( string $ className , string $ methodName ) : self { return ReflectionClass :: createFromName ( $ className ) -> getMethod ( $ methodName ) ; }

Create a reflection of a method by it's name using a named class

@throws IdentifierNotFound
@throws OutOfBoundsException

Create a reflection of a method by it s name using a named class


public static function createFromInstance($instance, string $methodName) : self
    {
        return ReflectionClass::createFromInstance($instance)->getMethod($methodName);
    }

public static function createFromInstance ( $ instance , string $ methodName ) : self { return ReflectionClass :: createFromInstance ( $ instance ) -> getMethod ( $ methodName ) ; }

Create a reflection of a method by it's name using an instance

@param object $instance

@throws InvalidArgumentException
@throws ReflectionException
@throws IdentifierNotFound
@throws OutOfBoundsException

Create a reflection of a method by it s name using an instance


public function getPrototype() : self
    {
        $currentClass = $this->getDeclaringClass();

        while ($currentClass) {
            foreach ($currentClass->getImmediateInterfaces() as $interface) {
                if ($interface->hasMethod($this->getName())) {
                    return $interface->getMethod($this->getName());
                }
            }

            $currentClass = $currentClass->getParentClass();

            if ($currentClass === null || ! $currentClass->hasMethod($this->getName())) {
                break;
            }

            $prototype = $currentClass->getMethod($this->getName())->findPrototype();

            if ($prototype !== null) {
                return $prototype;
            }
        }

        throw new Exception\MethodPrototypeNotFound(sprintf(
            'Method %s::%s does not have a prototype',
            $this->getDeclaringClass()->getName(),
            $this->getName()
        ));
    }

Find the prototype for this method, if it exists. If it does not exist
it will throw a MethodPrototypeNotFound exception.

@throws Exception\MethodPrototypeNotFound

Find the prototype for this method if it exists . If it does not exist it will throw a MethodPrototypeNotFound exception .


public function isConstructor() : bool
    {
        if (strtolower($this->getName()) === '__construct') {
            return true;
        }

        $declaringClass = $this->getDeclaringClass();
        if ($declaringClass->inNamespace()) {
            return false;
        }

        return strtolower($this->getName()) === strtolower($declaringClass->getShortName());
    }

public function isConstructor ( ) : bool { if ( strtolower ( $ this -> getName ( ) ) === '__construct' ) { return true ; } $ declaringClass = $ this -> getDeclaringClass ( ) ; if ( $ declaringClass -> inNamespace ( ) ) { return false ; } return strtolower ( $ this -> getName ( ) ) === strtolower ( $ declaringClass -> getShortName ( ) ) ; }


public static function export(?string $className) : string
    {
        if ($className === null) {
            throw new InvalidArgumentException('Class name must be provided');
        }

        return self::createFromName($className)->__toString();
    }

public
static
function
export
(
string
$
className
)
:
string
{
if
(
$
className
===
)
{
throw
new
InvalidArgumentException
(
'Class name must be provided'
)
;
}
return
self
::
createFromName
(
$
className
)
->
__toString
(
)
;
}

Create a reflection and return the string representation of a named class


public static function createFromNode(
        Reflector $reflector,
        ClassLikeNode $node,
        LocatedSource $locatedSource,
        ?NamespaceNode $namespace = null
    ) : self {
        $class = new self();

        $class->reflector     = $reflector;
        $class->locatedSource = $locatedSource;
        $class->node          = $node;

        if ($namespace !== null) {
            $class->declaringNamespace = $namespace;
        }

        return $class;
    }

public static function createFromNode ( Reflector $ reflector , ClassLikeNode $ node , LocatedSource $ locatedSource , ? NamespaceNode $ namespace = null ) : self { $ class = new self ( ) ; $ class -> reflector = $ reflector ; $ class -> locatedSource = $ locatedSource ; $ class -> node = $ node ; if ( $ namespace !== null ) { $ class -> declaringNamespace = $ namespace ; } return $ class ; }

Create from a Class Node.

@internal

@param ClassLikeNode      $node      Node has to be processed by the PhpParser\NodeVisitor\NameResolver
@param NamespaceNode|null $namespace optional - if omitted, we assume it is global namespaced class


private function getAllMethods() : array
    {
        return array_merge(
            [],
            array_map(
                function (ClassMethod $methodNode) : ReflectionMethod {
                    return ReflectionMethod::createFromNode(
                        $this->reflector,
                        $methodNode,
                        $this->declaringNamespace,
                        $this,
                        $this
                    );
                },
                $this->node->getMethods()
            ),
            ...array_map(
                function (ReflectionClass $trait) : array {
                    return array_map(function (ReflectionMethod $method) use ($trait) : ReflectionMethod {
                        /** @var ClassMethod $methodAst */
                        $methodAst = $method->getAst();

                        return ReflectionMethod::createFromNode(
                            $this->reflector,
                            $methodAst,
                            $this->declaringNamespace,
                            $trait,
                            $this
                        );
                    }, $trait->getMethods());
                },
                $this->getTraits()
            ),
            ...array_map(
                static function (ReflectionClass $ancestor) : array {
                    return $ancestor->getMethods();
                },
                array_values(array_merge(
                    array_filter([$this->getParentClass()]),
                    $this->getInterfaces()
                ))
            )
        );
    }

private
function
getAllMethods
(
)
:
array
{
return
array_merge
(
[
]
,
array_map
(
function
(
ClassMethod
$
methodNode
)
:
ReflectionMethod
{
return
ReflectionMethod
::
createFromNode
(
$
this
->
reflector
,
$
methodNode
,
$
this
->
declaringNamespace
,
$
this
,
$
this
)
;
}
,
$
this
->
node
->
getMethods
(
)
)
,
...
array_map
(
function
(
ReflectionClass
$
trait
)
:
array
{
return
array_map
(
function
(
ReflectionMethod
$
method
)
use
(
$
trait
)
:
ReflectionMethod
{
/** @var ClassMethod $methodAst */
$
methodAst
=
$
method
->
getAst
(
)
;
return
ReflectionMethod
::
createFromNode
(
$
this
->
reflector
,
$
methodAst
,
$
this
->
declaringNamespace
,
$
trait
,
$
this
)
;
}
,
$
trait
->
getMethods
(
)
)
;
}
,
$
this
->
getTraits
(
)
)
,
...
array_map
(
static
function
(
ReflectionClass
$
ancestor
)
:
array
{
return
$
ancestor
->
getMethods
(
)
;
}
,
array_values
(
array_merge
(
array_filter
(
[
$
this
->
getParentClass
(
)
]
)
,
$
this
->
getInterfaces
(
)
)
)
)
)
;
}

Construct a flat list of all methods from current class, traits,
parent classes and interfaces in this precise order.

@return ReflectionMethod[]

Construct a flat list of all methods from current class traits parent classes and interfaces in this precise order .


public function getMethods(?int $filter = null) : array
    {
        if ($filter === null) {
            return array_values($this->getMethodsIndexedByName());
        }

        return array_values(
            array_filter(
                $this->getMethodsIndexedByName(),
                static function (ReflectionMethod $method) use ($filter) : bool {
                    return (bool) ($filter & $method->getModifiers());
                }
            )
        );
    }

public function getMethods ( ? int $ filter = null ) : array { if ( $ filter === null ) { return array_values ( $ this -> getMethodsIndexedByName ( ) ) ; } return array_values ( array_filter ( $ this -> getMethodsIndexedByName ( ) , static function ( ReflectionMethod $ method ) use ( $ filter ) : bool { return ( bool ) ( $ filter & $ method -> getModifiers ( ) ) ; } ) ) ; }

Fetch an array of all methods for this class.

Filter the results to include only methods with certain attributes. Defaults
to no filtering.
Any combination of \ReflectionMethod::IS_STATIC,
\ReflectionMethod::IS_PUBLIC,
\ReflectionMethod::IS_PROTECTED,
\ReflectionMethod::IS_PRIVATE,
\ReflectionMethod::IS_ABSTRACT,
\ReflectionMethod::IS_FINAL.
For example if $filter = \ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_FINAL
the only the final public methods will be returned

@return ReflectionMethod[]

Fetch an array of all methods for this class .


public function hasMethod(string $methodName) : bool
    {
        try {
            $this->getMethod($methodName);

            return true;
        } catch (OutOfBoundsException $exception) {
            return false;
        }
    }

public function hasMethod ( string $ methodName ) : bool { try { $ this -> getMethod ( $ methodName ) ; return true ; } catch ( OutOfBoundsException $ exception ) { return false ; } }

Does the class have the specified method method?


public function getConstant(string $name)
    {
        $reflectionConstant = $this->getReflectionConstant($name);

        if (! $reflectionConstant) {
            return null;
        }

        return $reflectionConstant->getValue();
    }

public function getConstant ( string $ name ) { $ reflectionConstant = $ this -> getReflectionConstant ( $ name ) ; if ( ! $ reflectionConstant ) { return null ; } return $ reflectionConstant -> getValue ( ) ; }

Get the value of the specified class constant.

Returns null if not specified.

@return mixed|null

Get the value of the specified class constant .


public function getConstructor() : ReflectionMethod
    {
        $constructors = array_filter($this->getMethods(), static function (ReflectionMethod $method) : bool {
            return $method->isConstructor();
        });

        if (! isset($constructors[0])) {
            throw new OutOfBoundsException('Could not find method: __construct');
        }

        return $constructors[0];
    }

public
function
getConstructor
(
)
:
ReflectionMethod
{
$
constructors
=
array_filter
(
$
this
->
getMethods
(
)
,
static
function
(
ReflectionMethod
$
method
)
:
bool
{
return
$
method
->
isConstructor
(
)
;
}
)
;
if
(
!
isset
(
$
constructors
[
0
]
)
)
{
throw
new
OutOfBoundsException
(
'Could not find method: __construct'
)
;
}
return
$
constructors
[
0
]
;
}

Get the constructor method for this class.

@throws OutOfBoundsException

Get the constructor method for this class .


public function getProperties(?int $filter = null) : array
    {
        if ($this->cachedProperties === null) {
            // merging together properties from parent class, traits, current class (in this precise order)
            $this->cachedProperties = array_merge(
                array_merge(
                    [],
                    ...array_map(
                        static function (ReflectionClass $ancestor) use ($filter) : array {
                            return array_filter(
                                $ancestor->getProperties($filter),
                                static function (ReflectionProperty $property) : bool {
                                    return ! $property->isPrivate();
                                }
                            );
                        },
                        array_filter([$this->getParentClass()])
                    ),
                    ...array_map(
                        function (ReflectionClass $trait) use ($filter) {
                            return array_map(function (ReflectionProperty $property) use ($trait) : ReflectionProperty {
                                return ReflectionProperty::createFromNode(
                                    $this->reflector,
                                    $property->getAst(),
                                    $property->getPositionInAst(),
                                    $trait->declaringNamespace,
                                    $property->getDeclaringClass(),
                                    $this
                                );
                            }, $trait->getProperties($filter));
                        },
                        $this->getTraits()
                    )
                ),
                $this->getImmediateProperties()
            );
        }

        if ($filter === null) {
            return $this->cachedProperties;
        }

        return array_filter(
            $this->cachedProperties,
            static function (ReflectionProperty $property) use ($filter) : bool {
                return (bool) ($filter & $property->getModifiers());
            }
        );
    }

Get the properties for this class.

Filter the results to include only properties with certain attributes. Defaults
to no filtering.
Any combination of \ReflectionProperty::IS_STATIC,
\ReflectionProperty::IS_PUBLIC,
\ReflectionProperty::IS_PROTECTED,
\ReflectionProperty::IS_PRIVATE.
For example if $filter = \ReflectionProperty::IS_STATIC | \ReflectionProperty::IS_PUBLIC
only the static public properties will be returned

@return ReflectionProperty[]


public function getParentClass() : ?ReflectionClass
    {
        if (! ($this->node instanceof ClassNode) || $this->node->extends === null) {
            return null;
        }

        // @TODO use actual `ClassReflector` or `FunctionReflector`?
        /** @var self $parent */
        $parent = $this->reflector->reflect($this->node->extends->toString());

        if ($parent->isInterface() || $parent->isTrait()) {
            throw NotAClassReflection::fromReflectionClass($parent);
        }

        return $parent;
    }

Get the parent class, if it is defined. If this class does not have a
specified parent class, this will throw an exception.

@throws NotAClassReflection

Get the parent class if it is defined . If this class does not have a specified parent class this will throw an exception .


public function getParentClassNames() : array
    {
        return array_map(static function (self $parentClass) : string {
            return $parentClass->getName();
        }, array_slice(array_reverse($this->getInheritanceClassHierarchy()), 1));
    }

public function getParentClassNames ( ) : array { return array_map ( static function ( self $ parentClass ) : string { return $ parentClass -> getName ( ) ; } , array_slice ( array_reverse ( $ this -> getInheritanceClassHierarchy ( ) ) , 1 ) ) ; }

Gets the parent class names.

@return string[] A numerical array with parent class names as the values.


public function getTraits() : array
    {
        return array_map(
            function (Node\Name $importedTrait) : ReflectionClass {
                return $this->reflectClassForNamedNode($importedTrait);
            },
            array_merge(
                [],
                ...array_map(
                    static function (TraitUse $traitUse) : array {
                        return $traitUse->traits;
                    },
                    array_filter($this->node->stmts, static function (Node $node) : bool {
                        return $node instanceof TraitUse;
                    })
                )
            )
        );
    }

public function getTraits ( ) : array { return array_map ( function ( Node \ Name $ importedTrait ) : ReflectionClass { return $ this -> reflectClassForNamedNode ( $ importedTrait ) ; } , array_merge ( [ ] , ... array_map ( static function ( TraitUse $ traitUse ) : array { return $ traitUse -> traits ; } , array_filter ( $ this -> node -> stmts , static function ( Node $ node ) : bool { return $ node instanceof TraitUse ; } ) ) ) ) ; }

Get the traits used, if any are defined. If this class does not have any
defined traits, this will return an empty array.

@return ReflectionClass[]

Get the traits used if any are defined . If this class does not have any defined traits this will return an empty array .


private function reflectClassForNamedNode(Node\Name $node) : self
    {
        // @TODO use actual `ClassReflector` or `FunctionReflector`?
        if ($this->isAnonymous()) {
            /** @var self $class */
            $class = (new BetterReflection())->classReflector()->reflect($node->toString());
        } else {
            /** @var self $class */
            $class = $this->reflector->reflect($node->toString());
        }

        return $class;
    }

Given an AST Node\Name, create a new ReflectionClass for the element.

Given an AST Node \ Name create a new ReflectionClass for the element .


public function isInstance($object) : bool
    {
        if (! is_object($object)) {
            throw NotAnObject::fromNonObject($object);
        }

        $className = $this->getName();

        // note: since $object was loaded, we can safely assume that $className is available in the current
        //       php script execution context
        return $object instanceof $className;
    }

Checks whether the given object is an instance.

@link https://php.net/manual/en/reflectionclass.isinstance.php

@param object $object

@throws NotAnObject

@psalm-suppress DocblockTypeContradiction

Checks whether the given object is an instance .


public function isSubclassOf(string $className) : bool
    {
        return in_array(
            ltrim($className, '\\'),
            array_map(
                static function (self $reflectionClass) : string {
                    return $reflectionClass->getName();
                },
                array_slice(array_reverse($this->getInheritanceClassHierarchy()), 1)
            ),
            true
        );
    }

public function isSubclassOf ( string $ className ) : bool { return in_array ( ltrim ( $ className , '\\' ) , array_map ( static function ( self $ reflectionClass ) : string { return $ reflectionClass -> getName ( ) ; } , array_slice ( array_reverse ( $ this -> getInheritanceClassHierarchy ( ) ) , 1 ) ) , true ) ; }

Checks whether the given class string is a subclass of this class.

@link https://php.net/manual/en/reflectionclass.isinstance.php

Checks whether the given class string is a subclass of this class .


public function implementsInterface(string $interfaceName) : bool
    {
        return in_array(ltrim($interfaceName, '\\'), $this->getInterfaceNames(), true);
    }

public function implementsInterface ( string $ interfaceName ) : bool { return in_array ( ltrim ( $ interfaceName , '\\' ) , $ this -> getInterfaceNames ( ) , true ) ; }

Checks whether this class implements the given interface.

@link https://php.net/manual/en/reflectionclass.implementsinterface.php

Checks whether this class implements the given interface .


public function isInstantiable() : bool
    {
        // @TODO doesn't consider internal non-instantiable classes yet.

        if ($this->isAbstract()) {
            return false;
        }

        if ($this->isInterface()) {
            return false;
        }

        if ($this->isTrait()) {
            return false;
        }

        try {
            return $this->getConstructor()->isPublic();
        } catch (OutOfBoundsException $e) {
            return true;
        }
    }

Checks whether this reflection is an instantiable class

@link https://php.net/manual/en/reflectionclass.isinstantiable.php


public function isCloneable() : bool
    {
        if (! $this->isInstantiable()) {
            return false;
        }

        if (! $this->hasMethod('__clone')) {
            return true;
        }

        return $this->getMethod('__clone')->isPublic();
    }

public function isCloneable ( ) : bool { if ( ! $ this -> isInstantiable ( ) ) { return false ; } if ( ! $ this -> hasMethod ( '__clone' ) ) { return true ; } return $ this -> getMethod ( '__clone' ) -> isPublic ( ) ; }

Checks whether this is a reflection of a class that supports the clone operator

@link https://php.net/manual/en/reflectionclass.iscloneable.php


private function getInterfacesHierarchy() : array
    {
        if (! $this->isInterface()) {
            throw NotAnInterfaceReflection::fromReflectionClass($this);
        }

        /** @var InterfaceNode $node */
        $node = $this->node;

        return array_merge(
            [$this->getName() => $this],
            ...array_map(
                function (Node\Name $interfaceName) : array {
                    return $this
                        ->reflectClassForNamedNode($interfaceName)
                        ->getInterfacesHierarchy();
                },
                $node->extends
            )
        );
    }

private
function
getInterfacesHierarchy
(
)
:
array
{
if
(
!
$
this
->
isInterface
(
)
)
{
throw
NotAnInterfaceReflection
::
fromReflectionClass
(
$
this
)
;
}
/** @var InterfaceNode $node */
$
node
=
$
this
->
node
;
return
array_merge
(
[
$
this
->
getName
(
)
=>
$
this
]
,
...
array_map
(
function
(
Node
\
Name
$
interfaceName
)
:
array
{
return
$
this
->
reflectClassForNamedNode
(
$
interfaceName
)
->
getInterfacesHierarchy
(
)
;
}
,
$
node
->
extends
)
)
;
}

This method allows us to retrieve all interfaces parent of the this interface. Do not use on class nodes!

@return ReflectionClass[] parent interfaces of this interface

@throws NotAnInterfaceReflection

This method allows us to retrieve all interfaces parent of the this interface . Do not use on class nodes!