Beispiel #1
0
 public function testMuxCompiler()
 {
     $mux = new Mux();
     $mux->add('/hello/:name', array('FooController', 'index'));
     $mux->compile("hello_mux.php");
     $mux2 = new Mux();
     $mux2->add('/bye/:name', array('FooController', 'index'));
     $mux2->compile("bye_mux.php");
     $compiler = new MuxCompiler();
     ok($compiler->load("hello_mux.php"));
     ok($compiler->load("bye_mux.php"));
     $compiler->compileReflectionParameters();
     ok($compiler->compile("merged_mux.php"));
     path_ok("merged_mux.php");
     $mux = (require "merged_mux.php");
     ok($mux);
     $routes = $mux->getRoutes();
     ok($routes);
     count_ok(2, $routes);
     ok($mux->dispatch('/hello/John'));
     ok($mux->dispatch('/bye/John'));
     unlink("merged_mux.php");
     unlink("hello_mux.php");
     unlink("bye_mux.php");
 }
 public function testAnnotations()
 {
     if (defined('HHVM_VERSION')) {
         echo "HHVM does not support Reflection to expand controller action methods";
         return;
     }
     $controller = new ExpandableProductController();
     $this->assertTrue(is_array($map = $controller->getActionMethods()));
     $routes = $controller->getActionRoutes();
     $this->assertNotEmpty($routes);
     $this->assertEquals('', $routes[0][0], 'the path');
     $this->assertEquals('indexAction', $routes[0][1], 'the mapping method');
     $mux = new Pux\Mux();
     // works fine
     // $submux = $controller->expand();
     // $mux->mount('/product', $submux );
     // gc scan bug
     $mux->mount('/product', $controller->expand());
     $paths = array('/product/delete' => 'DELETE', '/product/update' => 'PUT', '/product/add' => 'POST', '/product/foo/bar' => null, '/product/item' => 'GET', '/product' => null);
     foreach ($paths as $path => $method) {
         if ($method) {
             $_SERVER['REQUEST_METHOD'] = $method;
         } else {
             $_SERVER['REQUEST_METHOD'] = 'GET';
         }
         ok($mux->dispatch($path), $path);
     }
 }
Beispiel #3
0
 public function mount($pattern, $mux, $options = array())
 {
     if ($mux instanceof \Pux\Controller) {
         $mux = $mux->expand();
     } else {
         if ((!is_object($mux) || !$mux instanceof Mux) && is_callable($mux)) {
             $mux($mux = new Mux());
         }
     }
     if ($this->expand) {
         $pcre = strpos($pattern, ':') !== false;
         // rewrite submux routes
         foreach ($mux->routes as $route) {
             // process for pcre
             if ($route[0] || $pcre) {
                 $newPattern = $pattern . ($route[0] ? $route[3]['pattern'] : $route[1]);
                 $routeArgs = PatternCompiler::compile($newPattern, array_merge_recursive($route[3], $options));
                 $this->appendPCRERoute($routeArgs, $route[2]);
             } else {
                 $this->routes[] = array(false, $pattern . $route[1], $route[2], isset($route[3]) ? array_merge($options, $route[3]) : $options);
             }
         }
     } else {
         $muxId = $mux->getId();
         $this->add($pattern, $muxId, $options);
         $this->submux[$muxId] = $mux;
     }
 }
Beispiel #4
0
 public function testMuxRoutePCRERouteDefine()
 {
     $mux = new Mux();
     // $mux->add('/hello/:name', ['IndexController', 'indexAction']);
     $mux->add('/hello', ['IndexController', 'indexAction']);
     $mux->add('/foo', ['IndexController', 'indexAction']);
     $mux->add('/bar', ['IndexController', 'indexAction']);
 }
Beispiel #5
0
 /**
  * build method returns a Mux object with registered resources.
  *
  * @return Pux\Mux
  */
 public function build()
 {
     $prefix = $this->options['prefix'];
     foreach ($this->resources as $resId => $controller) {
         $resourceMux = $controller->expand();
         $path = $prefix . '/' . $resId;
         $this->mux->mount($path, $resourceMux);
     }
     return $this->mux;
 }
 public function testIndexAction()
 {
     return $this->markTestSkipped('phifty bootstrap script is not fixed yet');
     $handler = new EmailTemplateCRUDHandler();
     $mux = new Mux();
     $mux->mount('/bs/email', $handler);
     $route = $mux->dispatch('/bs/email');
     $this->assertNotNull($route);
     $response = RouteExecutor::execute($route);
     $this->assertNotNull($response);
 }
Beispiel #7
0
 public static function mountWithUrlMap(array $map)
 {
     $mux = new Mux();
     foreach ($map as $path => $app) {
         if ($app instanceof Compositor) {
             $app = $app->wrap();
         }
         $mux->any($path, $app);
     }
     return new self($mux);
 }
Beispiel #8
0
 public function testSubmuxPcreRouteFound()
 {
     $mux = new \Pux\Mux();
     ok($mux);
     is(0, $mux->length());
     $submux = new \Pux\Mux();
     $submux->any('/hello/:name', array('HelloController2', 'indexAction'));
     ok($submux);
     ok($routes = $submux->getRoutes());
     is(1, $submux->length());
     is(0, $mux->length());
     $mux->mount('/sub', $submux);
     $r = $mux->dispatch('/sub/hello/John');
     ok($r);
     $this->assertPcreRoute($r, '/sub/hello/:name');
 }
Beispiel #9
0
 public function testMuxStaticIdGenerator()
 {
     $id = \Pux\Mux::generate_id();
     ok($id, "got mux ID {$id}");
     ok(is_numeric($id), "got mux ID {$id}");
     ok(is_integer($id), "got mux ID {$id}");
 }
Beispiel #10
0
 public function testMuxCustomDispatch()
 {
     $mux = new Mux();
     $mux->mount('/bs/product', function ($x) {
         $x = new Mux();
         $x->any('/create', ['Product', 'createAction']);
         $x->any('/edit/:id', ['Product', 'editAction']);
         return $x;
     });
     $route = $mux->dispatch('/bs/product/create');
     $this->assertNotEmpty($route);
     $this->assertEquals('/create', $route[1]);
     $route = $mux->dispatch('/bs/product/edit/30');
     $this->assertNotEmpty($route);
     $this->assertEquals('/edit/:id', $route[3]['pattern']);
 }
Beispiel #11
0
 public function test()
 {
     if (!extension_loaded('apc') && !extension_loaded('apcu')) {
         // echo 'APC or APCu extension is required.';
         return;
     }
     $mux = new Mux();
     $mux->add('/product/add', array('ProductController', 'addAction'));
     $dispatcher = new APCDispatcher($mux, array('namespace' => 'tests', 'expiry' => 10));
     ok($dispatcher);
     $route = $dispatcher->dispatch('/product/add');
     ok($route);
     $route = $dispatcher->dispatch('/product/add');
     ok($route);
     $route = $dispatcher->dispatch('/product/del');
     ok($route == false);
 }
Beispiel #12
0
 public function testMuxMountNoExpandAndDispatchToCallableSubMux()
 {
     $mux = new \Pux\Mux();
     $mux->expand = false;
     $mux->mount('/test', function (Mux $submux) {
         $submux->any('/hello/static', array('HelloController2', 'indexAction'));
         $submux->any('/hello/:name', array('HelloController2', 'indexAction'));
     });
     ok($mux);
     ok($routes = $mux->getRoutes());
     $r = $mux->dispatch('/test/hello/John');
     ok($r);
     $this->assertPcreRoute($r, '/hello/:name');
     $r = $mux->dispatch('/test/hello/static');
     ok($r);
     $this->assertNonPcreRoute($r, '/hello/static');
 }
Beispiel #13
0
 public function testMuxMountNoExpandAndDispatchToSubMux()
 {
     $mux = new \Pux\Mux();
     $mux->expand = false;
     ok($mux);
     $submux = new \Pux\Mux();
     $submux->any('/hello/:name', array('HelloController2', 'indexAction'));
     $submux->any('/foo', array('HelloController2', 'indexAction'));
     $mux->mount('/sub', $submux);
     ok($submux, 'submux');
     ok($submux->getRoutes(), 'submux routes');
     ok($mux->getRoutes(), 'mux routes');
     $submux2 = $mux->getSubMux($submux->getId());
     ok($submux2, 'submux2');
     ok($submux2->getRoutes(), 'submux2 routes');
     $r = $mux->dispatch('/sub/hello/John');
     ok($r);
     $this->assertPcreRoute($r, '/hello/:name');
     $r = $mux->dispatch('/sub/foo');
     $this->assertNonPcreRoute($r, '/foo');
 }
Beispiel #14
0
 /**
  * Expand controller actions into Mux object
  *
  * @return Mux
  */
 public function expand(array $options = array(), $dynamic = false)
 {
     $mux = new Mux();
     $target = $dynamic ? $this : $this->getClass();
     $mux->add('/:id', [$target, 'updateAction'], array_merge($options, array('method' => REQUEST_METHOD_POST)));
     $mux->add('/:id', [$target, 'loadAction'], array_merge($options, array('method' => REQUEST_METHOD_GET)));
     $mux->add('/:id', [$target, 'deleteAction'], array_merge($options, array('method' => REQUEST_METHOD_DELETE)));
     $mux->add('', [$target, 'createAction'], array_merge($options, array('method' => REQUEST_METHOD_POST)));
     $mux->add('', [$target, 'collectionAction'], array_merge($options, array('method' => REQUEST_METHOD_GET)));
     return $mux;
 }
Beispiel #15
0
 /**
  * Expand controller actions into Mux object
  *
  * @return Mux
  */
 public function expand($dynamic = false)
 {
     $mux = new Mux();
     $target = $dynamic ? $this : $this->getClass();
     $mux->add('/:id', [$target, 'updateAction'], ['method' => REQUEST_METHOD_POST]);
     $mux->add('/:id', [$target, 'getAction'], ['method' => REQUEST_METHOD_GET]);
     $mux->add('/:id', [$target, 'deleteAction'], ['method' => REQUEST_METHOD_DELETE]);
     $mux->add('', [$target, 'createAction'], ['method' => REQUEST_METHOD_POST]);
     $mux->add('', [$target, 'getCollectionAction'], ['method' => REQUEST_METHOD_GET]);
     return $mux;
 }
Beispiel #16
0
 public function testMuxGetMethod()
 {
     $mux = new Mux();
     $mux->get('/news', array('NewsController', 'listAction'));
     $mux->get('/news_item', array('NewsController', 'itemAction'), array());
     $routes = $mux->getRoutes();
     $this->assertCount(2, $routes);
     $this->assertEquals(2, $mux->length());
     $_SERVER['REQUEST_METHOD'] = "GET";
     $this->assertNotNull($mux->dispatch('/news_item'));
     $this->assertNotNull($mux->dispatch('/news'));
 }
Beispiel #17
0
 public function testMuxCompile()
 {
     $mux = new Mux();
     $mux->add('/product/:id', array('ProductController', 'itemAction'));
     $mux->add('/product', array('ProductController', 'listAction'));
     $mux->add('/foo', array('ProductController', 'fooAction'));
     $mux->add('/bar', array('ProductController', 'barAction'));
     $mux->add('/', array('ProductController', 'indexAction'));
     $ret = $mux->compile("_test_mux.php");
     ok($ret, "compile successfully");
     $newMux = (require "_test_mux.php");
     $this->assertNotNull($newMux);
     $this->assertNotNull($r = $newMux->dispatch("/foo"));
     $this->assertNonPcreRoute($r, "/foo");
     $this->assertNotNull($r = $newMux->dispatch("/product"));
     $this->assertNonPcreRoute($r, "/product");
     $this->assertNotNull($r = $newMux->dispatch('/'));
     $this->assertNonPcreRoute($r, '/');
     $this->assertNotNull($r = $newMux->dispatch('/bar'));
     $this->assertNonPcreRoute($r, '/bar');
     $this->assertNotNull($r = $newMux->dispatch('/product/10'));
     $this->assertPcreRoute($r, '/product/:id');
 }
Beispiel #18
0
 public function testMuxMounting()
 {
     $mainMux = new Mux();
     $mainMux->expand = false;
     $pageMux = new Mux();
     $pageMux->add('/page1', array('PageController', 'page1'));
     $pageMux->add('/page2', array('PageController', 'page2'));
     $mainMux->mount('/sub', $pageMux);
     $pageMux2 = new Mux();
     $pageMux2->add('/bar', array('PageController', 'page1'));
     $pageMux2->add('/zoo', array('PageController', 'page2'));
     is(2, $pageMux2->length());
     $mainMux->mount('/foo', $pageMux2);
     is(2, $mainMux->length());
     foreach (array('/sub/page1', '/sub/page2', '/foo/bar', '/foo/zoo') as $p) {
         $r = $mainMux->dispatch($p);
         ok($r, "Matched route for {$p}");
     }
     return $mainMux;
 }
Beispiel #19
0
 public function testRESTfulDispatch()
 {
     $con = new ProductResourceController();
     $routes = $con->getActionRoutes();
     $this->assertNotEmpty($routes);
     $this->assertTrue(is_array($routes));
     $methods = $con->getActionMethods();
     $this->assertNotEmpty($methods);
     $productMux = $con->expand();
     // there is a sorting bug (fixed), this tests it.
     $this->assertNotEmpty($productMux);
     $root = new Mux();
     $root->mount('/product', $con->expand());
     $_SERVER['REQUEST_METHOD'] = 'GET';
     $this->assertNotNull($root->dispatch('/product/10'));
     $_SERVER['REQUEST_METHOD'] = 'DELETE';
     $this->assertNotNull($root->dispatch('/product/10'));
     $_SERVER['REQUEST_METHOD'] = 'POST';
     $this->assertNotNull($root->dispatch('/product'));
     // create
     $_SERVER['REQUEST_METHOD'] = 'POST';
     $this->assertNotNull($root->dispatch('/product/10'));
     // update
 }
require 'SimpleBench/MatrixPrinter/EzcGraph.php';
require 'SimpleBench/MatrixPrinter/Console.php';
require 'SimpleBench/SystemInfo/Darwin.php';
require 'SimpleBench.php';
// requirement from symfon
require 'symfony/vendor/autoload.php';
require 'pux/PatternCompiler.php';
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
use Pux\Mux;
$bench = new SimpleBench();
$bench->setN(5000);
$mux = new Mux();
$mux->add('/product/:id', ['ProductController', 'index']);
$bench->iterate('pux extension (dispatch)', function () use($mux) {
    $route = $mux->dispatch('/product/23');
});
$routes = new RouteCollection();
$routes->add('product', new Route('/product/{id}', array('controller' => 'foo', 'action' => 'bar')));
$bench->iterate('symfony/routing (dispatch)', function () use($routes) {
    $context = new RequestContext();
    // this is optional and can be done without a Request instance
    $context->fromRequest(Request::createFromGlobals());
    $matcher = new UrlMatcher($routes, $context);
    $route = $matcher->match('/product/23');
});
$result = $bench->compare();
echo $result->output('console');
Beispiel #21
0
<?php

// require '../vendor/autoload.php';
use Pux\Mux;
$mux = new Mux();
$mux->add('/hello', ['HelloController', 'helloAction']);
return $mux;
Beispiel #22
0
<?php

require 'SimpleBench/Task.php';
require 'SimpleBench/Utils.php';
require 'SimpleBench/ComparisonMatrix.php';
require 'SimpleBench/SystemInfo/Darwin.php';
require 'SimpleBench/MatrixWriter/Writer.php';
require 'SimpleBench/MatrixWriter/JsonWriter.php';
require 'SimpleBench/MatrixPrinter/EzcGraph.php';
require 'SimpleBench/MatrixPrinter/Console.php';
require 'SimpleBench.php';
// requirement from symfon
require '../src/Pux/PatternCompiler.php';
use Pux\Mux;
$mux = Pux\Mux::__set_state(array('id' => NULL, 'routes' => array(0 => array(0 => false, 1 => '/hello', 2 => array(0 => 'HelloController', 1 => 'helloAction'), 3 => array())), 'routesById' => array(), 'staticRoutes' => array(), 'submux' => array(), 'expand' => true));
/* version */
$bench = new SimpleBench();
$bench->setN(10000);
$bench->iterate('match', function () use($mux) {
    $route = $mux->match('/hello');
});
$bench->iterate('dispatch', function () use($mux) {
    $route = $mux->dispatch('/hello');
});
$bench->iterate('__set_state', function () {
    $mux = Pux\Mux::__set_state(array('id' => NULL, 'routes' => array(0 => array(0 => false, 1 => '/hello', 2 => array(0 => 'HelloController', 1 => 'helloAction'), 3 => array())), 'routesById' => array(), 'staticRoutes' => array(), 'submux' => array(), 'expand' => true));
    /* version */
});
$result = $bench->compare();
echo $result->output('console');
Beispiel #23
0
 public function testNullStaticRoutes()
 {
     $mux = Mux::__set_state(array('id' => NULL, 'routes' => array(0 => array(0 => false, 1 => '/hello', 2 => array(0 => 'HelloController2', 1 => 'helloAction'), 3 => array())), 'submux' => array(), 'staticRoutes' => array(), 'routesById' => array(), 'expand' => true));
     ok($mux);
 }
Beispiel #24
0
    {
        $path = "office/{$name}";
        if (file_exists($path)) {
            $handle = fopen($path, "r");
            $size = filesize($path);
            $contents = fread($handle, $size);
            $SHA256 = base64_encode(hash('sha256', $contents, true));
            $json = array('BaseFileName' => $name, 'OwnerId' => 'admin', 'Size' => $size, 'SHA256' => $SHA256, 'Version' => '222888822');
            echo json_encode($json);
        } else {
            echo json_encode(array());
        }
    }
    public function getFileAction($name)
    {
        $path = "office/{$name}";
        if (file_exists($path)) {
            $handle = fopen($path, "r");
            $contents = fread($handle, filesize($path));
            header("Content-type: application/octet-stream");
            echo $contents;
        }
    }
}
$mux = new Mux();
$mux->get('/files/:name', ['FilesController', 'getFileInfoAction']);
$mux->get('/files/:name/contents', ['FilesController', 'getFileAction']);
$path = $_SERVER['PATH_INFO'];
$args = explode("&", $path);
$route = $mux->dispatch($args[0]);
Executor::execute($route);
Beispiel #25
0
 /**
  * @depends testControllerConstructor
  */
 public function testMountNoExpand($controller)
 {
     $mainMux = new Mux();
     $mainMux->expand = false;
     $mainMux->mount('/product', $controller);
     $mainMux->any('/', array('ProductController', 'indexAction'));
     ok($mainMux->getRoutes());
     count_ok(2, $mainMux->getRoutes(), 'route count should be 2');
     ok($r = $mainMux->dispatch('/product'), 'matched /product');
     // match indexAction
     $this->assertSame(array('CRUDProductController', 'indexAction'), $r[2]);
     ok($r = $mainMux->dispatch('/product/add'));
     $this->assertSame(array('CRUDProductController', 'addAction'), $r[2]);
     ok($r = $mainMux->dispatch('/product/del'));
     $this->assertSame(array('CRUDProductController', 'delAction'), $r[2]);
     ok(null == $mainMux->dispatch('/foo'));
     ok(null == $mainMux->dispatch('/bar'));
 }
 /**
  * Expand controller actions to Mux object.
  *
  * @return Mux
  */
 public function expand(array $options = array(), $dynamic = false)
 {
     $this->mux = $mux = new Mux();
     $routes = $this->getActionRoutes();
     foreach ($routes as $route) {
         if ($dynamic) {
             $mux->add($route[0], array($this, $route[1]), array_merge($options, $route[2]));
         } else {
             $mux->add($route[0], array(get_class($this), $route[1]), array_merge($options, $route[2]));
         }
     }
     $mux->sort();
     return $mux;
 }
Beispiel #27
0
 public function testRouteWithId()
 {
     $mux = new \Pux\Mux();
     $mux->add('/hello/:name', ['HelloController2', 'indexAction'], ['id' => 'hello-name']);
     $mux->add('/foo', ['HelloController2', 'indexAction'], ['id' => 'foo']);
     ok($mux->routesById);
     ok($mux->routesById['foo']);
     $r = $mux->getRoute('foo');
     ok($r, 'should return foo route');
     ok($mux->getRoute('hello-name'), 'should return hello-name route');
 }
Beispiel #28
0
 * Created by PhpStorm.
 * User: Aztyu
 * Date: 26/12/2015
 * Time: 15:24
 */
require_once 'vendor/autoload.php';
require_once 'controller/api/ApiUserController.php';
require_once 'controller/api/ApiFuelController.php';
require_once 'controller/api/SiteController.php';
require_once 'entities/Coordinates.php';
use Pux\Mux;
use Pux\Executor;
$path = explode("?", $_SERVER['REQUEST_URI'])[0];
$api_mux = new Mux();
$api_user_mux = new Mux();
$api_fuel_mux = new Mux();
$main_mux = new Mux();
$api_user_mux->get('/check', ['ApiUserController', 'checkAction']);
$api_user_mux->get('/connect', ['ApiUserController', 'connectAction']);
$api_user_mux->get('/create', ['ApiUserController', 'createAction']);
$api_mux->mount('/user', $api_user_mux);
$api_fuel_mux->get('/find', ['ApiFuelController', 'findAction']);
$api_fuel_mux->post('/insert', ['ApiFuelController', 'insertAction']);
$api_mux->mount('/fuel', $api_fuel_mux);
$main_mux->mount('/api', $api_mux);
$main_mux->get('/', ['SiteController', 'indexAction']);
$route = $main_mux->dispatch($path);
if ($route == null) {
    $route = $main_mux->dispatch('/');
}
echo Executor::execute($route);
Beispiel #29
0
 /**
  * Expand routes to RouteSet
  */
 public function expand(array $options = array(), $dynamic = false)
 {
     $this->mux = $mux = new Mux();
     // TODO:
     // here we construct the object, so that
     // we will register the CRUD actions.
     // we should move the ModelName extraction to static,
     // so that we won't waste too much resource on creating objects.
     $class = get_class($this);
     $mux->add('', [$class, 'indexAction'], $options);
     $mux->add('/summary', [$class, 'summaryAction'], $options);
     $mux->add('/search', [$class, 'searchAction'], $options);
     $mux->add('/export/csv', [$class, 'exportCsvAction'], $options);
     $mux->add('/export/excel', [$class, 'exportExcelAction'], $options);
     $mux->add('/crud/index', [$class, 'indexRegionAction'], $options);
     $mux->add('/crud/create', [$class, 'createRegionAction'], $options);
     $mux->add('/crud/edit', [$class, 'editRegionAction'], $options);
     $mux->add('/crud/view', [$class, 'viewRegionAction'], $options);
     $mux->add('/crud/item', [$class, 'itemRegionAction'], $options);
     $mux->add('/crud/list', [$class, 'listRegionAction'], $options);
     $mux->add('/crud/list_inner', [$class, 'listInnerRegionAction'], $options);
     $mux->add('/crud/modal', [$class, 'modalEditRegionAction'], $options);
     $mux->add('/crud/dialog', [$class, 'dialogEditRegionAction'], $options);
     $mux->add('/import/upload', [$class, 'importUploadRegionAction'], $options);
     $mux->add('/import/column-map', [$class, 'importColumnMapRegionAction'], $options);
     $mux->add('/import/sample', [$class, 'importSampleDownloadAction'], $options);
     $mux->add('/view', [$class, 'viewAction'], $options);
     $mux->add('/edit', [$class, 'editAction'], $options);
     $mux->add('/create', [$class, 'createAction'], $options);
     if ($this->primaryFields) {
         $mux->add('/crud/quick_create', [$class, 'quickCreateAction'], $options);
     }
     return $mux;
 }
Beispiel #30
0
 public function testSubmuxMergeOptions()
 {
     $mux = new Mux();
     $this->assertEquals(0, $mux->length());
     $submux = new Mux();
     $submux->any('/foo', array('FooController', 'indexAction'), array('default' => array('suffix' => 'csv')));
     $submux->any('/hello/:name', array('HelloController', 'indexAction'), array('default' => array('suffix' => 'json', 'prefix' => 'v1')));
     $this->assertEquals(2, $submux->length());
     ok($routes = $submux->getRoutes());
     $this->assertEquals(2, $submux->length());
     $this->assertEquals(0, $mux->length());
     // XXX: we don't expand routes now, so we won't have the default
     //      options for the subroutes...
     /*
     $mux->mount('/sub', $submux, array(
         'default' => array('suffix' => 'xml', 'prefix' => 'v1'),
         'require' => array('suffix' => '[a-z]{3,4}')
     ));
     */
     $mux->mount('/sub', $submux);
     $this->assertEquals(1, $mux->length());
     $r = $mux->dispatch('/sub/hello/John');
     $this->assertNotNull($r);
     $this->assertEquals('json', $r[3]['default']['suffix']);
     $this->assertEquals('v1', $r[3]['default']['prefix']);
     $this->assertPcreRoute($r, '/hello/:name');
     $r = $mux->dispatch('/sub/foo');
     $this->assertNotNull($r);
     // ok($r[3]['default']['suffix'] == 'csv');
     // ok($r[3]['require']['suffix'] == '[a-z]{3,4}');
     $this->assertNonPcreRoute($r, '/foo');
 }