Skip to content

Commit e07a109

Browse files
committed
XML, YAML and chain loaders.
1 parent 7648be8 commit e07a109

File tree

14 files changed

+484
-1
lines changed

14 files changed

+484
-1
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Serializer\Exception;
13+
14+
/**
15+
* MappingException
16+
*
17+
* @author Kévin Dunglas <dunglas@gmail.com>
18+
*/
19+
class MappingException extends RuntimeException
20+
{
21+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Serializer\Mapping\Loader;
13+
14+
use Symfony\Component\Serializer\Exception\MappingException;
15+
use Symfony\Component\Serializer\Mapping\ClassMetadata;
16+
17+
abstract class FileLoader implements LoaderInterface
18+
{
19+
protected $file;
20+
21+
/**
22+
* Constructor.
23+
*
24+
* @param string $file The mapping file to load
25+
*
26+
* @throws MappingException if the mapping file does not exist
27+
* @throws MappingException if the mapping file is not readable
28+
*/
29+
public function __construct($file)
30+
{
31+
if (!is_file($file)) {
32+
throw new MappingException(sprintf('The mapping file %s does not exist', $file));
33+
}
34+
35+
if (!is_readable($file)) {
36+
throw new MappingException(sprintf('The mapping file %s is not readable', $file));
37+
}
38+
39+
$this->file = $file;
40+
}
41+
42+
/**
43+
* {@inheritdoc}
44+
*/
45+
abstract public function loadClassMetadata(ClassMetadata $metadata);
46+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Serializer\Mapping\Loader;
13+
14+
use Symfony\Component\Serializer\Exception\MappingException;
15+
use Symfony\Component\Serializer\Mapping\ClassMetadata;
16+
17+
/**
18+
* Calls multiple LoaderInterface instances in a chain
19+
*
20+
* This class accepts multiple instances of LoaderInterface to be passed to the
21+
* constructor. When loadClassMetadata() is called, the same method is called
22+
* in <em>all</em> of these loaders, regardless of whether any of them was
23+
* successful or not.
24+
*
25+
* @author Bernhard Schussek <bschussek@gmail.com>
26+
*/
27+
class LoaderChain implements LoaderInterface
28+
{
29+
protected $loaders;
30+
31+
/**
32+
* Accepts a list of LoaderInterface instances
33+
*
34+
* @param LoaderInterface[] $loaders An array of LoaderInterface instances
35+
*
36+
* @throws MappingException If any of the loaders does not implement LoaderInterface
37+
*/
38+
public function __construct(array $loaders)
39+
{
40+
foreach ($loaders as $loader) {
41+
if (!$loader instanceof LoaderInterface) {
42+
throw new MappingException(sprintf('Class %s is expected to implement LoaderInterface', get_class($loader)));
43+
}
44+
}
45+
46+
$this->loaders = $loaders;
47+
}
48+
49+
/**
50+
* {@inheritdoc}
51+
*/
52+
public function loadClassMetadata(ClassMetadata $metadata)
53+
{
54+
$success = false;
55+
56+
foreach ($this->loaders as $loader) {
57+
$success = $loader->loadClassMetadata($metadata) || $success;
58+
}
59+
60+
return $success;
61+
}
62+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Serializer\Mapping\Loader;
13+
14+
use Symfony\Component\Config\Util\XmlUtils;
15+
use Symfony\Component\Serializer\Exception\MappingException;
16+
use Symfony\Component\Serializer\Mapping\ClassMetadata;
17+
18+
/**
19+
* Loads XML mapping files.
20+
*
21+
* @author Kévin Dunglas <dunglas@gmail.com>
22+
*/
23+
class XmlFileLoader extends FileLoader
24+
{
25+
/**
26+
* An array of SimpleXMLElement instances.
27+
*
28+
* @var \SimpleXMLElement[]|null
29+
*/
30+
private $classes;
31+
32+
/**
33+
* {@inheritdoc}
34+
*/
35+
public function loadClassMetadata(ClassMetadata $metadata)
36+
{
37+
if (null === $this->classes) {
38+
$this->classes = array();
39+
$xml = $this->parseFile($this->file);
40+
41+
foreach ($xml->class as $class) {
42+
$this->classes[(string) $class['name']] = $class;
43+
}
44+
}
45+
46+
if (isset($this->classes[$metadata->getClassName()])) {
47+
$xml = $this->classes[$metadata->getClassName()];
48+
49+
foreach ($xml->attribute as $attribute) {
50+
foreach ($attribute->group as $group) {
51+
$metadata->addAttributeGroup((string) $attribute['name'], (string) $group);
52+
}
53+
}
54+
55+
return true;
56+
}
57+
58+
return false;
59+
}
60+
61+
/**
62+
* Parse a XML File.
63+
*
64+
* @param string $file Path of file
65+
*
66+
* @return \SimpleXMLElement
67+
*
68+
* @throws MappingException
69+
*/
70+
private function parseFile($file)
71+
{
72+
try {
73+
$dom = XmlUtils::loadFile($file, __DIR__.'/schema/dic/serializer-mapping/serializer-mapping-1.0.xsd');
74+
} catch (\Exception $e) {
75+
throw new MappingException($e->getMessage(), $e->getCode(), $e);
76+
}
77+
78+
return simplexml_import_dom($dom);
79+
}
80+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Serializer\Mapping\Loader;
13+
14+
use Symfony\Component\Serializer\Exception\MappingException;
15+
use Symfony\Component\Serializer\Mapping\ClassMetadata;
16+
use Symfony\Component\Yaml\Parser;
17+
18+
/**
19+
* YAML File Loader
20+
*
21+
* @author Kévin Dunglas <dunglas@gmail.com>
22+
*/
23+
class YamlFileLoader extends FileLoader
24+
{
25+
private $yamlParser;
26+
27+
/**
28+
* An array of YAML class descriptions
29+
*
30+
* @var array
31+
*/
32+
private $classes = null;
33+
34+
/**
35+
* {@inheritdoc}
36+
*/
37+
public function loadClassMetadata(ClassMetadata $metadata)
38+
{
39+
if (null === $this->classes) {
40+
if (!stream_is_local($this->file)) {
41+
throw new MappingException(sprintf('This is not a local file "%s".', $this->file));
42+
}
43+
44+
if (null === $this->yamlParser) {
45+
$this->yamlParser = new Parser();
46+
}
47+
48+
$classes = $this->yamlParser->parse(file_get_contents($this->file));
49+
50+
if (empty($classes)) {
51+
return false;
52+
}
53+
54+
// not an array
55+
if (!is_array($classes)) {
56+
throw new MappingException(sprintf('The file "%s" must contain a YAML array.', $this->file));
57+
}
58+
59+
$this->classes = $classes;
60+
}
61+
62+
if (isset($this->classes[$metadata->getClassName()])) {
63+
$yaml = $this->classes[$metadata->getClassName()];
64+
65+
if (isset($yaml['attributes']) && is_array($yaml['attributes'])) {
66+
foreach ($yaml['attributes'] as $attribute => $data) {
67+
if (isset($data['groups'])) {
68+
foreach ($data['groups'] as $group) {
69+
$metadata->addAttributeGroup($attribute, $group);
70+
}
71+
}
72+
}
73+
}
74+
75+
return true;
76+
}
77+
78+
return false;
79+
}
80+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?xml version="1.0" ?>
2+
3+
<xsd:schema xmlns="http://symfony.com/schema/dic/serializer-mapping"
4+
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
5+
targetNamespace="http://symfony.com/schema/dic/serializer-mapping"
6+
elementFormDefault="qualified">
7+
8+
<xsd:annotation>
9+
<xsd:documentation><![CDATA[
10+
Symfony Serializer Mapping Schema, version 1.0
11+
Authors: Kévin Dunglas
12+
13+
A serializer mapping connects attributes with serialization groups.
14+
]]></xsd:documentation>
15+
</xsd:annotation>
16+
17+
<xsd:element name="serializer" type="serializer" />
18+
19+
<xsd:complexType name="serializer">
20+
<xsd:annotation>
21+
<xsd:documentation><![CDATA[
22+
The root element of the serializer mapping definition.
23+
]]></xsd:documentation>
24+
</xsd:annotation>
25+
<xsd:choice minOccurs="0" maxOccurs="unbounded">
26+
<xsd:element name="class" type="class" />
27+
</xsd:choice>
28+
</xsd:complexType>
29+
30+
<xsd:complexType name="class">
31+
<xsd:annotation>
32+
<xsd:documentation><![CDATA[
33+
Contains serialization groups for a single class.
34+
35+
Nested elements may be class property and/or getter definitions.
36+
]]></xsd:documentation>
37+
</xsd:annotation>
38+
<xsd:choice minOccurs="0" maxOccurs="unbounded">
39+
<xsd:element name="attribute" type="attribute" minOccurs="0" maxOccurs="unbounded" />
40+
</xsd:choice>
41+
<xsd:attribute name="name" type="xsd:string" use="required" />
42+
</xsd:complexType>
43+
44+
<xsd:complexType name="attribute">
45+
<xsd:annotation>
46+
<xsd:documentation><![CDATA[
47+
Contains serialization groups for a attributes. The name of the attribute should be given in the "name" option.
48+
]]></xsd:documentation>
49+
</xsd:annotation>
50+
<xsd:sequence>
51+
<xsd:element name="group" type="xsd:string" maxOccurs="unbounded" />
52+
</xsd:sequence>
53+
<xsd:attribute name="name" type="xsd:string" use="required" />
54+
</xsd:complexType>
55+
56+
</xsd:schema>

src/Symfony/Component/Serializer/Tests/Fixtures/empty-mapping.yml

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
foo
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" ?>
2+
3+
<serializer xmlns="http://symfony.com/schema/dic/serializer"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://symfony.com/schema/dic/serializer http://symfony.com/schema/dic/constraint-mapping/serializer-1.0.xsd">
6+
7+
<class name="Symfony\Component\Serializer\Tests\Fixtures\GroupDummy">
8+
<attribute name="foo">
9+
<group name="group1" />
10+
<group name="group2" />
11+
</attribute>
12+
13+
<attribute name="bar">
14+
<group name="group2" />
15+
</attribute>
16+
</class>
17+
18+
</serializer>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Symfony\Component\Serializer\Tests\Fixtures\GroupDummy:
2+
attributes:
3+
foo:
4+
groups: ['group1', 'group2']
5+
bar:
6+
groups: ['group2']

0 commit comments

Comments
 (0)