OSDN Git Service

add checking method visibility
[php-libraries/Router.git] / src / Route.php
index e4b0603..528fbce 100755 (executable)
@@ -1,23 +1,6 @@
 <?php
-/**
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * This software consists of voluntary contributions made by many individuals
- * and is licensed under the MIT license.
- */
-namespace PHPRouter;
 
-use Fig\Http\Message\RequestMethodInterface;
+namespace PHPRouter;
 
 class Route
 {
@@ -29,14 +12,10 @@ class Route
 
     /**
      * Accepted HTTP methods for this route.
+     *
      * @var string[]
      */
-    private $methods = array(
-        RequestMethodInterface::METHOD_GET,
-        RequestMethodInterface::METHOD_POST,
-        RequestMethodInterface::METHOD_PUT,
-        RequestMethodInterface::METHOD_DELETE,
-    );
+    private $methods;
 
     /**
      * Target for this route, can be anything.
@@ -63,11 +42,9 @@ class Route
     private $parameters = array();
 
     /**
-     * Set named parameters to target method
-     * @example [ [0] => [ ["link_id"] => "12312" ] ]
-     * @var bool
+     * @var null|string
      */
-    private $parametersByName;
+    private $action;
 
     /**
      * @var array
@@ -75,16 +52,26 @@ class Route
     private $config;
 
     /**
-     * @param       $resource
-     * @param array $config
+     * @var null|string
      */
-    public function __construct($resource, array $config)
+    private $class;
+
+    /**
+     * @param string $pathInfo
+     * @param array  $config
+     */
+    public function __construct($pathInfo, array $config)
     {
-        $this->url     = $resource;
-        $this->config  = $config;
-        $this->methods = isset($config['methods']) ? (array) $config['methods'] : array();
-        $this->target  = isset($config['target']) ? $config['target'] : null;
-        $this->name    = isset($config['name']) ? $config['name'] : null;
+        $this->url        = $pathInfo;
+        $this->config     = $config;
+        $this->methods    = isset($config['methods']) ? (array)$config['methods'] : array();
+        $this->target     = isset($config['target']) ? $config['target'] : null;
+        $this->name       = isset($config['name']) ? $config['name'] : null;
+        $this->parameters = isset($config['parameters']) ? $config['parameters'] : array();
+        $this->filters    = isset($config['filters']) ? $config['filters'] : array();
+        $action           = isset ($this->config['_controller']) ? explode('::', $this->config['_controller']) : array();
+        $this->class      = isset($action[0]) ? $action[0] : null;
+        $this->action     = isset($action[1]) ? $action[1] : null;
     }
 
     public function getUrl()
@@ -134,21 +121,27 @@ class Route
         $this->name = (string)$name;
     }
 
-    public function setFilters(array $filters, $parametersByName = false)
+    public function setFilters(array $filters)
     {
-        $this->filters          = $filters;
-        $this->parametersByName = $parametersByName;
+        $this->filters = $filters;
+    }
+
+    public function getFilters()
+    {
+        return $this->filters;
     }
 
     public function getRegex()
     {
-        return preg_replace_callback('/(:\w+)/', array(&$this, 'substituteFilter'), $this->url);
+        $url = preg_quote($this->url);
+
+        return preg_replace_callback('/(\\\\(:\w+))/', array(&$this, 'substituteFilter'), $url);
     }
 
     private function substituteFilter($matches)
     {
-        if (isset($matches[1], $this->filters[$matches[1]])) {
-            return $this->filters[$matches[1]];
+        if (isset($matches[1], $this->filters[$matches[2]])) {
+            return $this->filters[$matches[2]];
         }
 
         return '([\w-%]+)';
@@ -164,26 +157,128 @@ class Route
         $this->parameters = $parameters;
     }
 
-    public function dispatch()
+    public function getValidController()
+    {
+        $class  = $this->class;
+        $method = $this->action;
+
+        if (!class_exists($class)) {
+            return null;
+        }
+
+        if (empty($method) || trim($method) === '') {
+            $method = "__invoke";
+        }
+
+        try {
+            $classRexl = new \ReflectionClass($class);
+        } catch (\ReflectionException $ex) {
+            return null;
+        }
+
+        try {
+            $methRexl = $classRexl->getMethod($method);
+        } catch (\ReflectionException $ex) {
+            return null;
+        }
+
+        // use only public methods
+        // to avoid calling inherited protected methods
+        if(!$methRexl->isPublic()) {
+            return null;
+        }
+
+        return $this->config['_controller'];
+    }
+
+    /**
+     * sort parameters according the the method's arguments
+     *
+     * @return array
+     *
+     * @throws \ReflectionException
+     */
+    private function sortParameters()
     {
-        $action = explode('::', $this->config['_controller']);
+        $class      = $this->class;
+        $method     = $this->action;
+        $parameters = $this->parameters;
+        $arguments  = array();
 
-        if ($this->parametersByName) {
-            $this->parameters = array($this->parameters);
+        if (empty($method) || trim($method) === '') {
+            $method = "__invoke";
         }
 
-        $this->action = !empty($action[1]) && trim($action[1]) !== '' ? $action[1] : null;
+        $rexl = new \ReflectionMethod($class, $method);
 
-        if (!is_null($this->action)) {
-            $instance = new $action[0];
-            call_user_func_array(array($instance, $this->action), $this->parameters);
+        foreach ($rexl->getParameters() as $methArgs) {
+            $arg = $methArgs->getName();
+
+            if (array_key_exists($arg, $parameters)) {
+                $arguments[$arg] = $parameters[$arg];
+
+                unset($parameters[$arg]);
+            } else {
+                // argument is not in the parameters
+                $arguments[$arg] = null;
+            }
+        }
+
+        if (count($parameters) > 0) {
+            // fill the unset arguments
+            foreach ($arguments as $arg => &$v) {
+                if ($v === null) {
+                    //$key = array_keys($parameters)[0];
+
+                    $v = array_shift($parameters);
+                }
+
+                if (count($parameters) <= 0) {
+                    break;
+                }
+            }
+        }
+
+        // merge the remaining parameters
+        return array_merge($arguments, $parameters);
+    }
+
+    private function canBeEchoed($var)
+    {
+        return method_exists($var, '__toString') || (is_scalar($var) && !is_null($var));
+    }
+
+    public function dispatch($instance = null)
+    {
+        is_null($instance) and $instance = new $this->class();
+
+        $param = $this->sortParameters();
+
+        ob_start();
+
+        if (empty($this->action) || trim($this->action) === '') {
+            // __invoke on a class
+            $result = call_user_func_array($instance, $param);
         } else {
-            $instance = new $action[0]($this->parameters);
+            $result = call_user_func_array(array($instance, $this->action), $param);
+        }
+
+        if ($this->canBeEchoed($result)) {
+            echo $result;
         }
+
+        $buffer = ob_get_clean();
+
+        return $buffer;
     }
 
     public function getAction()
     {
         return $this->action;
     }
+
+    public function getClass()
+    {
+        return $this->class;
+    }
 }