Skip to content

Instantly share code, notes, and snippets.

@randm-ch
Last active August 29, 2015 14:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save randm-ch/adcacf8b9390aa563b38 to your computer and use it in GitHub Desktop.
Save randm-ch/adcacf8b9390aa563b38 to your computer and use it in GitHub Desktop.
<?php
namespace MyCompany\MyApplication\Utility;
use TYPO3\Flow\Annotations as Flow;
use MyCompany\MyApplication\Exception;
/**
* @Flow\Scope("singleton")
*/
class DataClassReflection {
/**
* @Flow\Inject
* @var \TYPO3\Flow\Reflection\ReflectionService
*/
protected $reflectionService;
/**
* @var \Doctrine\Common\Persistence\ObjectManager
* @Flow\Inject
*/
protected $entityManager;
/**
* @Flow\Inject
* @var \TYPO3\Flow\Persistence\PersistenceManagerInterface
*/
protected $persistenceManager;
/**
* @var array
*/
protected $fetchedClasses = array();
/**
* Returns an array of the configured properties of the provided class and all values stored in the Data objects.
*
* @param string $className
* @param integer $depth Provide if you want to limit the recursion depth
* @return array
*/
public function getProperties($className, $depth = 0) {
if(strpos($className, '\\') === 0) $className = substr($className, 1);
return $this->getPropertiesRecursive($className, $depth);
}
/**
* Returns an array of the configured properties of the provided class and all values stored in the Data objects.
*
* @param string $className
* @param integer $depth Provide if you want to limit the recursion depth
* @param integer $currentDepth For internal use only
* @param string $propertyPath For internal use only
* @param boolean $isRecursive For internal use only
* @return array
* @throws \TYPO3\Flow\Object\Exception\InvalidClassException
*/
protected function getPropertiesRecursive($className, $depth = 0, $currentDepth = 0, $propertyPath = NULL, $isRecursive = FALSE) {
if($depth !== 0 && $depth <= $currentDepth) return array();
if($isRecursive === FALSE) $this->fetchedClasses = array();
if(in_array($className, $this->fetchedClasses)) return array();
$this->fetchedClasses[] = $className;
$properties = array();
foreach($this->reflectionService->getClassPropertyNames($className) as $property) {
if(!in_array($property, $this->getForbiddenProperties())) {
$reflection = new \TYPO3\Flow\Reflection\PropertyReflection($className, $property);
if($reflection->isTaggedWith('onetoone') || $reflection->isTaggedWith('manytoone') || $reflection->isTaggedWith('onetomany') || $reflection->isTaggedWith('manytomany')) {
$varDefinition = $reflection->getTagValues('var');
$currentPropertyPath = (is_string($propertyPath) && strlen($propertyPath) > 0) ? $propertyPath.$property.'.' : $property.'.';
$properties = array_merge($this->getPropertiesRecursive($varDefinition[0], $depth, $currentDepth+1, $currentPropertyPath, TRUE), $properties);
}
else {
$properties[] = $propertyPath.$property;
}
}
}
if(in_array($className, $this->reflectionService->getAllSubClassNamesForClass('MyCompany\MyApplication\Domain\Model\AbstractDataModel'))) {
$dataQuery = $this->entityManager->createQuery('SELECT DISTINCT(d.identifier) FROM '.$className.' a JOIN a.data d WHERE d IS NOT NULL');
foreach($dataQuery->execute() as $result) {
$properties[] = $propertyPath.$result['identifier'];
}
}
return $properties;
}
/**
* Checks if all of the properties provided in $propertyPath are real class properties.
* If a dynamic Data propery is found, this method returns FALSE.
*
* @param string $className
* @param string $propertyPath
* @return boolean
*/
public function isRealPropertyOfClass($className, $propertyPath) {
$properties = explode('.', $propertyPath);
foreach($properties as $property) {
if(in_array($property, $this->reflectionService->getClassPropertyNames($className))) {
$reflection = new \TYPO3\Flow\Reflection\PropertyReflection($className, $property);
if($reflection->isTaggedWith('onetoone') || $reflection->isTaggedWith('manytoone') || $reflection->isTaggedWith('onetomany') || $reflection->isTaggedWith('manytomany')) {
$varDefinition = $reflection->getTagValues('var');
$className = $varDefinition[0];
}
else return TRUE;
}
else return FALSE;
}
return TRUE;
}
/**
* Returns an array of the configured properties of the provided object.
*
* @param \MyCompany\MyApplication\Domain\Model\AbstractDataModel $object
* @param array $configuration
* @return array
*/
public function getPropertiesAsArray($object, $configuration = array()) {
$properties = $this->getProperties(self::getClassNameFromProxyClassName(get_class($object)));
foreach($properties as $i => $property) {
$properties[$property] = $this->traverseObject($object, $property);
unset($properties[$i]);
}
if(count($configuration) > 0) {
$array = array();
foreach($configuration as $property) {
if(isset($properties[$property])) $array[$property] = $properties[$property];
else $array[$property] = NULL;
}
return $array;
}
return $properties;
}
/**
* Traverses the given object along the path, which is divided by full stops.
* Returns the value of the property at the end of the path or NULL if path
* destination was not found.
*
* @param \MyCompany\MyApplication\Domain\Model\AbstractDataModel $object
* @param string $propertyPath
* @param string $format If provided, \DateTime objects will automatically be returned as formatted strings
* @return mixed
*/
public function traverseObject($object, $propertyPath, $format = 'd.m.Y') {
$properties = explode('.', $propertyPath);
foreach($properties as $property) {
if(is_object($object)) {
try {
$method = 'get'.ucfirst($property);
$object = $object->$method();
}
catch(Exception\PropertyNotFoundException $e) {
return NULL;
}
}
else {
return NULL;
}
}
if(is_string($format) && strlen($format) > 0 && $object instanceof \DateTime) {
$object = $object->format($format);
}
return $object;
}
/**
* Returns a lowercase version of the class name, without namespace
*
* @param string $className
* @return string
*/
static public function getPropertyNameFromClassname($className) {
return strtolower(substr($className, strrpos($className, '\\')+1));
}
/**
* Gets the class name from a collection name with generic type definition like Collection<\The\Class\Name>
*
* @param $collectionName
* @return string
*/
static public function getGenericTypeOfCollection($collectionName) {
if(strpos($collectionName, '<') !== FALSE) {
return substr($collectionName, strpos($collectionName, '<')+1, strpos($collectionName, '>')-strpos($collectionName, '<')-1);
}
return $collectionName;
}
/**
* @param string $proxyClassName
* @return string
*/
static public function getClassNameFromProxyClassName($proxyClassName) {
return str_replace('TYPO3\Flow\Persistence\Doctrine\Proxies\__CG__', '', $proxyClassName);
}
/**
* @return array
*/
static public function getForbiddenProperties() {
return array(
'settings',
'data',
'dataRepository'
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment