Actualización

This commit is contained in:
Xes
2025-04-10 12:24:57 +02:00
parent 8969cc929d
commit 45420b6f0d
39760 changed files with 4303286 additions and 0 deletions

View File

@@ -0,0 +1,36 @@
{
"active": true,
"name": "Data fixtures",
"slug": "data-fixtures",
"docsSlug": "doctrine-data-fixtures",
"versions": [
{
"name": "1.6",
"branchName": "1.6.x",
"slug": "1.6",
"upcoming": true
},
{
"name": "1.5",
"branchName": "1.5.x",
"slug": "latest",
"current": true,
"aliases": [
"current",
"stable"
]
},
{
"name": "1.4",
"branchName": "1.4.x",
"slug": "1.4",
"maintained": false
},
{
"name": "1.3",
"branchName": "1.3",
"slug": "1.3",
"maintained": false
}
]
}

20
vendor/doctrine/data-fixtures/LICENSE vendored Normal file
View File

@@ -0,0 +1,20 @@
Copyright (c) 2006-2015 Doctrine Project
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

21
vendor/doctrine/data-fixtures/README.md vendored Normal file
View File

@@ -0,0 +1,21 @@
# Doctrine Data Fixtures Extension
[![Build Status](https://github.com/doctrine/data-fixtures/workflows/Continuous%20Integration/badge.svg)](https://github.com/doctrine/data-fixtures/actions)
This extension aims to provide a simple way to manage and execute the loading of data fixtures
for the [Doctrine ORM or ODM](http://www.doctrine-project.org/).
## More resources:
* [Website](http://www.doctrine-project.org)
* [Documentation](https://www.doctrine-project.org/projects/doctrine-data-fixtures/en/latest/index.html)
## Running the tests:
Phpunit is included in the dev requirements of this package.
To setup and run tests follow these steps:
- go to the root directory of data-fixtures
- run: **composer install --dev**
- run: **vendor/bin/phpunit**

17
vendor/doctrine/data-fixtures/UPGRADE vendored Normal file
View File

@@ -0,0 +1,17 @@
# Between v1.0.0-ALPHA1 and v1.0.0-ALPHA2
The FixtureInterface was changed from
interface FixtureInterface
{
load($manager);
}
to
use Doctrine\Common\Persistence\ObjectManager;
interface FixtureInterface
{
load(ObjectManager $manager);
}

View File

@@ -0,0 +1,59 @@
{
"name": "doctrine/data-fixtures",
"type": "library",
"description": "Data Fixtures for all Doctrine Object Managers",
"keywords": [
"database"
],
"homepage": "https://www.doctrine-project.org",
"license": "MIT",
"authors": [
{
"name": "Jonathan Wage",
"email": "jonwage@gmail.com"
}
],
"require": {
"php": "^7.2 || ^8.0",
"doctrine/common": "^2.13|^3.0",
"doctrine/persistence": "^1.3.3|^2.0|^3.0"
},
"conflict": {
"doctrine/phpcr-odm": "<1.3.0",
"doctrine/dbal": "<2.13"
},
"require-dev": {
"ext-sqlite3": "*",
"doctrine/coding-standard": "^10.0",
"doctrine/dbal": "^2.13 || ^3.0",
"doctrine/mongodb-odm": "^1.3.0 || ^2.0.0",
"doctrine/orm": "^2.7.0",
"phpstan/phpstan": "^1.5",
"phpunit/phpunit": "^8.5 || ^9.5",
"symfony/cache": "^5.0 || ^6.0",
"vimeo/psalm": "^4.10"
},
"suggest": {
"alcaeus/mongo-php-adapter": "For using MongoDB ODM 1.3 with PHP 7 (deprecated)",
"doctrine/mongodb-odm": "For loading MongoDB ODM fixtures",
"doctrine/orm": "For loading ORM fixtures",
"doctrine/phpcr-odm": "For loading PHPCR ODM fixtures"
},
"config": {
"sort-packages": true,
"allow-plugins": {
"composer/package-versions-deprecated": true,
"dealerdirect/phpcodesniffer-composer-installer": true
}
},
"autoload": {
"psr-4": {
"Doctrine\\Common\\DataFixtures\\": "lib/Doctrine/Common/DataFixtures"
}
},
"autoload-dev": {
"psr-4": {
"Doctrine\\Tests\\": "tests/Doctrine/Tests"
}
}
}

View File

@@ -0,0 +1,97 @@
<?php
declare(strict_types=1);
namespace Doctrine\Common\DataFixtures;
use BadMethodCallException;
/**
* Abstract Fixture class helps to manage references
* between fixture classes in order to set relations
* among other fixtures
*/
abstract class AbstractFixture implements SharedFixtureInterface
{
/**
* Fixture reference repository
*
* @var ReferenceRepository
*/
protected $referenceRepository;
/**
* {@inheritdoc}
*/
public function setReferenceRepository(ReferenceRepository $referenceRepository)
{
$this->referenceRepository = $referenceRepository;
}
/**
* Set the reference entry identified by $name
* and referenced to managed $object. If $name
* already is set, it overrides it
*
* @see Doctrine\Common\DataFixtures\ReferenceRepository::setReference
*
* @param string $name
* @param object $object - managed object
*
* @return void
*/
public function setReference($name, $object)
{
$this->referenceRepository->setReference($name, $object);
}
/**
* Set the reference entry identified by $name
* and referenced to managed $object. If $name
* already is set, it throws a
* BadMethodCallException exception
*
* @see Doctrine\Common\DataFixtures\ReferenceRepository::addReference
*
* @param string $name
* @param object $object - managed object
*
* @return void
*
* @throws BadMethodCallException - if repository already has a reference by $name.
*/
public function addReference($name, $object)
{
$this->referenceRepository->addReference($name, $object);
}
/**
* Loads an object using stored reference
* named by $name
*
* @see Doctrine\Common\DataFixtures\ReferenceRepository::getReference
*
* @param string $name
*
* @return object
*/
public function getReference($name)
{
return $this->referenceRepository->getReference($name);
}
/**
* Check if an object is stored using reference
* named by $name
*
* @see Doctrine\Common\DataFixtures\ReferenceRepository::hasReference
*
* @param string $name
*
* @return bool
*/
public function hasReference($name)
{
return $this->referenceRepository->hasReference($name);
}
}

View File

@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace Doctrine\Common\DataFixtures;
/**
* DependentFixtureInterface needs to be implemented by fixtures which depend on other fixtures
*/
interface DependentFixtureInterface
{
/**
* This method must return an array of fixtures classes
* on which the implementing class depends on
*
* @psalm-return array<class-string<FixtureInterface>>
*/
public function getDependencies();
}

View File

@@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
namespace Doctrine\Common\DataFixtures\Event\Listener;
use Doctrine\Common\DataFixtures\ReferenceRepository;
use Doctrine\Common\EventSubscriber;
use Doctrine\ODM\MongoDB\Event\LifecycleEventArgs;
/**
* Reference Listener populates identities for
* stored references
*/
final class MongoDBReferenceListener implements EventSubscriber
{
/** @var ReferenceRepository */
private $referenceRepository;
public function __construct(ReferenceRepository $referenceRepository)
{
$this->referenceRepository = $referenceRepository;
}
/**
* {@inheritdoc}
*/
public function getSubscribedEvents()
{
return ['postPersist'];
}
/**
* Populates identities for stored references
*
* @return void
*/
public function postPersist(LifecycleEventArgs $args)
{
$object = $args->getDocument();
$names = $this->referenceRepository->getReferenceNames($object);
if ($names === false) {
return;
}
foreach ($names as $name) {
$identity = $args->getDocumentManager()
->getUnitOfWork()
->getDocumentIdentifier($object);
$this->referenceRepository->setReferenceIdentity($name, $identity);
}
}
}

View File

@@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace Doctrine\Common\DataFixtures\Event\Listener;
use Doctrine\Common\DataFixtures\ReferenceRepository;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\LifecycleEventArgs;
/**
* Reference Listener populates identities for
* stored references
*/
final class ORMReferenceListener implements EventSubscriber
{
/** @var ReferenceRepository */
private $referenceRepository;
public function __construct(ReferenceRepository $referenceRepository)
{
$this->referenceRepository = $referenceRepository;
}
/**
* {@inheritdoc}
*/
public function getSubscribedEvents()
{
// would be better to use onClear, but it is supported only in 2.1
return ['postPersist'];
}
/**
* Populates identities for stored references
*
* @return void
*/
public function postPersist(LifecycleEventArgs $args)
{
$object = $args->getEntity();
$names = $this->referenceRepository->getReferenceNames($object);
if ($names === false) {
return;
}
foreach ($names as $name) {
$identity = $args->getEntityManager()
->getUnitOfWork()
->getEntityIdentifier($object);
$this->referenceRepository->setReferenceIdentity($name, $identity);
}
}
}

View File

@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace Doctrine\Common\DataFixtures\Exception;
use Doctrine\Common\CommonException;
class CircularReferenceException extends CommonException
{
}

View File

@@ -0,0 +1,161 @@
<?php
declare(strict_types=1);
namespace Doctrine\Common\DataFixtures\Executor;
use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Common\DataFixtures\Purger\PurgerInterface;
use Doctrine\Common\DataFixtures\ReferenceRepository;
use Doctrine\Common\DataFixtures\SharedFixtureInterface;
use Doctrine\Persistence\ObjectManager;
use Exception;
use function get_class;
use function interface_exists;
use function sprintf;
/**
* Abstract fixture executor.
*/
abstract class AbstractExecutor
{
/**
* Purger instance for purging database before loading data fixtures
*
* @var PurgerInterface
*/
protected $purger;
/**
* Logger callback for logging messages when loading data fixtures
*
* @var callable
*/
protected $logger;
/**
* Fixture reference repository
*
* @var ReferenceRepository
*/
protected $referenceRepository;
public function __construct(ObjectManager $manager)
{
$this->referenceRepository = new ReferenceRepository($manager);
}
/** @return ReferenceRepository */
public function getReferenceRepository()
{
return $this->referenceRepository;
}
public function setReferenceRepository(ReferenceRepository $referenceRepository)
{
$this->referenceRepository = $referenceRepository;
}
/**
* Sets the Purger instance to use for this executor instance.
*
* @return void
*/
public function setPurger(PurgerInterface $purger)
{
$this->purger = $purger;
}
/** @return PurgerInterface */
public function getPurger()
{
return $this->purger;
}
/**
* Set the logger callable to execute with the log() method.
*
* @param callable $logger
*
* @return void
*/
public function setLogger($logger)
{
$this->logger = $logger;
}
/**
* Logs a message using the logger.
*
* @param string $message
*
* @return void
*/
public function log($message)
{
$logger = $this->logger;
$logger($message);
}
/**
* Load a fixture with the given persistence manager.
*
* @return void
*/
public function load(ObjectManager $manager, FixtureInterface $fixture)
{
if ($this->logger) {
$prefix = '';
if ($fixture instanceof OrderedFixtureInterface) {
$prefix = sprintf('[%d] ', $fixture->getOrder());
}
$this->log('loading ' . $prefix . get_class($fixture));
}
// additionally pass the instance of reference repository to shared fixtures
if ($fixture instanceof SharedFixtureInterface) {
$fixture->setReferenceRepository($this->referenceRepository);
}
$fixture->load($manager);
$manager->clear();
}
/**
* Purges the database before loading.
*
* @return void
*
* @throws Exception if the purger is not defined.
*/
public function purge()
{
if ($this->purger === null) {
throw new Exception(
PurgerInterface::class .
' instance is required if you want to purge the database before loading your data fixtures.'
);
}
if ($this->logger) {
$this->log('purging database');
}
$this->purger->purge();
}
/**
* Executes the given array of data fixtures.
*
* @param FixtureInterface[] $fixtures Array of fixtures to execute.
* @param bool $append Whether to append the data fixtures or purge the database before loading.
*
* @return void
*/
abstract public function execute(array $fixtures, $append = false);
}
interface_exists(ObjectManager::class);

View File

@@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
namespace Doctrine\Common\DataFixtures\Executor;
use Doctrine\Common\DataFixtures\Event\Listener\MongoDBReferenceListener;
use Doctrine\Common\DataFixtures\Purger\MongoDBPurger;
use Doctrine\Common\DataFixtures\ReferenceRepository;
use Doctrine\ODM\MongoDB\DocumentManager;
/**
* Class responsible for executing data fixtures.
*/
class MongoDBExecutor extends AbstractExecutor
{
/** @var DocumentManager */
private $dm;
/** @var MongoDBReferenceListener */
private $listener;
/**
* Construct new fixtures loader instance.
*
* @param DocumentManager $dm DocumentManager instance used for persistence.
*/
public function __construct(DocumentManager $dm, ?MongoDBPurger $purger = null)
{
$this->dm = $dm;
if ($purger !== null) {
$this->purger = $purger;
$this->purger->setDocumentManager($dm);
}
parent::__construct($dm);
$this->listener = new MongoDBReferenceListener($this->referenceRepository);
$dm->getEventManager()->addEventSubscriber($this->listener);
}
/**
* Retrieve the DocumentManager instance this executor instance is using.
*
* @return DocumentManager
*/
public function getObjectManager()
{
return $this->dm;
}
/** @inheritDoc */
public function setReferenceRepository(ReferenceRepository $referenceRepository)
{
$this->dm->getEventManager()->removeEventListener(
$this->listener->getSubscribedEvents(),
$this->listener
);
$this->referenceRepository = $referenceRepository;
$this->listener = new MongoDBReferenceListener($this->referenceRepository);
$this->dm->getEventManager()->addEventSubscriber($this->listener);
}
/** @inheritDoc */
public function execute(array $fixtures, $append = false)
{
if ($append === false) {
$this->purge();
}
foreach ($fixtures as $fixture) {
$this->load($this->dm, $fixture);
}
}
}

View File

@@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
namespace Doctrine\Common\DataFixtures\Executor;
use Doctrine\Common\DataFixtures\Event\Listener\ORMReferenceListener;
use Doctrine\Common\DataFixtures\Purger\ORMPurgerInterface;
use Doctrine\Common\DataFixtures\ReferenceRepository;
use Doctrine\ORM\EntityManagerInterface;
/**
* Class responsible for executing data fixtures.
*/
class ORMExecutor extends AbstractExecutor
{
/** @var EntityManagerInterface */
private $em;
/** @var ORMReferenceListener */
private $listener;
/**
* Construct new fixtures loader instance.
*
* @param EntityManagerInterface $em EntityManagerInterface instance used for persistence.
*/
public function __construct(EntityManagerInterface $em, ?ORMPurgerInterface $purger = null)
{
$this->em = $em;
if ($purger !== null) {
$this->purger = $purger;
$this->purger->setEntityManager($em);
}
parent::__construct($em);
$this->listener = new ORMReferenceListener($this->referenceRepository);
$em->getEventManager()->addEventSubscriber($this->listener);
}
/**
* Retrieve the EntityManagerInterface instance this executor instance is using.
*
* @return EntityManagerInterface
*/
public function getObjectManager()
{
return $this->em;
}
/** @inheritDoc */
public function setReferenceRepository(ReferenceRepository $referenceRepository)
{
$this->em->getEventManager()->removeEventListener(
$this->listener->getSubscribedEvents(),
$this->listener
);
$this->referenceRepository = $referenceRepository;
$this->listener = new ORMReferenceListener($this->referenceRepository);
$this->em->getEventManager()->addEventSubscriber($this->listener);
}
/** @inheritDoc */
public function execute(array $fixtures, $append = false)
{
$executor = $this;
$this->em->transactional(static function (EntityManagerInterface $em) use ($executor, $fixtures, $append) {
if ($append === false) {
$executor->purge();
}
foreach ($fixtures as $fixture) {
$executor->load($em, $fixture);
}
});
}
}

View File

@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
namespace Doctrine\Common\DataFixtures\Executor;
use Doctrine\Common\DataFixtures\Purger\PHPCRPurger;
use Doctrine\ODM\PHPCR\DocumentManagerInterface;
use function method_exists;
/**
* Class responsible for executing data fixtures.
*/
class PHPCRExecutor extends AbstractExecutor
{
/** @var DocumentManagerInterface */
private $dm;
/**
* @param DocumentManagerInterface $dm manager instance used for persisting the fixtures
* @param PHPCRPurger|null $purger to remove the current data if append is false
*/
public function __construct(DocumentManagerInterface $dm, ?PHPCRPurger $purger = null)
{
parent::__construct($dm);
$this->dm = $dm;
if ($purger === null) {
return;
}
$purger->setDocumentManager($dm);
$this->setPurger($purger);
}
/** @return DocumentManagerInterface */
public function getObjectManager()
{
return $this->dm;
}
/** @inheritDoc */
public function execute(array $fixtures, $append = false)
{
$that = $this;
$function = static function ($dm) use ($append, $that, $fixtures) {
if ($append === false) {
$that->purge();
}
foreach ($fixtures as $fixture) {
$that->load($dm, $fixture);
}
};
if (method_exists($this->dm, 'transactional')) {
$this->dm->transactional($function);
} else {
$function($this->dm);
}
}
}

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace Doctrine\Common\DataFixtures;
use Doctrine\Persistence\ObjectManager;
use function interface_exists;
/**
* Interface contract for fixture classes to implement.
*/
interface FixtureInterface
{
/**
* Load data fixtures with the passed EntityManager
*/
public function load(ObjectManager $manager);
}
interface_exists(ObjectManager::class);

View File

@@ -0,0 +1,458 @@
<?php
declare(strict_types=1);
namespace Doctrine\Common\DataFixtures;
use ArrayIterator;
use Doctrine\Common\DataFixtures\Exception\CircularReferenceException;
use InvalidArgumentException;
use Iterator;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use ReflectionClass;
use RuntimeException;
use SplFileInfo;
use function array_keys;
use function array_merge;
use function asort;
use function class_exists;
use function class_implements;
use function count;
use function get_class;
use function get_declared_classes;
use function implode;
use function in_array;
use function is_array;
use function is_dir;
use function is_readable;
use function realpath;
use function sort;
use function sprintf;
use function usort;
/**
* Class responsible for loading data fixture classes.
*/
class Loader
{
/**
* Array of fixture object instances to execute.
*
* @psalm-var array<class-string<FixtureInterface>, FixtureInterface>
*/
private $fixtures = [];
/**
* Array of ordered fixture object instances.
*
* @psalm-var array<class-string<OrderedFixtureInterface>, OrderedFixtureInterface>|list<OrderedFixtureInterface>
*/
private $orderedFixtures = [];
/**
* Determines if we must order fixtures by number
*
* @var bool
*/
private $orderFixturesByNumber = false;
/**
* Determines if we must order fixtures by its dependencies
*
* @var bool
*/
private $orderFixturesByDependencies = false;
/**
* The file extension of fixture files.
*
* @var string
*/
private $fileExtension = '.php';
/**
* Find fixtures classes in a given directory and load them.
*
* @param string $dir Directory to find fixture classes in.
*
* @return array $fixtures Array of loaded fixture object instances.
*/
public function loadFromDirectory($dir)
{
if (! is_dir($dir)) {
throw new InvalidArgumentException(sprintf('"%s" does not exist', $dir));
}
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir),
RecursiveIteratorIterator::LEAVES_ONLY
);
return $this->loadFromIterator($iterator);
}
/**
* Find fixtures classes in a given file and load them.
*
* @param string $fileName File to find fixture classes in.
*
* @return array $fixtures Array of loaded fixture object instances.
*/
public function loadFromFile($fileName)
{
if (! is_readable($fileName)) {
throw new InvalidArgumentException(sprintf('"%s" does not exist or is not readable', $fileName));
}
$iterator = new ArrayIterator([new SplFileInfo($fileName)]);
return $this->loadFromIterator($iterator);
}
/**
* Has fixture?
*
* @param FixtureInterface $fixture
*
* @return bool
*/
public function hasFixture($fixture)
{
return isset($this->fixtures[get_class($fixture)]);
}
/**
* Get a specific fixture instance
*
* @param string $className
*
* @return FixtureInterface
*/
public function getFixture($className)
{
if (! isset($this->fixtures[$className])) {
throw new InvalidArgumentException(sprintf(
'"%s" is not a registered fixture',
$className
));
}
return $this->fixtures[$className];
}
/**
* Add a fixture object instance to the loader.
*/
public function addFixture(FixtureInterface $fixture)
{
$fixtureClass = get_class($fixture);
if (isset($this->fixtures[$fixtureClass])) {
return;
}
if ($fixture instanceof OrderedFixtureInterface && $fixture instanceof DependentFixtureInterface) {
throw new InvalidArgumentException(sprintf(
'Class "%s" can\'t implement "%s" and "%s" at the same time.',
get_class($fixture),
'OrderedFixtureInterface',
'DependentFixtureInterface'
));
}
$this->fixtures[$fixtureClass] = $fixture;
if ($fixture instanceof OrderedFixtureInterface) {
$this->orderFixturesByNumber = true;
} elseif ($fixture instanceof DependentFixtureInterface) {
$this->orderFixturesByDependencies = true;
foreach ($fixture->getDependencies() as $class) {
if (! class_exists($class)) {
continue;
}
$this->addFixture($this->createFixture($class));
}
}
}
/**
* Returns the array of data fixtures to execute.
*
* @psalm-return array<class-string<OrderedFixtureInterface>|int, OrderedFixtureInterface>
*/
public function getFixtures()
{
$this->orderedFixtures = [];
if ($this->orderFixturesByNumber) {
$this->orderFixturesByNumber();
}
if ($this->orderFixturesByDependencies) {
$this->orderFixturesByDependencies();
}
if (! $this->orderFixturesByNumber && ! $this->orderFixturesByDependencies) {
$this->orderedFixtures = $this->fixtures;
}
return $this->orderedFixtures;
}
/**
* Check if a given fixture is transient and should not be considered a data fixtures
* class.
*
* @psalm-param class-string<object> $className
*
* @return bool
*/
public function isTransient($className)
{
$rc = new ReflectionClass($className);
if ($rc->isAbstract()) {
return true;
}
$interfaces = class_implements($className);
return ! in_array(FixtureInterface::class, $interfaces);
}
/**
* Creates the fixture object from the class.
*
* @param string $class
*
* @return FixtureInterface
*/
protected function createFixture($class)
{
return new $class();
}
/**
* Orders fixtures by number
*
* @todo maybe there is a better way to handle reordering
*/
private function orderFixturesByNumber(): void
{
$this->orderedFixtures = $this->fixtures;
usort($this->orderedFixtures, static function (FixtureInterface $a, FixtureInterface $b): int {
if ($a instanceof OrderedFixtureInterface && $b instanceof OrderedFixtureInterface) {
if ($a->getOrder() === $b->getOrder()) {
return 0;
}
return $a->getOrder() < $b->getOrder() ? -1 : 1;
}
if ($a instanceof OrderedFixtureInterface) {
return $a->getOrder() === 0 ? 0 : 1;
}
if ($b instanceof OrderedFixtureInterface) {
return $b->getOrder() === 0 ? 0 : -1;
}
return 0;
});
}
/**
* Orders fixtures by dependencies
*
* @return void
*/
private function orderFixturesByDependencies()
{
/** @psalm-var array<class-string<DependentFixtureInterface>, int> */
$sequenceForClasses = [];
// If fixtures were already ordered by number then we need
// to remove classes which are not instances of OrderedFixtureInterface
// in case fixtures implementing DependentFixtureInterface exist.
// This is because, in that case, the method orderFixturesByDependencies
// will handle all fixtures which are not instances of
// OrderedFixtureInterface
if ($this->orderFixturesByNumber) {
$count = count($this->orderedFixtures);
for ($i = 0; $i < $count; ++$i) {
if ($this->orderedFixtures[$i] instanceof OrderedFixtureInterface) {
continue;
}
unset($this->orderedFixtures[$i]);
}
}
// First we determine which classes has dependencies and which don't
foreach ($this->fixtures as $fixture) {
$fixtureClass = get_class($fixture);
if ($fixture instanceof OrderedFixtureInterface) {
continue;
}
if ($fixture instanceof DependentFixtureInterface) {
$dependenciesClasses = $fixture->getDependencies();
$this->validateDependencies($dependenciesClasses);
if (! is_array($dependenciesClasses) || empty($dependenciesClasses)) {
throw new InvalidArgumentException(sprintf(
'Method "%s" in class "%s" must return an array of classes which are dependencies for the fixture, and it must be NOT empty.',
'getDependencies',
$fixtureClass
));
}
if (in_array($fixtureClass, $dependenciesClasses)) {
throw new InvalidArgumentException(sprintf(
'Class "%s" can\'t have itself as a dependency',
$fixtureClass
));
}
// We mark this class as unsequenced
$sequenceForClasses[$fixtureClass] = -1;
} else {
// This class has no dependencies, so we assign 0
$sequenceForClasses[$fixtureClass] = 0;
}
}
// Now we order fixtures by sequence
$sequence = 1;
$lastCount = -1;
while (($count = count($unsequencedClasses = $this->getUnsequencedClasses($sequenceForClasses))) > 0 && $count !== $lastCount) {
foreach ($unsequencedClasses as $key => $class) {
$fixture = $this->fixtures[$class];
$dependencies = $fixture->getDependencies();
$unsequencedDependencies = $this->getUnsequencedClasses($sequenceForClasses, $dependencies);
if (count($unsequencedDependencies) !== 0) {
continue;
}
$sequenceForClasses[$class] = $sequence++;
}
$lastCount = $count;
}
$orderedFixtures = [];
// If there're fixtures unsequenced left and they couldn't be sequenced,
// it means we have a circular reference
if ($count > 0) {
$msg = 'Classes "%s" have produced a CircularReferenceException. ';
$msg .= 'An example of this problem would be the following: Class C has class B as its dependency. ';
$msg .= 'Then, class B has class A has its dependency. Finally, class A has class C as its dependency. ';
$msg .= 'This case would produce a CircularReferenceException.';
throw new CircularReferenceException(sprintf($msg, implode(',', $unsequencedClasses)));
} else {
// We order the classes by sequence
asort($sequenceForClasses);
foreach ($sequenceForClasses as $class => $sequence) {
// If fixtures were ordered
$orderedFixtures[] = $this->fixtures[$class];
}
}
$this->orderedFixtures = array_merge($this->orderedFixtures, $orderedFixtures);
}
/** @psalm-param iterable<class-string> $dependenciesClasses */
private function validateDependencies(iterable $dependenciesClasses): bool
{
$loadedFixtureClasses = array_keys($this->fixtures);
foreach ($dependenciesClasses as $class) {
if (! in_array($class, $loadedFixtureClasses)) {
throw new RuntimeException(sprintf(
'Fixture "%s" was declared as a dependency, but it should be added in fixture loader first.',
$class
));
}
}
return true;
}
/**
* @psalm-param array<class-string<DependentFixtureInterface>, int> $sequences
* @psalm-param iterable<class-string<FixtureInterface>>|null $classes
*
* @psalm-return array<class-string<FixtureInterface>>
*/
private function getUnsequencedClasses(array $sequences, ?iterable $classes = null): array
{
$unsequencedClasses = [];
if ($classes === null) {
$classes = array_keys($sequences);
}
foreach ($classes as $class) {
if ($sequences[$class] !== -1) {
continue;
}
$unsequencedClasses[] = $class;
}
return $unsequencedClasses;
}
/**
* Load fixtures from files contained in iterator.
*
* @psalm-param Iterator<SplFileInfo> $iterator Iterator over files from
* which fixtures should be loaded.
*
* @psalm-return list<FixtureInterface> $fixtures Array of loaded fixture object instances.
*/
private function loadFromIterator(Iterator $iterator): array
{
$includedFiles = [];
foreach ($iterator as $file) {
$fileName = $file->getBasename($this->fileExtension);
if ($fileName === $file->getBasename()) {
continue;
}
$sourceFile = realpath($file->getPathName());
require_once $sourceFile;
$includedFiles[] = $sourceFile;
}
$fixtures = [];
$declared = get_declared_classes();
// Make the declared classes order deterministic
sort($declared);
foreach ($declared as $className) {
$reflClass = new ReflectionClass($className);
$sourceFile = $reflClass->getFileName();
if (! in_array($sourceFile, $includedFiles) || $this->isTransient($className)) {
continue;
}
$fixture = $this->createFixture($className);
$fixtures[] = $fixture;
$this->addFixture($fixture);
}
return $fixtures;
}
}

View File

@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Doctrine\Common\DataFixtures;
/**
* Ordered Fixture interface needs to be implemented
* by fixtures, which needs to have a specific order
* when being loaded by directory scan for example
*/
interface OrderedFixtureInterface
{
/**
* Get the order of this fixture
*
* @return int
*/
public function getOrder();
}

View File

@@ -0,0 +1,129 @@
<?php
declare(strict_types=1);
namespace Doctrine\Common\DataFixtures;
use function file_exists;
use function file_get_contents;
use function file_put_contents;
use function get_class;
use function serialize;
use function substr;
use function unserialize;
/**
* Proxy reference repository
*
* Allow data fixture references and identities to be persisted when cached data fixtures
* are pre-loaded, for example, by LiipFunctionalTestBundle\Test\WebTestCase loadFixtures().
*/
class ProxyReferenceRepository extends ReferenceRepository
{
/**
* Get real class name of a reference that could be a proxy
*
* @param string $className Class name of reference object
*
* @return string
*/
protected function getRealClass($className)
{
if (substr($className, -5) === 'Proxy') {
return substr($className, 0, -5);
}
return $className;
}
/**
* Serialize reference repository
*
* @return string
*/
public function serialize()
{
$unitOfWork = $this->getManager()->getUnitOfWork();
$simpleReferences = [];
foreach ($this->getReferences() as $name => $reference) {
$className = $this->getRealClass(get_class($reference));
$simpleReferences[$name] = [$className, $this->getIdentifier($reference, $unitOfWork)];
}
return serialize([
'references' => $simpleReferences,
'identities' => $this->getIdentities(),
]);
}
/**
* Unserialize reference repository
*
* @param string $serializedData Serialized data
*
* @return void
*/
public function unserialize($serializedData)
{
$repositoryData = unserialize($serializedData);
$references = $repositoryData['references'];
foreach ($references as $name => $proxyReference) {
$this->setReference(
$name,
$this->getManager()->getReference(
$proxyReference[0], // entity class name
$proxyReference[1] // identifiers
)
);
}
$identities = $repositoryData['identities'];
foreach ($identities as $name => $identity) {
$this->setReferenceIdentity($name, $identity);
}
}
/**
* Load data fixture reference repository
*
* @param string $baseCacheName Base cache name
*
* @return bool
*/
public function load($baseCacheName)
{
$filename = $baseCacheName . '.ser';
if (! file_exists($filename)) {
return false;
}
$serializedData = file_get_contents($filename);
if ($serializedData === false) {
return false;
}
$this->unserialize($serializedData);
return true;
}
/**
* Save data fixture reference repository
*
* @param string $baseCacheName Base cache name
*
* @return void
*/
public function save($baseCacheName)
{
$serializedData = $this->serialize();
file_put_contents($baseCacheName . '.ser', $serializedData);
}
}

View File

@@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
namespace Doctrine\Common\DataFixtures\Purger;
use Doctrine\ODM\MongoDB\DocumentManager;
/**
* Class responsible for purging databases of data before reloading data fixtures.
*/
class MongoDBPurger implements PurgerInterface
{
/** @var DocumentManager|null */
private $dm;
/**
* Construct new purger instance.
*
* @param DocumentManager|null $dm DocumentManager instance used for persistence.
*/
public function __construct(?DocumentManager $dm = null)
{
$this->dm = $dm;
}
/**
* Set the DocumentManager instance this purger instance should use.
*
* @return void
*/
public function setDocumentManager(DocumentManager $dm)
{
$this->dm = $dm;
}
/**
* Retrieve the DocumentManager instance this purger instance is using.
*
* @return DocumentManager
*/
public function getObjectManager()
{
return $this->dm;
}
/** @inheritDoc */
public function purge()
{
$metadatas = $this->dm->getMetadataFactory()->getAllMetadata();
foreach ($metadatas as $metadata) {
if ($metadata->isMappedSuperclass) {
continue;
}
$this->dm->getDocumentCollection($metadata->name)->drop();
}
$this->dm->getSchemaManager()->ensureIndexes();
}
}

View File

@@ -0,0 +1,283 @@
<?php
declare(strict_types=1);
namespace Doctrine\Common\DataFixtures\Purger;
use Doctrine\Common\DataFixtures\Sorter\TopologicalSorter;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\Identifier;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use function array_reverse;
use function array_search;
use function assert;
use function count;
use function is_callable;
use function method_exists;
use function preg_match;
/**
* Class responsible for purging databases of data before reloading data fixtures.
*/
class ORMPurger implements PurgerInterface, ORMPurgerInterface
{
public const PURGE_MODE_DELETE = 1;
public const PURGE_MODE_TRUNCATE = 2;
/** @var EntityManagerInterface|null */
private $em;
/**
* If the purge should be done through DELETE or TRUNCATE statements
*
* @var int
*/
private $purgeMode = self::PURGE_MODE_DELETE;
/**
* Table/view names to be excluded from purge
*
* @var string[]
*/
private $excluded;
/**
* Construct new purger instance.
*
* @param EntityManagerInterface|null $em EntityManagerInterface instance used for persistence.
* @param string[] $excluded array of table/view names to be excluded from purge
*/
public function __construct(?EntityManagerInterface $em = null, array $excluded = [])
{
$this->em = $em;
$this->excluded = $excluded;
}
/**
* Set the purge mode
*
* @param int $mode
*
* @return void
*/
public function setPurgeMode($mode)
{
$this->purgeMode = $mode;
}
/**
* Get the purge mode
*
* @return int
*/
public function getPurgeMode()
{
return $this->purgeMode;
}
/** @inheritDoc */
public function setEntityManager(EntityManagerInterface $em)
{
$this->em = $em;
}
/**
* Retrieve the EntityManagerInterface instance this purger instance is using.
*
* @return EntityManagerInterface
*/
public function getObjectManager()
{
return $this->em;
}
/** @inheritDoc */
public function purge()
{
$classes = [];
foreach ($this->em->getMetadataFactory()->getAllMetadata() as $metadata) {
if ($metadata->isMappedSuperclass || (isset($metadata->isEmbeddedClass) && $metadata->isEmbeddedClass)) {
continue;
}
$classes[] = $metadata;
}
$commitOrder = $this->getCommitOrder($this->em, $classes);
// Get platform parameters
$platform = $this->em->getConnection()->getDatabasePlatform();
// Drop association tables first
$orderedTables = $this->getAssociationTables($commitOrder, $platform);
// Drop tables in reverse commit order
for ($i = count($commitOrder) - 1; $i >= 0; --$i) {
$class = $commitOrder[$i];
if (
(isset($class->isEmbeddedClass) && $class->isEmbeddedClass) ||
$class->isMappedSuperclass ||
($class->isInheritanceTypeSingleTable() && $class->name !== $class->rootEntityName)
) {
continue;
}
$orderedTables[] = $this->getTableName($class, $platform);
}
$connection = $this->em->getConnection();
$filterExpr = method_exists(
$connection->getConfiguration(),
'getFilterSchemaAssetsExpression'
) ? $connection->getConfiguration()->getFilterSchemaAssetsExpression() : null;
$emptyFilterExpression = empty($filterExpr);
$schemaAssetsFilter = method_exists(
$connection->getConfiguration(),
'getSchemaAssetsFilter'
) ? $connection->getConfiguration()->getSchemaAssetsFilter() : null;
foreach ($orderedTables as $tbl) {
// If we have a filter expression, check it and skip if necessary
if (! $emptyFilterExpression && ! preg_match($filterExpr, $tbl)) {
continue;
}
// If the table is excluded, skip it as well
if (array_search($tbl, $this->excluded) !== false) {
continue;
}
// Support schema asset filters as presented in
if (is_callable($schemaAssetsFilter) && ! $schemaAssetsFilter($tbl)) {
continue;
}
if ($this->purgeMode === self::PURGE_MODE_DELETE) {
$connection->executeStatement($this->getDeleteFromTableSQL($tbl, $platform));
} else {
$connection->executeStatement($platform->getTruncateTableSQL($tbl, true));
}
}
}
/**
* @param ClassMetadata[] $classes
*
* @return ClassMetadata[]
*/
private function getCommitOrder(EntityManagerInterface $em, array $classes)
{
$sorter = new TopologicalSorter();
foreach ($classes as $class) {
if (! $sorter->hasNode($class->name)) {
$sorter->addNode($class->name, $class);
}
// $class before its parents
foreach ($class->parentClasses as $parentClass) {
$parentClass = $em->getClassMetadata($parentClass);
$parentClassName = $parentClass->getName();
if (! $sorter->hasNode($parentClassName)) {
$sorter->addNode($parentClassName, $parentClass);
}
$sorter->addDependency($class->name, $parentClassName);
}
foreach ($class->associationMappings as $assoc) {
if (! $assoc['isOwningSide']) {
continue;
}
$targetClass = $em->getClassMetadata($assoc['targetEntity']);
assert($targetClass instanceof ClassMetadata);
$targetClassName = $targetClass->getName();
if (! $sorter->hasNode($targetClassName)) {
$sorter->addNode($targetClassName, $targetClass);
}
// add dependency ($targetClass before $class)
$sorter->addDependency($targetClassName, $class->name);
// parents of $targetClass before $class, too
foreach ($targetClass->parentClasses as $parentClass) {
$parentClass = $em->getClassMetadata($parentClass);
$parentClassName = $parentClass->getName();
if (! $sorter->hasNode($parentClassName)) {
$sorter->addNode($parentClassName, $parentClass);
}
$sorter->addDependency($parentClassName, $class->name);
}
}
}
return array_reverse($sorter->sort());
}
/**
* @param ClassMetadata[] $classes
*
* @return string[]
*/
private function getAssociationTables(array $classes, AbstractPlatform $platform)
{
$associationTables = [];
foreach ($classes as $class) {
foreach ($class->associationMappings as $assoc) {
if (! $assoc['isOwningSide'] || $assoc['type'] !== ClassMetadata::MANY_TO_MANY) {
continue;
}
$associationTables[] = $this->getJoinTableName($assoc, $class, $platform);
}
}
return $associationTables;
}
private function getTableName(ClassMetadata $class, AbstractPlatform $platform): string
{
if (isset($class->table['schema']) && ! method_exists($class, 'getSchemaName')) {
return $class->table['schema'] . '.' .
$this->em->getConfiguration()
->getQuoteStrategy()
->getTableName($class, $platform);
}
return $this->em->getConfiguration()->getQuoteStrategy()->getTableName($class, $platform);
}
/** @param mixed[] $assoc */
private function getJoinTableName(
array $assoc,
ClassMetadata $class,
AbstractPlatform $platform
): string {
if (isset($assoc['joinTable']['schema']) && ! method_exists($class, 'getSchemaName')) {
return $assoc['joinTable']['schema'] . '.' .
$this->em->getConfiguration()
->getQuoteStrategy()
->getJoinTableName($assoc, $class, $platform);
}
return $this->em->getConfiguration()->getQuoteStrategy()->getJoinTableName($assoc, $class, $platform);
}
private function getDeleteFromTableSQL(string $tableName, AbstractPlatform $platform): string
{
$tableIdentifier = new Identifier($tableName);
return 'DELETE FROM ' . $tableIdentifier->getQuotedName($platform);
}
}

View File

@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Doctrine\Common\DataFixtures\Purger;
use Doctrine\ORM\EntityManagerInterface;
/**
* ORMPurgerInterface
*/
interface ORMPurgerInterface extends PurgerInterface
{
/**
* Set the EntityManagerInterface instance this purger instance should use.
*
* @return void
*/
public function setEntityManager(EntityManagerInterface $em);
}

View File

@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace Doctrine\Common\DataFixtures\Purger;
use Doctrine\ODM\PHPCR\DocumentManager;
use Doctrine\ODM\PHPCR\DocumentManagerInterface;
use PHPCR\Util\NodeHelper;
/**
* Class responsible for purging databases of data before reloading data fixtures.
*/
class PHPCRPurger implements PurgerInterface
{
/** @var DocumentManagerInterface|null */
private $dm;
public function __construct(?DocumentManagerInterface $dm = null)
{
$this->dm = $dm;
}
public function setDocumentManager(DocumentManager $dm)
{
$this->dm = $dm;
}
/** @return DocumentManagerInterface|null */
public function getObjectManager()
{
return $this->dm;
}
/** @inheritDoc */
public function purge()
{
$session = $this->dm->getPhpcrSession();
NodeHelper::purgeWorkspace($session);
$session->save();
}
}

View File

@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace Doctrine\Common\DataFixtures\Purger;
/**
* PurgerInterface
*/
interface PurgerInterface
{
/**
* Purge the data from the database for the given EntityManager.
*
* @return void
*/
public function purge();
}

View File

@@ -0,0 +1,265 @@
<?php
declare(strict_types=1);
namespace Doctrine\Common\DataFixtures;
use BadMethodCallException;
use Doctrine\ODM\PHPCR\DocumentManager as PhpcrDocumentManager;
use Doctrine\Persistence\ObjectManager;
use OutOfBoundsException;
use function array_key_exists;
use function array_keys;
use function get_class;
use function method_exists;
use function sprintf;
/**
* ReferenceRepository class manages references for
* fixtures in order to easily support the relations
* between fixtures
*/
class ReferenceRepository
{
/**
* List of named references to the fixture objects
* gathered during loads of fixtures
*
* @psalm-var array<string, object>
*/
private $references = [];
/**
* List of identifiers stored for references
* in case if reference gets unmanaged, it will
* use a proxy referenced by this identity
*
* @psalm-var array<string, mixed>
*/
private $identities = [];
/**
* Currently used object manager
*
* @var ObjectManager
*/
private $manager;
public function __construct(ObjectManager $manager)
{
$this->manager = $manager;
}
/**
* Get identifier for a unit of work
*
* @param object $reference Reference object
* @param object $uow Unit of work
*
* @return array
*/
protected function getIdentifier($reference, $uow)
{
// In case Reference is not yet managed in UnitOfWork
if (! $this->hasIdentifier($reference)) {
$class = $this->manager->getClassMetadata(get_class($reference));
return $class->getIdentifierValues($reference);
}
// Dealing with ORM UnitOfWork
if (method_exists($uow, 'getEntityIdentifier')) {
return $uow->getEntityIdentifier($reference);
}
// PHPCR ODM UnitOfWork
if ($this->manager instanceof PhpcrDocumentManager) {
return $uow->getDocumentId($reference);
}
// ODM UnitOfWork
return $uow->getDocumentIdentifier($reference);
}
/**
* Set the reference entry identified by $name
* and referenced to $reference. If $name
* already is set, it overrides it
*
* @param string $name
* @param object $reference
*
* @return void
*/
public function setReference($name, $reference)
{
$this->references[$name] = $reference;
if (! $this->hasIdentifier($reference)) {
return;
}
// in case if reference is set after flush, store its identity
$uow = $this->manager->getUnitOfWork();
$this->identities[$name] = $this->getIdentifier($reference, $uow);
}
/**
* Store the identifier of a reference
*
* @param string $name
* @param mixed $identity
*
* @return void
*/
public function setReferenceIdentity($name, $identity)
{
$this->identities[$name] = $identity;
}
/**
* Set the reference entry identified by $name
* and referenced to managed $object. $name must
* not be set yet
*
* Notice: in case if identifier is generated after
* the record is inserted, be sure tu use this method
* after $object is flushed
*
* @param string $name
* @param object $object - managed object
*
* @return void
*
* @throws BadMethodCallException - if repository already has a reference by $name.
*/
public function addReference($name, $object)
{
if (isset($this->references[$name])) {
throw new BadMethodCallException(sprintf(
'Reference to "%s" already exists, use method setReference in order to override it',
$name
));
}
$this->setReference($name, $object);
}
/**
* Loads an object using stored reference
* named by $name
*
* @param string $name
*
* @return object
*
* @throws OutOfBoundsException - if repository does not exist.
*/
public function getReference($name)
{
if (! $this->hasReference($name)) {
throw new OutOfBoundsException(sprintf('Reference to "%s" does not exist', $name));
}
$reference = $this->references[$name];
$meta = $this->manager->getClassMetadata(get_class($reference));
if (! $this->manager->contains($reference) && isset($this->identities[$name])) {
$reference = $this->manager->getReference(
$meta->name,
$this->identities[$name]
);
$this->references[$name] = $reference; // already in identity map
}
return $reference;
}
/**
* Check if an object is stored using reference
* named by $name
*
* @param string $name
*
* @return bool
*/
public function hasReference($name)
{
return isset($this->references[$name]);
}
/**
* Searches for reference names in the
* list of stored references
*
* @param object $reference
*
* @return array
*/
public function getReferenceNames($reference)
{
return array_keys($this->references, $reference, true);
}
/**
* Checks if reference has identity stored
*
* @param string $name
*
* @return bool
*/
public function hasIdentity($name)
{
return array_key_exists($name, $this->identities);
}
/**
* Get all stored identities
*
* @psalm-return array<string, object>
*/
public function getIdentities()
{
return $this->identities;
}
/**
* Get all stored references
*
* @psalm-return array<string, object>
*/
public function getReferences()
{
return $this->references;
}
/**
* Get object manager
*
* @return ObjectManager
*/
public function getManager()
{
return $this->manager;
}
/**
* Checks if object has identifier already in unit of work.
*
* @param string $reference
*
* @return bool
*/
private function hasIdentifier($reference)
{
// in case if reference is set after flush, store its identity
$uow = $this->manager->getUnitOfWork();
if ($this->manager instanceof PhpcrDocumentManager) {
return $uow->contains($reference);
}
return $uow->isInIdentityMap($reference);
}
}

View File

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace Doctrine\Common\DataFixtures;
/**
* Shared Fixture interface needs to be implemented
* by fixtures, which needs some references to be shared
* among other fixture classes in order to maintain
* relation mapping
*/
interface SharedFixtureInterface extends FixtureInterface
{
public function setReferenceRepository(ReferenceRepository $referenceRepository);
}

View File

@@ -0,0 +1,182 @@
<?php
declare(strict_types=1);
namespace Doctrine\Common\DataFixtures\Sorter;
use Doctrine\Common\DataFixtures\Exception\CircularReferenceException;
use Doctrine\ORM\Mapping\ClassMetadata;
use RuntimeException;
use function get_class;
use function sprintf;
/**
* TopologicalSorter is an ordering algorithm for directed graphs (DG) and/or
* directed acyclic graphs (DAG) by using a depth-first searching (DFS) to
* traverse the graph built in memory.
* This algorithm have a linear running time based on nodes (V) and dependency
* between the nodes (E), resulting in a computational complexity of O(V + E).
*
* @internal this class is to be used only by data-fixtures internals: do not
* rely on it in your own libraries/applications.
*/
class TopologicalSorter
{
/**
* Matrix of nodes (aka. vertex).
* Keys are provided hashes and values are the node definition objects.
*
* @var Vertex[]
*/
private $nodeList = [];
/**
* Volatile variable holding calculated nodes during sorting process.
*
* @var ClassMetadata[]
*/
private $sortedNodeList = [];
/**
* Allow or not cyclic dependencies
*
* @var bool
*/
private $allowCyclicDependencies;
/** @param bool $allowCyclicDependencies */
public function __construct($allowCyclicDependencies = true)
{
$this->allowCyclicDependencies = $allowCyclicDependencies;
}
/**
* Adds a new node (vertex) to the graph, assigning its hash and value.
*
* @param string $hash
*
* @return void
*/
public function addNode($hash, ClassMetadata $node)
{
$this->nodeList[$hash] = new Vertex($node);
}
/**
* Checks the existence of a node in the graph.
*
* @param string $hash
*
* @return bool
*/
public function hasNode($hash)
{
return isset($this->nodeList[$hash]);
}
/**
* Adds a new dependency (edge) to the graph using their hashes.
*
* @param string $fromHash
* @param string $toHash
*
* @return void
*/
public function addDependency($fromHash, $toHash)
{
$definition = $this->nodeList[$fromHash];
$definition->dependencyList[] = $toHash;
}
/**
* Return a valid order list of all current nodes.
* The desired topological sorting is the postorder of these searches.
*
* Note: Highly performance-sensitive method.
*
* @return ClassMetadata[]
*
* @throws RuntimeException
* @throws CircularReferenceException
*/
public function sort()
{
foreach ($this->nodeList as $definition) {
if ($definition->state !== Vertex::NOT_VISITED) {
continue;
}
$this->visit($definition);
}
$sortedList = $this->sortedNodeList;
$this->nodeList = [];
$this->sortedNodeList = [];
return $sortedList;
}
/**
* Visit a given node definition for reordering.
*
* Note: Highly performance-sensitive method.
*
* @return void
*
* @throws RuntimeException
* @throws CircularReferenceException
*/
private function visit(Vertex $definition)
{
$definition->state = Vertex::IN_PROGRESS;
foreach ($definition->dependencyList as $dependency) {
if (! isset($this->nodeList[$dependency])) {
throw new RuntimeException(sprintf(
'Fixture "%s" has a dependency of fixture "%s", but it not listed to be loaded.',
get_class($definition->value),
$dependency
));
}
$childDefinition = $this->nodeList[$dependency];
// allow self referencing classes
if ($definition === $childDefinition) {
continue;
}
switch ($childDefinition->state) {
case Vertex::VISITED:
break;
case Vertex::IN_PROGRESS:
if (! $this->allowCyclicDependencies) {
throw new CircularReferenceException(
sprintf(
<<<'EXCEPTION'
Graph contains cyclic dependency between the classes "%s" and
"%s". An example of this problem would be the following:
Class C has class B as its dependency. Then, class B has class A has its dependency.
Finally, class A has class C as its dependency.
EXCEPTION
,
$definition->value->getName(),
$childDefinition->value->getName()
)
);
}
break;
case Vertex::NOT_VISITED:
$this->visit($childDefinition);
}
}
$definition->state = Vertex::VISITED;
$this->sortedNodeList[] = $definition->value;
}
}

View File

@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Doctrine\Common\DataFixtures\Sorter;
use Doctrine\ORM\Mapping\ClassMetadata;
/**
* @internal this class is to be used only by data-fixtures internals: do not
* rely on it in your own libraries/applications. This class is
* designed to work with {@see \Doctrine\Common\DataFixtures\Sorter\TopologicalSorter}
* only.
*/
class Vertex
{
public const NOT_VISITED = 0;
public const IN_PROGRESS = 1;
public const VISITED = 2;
/** @var int one of either {@see self::NOT_VISITED}, {@see self::IN_PROGRESS} or {@see self::VISITED}. */
public $state = self::NOT_VISITED;
/** @var ClassMetadata Actual node value */
public $value;
/** @var string[] Map of node dependencies defined as hashes. */
public $dependencyList = [];
public function __construct(ClassMetadata $value)
{
$this->value = $value;
}
}

View File

@@ -0,0 +1,53 @@
<?xml version="1.0"?>
<ruleset>
<arg name="basepath" value="."/>
<arg name="extensions" value="php"/>
<arg name="parallel" value="80"/>
<arg name="cache" value=".phpcs-cache"/>
<arg name="colors" />
<config name="php_version" value="70200"/>
<!-- Ignore warnings and show progress of the run -->
<arg value="nps"/>
<file>lib</file>
<file>tests</file>
<rule ref="Doctrine">
<!-- Traversable type hints often end up as mixed[], so we skip them for now -->
<exclude name="SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingTraversableTypeHintSpecification"/>
<exclude name="SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingTraversableTypeHintSpecification"/>
<exclude name="SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingTraversableTypeHintSpecification"/>
<!-- Will cause BC breaks to method signatures - disabled for now -->
<exclude name="SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingNativeTypeHint"/>
<exclude name="SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint"/>
</rule>
<rule ref="PSR1.Classes.ClassDeclaration.MultipleClasses">
<exclude-pattern>tests/*</exclude-pattern>
</rule>
<rule ref="SlevomatCodingStandard.Classes.SuperfluousAbstractClassNaming">
<exclude-pattern>lib/Doctrine/Common/DataFixtures/AbstractFixture.php</exclude-pattern>
<exclude-pattern>lib/Doctrine/Common/DataFixtures/Executor/AbstractExecutor.php</exclude-pattern>
</rule>
<rule ref="SlevomatCodingStandard.Classes.SuperfluousExceptionNaming">
<exclude-pattern>lib/Doctrine/Common/DataFixtures/Exception/CircularReferenceException.php</exclude-pattern>
</rule>
<rule ref="SlevomatCodingStandard.Classes.SuperfluousInterfaceNaming">
<exclude-pattern>lib/Doctrine/Common/DataFixtures/DependentFixtureInterface.php</exclude-pattern>
<exclude-pattern>lib/Doctrine/Common/DataFixtures/FixtureInterface.php</exclude-pattern>
<exclude-pattern>lib/Doctrine/Common/DataFixtures/Purger/ORMPurgerInterface.php</exclude-pattern>
<exclude-pattern>lib/Doctrine/Common/DataFixtures/Purger/PurgerInterface.php</exclude-pattern>
<exclude-pattern>lib/Doctrine/Common/DataFixtures/OrderedFixtureInterface.php</exclude-pattern>
<exclude-pattern>lib/Doctrine/Common/DataFixtures/SharedFixtureInterface.php</exclude-pattern>
</rule>
<rule ref="Squiz.Classes.ClassFileName.NoMatch">
<exclude-pattern>tests/*</exclude-pattern>
</rule>
</ruleset>

View File

@@ -0,0 +1,117 @@
parameters:
ignoreErrors:
-
message: "#^Call to method transactional\\(\\) on an unknown class Doctrine\\\\ODM\\\\PHPCR\\\\DocumentManagerInterface\\.$#"
count: 1
path: lib/Doctrine/Common/DataFixtures/Executor/PHPCRExecutor.php
-
message: "#^Method Doctrine\\\\Common\\\\DataFixtures\\\\Executor\\\\PHPCRExecutor\\:\\:getObjectManager\\(\\) has invalid return type Doctrine\\\\ODM\\\\PHPCR\\\\DocumentManagerInterface\\.$#"
count: 1
path: lib/Doctrine/Common/DataFixtures/Executor/PHPCRExecutor.php
-
message: "#^Parameter \\$dm of method Doctrine\\\\Common\\\\DataFixtures\\\\Executor\\\\PHPCRExecutor\\:\\:__construct\\(\\) has invalid type Doctrine\\\\ODM\\\\PHPCR\\\\DocumentManagerInterface\\.$#"
count: 2
path: lib/Doctrine/Common/DataFixtures/Executor/PHPCRExecutor.php
-
message: "#^Property Doctrine\\\\Common\\\\DataFixtures\\\\Executor\\\\PHPCRExecutor\\:\\:\\$dm has unknown class Doctrine\\\\ODM\\\\PHPCR\\\\DocumentManagerInterface as its type\\.$#"
count: 1
path: lib/Doctrine/Common/DataFixtures/Executor/PHPCRExecutor.php
-
message: "#^Call to an undefined method Doctrine\\\\Common\\\\DataFixtures\\\\FixtureInterface\\:\\:getDependencies\\(\\)\\.$#"
count: 1
path: lib/Doctrine/Common/DataFixtures/Loader.php
-
message: "#^Property Doctrine\\\\Common\\\\DataFixtures\\\\Loader\\:\\:\\$orderedFixtures \\(array\\<class\\-string\\<Doctrine\\\\Common\\\\DataFixtures\\\\OrderedFixtureInterface\\>\\|int, Doctrine\\\\Common\\\\DataFixtures\\\\OrderedFixtureInterface\\>\\) does not accept array\\<class\\-string\\<Doctrine\\\\Common\\\\DataFixtures\\\\FixtureInterface\\>, Doctrine\\\\Common\\\\DataFixtures\\\\FixtureInterface\\>\\.$#"
count: 2
path: lib/Doctrine/Common/DataFixtures/Loader.php
-
message: "#^Call to an undefined method Doctrine\\\\Persistence\\\\ObjectManager\\:\\:getReference\\(\\)\\.$#"
count: 1
path: lib/Doctrine/Common/DataFixtures/ProxyReferenceRepository.php
-
message: "#^Call to an undefined method Doctrine\\\\Persistence\\\\ObjectManager\\:\\:getUnitOfWork\\(\\)\\.$#"
count: 1
path: lib/Doctrine/Common/DataFixtures/ProxyReferenceRepository.php
-
message: "#^Call to method getPhpcrSession\\(\\) on an unknown class Doctrine\\\\ODM\\\\PHPCR\\\\DocumentManagerInterface\\.$#"
count: 1
path: lib/Doctrine/Common/DataFixtures/Purger/PHPCRPurger.php
-
message: "#^Call to static method purgeWorkspace\\(\\) on an unknown class PHPCR\\\\Util\\\\NodeHelper\\.$#"
count: 1
path: lib/Doctrine/Common/DataFixtures/Purger/PHPCRPurger.php
-
message: "#^Method Doctrine\\\\Common\\\\DataFixtures\\\\Purger\\\\PHPCRPurger\\:\\:getObjectManager\\(\\) has invalid return type Doctrine\\\\ODM\\\\PHPCR\\\\DocumentManagerInterface\\.$#"
count: 1
path: lib/Doctrine/Common/DataFixtures/Purger/PHPCRPurger.php
-
message: "#^Parameter \\$dm of method Doctrine\\\\Common\\\\DataFixtures\\\\Purger\\\\PHPCRPurger\\:\\:__construct\\(\\) has invalid type Doctrine\\\\ODM\\\\PHPCR\\\\DocumentManagerInterface\\.$#"
count: 1
path: lib/Doctrine/Common/DataFixtures/Purger/PHPCRPurger.php
-
message: "#^Parameter \\$dm of method Doctrine\\\\Common\\\\DataFixtures\\\\Purger\\\\PHPCRPurger\\:\\:setDocumentManager\\(\\) has invalid type Doctrine\\\\ODM\\\\PHPCR\\\\DocumentManager\\.$#"
count: 1
path: lib/Doctrine/Common/DataFixtures/Purger/PHPCRPurger.php
-
message: "#^Property Doctrine\\\\Common\\\\DataFixtures\\\\Purger\\\\PHPCRPurger\\:\\:\\$dm \\(Doctrine\\\\ODM\\\\PHPCR\\\\DocumentManagerInterface\\|null\\) does not accept Doctrine\\\\ODM\\\\PHPCR\\\\DocumentManager\\.$#"
count: 1
path: lib/Doctrine/Common/DataFixtures/Purger/PHPCRPurger.php
-
message: "#^Property Doctrine\\\\Common\\\\DataFixtures\\\\Purger\\\\PHPCRPurger\\:\\:\\$dm has unknown class Doctrine\\\\ODM\\\\PHPCR\\\\DocumentManagerInterface as its type\\.$#"
count: 1
path: lib/Doctrine/Common/DataFixtures/Purger/PHPCRPurger.php
-
message: "#^Access to an undefined property Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\<object\\>\\:\\:\\$name\\.$#"
count: 1
path: lib/Doctrine/Common/DataFixtures/ReferenceRepository.php
-
message: "#^Call to an undefined method Doctrine\\\\Persistence\\\\ObjectManager\\:\\:getReference\\(\\)\\.$#"
count: 1
path: lib/Doctrine/Common/DataFixtures/ReferenceRepository.php
-
message: "#^Call to an undefined method Doctrine\\\\Persistence\\\\ObjectManager\\:\\:getUnitOfWork\\(\\)\\.$#"
count: 2
path: lib/Doctrine/Common/DataFixtures/ReferenceRepository.php
-
message: "#^Class Doctrine\\\\ODM\\\\PHPCR\\\\DocumentManager not found\\.$#"
count: 2
path: lib/Doctrine/Common/DataFixtures/ReferenceRepository.php
-
message: "#^Method Doctrine\\\\Tests\\\\Common\\\\DataFixtures\\\\FixtureWithUnexistentDependency\\:\\:getDependencies\\(\\) should return array\\<class\\-string\\<Doctrine\\\\Common\\\\DataFixtures\\\\FixtureInterface\\>\\> but returns array\\<int, string\\>\\.$#"
count: 1
path: tests/Doctrine/Tests/Common/DataFixtures/DependentFixtureTest.php
-
message: "#^Call to method expects\\(\\) on an unknown class Doctrine\\\\ODM\\\\PHPCR\\\\DocumentManager\\.$#"
count: 5
path: tests/Doctrine/Tests/Common/DataFixtures/Executor/PHPCRExecutorTest.php
-
message: "#^Method Doctrine\\\\Tests\\\\Common\\\\DataFixtures\\\\Executor\\\\PHPCRExecutorTest\\:\\:getDocumentManager\\(\\) has invalid return type Doctrine\\\\ODM\\\\PHPCR\\\\DocumentManager\\.$#"
count: 1
path: tests/Doctrine/Tests/Common/DataFixtures/Executor/PHPCRExecutorTest.php
-
message: "#^Call to an undefined method Doctrine\\\\ODM\\\\MongoDB\\\\DocumentManager\\:\\:getConnection\\(\\)\\.$#"
count: 1
path: tests/Doctrine/Tests/Common/DataFixtures/Purger/MongoDBPurgerTest.php

View File

@@ -0,0 +1,9 @@
parameters:
phpVersion: 70100
level: 3
paths:
- lib
- tests
includes:
- phpstan-baseline.neon

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="4.22.0@fc2c6ab4d5fa5d644d8617089f012f3bb84b8703">
<file src="lib/Doctrine/Common/DataFixtures/Executor/PHPCRExecutor.php">
<UndefinedClass occurrences="1">
<code>DocumentManagerInterface</code>
</UndefinedClass>
<UndefinedDocblockClass occurrences="2">
<code>DocumentManagerInterface</code>
<code>DocumentManagerInterface</code>
</UndefinedDocblockClass>
</file>
<file src="lib/Doctrine/Common/DataFixtures/Purger/PHPCRPurger.php">
<UndefinedClass occurrences="3">
<code>?DocumentManagerInterface</code>
<code>DocumentManager</code>
<code>NodeHelper</code>
</UndefinedClass>
<UndefinedDocblockClass occurrences="3">
<code>$this-&gt;dm</code>
<code>DocumentManagerInterface|null</code>
<code>DocumentManagerInterface|null</code>
</UndefinedDocblockClass>
</file>
<file src="lib/Doctrine/Common/DataFixtures/ReferenceRepository.php">
<UndefinedClass occurrences="2">
<code>PhpcrDocumentManager</code>
<code>PhpcrDocumentManager</code>
</UndefinedClass>
</file>
</files>

17
vendor/doctrine/data-fixtures/psalm.xml vendored Normal file
View File

@@ -0,0 +1,17 @@
<?xml version="1.0"?>
<psalm
phpVersion="7.4"
errorLevel="7"
resolveFromConfigFile="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
errorBaseline="psalm-baseline.xml"
>
<projectFiles>
<directory name="lib/Doctrine/Common/DataFixtures" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
</psalm>