Generates a closure capable of finalizing a cloned proxy

@param ClassMetadata $classMetadata
@param DocumentPersister $documentPersister

@return \Closure

@throws DocumentNotFoundException
494481
private function createCloner(
        ClassMetadata $classMetadata,
        DocumentPersister $documentPersister
    )
    {
        return function (BaseProxy $proxy) use ($documentPersister, $classMetadata) {
            if ($proxy->__isInitialized()) {
                return;
            }

            $proxy->__setInitialized(true);
            $proxy->__setInitializer(null);

            $identifier = $classMetadata->createIdentifier($proxy);
            $original   = $documentPersister->load($identifier);

            if ($original === null) {
                if (!$this->lifecycleEventManager->documentNotFound($proxy, $identifier)) {
                    throw DocumentNotFoundException::documentNotFound(get_class($proxy), $identifier);
                }
            }

            foreach ($classMetadata->getReflectionClass()->getProperties() as $reflectionProperty) {
                $propertyName = $reflectionProperty->getName();

                if ($classMetadata->hasField($propertyName) || $classMetadata->hasAssociation($propertyName)) {
                    $reflectionProperty->setAccessible(true);
                    $reflectionProperty->setValue($proxy, $reflectionProperty->getValue($original));
                }
            }
        };
    }
1250996
public function getProxyFactory(NodeEntityMetadata $entityMetadata)
    {
        if (!array_key_exists($entityMetadata->getClassName(), $this->proxyFactories)) {
            $this->proxyFactories[$entityMetadata->getClassName()] = new ProxyFactory($this, $entityMetadata);
        }

        return $this->proxyFactories[$entityMetadata->getClassName()];
    }
118766
private function createRepository(DocumentManager $documentManager, $documentName)
    {
        /* @var $metadata \Doctrine\ODM\CouchDB\Mapping\ClassMetadata */
        $metadata = $documentManager->getClassMetadata($documentName);
        $repositoryClassName = $metadata->customRepositoryClassName
            ?: $documentManager->getDefaultRepositoryClassName();

        return new $repositoryClassName($documentManager, $metadata);
    }
1170539
private function isSoftDeletable(ClassMetadata $classMetadata)
    {
        return $this->getClassAnalyzer()->hasTrait($classMetadata->reflClass, $this->softDeletableTrait, $this->isRecursive);
    }
546978
private function createCloner(ClassMetadata $classMetadata, ReflectionProperty $reflectionId = null)
    {
        $className = $classMetadata->getName();
        $documentManager = $this->documentManager;

        return function (Proxy $cloned) use ($className, $classMetadata, $documentManager, $reflectionId) {
            if ($cloned->__isInitialized()) {
                return;
            }

            $cloned->__setInitialized(true);
            $cloned->__setInitializer(null);

            if (!$reflectionId) {
                return;
            }

            $original = $documentManager->find($className, $reflectionId->getValue($cloned));

            if (null === $original) {
                throw new UnexpectedValueException(sprintf(
                    'Proxy with ID "%s" could not be loaded',
                    $reflectionId->getValue($cloned)
                ));
            }

            foreach ($classMetadata->getReflectionClass()->getProperties() as $reflectionProperty) {
                $propertyName = $reflectionProperty->getName();

                if ($classMetadata->hasField($propertyName) || $classMetadata->hasAssociation($propertyName)) {
                    $reflectionProperty->setAccessible(true);
                    $reflectionProperty->setValue($cloned, $reflectionProperty->getValue($original));
                }
            }
        };
    }
1056337
public function ensureDocumentIndexes($documentName, $timeout = null)
    {
        $class = $this->dm->getClassMetadata($documentName);
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument) {
            throw new \InvalidArgumentException('Cannot create document indexes for mapped super classes or embedded documents.');
        }
        if ($indexes = $this->getDocumentIndexes($documentName)) {
            $collection = $this->dm->getDocumentCollection($class->name);
            foreach ($indexes as $index) {
                $keys = $index['keys'];
                $options = $index['options'];

                if ( ! isset($options['safe']) && ! isset($options['w'])) {
                    if (version_compare(phpversion('mongo'), '1.3.0', '<')) {
                        $options['safe'] = true;
                    } else {
                        $options['w'] = 1;
                    }
                }

                if (isset($options['safe']) && ! isset($options['w']) &&
                    version_compare(phpversion('mongo'), '1.3.0', '>=')) {

                    $options['w'] = is_bool($options['safe']) ? (integer) $options['safe'] : $options['safe'];
                    unset($options['safe']);
                }

                if ( ! isset($options['timeout']) && isset($timeout)) {
                    $options['timeout'] = $timeout;
                }

                $collection->ensureIndex($keys, $options);
            }
        }
    }
946403
private function executeDeletions(ClassMetadata $class, array $options = array())
    {
        $hasPostRemoveLifecycleCallbacks = ! empty($class->lifecycleCallbacks[Events::postRemove]);
        $hasPostRemoveListeners = $this->evm->hasListeners(Events::postRemove);

        $className = $class->name;
        $persister = $this->getDocumentPersister($className);
        $collection = $this->dm->getDocumentCollection($className);
        foreach ($this->documentDeletions as $oid => $document) {
            if (get_class($document) == $className || $document instanceof Proxy && $document instanceof $className) {
                if ( ! $class->isEmbeddedDocument) {
                    $persister->delete($document, $options);
                }
                unset(
                    $this->documentDeletions[$oid],
                    $this->documentIdentifiers[$oid],
                    $this->originalDocumentData[$oid]
                );

                // Clear snapshot information for any referenced PersistentCollection
                // http://www.doctrine-project.org/jira/browse/MODM-95
                foreach ($class->fieldMappings as $fieldMapping) {
                    if (isset($fieldMapping['type']) && $fieldMapping['type'] === 'many') {
                        $value = $class->reflFields[$fieldMapping['fieldName']]->getValue($document);
                        if ($value instanceof PersistentCollection) {
                            $value->clearSnapshot();
                        }
                    }
                }

                // Document with this $oid after deletion treated as NEW, even if the $oid
                // is obtained by a new document because the old one went out of scope.
                $this->documentStates[$oid] = self::STATE_NEW;

                if ($hasPostRemoveLifecycleCallbacks) {
                    $class->invokeLifecycleCallbacks(Events::postRemove, $document);
                }
                if ($hasPostRemoveListeners) {
                    $this->evm->dispatchEvent(Events::postRemove, new LifecycleEventArgs($document, $this->dm));
                }
                $this->cascadePostRemove($class, $document);
            }
        }
    }
1355015
private function createCollection(DocumentManager $documentManager, ClassMetadata $classMetadata, $collectionName)
    {

        $database = $documentManager->getDatabase();

        $exists = false;
        foreach ($database->listCollections() as $collection) {
            if ($collection->getName() == $collectionName) {
                $exists = true;
            }
        }

        $creationOptions = $classMetadata->getCollectionCreationOptions();
        if (!empty($creationOptions) && !$exists) {
            $database->createCollection($collectionName, $creationOptions);
        }

        $collectionOptions = $classMetadata->getCollectionOptions();

        return $database->selectCollection($collectionName, $collectionOptions);
    }
769074
private function cascadeDetach(ClassMetadata $class, $document, array &$visited)
    {
        foreach ($class->childrenMappings as $fieldName) {
            $mapping = $class->mappings[$fieldName];
            if (!($mapping['cascade'] & ClassMetadata::CASCADE_DETACH)) {
                continue;
            }
            $related = $class->reflFields[$fieldName]->getValue($document);
            if ($related instanceof Collection || is_array($related)) {
                foreach ($related as $relatedDocument) {
                    $this->doDetach($relatedDocument, $visited);
                }
            } elseif ($related !== null) {
                $this->doDetach($related, $visited);
            }
        }

        foreach (array_merge($class->referenceMappings, $class->referrersMappings) as $fieldName) {
            $mapping = $class->mappings[$fieldName];
            if (!($mapping['cascade'] & ClassMetadata::CASCADE_DETACH)) {
                continue;
            }
            $related = $class->reflFields[$fieldName]->getValue($document);
            if ($related instanceof Collection || is_array($related)) {
                foreach ($related as $relatedDocument) {
                    $this->doDetach($relatedDocument, $visited);
                }
            } elseif ($related !== null) {
                $this->doDetach($related, $visited);
            }
        }

        // no cascade for mixed referrers
    }
841627
public function getDocumentPersister(string $documentClass): DocumentPersister
    {
        if (isset($this->documentPersisters[$documentClass])) {
            return $this->documentPersisters[$documentClass];
        }

        return $this->documentPersisters[$documentClass] = new DocumentPersister($this->manager, $this->manager->getClassMetadata($documentClass));
    }
1118120
public function generate(ReflectionClass $originalClass, ClassGenerator $classGenerator)
    {
        CanProxyAssertion::assertClassCanBeProxied($originalClass, false);

        $classGenerator->setExtendedClass($originalClass->getName());
        $classGenerator->setImplementedInterfaces([AccessInterceptorInterface::class]);
        $classGenerator->addPropertyFromGenerator($prefixInterceptors = new MethodPrefixInterceptors());
        $classGenerator->addPropertyFromGenerator($suffixInterceptors = new MethodSuffixInterceptors());

        array_map(
            static function (MethodGenerator $generatedMethod) use ($originalClass, $classGenerator) : void {
                ClassGeneratorUtils::addMethodIfNotFinal($originalClass, $classGenerator, $generatedMethod);
            },
            array_merge(
                array_map(
                    $this->buildMethodInterceptor($prefixInterceptors, $suffixInterceptors),
                    ProxiedMethodsFilter::getProxiedMethods(
                        $originalClass,
                        ['__get', '__set', '__isset', '__unset', '__clone', '__sleep']
                    )
                ),
                [
                    new StaticProxyConstructor($originalClass),
                    new BindProxyProperties($originalClass, $prefixInterceptors, $suffixInterceptors),
                    new SetMethodPrefixInterceptor($prefixInterceptors),
                    new SetMethodSuffixInterceptor($suffixInterceptors),
                    new MagicGet($originalClass, $prefixInterceptors, $suffixInterceptors),
                    new MagicSet($originalClass, $prefixInterceptors, $suffixInterceptors),
                    new MagicIsset($originalClass, $prefixInterceptors, $suffixInterceptors),
                    new MagicUnset($originalClass, $prefixInterceptors, $suffixInterceptors),
                    new MagicSleep($originalClass, $prefixInterceptors, $suffixInterceptors),
                    new MagicClone($originalClass, $prefixInterceptors, $suffixInterceptors),
                ]
            )
        );
    }
998674
public function postRemove(ClassMetadata $class, $document)
    {
        $class->invokeLifecycleCallbacks(Event::postRemove, $document, [new LifecycleEventArgs($document, $this->dm)]);
        $this->evm->dispatchEvent(Event::postRemove, new LifecycleEventArgs($document, $this->dm));
    }
935043
private function newInstance(DocumentMetadata $class)
    {
        $document = $class->newInstance();

        // inject ObjectManager upon refresh.
        if ($document instanceof ObjectManagerAware) {
            $document->injectObjectManager($this->manager, $class);
        }

        return $document;
    }
318157
protected function createResourceRepository(
        $class,
        DocumentManager $documentManager,
        ClassMetadata $metadata,
        ResourceInterface $resource = null
    ) {
        if ($resource !== null && is_a($class, BaseRepositoryInterface::class, true)) {
            return new $class($documentManager, $documentManager->getUnitOfWork(), $metadata, $resource);
        }

        return parent::createRepository($documentManager, $metadata->getName());
    }
103629
public function generateProxyClass(string $documentClass): string
    {
        if (array_key_exists($documentClass, $this->proxyClassNames)) {
            return $this->proxyClassNames[$documentClass];
        }

        $proxyClassName = $this->getProxyClassName($documentClass);
        $proxyGenerator = $this->getGenerator($documentClass);
        $classGenerator = $proxyGenerator->generateClass();
        $proxyFile = $this->getProxyFileName($proxyClassName);
        $proxyDir = dirname($proxyFile);
        is_dir($proxyDir) || mkdir($proxyDir, 0777, true);
        $fileGenerator = new FileGenerator();
        $fileGenerator->setClass($classGenerator);
        $code = $fileGenerator->generate();
        file_put_contents($proxyFile, $code);

        $this->proxyClassNames[$documentClass] = $proxyClassName;

        return $proxyClassName;
    }
495989
public function scheduleForUpsert(ClassMetadata $class, object $document) : void
    {
        $oid = spl_object_hash($document);

        if ($class->isEmbeddedDocument) {
            throw new InvalidArgumentException('Embedded document can not be scheduled for upsert.');
        }
        if (isset($this->documentUpdates[$oid])) {
            throw new InvalidArgumentException('Dirty document can not be scheduled for upsert.');
        }
        if (isset($this->documentDeletions[$oid])) {
            throw new InvalidArgumentException('Removed document can not be scheduled for upsert.');
        }
        if (isset($this->documentUpserts[$oid])) {
            throw new InvalidArgumentException('Document can not be scheduled for upsert twice.');
        }

        $this->documentUpserts[$oid]     = $document;
        $this->documentIdentifiers[$oid] = $class->getIdentifierValue($document);
        $this->addToIdentityMap($document);
    }
343330
private function cascadePreRemove(ClassMetadata $class, $document)
    {
        $hasPreRemoveListeners = $this->evm->hasListeners(Events::preRemove);

        foreach ($class->fieldMappings as $mapping) {
            if (isset($mapping['embedded'])) {
                $value = $class->reflFields[$mapping['fieldName']]->getValue($document);
                if ($value === null) {
                    continue;
                }
                if ($mapping['type'] === 'one') {
                    $value = array($value);
                }
                foreach ($value as $entry) {
                    $entryClass = $this->dm->getClassMetadata(get_class($entry));
                    if ( ! empty($entryClass->lifecycleCallbacks[Events::preRemove])) {
                        $entryClass->invokeLifecycleCallbacks(Events::preRemove, $entry);
                    }
                    if ($hasPreRemoveListeners) {
                        $this->evm->dispatchEvent(Events::preRemove, new LifecycleEventArgs($entry, $this->dm));
                    }
                    $this->cascadePreRemove($entryClass, $entry);
                }
            }
        }
    }