      1: <?php
      2: /*************************************************************************************/
      3: /*                                                                                   */
      4: /*      Thelia                                                                       */
      5: /*                                                                                   */
      6: /*      Copyright (c) OpenStudio                                                     */
      7: /*  email : info@thelia.net                                                      */
      8: /*      web : http://www.thelia.net                                                  */
      9: /*                                                                                   */
     10: /*      This program is free software; you can redistribute it and/or modify         */
     11: /*      it under the terms of the GNU General Public License as published by         */
     12: /*      the Free Software Foundation; either version 3 of the License                */
     13: /*                                                                                   */
     14: /*      This program is distributed in the hope that it will be useful,              */
     15: /*      but WITHOUT ANY WARRANTY; without even the implied warranty of               */
     16: /*      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                */
     17: /*      GNU General Public License for more details.                                 */
     18: /*                                                                                   */
     19: /*      You should have received a copy of the GNU General Public License            */
     20: /*      along with this program. If not, see <http://www.gnu.org/licenses/>.     */
     21: /*                                                                                   */
     22: /*************************************************************************************/
     23:
     24: namespace Thelia\Core\DependencyInjection\Loader;
     25:
     26: use Symfony\Component\Config\Resource\FileResource;
     27: use Symfony\Component\DependencyInjection\Loader\XmlFileLoader as XmlLoader;
     28: use Symfony\Component\Config\Util\XmlUtils;
     29: use Symfony\Component\DependencyInjection\DefinitionDecorator;
     30: use Symfony\Component\DependencyInjection\ContainerInterface;
     31: use Symfony\Component\DependencyInjection\Alias;
     32: use Symfony\Component\DependencyInjection\Definition;
     33: use Symfony\Component\DependencyInjection\Reference;
     34: use Symfony\Component\DependencyInjection\SimpleXMLElement;
     35: use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
     36: use Symfony\Component\DependencyInjection\Exception\RuntimeException;
     37: use Symfony\Component\DependencyInjection\Loader\FileLoader;
     38:
     39: class XmlFileLoader extends FileLoader
     40: {
     41:     /**
     42:      * Loads an XML file.
     43:      *
     44:      * @param mixed  $file The resource
     45:      * @param string $type The resource type
     46:      */
     47:     public function load($file, $type = null)
     48:     {
     49:         $path = $this->locator->locate($file);
     50:
     51:         $xml = $this->parseFile($path);
     52:         $xml->registerXPathNamespace('config', 'http://thelia.net/schema/dic/config');
     53:
     54:         $this->container->addResource(new FileResource($path));
     55:
     56:         $this->parseLoops($xml);
     57:
     58:         $this->parseFilters($xml);
     59:
     60:         $this->parseBaseParams($xml);
     61:
     62:         $this->parseTestLoops($xml);
     63:
     64:         $this->parseParameters($xml);
     65:
     66:         $this->parseDefinitions($xml, $path);
     67:     }
     68:
     69:     /**
     70:      * Parses parameters
     71:      *
     72:      * @param SimpleXMLElement $xml
     73:      */
     74:     protected function parseParameters(SimpleXMLElement $xml)
     75:     {
     76:         if (!$xml->parameters) {
     77:             return;
     78:         }
     79:
     80:         $this->container->getParameterBag()->add($xml->parameters->getArgumentsAsPhp('parameter'));
     81:     }
     82:
     83:     /**
     84:      *
     85:      * parse Loops property
     86:      *
     87:      * @param SimpleXMLElement $xml
     88:      */
     89:     protected function parseLoops(SimpleXMLElement $xml)
     90:     {
     91:         if (false === $loops = $xml->xpath('//config:loops/config:loop')) {
     92:             return;
     93:         }
     94:         try {
     95:             $loopConfig = $this->container->getParameter("Tpex.loop");
     96:         } catch (\Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException $e) {
     97:             $loopConfig = array();
     98:         }
     99:
    100:         foreach ($loops as $loop) {
    101:             $loopConfig[$loop->getAttributeAsPhp("name")] = $loop->getAttributeAsPhp("class");
    102:         }
    103:
    104:         $this->container->setParameter("Tpex.loop", $loopConfig);
    105:     }
    106:
    107:     /**
    108:      * parse Filters property
    109:      *
    110:      * @param SimpleXMLElement $xml
    111:      */
    112:     protected function parseFilters(SimpleXMLElement $xml)
    113:     {
    114:         if (false === $filters = $xml->xpath('//config:filters/config:filter')) {
    115:             return;
    116:         }
    117:         try {
    118:             $filterConfig = $this->container->getParameter("Tpex.filter");
    119:         } catch (\Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException $e) {
    120:             $filterConfig = array();
    121:         }
    122:
    123:         foreach ($filters as $filter) {
    124:             $filterConfig[$filter->getAttributeAsPhp("name")] = $filter->getAttributeAsPhp("class");
    125:         }
    126:
    127:         $this->container->setParameter("Tpex.filter", $filterConfig);
    128:     }
    129:
    130:     /**
    131:      * parse BaseParams property
    132:      *
    133:      * @param SimpleXMLElement $xml
    134:      */
    135:     protected function parseBaseParams(SimpleXMLElement $xml)
    136:     {
    137:         if (false === $baseParams = $xml->xpath('//config:baseParams/config:baseParam')) {
    138:             return;
    139:         }
    140:         try {
    141:             $baseParamConfig = $this->container->getParameter("Tpex.baseParam");
    142:         } catch (\Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException $e) {
    143:             $baseParamConfig = array();
    144:         }
    145:
    146:         foreach ($baseParams as $baseParam) {
    147:             $baseParamConfig[$baseParam->getAttributeAsPhp("name")] = $baseParam->getAttributeAsPhp("class");
    148:         }
    149:
    150:         $this->container->setParameter("Tpex.baseParam", $baseParamConfig);
    151:     }
    152:
    153:     /**
    154:      * parse testLoops property
    155:      *
    156:      * @param SimpleXMLElement $xml
    157:      */
    158:     protected function parseTestLoops(SimpleXMLElement $xml)
    159:     {
    160:         if (false === $testLoops = $xml->xpath('//config:testLoops/config:testLoop')) {
    161:             return;
    162:         }
    163:         try {
    164:             $baseParamConfig = $this->container->getParameter("Tpex.baseParam");
    165:         } catch (\Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException $e) {
    166:             $baseParamConfig = array();
    167:         }
    168:
    169:         foreach ($testLoops as $testLoop) {
    170:             $baseParamConfig[$testLoop->getAttributeAsPhp("name")] = $testLoop->getAttributeAsPhp("class");
    171:         }
    172:
    173:         $this->container->setParameter("Tpex.testLoop", $baseParamConfig);
    174:     }
    175:
    176:     /**
    177:      * Parses multiple definitions
    178:      *
    179:      * @param SimpleXMLElement $xml
    180:      * @param string           $file
    181:      */
    182:     protected function parseDefinitions(SimpleXMLElement $xml, $file)
    183:     {
    184:         if (false === $services = $xml->xpath('//config:services/config:service')) {
    185:             return;
    186:         }
    187:         foreach ($services as $service) {
    188:             $this->parseDefinition((string) $service['id'], $service, $file);
    189:         }
    190:     }
    191:
    192:     /**
    193:      * Parses an individual Definition
    194:      *
    195:      * @param string           $id
    196:      * @param SimpleXMLElement $service
    197:      * @param string           $file
    198:      */
    199:     protected function parseDefinition($id, $service, $file)
    200:     {
    201:
    202:         if ((string) $service['alias']) {
    203:             $public = true;
    204:             if (isset($service['public'])) {
    205:                 $public = $service->getAttributeAsPhp('public');
    206:             }
    207:             $this->container->setAlias($id, new Alias((string) $service['alias'], $public));
    208:
    209:             return;
    210:         }
    211:
    212:         if (isset($service['parent'])) {
    213:             $definition = new DefinitionDecorator((string) $service['parent']);
    214:         } else {
    215:             $definition = new Definition();
    216:         }
    217:
    218:         foreach (array('class', 'scope', 'public', 'factory-class', 'factory-method', 'factory-service', 'synthetic', 'abstract') as $key) {
    219:             if (isset($service[$key])) {
    220:                 $method = 'set'.str_replace('-', '', $key);
    221:                 $definition->$method((string) $service->getAttributeAsPhp($key));
    222:             }
    223:         }
    224:
    225:         if ($service->file) {
    226:             $definition->setFile((string) $service->file);
    227:         }
    228:
    229:         $definition->setArguments($service->getArgumentsAsPhp('argument'));
    230:         $definition->setProperties($service->getArgumentsAsPhp('property'));
    231:
    232:         if (isset($service->configurator)) {
    233:             if (isset($service->configurator['function'])) {
    234:                 $definition->setConfigurator((string) $service->configurator['function']);
    235:             } else {
    236:                 if (isset($service->configurator['service'])) {
    237:                     $class = new Reference((string) $service->configurator['service'], ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false);
    238:                 } else {
    239:                     $class = (string) $service->configurator['class'];
    240:                 }
    241:
    242:                 $definition->setConfigurator(array($class, (string) $service->configurator['method']));
    243:             }
    244:         }
    245:
    246:         foreach ($service->call as $call) {
    247:             $definition->addMethodCall((string) $call['method'], $call->getArgumentsAsPhp('argument'));
    248:         }
    249:
    250:         foreach ($service->tag as $tag) {
    251:             $parameters = array();
    252:             foreach ($tag->attributes() as $name => $value) {
    253:                 if ('name' === $name) {
    254:                     continue;
    255:                 }
    256:
    257:                 $parameters[$name] = SimpleXMLElement::phpize($value);
    258:             }
    259:
    260:             $definition->addTag((string) $tag['name'], $parameters);
    261:         }
    262:
    263:         $this->container->setDefinition($id, $definition);
    264:     }
    265:
    266:     /**
    267:      * Parses a XML file.
    268:      *
    269:      * @param string $file Path to a file
    270:      *
    271:      * @return SimpleXMLElement
    272:      *
    273:      * @throws InvalidArgumentException When loading of XML file returns error
    274:      */
    275:     protected function parseFile($file)
    276:     {
    277:         try {
    278:             $dom = XmlUtils::loadFile($file, array($this, 'validateSchema'));
    279:         } catch (\InvalidArgumentException $e) {
    280:             throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
    281:         }
    282:
    283:         return simplexml_import_dom($dom, 'Symfony\\Component\\DependencyInjection\\SimpleXMLElement');
    284:     }
    285:
    286:     /**
    287:      * Validates a documents XML schema.
    288:      *
    289:      * @param \DOMDocument $dom
    290:      *
    291:      * @return Boolean
    292:      *
    293:      * @throws RuntimeException When extension references a non-existent XSD file
    294:      */
    295:     public function validateSchema(\DOMDocument $dom)
    296:     {
    297:         $schemaLocations = array('http://thelia.net/schema/dic/config' => str_replace('\\', '/',__DIR__.'/schema/dic/config/thelia-1.0.xsd'));
    298:
    299:         $tmpfiles = array();
    300:         $imports = '';
    301:         foreach ($schemaLocations as $namespace => $location) {
    302:             $parts = explode('/', $location);
    303:             if (0 === stripos($location, 'phar://')) {
    304:                 $tmpfile = tempnam(sys_get_temp_dir(), 'sf2');
    305:                 if ($tmpfile) {
    306:                     copy($location, $tmpfile);
    307:                     $tmpfiles[] = $tmpfile;
    308:                     $parts = explode('/', str_replace('\\', '/', $tmpfile));
    309:                 }
    310:             }
    311:             $drive = '\\' === DIRECTORY_SEPARATOR ? array_shift($parts).'/' : '';
    312:             $location = 'file:///'.$drive.implode('/', array_map('rawurlencode', $parts));
    313:
    314:             $imports .= sprintf('  <xsd:import namespace="%s" schemaLocation="%s" />'."\n", $namespace, $location);
    315:         }
    316:
    317:         $source = <<<EOF
    318: <?xml version="1.0" encoding="utf-8" ?>
    319: <xsd:schema xmlns="http://symfony.com/schema"
    320:     xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    321:     targetNamespace="http://symfony.com/schema"
    322:     elementFormDefault="qualified">
    323:
    324:     <xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
    325: $imports
    326: </xsd:schema>
    327: EOF
    328:         ;
    329:
    330:         $valid = @$dom->schemaValidateSource($source);
    331:
    332:         foreach ($tmpfiles as $tmpfile) {
    333:             @unlink($tmpfile);
    334:         }
    335:
    336:         return $valid;
    337:     }
    338:
    339:     /**
    340:      * Returns true if this class supports the given resource.
    341:      *
    342:      * @param mixed $resource A resource
    343:      * @param string $type     The resource type
    344:      *
    345:      * @return Boolean true if this class supports the given resource, false otherwise
    346:      */
    347:     public function supports($resource, $type = null)
    348:     {
    349:         // TODO: Implement supports() method.
    350:     }
    351: }