Преглед изворни кода

[Config] Improving the exception when a resource cannot be imported

This improves, for example, the exception one would receive if they tried to import a resource from a bundle that doesn't exist.
Previously, the deep "bundle is not activated" exception would be thrown. That has value, however there is no indication of where
the exception is actually occurring.

In this new implementation, we throw an exception that explains exactly which resource, and from which source resource, cannot be
loaded. The deeper exception is still thrown as a nested exception.

Two caveats:

  * The `HttpKernel::varToString` method was replicated
  * This introduces a new `Exception` class, which allows us to prevent lot's of exceptions from nesting into each other in the case
    that some deeply imported resource cannot be imported (each upstream import that fails doesn't add its own exception).
Ryan Weaver пре 14 година
родитељ
комит
b9883a3bad

+ 63 - 0
src/Symfony/Component/Config/Exception/FileLoaderImportException.php

@@ -0,0 +1,63 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Config\Exception;
+
+/**
+ * Exception class for when a resource cannot be imported.
+ *
+ * @author Ryan Weaver <ryan@thatsquality.com>
+ */
+class FileLoaderImportException extends \Exception
+{
+    /**
+     * @param  string    $resource The resource that could not be imported
+     * @param  string    $sourceResource The original resource importing the new resource
+     * @param  integer   $code     The error code
+     * @param  Exception $previous A previous exception
+     */
+    public function __construct($resource, $sourceResource, $code = null, $previous = null)
+    {
+        if (null === $sourceResource) {
+            $message = sprintf('Cannot import resource "%s".', $this->varToString($resource));
+        } else {
+            $message = sprintf('Cannot import resource "%s" from "%s".', $this->varToString($resource), $this->varToString($sourceResource));
+        }
+
+        parent::__construct($message, $code, $previous);
+    }
+
+    private function varToString($var)
+    {
+        if (is_object($var)) {
+            return sprintf('[object](%s)', get_class($var));
+        }
+
+        if (is_array($var)) {
+            $a = array();
+            foreach ($var as $k => $v) {
+                $a[] = sprintf('%s => %s', $k, $this->varToString($v));
+            }
+
+            return sprintf("[array](%s)", implode(', ', $a));
+        }
+
+        if (is_resource($var)) {
+            return '[resource]';
+        }
+
+        if (null === $var) {
+            return 'null';
+        }
+
+        return str_replace("\n", '', var_export((string) $var, true));
+    }
+}

+ 8 - 2
src/Symfony/Component/Config/Loader/FileLoader.php

@@ -12,6 +12,7 @@
 namespace Symfony\Component\Config\Loader;
 
 use Symfony\Component\Config\FileLocatorInterface;
+use Symfony\Component\Config\Exception\FileLoaderImportException;
 
 /**
  * FileLoader is the abstract class used by all built-in loaders that are file based.
@@ -51,7 +52,7 @@ abstract class FileLoader extends Loader
      *
      * @return mixed
      */
-    public function import($resource, $type = null, $ignoreErrors = false)
+    public function import($resource, $type = null, $ignoreErrors = false, $sourceResource = null)
     {
         try {
             $loader = $this->resolve($resource, $type);
@@ -63,7 +64,12 @@ abstract class FileLoader extends Loader
             return $loader->load($resource);
         } catch (\Exception $e) {
             if (!$ignoreErrors) {
-                throw $e;
+                // prevent embedded imports from nesting multiple exceptions
+                if ($e instanceof FileLoaderImportException) {
+                    throw $e;
+                }
+
+                throw new FileLoaderImportException($resource, $sourceResource, null, $e);
             }
         }
     }

+ 1 - 1
src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php

@@ -107,7 +107,7 @@ class XmlFileLoader extends FileLoader
 
         foreach ($imports as $import) {
             $this->setCurrentDir(dirname($file));
-            $this->import((string) $import['resource'], null, (Boolean) $import->getAttributeAsPhp('ignore-errors'));
+            $this->import((string) $import['resource'], null, (Boolean) $import->getAttributeAsPhp('ignore-errors'), $file);
         }
     }
 

+ 1 - 1
src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php

@@ -97,7 +97,7 @@ class YamlFileLoader extends FileLoader
 
         foreach ($content['imports'] as $import) {
             $this->setCurrentDir(dirname($file));
-            $this->import($import['resource'], null, isset($import['ignore_errors']) ? (Boolean) $import['ignore_errors'] : false);
+            $this->import($import['resource'], null, isset($import['ignore_errors']) ? (Boolean) $import['ignore_errors'] : false, $file);
         }
     }
 

+ 1 - 1
src/Symfony/Component/Routing/Loader/XmlFileLoader.php

@@ -57,7 +57,7 @@ class XmlFileLoader extends FileLoader
                     $type = (string) $node->getAttribute('type');
                     $prefix = (string) $node->getAttribute('prefix');
                     $this->setCurrentDir(dirname($path));
-                    $collection->addCollection($this->import($resource, ('' !== $type ? $type : null)), $prefix);
+                    $collection->addCollection($this->import($resource, ('' !== $type ? $type : null), false, $file), $prefix);
                     break;
                 default:
                     throw new \InvalidArgumentException(sprintf('Unable to parse tag "%s"', $node->tagName));

+ 1 - 1
src/Symfony/Component/Routing/Loader/YamlFileLoader.php

@@ -64,7 +64,7 @@ class YamlFileLoader extends FileLoader
                 $type = isset($config['type']) ? $config['type'] : null;
                 $prefix = isset($config['prefix']) ? $config['prefix'] : null;
                 $this->setCurrentDir(dirname($path));
-                $collection->addCollection($this->import($config['resource'], $type), $prefix);
+                $collection->addCollection($this->import($config['resource'], $type, false, $file), $prefix);
             } elseif (isset($config['pattern'])) {
                 $this->parseRoute($collection, $name, $config, $path);
             } else {