private function encode(EventAdapterInterface $ea, $object, $config) { $om = $ea->getObjectManager(); $meta = $om->getClassMetadata(get_class($object)); $uow = $om->getUnitOfWork(); foreach ($config['encode'] as $field => $options) { $value = $meta->getReflectionProperty($field)->getValue($object); $method = $options['type']; $encoded = $method($options['secret'] . $value); $meta->getReflectionProperty($field)->setValue($object, $encoded); } // recalculate changeset $ea->recomputeSingleObjectChangeSet($uow, $meta, $object); }
/** * {@inheritdoc} */ protected function releaseTreeLocks(ObjectManager $om, AdapterInterface $ea) { $uow = $om->getUnitOfWork(); foreach ($this->rootsOfTreesWhichNeedsLocking as $oid => $root) { $meta = $om->getClassMetadata(get_class($root)); $config = $this->listener->getConfiguration($om, $meta->name); $lockTimeProp = $meta->getReflectionProperty($config['lock_time']); $lockTimeProp->setAccessible(true); $lockTimeValue = null; $lockTimeProp->setValue($root, $lockTimeValue); $changes = array($config['lock_time'] => array(null, null)); $ea->recomputeSingleObjectChangeSet($uow, $meta, $root); unset($this->rootsOfTreesWhichNeedsLocking[$oid]); } }
/** * Update the $node * * @param ObjectManager $om * @param object $node - target node * @param AdapterInterface $ea - event adapter * * @return void */ public function updateNode(ObjectManager $om, $node, AdapterInterface $ea) { $oid = spl_object_hash($node); $meta = $om->getClassMetadata(get_class($node)); $config = $this->listener->getConfiguration($om, $meta->name); $uow = $om->getUnitOfWork(); $parentProp = $meta->getReflectionProperty($config['parent']); $parentProp->setAccessible(true); $parent = $parentProp->getValue($node); $pathProp = $meta->getReflectionProperty($config['path']); $pathProp->setAccessible(true); $pathSourceProp = $meta->getReflectionProperty($config['path_source']); $pathSourceProp->setAccessible(true); $path = $pathSourceProp->getValue($node); // We need to avoid the presence of the path separator in the path source if (strpos($path, $config['path_separator']) !== false) { $msg = 'You can\'t use the Path separator ("%s") as a character for your PathSource field value.'; throw new RuntimeException(sprintf($msg, $config['path_separator'])); } $fieldMapping = $meta->getFieldMapping($config['path_source']); // default behavior: if PathSource field is a string, we append the ID to the path // path_append_id is true: always append id // path_append_id is false: never append id if ($config['path_append_id'] === true || $fieldMapping['type'] === 'string' && $config['path_append_id'] !== false) { if (method_exists($meta, 'getIdentifierValue')) { $identifier = $meta->getIdentifierValue($node); } else { $identifierProp = $meta->getReflectionProperty($meta->getSingleIdentifierFieldName()); $identifierProp->setAccessible(true); $identifier = $identifierProp->getValue($node); } $path .= '-' . $identifier; } if ($parent) { // Ensure parent has been initialized in the case where it's a proxy $om->initializeObject($parent); $changeSet = $uow->isScheduledForUpdate($parent) ? $ea->getObjectChangeSet($uow, $parent) : false; $pathOrPathSourceHasChanged = $changeSet && (isset($changeSet[$config['path_source']]) || isset($changeSet[$config['path']])); if ($pathOrPathSourceHasChanged || !$pathProp->getValue($parent)) { $this->updateNode($om, $parent, $ea); } $parentPath = $pathProp->getValue($parent); // if parent path not ends with separator if ($parentPath[strlen($parentPath) - 1] !== $config['path_separator']) { // add separator $path = $pathProp->getValue($parent) . $config['path_separator'] . $path; } else { // don't add separator $path = $pathProp->getValue($parent) . $path; } } if ($config['path_starts_with_separator'] && (strlen($path) > 0 && $path[0] !== $config['path_separator'])) { $path = $config['path_separator'] . $path; } if ($config['path_ends_with_separator'] && $path[strlen($path) - 1] !== $config['path_separator']) { $path .= $config['path_separator']; } $pathProp->setValue($node, $path); $changes = array($config['path'] => array(null, $path)); if (isset($config['path_hash'])) { $pathHash = md5($path); $pathHashProp = $meta->getReflectionProperty($config['path_hash']); $pathHashProp->setAccessible(true); $pathHashProp->setValue($node, $pathHash); $changes[$config['path_hash']] = array(null, $pathHash); } if (isset($config['level'])) { $level = substr_count($path, $config['path_separator']); $levelProp = $meta->getReflectionProperty($config['level']); $levelProp->setAccessible(true); $levelProp->setValue($node, $level); $changes[$config['level']] = array(null, $level); } if (!$uow instanceof MongoDBUnitOfWork) { $ea->setOriginalObjectProperty($uow, $oid, $config['path'], $path); $uow->scheduleExtraUpdate($node, $changes); } else { $ea->recomputeSingleObjectChangeSet($uow, $meta, $node); } if (isset($config['path_hash'])) { $ea->setOriginalObjectProperty($uow, $oid, $config['path_hash'], $pathHash); } }
/** * {@inheritdoc} */ public function processPostPersist($em, $entity, AdapterInterface $ea) { $uow = $em->getUnitOfWork(); while ($node = array_shift($this->pendingChildNodeInserts)) { $meta = $em->getClassMetadata(get_class($node)); $config = $this->listener->getConfiguration($em, $meta->name); $identifier = $meta->getSingleIdentifierFieldName(); $nodeId = $meta->getReflectionProperty($identifier)->getValue($node); $parent = $meta->getReflectionProperty($config['parent'])->getValue($node); $closureClass = $config['closure']; $closureMeta = $em->getClassMetadata($closureClass); $closureTable = $closureMeta->getTableName(); $ancestorColumnName = $this->getJoinColumnFieldName($em->getClassMetadata($config['closure'])->getAssociationMapping('ancestor')); $descendantColumnName = $this->getJoinColumnFieldName($em->getClassMetadata($config['closure'])->getAssociationMapping('descendant')); $depthColumnName = $em->getClassMetadata($config['closure'])->getColumnName('depth'); $entries = array(array($ancestorColumnName => $nodeId, $descendantColumnName => $nodeId, $depthColumnName => 0)); if ($parent) { $dql = "SELECT c, a FROM {$closureMeta->name} c"; $dql .= " JOIN c.ancestor a"; $dql .= " WHERE c.descendant = :parent"; $q = $em->createQuery($dql); $q->setParameters(compact('parent')); $ancestors = $q->getArrayResult(); foreach ($ancestors as $ancestor) { $entries[] = array($ancestorColumnName => $ancestor['ancestor']['id'], $descendantColumnName => $nodeId, $depthColumnName => $ancestor['depth'] + 1); } if (isset($config['level'])) { $this->pendingNodesLevelProcess[$nodeId] = $node; } } else { if (isset($config['level'])) { $uow->scheduleExtraUpdate($node, array($config['level'] => array(null, 1))); $ea->setOriginalObjectProperty($uow, spl_object_hash($node), $config['level'], 1); } } foreach ($entries as $closure) { if (!$em->getConnection()->insert($closureTable, $closure)) { throw new RuntimeException('Failed to insert new Closure record'); } } } // Process pending node updates if (!empty($this->pendingNodeUpdates)) { foreach ($this->pendingNodeUpdates as $info) { $this->updateNode($em, $info['node'], $info['oldParent']); } $this->pendingNodeUpdates = array(); } // Process TreeLevel field value $this->setLevelFieldOnPendingNodes($em); }
/** * Update the $node * * @param ObjectManager $om * @param object $node - target node * @param object $ea - event adapter * @return void */ public function updateNode(ObjectManager $om, $node, AdapterInterface $ea) { $oid = spl_object_hash($node); $meta = $om->getClassMetadata(get_class($node)); $config = $this->listener->getConfiguration($om, $meta->name); $uow = $om->getUnitOfWork(); $parentProp = $meta->getReflectionProperty($config['parent']); $parentProp->setAccessible(true); $parent = $parentProp->getValue($node); $pathProp = $meta->getReflectionProperty($config['path']); $pathProp->setAccessible(true); $pathSourceProp = $meta->getReflectionProperty($config['path_source']); $pathSourceProp->setAccessible(true); $path = $pathSourceProp->getValue($node); // We need to avoid the presence of the path separator in the path source if (strpos($path, $config['path_separator']) !== false) { $msg = 'You can\'t use the Path separator ("%s") as a character for your PathSource field value.'; throw new RuntimeException(sprintf($msg, $config['path_separator'])); } $fieldMapping = $meta->getFieldMapping($config['path_source']); // If PathSource field is a string, we append the ID to the path if ($fieldMapping['type'] === 'string') { if (method_exists($meta, 'getIdentifierValue')) { $identifier = $meta->getIdentifierValue($node); } else { $identifierProp = $meta->getReflectionProperty($meta->getSingleIdentifierFieldName()); $identifierProp->setAccessible(true); $identifier = $identifierProp->getValue($node); } $path .= '-' . $identifier; } $path .= $config['path_separator']; if ($parent) { $changeSet = $uow->isScheduledForUpdate($parent) ? $ea->getObjectChangeSet($uow, $parent) : false; $pathOrPathSourceHasChanged = $changeSet && (isset($changeSet[$config['path_source']]) || isset($changeSet[$config['path']])); if ($pathOrPathSourceHasChanged || !$pathProp->getValue($parent)) { $this->updateNode($om, $parent, $ea); } $path = $pathProp->getValue($parent) . $path; } $pathProp->setValue($node, $path); $changes = array($config['path'] => array(null, $path)); if (isset($config['level'])) { $level = substr_count($path, $config['path_separator']); $levelProp = $meta->getReflectionProperty($config['level']); $levelProp->setAccessible(true); $levelProp->setValue($node, $level); $changes[$config['level']] = array(null, $level); } $uow->scheduleExtraUpdate($node, $changes); $ea->setOriginalObjectProperty($uow, $oid, $config['path'], $path); }
protected function updateField($object, $uow, AdapterInterface $ea, $meta, $field, $value, $notifyPropertyChanged = true) { $property = $meta->getReflectionProperty($field); $oldValue = $property->getValue($object); $property->setValue($object, $value); if ($notifyPropertyChanged && $object instanceof NotifyPropertyChanged) { $uow = $ea->getObjectManager()->getUnitOfWork(); $uow->propertyChanged($object, $field, $oldValue, $value); } }
/** * If it's a Uploadable object, verify if the file was uploaded. * If that's the case, process it. * * @param UnitOfWork * @param AdapterInterface * @param ClassMetadata * @param array - Configuration * @param object - The entity * @param string - String representing the action (insert or update) * * @return void */ public function processFile(UnitOfWork $uow, AdapterInterface $ea, ClassMetadata $meta, array $config, $object, $action) { $refl = $meta->getReflectionClass(); $oid = spl_object_hash($object); if (!isset($this->fileInfoObjects[$oid])) { // Nothing to do return; } $fileInfo = $this->fileInfoObjects[$oid]; // Validations if ($config['maxSize'] > 0 && $fileInfo->getSize() > $config['maxSize']) { $msg = 'File "%s" exceeds the maximum allowed size of %d bytes. File size: %d bytes'; throw new UploadableMaxSizeException(sprintf($msg, $fileInfo->getName(), $config['maxSize'], $fileInfo->getSize())); } $mime = $this->mimeTypeGuesser->guess($fileInfo->getTmpName()); if (!$mime) { throw new UploadableCouldntGuessMimeTypeException(sprintf('Couldn\'t guess mime type for file "%s".', $fileInfo->getName())); } if ($config['allowedTypes'] || $config['disallowedTypes']) { $ok = $config['allowedTypes'] ? false : true; $mimes = $config['allowedTypes'] ? $config['allowedTypes'] : $config['disallowedTypes']; foreach ($mimes as $m) { if ($mime === $m) { $ok = $config['allowedTypes'] ? true : false; break; } } if (!$ok) { throw new UploadableInvalidMimeTypeException(sprintf('Invalid mime type "%s" for file "%s".', $mime, $fileInfo->getName())); } } $filePathField = $refl->getProperty($config['filePathField']); $filePathField->setAccessible(true); $path = $config['path']; if ($path === '') { if ($config['pathMethod'] !== '') { $pathMethod = $refl->getMethod($config['pathMethod']); $pathMethod->setAccessible(true); $path = $pathMethod->invoke($object); } else { if ($this->getDefaultPath() !== null) { $path = $this->getDefaultPath(); } else { $msg = 'You have to define the path to save files either in the listener, or in the class "%s"'; throw new UploadableNoPathDefinedException(sprintf($msg, $meta->name)); } } } Validator::validatePath($path); $path = substr($path, strlen($path) - 1) === '/' ? substr($path, 0, strlen($path) - 2) : $path; if ($config['fileMimeTypeField']) { $fileMimeTypeField = $refl->getProperty($config['fileMimeTypeField']); $fileMimeTypeField->setAccessible(true); } if ($config['fileSizeField']) { $fileSizeField = $refl->getProperty($config['fileSizeField']); $fileSizeField->setAccessible(true); } if ($action === self::ACTION_UPDATE) { // First we add the original file to the pendingFileRemovals array $this->pendingFileRemovals[] = $this->getFilePath($meta, $config, $object); } // We generate the filename based on configuration $generatorNamespace = 'Gedmo\\Uploadable\\FilenameGenerator'; switch ($config['filenameGenerator']) { case Validator::FILENAME_GENERATOR_ALPHANUMERIC: $generatorClass = $generatorNamespace . '\\FilenameGeneratorAlphanumeric'; break; case Validator::FILENAME_GENERATOR_SHA1: $generatorClass = $generatorNamespace . '\\FilenameGeneratorSha1'; break; case Validator::FILENAME_GENERATOR_NONE: $generatorClass = false; break; default: $generatorClass = $config['filenameGenerator']; } $info = $this->moveFile($fileInfo, $path, $generatorClass, $config['allowOverwrite'], $config['appendNumber']); // We override the mime type with the guessed one $info['fileMimeType'] = $mime; $filePathField->setValue($object, $info['filePath']); if ($config['callback'] !== '') { $callbackMethod = $refl->getMethod($config['callback']); $callbackMethod->setAccessible(true); $callbackMethod->invokeArgs($object, array($info)); } $changes = array($config['filePathField'] => array($filePathField->getValue($object), $info['filePath'])); if ($config['fileMimeTypeField']) { $changes[$config['fileMimeTypeField']] = array($fileMimeTypeField->getValue($object), $info['fileMimeType']); $ea->setOriginalObjectProperty($uow, $oid, $config['fileMimeTypeField'], $info['fileMimeType']); } if ($config['fileSizeField']) { $changes[$config['fileSizeField']] = array($fileSizeField->getValue($object), $info['fileSize']); $ea->setOriginalObjectProperty($uow, $oid, $config['fileSizeField'], $info['fileSize']); } $uow->scheduleExtraUpdate($object, $changes); $ea->setOriginalObjectProperty($uow, $oid, $config['filePathField'], $info['filePath']); unset($this->fileInfoObjects[$oid]); }
/** * @param AdapterInterface $ea * @throws \RuntimeException */ protected function initUploadableSubscriber(AdapterInterface $ea) { $em = $ea->getObjectManager()->getEventManager(); if (null === $this->uploadableSubscriber) { foreach ($em->getListeners('loadClassMetadata') as $listener) { if ($listener instanceof UploadableSubscriber) { $this->setUploadableSubscriber($listener); break; } } if (!$this->uploadableSubscriber) { throw new RuntimeException("UploadableSubscriber can't be found"); } } foreach ($this->uploadableSubscriberEvents as $event) { if (!$em->hasListeners($event) || !in_array($this, $em->getListeners($event), true)) { $em->addEventListener($event, $this); } } }
/** * @param AdapterInterface $ea * @param ObjectManager $om * @param UnitOfWork $uow * @param object $entity * @param string $method */ protected function handle(AdapterInterface $ea, ObjectManager $om, UnitOfWork $uow, $entity, $method) { /** * @var \Doctrine\ORM\EntityManager $om */ $meta = $om->getClassMetadata(get_class($entity)); $config = $this->getConfiguration($om, $meta->name); if (isset($config[self::TRANSFORMABLE]) && $config[self::TRANSFORMABLE]) { foreach ($config[self::TRANSFORMABLE] as $column) { $this->handleField($entity, $method, $column, $meta); } $ea->recomputeSingleObjectChangeSet($uow, $meta, $entity); } }