/** * @test */ public function ifCommandCantBeResolvedTheHelpScreenIsShown() { // The following call is only made to satisfy PHPUnit. For some weird reason PHPUnit complains that the // mocked method ("getObjectNameByClassName") does not exist _if the mock object is not used_. $this->mockObjectManager->getObjectNameByClassName('Acme\\Test\\Command\\DefaultCommandController'); $this->mockCommandManager->getCommandByIdentifier('acme.test:default:list'); $mockCommandManager = $this->getMock(\TYPO3\Flow\Cli\CommandManager::class); $mockCommandManager->expects($this->any())->method('getCommandByIdentifier')->with('test:default:list')->will($this->throwException(new \TYPO3\Flow\Mvc\Exception\NoSuchCommandException())); $this->requestBuilder->injectCommandManager($mockCommandManager); $request = $this->requestBuilder->build('test:default:list'); $this->assertSame(\TYPO3\Flow\Command\HelpCommandController::class, $request->getControllerObjectName()); }
/** * Around advice, wrapping every method of a scope session object. It redirects * all method calls to the session object once there is one. * * @param \TYPO3\Flow\Aop\JoinPointInterface $joinPoint The current join point * @return mixed * @Flow\Around("filter(TYPO3\Flow\Session\Aspect\SessionObjectMethodsPointcutFilter)") */ public function callMethodOnOriginalSessionObject(\TYPO3\Flow\Aop\JoinPointInterface $joinPoint) { $objectName = $this->objectManager->getObjectNameByClassName(get_class($joinPoint->getProxy())); $methodName = $joinPoint->getMethodName(); $proxy = $joinPoint->getProxy(); if (!isset($this->sessionOriginalInstances[$objectName])) { $this->sessionOriginalInstances[$objectName] = $this->objectManager->get($objectName); } if ($this->sessionOriginalInstances[$objectName] === $proxy) { return $joinPoint->getAdviceChain()->proceed($joinPoint); } else { return call_user_func_array(array($this->sessionOriginalInstances[$objectName], $methodName), $joinPoint->getMethodArguments()); } }
/** * Resumes an existing session, if any. * * @return integer If a session was resumed, the inactivity of since the last request is returned * @api */ public function resume() { if ($this->started === false && $this->canBeResumed()) { $this->sessionIdentifier = $this->sessionCookie->getValue(); $this->response->setCookie($this->sessionCookie); $this->started = true; $sessionObjects = $this->storageCache->get($this->storageIdentifier . md5('TYPO3_Flow_Object_ObjectManager')); if (is_array($sessionObjects)) { foreach ($sessionObjects as $object) { if ($object instanceof ProxyInterface) { $objectName = $this->objectManager->getObjectNameByClassName(get_class($object)); if ($this->objectManager->getScope($objectName) === ObjectConfiguration::SCOPE_SESSION) { $this->objectManager->setInstance($objectName, $object); $this->objectManager->get(\TYPO3\Flow\Session\Aspect\LazyLoadingAspect::class)->registerSessionInstance($objectName, $object); } } } } else { // Fallback for some malformed session data, if it is no array but something else. // In this case, we reset all session objects (graceful degradation). $this->storageCache->set($this->storageIdentifier . md5('TYPO3_Flow_Object_ObjectManager'), array(), array($this->storageIdentifier), 0); } $lastActivitySecondsAgo = $this->now - $this->lastActivityTimestamp; $this->lastActivityTimestamp = $this->now; return $lastActivitySecondsAgo; } }
/** * Builds a CLI request object from a command line. * * The given command line may be a string (e.g. "mypackage:foo do-that-thing --force") or * an array consisting of the individual parts. The array must not include the script * name (like in $argv) but start with command right away. * * @param mixed $commandLine The command line, either as a string or as an array * @return \TYPO3\Flow\Cli\Request The CLI request as an object */ public function build($commandLine) { $request = new Request(); $request->setControllerObjectName('TYPO3\\Flow\\Command\\HelpCommandController'); $rawCommandLineArguments = is_array($commandLine) ? $commandLine : explode(' ', $commandLine); if (count($rawCommandLineArguments) === 0) { $request->setControllerCommandName('helpStub'); return $request; } $commandIdentifier = trim(array_shift($rawCommandLineArguments)); try { $command = $this->commandManager->getCommandByIdentifier($commandIdentifier); } catch (\TYPO3\Flow\Mvc\Exception\CommandException $exception) { $request->setArgument('exception', $exception); $request->setControllerCommandName('error'); return $request; } $controllerObjectName = $this->objectManager->getObjectNameByClassName($command->getControllerClassName()); $controllerCommandName = $command->getControllerCommandName(); $request->setControllerObjectName($controllerObjectName); $request->setControllerCommandName($controllerCommandName); list($commandLineArguments, $exceedingCommandLineArguments) = $this->parseRawCommandLineArguments($rawCommandLineArguments, $controllerObjectName, $controllerCommandName); $request->setArguments($commandLineArguments); $request->setExceedingArguments($exceedingCommandLineArguments); return $request; }
/** * Traverses the given object structure in order to transform it into an * array structure. * * @param object $object Object to traverse * @param array $configuration Configuration for transforming the given object * @param boolean $onlyIdentifier * @param boolean $enableSideLoading * @return array Object structure as an array * @todo implement sideloading */ protected function transformObject($object, array $configuration, $onlyIdentifier = TRUE, $enableSideLoading = TRUE) { if ($object instanceof \DateTime) { return $object->format('Y-m-d\\TH:i:s'); } elseif ($onlyIdentifier === TRUE) { return $this->persistenceManager->getIdentifierByObject($object); } else { $transformedObject = array('id' => $this->persistenceManager->getIdentifierByObject($object)); $objectName = $this->objectManager->getObjectNameByClassName($this->reflectionService->getClassNameByObject($object)); foreach (ObjectAccess::getGettablePropertyNames($object) as $propertyName) { if ($this->isPropertyIgnored($objectName, $propertyName)) { continue; } $propertyValue = ObjectAccess::getProperty($object, $propertyName); if (is_array($propertyValue) || $propertyValue instanceof \ArrayAccess) { $propertyValue = $this->transformValue($propertyValue, array(), TRUE, $enableSideLoading); if ($this->isRelation($objectName, $propertyName)) { $this->addLink($transformedObject, $propertyName, $propertyValue); if ($enableSideLoading === TRUE && !$this->reflectionService->isPropertyAnnotatedWith($objectName, $propertyName, 'TYPO3\\Flow\\Annotations\\Lazy')) { $propertyTags = $this->reflectionService->getPropertyTagValues($objectName, $propertyName, 'var'); $propertyObjectType = TypeHandling::parseType($propertyTags[0]); $this->sideLoad($propertyObjectType['elementType'], $propertyValue); } $propertyValue = NULL; } elseif (empty($propertyValue)) { $propertyValue = NULL; } } elseif ($this->isSimpleValue($propertyValue)) { if (is_object($propertyValue)) { $propertyValue = $this->transformObject($propertyValue, $configuration, FALSE, $enableSideLoading); } } elseif (is_object($propertyValue)) { $propertyObjectName = $this->objectManager->getObjectNameByClassName($this->reflectionService->getClassNameByObject($propertyValue)); if (!$this->isObjectIgnored($propertyObjectName)) { $propertyValue = $this->transformObject($propertyValue, $configuration, TRUE, $enableSideLoading); if ($this->isRelation($objectName, $propertyName)) { $this->addLink($transformedObject, $propertyName, $propertyValue); if ($enableSideLoading === TRUE && !$this->reflectionService->isPropertyAnnotatedWith($objectName, $propertyName, 'TYPO3\\Flow\\Annotations\\Lazy')) { $this->sideLoad($propertyObjectName, $propertyValue); } $propertyValue = NULL; } elseif (empty($propertyValue)) { $propertyValue = NULL; } } } if ($propertyValue !== NULL) { if (is_object($propertyValue)) { $propertyObjectName = $this->objectManager->getObjectNameByClassName($this->reflectionService->getClassNameByObject($propertyValue)); if (!$this->isObjectIgnored($propertyObjectName)) { $transformedObject[$propertyName] = $propertyValue; } } else { $transformedObject[$propertyName] = $propertyValue; } } } return $transformedObject; } }
/** * Checks if the specified class and method matches against the filter * * @param string $className Name of the class to check against * @param string $methodName Name of the method to check against * @param string $methodDeclaringClassName Name of the class the method was originally declared in * @param mixed $pointcutQueryIdentifier Some identifier for this query - must at least differ from a previous identifier. Used for circular reference detection. * @return boolean TRUE if the class / method match, otherwise FALSE */ public function matches($className, $methodName, $methodDeclaringClassName, $pointcutQueryIdentifier) { if ($methodName === null) { return false; } $objectName = $this->objectManager->getObjectNameByClassName($className); if (empty($objectName)) { return false; } if ($this->objectManager->getScope($objectName) !== ObjectConfiguration::SCOPE_SESSION) { return false; } if (preg_match('/^__wakeup|__construct|__destruct|__sleep|__serialize|__unserialize|__clone|shutdownObject|initializeObject|inject.*$/', $methodName) !== 0) { return false; } return true; }
/** * Traverses the given object structure in order to transform it into an * array structure. * * @param object $object Object to traverse * @param array $configuration Configuration for transforming the given object * @param boolean $onlyIdentifier * @return array Object structure as an array * @todo implement sideloading */ protected function transformObject($object, array $configuration, $onlyIdentifier = TRUE) { if ($object instanceof \DateTime) { return $object->format('Y-m-d\\TH:i:s'); } elseif ($onlyIdentifier === TRUE) { $objectIdentifier = $this->persistenceManager->getIdentifierByObject($object); if ($objectIdentifier === NULL && method_exists($object, 'getId')) { $objectIdentifier = $object->getId(); } return $objectIdentifier; } else { $transformedObject = array(); if ($this->excludeIdentifier === FALSE) { $objectIdentifier = $this->persistenceManager->getIdentifierByObject($object); $transformedObject['id'] = $objectIdentifier; } $propertyNames = ObjectAccess::getGettablePropertyNames($object); foreach ($propertyNames as $propertyName) { if (substr($propertyName, 0, 1) !== '_') { if (isset($configuration['_only']) && is_array($configuration['_only']) && !in_array($propertyName, $configuration['_only'])) { continue; } if ($this->propertyIsExcluded($propertyName, $configuration)) { continue; } $propertyValue = ObjectAccess::getProperty($object, $propertyName); $objectName = $this->objectManager->getObjectNameByClassName($this->reflectionService->getClassNameByObject($object)); if (!$this->reflectionService->isPropertyAnnotatedWith($objectName, $propertyName, 'TYPO3\\Flow\\Annotations\\Transient')) { if (is_array($propertyValue) || $propertyValue instanceof \ArrayAccess) { $transformedObject[EmberDataUtility::uncamelize($propertyName)] = $this->transformValue($propertyValue, array()); } elseif ($propertyValue instanceof \DateTime) { $transformedObject[EmberDataUtility::uncamelize($propertyName)] = $propertyValue->format('Y-m-d\\TH:i:s'); } elseif (is_object($propertyValue)) { $objectName = $this->objectManager->getObjectNameByClassName($this->reflectionService->getClassNameByObject($object)); if ($this->objectManager->getScope($objectName) !== \TYPO3\Flow\Object\Configuration\Configuration::SCOPE_SINGLETON) { $transformedObject[EmberDataUtility::uncamelize($propertyName) . '_id'] = $this->persistenceManager->getIdentifierByObject($propertyValue); if (isset($configuration['sideloadedAssociations']) && in_array($propertyName, $configuration['sideloadedAssociations'])) { // sideload this propertyvalue } } } else { $transformedObject[EmberDataUtility::uncamelize($propertyName)] = $propertyValue; } } } } return $transformedObject; } }
/** * Builds a CLI request object from a command line. * * The given command line may be a string (e.g. "mypackage:foo do-that-thing --force") or * an array consisting of the individual parts. The array must not include the script * name (like in $argv) but start with command right away. * * @param mixed $commandLine The command line, either as a string or as an array * @return Request The CLI request as an object * @throws InvalidArgumentMixingException * @throws InvalidArgumentNameException */ public function build($commandLine) { $request = new Request(); $request->setControllerObjectName(HelpCommandController::class); if (is_array($commandLine) === true) { $rawCommandLineArguments = $commandLine; } else { preg_match_all(self::ARGUMENT_MATCHING_EXPRESSION, $commandLine, $commandLineMatchings, PREG_SET_ORDER); $rawCommandLineArguments = []; foreach ($commandLineMatchings as $match) { if (isset($match['NoQuotes'])) { $rawCommandLineArguments[] = str_replace(['\\ ', '\\"', "\\'", '\\\\'], [' ', '"', "'", '\\'], $match['NoQuotes']); } elseif (isset($match['DoubleQuotes'])) { $rawCommandLineArguments[] = str_replace('\\"', '"', $match['DoubleQuotes']); } elseif (isset($match['SingleQuotes'])) { $rawCommandLineArguments[] = str_replace('\\\'', '\'', $match['SingleQuotes']); } else { throw new InvalidArgumentNameException(sprintf('Could not parse the command line "%s" - specifically the part "%s".', $commandLine, $match[0])); } } } if (count($rawCommandLineArguments) === 0) { $request->setControllerCommandName('helpStub'); return $request; } $commandIdentifier = trim(array_shift($rawCommandLineArguments)); try { $command = $this->commandManager->getCommandByIdentifier($commandIdentifier); } catch (CommandException $exception) { $request->setArgument('exception', $exception); $request->setControllerCommandName('error'); return $request; } $controllerObjectName = $this->objectManager->getObjectNameByClassName($command->getControllerClassName()); $controllerCommandName = $command->getControllerCommandName(); $request->setControllerObjectName($controllerObjectName); $request->setControllerCommandName($controllerCommandName); list($commandLineArguments, $exceedingCommandLineArguments) = $this->parseRawCommandLineArguments($rawCommandLineArguments, $controllerObjectName, $controllerCommandName); $request->setArguments($commandLineArguments); $request->setExceedingArguments($exceedingCommandLineArguments); return $request; }
/** * Renders a dump of the given object * * @param object $object * @param integer $level * @param boolean $renderProperties * @param boolean $plaintext * @param boolean $ansiColors * @return string */ protected static function renderObjectDump($object, $level, $renderProperties = TRUE, $plaintext = FALSE, $ansiColors = FALSE) { $dump = ''; $scope = ''; $additionalAttributes = ''; if ($object instanceof \Doctrine\Common\Collections\Collection) { return self::renderArrayDump(\Doctrine\Common\Util\Debug::export($object, 3), $level, $plaintext, $ansiColors); } // Objects returned from Doctrine's Debug::export function are stdClass with special properties: try { $objectIdentifier = ObjectAccess::getProperty($object, 'Persistence_Object_Identifier', TRUE); } catch (\TYPO3\Flow\Reflection\Exception\PropertyNotAccessibleException $exception) { $objectIdentifier = spl_object_hash($object); } $className = $object instanceof \stdClass && isset($object->__CLASS__) ? $object->__CLASS__ : get_class($object); if (isset(self::$renderedObjects[$objectIdentifier]) || preg_match(self::getIgnoredClassesRegex(), $className) !== 0) { $renderProperties = FALSE; } self::$renderedObjects[$objectIdentifier] = TRUE; if (self::$objectManager !== NULL) { $objectName = self::$objectManager->getObjectNameByClassName(get_class($object)); if ($objectName !== FALSE) { switch (self::$objectManager->getScope($objectName)) { case \TYPO3\Flow\Object\Configuration\Configuration::SCOPE_PROTOTYPE: $scope = 'prototype'; break; case \TYPO3\Flow\Object\Configuration\Configuration::SCOPE_SINGLETON: $scope = 'singleton'; break; case \TYPO3\Flow\Object\Configuration\Configuration::SCOPE_SESSION: $scope = 'session'; break; } } else { $additionalAttributes .= ' debug-unregistered'; } } if ($renderProperties === TRUE && !$plaintext) { if ($scope === '') { $scope = 'prototype'; } $scope .= '<a id="o' . $objectIdentifier . '"></a>'; } if ($plaintext) { $dump .= $className; $dump .= $scope !== '' ? ' ' . self::ansiEscapeWrap($scope, '44;37', $ansiColors) : ''; } else { $dump .= '<span class="debug-object' . $additionalAttributes . '" title="' . $objectIdentifier . '">' . $className . '</span>'; $dump .= $scope !== '' ? '<span class="debug-scope">' . $scope . '</span>' : ''; } if (property_exists($object, 'Persistence_Object_Identifier')) { $persistenceIdentifier = $objectIdentifier; $persistenceType = 'persistable'; } elseif ($object instanceof \Closure) { $persistenceIdentifier = 'n/a'; $persistenceType = 'closure'; } else { $persistenceIdentifier = 'unknown'; $persistenceType = 'object'; } if ($plaintext) { $dump .= ' ' . self::ansiEscapeWrap($persistenceType, '42;37', $ansiColors); } else { $dump .= '<span class="debug-ptype" title="' . $persistenceIdentifier . '">' . $persistenceType . '</span>'; } if ($object instanceof \TYPO3\Flow\Object\Proxy\ProxyInterface || property_exists($object, '__IS_PROXY__') && $object->__IS_PROXY__ === TRUE) { if ($plaintext) { $dump .= ' ' . self::ansiEscapeWrap('proxy', '41;37', $ansiColors); } else { $dump .= '<span class="debug-proxy" title="' . $className . '">proxy</span>'; } } if ($renderProperties === TRUE) { if ($object instanceof \SplObjectStorage) { $dump .= ' (' . (count($object) ?: 'empty') . ')'; foreach ($object as $value) { $dump .= chr(10); $dump .= str_repeat(' ', $level); $dump .= self::renderObjectDump($value, 0, FALSE, $plaintext, $ansiColors); } } else { $classReflection = new \ReflectionClass($className); $properties = $classReflection->getProperties(); foreach ($properties as $property) { if (preg_match(self::$blacklistedPropertyNames, $property->getName())) { continue; } $dump .= chr(10); $dump .= str_repeat(' ', $level) . ($plaintext ? '' : '<span class="debug-property">') . self::ansiEscapeWrap($property->getName(), '36', $ansiColors) . ($plaintext ? '' : '</span>') . ' => '; $property->setAccessible(TRUE); $value = $property->getValue($object); if (is_array($value)) { $dump .= self::renderDump($value, $level + 1, $plaintext, $ansiColors); } elseif (is_object($value)) { $dump .= self::renderObjectDump($value, $level + 1, TRUE, $plaintext, $ansiColors); } else { $dump .= self::renderDump($value, $level, $plaintext, $ansiColors); } } } } elseif (isset(self::$renderedObjects[$objectIdentifier])) { if (!$plaintext) { $dump = '<a href="#o' . $objectIdentifier . '" onclick="document.location.hash=\'#o' . $objectIdentifier . '\'; return false;" class="debug-seeabove" title="see above">' . $dump . '</a>'; } } return $dump; }
/** * @param ObjectManagerInterface $objectManager * @return array Array of method arguments per controller and method. * @Flow\CompileStatic */ public static function getCommandControllerMethodArguments($objectManager) { /** @var ReflectionService $reflectionService */ $reflectionService = $objectManager->get(ReflectionService::class); $commandControllerMethodArgumentMap = []; foreach ($reflectionService->getAllSubClassNamesForClass(CommandController::class) as $className) { if (!class_exists($className) || $reflectionService->isClassAbstract($className)) { continue; } $controllerObjectName = $objectManager->getObjectNameByClassName($className); $commandControllerMethodArgumentMap[$controllerObjectName] = []; foreach (get_class_methods($className) as $methodName) { if (substr($methodName, -7, 7) === 'Command') { $commandControllerMethodArgumentMap[$className][$methodName] = $reflectionService->getMethodParameters($controllerObjectName, $methodName); } } } return $commandControllerMethodArgumentMap; }
/** * Serializes an object as property array. * * @param object $object The object to store in the registry * @param boolean $isTopLevelItem Internal flag for managing the recursion * @return array The property array */ public function serializeObjectAsPropertyArray($object, $isTopLevelItem = true) { if ($isTopLevelItem) { $this->objectReferences = new \SplObjectStorage(); } $this->objectReferences->attach($object); $className = get_class($object); $propertyArray = array(); foreach ($this->reflectionService->getClassPropertyNames($className) as $propertyName) { if ($this->reflectionService->isPropertyTaggedWith($className, $propertyName, 'transient')) { continue; } $propertyReflection = new \TYPO3\Flow\Reflection\PropertyReflection($className, $propertyName); $propertyValue = $propertyReflection->getValue($object); if (is_object($propertyValue) && $propertyValue instanceof \TYPO3\Flow\Object\DependencyInjection\DependencyProxy) { continue; } if (is_object($propertyValue) && isset($this->objectReferences[$propertyValue])) { $propertyArray[$propertyName][self::TYPE] = 'object'; $propertyArray[$propertyName][self::VALUE] = \spl_object_hash($propertyValue); continue; } $propertyClassName = is_object($propertyValue) ? get_class($propertyValue) : ''; if ($propertyClassName === 'SplObjectStorage') { $propertyArray[$propertyName][self::TYPE] = 'SplObjectStorage'; $propertyArray[$propertyName][self::VALUE] = array(); foreach ($propertyValue as $storedObject) { $propertyArray[$propertyName][self::VALUE][] = spl_object_hash($storedObject); $this->serializeObjectAsPropertyArray($storedObject, false); } } elseif (is_object($propertyValue) && $propertyValue instanceof \Doctrine\Common\Collections\Collection) { $propertyArray[$propertyName][self::TYPE] = 'Collection'; $propertyArray[$propertyName][self::CLASSNAME] = get_class($propertyValue); foreach ($propertyValue as $storedObject) { $propertyArray[$propertyName][self::VALUE][] = spl_object_hash($storedObject); $this->serializeObjectAsPropertyArray($storedObject, false); } } elseif (is_object($propertyValue) && $propertyValue instanceof \ArrayObject) { $propertyArray[$propertyName][self::TYPE] = 'ArrayObject'; $propertyArray[$propertyName][self::VALUE] = $this->buildStorageArrayForArrayProperty($propertyValue->getArrayCopy()); } elseif (is_object($propertyValue) && $this->persistenceManager->isNewObject($propertyValue) === false && ($this->reflectionService->isClassAnnotatedWith($propertyClassName, \TYPO3\Flow\Annotations\Entity::class) || $this->reflectionService->isClassAnnotatedWith($propertyClassName, \TYPO3\Flow\Annotations\ValueObject::class) || $this->reflectionService->isClassAnnotatedWith($propertyClassName, 'Doctrine\\ORM\\Mapping\\Entity'))) { $propertyArray[$propertyName][self::TYPE] = 'persistenceObject'; $propertyArray[$propertyName][self::VALUE] = get_class($propertyValue) . ':' . $this->persistenceManager->getIdentifierByObject($propertyValue); } elseif (is_object($propertyValue)) { $propertyObjectName = $this->objectManager->getObjectNameByClassName($propertyClassName); if ($this->objectManager->getScope($propertyObjectName) === \TYPO3\Flow\Object\Configuration\Configuration::SCOPE_SINGLETON) { continue; } $propertyArray[$propertyName][self::TYPE] = 'object'; $propertyArray[$propertyName][self::VALUE] = spl_object_hash($propertyValue); $this->serializeObjectAsPropertyArray($propertyValue, false); } elseif (is_array($propertyValue)) { $propertyArray[$propertyName][self::TYPE] = 'array'; $propertyArray[$propertyName][self::VALUE] = $this->buildStorageArrayForArrayProperty($propertyValue); } else { $propertyArray[$propertyName][self::TYPE] = 'simple'; $propertyArray[$propertyName][self::VALUE] = $propertyValue; } } $this->objectsAsArray[spl_object_hash($object)] = array(self::CLASSNAME => $className, self::PROPERTIES => $propertyArray); if ($isTopLevelItem) { return $this->objectsAsArray; } }
/** * Detects plugins for this command controller * * @param ObjectManagerInterface $objectManager * @return array */ protected static function detectPlugins(ObjectManagerInterface $objectManager) { $pluginConfigurations = array(); $classNames = $objectManager->get('TYPO3\\Flow\\Reflection\\ReflectionService')->getAllImplementationClassNamesForInterface('TYPO3\\TYPO3CR\\Command\\NodeCommandControllerPluginInterface'); foreach ($classNames as $className) { $pluginConfigurations[$className] = array('object' => $objectManager->get($objectManager->getObjectNameByClassName($className))); } return $pluginConfigurations; }