1313
1414use Doctrine \Common \Annotations \AnnotationRegistry ;
1515use Neos \Cache \CacheFactoryInterface ;
16+ use Neos \Cache \Frontend \PhpFrontend ;
17+ use Neos \Cache \Frontend \VariableFrontend ;
1618use Neos \Flow \Annotations as Flow ;
1719use Neos \Flow \Cache \CacheFactory ;
1820use Neos \Flow \Cache \CacheManager ;
3032use Neos \Flow \Core \ProxyClassLoader ;
3133use Neos \Flow \Error \Debugger ;
3234use Neos \Flow \Error \ErrorHandler ;
35+ use Neos \Flow \Error \ExceptionHandlerInterface ;
3336use Neos \Flow \Error \ProductionExceptionHandler ;
3437use Neos \Flow \Http \Helper \RequestInformationHelper ;
3538use Neos \Flow \Http \HttpRequestHandlerInterface ;
4043use Neos \Flow \ObjectManagement \CompileTimeObjectManager ;
4144use Neos \Flow \ObjectManagement \ObjectManager ;
4245use Neos \Flow \ObjectManagement \ObjectManagerInterface ;
46+ use Neos \Flow \Package ;
4347use Neos \Flow \Package \FlowPackageInterface ;
4448use Neos \Flow \Package \GenericPackage ;
49+ use Neos \Flow \Package \PackageInterface ;
50+ use Neos \Flow \Package \PackageKeyAwareInterface ;
4551use Neos \Flow \Package \PackageManager ;
4652use Neos \Flow \Reflection \ReflectionService ;
4753use Neos \Flow \Reflection \ReflectionServiceFactory ;
5157use Neos \Utility \Files ;
5258use Neos \Utility \OpcodeCacheHelper ;
5359use 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 )
0 commit comments