public static function TxnVoidResponse($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $xml, $idents, $config = array())
	{
		$Driver = QuickBooks_Driver_Singleton::getInstance();

		// Figure out what SQL table this object came from
		$map = array();
		$others = array();
		QuickBooks_SQL_Schema::mapToSchema(trim(QuickBooks_Utilities::objectToXMLElement($extra['object'])), QUICKBOOKS_SQL_SCHEMA_MAP_TO_SQL, $map, $others);

		$table = QUICKBOOKS_DRIVER_SQL_PREFIX_SQL . $map[0];

		// We just need to set the voided flag on the transaction
		$update = array(
			QUICKBOOKS_DRIVER_SQL_FIELD_FLAG_VOIDED => 1,
			'AmountDue' => 0.0,
			'Amount' => 0.0,
			'OpenAmount' => 0.0,
			'Amount' => 0.0,
			'Subtotal' => 0.0,
			'TotalAmount' => 0.0,
			'BalanceRemaining' => 0.0,
			'SalesTaxTotal' => 0.0,
			);

		$where = array(
			array( QUICKBOOKS_DRIVER_SQL_FIELD_ID => $ID ),
			);

		// Update the SQL table to indicate it was voided
		$Driver->update($table, $update, $where);

		return true;
	}

Receive a response from QuickBooks about a voided transaction

@return boolean


public static function SalesReceiptModRequest($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $version, $locale, $config = array())
	{
		$Driver = QuickBooks_Driver_Singleton::getInstance();
		if ($SalesReceipt = $Driver->get(QUICKBOOKS_DRIVER_SQL_PREFIX_SQL . 'salesreceipt', array( QUICKBOOKS_DRIVER_SQL_FIELD_ID => $ID )))
		{
			return QuickBooks_Callbacks_SQL_Callbacks::_AddRequest(QUICKBOOKS_OBJECT_SALESRECEIPT, $SalesReceipt, $requestID, $user, $action, $ID, $extra, $err, $last_action_time, $last_actionident_time, $version, $locale, $config);
		}

		return '';
	}

public static function SalesReceiptModRequest ( $ requestID , $ user , $ action , $ ID , $ extra , & $ err , $ last_action_time , $ last_actionident_time , $ version , $ locale , $ config = array ( ) ) { $ Driver = QuickBooks_Driver_Singleton :: getInstance ( ) ; if ( $ SalesReceipt = $ Driver -> get ( QUICKBOOKS_DRIVER_SQL_PREFIX_SQL . 'salesreceipt' , array ( QUICKBOOKS_DRIVER_SQL_FIELD_ID => $ ID ) ) ) { return QuickBooks_Callbacks_SQL_Callbacks :: _AddRequest ( QUICKBOOKS_OBJECT_SALESRECEIPT , $ SalesReceipt , $ requestID , $ user , $ action , $ ID , $ extra , $ err , $ last_action_time , $ last_actionident_time , $ version , $ locale , $ config ) ; } return '' ; }

Generate a qbXML InvoiceMod request to update an invoice


public static function JobTypeAddRequest($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $version, $locale, $config = array())
	{
		$Driver = QuickBooks_Driver_Singleton::getInstance();
		if ($JobType = $Driver->get(QUICKBOOKS_DRIVER_SQL_PREFIX_SQL . 'jobtype', array( QUICKBOOKS_DRIVER_SQL_FIELD_ID => $ID )))
		{
			return QuickBooks_Callbacks_SQL_Callbacks::_AddRequest(QUICKBOOKS_OBJECT_JOBTYPE, $JobType, $requestID, $user, $action, $ID, $extra, $err, $last_action_time, $last_actionident_time, $version, $locale, $config);
		}

		return '';
	}

public static function JobTypeAddRequest ( $ requestID , $ user , $ action , $ ID , $ extra , & $ err , $ last_action_time , $ last_actionident_time , $ version , $ locale , $ config = array ( ) ) { $ Driver = QuickBooks_Driver_Singleton :: getInstance ( ) ; if ( $ JobType = $ Driver -> get ( QUICKBOOKS_DRIVER_SQL_PREFIX_SQL . 'jobtype' , array ( QUICKBOOKS_DRIVER_SQL_FIELD_ID => $ ID ) ) ) { return QuickBooks_Callbacks_SQL_Callbacks :: _AddRequest ( QUICKBOOKS_OBJECT_JOBTYPE , $ JobType , $ requestID , $ user , $ action , $ ID , $ extra , $ err , $ last_action_time , $ last_actionident_time , $ version , $ locale , $ config ) ; } return '' ; }

Generate a JobTypeAdd qbXML request to send to QuickBooks


protected static function _buildFilter($user, $action, $extra, $filter_wrap = false)
	{
		$Driver = QuickBooks_Driver_Singleton::getInstance();
		$xml = '';
		$type = '';

		$key_prev = QuickBooks_Callbacks_SQL_Callbacks::_keySyncPrev($action);
		$key_curr = QuickBooks_Callbacks_SQL_Callbacks::_keySyncCurr($action);

		$module = __CLASS__;

		//$action = null;
		$type = null;
		$opts = null;
		// 					configRead($user, $module, $key, &$type, &$opts)
		$prev_sync_datetime = $Driver->configRead($user, $module, $key_prev, $type, $opts);	// last sync started...

		if (!$prev_sync_datetime)
		{
			// If this query has *never* run before, let's get *all* of the records
			$timestamp = time() - (60 * 60 * 24 * 365 * 25);
			$prev_sync_datetime = date('Y-m-d', $timestamp) . 'T' . date('H:i:s', $timestamp);
			$extra = array();			// If an iterator exists, get rid of it (this should *never* happen... how could it?)

			//			configWrite($user, $module, $key, $value, $type, $opts
			$Driver->configWrite($user, $module, $key_prev, $prev_sync_datetime, null);
		}

		// @TODO MAKE SURE THIS DOESN'T BREAK ANYTHING!
		$prev_sync_datetime = date('Y-m-d', strtotime($prev_sync_datetime) - 600) . 'T' . date('H:i:s', strtotime($prev_sync_datetime) - 600);

		if (!is_array($extra) or
			empty($extra['iteratorID'])) 	// Checks to see if this is the first iteration or not
		{
			// Start of a new iterator!

			// Store when we started to do this iterator (this will become the $prev_sync_datetime after we finish with this iterator)
			$curr_sync_datetime = date('Y-m-d') . 'T' . date('H:i:s');
			$Driver->configWrite($user, $module, $key_curr, $curr_sync_datetime, null);

			if ($filter_wrap)
			{
				if ($action == QUICKBOOKS_QUERY_DELETEDLISTS or $action == QUICKBOOKS_QUERY_DELETEDTXNS)
				{
					$xml .= '<DeletedDateRangeFilter>' . "\n";
					$xml .= '	<FromDeletedDate>' . $prev_sync_datetime . '</FromDeletedDate>' . "\n";
					$xml .= '</DeletedDateRangeFilter>' . "\n";
				}
				else
				{
					$xml .= '<ModifiedDateRangeFilter>' . "\n";
					$xml .= '	<FromModifiedDate>' . $prev_sync_datetime . '</FromModifiedDate>' . "\n";
					$xml .= '</ModifiedDateRangeFilter>' . "\n";
				}
			}
			else
			{
				$xml .= '<FromModifiedDate>' . $prev_sync_datetime . '</FromModifiedDate>';
			}
		}
		else 	// ... otherwise use what we found in previous time stamp
		{
			if ($filter_wrap)
			{
				if ($action == QUICKBOOKS_QUERY_DELETEDLISTS or $action == QUICKBOOKS_QUERY_DELETEDTXNS)
				{
					$xml .= '<DeletedDateRangeFilter>' . "\n";
					$xml .= '	<FromDeletedDate>' . $prev_sync_datetime . '</FromDeletedDate>' . "\n";
					$xml .= '</DeletedDateRangeFilter>' . "\n";
				}
				else
				{
					$xml .= '<ModifiedDateRangeFilter>' . "\n";
					$xml .= '	<FromModifiedDate>' . $prev_sync_datetime . '</FromModifiedDate>' . "\n";
					$xml .= '</ModifiedDateRangeFilter>' . "\n";
				}
			}
			else
			{
				$xml .= '<FromModifiedDate>' . $prev_sync_datetime . '</FromModifiedDate>';
			}
		}

		return $xml;
	}

Used to build the xml that limits the results to only updated results

@param string $user		Same deal: pass along the $user parameter
@param string $action	Ditto with $action parameter
@param array $extra		Simply pass in the $extra value that is passed to the function you're calling this from.
@param boolean $filter_wrap
@return string


public function flushGroups(array $groups)
    {
        $this->ensureCacheGroupsExist($groups);
        foreach ($groups as $group) {
            $this->cacheManager->flushCachesInGroup($group);
        }
    }

public function flushGroups ( array $ groups ) { $ this -> ensureCacheGroupsExist ( $ groups ) ; foreach ( $ groups as $ group ) { $ this -> cacheManager -> flushCachesInGroup ( $ group ) ; } }

Flushes all caches in specified groups.

@param array $groups
@throws NoSuchCacheGroupException

Flushes all caches in specified groups .


public function flushByTagsAndGroups(array $tags, array $groups = null)
    {
        if ($groups === null) {
            $this->flushByTags($tags);
        } else {
            $this->ensureCacheGroupsExist($groups);
            foreach ($groups as $group) {
                $this->flushByTags($tags, $group);
            }
        }
    }

public function flushByTagsAndGroups ( array $ tags , array $ groups = null ) { if ( $ groups === null ) { $ this -> flushByTags ( $ tags ) ; } else { $ this -> ensureCacheGroupsExist ( $ groups ) ; foreach ( $ groups as $ group ) { $ this -> flushByTags ( $ tags , $ group ) ; } } }

Flushes caches by tags, optionally only in specified groups.

@param array $tags
@param array $groups

Flushes caches by tags optionally only in specified groups .


public function find(string $possibleName): string
    {
        $allCommands = $this->getNames();
        $expr = preg_replace_callback('{([^:]+|)}', function ($matches) {
            return preg_quote($matches[1], '/') . '[^:]*';
        }, $possibleName);
        $commands = preg_grep('{^' . $expr . '}', $allCommands);

        if (empty($commands)) {
            $commands = preg_grep('{^' . $expr . '}i', $allCommands);
        }

        // filter out aliases for commands which are already on the list
        if (count($commands) > 1) {
            $commandList = $this->commands;
            $commands = array_unique(array_filter($commands, function ($nameOrAlias) use ($commandList, $commands) {
                $commandName = $commandList[$nameOrAlias]['name'];

                return $commandName === $nameOrAlias || !in_array($commandName, $commands, true);
            }));
        }

        return !empty($commands) && count($commands) === 1 ? $this->commands[reset($commands)]['name'] : $possibleName;
    }

Try resolving short command names and aliases
If that fails, we return the command name as is and let the application throw an exception.

Inspired by \Symfony\Component\Console\Application::find()

@param string $possibleName
@return string

Try resolving short command names and aliases If that fails we return the command name as is and let the application throw an exception .


public function run(ScriptEvent $event): bool
    {
        $composer = $event->getComposer();
        $composerConfig = $composer->getConfig();
        $basePath = realpath(substr($composerConfig->get('vendor-dir'), 0, -strlen($composerConfig->get('vendor-dir', $composerConfig::RELATIVE_PATHS))));
        $commandConfiguration = [];
        foreach ($this->extractPackageMapFromComposer($composer) as $item) {
            /** @var \Composer\Package\PackageInterface $package */
            list($package, $installPath) = $item;
            $installPath = ($installPath ?: $basePath);
            $packageName = $package->getName();
            $packageType = $package->getType();
            if (in_array($packageType, ['metapackage', 'typo3-cms-extension', 'typo3-cms-framework'], true)) {
                // Commands in TYPO3 extensions are scanned for anyway at a later point.
                // With that we ensure not showing commands for extensions that aren't active.
                // Since meta packages have no code, thus cannot include any commands, we ignore them as well.
                continue;
            }
            $packageConfig = $this->getConfigFromPackage($installPath, $packageName);
            if ($packageConfig !== []) {
                $commandConfiguration[] = $packageConfig;
            }
        }

        $success = file_put_contents(
            __DIR__ . '/../../../../Configuration/ComposerPackagesCommands.php',
            '<?php' . chr(10)
            . 'return '
            . var_export($commandConfiguration, true)
            . ';'
        );

        return $success !== false;
    }

Called from Composer

@param ScriptEvent $event
@return bool
@internal


private function canLoadExtLocalconfFile($extensionKey)
    {
        $activePackages = $this->packageManager->getActivePackages();
        foreach ($activePackages as $package) {
            $this->loadExtLocalconfForExtension($package->getPackageKey());
            if ($package->getPackageKey() === $extensionKey) {
                break;
            }
        }

        return true;
    }

private function canLoadExtLocalconfFile ( $ extensionKey ) { $ activePackages = $ this -> packageManager -> getActivePackages ( ) ; foreach ( $ activePackages as $ package ) { $ this -> loadExtLocalconfForExtension ( $ package -> getPackageKey ( ) ) ; if ( $ package -> getPackageKey ( ) === $ extensionKey ) { break ; } } return true ; }

Load all ext_localconf files in order until given extension key

@param string $extensionKey
@return bool


private function canLoadExtTablesFile($extensionKey)
    {
        $activePackages = $this->packageManager->getActivePackages();
        foreach ($activePackages as $package) {
            // Load all ext_localconf files first
            $this->loadExtLocalconfForExtension($package->getPackageKey());
        }
        foreach ($activePackages as $package) {
            $this->loadExtTablesForExtension($package->getPackageKey());
            if ($package->getPackageKey() === $extensionKey) {
                break;
            }
        }

        return true;
    }

private
function
canLoadExtTablesFile
(
$
extensionKey
)
{
$
activePackages
=
$
this
->
packageManager
->
getActivePackages
(
)
;
foreach
(
$
activePackages
as
$
package
)
{
// Load all ext_localconf files first
$
this
->
loadExtLocalconfForExtension
(
$
package
->
getPackageKey
(
)
)
;
}
foreach
(
$
activePackages
as
$
package
)
{
$
this
->
loadExtTablesForExtension
(
$
package
->
getPackageKey
(
)
)
;
if
(
$
package
->
getPackageKey
(
)
===
$
extensionKey
)
{
break
;
}
}
return
true
;
}

Load all ext_table files in order until given extension key

@param string $extensionKey
@return bool


private function loadExtLocalconfForExtension($extensionKey)
    {
        $extensionInfo = $GLOBALS['TYPO3_LOADED_EXT'][$extensionKey];
        // This is the main array meant to be manipulated in the ext_localconf.php files
        // In general it is recommended to not rely on it to be globally defined in that
        // scope but to use $GLOBALS['TYPO3_CONF_VARS'] instead.
        // Nevertheless we define it here as global for backwards compatibility.
        global $TYPO3_CONF_VARS;
        $_EXTKEY = $extensionKey;
        if (isset($extensionInfo['ext_localconf.php']) && $extensionInfo['ext_localconf.php']) {
            // $_EXTKEY and $_EXTCONF are available in ext_localconf.php
            // and are explicitly set in cached file as well
            $_EXTCONF = $GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][$_EXTKEY];
            require $extensionInfo['ext_localconf.php'];
        }
    }

Loads ext_localconf.php for a single extension. Method is a modified copy of
the original bootstrap method.

@param string $extensionKey

Loads ext_localconf . php for a single extension . Method is a modified copy of the original bootstrap method .


private function loadExtTablesForExtension($extensionKey)
    {
        $extensionInfo = $GLOBALS['TYPO3_LOADED_EXT'][$extensionKey];
        // In general it is recommended to not rely on it to be globally defined in that
        // scope, but we can not prohibit this without breaking backwards compatibility
        global $T3_SERVICES, $T3_VAR, $TYPO3_CONF_VARS;
        global $TBE_MODULES, $TBE_MODULES_EXT, $TCA;
        global $PAGES_TYPES, $TBE_STYLES;
        global $_EXTKEY;
        // Load each ext_tables.php file of loaded extensions
        $_EXTKEY = $extensionKey;
        if (isset($extensionInfo['ext_tables.php']) && $extensionInfo['ext_tables.php']) {
            // $_EXTKEY and $_EXTCONF are available in ext_tables.php
            // and are explicitly set in cached file as well
            $_EXTCONF = $GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][$_EXTKEY];
            require $extensionInfo['ext_tables.php'];
        }
    }

Loads ext_tables.php for a single extension. Method is a modified copy of
the original bootstrap method.

@param string $extensionKey

Loads ext_tables . php for a single extension . Method is a modified copy of the original bootstrap method .


private function ensureInstallationIsPossible(array $options)
    {
        $integrityCheck = $options['integrityCheck'] ?? false;
        if (!$integrityCheck) {
            return;
        }

        $isInteractive = $options['interactive'] ?? $this->output->getSymfonyConsoleInput()->isInteractive();
        $forceInstall = $options['forceInstall'] ?? false;

        $localConfFile = PATH_typo3conf . 'LocalConfiguration.php';
        $packageStatesFile = PATH_typo3conf . 'PackageStates.php';
        if (!$forceInstall && file_exists($localConfFile)) {
            $this->output->outputLine();
            $this->output->outputLine('<error>TYPO3 seems to be already set up!</error>');
            $proceed = $isInteractive;
            if ($isInteractive) {
                $this->output->outputLine();
                $this->output->outputLine('<info>If you continue, your typo3conf/LocalConfiguration.php</info>');
                $this->output->outputLine('<info>and typo3conf/PackageStates.php files will be deleted!</info>');
                $this->output->outputLine();
                $proceed = $this->output->askConfirmation('<info>Do you really want to proceed?</info> (<comment>no</comment>) ', false);
            }
            if (!$proceed) {
                $this->output->outputLine('<error>Installation aborted!</error>');
                throw new InstallationFailedException('Installation aborted by user', 1529926774);
            }
        }
        @unlink($localConfFile);
        @unlink($packageStatesFile);
        clearstatcache();
        if (file_exists($localConfFile)) {
            $this->output->outputLine();
            $this->output->outputLine('<error>Unable to delete configuration file!</error>');
            $this->output->outputLine('<error>Installation aborted!</error>');
            throw new InstallationFailedException('Installation aborted because of insufficient premissions', 1529926810);
        }
    }

Handles the case when LocalConfiguration.php file already exists

@param array $options
@throws InstallationFailedException

Handles the case when LocalConfiguration . php file already exists


protected function executeScheduledTasks()
    {
        // Loop as long as there are tasks
        do {
            // Try getting the next task and execute it
            // If there are no more tasks to execute, an exception is thrown by \TYPO3\CMS\Scheduler\Scheduler::fetchTask()
            try {
                /** @var $task \TYPO3\CMS\Scheduler\Task\AbstractTask */
                $task = $this->scheduler->fetchTask();
                $hasTask = true;
                try {
                    $this->scheduler->executeTask($task);
                } catch (\Exception $e) {
                    // We ignore any exception that may have been thrown during execution,
                    // as this is a background process.
                    // The exception message has been recorded to the database anyway
                    continue;
                }
            } catch (\OutOfBoundsException $e) {
                $hasTask = false;
            } catch (\UnexpectedValueException $e) {
                continue;
            }
        } while ($hasTask);
        // Record the run in the system registry
        $this->scheduler->recordLastRun();
    }


protected function executeSingleTask($taskId, $forceExecution)
    {
        // Force the execution of the task even if it is disabled or no execution scheduled
        if ($forceExecution) {
            $task = $this->scheduler->fetchTask($taskId);
        } else {
            $whereClause = 'uid = ' . (int)$taskId . ' AND nextexecution != 0 AND nextexecution <= ' . (int)$GLOBALS['EXEC_TIME'];
            list($task) = $this->scheduler->fetchTasksWithCondition($whereClause);
        }
        if ($this->scheduler->isValidTaskObject($task)) {
            try {
                $this->scheduler->executeTask($task);
            } finally {
                // Record the run in the system registry
                $this->scheduler->recordLastRun('cli-by-id');
            }
        }
    }

Execute a single task

@param int $taskId
@param bool $forceExecution


public function migrate(array $statements, array $selectedStatements)
    {
        return $this->schemaMigrator->migrate($this->sqlReader->getCreateTableStatementArray($this->sqlReader->getTablesDefinitionString()), $selectedStatements);
    }

public function migrate ( array $ statements , array $ selectedStatements ) { return $ this -> schemaMigrator -> migrate ( $ this -> sqlReader -> getCreateTableStatementArray ( $ this -> sqlReader -> getTablesDefinitionString ( ) ) , $ selectedStatements ) ; }

Actually execute the migration to the new schema

@param array $statements
@param array $selectedStatements
@return array


public function lockForEditorsCommand()
    {
        $this->ensureConfigValueModifiable();
        $lockedForEditors = $this->configurationService->getLocal('BE/adminOnly') !== self::LOCK_TYPE_UNLOCKED;
        if (!$lockedForEditors) {
            $this->configurationService->setLocal('BE/adminOnly', self::LOCK_TYPE_ADMIN);
            $this->outputLine('<info>Locked backend for editor access.</info>');
        } else {
            $this->outputLine('<warning>The backend was already locked for editors, hence nothing was done.</warning>');
        }
    }

Lock backend for editors

Deny backend access, but only for editors.
Admins will still be able to log in and work with the backend.

@see typo3_console:backend:unlockforeditors


public function unlockForEditorsCommand()
    {
        $this->ensureConfigValueModifiable();
        $lockedForEditors = $this->configurationService->getLocal('BE/adminOnly') !== self::LOCK_TYPE_UNLOCKED;
        if ($lockedForEditors) {
            $this->configurationService->setLocal('BE/adminOnly', self::LOCK_TYPE_UNLOCKED);
            $this->outputLine('<info>Unlocked backend for editors.</info>');
        } else {
            $this->outputLine('<warning>The backend was not locked for editors.</warning>');
        }
    }

Unlock backend for editors

Allow backend access for editors again (e.g. after having been locked with backend:lockforeditors command).

@see typo3_console:backend:lockforeditors


public function createAdminCommand(string $username, string $password)
    {
        $givenUsername = $username;
        $username = strtolower(preg_replace('/\\s/i', '', $username));

        if ($givenUsername !== $username) {
            $this->outputLine('<warning>Given username "%s" contains invalid characters. Using "%s" instead.</warning>', [$givenUsername, $username]);
        }

        if (strlen($username) < 4) {
            $this->outputLine('<error>Username must be at least 4 characters.</error>');
            $this->quit(1);
        }
        if (strlen($password) < 8) {
            $this->outputLine('<error>Password must be at least 8 characters.</error>');
            $this->quit(1);
        }
        $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
        $userExists = $connectionPool->getConnectionForTable('be_users')
            ->count(
                'uid',
                'be_users',
                ['username' => $username]
            );
        if ($userExists) {
            $this->outputLine('<error>A user with username "%s" already exists.</error>', [$username]);
            $this->quit(1);
        }
        $adminUserFields = [
            'username' => $username,
            'password' => $this->salt->getHashedPassword($password),
            'admin' => 1,
            'tstamp' => $GLOBALS['EXEC_TIME'],
            'crdate' => $GLOBALS['EXEC_TIME'],
        ];
        $connectionPool->getConnectionForTable('be_users')
            ->insert('be_users', $adminUserFields);

        $this->outputLine('<info>Created admin user with username "%s".</info>', [$username]);
    }

Create admin backend user

Create a new user with administrative access.

@param string $username Username of the user
@param string $password Password of the user


public function listWizards($includeDone = false)
    {
        if (empty($this->listCache)) {
            $availableUpgradeWizards = [];
            foreach ($this->wizardRegistry as $identifier => $className) {
                $updateObject = $this->factory->create($identifier);
                $shortIdentifier = $this->factory->getShortIdentifier($identifier);
                $availableUpgradeWizards[$shortIdentifier] = [
                    'className' => $className,
                    'title' => $updateObject->getTitle(),
                    'done' => false,
                ];
                $explanation = '';
                $wizardImplementsInterface = $updateObject instanceof UpgradeWizardInterface && !$updateObject instanceof AbstractUpdate;
                $markedAsDone = $this->registry->get('installUpdate', $className, false) || $this->registry->get('installUpdate', $identifier, false);
                if ($wizardImplementsInterface) {
                    $explanation = $updateObject->getDescription();
                    $wizardClaimsExecution = $updateObject->updateNecessary();
                } else {
                    $wizardClaimsExecution = $updateObject->checkForUpdate($explanation);
                }
                if ($markedAsDone || !$wizardClaimsExecution) {
                    $availableUpgradeWizards[$shortIdentifier]['done'] = true;
                }
                $availableUpgradeWizards[$shortIdentifier]['explanation'] = html_entity_decode(strip_tags($explanation));
            }
            $this->listCache = $availableUpgradeWizards;
        }

        return array_filter(
            $this->listCache,
            function ($info) use ($includeDone) {
                return $includeDone || !$info['done'];
            }
        );
    }

public function listWizards ( $ includeDone = false ) { if ( empty ( $ this -> listCache ) ) { $ availableUpgradeWizards = [ ] ; foreach ( $ this -> wizardRegistry as $ identifier => $ className ) { $ updateObject = $ this -> factory -> create ( $ identifier ) ; $ shortIdentifier = $ this -> factory -> getShortIdentifier ( $ identifier ) ; $ availableUpgradeWizards [ $ shortIdentifier ] = [ 'className' => $ className , 'title' => $ updateObject -> getTitle ( ) , 'done' => false , ] ; $ explanation = '' ; $ wizardImplementsInterface = $ updateObject instanceof UpgradeWizardInterface && ! $ updateObject instanceof AbstractUpdate ; $ markedAsDone = $ this -> registry -> get ( 'installUpdate' , $ className , false ) || $ this -> registry -> get ( 'installUpdate' , $ identifier , false ) ; if ( $ wizardImplementsInterface ) { $ explanation = $ updateObject -> getDescription ( ) ; $ wizardClaimsExecution = $ updateObject -> updateNecessary ( ) ; } else { $ wizardClaimsExecution = $ updateObject -> checkForUpdate ( $ explanation ) ; } if ( $ markedAsDone || ! $ wizardClaimsExecution ) { $ availableUpgradeWizards [ $ shortIdentifier ] [ 'done' ] = true ; } $ availableUpgradeWizards [ $ shortIdentifier ] [ 'explanation' ] = html_entity_decode ( strip_tags ( $ explanation ) ) ; } $ this -> listCache = $ availableUpgradeWizards ; } return array_filter ( $ this -> listCache , function ( $ info ) use ( $ includeDone ) { return $ includeDone || ! $ info [ 'done' ] ; } ) ; }

List available upgrade wizards

@param bool $includeDone
@return array


public function generatePackageStatesCommand(array $frameworkExtensions = [], array $excludedExtensions = [], $activateDefault = false)
    {
        if ($activateDefault && CompatibilityScripts::isComposerMode()) {
            // @deprecated for composer usage in 5.0 will be removed with 6.0
            $this->outputLine('<warning>Using --activate-default is deprecated in composer managed TYPO3 installations.</warning>');
            $this->outputLine('<warning>Instead of requiring typo3/cms in your project, you should consider only requiring individual packages you need.</warning>');
        }
        $frameworkExtensions = $frameworkExtensions ?: explode(',', (string)getenv('TYPO3_ACTIVE_FRAMEWORK_EXTENSIONS'));
        $packageStatesGenerator = new PackageStatesGenerator($this->packageManager);
        $activatedExtensions = $packageStatesGenerator->generate($frameworkExtensions, $excludedExtensions, $activateDefault);

        try {
            // Make sure file caches are empty after generating package states file
            CommandDispatcher::createFromCommandRun()->executeCommand('cache:flush', ['--files-only']);
        } catch (FailedSubProcessCommandException $e) {
            // Ignore errors here.
            // They might be triggered from extensions accessing db or having other things
            // broken in ext_tables or ext_localconf
            // In such case we cannot do much about it other than ignoring it for
            // generating packages states
        }

        $this->outputLine(
            '<info>The following extensions have been added to the generated PackageStates.php file:</info> %s',
            [
                implode(', ', array_map(function (PackageInterface $package) {
                    return $package->getPackageKey();
                }, $activatedExtensions)),
            ]
        );
        if (!empty($excludedExtensions)) {
            $this->outputLine(
                '<info>The following third party extensions were excluded during this process:</info> %s',
                [
                    implode(', ', $excludedExtensions),
                ]
            );
        }
    }

Generate PackageStates.php file

Generates and writes typo3conf/PackageStates.php file.
Goal is to not have this file in version control, but generate it on composer install.

Marks the following extensions as active:

- Third party extensions
- All core extensions that are required (or part of minimal usable system)
- All core extensions which are provided with the --framework-extensions argument.
- In composer mode all composer dependencies to TYPO3 framework extensions are detected and activated by default.

To require TYPO3 core extensions use the following command:

composer require typo3/cms-foo "*"

This updates your composer.json and composer.lock without any other changes.

Example: %command.full_name%

@param array $frameworkExtensions TYPO3 system extensions that should be marked as active. Extension keys separated by comma.
@param array $excludedExtensions Extensions which should stay inactive. This does not affect provided framework extensions or framework extensions that are required or part as minimal usable system.
@param bool $activateDefault (DEPRECATED) If true, typo3/cms extensions that are marked as TYPO3 factory default, will be activated, even if not in the list of configured active framework extensions.


public function fixFolderStructureCommand()
    {
        $folderStructureFactory = new ExtensionFactory($this->packageManager);
        $fixedStatusObjects = $folderStructureFactory
            ->getStructure()
            ->fix();

        if (empty($fixedStatusObjects)) {
            $this->outputLine('<info>No action performed!</info>');
        } else {
            $this->outputLine('<info>The following directory structure has been fixed:</info>');
            foreach ($fixedStatusObjects as $fixedStatusObject) {
                $this->outputLine($fixedStatusObject->getTitle());
            }
        }
    }

Fix folder structure

Automatically create files and folders, required for a TYPO3 installation.

This command creates the required folder structure needed for TYPO3 including extensions.
It is recommended to be executed after executing
typo3cms install:generatepackagestates, to ensure proper generation of
required folders for all active extensions.

@see typo3_console:install:generatepackagestates

@throws \TYPO3\CMS\Install\FolderStructure\Exception
@throws \TYPO3\CMS\Install\FolderStructure\Exception\InvalidArgumentException
@throws \TYPO3\CMS\Install\FolderStructure\Exception\RootNodeException
@throws \TYPO3\CMS\Install\Status\Exception


public function extensionSetupIfPossibleCommand()
    {
        $commandDispatcher = CommandDispatcher::createFromCommandRun();
        try {
            $this->outputLine($commandDispatcher->executeCommand('database:updateschema'));
            $this->outputLine($commandDispatcher->executeCommand('cache:flush'));
            $this->outputLine($commandDispatcher->executeCommand('extension:setupactive'));
        } catch (FailedSubProcessCommandException $e) {
            $this->outputLine('<warning>Extension setup skipped.</warning>');
        }
    }

Setup TYPO3 with extensions if possible

This command tries up all TYPO3 extensions, but quits gracefully if this is not possible.
This can be used in composer.json scripts to ensure that extensions
are always set up correctly after a composer run on development systems,
but does not fail on packaging for deployment where no database connection is available.

Besides that, it can be used for a first deploy of a TYPO3 instance in a new environment,
but also works for subsequent deployments.

@see typo3_console:extension:setupactive


private function executeActionWithArguments($actionName, array $arguments = [], $dryRun = false)
    {
        $this->outputLine(serialize($this->installStepActionExecutor->executeActionWithArguments($actionName, $arguments, $dryRun)));
    }

private function executeActionWithArguments ( $ actionName , array $ arguments = [ ] , $ dryRun = false ) { $ this -> outputLine ( serialize ( $ this -> installStepActionExecutor -> executeActionWithArguments ( $ actionName , $ arguments , $ dryRun ) ) ) ; }

Executes the given action and outputs the serialized result messages

@param string $actionName Name of the install step
@param array $arguments Arguments for the install step
@param bool $dryRun If true, do not execute the action, but only check if execution is necessary


public static function createFromComposerRun(...$arguments): self
    {
        if (isset($arguments[0]) && $arguments[0] instanceof ScriptEvent) {
            // Calling createFromComposerRun with ScriptEvent as first argument is deprecated and will be removed with 6.0
            array_shift($arguments);
        }

        $commandLine = $arguments[0] ?? [];
        $environmentVars = $arguments[1] ?? [];
        $phpFinder = $arguments[2] ?? null;

        // should be Application::COMMAND_NAME, but our Application class currently conflicts with symfony/console 2.7, which is used by Composer
        $typo3CommandPath = dirname(__DIR__, 4) . '/typo3cms';
        $environmentVars['TYPO3_CONSOLE_PLUGIN_RUN'] = true;

        return self::create($typo3CommandPath, $commandLine, $environmentVars, $phpFinder);
    }

Create the dispatcher from within a composer plugin context

@param array $arguments
@internal param ScriptEvent $event (deprecated) Possibly given but deprecated event
@internal param array $commandLine
@internal param array $environmentVars
@internal param PhpExecutableFinder $phpFinder
@return CommandDispatcher


public static function createFromCommandRun(array $commandLine = [], array $environmentVars = [], PhpExecutableFinder $phpFinder = null): self
    {
        if (!isset($_SERVER['argv'][0]) && strpos($_SERVER['argv'][0], Application::COMMAND_NAME) === false) {
            throw new RuntimeException('Tried to create typo3 command runner from wrong context', 1484945065);
        }
        $typo3CommandPath = $_SERVER['argv'][0];

        return self::create($typo3CommandPath, $commandLine, $environmentVars, $phpFinder);
    }

Useful for creating the object during the runtime of another command

Just use the method without arguments for best results

@param array $commandLine
@param array $environmentVars
@param PhpExecutableFinder $phpFinder
@throws RuntimeException
@return CommandDispatcher


public static function create($typo3CommandPath, array $commandLine = [], array $environmentVars = [], PhpExecutableFinder $phpFinder = null): self
    {
        $environmentVars['TYPO3_CONSOLE_SUB_PROCESS'] = true;
        $phpFinder = $phpFinder ?: new PhpExecutableFinder();
        if (!($php = $phpFinder->find(false))) {
            throw new RuntimeException('The "php" binary could not be found.', 1485128615);
        }
        array_unshift($commandLine, $typo3CommandPath);
        $phpArguments = $phpFinder->findArguments();
        if (getenv('PHP_INI_PATH')) {
            $phpArguments[] = '-c';
            $phpArguments[] = getenv('PHP_INI_PATH');
        }
        // Ensure we do not output PHP startup errors for sub-processes to not have them interfere with process output
        // Later, very early in booting the error reporting is set to an appropriate value anyway
        $phpArguments[] = '-d';
        $phpArguments[] = 'error_reporting=0';
        array_unshift($commandLine, ...$phpArguments);
        array_unshift($commandLine, $php);

        return new self($commandLine, $environmentVars);
    }

Basic factory method, which need the exact path to the typo3 console binary to create the dispatcher

@param string $typo3CommandPath Absolute path to the typo3 console binary
@param array $commandLine
@param array $environmentVars
@param PhpExecutableFinder $phpFinder
@throws RuntimeException
@return CommandDispatcher

Basic factory method which need the exact path to the typo3 console binary to create the dispatcher


public function executeCommand($command, array $arguments = [], array $envVars = [], $input = null): string
    {
        $envVars = array_replace($this->environmentVars, $envVars);
        $commandLine = $this->commandLinePrefix;

        $commandLine[] = $command;
        foreach ($arguments as $argumentName => $argumentValue) {
            if (is_int($argumentName)) {
                $commandLine[] = $argumentValue;
            } else {
                $commandLine[] = $this->getDashedArgumentName($argumentName);
                $commandLine[] = is_array($argumentValue) ? implode(',', $argumentValue) : $argumentValue;
            }
        }

        $process = $this->getProcess($commandLine, $envVars, $input);
        $process->run();

        $output = str_replace("\r\n", "\n", trim($process->getOutput()));

        if (!$process->isSuccessful()) {
            throw FailedSubProcessCommandException::forProcess($command, $process);
        }

        return $output;
    }

public function executeCommand ( $ command , array $ arguments = [ ] , array $ envVars = [ ] , $ input = null ) : string { $ envVars = array_replace ( $ this -> environmentVars , $ envVars ) ; $ commandLine = $ this -> commandLinePrefix ; $ commandLine [ ] = $ command ; foreach ( $ arguments as $ argumentName => $ argumentValue ) { if ( is_int ( $ argumentName ) ) { $ commandLine [ ] = $ argumentValue ; } else { $ commandLine [ ] = $ this -> getDashedArgumentName ( $ argumentName ) ; $ commandLine [ ] = is_array ( $ argumentValue ) ? implode ( ',' , $ argumentValue ) : $ argumentValue ; } } $ process = $ this -> getProcess ( $ commandLine , $ envVars , $ input ) ; $ process -> run ( ) ; $ output = str_replace ( "\r\n" , "\n" , trim ( $ process -> getOutput ( ) ) ) ; if ( ! $ process -> isSuccessful ( ) ) { throw FailedSubProcessCommandException :: forProcess ( $ command , $ process ) ; } return $ output ; }

Execute a command in a sub process

@param string $command Command identifier
@param array $arguments Argument names will automatically be converted to dashed version, if not provided like so
@param array $envVars Environment vars to be added to the command
@param resource|string|\Traversable|null $input Inpupt (stdin) for the command
@throws FailedSubProcessCommandException
@return string


public function handleException(\Throwable $exception)
    {
        $this->exceptionRenderer->render($exception, $this->output->getErrorOutput());

        echo PHP_EOL;
        exit(1);
    }

public function handleException ( \ Throwable $ exception ) { $ this -> exceptionRenderer -> render ( $ exception , $ this -> output -> getErrorOutput ( ) ) ; echo PHP_EOL ; exit ( 1 ) ; }

Formats and echoes the exception for the command line.

@param \Throwable $exception The exception object
@return void

Formats and echoes the exception for the command line .


public function create(string $identifier)
    {
        /** @var AbstractUpdate|UpgradeWizardInterface $upgradeWizard */
        $upgradeWizard = $this->objectManager->get($this->getClassNameFromIdentifier($identifier));
        if ($upgradeWizard instanceof AbstractUpdate) {
            $upgradeWizard->setIdentifier($identifier);
        }
        if ($upgradeWizard instanceof ChattyInterface) {
            $output = new BufferedOutput();
            $upgradeWizard->setOutput($output);
        }

        return $upgradeWizard;
    }

Creates instance of an upgrade wizard

@param string $identifier The identifier or class name of an upgrade wizard
@throws RuntimeException
@return AbstractUpdate|UpgradeWizardInterface Newly instantiated upgrade wizard


private function wordWrap(string $stringToWrap, int $indent, $maxWidth): string
    {
        $wrapped = $maxWidth === null ? $stringToWrap : wordwrap($stringToWrap, $maxWidth, "\n", true);

        return preg_replace('/\s*[\r\n]\s*/', "\n" . str_repeat(' ', $indent), $wrapped);
    }

private
function
wordWrap
(
string
$
stringToWrap
,
int
$
indent
,
$
maxWidth
)
:
string
{
$
wrapped
=
$
maxWidth
===
$
stringToWrap
:
wordwrap
(
$
stringToWrap
,
$
maxWidth
,
"\n"
,
true
)
;
return
preg_replace
(
'/\s*[\r\n]\s*/'
,
"\n"
.
str_repeat
(
' '
,
$
indent
)
,
$
wrapped
)
;
}

Wraps a text and adds indentation to new lines

@param string $stringToWrap
@param int $indent
@param int|null $maxWidth
@return string


public function requestCommand($requestUrl)
    {
        // TODO: this needs heavy cleanup!
        $template = file_get_contents(__DIR__ . '/../../../Resources/Private/Templates/request.tpl');
        $arguments = [
            'documentRoot' => getenv('TYPO3_PATH_WEB') ?: PATH_site,
            'requestUrl' => $this->makeAbsolute($requestUrl),
        ];
        // No other solution atm than to fake a CLI request type
        $code = str_replace('{arguments}', var_export($arguments, true), $template);
        $process = new PhpProcess($code, null, null, 0);
        $process->mustRun();
        $rawResponse = json_decode($process->getOutput());
        if ($rawResponse === null || $rawResponse->status === 'failure') {
            $this->outputLine('<error>An error occurred while trying to request the specified URL.</error>');
            $this->outputLine(sprintf('<error>Error: %s</error>', !empty($rawResponse->error) ? $rawResponse->error : 'Could not decode response. Please check your error log!'));
            $this->outputLine(sprintf('<error>Content: %s</error>', $process->getOutput()));
            $this->quit(1);
        }

        $this->output($rawResponse->content);
    }

Submit frontend request

Submits a frontend request to TYPO3 on the specified URL.

@param string $requestUrl URL to make a frontend request.


protected function makeAbsolute($url)
    {
        $parsedUrl = parse_url($url);

        $scheme = isset($parsedUrl['scheme']) ? $parsedUrl['scheme'] : 'http';
        $host = isset($parsedUrl['host']) ? $parsedUrl['host'] : 'localhost';
        $path = isset($parsedUrl['path']) ? $parsedUrl['path'] : '/';
        $query = isset($parsedUrl['query']) ? '?' . $parsedUrl['query'] : '';

        return $scheme . '://' . $host . '/' . ltrim($path, '/') . $query;
    }

protected function makeAbsolute ( $ url ) { $ parsedUrl = parse_url ( $ url ) ; $ scheme = isset ( $ parsedUrl [ 'scheme' ] ) ? $ parsedUrl [ 'scheme' ] : 'http' ; $ host = isset ( $ parsedUrl [ 'host' ] ) ? $ parsedUrl [ 'host' ] : 'localhost' ; $ path = isset ( $ parsedUrl [ 'path' ] ) ? $ parsedUrl [ 'path' ] : '/' ; $ query = isset ( $ parsedUrl [ 'query' ] ) ? '?' . $ parsedUrl [ 'query' ] : '' ; return $ scheme . '://' . $ host . '/' . ltrim ( $ path , '/' ) . $ query ; }

Make URL absolute, so that the core fake frontend request bootstrap
correctly configures the environment for trusted host pattern.

@param string $url
@return string

Make URL absolute so that the core fake frontend request bootstrap correctly configures the environment for trusted host pattern .


public function checkExtensionConstraintsCommand(array $extensionKeys = [], $typo3Version = TYPO3_version)
    {
        if (empty($extensionKeys)) {
            $failedPackageMessages = $this->upgradeHandling->matchAllExtensionConstraints($typo3Version);
        } else {
            $failedPackageMessages = [];
            foreach ($extensionKeys as $extensionKey) {
                try {
                    if (!empty($result = $this->upgradeHandling->matchExtensionConstraints($extensionKey, $typo3Version))) {
                        $failedPackageMessages[$extensionKey] = $result;
                    }
                } catch (UnknownPackageException $e) {
                    $this->outputLine('<warning>Extension "%s" is not found in the system</warning>', [$extensionKey]);
                }
            }
        }
        foreach ($failedPackageMessages as $constraintMessage) {
            $this->outputLine('<error>%s</error>', [$constraintMessage]);
        }
        if (empty($failedPackageMessages)) {
            $this->outputLine('<info>All third party extensions claim to be compatible with TYPO3 version %s</info>', [$typo3Version]);
        } else {
            $this->quit(1);
        }
    }

Check TYPO3 version constraints of extensions

This command is especially useful **before** switching sources to a new TYPO3 version.
It checks the version constraints of all third party extensions against a given TYPO3 version.
It therefore relies on the constraints to be correct.

@param array $extensionKeys Extension keys to check. Separate multiple extension keys with comma.
@param string $typo3Version TYPO3 version to check against. Defaults to current TYPO3 version.
@Definition\Argument(name="extensionKeys")


public function listCommand($all = false)
    {
        $verbose = $this->output->getSymfonyConsoleOutput()->isVerbose();
        $messages = [];
        $wizards = $this->upgradeHandling->executeInSubProcess('listWizards', [], $messages);

        $listRenderer = new UpgradeWizardListRenderer();
        $this->outputLine('<comment>Wizards scheduled for execution:</comment>');
        $listRenderer->render($wizards['scheduled'], $this->output, $verbose);

        if ($all) {
            $this->outputLine(PHP_EOL . '<comment>Wizards marked as done:</comment>');
            $listRenderer->render($wizards['done'], $this->output, $verbose);
        }
        $this->outputLine();
        foreach ($messages as $message) {
            $this->outputLine($message);
        }
    }

List upgrade wizards

@param bool $all If set, all wizards will be listed, even the once marked as ready or done


public function wizardCommand($identifier, array $arguments = [], $force = false)
    {
        $messages = [];
        $result = $this->upgradeHandling->executeInSubProcess('executeWizard', [$identifier, $arguments, $force], $messages);
        (new UpgradeWizardResultRenderer())->render([$identifier => $result], $this->output);
        $this->outputLine();
        foreach ($messages as $message) {
            $this->outputLine($message);
        }
    }

public function wizardCommand ( $ identifier , array $ arguments = [ ] , $ force = false ) { $ messages = [ ] ; $ result = $ this -> upgradeHandling -> executeInSubProcess ( 'executeWizard' , [ $ identifier , $ arguments , $ force ] , $ messages ) ; ( new UpgradeWizardResultRenderer ( ) ) -> render ( [ $ identifier => $ result ] , $ this -> output ) ; $ this -> outputLine ( ) ; foreach ( $ messages as $ message ) { $ this -> outputLine ( $ message ) ; } }

Execute a single upgrade wizard

@param string $identifier Identifier of the wizard that should be executed
@param array $arguments Arguments for the wizard prefixed with the identifier, e.g. compatibility7Extension[install]=0
@param bool $force Force execution, even if the wizard has been marked as done


public function allCommand(array $arguments = [])
    {
        $verbose = $this->output->getSymfonyConsoleOutput()->isVerbose();
        $this->outputLine(PHP_EOL . 'Initiating TYPO3 upgrade' . PHP_EOL);

        $messages = [];
        $results = $this->upgradeHandling->executeAll($arguments, $this->output, $messages);

        $this->outputLine(PHP_EOL . PHP_EOL . 'Successfully upgraded TYPO3 to version %s', [TYPO3_version]);

        if ($verbose) {
            $this->outputLine();
            $this->outputLine('<comment>Upgrade report:</comment>');
            (new UpgradeWizardResultRenderer())->render($results, $this->output);
        }
        $this->outputLine();
        foreach ($messages as $message) {
            $this->outputLine($message);
        }
    }

'<i>Successfully upgraded TYPO3 to version %s</i>'

Execute all upgrade wizards that are scheduled for execution

@param array $arguments Arguments for the wizard prefixed with the identifier, e.g. compatibility7Extension[install]=0; multiple arguments separated with comma


public function subProcessCommand($upgradeCommand, $arguments)
    {
        $arguments = unserialize($arguments, ['allowed_classes' => false]);
        $result = $this->upgradeHandling->$upgradeCommand(...$arguments);
        $this->output(serialize($result));
    }

public function subProcessCommand ( $ upgradeCommand , $ arguments ) { $ arguments = unserialize ( $ arguments , [ 'allowed_classes' => false ] ) ; $ result = $ this -> upgradeHandling -> $ upgradeCommand ( ... $ arguments ) ; $ this -> output ( serialize ( $ result ) ) ; }

This is where the hard work happens in a fully bootstrapped TYPO3
It will be called as sub process

@param string $upgradeCommand
@param string $arguments Serialized arguments
@internal


public function checkExtensionCompatibilityCommand($extensionKey, $configOnly = false)
    {
        $this->output(\json_encode($this->upgradeHandling->isCompatible($extensionKey, $configOnly)));
    }

public function checkExtensionCompatibilityCommand ( $ extensionKey , $ configOnly = false ) { $ this -> output ( \ json_encode ( $ this -> upgradeHandling -> isCompatible ( $ extensionKey , $ configOnly ) ) ) ; }

Checks for broken extensions

This command in meant to be executed as sub process as it is is subject to cause fatal errors
when extensions have broken (incompatible) code

@param string $extensionKey Extension key for extension to check
@param bool $configOnly
@internal


public function setupExtensions(array $packages)
    {
        foreach ($packages as $package) {
            $this->extensionFactory->getExtensionStructure($package)->fix();
            $this->callInstaller('importInitialFiles', [PathUtility::stripPathSitePrefix($package->getPackagePath()), $package->getPackageKey()]);
            $this->extensionConfiguration->saveDefaultConfiguration($package->getPackageKey());
        }

        $this->schemaService->updateSchema(SchemaUpdateType::expandSchemaUpdateTypes(['safe']));

        foreach ($packages as $package) {
            $relativeExtensionPath = PathUtility::stripPathSitePrefix($package->getPackagePath());
            $extensionKey = $package->getPackageKey();
            $this->callInstaller('importStaticSqlFile', [$relativeExtensionPath]);
            $this->callInstaller('importT3DFile', [$relativeExtensionPath]);
            $this->callInstaller('emitAfterExtensionInstallSignal', [$extensionKey]);
        }
    }

public function setupExtensions ( array $ packages ) { foreach ( $ packages as $ package ) { $ this -> extensionFactory -> getExtensionStructure ( $ package ) -> fix ( ) ; $ this -> callInstaller ( 'importInitialFiles' , [ PathUtility :: stripPathSitePrefix ( $ package -> getPackagePath ( ) ) , $ package -> getPackageKey ( ) ] ) ; $ this -> extensionConfiguration -> saveDefaultConfiguration ( $ package -> getPackageKey ( ) ) ; } $ this -> schemaService -> updateSchema ( SchemaUpdateType :: expandSchemaUpdateTypes ( [ 'safe' ] ) ) ; foreach ( $ packages as $ package ) { $ relativeExtensionPath = PathUtility :: stripPathSitePrefix ( $ package -> getPackagePath ( ) ) ; $ extensionKey = $ package -> getPackageKey ( ) ; $ this -> callInstaller ( 'importStaticSqlFile' , [ $ relativeExtensionPath ] ) ; $ this -> callInstaller ( 'importT3DFile' , [ $ relativeExtensionPath ] ) ; $ this -> callInstaller ( 'emitAfterExtensionInstallSignal' , [ $ extensionKey ] ) ; } }

Performs all necessary operations to integrate extensions into the system.
Instead of using buggy TYPO3 API, we created our own instead.
This might be removed, once the bug is fixed in TYPO3.

@param PackageInterface[] $packages

Performs all necessary operations to integrate extensions into the system . Instead of using buggy TYPO3 API we created our own instead . This might be removed once the bug is fixed in TYPO3 .


public function getStructure()
    {
        $structure = $this->getDefaultStructureDefinition();
        $structure['children'] = $this->appendStructureDefinition($structure['children'], $this->createExtensionStructureDefinition($this->packageManager->getActivePackages()));

        return new StructureFacade(new RootNode($structure));
    }

public function getStructure ( ) { $ structure = $ this -> getDefaultStructureDefinition ( ) ; $ structure [ 'children' ] = $ this -> appendStructureDefinition ( $ structure [ 'children' ] , $ this -> createExtensionStructureDefinition ( $ this -> packageManager -> getActivePackages ( ) ) ) ; return new StructureFacade ( new RootNode ( $ structure ) ) ; }

Get default structure object hierarchy

@throws \TYPO3\CMS\Install\FolderStructure\Exception\RootNodeException
@throws \TYPO3\CMS\Install\FolderStructure\Exception\InvalidArgumentException
@return StructureFacade


public function getExtensionStructure(PackageInterface $package)
    {
        $structure = [
            'name' => substr(PATH_site, 0, -1),
            'targetPermission' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'],
            'children' => $this->appendStructureDefinition([], $this->createExtensionStructureDefinition([$package])),
        ];

        return new StructureFacade(new RootNode($structure));
    }

public function getExtensionStructure ( PackageInterface $ package ) { $ structure = [ 'name' => substr ( PATH_site , 0 , - 1 ) , 'targetPermission' => $ GLOBALS [ 'TYPO3_CONF_VARS' ] [ 'SYS' ] [ 'folderCreateMask' ] , 'children' => $ this -> appendStructureDefinition ( [ ] , $ this -> createExtensionStructureDefinition ( [ $ package ] ) ) , ] ; return new StructureFacade ( new RootNode ( $ structure ) ) ; }

Creates the folder structure for one extension

@param PackageInterface $package
@return StructureFacade


private function createExtensionStructureDefinition(array $packages)
    {
        $structureBase = [];
        foreach ($packages as $package) {
            $extensionConfiguration = $this->packageManager->getExtensionConfiguration($package);

            if (isset($extensionConfiguration['uploadfolder']) && (bool)$extensionConfiguration['uploadfolder']) {
                $structureBase[] = $this->getExtensionUploadDirectory($package->getPackageKey());
            }

            if (!empty($extensionConfiguration['createDirs'])) {
                foreach (explode(',', $extensionConfiguration['createDirs']) as $directoryToCreate) {
                    $absolutePath = GeneralUtility::getFileAbsFileName(trim($directoryToCreate));
                    // Only create valid paths.
                    if (!empty($absolutePath)) {
                        $structureBase[] = $this->getDirectoryNodeByPath(PathUtility::stripPathSitePrefix($absolutePath));
                    }
                }
            }
        }

        return $structureBase;
    }

private
function
createExtensionStructureDefinition
(
array
$
packages
)
{
$
structureBase
=
[
]
;
foreach
(
$
packages
as
$
package
)
{
$
extensionConfiguration
=
$
this
->
packageManager
->
getExtensionConfiguration
(
$
package
)
;
if
(
isset
(
$
extensionConfiguration
[
'uploadfolder'
]
)
&&
(
bool
)
$
extensionConfiguration
[
'uploadfolder'
]
)
{
$
structureBase
[
]
=
$
this
->
getExtensionUploadDirectory
(
$
package
->
getPackageKey
(
)
)
;
}
if
(
!
empty
(
$
extensionConfiguration
[
'createDirs'
]
)
)
{
foreach
(
explode
(
','
,
$
extensionConfiguration
[
'createDirs'
]
)
as
$
directoryToCreate
)
{
$
absolutePath
=
GeneralUtility
::
getFileAbsFileName
(
trim
(
$
directoryToCreate
)
)
;
// Only create valid paths.
if
(
!
empty
(
$
absolutePath
)
)
{
$
structureBase
[
]
=
$
this
->
getDirectoryNodeByPath
(
PathUtility
::
stripPathSitePrefix
(
$
absolutePath
)
)
;
}
}
}
}
return
$
structureBase
;
}

Default definition of folder and file structure with dynamic
permission settings

@param PackageInterface[] $packages
@return array


protected function generateXmlForClassName($className, \SimpleXMLElement $xmlRootNode)
    {
        $reflectionClass = new \ReflectionClass($className);
        $tagName = $this->getTagNameForClass($className);
        $xsdElement = $xmlRootNode->addChild('xsd:element');
        $xsdElement['name'] = $tagName;
        $this->docCommentParser->parseDocComment($reflectionClass->getDocComment());
        $this->addDocumentation($this->docCommentParser->getDescription(), $xsdElement);

        $xsdComplexType = $xsdElement->addChild('xsd:complexType');
        $xsdComplexType['mixed'] = 'true';
        $xsdSequence = $xsdComplexType->addChild('xsd:sequence');
        $xsdAny = $xsdSequence->addChild('xsd:any');
        $xsdAny['minOccurs'] = '0';
        $xsdAny['maxOccurs'] = 'unbounded';

        $this->addAttributes($className, $xsdComplexType);
    }

protected function generateXmlForClassName ( $ className , \ SimpleXMLElement $ xmlRootNode ) { $ reflectionClass = new \ ReflectionClass ( $ className ) ; $ tagName = $ this -> getTagNameForClass ( $ className ) ; $ xsdElement = $ xmlRootNode -> addChild ( 'xsd:element' ) ; $ xsdElement [ 'name' ] = $ tagName ; $ this -> docCommentParser -> parseDocComment ( $ reflectionClass -> getDocComment ( ) ) ; $ this -> addDocumentation ( $ this -> docCommentParser -> getDescription ( ) , $ xsdElement ) ; $ xsdComplexType = $ xsdElement -> addChild ( 'xsd:complexType' ) ; $ xsdComplexType [ 'mixed' ] = 'true' ; $ xsdSequence = $ xsdComplexType -> addChild ( 'xsd:sequence' ) ; $ xsdAny = $ xsdSequence -> addChild ( 'xsd:any' ) ; $ xsdAny [ 'minOccurs' ] = '0' ; $ xsdAny [ 'maxOccurs' ] = 'unbounded' ; $ this -> addAttributes ( $ className , $ xsdComplexType ) ; }

Generate the XML Schema for a given class name.

@param string $className Class name to generate the schema for.
@param \SimpleXMLElement $xmlRootNode XML root node where the xsd:element is appended.
@return void

Generate the XML Schema for a given class name .


protected function addAttributes($className, \SimpleXMLElement $xsdElement)
    {
        /** @var AbstractViewHelper $viewHelper */
        $viewHelper = $this->objectManager->get($className);
        $argumentDefinitions = $viewHelper->prepareArguments();

        /** @var $argumentDefinition ArgumentDefinition */
        foreach ($argumentDefinitions as $argumentDefinition) {
            $xsdAttribute = $xsdElement->addChild('xsd:attribute');
            $xsdAttribute['type'] = 'xsd:string';
            $xsdAttribute['name'] = $argumentDefinition->getName();
            $this->addDocumentation($argumentDefinition->getDescription(), $xsdAttribute);
            if ($argumentDefinition->isRequired()) {
                $xsdAttribute['use'] = 'required';
            }
        }
    }

Add attribute descriptions to a given tag.
Initializes the view helper and its arguments, and then reads out the list of arguments.

@param string $className Class name where to add the attribute descriptions
@param \SimpleXMLElement $xsdElement XML element to add the attributes to.
@return void

Add attribute descriptions to a given tag . Initializes the view helper and its arguments and then reads out the list of arguments .


protected function addDocumentation($documentation, \SimpleXMLElement $xsdParentNode)
    {
        $xsdAnnotation = $xsdParentNode->addChild('xsd:annotation');
        $this->addChildWithCData($xsdAnnotation, 'xsd:documentation', $documentation);
    }

protected function addDocumentation ( $ documentation , \ SimpleXMLElement $ xsdParentNode ) { $ xsdAnnotation = $ xsdParentNode -> addChild ( 'xsd:annotation' ) ; $ this -> addChildWithCData ( $ xsdAnnotation , 'xsd:documentation' , $ documentation ) ; }

Add documentation XSD to a given XML node

@param string $documentation Documentation string to add.
@param \SimpleXMLElement $xsdParentNode Node to add the documentation to
@return void


protected function resolveCommandMethodName()
    {
        $commandMethodName = $this->request->getControllerCommandName() . 'Command';
        if (!is_callable([$this, $commandMethodName])) {
            throw new NoSuchCommandException(sprintf('A command method "%s()" does not exist in controller "%s".', $commandMethodName, get_class($this)), 1300902143);
        }

        return $commandMethodName;
    }

Resolves and checks the current command method name

Note: The resulting command method name might not have the correct case, which isn't a problem because PHP is
case insensitive regarding method names.

@throws NoSuchCommandException
@return string Method name of the current command


protected function mapRequestArgumentsToControllerArguments()
    {
        /** @var Argument $argument */
        foreach ($this->arguments as $argument) {
            $argumentName = $argument->getName();
            if ($this->request->hasArgument($argumentName)) {
                $argument->setValue($this->request->getArgument($argumentName));
                continue;
            }
            if (!$argument->isRequired()) {
                continue;
            }
            $argumentValue = null;
            $commandArgumentDefinition = $this->objectManager->get(CommandArgumentDefinition::class, $argumentName, true, null);
            while ($argumentValue === null) {
                $argumentValue = $this->output->ask(sprintf('<comment>Please specify the required argument "%s":</comment> ', $commandArgumentDefinition->getDashedName()));
            }
            $argument->setValue($argumentValue);
        }
    }

Maps arguments delivered by the request object to the local controller arguments.

Maps arguments delivered by the request object to the local controller arguments .


protected function callCommandMethod()
    {
        $preparedArguments = [];
        foreach ($this->arguments as $argument) {
            $preparedArguments[] = $argument->getValue();
        }
        $commandResult = $this->{$this->commandMethodName}(...$preparedArguments);
        if ($commandResult !== null) {
            $this->outputLine((string)$commandResult);
            $this->output->getSymfonyConsoleOutput()->getErrorOutput()->writeln('<warning>Returning a string from a command method is deprecated.</warning>');
            $this->output->getSymfonyConsoleOutput()->getErrorOutput()->writeln('<warning>Please use $this->outputLine() instead.</warning>');
        }
    }

Calls the specified command method and passes the arguments.

If the command returns a string, it is appended to the content in the
response object. If the command doesn't return anything and a valid
view exists, the view is rendered automatically.

Calls the specified command method and passes the arguments .


protected function createDefaultLogger($minimumLevel = LogLevel::DEBUG, $options = [])
    {
        $options['output'] = $this->output->getSymfonyConsoleOutput();
        $logger = new Logger(get_class($this));
        $logger->addWriter($minimumLevel, new ConsoleWriter($options));

        return $logger;
    }

protected function createDefaultLogger ( $ minimumLevel = LogLevel :: DEBUG , $ options = [ ] ) { $ options [ 'output' ] = $ this -> output -> getSymfonyConsoleOutput ( ) ; $ logger = new Logger ( get_class ( $ this ) ) ; $ logger -> addWriter ( $ minimumLevel , new ConsoleWriter ( $ options ) ) ; return $ logger ; }

Creates a logger which outputs to the console

@param int $minimumLevel Minimum log level that should trigger output
@param array $options Additional options for the console writer
@return LoggerInterface


public function getShortDescription(): string
    {
        $lines = explode(LF, $this->commandReflection->getDescription());

        return !empty($lines) ? trim($lines[0]) : '<no description available>';
    }

public
function
getShortDescription
(
)
:
string
{
$
lines
=
explode
(
LF
,
$
this
->
commandReflection
->
getDescription
(
)
)
;
return
!
empty
(
$
lines
)
trim
(
$
lines
[
0
]
)
:
'<no description available>'
;
}

Returns a short description of this command

@return string A short description


private function parseDefinitions(): array
    {
        $definitions = [];
        $reader = new AnnotationReader();
        $method = new \ReflectionMethod($this->controllerClassName, $this->controllerCommandMethod);
        foreach ($reader->getMethodAnnotations($method) as $annotation) {
            if ($annotation instanceof Option) {
                $definitions['Option'][] = $annotation;
            }
            if ($annotation instanceof Argument) {
                $definitions['Argument'][] = $annotation;
            }
            if ($annotation instanceof Validate) {
                $definitions['Validate'] = $annotation;
            }
        }

        return $definitions;
    }

private function parseDefinitions ( ) : array { $ definitions = [ ] ; $ reader = new AnnotationReader ( ) ; $ method = new \ ReflectionMethod ( $ this -> controllerClassName , $ this -> controllerCommandMethod ) ; foreach ( $ reader -> getMethodAnnotations ( $ method ) as $ annotation ) { if ( $ annotation instanceof Option ) { $ definitions [ 'Option' ] [ ] = $ annotation ; } if ( $ annotation instanceof Argument ) { $ definitions [ 'Argument' ] [ ] = $ annotation ; } if ( $ annotation instanceof Validate ) { $ definitions [ 'Validate' ] = $ annotation ; } } return $ definitions ; }

Get parsed annotations if command has any

@throws InvalidArgumentException
@return array


public function getSynopsis($short = false): string
    {
        $key = $short ? 'short' : 'long';
        if (isset($this->synopsis[$key])) {
            return $this->synopsis[$key];
        }

        $elements = [];
        if ($short && $this->hasOptions()) {
            $elements[] = '[options]';
        } elseif (!$short) {
            foreach ($this->getOptions() as $argumentDefinition) {
                $value = '';
                if ($argumentDefinition->acceptsValue()) {
                    $value = ' ' . strtoupper($argumentDefinition->getOptionName());
                }
                $elements[] = sprintf('[%s%s]', $argumentDefinition->getDashedName(), $value);
            }
        }
        if (count($elements) && $this->hasRequiredArguments()) {
            $elements[] = '[--]';
        }
        foreach ($this->getArguments() as $argumentDefinition) {
            $elements[] = '<' . $argumentDefinition->getName() . '>';
        }

        return $this->synopsis[$key] = implode(' ', $elements);
    }

public
function
getSynopsis
(
$
short
=
false
)
:
string
{
$
key
=
$
short
'short'
:
'long'
;
if
(
isset
(
$
this
->
synopsis
[
$
key
]
)
)
{
return
$
this
->
synopsis
[
$
key
]
;
}
$
elements
=
[
]
;
if
(
$
short
&&
$
this
->
hasOptions
(
)
)
{
$
elements
[
]
=
'[options]'
;
}
elseif
(
!
$
short
)
{
foreach
(
$
this
->
getOptions
(
)
as
$
argumentDefinition
)
{
$
value
=
''
;
if
(
$
argumentDefinition
->
acceptsValue
(
)
)
{
$
value
=
' '
.
strtoupper
(
$
argumentDefinition
->
getOptionName
(
)
)
;
}
$
elements
[
]
=
sprintf
(
'[%s%s]'
,
$
argumentDefinition
->
getDashedName
(
)
,
$
value
)
;
}
}
if
(
count
(
$
elements
)
&&
$
this
->
hasRequiredArguments
(
)
)
{
$
elements
[
]
=
'[--]'
;
}
foreach
(
$
this
->
getArguments
(
)
as
$
argumentDefinition
)
{
$
elements
[
]
=
'<'
.
$
argumentDefinition
->
getName
(
)
.
'>'
;
}
return
$
this
->
synopsis
[
$
key
]
=
implode
(
' '
,
$
elements
)
;
}

Get synopsis for this command, either short or long

@param bool $short
@return string

Get synopsis for this command either short or long


public function removeStep($stepIdentifier)
    {
        $removedOccurrences = 0;
        foreach ($this->steps as $previousStepIdentifier => $steps) {
            foreach ($steps as $index => $step) {
                if ($step->getIdentifier() === $stepIdentifier) {
                    unset($this->steps[$previousStepIdentifier][$index]);
                    $removedOccurrences ++;
                }
            }
        }
        if ($removedOccurrences === 0) {
            throw new Exception(sprintf('Cannot remove sequence step with identifier "%s" because no such step exists in the given sequence.', $stepIdentifier), 1322591669);
        }
    }

Removes all occurrences of the specified step from this sequence

@param string $stepIdentifier
@throws Exception
@return void


public function invoke(Bootstrap $bootstrap)
    {
        if (isset($this->steps['start'])) {
            foreach ($this->steps['start'] as $step) {
                $this->invokeStep($step, $bootstrap);
            }
        }
    }

public function invoke ( Bootstrap $ bootstrap ) { if ( isset ( $ this -> steps [ 'start' ] ) ) { foreach ( $ this -> steps [ 'start' ] as $ step ) { $ this -> invokeStep ( $ step , $ bootstrap ) ; } } }

Executes all steps of this sequence

@param Bootstrap $bootstrap
@throws StepFailedException
@return void


protected function invokeStep(Step $step, Bootstrap $bootstrap)
    {
        $identifier = $step->getIdentifier();
        try {
            $step($bootstrap);
        } catch (\Throwable $e) {
            throw new StepFailedException($step, $e);
        }
        if (isset($this->steps[$identifier])) {
            foreach ($this->steps[$identifier] as $followingStep) {
                $this->invokeStep($followingStep, $bootstrap);
            }
        }
    }

protected function invokeStep ( Step $ step , Bootstrap $ bootstrap ) { $ identifier = $ step -> getIdentifier ( ) ; try { $ step ( $ bootstrap ) ; } catch ( \ Throwable $ e ) { throw new StepFailedException ( $ step , $ e ) ; } if ( isset ( $ this -> steps [ $ identifier ] ) ) { foreach ( $ this -> steps [ $ identifier ] as $ followingStep ) { $ this -> invokeStep ( $ followingStep , $ bootstrap ) ; } } }

Invokes a single step of this sequence and also invokes all steps registered
to be executed after the given step.

@param Step $step The step to invoke
@param Bootstrap $bootstrap
@throws StepFailedException
@return void

Invokes a single step of this sequence and also invokes all steps registered to be executed after the given step .


private function setupExtensions(array $packages, $verbose = false)
    {
        $extensionSetupResultRenderer = new ExtensionSetupResultRenderer($this->signalSlotDispatcher);

        $extensionSetup = new ExtensionSetup(
            new ExtensionFactory($this->packageManager),
            $this->getExtensionInstaller()
        );

        $extensionSetup->setupExtensions($packages);
        $extensionKeysAsString = implode('", "', array_map(function (PackageInterface $package) {
            return $package->getPackageKey();
        }, $packages));
        if (count($packages) === 1) {
            $this->outputLine('<info>Extension "%s" is now set up.</info>', [$extensionKeysAsString]);
        } else {
            $this->outputLine('<info>Extensions "%s" are now set up.</info>', [$extensionKeysAsString]);
        }

        if ($verbose) {
            $this->outputLine();
            $extensionSetupResultRenderer->renderSchemaResult($this->output);
            $extensionSetupResultRenderer->renderExtensionDataImportResult($this->output);
            $extensionSetupResultRenderer->renderExtensionFileImportResult($this->output);
            $extensionSetupResultRenderer->renderImportedStaticDataResult($this->output);
        }
    }

Performs all necessary operations to integrate an extension into the system.
To do so, we avoid buggy TYPO3 API and use our own instead.

@param PackageInterface[] $packages
@param bool $verbose Whether or not to output results

Performs all necessary operations to integrate an extension into the system . To do so we avoid buggy TYPO3 API and use our own instead .


public function setupActiveCommand()
    {
        $verbose = $this->output->getSymfonyConsoleOutput()->isVerbose();
        $this->setupExtensions($this->packageManager->getActivePackages(), $verbose);
    }

public function setupActiveCommand ( ) { $ verbose = $ this -> output -> getSymfonyConsoleOutput ( ) -> isVerbose ( ) ; $ this -> setupExtensions ( $ this -> packageManager -> getActivePackages ( ) , $ verbose ) ; }

Set up all active extensions

Sets up all extensions that are marked as active in the system.

This command is especially useful for deployment, where extensions
are already marked as active, but have not been set up yet or might have changed. It ensures every necessary
setup step for the (changed) extensions is performed.
As an additional benefit no caches are flushed, which significantly improves performance of this command
and avoids unnecessary cache clearing.

@see typo3_console:extension:setup
@see typo3_console:install:generatepackagestates
@see typo3_console:cache:flush


public function removeInactiveCommand($force = false)
    {
        $this->outputLine('<warning>This command is deprecated and will be removed with TYPO3 Console 6.0</warning>');
        if ($force) {
            $activePackages = $this->packageManager->getActivePackages();
            $this->packageManager->scanAvailablePackages();
            foreach ($this->packageManager->getAvailablePackages() as $package) {
                if (empty($activePackages[$package->getPackageKey()])) {
                    $this->packageManager->unregisterPackage($package);
                    if (is_dir($package->getPackagePath())) {
                        GeneralUtility::flushDirectory($package->getPackagePath());
                        $removedPaths[] = PathUtility::stripPathSitePrefix($package->getPackagePath());
                    }
                }
            }
            $this->packageManager->forceSortAndSavePackageStates();
            if (!empty($removedPaths)) {
                $this->outputLine('<info>The following directories have been removed:</info>' . chr(10) . implode(chr(10), $removedPaths));
            } else {
                $this->outputLine('<info>Nothing was removed</info>');
            }
        } else {
            $this->outputLine('<warning>Operation not confirmed and has been skipped</warning>');
            $this->quit(1);
        }
    }

Removes all extensions that are not marked as active

Directories of inactive extension are <comment>removed</comment> from typo3/sysext and typo3conf/ext.
This is a one way command with no way back. Don't blame anybody if this command destroys your data.
<comment>Handle with care!</comment>

This command is deprecated.
Instead of adding extensions and then removing them, just don't add them in the first place.

@param bool $force The option has to be specified, otherwise nothing happens


public function listCommand($active = false, $inactive = false, $raw = false)
    {
        $extensionInformation = [];
        if (!$active || $inactive) {
            $this->emitPackagesMayHaveChangedSignal();
            $packages = $this->packageManager->getAvailablePackages();
        } else {
            $packages = $this->packageManager->getActivePackages();
        }
        foreach ($packages as $package) {
            if ($inactive && $this->packageManager->isPackageActive($package->getPackageKey())) {
                continue;
            }
            $metaData = $package->getPackageMetaData();
            $extensionInformation[] = [
                'package_key' => $package->getPackageKey(),
                'version' => $metaData->getVersion(),
                'description' => $metaData->getDescription(),
            ];
        }
        if ($raw) {
            $this->outputLine('%s', [implode(PHP_EOL, array_column($extensionInformation, 'package_key'))]);
        } else {
            $this->output->outputTable(
                $extensionInformation,
                ['Extension key', 'Version', 'Description']
            );
        }
    }

public
function
listCommand
(
$
active
=
false
,
$
inactive
=
false
,
$
raw
=
false
)
{
$
extensionInformation
=
[
]
;
if
(
!
$
active
||
$
inactive
)
{
$
this
->
emitPackagesMayHaveChangedSignal
(
)
;
$
packages
=
$
this
->
packageManager
->
getAvailablePackages
(
)
;
}
else
{
$
packages
=
$
this
->
packageManager
->
getActivePackages
(
)
;
}
foreach
(
$
packages
as
$
package
)
{
if
(
$
inactive
&&
$
this
->
packageManager
->
isPackageActive
(
$
package
->
getPackageKey
(
)
)
)
{
continue
;
}
$
metaData
=
$
package
->
getPackageMetaData
(
)
;
$
extensionInformation
[
]
=
[
'package_key'
=>
$
package
->
getPackageKey
(
)
,
'version'
=>
$
metaData
->
getVersion
(
)
,
'description'
=>
$
metaData
->
getDescription
(
)
,
]
;
}
if
(
$
raw
)
{
$
this
->
outputLine
(
'%s'
,
[
implode
(
PHP_EOL
,
array_column
(
$
extensionInformation
,
'package_key'
)
)
]
)
;
}
else
{
$
this
->
output
->
outputTable
(
$
extensionInformation
,
[
'Extension key'
,
'Version'
,
'Description'
]
)
;
}
}

List extensions that are available in the system

@param bool $active Only show active extensions
@param bool $inactive Only show inactive extensions
@param bool $raw Enable machine readable output (just extension keys separated by line feed)


public function forceFlushDatabaseCacheTables()
    {
        // Get all table names from Default connection starting with 'cf_' and truncate them
        $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
        $connection = $connectionPool->getConnectionByName('Default');
        $tableNames = $connection->getSchemaManager()->listTableNames();
        foreach ($tableNames as $tableName) {
            if ($tableName === 'cache_treelist' || strpos($tableName, 'cf_') === 0) {
                $connection->truncate($tableName);
            }
        }
        // Check tables on other connections
        $remappedTables = isset($GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping'])
            ? array_keys((array)$GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping'])
            : [];
        foreach ($remappedTables as $tableName) {
            if ($tableName === 'cache_treelist' || strpos($tableName, 'cf_') === 0) {
                $connectionPool->getConnectionForTable($tableName)->truncate($tableName);
            }
        }
    }

Truncate all DB tables prefixed with 'cf_'

Truncate all DB tables prefixed with cf_


public function handleError($errorLevel, $errorMessage, $errorFile, $errorLine)
    {
        if (error_reporting() === 0) {
            return;
        }

        $errorLevels = [
            E_WARNING => 'Warning',
            E_NOTICE => 'Notice',
            E_USER_ERROR => 'User Error',
            E_USER_WARNING => 'User Warning',
            E_USER_NOTICE => 'User Notice',
            E_STRICT => 'Runtime Notice',
            E_RECOVERABLE_ERROR => 'Catchable Fatal Error',
        ];

        if (in_array($errorLevel, (array)$this->exceptionalErrors, true)) {
            throw new \TYPO3\CMS\Core\Error\Exception($errorLevels[$errorLevel] . ': ' . $errorMessage . ' in ' . $errorFile . ' line ' . $errorLine, 1);
        }
    }

public
function
handleError
(
$
errorLevel
,
$
errorMessage
,
$
errorFile
,
$
errorLine
)
{
if
(
error_reporting
(
)
===
0
)
{
return
;
}
$
errorLevels
=
[
E_WARNING
=>
'Warning'
,
E_NOTICE
=>
'Notice'
,
E_USER_ERROR
=>
'User Error'
,
E_USER_WARNING
=>
'User Warning'
,
E_USER_NOTICE
=>
'User Notice'
,
E_STRICT
=>
'Runtime Notice'
,
E_RECOVERABLE_ERROR
=>
'Catchable Fatal Error'
,
]
;
if
(
in_array
(
$
errorLevel
,
(
array
)
$
this
->
exceptionalErrors
,
true
)
)
{
throw
new
\
TYPO3
\
CMS
\
Core
\
Error
\
Exception
(
$
errorLevels
[
$
errorLevel
]
.
': '
.
$
errorMessage
.
' in '
.
$
errorFile
.
' line '
.
$
errorLine
,
1
)
;
}
}

Handles an error by converting it into an exception.

If error reporting is disabled, either in the php.ini or temporarily through
the shut-up operator "@", no exception will be thrown.

@param int $errorLevel The error level - one of the E_* constants
@param string $errorMessage The error message
@param string $errorFile Name of the file the error occurred in
@param int $errorLine Line number where the error occurred
@throws \TYPO3\CMS\Core\Error\Exception with the data passed to this method
@return void

Handles an error by converting it into an exception .


public static function register(Event $event, ScriptDispatcher $scriptDispatcher)
    {
        $scriptDispatcher->addInstallerScript(new PopulateCommandConfiguration(), 70);
        if (!class_exists(\TYPO3\CMS\Core\Composer\InstallerScripts::class)
            && !class_exists(\Helhum\Typo3ComposerSetup\Composer\InstallerScripts::class)
            && $event->getComposer()->getRepositoryManager()->getLocalRepository()->findPackage('typo3/cms', new EmptyConstraint()) !== null
        ) {
            // @deprecated can be removed once TYPO3 8 support is removed
            $scriptDispatcher->addInstallerScript(new WebDirectory());
            $scriptDispatcher->addInstallerScript(new AutoloadConnector());
        }
    }

Allows to register one or more script objects that implement this interface
This will be called in the Plugin right before the scripts are executed.

@param Event $event
@param ScriptDispatcher $scriptDispatcher
@return void

Allows to register one or more script objects that implement this interface This will be called in the Plugin right before the scripts are executed .


public function select($question, $choices, $default = null, $multiSelect = false, $attempts = false)
    {
        $question = (new ChoiceQuestion($question, $choices, $default))
            ->setMultiselect($multiSelect)
            ->setMaxAttempts($attempts)
            ->setErrorMessage('Value "%s" is invalid');

        return $this->getQuestionHelper()->ask($this->getInput(), $this->output, $question);
    }

public
function
select
(
$
question
,
$
choices
,
$
default
=
,
$
multiSelect
=
false
,
$
attempts
=
false
)
{
$
question
=
(
new
ChoiceQuestion
(
$
question
,
$
choices
,
$
default
)
)
->
setMultiselect
(
$
multiSelect
)
->
setMaxAttempts
(
$
attempts
)
->
setErrorMessage
(
'Value "%s" is invalid'
)
;
return
$
this
->
getQuestionHelper
(
)
->
ask
(
$
this
->
getInput
(
)
,
$
this
->
output
,
$
question
)
;
}

Asks the user to select a value

@param string|array $question The question to ask. If an array each array item is turned into one line of a multi-line question
@param array $choices List of choices to pick from
@param bool $default The default answer if the user enters nothing
@param bool $multiSelect If true the result will be an array with the selected options. Multiple options can be given separated by commas
@param bool|int $attempts Max number of times to ask before giving up (false by default, which means infinite)
@throws \Symfony\Component\Console\Exception\RuntimeException
@throws \Symfony\Component\Console\Exception\InvalidArgumentException
@return int|string|array The selected value or values (the key of the choices array)


public function ask($question, $default = null, array $autocomplete = null)
    {
        $question = (new Question($question, $default))
            ->setAutocompleterValues($autocomplete);

        return $this->getQuestionHelper()->ask($this->getInput(), $this->output, $question);
    }

public function ask ( $ question , $ default = null , array $ autocomplete = null ) { $ question = ( new Question ( $ question , $ default ) ) -> setAutocompleterValues ( $ autocomplete ) ; return $ this -> getQuestionHelper ( ) -> ask ( $ this -> getInput ( ) , $ this -> output , $ question ) ; }

Asks a question to the user

@param string|array $question The question to ask. If an array each array item is turned into one line of a multi-line question
@param string $default The default answer if none is given by the user
@param array $autocomplete List of values to autocomplete. This only works if "stty" is installed
@throws \Symfony\Component\Console\Exception\InvalidArgumentException
@throws \Symfony\Component\Console\Exception\LogicException
@throws \Symfony\Component\Console\Exception\RuntimeException
@return string The user answer


public function askConfirmation($question, $default = true)
    {
        $question = new ConfirmationQuestion($question, $default);

        return $this->getQuestionHelper()->ask($this->getInput(), $this->output, $question);
    }

public function askConfirmation ( $ question , $ default = true ) { $ question = new ConfirmationQuestion ( $ question , $ default ) ; return $ this -> getQuestionHelper ( ) -> ask ( $ this -> getInput ( ) , $ this -> output , $ question ) ; }

Asks a confirmation to the user.

The question will be asked until the user answers by nothing, yes, or no.

@param string|array $question The question to ask. If an array each array item is turned into one line of a multi-line question
@param bool $default The default answer if the user enters nothing
@return bool true if the user has confirmed, false otherwise


public function askHiddenResponse($question, $fallback = true)
    {
        $question = (new Question($question))
            ->setHidden(true)
            ->setHiddenFallback($fallback);

        return $this->getQuestionHelper()->ask($this->getInput(), $this->output, $question);
    }

public function askHiddenResponse ( $ question , $ fallback = true ) { $ question = ( new Question ( $ question ) ) -> setHidden ( true ) -> setHiddenFallback ( $ fallback ) ; return $ this -> getQuestionHelper ( ) -> ask ( $ this -> getInput ( ) , $ this -> output , $ question ) ; }

Asks a question to the user, the response is hidden

@param string|array $question The question. If an array each array item is turned into one line of a multi-line question
@param bool $fallback In case the response can not be hidden, whether to fallback on non-hidden question or not
@throws \Symfony\Component\Console\Exception\LogicException
@throws \Symfony\Component\Console\Exception\RuntimeException
@return string The answer

Asks a question to the user the response is hidden


public function askAndValidate($question, $validator, $attempts = false, $default = null, array $autocomplete = null)
    {
        $question = (new Question($question, $default))
            ->setValidator($validator)
            ->setMaxAttempts($attempts)
            ->setAutocompleterValues($autocomplete);

        return $this->getQuestionHelper()->ask($this->getInput(), $this->output, $question);
    }

public function askAndValidate ( $ question , $ validator , $ attempts = false , $ default = null , array $ autocomplete = null ) { $ question = ( new Question ( $ question , $ default ) ) -> setValidator ( $ validator ) -> setMaxAttempts ( $ attempts ) -> setAutocompleterValues ( $ autocomplete ) ; return $ this -> getQuestionHelper ( ) -> ask ( $ this -> getInput ( ) , $ this -> output , $ question ) ; }

Asks for a value and validates the response

The validator receives the data to validate. It must return the
validated data when the data is valid and throw an exception
otherwise.

@param string|array $question The question to ask. If an array each array item is turned into one line of a multi-line question
@param callable $validator A PHP callback that gets a value and is expected to return the (transformed) value or throw an exception if it wasn't valid
@param int|bool $attempts Max number of times to ask before giving up (false by default, which means infinite)
@param string $default The default answer if none is given by the user
@param array $autocomplete List of values to autocomplete. This only works if "stty" is installed
@throws \Symfony\Component\Console\Exception\RuntimeException
@throws \Symfony\Component\Console\Exception\LogicException
@throws \Symfony\Component\Console\Exception\InvalidArgumentException
@return mixed


public function askHiddenResponseAndValidate($question, $validator, $attempts = false, $fallback = true)
    {
        $question = (new Question($question))
            ->setValidator($validator)
            ->setMaxAttempts($attempts)
            ->setHidden(true)
            ->setHiddenFallback($fallback);

        return $this->getQuestionHelper()->ask($this->getInput(), $this->output, $question);
    }

public function askHiddenResponseAndValidate ( $ question , $ validator , $ attempts = false , $ fallback = true ) { $ question = ( new Question ( $ question ) ) -> setValidator ( $ validator ) -> setMaxAttempts ( $ attempts ) -> setHidden ( true ) -> setHiddenFallback ( $ fallback ) ; return $ this -> getQuestionHelper ( ) -> ask ( $ this -> getInput ( ) , $ this -> output , $ question ) ; }

Asks for a value, hide and validates the response

The validator receives the data to validate. It must return the
validated data when the data is valid and throw an exception
otherwise.

@param string|array $question The question to ask. If an array each array item is turned into one line of a multi-line question
@param callable $validator A PHP callback that gets a value and is expected to return the (transformed) value or throw an exception if it wasn't valid
@param int|bool $attempts Max number of times to ask before giving up (false by default, which means infinite)
@param bool $fallback In case the response can not be hidden, whether to fallback on non-hidden question or not
@throws \Symfony\Component\Console\Exception\InvalidArgumentException
@throws \Symfony\Component\Console\Exception\LogicException
@throws \Symfony\Component\Console\Exception\RuntimeException
@return string The response

Asks for a value hide and validates the response


protected function execute(InputInterface $input, OutputInterface $output)
    {
        $helper = new DescriptorHelper();
        $helper->register('txt', new TextDescriptor());
        $helper->describe($output, $this->getApplication(), [
            'format' => $input->getOption('format'),
            'raw_text' => $input->getOption('raw'),
            'show_unavailable' => $input->getOption('all'),
            'namespace' => $input->getArgument('namespace'),
            'screen_width' => (new Terminal())->getWidth() - 4,
        ]);
        $application = $this->getApplication();
        if (!$application instanceof Application) {
            return 0;
        }
        if (!$input->getArgument('namespace') && !$application->isFullyCapable() && !$input->getOption('all')) {
            $outputHelper = new SymfonyStyle($input, $output);
            $messages = [
                '',
                sprintf(
                    '<comment>TYPO3 %s.</comment>',
                    $application->hasErrors() ? 'has errors' : 'is not fully set up'
                ),
                '<comment>Command list is reduced to only show low level commands.</comment>',
                sprintf(
                    '<comment>Not listed commands will not work until %s.</comment>',
                    $application->hasErrors() ? 'the errors are fixed' : 'TYPO3 is set up'
                ),
                sprintf(
                    '<comment>Run "%s --all" to list all commands.</comment>',
                    $_SERVER['PHP_SELF']
                ),
            ];

            $outputHelper->getErrorStyle()->writeln($messages);
        }

        return null;
    }

Subclass Symfony list command to be able to register our own text descriptor

@param InputInterface $input
@param OutputInterface $output
@throws \Symfony\Component\Console\Exception\InvalidArgumentException
@return null|int null or 0 if everything went fine, or an error code


public function writeLog(\TYPO3\CMS\Core\Log\LogRecord $record)
    {
        $this->output->write(
            $this->wrapMessage(vsprintf($record->getMessage(), $record->getData()), $record->getLevel()),
            true
        );

        return $this;
    }

public function writeLog ( \ TYPO3 \ CMS \ Core \ Log \ LogRecord $ record ) { $ this -> output -> write ( $ this -> wrapMessage ( vsprintf ( $ record -> getMessage ( ) , $ record -> getData ( ) ) , $ record -> getLevel ( ) ) , true ) ; return $ this ; }

Writes the log record

@param \TYPO3\CMS\Core\Log\LogRecord $record Log record
@return \TYPO3\CMS\Core\Log\Writer\WriterInterface $this


public function getPerformedUpdateTypes()
    {
        $typesCount = [];
        foreach ($this->performedUpdates as $type => $performedUpdates) {
            $typesCount[$type] = count($performedUpdates);
        }

        return $typesCount;
    }

public function getPerformedUpdateTypes ( ) { $ typesCount = [ ] ; foreach ( $ this -> performedUpdates as $ type => $ performedUpdates ) { $ typesCount [ $ type ] = count ( $ performedUpdates ) ; } return $ typesCount ; }

Returns the list of performed update types including the count

@return array


public function addPerformedUpdates(SchemaUpdateType $schemaUpdateType, array $updates)
    {
        $this->performedUpdates[(string)$schemaUpdateType] = array_merge((array)$this->performedUpdates[(string)$schemaUpdateType], $updates);
    }

public function addPerformedUpdates ( SchemaUpdateType $ schemaUpdateType , array $ updates ) { $ this -> performedUpdates [ ( string ) $ schemaUpdateType ] = array_merge ( ( array ) $ this -> performedUpdates [ ( string ) $ schemaUpdateType ] , $ updates ) ; }

Adds to the number of updates performed for a schema update type

@param SchemaUpdateType $schemaUpdateType Schema update type
@param array $updates Updates performed


public function addErrors(SchemaUpdateType $schemaUpdateType, array $errors, array $statements = [])
    {
        $collectedErrors = [];
        foreach ($errors as $id => $error) {
            $collectedErrors[] = [
                'message' => $error,
                'statement' => $statements[$id],
            ];
        }
        $this->errors[(string)$schemaUpdateType] = array_merge((array)$this->errors[(string)$schemaUpdateType], $collectedErrors);
    }

public function addErrors ( SchemaUpdateType $ schemaUpdateType , array $ errors , array $ statements = [ ] ) { $ collectedErrors = [ ] ; foreach ( $ errors as $ id => $ error ) { $ collectedErrors [ ] = [ 'message' => $ error , 'statement' => $ statements [ $ id ] , ] ; } $ this -> errors [ ( string ) $ schemaUpdateType ] = array_merge ( ( array ) $ this -> errors [ ( string ) $ schemaUpdateType ] , $ collectedErrors ) ; }

Adds to the list of errors occurred for a schema update type

@param SchemaUpdateType $schemaUpdateType Schema update type
@param array $errors List of error messages
@param array $statements SQL Statements that were executed


public function removeCommand(array $paths, $force = false)
    {
        foreach ($paths as $path) {
            if (!$this->configurationService->localIsActive($path)) {
                $this->outputLine('<warning>It seems that configuration for path "%s" is overridden.</warning>', [$path]);
                $this->outputLine('<warning>Removing the new value might have no effect.</warning>');
            }
            if (!$force && $this->configurationService->hasLocal($path)) {
                $reallyDelete = $this->output->askConfirmation('Remove ' . $path . ' from system configuration (TYPO3_CONF_VARS)? (yes/<b>no</b>): ', false);
                if (!$reallyDelete) {
                    continue;
                }
            }
            $removed = $this->configurationService->removeLocal($path);
            if ($removed) {
                $this->outputLine('<info>Removed "%s" from system configuration.</info>', [$path]);
            } else {
                $this->outputLine('<warning>Path "%s" seems invalid or empty. Nothing done!</warning>', [$path]);
            }
        }
    }

Remove configuration option

Removes a system configuration option by path.

For this command to succeed, the configuration option(s) must be in
LocalConfiguration.php and not be overridden elsewhere.

Example: %command.full_name% DB,EXT/EXTCONF/realurl

@param array $paths Path to system configuration that should be removed. Multiple paths can be specified separated by comma
@param bool $force If set, does not ask for confirmation


public function showCommand($path)
    {
        $hasActive = $this->configurationService->hasActive($path);
        $hasLocal = $this->configurationService->hasLocal($path);
        if (!$hasActive && !$hasLocal) {
            $this->outputLine('<error>No configuration found for path "%s"</error>', [$path]);
            $this->quit(1);
        }
        $active = null;
        if ($hasActive) {
            $active = $this->configurationService->getActive($path);
        }
        if ($this->configurationService->localIsActive($path) && $hasActive) {
            $this->outputLine($this->consoleRenderer->render($active));
        } else {
            $local = null;
            if ($hasLocal) {
                $local = $this->configurationService->getLocal($path);
            }
            $this->outputLine($this->consoleRenderer->renderDiff($local, $active));
        }
    }

Show configuration value

Shows system configuration value by path.
If the currently active configuration differs from the value in LocalConfiguration.php
the difference between these values is shown.

Example: %command.full_name% DB

@param string $path Path to system configuration option


public function showActiveCommand($path, $json = false)
    {
        if (!$this->configurationService->hasActive($path)) {
            $this->outputLine('<error>No configuration found for path "%s"</error>', [$path]);
            $this->quit(1);
        }
        $active = $this->configurationService->getActive($path);
        $this->outputLine($this->consoleRenderer->render($active, $json));
    }

Show active configuration value

Shows active system configuration by path.
Shows the configuration value that is currently effective, no matter where and how it is set.

Example: %command.full_name% DB --json

@param string $path Path to system configuration
@param bool $json If set, the configuration is shown as JSON


public function showLocalCommand($path, $json = false)
    {
        if (!$this->configurationService->hasLocal($path)) {
            $this->outputLine('<error>No configuration found for path "%s"</error>', [$path]);
            $this->quit(1);
        }
        $active = $this->configurationService->getLocal($path);
        $this->outputLine($this->consoleRenderer->render($active, $json));
    }

Show local configuration value

Shows local configuration option value by path.
Shows the value which is stored in LocalConfiguration.php.
Note that this value could be overridden. Use typo3cms configuration:show <path> to see if this is the case.

Example: %command.full_name% DB

@param string $path Path to local system configuration
@param bool $json If set, the configuration is shown as JSON
@see typo3_console:configuration:show