Skip to content

Commit

Permalink
Merge branch '2.13.x' into 3.0.x
Browse files Browse the repository at this point in the history
* 2.13.x:
  PHPStan 1.6.1 (doctrine#9688)
  Drop SymfonyStyle[listing] for sqls (doctrine#9679)
  Remove dynamic property declarations (doctrine#9683)
  PHPStan 1.6.0 (doctrine#9682)
  Validate XML mapping against XSD file
  Document ORM drivers only really load ORM metadata
  Fix HydrationException::invalidDiscriminatorValue parameter type (doctrine#9676)
  Fix type on ClassMetadata discriminatorMap (doctrine#9675)
  • Loading branch information
derrabus committed Apr 27, 2022
2 parents aebd3f3 + 825e964 commit 01edf11
Show file tree
Hide file tree
Showing 36 changed files with 379 additions and 375 deletions.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"doctrine/annotations": "^1.13",
"doctrine/coding-standard": "^9.0",
"phpbench/phpbench": "^1.0",
"phpstan/phpstan": "1.5.0",
"phpstan/phpstan": "1.6.1",
"phpunit/phpunit": "^9.5",
"psr/log": "^1 || ^2 || ^3",
"squizlabs/php_codesniffer": "3.6.2",
Expand All @@ -50,6 +50,7 @@
"doctrine/annotations": "<1.13 || >= 2.0"
},
"suggest": {
"ext-dom": "Provides support for XSD validation for XML mapping files",
"symfony/cache": "Provides cache support for Setup Tool with doctrine/cache 2.0"
},
"autoload": {
Expand Down
8 changes: 4 additions & 4 deletions lib/Doctrine/ORM/Internal/Hydration/HydrationException.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,15 @@ public static function missingDiscriminatorMetaMappingColumn(string $entityName,
}

/**
* @param string[] $discrMap
* @psalm-param array<string, string> $discrMap
* @param string[] $discrValues
* @psalm-param list<string> $discrValues
*/
public static function invalidDiscriminatorValue(string $discrValue, array $discrMap): self
public static function invalidDiscriminatorValue(string $discrValue, array $discrValues): self
{
return new self(sprintf(
'The discriminator value "%s" is invalid. It must be one of "%s".',
$discrValue,
implode('", "', $discrMap)
implode('", "', $discrValues)
));
}
}
4 changes: 3 additions & 1 deletion lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,9 @@ class ClassMetadataInfo implements ClassMetadata
*
* @see discriminatorColumn
*
* @var mixed
* @var array<string, string>
*
* @psalm-var array<string, class-string>
*/
public $discriminatorMap = [];

Expand Down
22 changes: 13 additions & 9 deletions lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
use Doctrine\ORM\Events;
use Doctrine\ORM\Mapping;
use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\Persistence\Mapping\ClassMetadata;
use Doctrine\Persistence\Mapping\ClassMetadata as PersistenceClassMetadata;
use Doctrine\Persistence\Mapping\Driver\ColocatedMappingDriver;
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
use ReflectionClass;
Expand Down Expand Up @@ -68,10 +68,14 @@ public function __construct($reader, $paths = null)

/**
* {@inheritDoc}
*
* @psalm-param class-string<T> $className
* @psalm-param ClassMetadata<T> $metadata
*
* @template T of object
*/
public function loadMetadataForClass($className, ClassMetadata $metadata)
public function loadMetadataForClass($className, PersistenceClassMetadata $metadata)
{
assert($metadata instanceof Mapping\ClassMetadata);
$class = $metadata->getReflectionClass()
// this happens when running annotation driver in combination with
// static reflection services. This is not the nicest fix
Expand Down Expand Up @@ -259,7 +263,7 @@ public function loadMetadataForClass($className, ClassMetadata $metadata)
constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceTypeAnnot->value)
);

if ($metadata->inheritanceType !== ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
if ($metadata->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) {
// Evaluate DiscriminatorColumn annotation
if (isset($classAnnotations[Mapping\DiscriminatorColumn::class])) {
$discrColumnAnnot = $classAnnotations[Mapping\DiscriminatorColumn::class];
Expand Down Expand Up @@ -504,7 +508,7 @@ public function loadMetadataForClass($className, ClassMetadata $metadata)
private function loadRelationShipMapping(
ReflectionProperty $property,
array &$mapping,
ClassMetadata $metadata,
PersistenceClassMetadata $metadata,
array $joinColumns,
string $className
): void {
Expand Down Expand Up @@ -608,7 +612,7 @@ private function loadRelationShipMapping(
/**
* Attempts to resolve the fetch mode.
*
* @psalm-return \Doctrine\ORM\Mapping\ClassMetadata::FETCH_* The fetch mode as defined in ClassMetadata.
* @psalm-return ClassMetadata::FETCH_* The fetch mode as defined in ClassMetadata.
*
* @throws MappingException If the fetch mode is not valid.
*/
Expand All @@ -624,7 +628,7 @@ private function getFetchMode(string $className, string $fetchMode): int
/**
* Attempts to resolve the generated mode.
*
* @psalm-return ClassMetadataInfo::GENERATED_*
* @psalm-return ClassMetadata::GENERATED_*
*
* @throws MappingException If the fetch mode is not valid.
*/
Expand Down Expand Up @@ -724,7 +728,7 @@ private function joinColumnToArray(Mapping\JoinColumn $joinColumn): array
* precision: int,
* notInsertable?: bool,
* notUpdateble?: bool,
* generated?: ClassMetadataInfo::GENERATED_*,
* generated?: ClassMetadata::GENERATED_*,
* enumType?: class-string,
* options?: mixed[],
* columnName?: string,
Expand Down
22 changes: 14 additions & 8 deletions lib/Doctrine/ORM/Mapping/Driver/AttributeDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
use Doctrine\ORM\Events;
use Doctrine\ORM\Mapping;
use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\Persistence\Mapping\ClassMetadata;
use Doctrine\Persistence\Mapping\ClassMetadata as PersistenceClassMetadata;
use Doctrine\Persistence\Mapping\Driver\ColocatedMappingDriver;
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
use ReflectionClass;
Expand Down Expand Up @@ -88,10 +88,16 @@ public function isTransient($className)
return true;
}

public function loadMetadataForClass($className, ClassMetadata $metadata): void
/**
* {@inheritDoc}
*
* @psalm-param class-string<T> $className
* @psalm-param ClassMetadata<T> $metadata
*
* @template T of object
*/
public function loadMetadataForClass($className, PersistenceClassMetadata $metadata): void
{
assert($metadata instanceof ClassMetadataInfo);

$reflectionClass = $metadata->getReflectionClass();

$classAttributes = $this->reader->getClassAnnotations($reflectionClass);
Expand Down Expand Up @@ -228,7 +234,7 @@ public function loadMetadataForClass($className, ClassMetadata $metadata): void
constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceTypeAttribute->value)
);

if ($metadata->inheritanceType !== Mapping\ClassMetadata::INHERITANCE_TYPE_NONE) {
if ($metadata->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) {
// Evaluate DiscriminatorColumn annotation
if (isset($classAttributes[Mapping\DiscriminatorColumn::class])) {
$discrColumnAttribute = $classAttributes[Mapping\DiscriminatorColumn::class];
Expand Down Expand Up @@ -481,7 +487,7 @@ public function loadMetadataForClass($className, ClassMetadata $metadata): void

// Check for `fetch`
if ($associationOverride->fetch) {
$override['fetch'] = constant(Mapping\ClassMetadata::class . '::FETCH_' . $associationOverride->fetch);
$override['fetch'] = constant(ClassMetadata::class . '::FETCH_' . $associationOverride->fetch);
}

$metadata->setAssociationOverride($fieldName, $override);
Expand Down Expand Up @@ -548,7 +554,7 @@ public function loadMetadataForClass($className, ClassMetadata $metadata): void
* @param string $className The class name.
* @param string $fetchMode The fetch mode.
*
* @return int The fetch mode as defined in ClassMetadata.
* @return ClassMetadata::FETCH_* The fetch mode as defined in ClassMetadata.
*
* @throws MappingException If the fetch mode is not valid.
*/
Expand Down
10 changes: 8 additions & 2 deletions lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
use Doctrine\DBAL\Types\Types;
use Doctrine\Inflector\Inflector;
use Doctrine\Inflector\InflectorFactory;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\Persistence\Mapping\ClassMetadata;
use Doctrine\Persistence\Mapping\ClassMetadata as PersistenceClassMetadata;
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
use InvalidArgumentException;

Expand Down Expand Up @@ -159,8 +160,13 @@ public function setInflector(Inflector $inflector): void

/**
* {@inheritDoc}
*
* @psalm-param class-string<T> $className
* @psalm-param ClassMetadata<T> $metadata
*
* @template T of object
*/
public function loadMetadataForClass($className, ClassMetadata $metadata)
public function loadMetadataForClass($className, PersistenceClassMetadata $metadata)
{
$this->reverseEngineerMappingFromDatabase();

Expand Down
66 changes: 60 additions & 6 deletions lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
namespace Doctrine\ORM\Mapping\Driver;

use Doctrine\Common\Collections\Criteria;
use Doctrine\Deprecations\Deprecation;
use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder;
use Doctrine\ORM\Mapping\ClassMetadata as Metadata;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\Persistence\Mapping\ClassMetadata;
use Doctrine\Persistence\Mapping\ClassMetadata as PersistenceClassMetadata;
use Doctrine\Persistence\Mapping\Driver\FileDriver;
use DOMDocument;
use InvalidArgumentException;
use LogicException;
use SimpleXMLElement;
Expand All @@ -22,6 +24,9 @@
use function extension_loaded;
use function file_get_contents;
use function in_array;
use function libxml_clear_errors;
use function libxml_get_errors;
use function libxml_use_internal_errors;
use function simplexml_load_string;
use function sprintf;
use function str_replace;
Expand All @@ -36,10 +41,13 @@ class XmlDriver extends FileDriver
{
public const DEFAULT_FILE_EXTENSION = '.dcm.xml';

/** @var bool */
private $isXsdValidationEnabled;

/**
* {@inheritDoc}
*/
public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENSION)
public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENSION, bool $isXsdValidationEnabled = false)
{
if (! extension_loaded('simplexml')) {
throw new LogicException(sprintf(
Expand All @@ -48,13 +56,37 @@ public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENS
));
}

if (! $isXsdValidationEnabled) {
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/pull/6728',
sprintf(
'Using XML mapping driver with XSD validation disabled is deprecated'
. ' and will not be supported in Doctrine ORM 3.0.'
)
);
}

if ($isXsdValidationEnabled && ! extension_loaded('dom')) {
throw new LogicException(sprintf(
'XSD validation cannot be enabled because the DOM extension is missing.'
));
}

$this->isXsdValidationEnabled = $isXsdValidationEnabled;

parent::__construct($locator, $fileExtension);
}

/**
* {@inheritDoc}
*
* @psalm-param class-string<T> $className
* @psalm-param ClassMetadata<T> $metadata
*
* @template T of object
*/
public function loadMetadataForClass($className, ClassMetadata $metadata)
public function loadMetadataForClass($className, PersistenceClassMetadata $metadata)
{
$xmlRoot = $this->getElement($className);
assert($xmlRoot instanceof SimpleXMLElement);
Expand Down Expand Up @@ -142,7 +174,7 @@ public function loadMetadataForClass($className, ClassMetadata $metadata)
$inheritanceType = (string) $xmlRoot['inheritance-type'];
$metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceType));

if ($metadata->inheritanceType !== Metadata::INHERITANCE_TYPE_NONE) {
if ($metadata->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) {
// Evaluate <discriminator-column...>
if (isset($xmlRoot->{'discriminator-column'})) {
$discrColumn = $xmlRoot->{'discriminator-column'};
Expand Down Expand Up @@ -659,7 +691,7 @@ public function loadMetadataForClass($className, ClassMetadata $metadata)

// Check for `fetch`
if (isset($overrideElement['fetch'])) {
$override['fetch'] = constant(Metadata::class . '::FETCH_' . (string) $overrideElement['fetch']);
$override['fetch'] = constant(ClassMetadata::class . '::FETCH_' . (string) $overrideElement['fetch']);
}

$metadata->setAssociationOverride($fieldName, $override);
Expand Down Expand Up @@ -909,6 +941,7 @@ private function getCascadeMappings(SimpleXMLElement $cascadeElement): array
*/
protected function loadMappingFile($file)
{
$this->validateMapping($file);
$result = [];
// Note: we do not use `simplexml_load_file()` because of https://bugs.php.net/bug.php?id=62577
$xmlElement = simplexml_load_string(file_get_contents($file));
Expand Down Expand Up @@ -936,6 +969,27 @@ protected function loadMappingFile($file)
return $result;
}

private function validateMapping(string $file): void
{
if (! $this->isXsdValidationEnabled) {
return;
}

$backedUpErrorSetting = libxml_use_internal_errors(true);

try {
$document = new DOMDocument();
$document->load($file);

if (! $document->schemaValidate(__DIR__ . '/../../../../../doctrine-mapping.xsd')) {
throw MappingException::fromLibXmlErrors(libxml_get_errors());
}
} finally {
libxml_clear_errors();
libxml_use_internal_errors($backedUpErrorSetting);
}
}

/**
* @param mixed $element
*
Expand Down
20 changes: 20 additions & 0 deletions lib/Doctrine/ORM/Mapping/MappingException.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use BackedEnum;
use Doctrine\ORM\Exception\ORMException;
use Exception;
use LibXMLError;
use ReflectionException;
use ValueError;

Expand All @@ -18,6 +19,8 @@
use function implode;
use function sprintf;

use const PHP_EOL;

/**
* A MappingException indicates that something is wrong with the mapping setup.
*/
Expand Down Expand Up @@ -984,4 +987,21 @@ public static function invalidEnumValue(
$enumType
), 0, $previous);
}

/**
* @param LibXMLError[] $errors
*/
public static function fromLibXmlErrors(array $errors): self
{
$formatter = static function (LibXMLError $error): string {
return sprintf(
'libxml error: %s in %s at line %d',
$error->message,
$error->file,
$error->line
);
};

return new self(implode(PHP_EOL, array_map($formatter, $errors)));
}
}
5 changes: 3 additions & 2 deletions lib/Doctrine/ORM/ORMSetup.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,11 @@ public static function createXMLMetadataConfiguration(
array $paths,
bool $isDevMode = false,
?string $proxyDir = null,
?CacheItemPoolInterface $cache = null
?CacheItemPoolInterface $cache = null,
bool $isXsdValidationEnabled = false
): Configuration {
$config = self::createConfiguration($isDevMode, $proxyDir, $cache);
$config->setMetadataDriverImpl(new XmlDriver($paths));
$config->setMetadataDriverImpl(new XmlDriver($paths, XmlDriver::DEFAULT_FILE_EXTENSION, $isXsdValidationEnabled));

return $config;
}
Expand Down

0 comments on commit 01edf11

Please sign in to comment.