Skip to content

Commit 79498d7

Browse files
mficzelBernhard Schmitt
andcommitted
Adjust Neos.Flow Core sub-context
Co-authored-by: Bernhard Schmitt <schmitt@sitegeist.de>
1 parent 4099bfa commit 79498d7

6 files changed

Lines changed: 157 additions & 46 deletions

File tree

Neos.Flow/Classes/Core/Booting/Scripts.php

Lines changed: 108 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
use Doctrine\Common\Annotations\AnnotationRegistry;
1515
use Neos\Cache\CacheFactoryInterface;
16+
use Neos\Cache\Frontend\PhpFrontend;
17+
use Neos\Cache\Frontend\VariableFrontend;
1618
use Neos\Flow\Annotations as Flow;
1719
use Neos\Flow\Cache\CacheFactory;
1820
use Neos\Flow\Cache\CacheManager;
@@ -30,6 +32,7 @@
3032
use Neos\Flow\Core\ProxyClassLoader;
3133
use Neos\Flow\Error\Debugger;
3234
use Neos\Flow\Error\ErrorHandler;
35+
use Neos\Flow\Error\ExceptionHandlerInterface;
3336
use Neos\Flow\Error\ProductionExceptionHandler;
3437
use Neos\Flow\Http\Helper\RequestInformationHelper;
3538
use Neos\Flow\Http\HttpRequestHandlerInterface;
@@ -40,8 +43,11 @@
4043
use Neos\Flow\ObjectManagement\CompileTimeObjectManager;
4144
use Neos\Flow\ObjectManagement\ObjectManager;
4245
use Neos\Flow\ObjectManagement\ObjectManagerInterface;
46+
use Neos\Flow\Package;
4347
use Neos\Flow\Package\FlowPackageInterface;
4448
use Neos\Flow\Package\GenericPackage;
49+
use Neos\Flow\Package\PackageInterface;
50+
use Neos\Flow\Package\PackageKeyAwareInterface;
4551
use Neos\Flow\Package\PackageManager;
4652
use Neos\Flow\Reflection\ReflectionService;
4753
use Neos\Flow\Reflection\ReflectionServiceFactory;
@@ -51,7 +57,6 @@
5157
use Neos\Utility\Files;
5258
use Neos\Utility\OpcodeCacheHelper;
5359
use Neos\Flow\Exception as FlowException;
54-
use Psr\Http\Message\RequestInterface;
5560

5661
/**
5762
* Initialization scripts for modules of the Flow package
@@ -73,7 +78,9 @@ class Scripts
7378
public static function initializeClassLoader(Bootstrap $bootstrap)
7479
{
7580
$proxyClassLoader = new ProxyClassLoader($bootstrap->getContext());
76-
spl_autoload_register([$proxyClassLoader, 'loadClass'], true, true);
81+
$proxyClassLoaderCallback = [$proxyClassLoader, 'loadClass'];
82+
/** @phpstan-ignore argument.type (should work out just fine) */
83+
spl_autoload_register($proxyClassLoaderCallback, true, true);
7784
$bootstrap->setEarlyInstance(ProxyClassLoader::class, $proxyClassLoader);
7885

7986
if (!self::useClassLoader($bootstrap)) {
@@ -94,7 +101,9 @@ public static function initializeClassLoader(Bootstrap $bootstrap)
94101
];
95102

96103
$classLoader = new ClassLoader($initialClassLoaderMappings);
97-
spl_autoload_register([$classLoader, 'loadClass'], true);
104+
$classLoaderCallback = [$classLoader, 'loadClass'];
105+
/** @phpstan-ignore argument.type (should work out just fine) */
106+
spl_autoload_register($classLoaderCallback, true);
98107
$bootstrap->setEarlyInstance(ClassLoader::class, $classLoader);
99108
$classLoader->setConsiderTestsNamespace(true);
100109
}
@@ -123,6 +132,9 @@ public static function registerClassLoaderInAnnotationRegistry(Bootstrap $bootst
123132
public static function initializeClassLoaderClassesCache(Bootstrap $bootstrap)
124133
{
125134
$classesCache = $bootstrap->getEarlyInstance(CacheManager::class)->getCache('Flow_Object_Classes');
135+
if (!$classesCache instanceof PhpFrontend) {
136+
throw new \Exception('The classes cache must be a PhpFrontend', 1744461618);
137+
}
126138
$bootstrap->getEarlyInstance(ProxyClassLoader::class)->injectClassesCache($classesCache);
127139
}
128140

@@ -185,7 +197,10 @@ public static function initializePackageManagement(Bootstrap $bootstrap)
185197

186198
$packageManager->initialize($bootstrap);
187199
if (self::useClassLoader($bootstrap)) {
188-
$bootstrap->getEarlyInstance(ClassLoader::class)->setPackages($packageManager->getAvailablePackages());
200+
$bootstrap->getEarlyInstance(ClassLoader::class)->setPackages(array_filter(
201+
$packageManager->getAvailablePackages(),
202+
fn ($package) => $package instanceof GenericPackage,
203+
));
189204
}
190205
}
191206

@@ -260,7 +275,15 @@ public static function initializeSystemLogger(Bootstrap $bootstrap): void
260275
* Initialize the exception storage
261276
*
262277
* @param Bootstrap $bootstrap
263-
* @param array $settings The Neos.Flow settings
278+
* @param array{
279+
* log?: array{
280+
* throwables?: array{
281+
* storageClass?: class-string<ThrowableStorageInterface>,
282+
* optionsByImplementation?: array<class-string<ThrowableStorageInterface>, array<mixed>>,
283+
* renderRequestInformation?: bool,
284+
* }
285+
* }
286+
* } $settings The Neos.Flow settings
264287
* @return ThrowableStorageInterface
265288
* @throws FlowException
266289
* @throws InvalidConfigurationTypeException
@@ -271,7 +294,6 @@ protected static function initializeExceptionStorage(Bootstrap $bootstrap, array
271294
$storageOptions = $settings['log']['throwables']['optionsByImplementation'][$storageClassName] ?? [];
272295
$renderRequestInformation = $settings['log']['throwables']['renderRequestInformation'] ?? true;
273296

274-
275297
if (!in_array(ThrowableStorageInterface::class, class_implements($storageClassName, true))) {
276298
throw new \Exception(
277299
sprintf('The class "%s" configured as throwable storage does not implement the ThrowableStorageInterface', $storageClassName),
@@ -304,7 +326,7 @@ protected static function initializeExceptionStorage(Bootstrap $bootstrap, array
304326

305327
$request = $requestHandler->getHttpRequest();
306328
if ($renderRequestInformation) {
307-
$output .= PHP_EOL . 'HTTP REQUEST:' . PHP_EOL . ($request instanceof RequestInterface ? RequestInformationHelper::renderRequestInformation($request) : '[request was empty]') . PHP_EOL;
329+
$output .= PHP_EOL . 'HTTP REQUEST:' . PHP_EOL . RequestInformationHelper::renderRequestInformation($request) . PHP_EOL;
308330
}
309331
$output .= PHP_EOL . 'PHP PROCESS:' . PHP_EOL . 'Inode: ' . getmyinode() . PHP_EOL . 'PID: ' . getmypid() . PHP_EOL . 'UID: ' . getmyuid() . PHP_EOL . 'GID: ' . getmygid() . PHP_EOL . 'User: ' . get_current_user() . PHP_EOL;
310332

@@ -325,11 +347,25 @@ protected static function initializeExceptionStorage(Bootstrap $bootstrap, array
325347
public static function initializeErrorHandling(Bootstrap $bootstrap)
326348
{
327349
$configurationManager = $bootstrap->getEarlyInstance(ConfigurationManager::class);
350+
/**
351+
* @var array{
352+
* error: array{
353+
* errorHandler: array{
354+
* exceptionalErrors: array<int>,
355+
* },
356+
* exceptionHandler: array{
357+
* className: class-string<ExceptionHandlerInterface>,
358+
* }
359+
* }
360+
* } $settings
361+
*/
328362
$settings = $configurationManager->getConfiguration(ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, 'Neos.Flow');
329363

330364
$errorHandler = new ErrorHandler();
331365
$errorHandler->setExceptionalErrors($settings['error']['errorHandler']['exceptionalErrors']);
332-
$exceptionHandler = class_exists($settings['error']['exceptionHandler']['className']) ? new $settings['error']['exceptionHandler']['className'] : new ProductionExceptionHandler();
366+
$exceptionHandler = class_exists($settings['error']['exceptionHandler']['className'])
367+
? new $settings['error']['exceptionHandler']['className']
368+
: new ProductionExceptionHandler();
333369

334370
if (is_callable([$exceptionHandler, 'injectLogger'])) {
335371
$exceptionHandler->injectLogger($bootstrap->getEarlyInstance(PsrLoggerFactoryInterface::class)->get('systemLogger'));
@@ -421,7 +457,11 @@ public static function initializeProxyClasses(Bootstrap $bootstrap)
421457
} else {
422458
$phpBinaryPathAndFilename = escapeshellarg(Files::getUnixStylePath($settings['core']['phpBinaryPathAndFilename']));
423459
}
424-
$command = sprintf('%s -c %s -v', $phpBinaryPathAndFilename, escapeshellarg(php_ini_loaded_file()));
460+
$iniPath = php_ini_loaded_file();
461+
if ($iniPath === false) {
462+
throw new \Exception('Failed to resolve PHP ini path', 1744460978);
463+
}
464+
$command = sprintf('%s -c %s -v', $phpBinaryPathAndFilename, escapeshellarg($iniPath));
425465
exec($command, $output, $result);
426466
if ($result !== 0) {
427467
if (!file_exists($phpBinaryPathAndFilename)) {
@@ -451,6 +491,7 @@ public static function recompileClasses(Bootstrap $bootstrap)
451491
* Initializes the Compiletime Object Manager (phase 1)
452492
*
453493
* @param Bootstrap $bootstrap
494+
* @return void
454495
*/
455496
public static function initializeObjectManagerCompileTimeCreate(Bootstrap $bootstrap)
456497
{
@@ -475,7 +516,7 @@ public static function initializeObjectManagerCompileTimeCreate(Bootstrap $boots
475516
*/
476517
public static function initializeObjectManagerCompileTimeFinalize(Bootstrap $bootstrap)
477518
{
478-
/** @var CompileTimeObjectManager $objectManager */
519+
/** @var CompileTimeObjectManager $objectManager @phpstan-ignore missingType.generics */
479520
$objectManager = $bootstrap->getObjectManager();
480521
$configurationManager = $bootstrap->getEarlyInstance(ConfigurationManager::class);
481522
$reflectionService = $objectManager->get(ReflectionService::class);
@@ -485,7 +526,11 @@ public static function initializeObjectManagerCompileTimeFinalize(Bootstrap $boo
485526

486527
$objectManager->injectReflectionService($reflectionService);
487528
$objectManager->injectConfigurationManager($configurationManager);
488-
$objectManager->injectConfigurationCache($cacheManager->getCache('Flow_Object_Configuration'));
529+
$configurationCache = $cacheManager->getCache('Flow_Object_Configuration');
530+
if (!$configurationCache instanceof VariableFrontend) {
531+
throw new \Exception('Configuration cache must be of type VariableFrontend', 1744459785);
532+
}
533+
$objectManager->injectConfigurationCache($configurationCache);
489534
$objectManager->injectLogger($logger);
490535
$objectManager->initialize($packageManager->getAvailablePackages());
491536

@@ -539,6 +584,7 @@ public static function initializeReflectionServiceFactory(Bootstrap $bootstrap)
539584
* Initializes the Reflection Service
540585
*
541586
* @param Bootstrap $bootstrap
587+
* @return void
542588
* @throws FlowException
543589
*/
544590
public static function initializeReflectionService(Bootstrap $bootstrap)
@@ -605,12 +651,12 @@ public static function initializeSystemFileMonitor(Bootstrap $bootstrap)
605651

606652
/**
607653
* @param Bootstrap $bootstrap
608-
* @return array
654+
* @return array<mixed>
609655
*/
610656
protected static function getListOfPackagesWithConfiguredObjects(Bootstrap $bootstrap): array
611657
{
612658
$objectManager = $bootstrap->getObjectManager();
613-
/** @phpstan-ignore-next-line the object manager interface doesn't specify this method */
659+
/** @phpstan-ignore method.notFound (not part of the interface) */
614660
$allObjectConfigurations = $objectManager->getAllObjectConfigurations();
615661
$packagesWithConfiguredObjects = array_reduce($allObjectConfigurations, function ($foundPackages, $item) {
616662
if (isset($item['p']) && !in_array($item['p'], $foundPackages)) {
@@ -681,9 +727,17 @@ public static function initializeResources(Bootstrap $bootstrap)
681727
* Executes the given command as a sub-request to the Flow CLI system.
682728
*
683729
* @param string $commandIdentifier E.g. neos.flow:cache:flush
684-
* @param array $settings The Neos.Flow settings
730+
* @param array{
731+
* core: array{
732+
* context: string,
733+
* phpBinaryPathAndFilename: string,
734+
* subRequestPhpIniPathAndFilename?: mixed,
735+
* subRequestEnvironmentVariables?: array<mixed>,
736+
* subRequestIniEntries?: array<mixed>,
737+
* },
738+
* } $settings The Neos.Flow settings
685739
* @param boolean $outputResults Echo the commands output on success
686-
* @param array $commandArguments Command arguments
740+
* @param array<mixed> $commandArguments Command arguments
687741
* @return true Legacy return value. Will always be true. A failure is expressed as a thrown exception
688742
* @throws Exception\SubProcessException The execution of the sub process failed
689743
* @api
@@ -710,7 +764,7 @@ public static function executeCommand(string $commandIdentifier, array $settings
710764
}
711765
if (file_exists(FLOW_PATH_DATA . 'Logs/Exceptions') && is_dir(FLOW_PATH_DATA . 'Logs/Exceptions') && is_writable(FLOW_PATH_DATA . 'Logs/Exceptions')) {
712766
// Logs the command string `php ./flow foo:bar` inside `Logs/Exceptions/123-command.txt`
713-
$referenceCode = date('YmdHis', $_SERVER['REQUEST_TIME']) . substr(md5(rand()), 0, 6);
767+
$referenceCode = date('YmdHis', $_SERVER['REQUEST_TIME']) . substr(md5((string)rand()), 0, 6);
714768
$errorDumpPathAndFilename = FLOW_PATH_DATA . 'Logs/Exceptions/' . $referenceCode . '-command.txt';
715769
file_put_contents($errorDumpPathAndFilename, $command);
716770
$exceptionMessage .= sprintf(' It has been stored in: %s', basename($errorDumpPathAndFilename));
@@ -733,8 +787,16 @@ public static function executeCommand(string $commandIdentifier, array $settings
733787
* Note: As the command execution is done in a separate thread potential exceptions or failures will *not* be reported
734788
*
735789
* @param string $commandIdentifier E.g. neos.flow:cache:flush
736-
* @param array $settings The Neos.Flow settings
737-
* @param array $commandArguments Command arguments
790+
* @param array{
791+
* core: array{
792+
* context: string,
793+
* phpBinaryPathAndFilename: string,
794+
* subRequestPhpIniPathAndFilename?: mixed,
795+
* subRequestEnvironmentVariables?: array<mixed>,
796+
* subRequestIniEntries?: array<mixed>,
797+
* },
798+
* } $settings The Neos.Flow settings
799+
* @param array<mixed> $commandArguments Command arguments
738800
* @return void
739801
* @api
740802
*/
@@ -744,14 +806,25 @@ public static function executeCommandAsync(string $commandIdentifier, array $set
744806
if (DIRECTORY_SEPARATOR === '/') {
745807
exec($command . ' > /dev/null 2>/dev/null &');
746808
} else {
747-
pclose(popen('START /B CMD /S /C "' . $command . '" > NUL 2> NUL &', 'r'));
809+
$handle = popen('START /B CMD /S /C "' . $command . '" > NUL 2> NUL &', 'r');
810+
if ($handle) {
811+
pclose($handle);
812+
}
748813
}
749814
}
750815

751816
/**
752817
* @param string $commandIdentifier E.g. neos.flow:cache:flush
753-
* @param array $settings The Neos.Flow settings
754-
* @param array $commandArguments Command arguments
818+
* @param array{
819+
* core: array{
820+
* context: string,
821+
* phpBinaryPathAndFilename: string,
822+
* subRequestPhpIniPathAndFilename?: mixed,
823+
* subRequestEnvironmentVariables?: array<mixed>,
824+
* subRequestIniEntries?: mixed,
825+
* },
826+
* } $settings The Neos.Flow settings
827+
* @param array<mixed> $commandArguments Command arguments
755828
* @return string A command line command ready for being exec()uted
756829
*/
757830
protected static function buildSubprocessCommand(string $commandIdentifier, array $settings, array $commandArguments = []): string
@@ -779,7 +852,14 @@ protected static function buildSubprocessCommand(string $commandIdentifier, arra
779852
}
780853

781854
/**
782-
* @param array $settings The Neos.Flow settings
855+
* @param array{
856+
* core: array{
857+
* context: string,
858+
* phpBinaryPathAndFilename: string,
859+
* subRequestPhpIniPathAndFilename?: mixed,
860+
* subRequestEnvironmentVariables?: array<mixed>,
861+
* },
862+
* } $settings The Neos.Flow settings
783863
* @return string A command line command for PHP, which can be extended and then exec()uted
784864
* @throws Exception\SubProcessException in case the phpBinaryPathAndFilename is incorrect
785865
*/
@@ -821,6 +901,9 @@ public static function buildPhpCommand(array $settings): string
821901
} else {
822902
$useIniFile = $settings['core']['subRequestPhpIniPathAndFilename'];
823903
}
904+
if (!is_string($useIniFile)) {
905+
throw new \Exception('Could not resolve ini file', 1744460624);
906+
}
824907
$command .= ' -c ' . escapeshellarg($useIniFile);
825908
}
826909

@@ -834,6 +917,7 @@ public static function buildPhpCommand(array $settings): string
834917
* This avoids config errors where users forget to set Neos.Flow.core.phpBinaryPathAndFilename in CLI.
835918
*
836919
* @param string $phpBinaryPathAndFilename
920+
* @return void
837921
* @throws Exception\SubProcessException in case the php binary doesn't exist / is a different one for the current cli request
838922
*/
839923
protected static function ensureCLISubrequestsUseCurrentlyRunningPhpBinary($phpBinaryPathAndFilename)
@@ -844,7 +928,7 @@ protected static function ensureCLISubrequestsUseCurrentlyRunningPhpBinary($phpB
844928
}
845929

846930
// Ensure the actual PHP binary is known before checking if it is correct.
847-
if (!$phpBinaryPathAndFilename || strlen($phpBinaryPathAndFilename) === 0) {
931+
if (!$phpBinaryPathAndFilename) {
848932
throw new Exception\SubProcessException('"Neos.Flow.core.phpBinaryPathAndFilename" is not set.', 1689676816060);
849933
}
850934

@@ -911,6 +995,7 @@ protected static function ensureCLISubrequestsUseCurrentlyRunningPhpBinary($phpB
911995
* server.
912996
*
913997
* @param string $phpCommand the completely build php string that is used to execute subrequests
998+
* @return void
914999
* @throws Exception\SubProcessException in case the php binary doesn't exist, or is not suitable for cli usage, or its version doesn't match
9151000
*/
9161001
protected static function ensureWebSubrequestsUseCurrentlyRunningPhpVersion($phpCommand)

Neos.Flow/Classes/Core/Booting/Sequence.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class Sequence
2828
protected $identifier;
2929

3030
/**
31-
* @var array
31+
* @var array<string,array<int|string,Step>>
3232
*/
3333
protected $steps = [];
3434

0 commit comments

Comments
 (0)