├── tests
├── views
│ ├── admin
│ │ └── login.phtml
│ ├── general
│ │ ├── d.phtml
│ │ ├── c.phtml
│ │ ├── b.phtml
│ │ ├── disable-layout.phtml
│ │ ├── pull-driven.phtml
│ │ ├── direct.phtml
│ │ ├── partial-eg.phtml
│ │ └── title-helper.phtml
│ ├── only-base.phtml
│ ├── partial.phtml
│ ├── pull
│ │ ├── driven.phtml
│ │ ├── missing-pull-action.phtml
│ │ ├── missing-pull.phtml
│ │ ├── buffer-out.phtml
│ │ └── driven-data.phtml
│ ├── view-test.phtml
│ ├── title.phtml
│ ├── view-mix-test.phtml
│ └── base.phtml
├── views-rewrite
│ ├── base.phtml
│ ├── general
│ │ ├── d.phtml
│ │ ├── c.phtml
│ │ └── partial-eg.phtml
│ └── title.phtml
├── layouts
│ ├── alternate.phtml
│ ├── layout.phtml
│ └── title-helper.phtml
├── classes
│ ├── pr
│ │ └── Clazz.php
│ └── ns
│ │ └── Clazz.php
├── bootstrap.php
├── controllers
│ ├── AdminController.php
│ ├── AloneController.php
│ ├── ErrorController.php
│ ├── InitController.php
│ ├── ThenController.php
│ └── GeneralController.php
├── RequestTest.php
├── resources
│ └── default.request.xml
├── BootstrapTest.php
├── LoaderTest.php
├── LayoutTest.php
├── RouteTest.php
├── DispatcherTest.php
├── EventManagerTest.php
├── ControllerTest.php
├── RouterTest.php
├── ViewTest.php
└── ApplicationTest.php
├── examples
├── twig-integration
│ ├── views
│ │ ├── cache
│ │ │ └── .gitentry
│ │ ├── error
│ │ │ └── error.twig
│ │ └── index
│ │ │ └── index.twig
│ ├── .gitignore
│ ├── composer.json
│ ├── public
│ │ ├── .htaccess
│ │ └── index.php
│ ├── controllers
│ │ └── IndexController.php
│ ├── README.md
│ ├── layouts
│ │ └── base.twig
│ └── composer.lock
└── base
│ ├── views
│ ├── error
│ │ └── error.phtml
│ ├── index
│ │ ├── kindle.phtml
│ │ ├── raw-post.phtml
│ │ └── index.phtml
│ └── pull
│ │ └── result.phtml
│ ├── layouts
│ └── layout.phtml
│ ├── controllers
│ ├── ErrorController.php
│ └── IndexController.php
│ └── public
│ ├── .htaccess
│ └── index.php
├── pack
├── composer.json
├── composer.lock
├── README.md
└── compile
├── .travis.yml
├── .gitignore
├── docs
├── README.md
├── index.rst
├── example.rst
├── autoloader.rst
├── events.rst
├── pull-driven.rst
├── getting-started.rst
├── controllers.rst
├── make.bat
├── Makefile
├── views.rst
└── conf.py
├── exts
├── README.md
├── HostnameRouter.php
├── StaticRouter.php
└── TwigView.php
├── src
├── Layout.php
├── Bootstrap.php
├── EventManager.php
├── Loader.php
├── Request.php
├── Route.php
├── Router.php
├── Controller.php
├── View.php
├── Dispatcher.php
└── Application.php
├── composer.json
├── LICENSE
├── phpunit.xml
└── README.md
/tests/views/admin/login.phtml:
--------------------------------------------------------------------------------
1 | The login
--------------------------------------------------------------------------------
/tests/views/general/d.phtml:
--------------------------------------------------------------------------------
1 | This is D
--------------------------------------------------------------------------------
/tests/views/general/c.phtml:
--------------------------------------------------------------------------------
1 | Original C view
--------------------------------------------------------------------------------
/examples/twig-integration/views/cache/.gitentry:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/views/only-base.phtml:
--------------------------------------------------------------------------------
1 |
Only base
--------------------------------------------------------------------------------
/examples/base/views/error/error.phtml:
--------------------------------------------------------------------------------
1 | Error page...
--------------------------------------------------------------------------------
/examples/twig-integration/views/error/error.twig:
--------------------------------------------------------------------------------
1 | Hello
--------------------------------------------------------------------------------
/tests/views-rewrite/base.phtml:
--------------------------------------------------------------------------------
1 | REWRITED!
--------------------------------------------------------------------------------
/tests/views-rewrite/general/d.phtml:
--------------------------------------------------------------------------------
1 | This is D but rewrited
--------------------------------------------------------------------------------
/tests/views/general/b.phtml:
--------------------------------------------------------------------------------
1 | This is b?>
--------------------------------------------------------------------------------
/tests/views/partial.phtml:
--------------------------------------------------------------------------------
1 | ciao; ?>
--------------------------------------------------------------------------------
/tests/views/pull/driven.phtml:
--------------------------------------------------------------------------------
1 | Complete pull driven
--------------------------------------------------------------------------------
/tests/views/general/disable-layout.phtml:
--------------------------------------------------------------------------------
1 | Only this view...
2 |
--------------------------------------------------------------------------------
/tests/views/view-test.phtml:
--------------------------------------------------------------------------------
1 | value?>
--------------------------------------------------------------------------------
/examples/twig-integration/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor
2 | /composer.phar
3 |
--------------------------------------------------------------------------------
/tests/layouts/alternate.phtml:
--------------------------------------------------------------------------------
1 | content?>
--------------------------------------------------------------------------------
/tests/layouts/layout.phtml:
--------------------------------------------------------------------------------
1 | content?>
--------------------------------------------------------------------------------
/tests/views-rewrite/title.phtml:
--------------------------------------------------------------------------------
1 | ciao?>
--------------------------------------------------------------------------------
/tests/views/general/pull-driven.phtml:
--------------------------------------------------------------------------------
1 | Pull-driven experience
--------------------------------------------------------------------------------
/tests/views/title.phtml:
--------------------------------------------------------------------------------
1 | ciao?>
--------------------------------------------------------------------------------
/examples/base/views/index/kindle.phtml:
--------------------------------------------------------------------------------
1 | cose;?>
--------------------------------------------------------------------------------
/tests/classes/pr/Clazz.php:
--------------------------------------------------------------------------------
1 | title()?>
--------------------------------------------------------------------------------
/tests/views-rewrite/general/c.phtml:
--------------------------------------------------------------------------------
1 | This is c?> but rewrited
--------------------------------------------------------------------------------
/tests/views/general/direct.phtml:
--------------------------------------------------------------------------------
1 | pull("/general/pull");?>
--------------------------------------------------------------------------------
/tests/views/pull/missing-pull-action.phtml:
--------------------------------------------------------------------------------
1 | pull("/missing/action");?>
--------------------------------------------------------------------------------
/tests/views/pull/missing-pull.phtml:
--------------------------------------------------------------------------------
1 | pull("/general/missing-action");?>
--------------------------------------------------------------------------------
/pack/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "require": {
3 | "symfony/finder": "*"
4 | }
5 | }
--------------------------------------------------------------------------------
/tests/classes/ns/Clazz.php:
--------------------------------------------------------------------------------
1 | pull("/general/out");?>
--------------------------------------------------------------------------------
/examples/base/views/index/raw-post.phtml:
--------------------------------------------------------------------------------
1 | escape($this->rawBody);?>
--------------------------------------------------------------------------------
/tests/views/pull/driven-data.phtml:
--------------------------------------------------------------------------------
1 | pull("/general/pull-data")->title;?>
--------------------------------------------------------------------------------
/tests/views/view-mix-test.phtml:
--------------------------------------------------------------------------------
1 | value?>
value2?>
--------------------------------------------------------------------------------
/tests/views/base.phtml:
--------------------------------------------------------------------------------
1 | partial("partial.phtml", array("ciao" => 'mondo'));?>
--------------------------------------------------------------------------------
/tests/views/general/partial-eg.phtml:
--------------------------------------------------------------------------------
1 | partial("title.phtml", array("ciao" => 'ciao'));
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 | php:
3 | - 5.3.3
4 | - 5.3
5 | - 5.4
6 | before_script:
7 | script: phpunit
--------------------------------------------------------------------------------
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 | title("the title helper");
3 | $this->title("second");
--------------------------------------------------------------------------------
/tests/views-rewrite/general/partial-eg.phtml:
--------------------------------------------------------------------------------
1 | partial("title.phtml", array("ciao" => 'ciao'));
--------------------------------------------------------------------------------
/examples/base/views/index/index.phtml:
--------------------------------------------------------------------------------
1 | hello?>
2 | escape("
Escape this output
");?>
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .project
2 | .buildpath
3 | .settings
4 | .DS_Store
5 | res
6 | *.phar
7 | pack/vendor
8 | *.swp
9 | *.swo
10 | docs/_build
11 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Documentation
2 |
3 | You can read the docs directly on: [ReadTheDocs](http://simple-mvc.readthedocs.org/en/latest/index.html)
4 |
--------------------------------------------------------------------------------
/examples/base/views/pull/result.phtml:
--------------------------------------------------------------------------------
1 | Pull Driven Requests
2 |
3 | pull("/index/user/id/1")->name; ?>
4 |
--------------------------------------------------------------------------------
/examples/twig-integration/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "require": {
3 | "twig/twig": "1.*",
4 | "wdalmut/simple-mvc": "0.1.*"
5 | }
6 | }
7 |
8 |
9 |
--------------------------------------------------------------------------------
/tests/controllers/AdminController.php:
--------------------------------------------------------------------------------
1 | ";
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/examples/base/layouts/layout.phtml:
--------------------------------------------------------------------------------
1 |
2 |
3 | title() ?>
4 |
5 |
6 | content ?>
7 |
8 |
9 |
--------------------------------------------------------------------------------
/tests/controllers/AloneController.php:
--------------------------------------------------------------------------------
1 | error action <--";
7 | $this->setNoRender();
8 | }
9 | }
--------------------------------------------------------------------------------
/examples/base/controllers/ErrorController.php:
--------------------------------------------------------------------------------
1 | ";
7 | var_dump($this->getParams());
8 |
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/examples/base/public/.htaccess:
--------------------------------------------------------------------------------
1 | RewriteEngine On
2 | RewriteCond %{REQUEST_FILENAME} -s [OR]
3 | RewriteCond %{REQUEST_FILENAME} -l [OR]
4 | RewriteCond %{REQUEST_FILENAME} -d
5 | RewriteRule ^.*$ - [NC,L]
6 | RewriteRule ^.*$ index.php [NC,L]
7 |
--------------------------------------------------------------------------------
/examples/twig-integration/public/.htaccess:
--------------------------------------------------------------------------------
1 | RewriteEngine On
2 | RewriteCond %{REQUEST_FILENAME} -s [OR]
3 | RewriteCond %{REQUEST_FILENAME} -l [OR]
4 | RewriteCond %{REQUEST_FILENAME} -d
5 | RewriteRule ^.*$ - [NC,L]
6 | RewriteRule ^.*$ index.php [NC,L]
--------------------------------------------------------------------------------
/exts/README.md:
--------------------------------------------------------------------------------
1 | # simple-mvc Extensions
2 |
3 | Just useful extensions
4 |
5 | * HostnameRouter
6 | * A simple hostname router
7 | * StaticRouter
8 | * A simple static router
9 | * SimpleTwigView
10 | * A simple Twig integration
11 |
12 |
13 |
--------------------------------------------------------------------------------
/tests/controllers/InitController.php:
--------------------------------------------------------------------------------
1 | ";
7 | }
8 |
9 | public function indexAction()
10 | {
11 |
12 | }
13 | }
--------------------------------------------------------------------------------
/tests/RequestTest.php:
--------------------------------------------------------------------------------
1 | markTestSkipped("Need Selenium or PhantomJS...");
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/tests/resources/default.request.xml:
--------------------------------------------------------------------------------
1 |
2 | /
3 | t.test.local
4 |
5 | value
6 |
7 |
8 | value
9 |
10 |
11 | value
12 |
13 |
14 |
--------------------------------------------------------------------------------
/tests/controllers/ThenController.php:
--------------------------------------------------------------------------------
1 | ";
7 | $this->then("/then/second");
8 | }
9 |
10 | public function secondAction()
11 | {
12 | echo "<-second";
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/examples/twig-integration/controllers/IndexController.php:
--------------------------------------------------------------------------------
1 | view->hello = "hello...";
7 |
8 | $this->view->texts = new stdClass();
9 | $this->view->texts->para = "paragraph";
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Layout.php:
--------------------------------------------------------------------------------
1 | _scriptName = $name;
14 | }
15 |
16 | public function getScriptName()
17 | {
18 | return $this->_scriptName;
19 | }
20 | }
--------------------------------------------------------------------------------
/examples/twig-integration/views/index/index.twig:
--------------------------------------------------------------------------------
1 | {% extends "base.twig" %}
2 |
3 | {% block title %}Index{% endblock %}
4 | {% block head %}
5 | {{ parent() }}
6 |
9 | {% endblock %}
10 | {% block content %}
11 | Index
12 |
13 | Welcome to Twig example.
14 |
15 | {% endblock %}
16 |
--------------------------------------------------------------------------------
/examples/twig-integration/README.md:
--------------------------------------------------------------------------------
1 | # Twig Integration
2 |
3 | First of all using composer for download all dependencies
4 |
5 | ```shell
6 | $ curl -s http://getcomposer.org/installer | php
7 | $ php composer.phar update
8 | ```
9 |
10 | A `vendor` folder will be created with `simple-mvc` and `twig` template engine.
11 |
12 | Now you are ready to see the twig integration. The adapter `SimpleTwigView`
13 | force the view engine to use `twig` instead the original one.
--------------------------------------------------------------------------------
/pack/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "hash": "47587ad7333cb232942456948370f378",
3 | "packages": [
4 | {
5 | "package": "symfony/finder",
6 | "version": "dev-master",
7 | "source-reference": "v2.1.0-BETA1",
8 | "commit-date": "1340118982"
9 | }
10 | ],
11 | "packages-dev": null,
12 | "aliases": [
13 |
14 | ],
15 | "minimum-stability": "dev",
16 | "stability-flags": [
17 |
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/exts/HostnameRouter.php:
--------------------------------------------------------------------------------
1 | _hostname = $hostname;
9 | }
10 |
11 | public function match(Request $request, $route = false)
12 | {
13 | if ($request->getHostname() == $this->_hostname) {
14 | return parent::match($request, new Route());
15 | }
16 |
17 | return false;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/examples/twig-integration/public/index.php:
--------------------------------------------------------------------------------
1 | setControllerPath(__DIR__ . '/../controllers');
8 |
9 | $app->bootstrap("view", function(){
10 | $view = new TwigView();
11 | $view->addViewPath(realpath(__DIR__ . '/../layouts'));
12 | $view->addViewPath(realpath(__DIR__ . '/../views'));
13 | $view->setViewExt(".twig");
14 | $view->initTwig(__DIR__ . '/../views/cache');
15 |
16 | return $view;
17 | });
18 |
19 | $app->run();
20 |
--------------------------------------------------------------------------------
/pack/README.md:
--------------------------------------------------------------------------------
1 | # PHAR Archive
2 |
3 | You can compile the `simple-mvc` phar archive. First of all
4 | download dependencies using Composer.
5 |
6 | ```shell
7 | $ curl -s http://getcomposer.org/installer | php
8 | $ php composer.phar install
9 | ```
10 |
11 | Now you can run the compile operation
12 |
13 | ```shell
14 | $ ./compile
15 | ```
16 |
17 | A `simple-mvc.phar` file will be created. Use it into your apps.
18 |
19 | ```php
20 |
2 |
3 |
4 | {% block head %}
5 |
6 | {% block title %}{% endblock %} - My Webpage
7 | {% endblock %}
8 |
9 |
10 | {% block content %}{% endblock %}
11 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | .. simple-mvc documentation master file, created by
2 | sphinx-quickstart on Sat Sep 8 12:59:34 2012.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | Welcome to simple-mvc's documentation!
7 | ======================================
8 |
9 | Contents:
10 |
11 | .. toctree::
12 | :maxdepth: 2
13 |
14 | getting-started
15 | autoloader
16 | controllers
17 | views
18 | events
19 | pull-driven
20 | example
21 |
22 |
23 | Indices and tables
24 | ==================
25 |
26 | * :ref:`genindex`
27 | * :ref:`modindex`
28 | * :ref:`search`
29 |
30 |
--------------------------------------------------------------------------------
/exts/StaticRouter.php:
--------------------------------------------------------------------------------
1 | _path = $path;
11 | $this->_action = $action;
12 | $this->_controller = $controller;
13 | }
14 |
15 | public function match(Request $request, $route = false)
16 | {
17 | if ($request->getUri() == $this->_path) {
18 | $static = new Route();
19 | $static->setControllerName($this->_controller);
20 | $static->setActionName($this->_action);
21 | $static->merge($route);
22 |
23 | return parent::match($request, $static);
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/exts/TwigView.php:
--------------------------------------------------------------------------------
1 | getViewPaths());
9 |
10 | if ($cache) {
11 | $this->_twig = new Twig_Environment($loader, array(
12 | 'cache' => $cache
13 | ));
14 | } else {
15 | $this->_twig = new Twig_Environment($loader);
16 | }
17 | }
18 |
19 | public function render($filename, $data = false)
20 | {
21 | $template = $this->_twig->loadTemplate($filename);
22 | $data = (is_array($data)) ? array_merge($this->_getData(), $data) : $this->_getData();
23 |
24 | return $template->render($data);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/examples/base/public/index.php:
--------------------------------------------------------------------------------
1 | setControllerPath(__DIR__ . '/../controllers');
9 |
10 | $app->bootstrap("view", function(){
11 | $view = new View();
12 | $view->setViewPath(__DIR__ . '/../views');
13 |
14 | $view->addHelper("title", function($part = false){
15 | static $parts = array();
16 | static $delimiter = ' :: ';
17 |
18 | return ($part === false) ? implode($delimiter, $parts) : $parts[] = $part;
19 | });
20 |
21 | return $view;
22 | });
23 |
24 | $app->bootstrap("layout", function(){
25 | $layout = new Layout();
26 | $layout->setViewPath(__DIR__ . '/../layouts');
27 |
28 | return $layout;
29 | });
30 |
31 | $app->run();
32 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wdalmut/simple-mvc",
3 | "description": "A simple and full stack Push & Pull MVC framework.",
4 | "type":"library",
5 | "keywords":["mvc","framework","microframework","micro"],
6 | "license":"MIT",
7 | "authors": [
8 | {
9 | "name":"Walter Dal Mut",
10 | "email":"walter.dalmut@gmail.com",
11 | "homepage":"http://walterdalmut.com",
12 | "role":"Developer"
13 | }
14 | ],
15 | "support": {
16 | "email":"walter.dalmut@gmail.com"
17 | },
18 | "require": {
19 | "php": ">=5.3.3"
20 | },
21 | "autoload": {
22 | "classmap": [
23 | "src/",
24 | "exts/"
25 | ]
26 | },
27 | "repositories": [
28 | {
29 | "type": "vcs",
30 | "url": "https://github.com/wdalmut/simple-mvc"
31 | }
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/src/Bootstrap.php:
--------------------------------------------------------------------------------
1 | _bootstrap[$name] = $hook;
13 | }
14 |
15 | public function getResource($name)
16 | {
17 | if (array_key_exists($name, $this->_bootstrap)) {
18 | $b = $this->_bootstrap[$name];
19 |
20 | if (is_callable($b)) {
21 | $this->_bootstrap[$name] = call_user_func($b);
22 | }
23 |
24 | return $this->_bootstrap[$name];
25 | } else {
26 | return false;
27 | }
28 | }
29 |
30 | public function testMissingBootstrapResource()
31 | {
32 | $this->assertSame(false, $this->object->getResource("missing"));
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/tests/controllers/GeneralController.php:
--------------------------------------------------------------------------------
1 | disableLayout();
11 | }
12 |
13 | public function pullAction()
14 | {
15 | return array('title' => 'ok');
16 | }
17 |
18 | public function directAction()
19 | {
20 |
21 | }
22 |
23 | public function pullDataAction()
24 | {
25 | $clazz = new stdClass();
26 |
27 | $clazz->title = 'Controller Data';
28 |
29 | return $clazz;
30 | }
31 |
32 | public function aAction()
33 | {
34 | $this->view->b = "B";
35 | $this->setRenderer("/general/b");
36 | }
37 |
38 | public function cAction()
39 | {
40 | $this->view->c = "C";
41 | }
42 |
43 | public function outAction()
44 | {
45 | echo "opssssss!";
46 | return "ret from out";
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (C) <2012>
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
4 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
5 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
6 | persons to whom the Software is furnished to do so, subject to the following conditions:
7 |
8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
9 | Software.
10 |
11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
12 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
13 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
14 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15 |
16 |
--------------------------------------------------------------------------------
/examples/base/controllers/IndexController.php:
--------------------------------------------------------------------------------
1 | view->title("The index");
7 |
8 | $this->view->hello = "hello";
9 |
10 | $this->then("/index/kindle");
11 | }
12 |
13 | public function kindleAction()
14 | {
15 | $this->view->title("The kindle");
16 |
17 | $this->view->cose = "ciao";
18 | }
19 |
20 | public function xmlAction()
21 | {
22 | $this->setNoRender();
23 | $this->disableLayout();
24 |
25 | $dom = new DOMDocument("1.0", "UTF-8");
26 | $element = $dom->createElement("example", "walter");
27 | $dom->appendChild($element);
28 |
29 | echo $dom->saveXML();
30 |
31 | $this->addHeader("Content-Type", "text/xml");
32 | }
33 |
34 | public function userAction()
35 | {
36 | $this->setNoRender();
37 |
38 | $params = $this->getParams();
39 |
40 | $obj = new stdClass();
41 | $obj->name = "User id: {$params["id"]}";
42 |
43 | return $obj;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/tests/BootstrapTest.php:
--------------------------------------------------------------------------------
1 | object = new Bootstrap();
13 | }
14 |
15 |
16 | /**
17 | * @covers Application::bootstrap
18 | * @covers Application::getBootstrap
19 | */
20 | public function testBootstrap()
21 | {
22 | $this->object->addResource("hello", function(){return "ciao";});
23 | $boot = $this->object->getResource("hello");
24 |
25 | $this->assertEquals($boot, "ciao");
26 | }
27 |
28 | /**
29 | * Resources must bootstrap onetime
30 | *
31 | * @covers Application::getBootstrap
32 | */
33 | public function testGetMultipleTimes()
34 | {
35 | $this->object->addResource("hello", function(){
36 | return new View();
37 | });
38 | $boot = $this->object->getResource("hello");
39 | $boot2 = $this->object->getResource("hello");
40 |
41 | $this->assertInstanceOf("View", $boot);
42 |
43 | $this->assertSame($boot, $boot2);
44 | }
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/pack/compile:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | files()
12 | ->name('*.php')
13 | ->in(__DIR__ . "/../src");
14 |
15 | $phar = new \Phar("simple-mvc.phar");
16 | $phar->setSignatureAlgorithm(\Phar::SHA1);
17 |
18 | $phar->startBuffering();
19 | $phar->setStub(getStub());
20 | foreach ($iterator as $file) {
21 | $phar->addFile($file->getRealpath(), $file->getBasename());
22 | echo "\033[34m{$file->getRealpath()}\033[37m -> \033[32m{$file->getBasename()}\033[37m\n";
23 | }
24 |
25 | $phar->stopBuffering();
26 | $phar->compressFiles(\Phar::GZ);
27 | unset($phar);
28 |
29 | function getStub()
30 | {
31 | return <<
37 | *
38 | * This source file is subject to the MIT license that is bundled
39 | * with this source code in the file LICENSE.
40 | */
41 |
42 | Phar::mapPhar('simple-mvc.phar');
43 |
44 | require_once 'phar://simple-mvc.phar/Loader.php';
45 | Loader::classmap();
46 | __HALT_COMPILER();
47 | EOF;
48 | }
--------------------------------------------------------------------------------
/tests/LoaderTest.php:
--------------------------------------------------------------------------------
1 | assertTrue(class_exists("Application", true));
15 | $this->assertTrue(class_exists("Controller", true));
16 | $this->assertTrue(class_exists("EventManager", true));
17 | $this->assertTrue(class_exists("Layout", true));
18 | $this->assertTrue(class_exists("Route", true));
19 | $this->assertTrue(class_exists("View", true));
20 | }
21 |
22 | public function testRegisterAutoloader()
23 | {
24 | $this->markTestSkipped();
25 | set_include_path(
26 | implode(
27 | PATH_SEPARATOR,
28 | array(
29 | __DIR__ . '/classes',
30 | get_include_path()
31 | )
32 | )
33 | );
34 | Loader::register();
35 |
36 | $this->assertTrue(class_exists("ns\Clazz", true));
37 | $this->assertTrue(class_exists("pr_Clazz", true));
38 | }
39 | }
40 |
41 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 | ./tests
22 |
23 |
24 |
25 |
28 |
29 |
30 |
31 | ./tests
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/EventManager.php:
--------------------------------------------------------------------------------
1 | _listeners[$event])) {
13 | $this->_listeners[$event] = array();
14 | }
15 |
16 | $this->_listeners[$event][] = $listener;
17 | }
18 |
19 | public function publish($event, array $arguments = array())
20 | {
21 | if ($this->_listeners($event)) {
22 | foreach ($this->_listeners($event) as $listener) {
23 | call_user_func_array($listener, $arguments);
24 | }
25 | }
26 | }
27 |
28 | protected function _listeners($event)
29 | {
30 | if (
31 | $this->_listeners && is_array($this->_listeners) &&
32 | array_key_exists($event, $this->_listeners)
33 | ) {
34 | return $this->_listeners[$event];
35 | } else {
36 | return array();
37 | }
38 | }
39 |
40 | public function clear($event = null)
41 | {
42 | if ($event !== null) {
43 | unset($this->_listeners[$event]);
44 | } else {
45 | $this->_listeners = array();
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/docs/example.rst:
--------------------------------------------------------------------------------
1 | Examples of usage
2 | =================
3 |
4 | A simple base app execution
5 |
6 | .. code-block:: php
7 | :linenos:
8 |
9 | run();
13 |
14 | Execute with bootstrap
15 | ----------------------
16 |
17 | .. code-block:: php
18 | :linenos:
19 |
20 | bootstrap("say-hello", function(){
24 | return array('example' => 'ciao');
25 | });
26 |
27 | $app->run();
28 |
29 | Into a controller
30 |
31 | .. code-block:: php
32 | :linenos:
33 |
34 | getResource('example');
40 |
41 | echo $element["example"];
42 | }
43 | }
44 |
45 | Controller Forward
46 | ------------------
47 |
48 | You can pass to another controller using `then()`
49 |
50 | .. code-block:: php
51 | :linenos:
52 |
53 | then("/index/forward");
60 | }
61 |
62 | public function forwardAction()
63 | {
64 | // append to index or use it directly
65 | }
66 | }
67 |
68 | See `example` folder for a complete working example.
69 |
70 |
--------------------------------------------------------------------------------
/src/Loader.php:
--------------------------------------------------------------------------------
1 | ClassName.php
58 | class Prefix_ClassName
59 | {
60 |
61 | }
62 |
63 | Namespace example:
64 |
65 | .. code-block:: php
66 | :linenos:
67 |
68 | ClassName.php
72 | class ClassName
73 | {
74 |
75 | }
76 |
77 |
78 |
--------------------------------------------------------------------------------
/docs/events.rst:
--------------------------------------------------------------------------------
1 | Events
2 | ======
3 |
4 | Events
5 |
6 | * `loop.startup`
7 | * `loop.shutdown`
8 | * `pre.dispatch`
9 | * `post.dispatch`
10 |
11 | Hooks
12 | -----
13 |
14 | The `loop.startup` and `loop.shutdown` is called once at the start and at the
15 | end of the simple-mvc workflow.
16 |
17 | The `pre.dispatch` and `post.dispatch` is called for every controlled pushed
18 | onto the stack (use the `then()` method).
19 |
20 | Hooks params
21 | ~~~~~~~~~~~~
22 |
23 | The `loop.startup` and the `loop.shutdown` have the `Application` object as
24 | first parameter.
25 |
26 | The `pre.dispatch` hook has the `Route` object as first parameter and the
27 | `Application` object as second.
28 |
29 | The `post.dispatch` hook has the `Controller` object as first paramter.
30 |
31 | * The router object is useful for modify the application flow.
32 |
33 | .. code-block:: php
34 | :linenos:
35 |
36 | getEventManager()->subscribe("pre.dispatch", function($router, $app) {
38 | // Use a real and better auth system
39 | if ($_SESSION["auth"] !== true) {
40 | $router->setControllerName("admin");
41 | $router->setActionName("login");
42 |
43 | $app->getBootstrap("layout")->setScriptName("admin.phtml");
44 | }
45 | });
46 |
47 | Create new events
48 | -----------------
49 |
50 | .. code-block:: php
51 | :linenos:
52 |
53 | getEventManager()->publish("my.hook", array($app));
56 |
57 | You can use the self-created hook using
58 |
59 | .. code-block:: php
60 | :linenos:
61 |
62 | getEventManager()->subscribe("my.hook", function($app) {/*The body*/});
64 |
65 |
66 |
--------------------------------------------------------------------------------
/tests/LayoutTest.php:
--------------------------------------------------------------------------------
1 | object = new Layout;
23 | $this->object->setViewPath(__DIR__ . '/layouts');
24 | $this->object->setScriptName("layout.phtml");
25 | }
26 |
27 | /**
28 | * Tears down the fixture, for example, closes a network connection.
29 | * This method is called after a test is executed.
30 | */
31 | protected function tearDown()
32 | {
33 | }
34 |
35 | public function testSetGetContent()
36 | {
37 | $this->object->content = 'ciao';
38 |
39 | $this->assertEquals("ciao", $this->object->content);
40 | }
41 |
42 | public function testBaseLayout()
43 | {
44 | $this->object->content = 'hello-world';
45 | $this->assertEquals(
46 | "hello-world",
47 | $this->object->render(
48 | $this->object->getScriptName()
49 | )
50 | );
51 | }
52 |
53 | public function testAlternateName()
54 | {
55 | $this->object->content = 'hello-world';
56 | $this->object->setScriptName("alternate.phtml");
57 | $this->assertEquals(
58 | "hello-world ",
59 | $this->object->render(
60 | $this->object->getScriptName()
61 | )
62 | );
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/tests/RouteTest.php:
--------------------------------------------------------------------------------
1 | object = new Route;
22 | }
23 |
24 | /**
25 | * Tears down the fixture, for example, closes a network connection.
26 | * This method is called after a test is executed.
27 | */
28 | protected function tearDown()
29 | {
30 | }
31 |
32 | /**
33 | * @expectedException RuntimeException
34 | */
35 | public function testStrangeExplode()
36 | {
37 | $this->markTestSkipped("Check if is router test");
38 | $this->object->explode(array('controller' => 'ciao', 'action' => 'hello'));
39 | }
40 |
41 | public function testAddParam()
42 | {
43 | $this->markTestSkipped("Check if is request test");
44 | $this->object->explode("/index/index");
45 | $this->object->addParams(array("hello" => "ciao"));
46 | $this->object->addParams(array("bella" => 'zi'));
47 |
48 | $p = $this->object->getParams();
49 | $this->assertEquals(2, count($p));
50 | $this->assertEquals("ciao", $p["hello"]);
51 | $this->assertEquals("zi", $p["bella"]);
52 | }
53 |
54 | public function testClearGetParams()
55 | {
56 | $this->markTestSkipped("Check if is router test");
57 | $uri = '/?hello=world';
58 | $this->object->explode($uri);
59 | $this->assertEquals("Index", $this->object->getControllerName());
60 | $this->assertEquals("index", $this->object->getActionName());
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # A simple MVC [VC] framework
2 |
3 | A simple ***push & pull MVC framework*** heavly inspired to different PHP microframeworks and
4 | PHP MVC framework like ZF1.
5 |
6 | ## Why?
7 |
8 | I want to try out the test-driven development [at least write some tests ;)].
9 |
10 | Just for my pleasure.
11 |
12 | ## Goals
13 |
14 | * PHPUnit
15 | * Very simple implementation (***only 8 classes*** + autoloader)
16 | * PHP 5.3+ implementation
17 |
18 | ## Features
19 |
20 | * 100% MVC implementation [66% no model support] ;)
21 | * Useful hooks (Fixed events)
22 | * Loop Startup
23 | * Pre Dispatch
24 | * Init Hook
25 | * Post Dispatch
26 | * Loop Shutdown
27 | * View Renderer Switch
28 | * View Helpers
29 | * Partial views
30 | * Two step view (Layout support)
31 | * Controllers stack
32 | * Headers handler
33 | * Event manager (Self designed hooks)
34 | * Router
35 | * Only controller/action names
36 | * Dash URLs support (/a-dash/the-name-of-content)
37 | * Pull Driven requests
38 | * View request data to a controller-action
39 | * Rewritable views
40 | * Different views mount points for rewrite views
41 |
42 | ## Install with Composer
43 |
44 | If you want you can use Composer for install simple-mvc.
45 | Create the `composer.json`
46 |
47 | ```json
48 | {
49 | "require": {
50 | "wdalmut/simple-mvc": "*"
51 | }
52 | }
53 | ```
54 |
55 | Now you can install the framework
56 |
57 | ```shell
58 | $ curl -s http://getcomposer.org/installer | php
59 | $ php composer.phar install
60 | ```
61 |
62 | You can use the Composer autoloader
63 |
64 | ```php
65 | object = new Dispatcher(new View());
15 | $this->object->setBootstrap(new Bootstrap());
16 | $this->object->setEventManager(new EventManager());
17 | $this->object->setControllerPath(__DIR__ . '/controllers');
18 | $router = new Router();
19 | $this->object->setRouter($router);
20 | }
21 |
22 | public function testDispatchARoute()
23 | {
24 | $request = new Request("alone/an");
25 | $this->object->setRequest($request);
26 | $route = $this->object->getRouter()->match($request);
27 | $content = $this->object->dispatch($route);
28 |
29 | $this->assertEquals("an-action", $content);
30 | }
31 |
32 | /**
33 | * @expectedException RuntimeException
34 | */
35 | public function testDispatchAnError()
36 | {
37 | $request = new Request("/not/exists-this-action");
38 | $this->object->setRequest($request);
39 | $route = $this->object->getRouter()->match($request);
40 | $this->object->dispatch($route);
41 | }
42 |
43 | public function testHeaderCodes()
44 | {
45 | $this->object->addHeader("content-type", "text/html", "202");
46 |
47 | $headers = $this->object->getHeaders();
48 | $this->assertCount(1, $headers);
49 | $this->assertSame(202, $headers[0]["code"]);
50 | }
51 |
52 | public function testSetGetHeaders()
53 | {
54 | $this->object->addHeader("content-type", "text/html");
55 |
56 | $headers = $this->object->getHeaders();
57 | $this->assertCount(1, $headers);
58 |
59 | $this->object->addHeader("content-disposition", "inline;");
60 | $headers = $this->object->getHeaders();
61 | $this->assertCount(2, $headers);
62 |
63 | $this->assertStringStartsWith("content-type", $headers[0]["string"]);
64 | $this->assertStringEndsWith("text/html", $headers[0]["string"]);
65 |
66 | $this->assertStringStartsWith("content-disposition", $headers[1]["string"]);
67 | $this->assertStringEndsWith("inline;", $headers[1]["string"]);
68 | }
69 | }
70 |
71 |
--------------------------------------------------------------------------------
/src/Request.php:
--------------------------------------------------------------------------------
1 | setUri($uri);
14 | }
15 |
16 | public function setHostname($hostname)
17 | {
18 | $this->_hostname = $hostname;
19 | }
20 |
21 | public function getHostname()
22 | {
23 | return $this->_hostname;
24 | }
25 |
26 | public function setUri($uri)
27 | {
28 | $this->_uri = $uri;
29 | }
30 |
31 | public function getUri()
32 | {
33 | return $this->_uri;
34 | }
35 |
36 | public function setGetParams($params)
37 | {
38 | $this->_getParams = $params;
39 | }
40 |
41 | public function getGetParams()
42 | {
43 | return $this->_getParams;
44 | }
45 |
46 | public function setPostParams($params)
47 | {
48 | $this->_postParams = $params;
49 | }
50 |
51 | public function getPostParams()
52 | {
53 | return $this->_postParams;
54 | }
55 |
56 | public function setParams(array $params)
57 | {
58 | $this->_params = $params;
59 | }
60 |
61 | public function addParam($key, $value)
62 | {
63 | $this->_params[$key] = $value;
64 | }
65 |
66 | public function getParams()
67 | {
68 | return $this->_params;
69 | }
70 |
71 | public function setRawBody($body)
72 | {
73 | $this->_rawBody = $body;
74 | }
75 |
76 | public function getRawBody()
77 | {
78 | return $this->_rawBody;
79 | }
80 |
81 | public static function newHttp()
82 | {
83 | $request = new Request();
84 | $request->setHostname($_SERVER["SERVER_NAME"]);
85 | $request->setUri($_SERVER["REQUEST_URI"]);
86 | $request->setGetParams($_GET);
87 | $request->setPostParams($_POST);
88 | $request->setRawBody(@file_get_contents('php://input'));
89 |
90 | return $request;
91 | }
92 |
93 | public static function fromXml($path)
94 | {
95 | $xml = simplexml_load_string($path);
96 | $this->setHostname((string)$xml->hostname);
97 | $this->setUri((string)$xml->uri);
98 |
99 | //TODO: end this section
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/Route.php:
--------------------------------------------------------------------------------
1 | _route["controller"] = ucfirst($this->_toCamelCase($name));
12 | $this->_route["controller-clear"] = $name;
13 | }
14 |
15 | public function getControllerName()
16 | {
17 | return $this->_route["controller"];
18 | }
19 |
20 | public function getControllerPath()
21 | {
22 | return $this->_route["controller-clear"];
23 | }
24 |
25 | public function getActionName()
26 | {
27 | return $this->_route["action"];
28 | }
29 |
30 | public function setActionName($name)
31 | {
32 | $this->_route["action"] = $this->_toCamelCase($name);
33 | $this->_route["action-clear"] = $name;
34 | }
35 |
36 | public function getActionPath()
37 | {
38 | return $this->_route["action-clear"];
39 | }
40 |
41 | /**
42 | * Merge parent route with this
43 | *
44 | * @param Route $route The parent route
45 | *
46 | * @return Route mixed route
47 | */
48 | public function merge($parent)
49 | {
50 | $merged = clone $this;
51 | if ($parent) {
52 | //Merge with parent route
53 | ($this->getActionName()) ?
54 | $merged->setActionName($this->getActionName()) :
55 | $merged->setActionName($parent->getActioName());
56 |
57 | ($this->getControllerName()) ?
58 | $merged->setControllerName($this->getControllerName()) :
59 | $merged->setControllerName($parent->getControllerName());
60 |
61 | $this->addParams(array_merge($parent->getParams(), $this->getParams()));
62 | }
63 |
64 | return $merged;
65 | }
66 |
67 | private function _toCamelCase($part)
68 | {
69 | $pos = 0;
70 | while(($pos = strpos($part, "-", $pos)) !== false && $pos < strlen($part)) {
71 | if ($pos+1 < strlen($part)) {
72 | $part[$pos+1] = strtoupper($part[$pos+1]);
73 | }
74 | ++$pos;
75 | }
76 | return str_replace("-", "", $part);
77 | }
78 |
79 | public function getRoute()
80 | {
81 | return $this->_route;
82 | }
83 |
84 | public function getParams()
85 | {
86 | return $this->_params;
87 | }
88 |
89 | public function addParams($params)
90 | {
91 | $this->_params = array_merge($this->_params, $params);
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/docs/pull-driven.rst:
--------------------------------------------------------------------------------
1 | Pull Driven Requests
2 | ====================
3 |
4 | Typically MVC frameworks are "push" based. In otherwords use mechanisms to "push" data to
5 | a view and not vice-versa. A "pull" framework instead request ("pull") data from a view.
6 |
7 | Pull strategy is useful for example during a `for` statement (not only for that [obviously]...). Look
8 | for an example:
9 |
10 | .. code-block:: php
11 | :linenos:
12 |
13 | users as $user) : ?>
14 | pull("/detail/user/id/{$user->id}");
17 | ?>
18 |
19 |
name;?> surname; ?>
20 |
21 |
22 |
23 |
24 | `simple-mvc` implementation
25 | ---------------------------
26 |
27 | `simple-mvc` has ***push*** and ***pull*** mechanisms. The *push* is quite simple and a typical
28 | operation. See an example
29 |
30 | .. code-block:: php
31 | :linenos:
32 |
33 | var
39 | $this->view->var = "hello";
40 | }
41 | }
42 |
43 | The view show the pushed variable
44 |
45 | .. code-block:: php
46 | :linenos:
47 |
48 | var; ?>
49 |
50 | The `pull` strategy is quite similar but use the return statement of a controller to retrive
51 | all the information. Consider in advance that `simple-mvc` doesn't require a valid controller
52 | for retrive a view, that view is mapped directly. See an example
53 |
54 | .. code-block:: php
55 | :linenos:
56 |
57 |
58 |
59 |
Missing controller and action
60 |
61 | pull("/ctr/act"); ?>
62 |
63 |
64 | title; ?>
65 |
66 |
67 | The view require a `pull` operation from a controller named `ctr` and action `act`. See it:
68 |
69 | .. code-block:: php
70 | :linenos:
71 |
72 | title = "The title";
80 |
81 | // The return type doesn't care...
82 | return $data;
83 | }
84 | }
85 |
86 | You can use a "pull" controller as a normal controller with the attached view, but remember
87 | that when you request for a "pull" operation the view is never considered and the framework
88 | remove it without consider the output, only the `return` statement will be used.
89 |
--------------------------------------------------------------------------------
/docs/getting-started.rst:
--------------------------------------------------------------------------------
1 | Getting Started
2 | ===============
3 |
4 | The goal is realize a web application in few steps.
5 |
6 | See scripts into `example` for a real example. The base is create
7 | a public folder where your web server dispatch the `index.php`.
8 |
9 | Create out from this folder the `controllers` path or whatever you
10 | want (eg. `ctrs`).::
11 |
12 | - controllers
13 | - IndexController.php
14 | - public
15 | - .htaccess
16 | - index.php
17 |
18 | In practice you are ready. See the `.htaccess`::
19 |
20 | RewriteEngine On
21 | RewriteCond %{REQUEST_FILENAME} -s [OR]
22 | RewriteCond %{REQUEST_FILENAME} -l [OR]
23 | RewriteCond %{REQUEST_FILENAME} -d
24 | RewriteRule ^.*$ - [NC,L]
25 | RewriteRule ^.*$ index.php [NC,L]
26 |
27 | The `index.php` is the main app entry point
28 |
29 | .. code-block:: php
30 | :linenos:
31 |
32 | setControllerPath(__DIR__ . '/../controllers');
40 | $app->run();
41 |
42 | The controller `IndexController.php` file should be like this
43 |
44 | .. code-block:: php
45 | :linenos:
46 |
47 | TheControllerName
73 | class TheControllerName extends Controller
74 | {
75 | public function theActionNameAction()
76 | {
77 | //the-action-name => theActionName
78 | }
79 | }
80 |
81 | Bootstrap resources
82 | -------------------
83 |
84 | You can bootstrap resources:
85 |
86 | .. code-block:: php
87 | :linenos:
88 |
89 | bootstrap('my-resource', function() {
92 | return new MyObject();
93 | });
94 |
95 | The bootstrap do not executes all hooks (lazy-loading of resources) but execute
96 | it ones only if your application needs it.
97 |
98 | .. code-block:: php
99 | :linenos:
100 |
101 | getResource("my-resource");
104 | $another = $this->getResource("my-resource");
105 |
106 | // IT IS TRUE!
107 | var_dump($resource === $another);
108 |
109 |
110 |
--------------------------------------------------------------------------------
/tests/EventManagerTest.php:
--------------------------------------------------------------------------------
1 | object = new EventManager;
21 | }
22 |
23 | /**
24 | * Tears down the fixture, for example, closes a network connection.
25 | * This method is called after a test is executed.
26 | */
27 | protected function tearDown()
28 | {
29 | }
30 |
31 | /**
32 | * @covers EventManager::subscribe
33 | */
34 | public function testSubscribe()
35 | {
36 | $this->object->subscribe('hook', function(){});
37 | }
38 |
39 | /**
40 | * @expectedException RuntimeException
41 | */
42 | public function testNotCallable()
43 | {
44 | $this->object->subscribe("hook", "hello-world");
45 | }
46 |
47 | /**
48 | * @covers EventManager::publish
49 | */
50 | public function testPublish()
51 | {
52 | $calls = 0;
53 | $this->object->subscribe("hook", function() use (&$calls) {
54 | ++$calls;
55 | });
56 |
57 | $this->object->publish('hook');
58 |
59 | $this->assertSame(1, $calls);
60 | }
61 |
62 | /**
63 | * @covers EventManager::publish
64 | */
65 | public function testCallEmptyListener()
66 | {
67 | $this->object->publish('hook');
68 | }
69 |
70 | /**
71 | * @covers EventManager::clear
72 | * @todo Implement testClear().
73 | */
74 | public function testClear()
75 | {
76 | $calls = 0;
77 | $this->object->subscribe("hook", function() use (&$calls) {
78 | ++$calls;
79 | });
80 |
81 | $this->object->clear();
82 |
83 | $this->object->publish("hook");
84 |
85 | $this->assertSame(0, $calls);
86 | }
87 |
88 | public function testClearSingle()
89 | {
90 | $calls = 0;
91 | $this->object->subscribe("hook", function() use (&$calls) {
92 | ++$calls;
93 | });
94 |
95 | $malls = 0;
96 | $this->object->subscribe("pook", function() use (&$malls) {
97 | ++$malls;
98 | });
99 |
100 | $this->object->clear("pook");
101 |
102 | $this->object->publish("hook");
103 | $this->object->publish("pook");
104 |
105 | $this->assertSame(1, $calls);
106 | $this->assertSame(0, $malls);
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/src/Router.php:
--------------------------------------------------------------------------------
1 | _childs[$name] = $router;
9 | }
10 |
11 | /**
12 | * Match a request on a route
13 | *
14 | * @param Request the actual request
15 | * @return Route
16 | */
17 | public function match(Request $request, $route = false)
18 | {
19 | if (count($this->_childs)) {
20 | $route = $this->_match($request, $route);
21 | }
22 |
23 | if (!$route) {
24 | $route = $this->_default($request)->merge($route);
25 | }
26 |
27 |
28 | if (!$route) {
29 | throw new RuntimeException("Missing route...");
30 | }
31 |
32 | return $route;
33 | }
34 |
35 | protected function _match($request, $route)
36 | {
37 | foreach ($this->_childs as $router) {
38 | $route = $router->match($request, $route);
39 | if ($route instanceOf Route) {
40 | return $route;
41 | }
42 | }
43 |
44 | return false;
45 | }
46 |
47 | private function _filter($parts)
48 | {
49 | $clean = array();
50 | foreach ($parts as $part) {
51 | $part = trim($part);
52 | if (!empty($part)) {
53 | $clean[] = $part;
54 | }
55 | }
56 |
57 | return $clean;
58 | }
59 |
60 |
61 | protected function _default(Request $request)
62 | {
63 | $route = new Route();
64 | $uri = $request->getUri();
65 |
66 | $uri = (strpos($uri, "?") !== false) ? substr($uri, 0, strpos($uri, "?")) : $uri;
67 | $parts = explode("/", $uri);
68 |
69 | $parts = $this->_filter($parts);
70 |
71 | switch (count($parts)) {
72 | case 0:
73 | $route->setControllerName("index");
74 | $route->setActionName("index");
75 | break;
76 | case 1:
77 | $route->setControllerName($parts[0]);
78 | $route->setActionName("index");
79 | array_shift($parts);
80 | break;
81 | default:
82 | $route->setControllerName($parts[0]);
83 | $route->setActionName($parts[1]);
84 | array_shift($parts);
85 | array_shift($parts);
86 | break;
87 | }
88 |
89 | (count($parts) % 2 !== 0) ? array_pop($parts) : false;
90 |
91 | if (count($parts)) {
92 | for ($i=0; $iaddParam($parts[$i], $parts[$i+1]);
94 | }
95 | }
96 |
97 | return $route;
98 | }
99 |
100 |
101 | public function assemble($params, $name = false)
102 | {
103 | if (!$name) {
104 | //Assemble this route
105 | } else {
106 | return $this->_childs[$name]->assemble($params);
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/Controller.php:
--------------------------------------------------------------------------------
1 | _request = $request;
16 | }
17 |
18 | public function getRequest()
19 | {
20 | return $this->_request;
21 | }
22 |
23 | public function setRouter(Router $router)
24 | {
25 | $this->_router = $router;
26 | }
27 |
28 | public function getRouter()
29 | {
30 | return $this->_router;
31 | }
32 |
33 | public function setView($view)
34 | {
35 | $this->view = $view;
36 | }
37 |
38 | public function getView()
39 | {
40 | return $this->view;
41 | }
42 |
43 | public function init(){}
44 |
45 | public function setParams($params)
46 | {
47 | $this->_params = $params;
48 | }
49 |
50 | public function getParams()
51 | {
52 | return $this->_params;
53 | }
54 |
55 | public function getParam($key)
56 | {
57 | return (array_key_exists($key, $this->_params)) ? $this->_params[$key] : false;
58 | }
59 |
60 | public function setRawBody($body)
61 | {
62 | $this->_rawBody = $body;
63 | }
64 |
65 | public function getRawBody()
66 | {
67 | return $this->_rawBody;
68 | }
69 |
70 | public function then($uri)
71 | {
72 | $request = clone $this->_request;
73 | $request->setUri($uri);
74 |
75 | $route = $this->_router->match($request);
76 | $this->_params["dispatcher"]->add($route);
77 | }
78 |
79 | public function clearHeaders()
80 | {
81 | $this->_params["dispatcher"]->clearHeaders();
82 | }
83 |
84 | public function addHeader($key, $value, $httpCode = 200, $replace = true)
85 | {
86 | $this->_params["dispatcher"]->addHeader($key, $value, $httpCode, $replace);
87 | return $this;
88 | }
89 |
90 | public function getHeaders()
91 | {
92 | return $this->_params["dispatcher"]->getHeaders();
93 | }
94 |
95 | /**
96 | * Get a resource from bootstrap
97 | *
98 | * @param The name of resource
99 | * @return mixed The resource
100 | */
101 | public function getResource($name)
102 | {
103 | return $this->_params["dispatcher"]
104 | ->getBootstrap()->getResource($name);
105 | }
106 |
107 | /**
108 | * Using the dispatcher
109 | */
110 | public function redirect($url, $header=301)
111 | {
112 | $this->disableLayout();
113 | $this->setNoRender();
114 |
115 | $this->_params["dispatcher"]->clearHeaders();
116 | $this->_params["dispatcher"]->addHeader("Location", $url, $header);
117 | }
118 |
119 | public function setRenderer($renderer)
120 | {
121 | $this->_viewScript = $renderer;
122 | }
123 |
124 | public function getViewPath()
125 | {
126 | return ($this->_viewScript) ? $this->_viewScript . ".phtml" : false;
127 | }
128 |
129 | public function disableLayout()
130 | {
131 | $this->_params["dispatcher"]->getBootstrap()
132 | ->addResource("layout", function(){
133 | return null;
134 | });
135 | }
136 |
137 | public function setNoRender()
138 | {
139 | $this->view = new View();
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/examples/twig-integration/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "hash": "bbc67bb2fca4287c2501e8ad26c05614",
3 | "packages": [
4 | {
5 | "name": "twig/twig",
6 | "version": "dev-master",
7 | "source": {
8 | "type": "git",
9 | "url": "git://github.com/fabpot/Twig.git",
10 | "reference": "b4d1d62b82e83c6fd3d5a6cd46a186de64275bb4"
11 | },
12 | "dist": {
13 | "type": "zip",
14 | "url": "https://github.com/fabpot/Twig/zipball/b4d1d62b82e83c6fd3d5a6cd46a186de64275bb4",
15 | "reference": "b4d1d62b82e83c6fd3d5a6cd46a186de64275bb4",
16 | "shasum": ""
17 | },
18 | "require": {
19 | "php": ">=5.2.4"
20 | },
21 | "time": "1347961095",
22 | "type": "library",
23 | "extra": {
24 | "branch-alias": {
25 | "dev-master": "1.10-dev"
26 | }
27 | },
28 | "installation-source": "source",
29 | "autoload": {
30 | "psr-0": {
31 | "Twig_": "lib/"
32 | }
33 | },
34 | "license": [
35 | "BSD-3"
36 | ],
37 | "authors": [
38 | {
39 | "name": "Fabien Potencier",
40 | "email": "fabien@symfony.com"
41 | },
42 | {
43 | "name": "Armin Ronacher",
44 | "email": "armin.ronacher@active-4.com"
45 | }
46 | ],
47 | "description": "Twig, the flexible, fast, and secure template language for PHP",
48 | "homepage": "http://twig.sensiolabs.org",
49 | "keywords": [
50 | "templating"
51 | ]
52 | },
53 | {
54 | "name": "wdalmut/simple-mvc",
55 | "version": "0.1.2",
56 | "source": {
57 | "type": "git",
58 | "url": "https://github.com/wdalmut/simple-mvc",
59 | "reference": "0.1.2"
60 | },
61 | "dist": {
62 | "type": "zip",
63 | "url": "https://github.com/wdalmut/simple-mvc/zipball/0.1.2",
64 | "reference": "0.1.2",
65 | "shasum": ""
66 | },
67 | "require": {
68 | "php": ">=5.3.3"
69 | },
70 | "time": "2012-09-23 00:00:00",
71 | "type": "library",
72 | "installation-source": "source",
73 | "autoload": {
74 | "classmap": [
75 | "src/",
76 | "exts/"
77 | ]
78 | },
79 | "license": [
80 | "MIT"
81 | ],
82 | "authors": [
83 | {
84 | "name": "Walter Dal Mut",
85 | "email": "walter.dalmut@gmail.com",
86 | "homepage": "http://walterdalmut.com",
87 | "role": "Developer"
88 | }
89 | ],
90 | "description": "A simple and full stack Push & Pull MVC framework.",
91 | "keywords": [
92 | "framework",
93 | "microframework",
94 | "mvc",
95 | "micro"
96 | ]
97 | }
98 | ],
99 | "packages-dev": null,
100 | "aliases": [
101 |
102 | ],
103 | "minimum-stability": "stable",
104 | "stability-flags": [
105 |
106 | ]
107 | }
108 |
--------------------------------------------------------------------------------
/docs/controllers.rst:
--------------------------------------------------------------------------------
1 | Controllers
2 | ===========
3 |
4 | The controller section
5 |
6 | Init hook
7 | ---------
8 |
9 | Before any action dispatch the framework executes the `init()` method.
10 |
11 | .. code-block:: php
12 | :linenos:
13 |
14 | view->hello = "hello";
52 |
53 |
54 | $this->then("/index/next");
55 | }
56 |
57 | public function nextAction()
58 | {
59 | $this->view->cose = "ciao";
60 | }
61 | }
62 |
63 | The result is the first view (`index.phtml`) concatenated to the
64 | second view (`next.phtml`).
65 |
66 | Redirects
67 | ---------
68 |
69 | You can handle redirects using the `redirect()` method
70 |
71 | .. code-block:: php
72 | :linenos:
73 |
74 | redirect("/contr/act", 302);
81 | }
82 | }
83 |
84 | Interact with layout and views
85 | ------------------------------
86 |
87 | You can disable the layout system at any time using the `disableLayout()`
88 | method.
89 |
90 | .. code-block:: php
91 | :linenos:
92 |
93 | disableLayout();
100 | }
101 | }
102 |
103 | You can disable the view attached to a controller using the `setNoRender()`
104 | method
105 |
106 | .. code-block:: php
107 | :linenos:
108 |
109 | setNoRender();
116 | }
117 | }
118 |
119 | Change the layout on the fly
120 | ----------------------------
121 |
122 | If you want to change your layout during an action or a plugin interaction
123 | you can use the resources manager
124 |
125 | .. code-block:: php
126 | :linenos:
127 |
128 | getResource("layout")->setScriptName("full-width.phtml");
134 | }
135 | }
136 |
137 | Obviously you must use the layout manager.
138 |
139 | Using headers
140 | -------------
141 |
142 | You can send different headers using `addHeader()` method
143 |
144 | .. code-block:: php
145 | :linenos:
146 |
147 | addHeader("Content-Type", "text/plain");
153 | }
154 | }
155 |
156 | Change View Renderer
157 | --------------------
158 |
159 | You can change the view renderer at runtime during an action execution.
160 |
161 | .. code-block:: php
162 | :linenos:
163 |
164 | setRenderer("/use/me");
170 | }
171 | }
172 |
173 | The framework will use the `use/me.phtml`
174 |
175 | The end.
176 |
--------------------------------------------------------------------------------
/src/View.php:
--------------------------------------------------------------------------------
1 | _charset;
16 |
17 | $this->addHelper("escape", function($text, $flags = ENT_COMPAT, $charset = null, $doubleEncode = true) use ($cs) {
18 | return htmlspecialchars($text, $flags, $charset ?: $cs, $doubleEncode);
19 | });
20 |
21 | $this->addHelper("partial", function($path, $data) use ($view) {
22 | $view = $view->cloneThis();
23 | return $view->render($path, $data);
24 | });
25 | }
26 |
27 | public function __set($key, $value)
28 | {
29 | $this->_data[$key] = $value;
30 | }
31 |
32 | public function __get($key)
33 | {
34 | return (isset($this->_data[$key])) ? $this->_data[$key] : false;
35 | }
36 |
37 | public function __call($method, $args)
38 | {
39 | if (array_key_exists($method, static::$_helpers)) {
40 | return call_user_func_array(static::$_helpers[$method], $args);
41 | } else {
42 | throw new RuntimeException("Helper view {$method} doesn't exists. Add it using addHelper method.");
43 | }
44 | }
45 |
46 | public function addViewPath($path)
47 | {
48 | if (!is_dir($path)) {
49 | throw new RuntimeException("View path {$path} must be a directory");
50 | }
51 | $this->_path[] = $path;
52 | }
53 |
54 | public function getViewPaths()
55 | {
56 | return $this->_path;
57 | }
58 |
59 | /**
60 | * @deprecated This method is deprecated use addViewPath()
61 | */
62 | public function setViewPath($path)
63 | {
64 | if (!is_dir($path)) {
65 | throw new RuntimeException("View path {$path} must be a directory");
66 | }
67 | $this->_path = array($path);
68 | }
69 |
70 | /**
71 | * @deprecated This method is deprecated in favor of getViewPaths()
72 | */
73 | public function getViewPath()
74 | {
75 | return (count($this->_path) > 0) ? $this->_path[0] : false;
76 | }
77 |
78 | public function render($filename, $data = false)
79 | {
80 | if($data) {
81 | if (!is_array($data)) {
82 | throw new RuntimeException("You must pass an array to data view.");
83 | }
84 | $this->_data = array_merge($this->_data, $data);
85 | }
86 |
87 | $filename = $this->_selectView($this->getViewPaths(), $filename);
88 |
89 | $rendered = "";
90 |
91 | ob_start();
92 | require($filename);
93 | $rendered = ob_get_contents();
94 | ob_end_clean();
95 |
96 | return $rendered;
97 | }
98 |
99 | protected function _selectView($paths, $filename)
100 | {
101 | $p = implode(PATH_SEPARATOR, $paths);
102 |
103 | for ($i=count($paths)-1; $i>=0; $i--) {
104 | $f = $paths[$i] . "/" . $filename ;
105 |
106 | if (file_exists($f)) {
107 | return $f;
108 | }
109 | }
110 |
111 | throw new RuntimeException("Unable to get view {$filename} in paths: {$p}");
112 | }
113 |
114 | public function cloneThis()
115 | {
116 | return clone($this);
117 | }
118 |
119 | public function addHelper($name, $helper)
120 | {
121 | static::$_helpers[$name] = $helper;
122 | }
123 |
124 | public function addHelpers(array $helpers)
125 | {
126 | static::$_helpers = array_merge(self::$_helpers, $helpers);
127 | }
128 |
129 | public function getHelpers()
130 | {
131 | return static::$_helpers;
132 | }
133 |
134 | protected function _getData()
135 | {
136 | return $this->_data;
137 | }
138 |
139 | public function setViewExt($ext)
140 | {
141 | $this->_ext = $ext;
142 | }
143 |
144 | public function getViewExt()
145 | {
146 | return $this->_ext;
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/tests/ControllerTest.php:
--------------------------------------------------------------------------------
1 | object = new Controller();
23 | $dispatcher = new Dispatcher(new View());
24 | $dispatcher->setBootstrap(new Bootstrap());
25 |
26 | $this->object->setParams(
27 | array(
28 | 'dispatcher' => $dispatcher
29 | )
30 | );
31 | }
32 |
33 | /**
34 | * Tears down the fixture, for example, closes a network connection.
35 | * This method is called after a test is executed.
36 | */
37 | protected function tearDown()
38 | {
39 | }
40 |
41 | /**
42 | * @covers Controller::setView
43 | * @covers Controller::getView
44 | */
45 | public function testSetGetView()
46 | {
47 | $view = new View();
48 |
49 | $this->object->setView($view);
50 |
51 | $v = $this->object->getView();
52 |
53 | $this->assertSame($view, $v);
54 | }
55 |
56 | /**
57 | * @covers Controller::setParams
58 | * @covers Controller::getParams
59 | */
60 | public function testSetGetParams()
61 | {
62 | $p = array("ciao");
63 | $this->object->setParams($p);
64 |
65 | $this->assertSame($p, $this->object->getParams());
66 | }
67 |
68 | public function testGetParam()
69 | {
70 | $p = array('hello' => "ciao");
71 | $this->object->setParams($p);
72 |
73 | $this->assertEquals("ciao", $this->object->getParam('hello'));
74 |
75 | $this->assertFalse($this->object->getParam("missing-key"));
76 | }
77 |
78 | public function testAddHeaders()
79 | {
80 | $this->object->addHeader("a", "b");
81 | $this->object->addHeader("c", "d");
82 |
83 | $this->assertSame(2, count($this->object->getHeaders()));
84 |
85 | $headers = $this->object->getHeaders();
86 |
87 | $first = $headers[0];
88 | $second = $headers[1];
89 | $this->assertEquals("a:b", $first["string"]);
90 | $this->assertEquals("c:d", $second["string"]);
91 | }
92 |
93 | public function testAddHeaderCode()
94 | {
95 | $this->object->addHeader("a", "b", 500);
96 | $headers = $this->object->getHeaders();
97 |
98 | $this->assertEquals(500, $headers[0]["code"]);
99 | }
100 |
101 | public function testClearHeaders()
102 | {
103 | $this->object->addHeader("a", "b");
104 | $this->object->addHeader("c", "d");
105 |
106 | $this->object->clearHeaders();
107 |
108 | $this->assertSame(0, count($this->object->getHeaders()));
109 | }
110 |
111 | public function testSetNoRender()
112 | {
113 | $v = new View();
114 | $v->setViewPath(__DIR__ . '/views');
115 | $this->object->setView($v);
116 |
117 | $this->object->value = "hello";
118 |
119 | $this->assertSame($v, $this->object->getView());
120 | $this->object->setNoRender();
121 | $this->assertNotSame($v, $this->object->getView());
122 | }
123 |
124 | public function testRedirectBase()
125 | {
126 | $this->object->addHeader("content-type", "text/html");
127 | $this->object->redirect("/admin/login");
128 | $headers = $this->object->getHeaders();
129 |
130 | $this->assertSame(1, count($headers));
131 | $redirectHeader = $headers[0];
132 |
133 | $this->assertSame(301, $redirectHeader["code"]);
134 | }
135 |
136 | public function testRedirectBase302()
137 | {
138 | $this->object->addHeader("content-type", "text/html");
139 | $this->object->redirect("/admin/login", 302);
140 | $headers = $this->object->getHeaders();
141 |
142 | $this->assertSame(1, count($headers));
143 | $redirectHeader = $headers[0];
144 |
145 | $this->assertSame(302, $redirectHeader["code"]);
146 | }
147 |
148 | public function testGetViewPath()
149 | {
150 | $ctr = new Controller();
151 | $ctr->setRenderer("a/b");
152 | $this->assertEquals("a/b.phtml", $ctr->getViewPath());
153 | }
154 |
155 | public function testEmptyGetViewPath()
156 | {
157 | $ctr = new Controller();
158 | $ctr->init();
159 | $this->assertFalse($ctr->getViewPath());
160 | }
161 |
162 | public function testSetGetRawBody()
163 | {
164 | $this->object->setRawBody("Hello ");
165 | $body = $this->object->getRawBody();
166 |
167 | $this->assertEquals("Hello ", $body);
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/src/Dispatcher.php:
--------------------------------------------------------------------------------
1 | _view = $view;
25 | $this->_actions = array();
26 | $this->_viewsQueue = array();
27 | }
28 |
29 | public function setRouter(Router $router)
30 | {
31 | $this->_router = $router;
32 | }
33 |
34 | public function getRouter()
35 | {
36 | return $this->_router;
37 | }
38 |
39 | public function setRequest(Request $request)
40 | {
41 | $this->_request = $request;
42 | }
43 |
44 | public function getRequest()
45 | {
46 | return $this->_request;
47 | }
48 |
49 | public function setControllerPath($path)
50 | {
51 | $this->_controllerPath = $path;
52 | }
53 |
54 | public function getControllerPath()
55 | {
56 | return $this->_controllerPath;
57 | }
58 |
59 | public function setEventManager(EventManager $em)
60 | {
61 | $this->_eventManager = $em;
62 | }
63 |
64 | public function getEventManager()
65 | {
66 | return $this->_eventManager;
67 | }
68 |
69 | public function setBootstrap($bootstrap)
70 | {
71 | $this->_bootstrap = $bootstrap;
72 | }
73 |
74 | public function getBootstrap()
75 | {
76 | return $this->_bootstrap;
77 | }
78 |
79 | public function add(Route $route)
80 | {
81 | $this->_actions[] = $route;
82 | }
83 |
84 | /**
85 | * dispatch an action
86 | */
87 | public function dispatch(Route $route)
88 | {
89 | do {
90 | $this->getEventManager()->publish("pre.dispatch", array($route, $this));
91 |
92 | $protoView = $this->_view->cloneThis();
93 | $controllerClassName = $route->getControllerName() . "Controller";
94 | $action = $route->getActionName() . "Action";
95 | $classPath = $this->getControllerPath() . DIRECTORY_SEPARATOR . $controllerClassName . ".php";
96 | $viewPath = $route->getControllerPath()
97 | . DIRECTORY_SEPARATOR
98 | . $route->getActionPath()
99 | . $protoView->getViewExt();
100 |
101 | if (!file_exists($classPath)) {
102 | // Use base controller
103 | $controllerClassName = 'Controller';
104 | } else {
105 | require_once $classPath;
106 | }
107 |
108 | $controller = new $controllerClassName($this);
109 | $controller->setRouter($this->getRouter());
110 | $controller->setRequest($this->getRequest());
111 | $controller->setParams(
112 | array_merge(
113 | array(
114 | "dispatcher" => $this,
115 | "bootstrap" => $this->getBootstrap()
116 | ),
117 | $route->getParams()));
118 | $controller->setRawBody(@file_get_contents('php://input'));
119 |
120 | $controller->setView($protoView->cloneThis());
121 | $controller->view->controllerPath = $this->getControllerPath();
122 |
123 | if (method_exists($controller, $action)) {
124 | ob_start();
125 | $controller->init();
126 | $controller->$action();
127 | array_push($this->_viewsQueue, ob_get_contents());
128 | ob_end_clean();
129 | } else {
130 | if (!file_exists($controller->view->getViewPath() . DIRECTORY_SEPARATOR . $viewPath)) {
131 | throw new RuntimeException("Page not found {$route->getControllerName()} -> {$route->getActionName()}", 404);
132 | }
133 | }
134 |
135 | if ($controller->view->getViewPath()) {
136 | array_push(
137 | $this->_viewsQueue,
138 | $controller->getView()->render(
139 | (($controller->getViewPath() !== false) ? $controller->getViewPath() : $viewPath))
140 | );
141 | }
142 |
143 | $this->getEventManager()->publish("post.dispatch", array($this));
144 | } while(($route = array_shift($this->_actions)));
145 |
146 | return implode("", $this->_viewsQueue);
147 | }
148 |
149 | public function sendHeaders()
150 | {
151 | $headers = $this->getHeaders();
152 | foreach ($headers as $header) {
153 | header($header["string"], $header["replace"], $header["code"]);
154 | }
155 | }
156 |
157 | public function clearHeaders()
158 | {
159 | $this->_headers = array();
160 | }
161 |
162 | public function addHeader($key, $value, $httpCode = 200, $replace = true)
163 | {
164 | $this->_headers[] = array('string' => "{$key}:{$value}", "replace" => $replace, "code" => (int)$httpCode);
165 | }
166 |
167 | public function getHeaders()
168 | {
169 | return $this->_headers;
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/src/Application.php:
--------------------------------------------------------------------------------
1 | _bootstrap = ($bootstrap) ? $bootstrap : new Bootstrap();
15 | $this->_eventManager = ($eventManager) ? $eventManager : new EventManager();
16 | }
17 |
18 | public function setControllerPath($path)
19 | {
20 | $this->_controllerPath = $path;
21 | }
22 |
23 | public function getControllerPath()
24 | {
25 | return $this->_controllerPath;
26 | }
27 |
28 | public function setEventManager(EventManager $manager)
29 | {
30 | $this->_eventManager = $manager;
31 | }
32 |
33 | public function getEventManager()
34 | {
35 | return $this->_eventManager;
36 | }
37 |
38 | public function bootstrap($name, $hook)
39 | {
40 | $this->_bootstrap->addResource($name, $hook);
41 | }
42 |
43 | public function getBootstrap()
44 | {
45 | return $this->_bootstrap;
46 | }
47 |
48 | public function getRequest()
49 | {
50 | return $this->_request;
51 | }
52 |
53 | public function dispatch(Route $route)
54 | {
55 | $protoView = ($this->getBootstrap()->getResource("view")) ? $this->getBootstrap()->getResource("view") : new View();
56 |
57 | $controllerPath = $this->getControllerPath();
58 | $router = $this->_router;
59 | $request = $this->_request;
60 | $protoView->addHelper("pull", function($uri) use ($controllerPath, $router, $request) {
61 | $request = clone $request;
62 | $request->setUri($uri);
63 | $routeObj = $router->match($request);
64 |
65 | $controllerClassName = $routeObj->getControllerName() . "Controller";
66 | $action = $routeObj->getActionName() . "Action";
67 | $classPath = realpath($controllerPath . DIRECTORY_SEPARATOR . $controllerClassName . ".php");
68 | if (file_exists($classPath)) {
69 | require_once $classPath;
70 |
71 | $controller = new $controllerClassName();
72 | $controller->setParams($routeObj->getParams());
73 |
74 | if (method_exists($controller, $action)) {
75 | ob_start();
76 | $controller->init();
77 | $data = $controller->$action();
78 | ob_end_clean();
79 | return $data;
80 | } else {
81 | throw new RuntimeException("Pull operation {$routeObj->getControllerName()} - {$routeObj->getActionName()} failed.", 404);
82 | }
83 | } else {
84 | throw new RuntimeException("Pull operation {$routeObj->getControllerName()} - {$routeObj->getActionName()} failed.", 404);
85 | }
86 | });
87 |
88 | $dispatcher = new Dispatcher($protoView);
89 | $dispatcher->setRouter($this->_router);
90 | $dispatcher->setRequest($this->_request);
91 | $dispatcher->setEventManager($this->getEventManager());
92 | $dispatcher->setBootstrap($this->_bootstrap);
93 | $dispatcher->setControllerPath($this->getControllerPath());
94 |
95 | try {
96 | $this->_page = $dispatcher->dispatch($route);
97 | } catch (Exception $e) {
98 | $errorRoute = new Route();
99 | $errorRoute->addParams(
100 | array(
101 | 'exception' => $e
102 | )
103 | );
104 |
105 | $dispatcher->clearHeaders();
106 | $dispatcher->addHeader("","",404);
107 |
108 | $errorRoute->setControllerName("error");
109 | $errorRoute->setActionName("error");
110 |
111 | $this->_page = $dispatcher->dispatch($errorRoute);
112 | }
113 |
114 | return array('headers' => $dispatcher->getHeaders());
115 | }
116 |
117 | public function run(Request $request = null)
118 | {
119 | $this->_router = ($this->getBootstrap()->getResource("router")) ? $this->getBootstrap()->getResource("router") : new Router();
120 | $this->_request = (!$request) ? Request::newHttp() : $request;
121 |
122 | $outputBuffer = '';
123 | $this->getEventManager()->publish("loop.startup", array($this));
124 |
125 | $status = $this->dispatch($this->_router->match($this->_request));
126 |
127 | if (($layout = $this->getBootstrap()->getResource("layout")) instanceof Layout) {
128 | $layout->content = $this->_page;
129 |
130 | $outputBuffer = $layout->render($layout->getScriptName());
131 | } else {
132 | $outputBuffer = $this->_page;
133 | }
134 |
135 | $this->getEventManager()->publish("loop.shutdown", array($this));
136 |
137 | $this->sendHeaders($status["headers"]);
138 | echo $outputBuffer;
139 | }
140 |
141 | public function addRequest($uri)
142 | {
143 | $this->_requests[] = $uri;
144 | }
145 |
146 | public function sendHeaders($headers)
147 | {
148 | foreach ($headers as $header) {
149 | header($header["string"], $header["replace"], $header["code"]);
150 | }
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | REM Command file for Sphinx documentation
4 |
5 | if "%SPHINXBUILD%" == "" (
6 | set SPHINXBUILD=sphinx-build
7 | )
8 | set BUILDDIR=_build
9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
10 | set I18NSPHINXOPTS=%SPHINXOPTS% .
11 | if NOT "%PAPER%" == "" (
12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
14 | )
15 |
16 | if "%1" == "" goto help
17 |
18 | if "%1" == "help" (
19 | :help
20 | echo.Please use `make ^` where ^ is one of
21 | echo. html to make standalone HTML files
22 | echo. dirhtml to make HTML files named index.html in directories
23 | echo. singlehtml to make a single large HTML file
24 | echo. pickle to make pickle files
25 | echo. json to make JSON files
26 | echo. htmlhelp to make HTML files and a HTML help project
27 | echo. qthelp to make HTML files and a qthelp project
28 | echo. devhelp to make HTML files and a Devhelp project
29 | echo. epub to make an epub
30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
31 | echo. text to make text files
32 | echo. man to make manual pages
33 | echo. texinfo to make Texinfo files
34 | echo. gettext to make PO message catalogs
35 | echo. changes to make an overview over all changed/added/deprecated items
36 | echo. linkcheck to check all external links for integrity
37 | echo. doctest to run all doctests embedded in the documentation if enabled
38 | goto end
39 | )
40 |
41 | if "%1" == "clean" (
42 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
43 | del /q /s %BUILDDIR%\*
44 | goto end
45 | )
46 |
47 | if "%1" == "html" (
48 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
49 | if errorlevel 1 exit /b 1
50 | echo.
51 | echo.Build finished. The HTML pages are in %BUILDDIR%/html.
52 | goto end
53 | )
54 |
55 | if "%1" == "dirhtml" (
56 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
57 | if errorlevel 1 exit /b 1
58 | echo.
59 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
60 | goto end
61 | )
62 |
63 | if "%1" == "singlehtml" (
64 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
65 | if errorlevel 1 exit /b 1
66 | echo.
67 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
68 | goto end
69 | )
70 |
71 | if "%1" == "pickle" (
72 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
73 | if errorlevel 1 exit /b 1
74 | echo.
75 | echo.Build finished; now you can process the pickle files.
76 | goto end
77 | )
78 |
79 | if "%1" == "json" (
80 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
81 | if errorlevel 1 exit /b 1
82 | echo.
83 | echo.Build finished; now you can process the JSON files.
84 | goto end
85 | )
86 |
87 | if "%1" == "htmlhelp" (
88 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
89 | if errorlevel 1 exit /b 1
90 | echo.
91 | echo.Build finished; now you can run HTML Help Workshop with the ^
92 | .hhp project file in %BUILDDIR%/htmlhelp.
93 | goto end
94 | )
95 |
96 | if "%1" == "qthelp" (
97 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
98 | if errorlevel 1 exit /b 1
99 | echo.
100 | echo.Build finished; now you can run "qcollectiongenerator" with the ^
101 | .qhcp project file in %BUILDDIR%/qthelp, like this:
102 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\simple-mvc.qhcp
103 | echo.To view the help file:
104 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\simple-mvc.ghc
105 | goto end
106 | )
107 |
108 | if "%1" == "devhelp" (
109 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
110 | if errorlevel 1 exit /b 1
111 | echo.
112 | echo.Build finished.
113 | goto end
114 | )
115 |
116 | if "%1" == "epub" (
117 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
118 | if errorlevel 1 exit /b 1
119 | echo.
120 | echo.Build finished. The epub file is in %BUILDDIR%/epub.
121 | goto end
122 | )
123 |
124 | if "%1" == "latex" (
125 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
126 | if errorlevel 1 exit /b 1
127 | echo.
128 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
129 | goto end
130 | )
131 |
132 | if "%1" == "text" (
133 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
134 | if errorlevel 1 exit /b 1
135 | echo.
136 | echo.Build finished. The text files are in %BUILDDIR%/text.
137 | goto end
138 | )
139 |
140 | if "%1" == "man" (
141 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
142 | if errorlevel 1 exit /b 1
143 | echo.
144 | echo.Build finished. The manual pages are in %BUILDDIR%/man.
145 | goto end
146 | )
147 |
148 | if "%1" == "texinfo" (
149 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
150 | if errorlevel 1 exit /b 1
151 | echo.
152 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
153 | goto end
154 | )
155 |
156 | if "%1" == "gettext" (
157 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
158 | if errorlevel 1 exit /b 1
159 | echo.
160 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
161 | goto end
162 | )
163 |
164 | if "%1" == "changes" (
165 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
166 | if errorlevel 1 exit /b 1
167 | echo.
168 | echo.The overview file is in %BUILDDIR%/changes.
169 | goto end
170 | )
171 |
172 | if "%1" == "linkcheck" (
173 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
174 | if errorlevel 1 exit /b 1
175 | echo.
176 | echo.Link check complete; look for any errors in the above output ^
177 | or in %BUILDDIR%/linkcheck/output.txt.
178 | goto end
179 | )
180 |
181 | if "%1" == "doctest" (
182 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
183 | if errorlevel 1 exit /b 1
184 | echo.
185 | echo.Testing of doctests in the sources finished, look at the ^
186 | results in %BUILDDIR%/doctest/output.txt.
187 | goto end
188 | )
189 |
190 | :end
191 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | PAPER =
8 | BUILDDIR = _build
9 |
10 | # Internal variables.
11 | PAPEROPT_a4 = -D latex_paper_size=a4
12 | PAPEROPT_letter = -D latex_paper_size=letter
13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
14 | # the i18n builder cannot share the environment and doctrees with the others
15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
16 |
17 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
18 |
19 | help:
20 | @echo "Please use \`make ' where is one of"
21 | @echo " html to make standalone HTML files"
22 | @echo " dirhtml to make HTML files named index.html in directories"
23 | @echo " singlehtml to make a single large HTML file"
24 | @echo " pickle to make pickle files"
25 | @echo " json to make JSON files"
26 | @echo " htmlhelp to make HTML files and a HTML help project"
27 | @echo " qthelp to make HTML files and a qthelp project"
28 | @echo " devhelp to make HTML files and a Devhelp project"
29 | @echo " epub to make an epub"
30 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
31 | @echo " latexpdf to make LaTeX files and run them through pdflatex"
32 | @echo " text to make text files"
33 | @echo " man to make manual pages"
34 | @echo " texinfo to make Texinfo files"
35 | @echo " info to make Texinfo files and run them through makeinfo"
36 | @echo " gettext to make PO message catalogs"
37 | @echo " changes to make an overview of all changed/added/deprecated items"
38 | @echo " linkcheck to check all external links for integrity"
39 | @echo " doctest to run all doctests embedded in the documentation (if enabled)"
40 |
41 | clean:
42 | -rm -rf $(BUILDDIR)/*
43 |
44 | html:
45 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
46 | @echo
47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
48 |
49 | dirhtml:
50 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
51 | @echo
52 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
53 |
54 | singlehtml:
55 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
56 | @echo
57 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
58 |
59 | pickle:
60 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
61 | @echo
62 | @echo "Build finished; now you can process the pickle files."
63 |
64 | json:
65 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
66 | @echo
67 | @echo "Build finished; now you can process the JSON files."
68 |
69 | htmlhelp:
70 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
71 | @echo
72 | @echo "Build finished; now you can run HTML Help Workshop with the" \
73 | ".hhp project file in $(BUILDDIR)/htmlhelp."
74 |
75 | qthelp:
76 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
77 | @echo
78 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \
79 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
80 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/simple-mvc.qhcp"
81 | @echo "To view the help file:"
82 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/simple-mvc.qhc"
83 |
84 | devhelp:
85 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
86 | @echo
87 | @echo "Build finished."
88 | @echo "To view the help file:"
89 | @echo "# mkdir -p $$HOME/.local/share/devhelp/simple-mvc"
90 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/simple-mvc"
91 | @echo "# devhelp"
92 |
93 | epub:
94 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
95 | @echo
96 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
97 |
98 | latex:
99 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
100 | @echo
101 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
102 | @echo "Run \`make' in that directory to run these through (pdf)latex" \
103 | "(use \`make latexpdf' here to do that automatically)."
104 |
105 | latexpdf:
106 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
107 | @echo "Running LaTeX files through pdflatex..."
108 | $(MAKE) -C $(BUILDDIR)/latex all-pdf
109 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
110 |
111 | text:
112 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
113 | @echo
114 | @echo "Build finished. The text files are in $(BUILDDIR)/text."
115 |
116 | man:
117 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
118 | @echo
119 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
120 |
121 | texinfo:
122 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
123 | @echo
124 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
125 | @echo "Run \`make' in that directory to run these through makeinfo" \
126 | "(use \`make info' here to do that automatically)."
127 |
128 | info:
129 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
130 | @echo "Running Texinfo files through makeinfo..."
131 | make -C $(BUILDDIR)/texinfo info
132 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
133 |
134 | gettext:
135 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
136 | @echo
137 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
138 |
139 | changes:
140 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
141 | @echo
142 | @echo "The overview file is in $(BUILDDIR)/changes."
143 |
144 | linkcheck:
145 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
146 | @echo
147 | @echo "Link check complete; look for any errors in the above output " \
148 | "or in $(BUILDDIR)/linkcheck/output.txt."
149 |
150 | doctest:
151 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
152 | @echo "Testing of doctests in the sources finished, look at the " \
153 | "results in $(BUILDDIR)/doctest/output.txt."
154 |
--------------------------------------------------------------------------------
/docs/views.rst:
--------------------------------------------------------------------------------
1 | Views
2 | =====
3 |
4 | The framework starts without view system. For add view support
5 | you have to add `view` at bootstrap.
6 |
7 | .. code-block:: php
8 | :linenos:
9 |
10 | bootstrap('view', function(){
14 | $view = new View();
15 | $view->addViewPath(__DIR__ . '/../views');
16 |
17 | return $view;
18 | });
19 |
20 | $app->run();
21 |
22 | The framework append automatically to a controller the right
23 | view using controller and action name. Tipically you have to
24 | create a folder tree like this: ::
25 |
26 | site
27 | + public
28 | + controllers
29 | - views
30 | - index
31 | - index.phtml
32 |
33 | In this way the system load correctly the controller path and the view
34 | script.
35 |
36 | Layout support
37 | --------------
38 |
39 | The layout is handled as a simple view that wrap the controller view.
40 |
41 | You need to bootstrap it. The normal layout name is "layout.phtml"
42 |
43 | .. code-block:: php
44 | :linenos:
45 |
46 | bootstrap('layout', function(){
49 | $layout = new Layout();
50 | $layout->addViewPath(__DIR__ . '/../layouts');
51 |
52 | return $layout;
53 | });
54 |
55 | You can change the layout script name using the setter.
56 |
57 | .. code-block:: php
58 | :linenos:
59 |
60 | setScriptName("base.phtml");
62 |
63 | View Helpers
64 | ------------
65 |
66 | If you want to create view helpers during your view bootstrap
67 | add an helper closure.
68 |
69 | .. code-block:: php
70 | :linenos:
71 |
72 | bootstrap('view', function(){
74 | $view = new View();
75 | $view->addViewPath(__DIR__ . '/../views');
76 |
77 | $view->addHelper("now", function(){
78 | return date("d-m-Y");
79 | });
80 |
81 | return $view;
82 | });
83 |
84 | You can use it into you view as:
85 |
86 | .. code-block:: php
87 | :linenos:
88 |
89 | now()?>
90 |
91 | You can create helpers with many variables
92 |
93 | .. code-block:: php
94 | :linenos:
95 |
96 | addHelper("sayHello", function($name){
98 | return "Hello {$name}";
99 | });
100 |
101 | View system is based using the prototype pattern all of your
102 | helpers attached at bootstrap time existing into all of your
103 | real views.
104 |
105 | Share view helpers
106 | ~~~~~~~~~~~~~~~~~~
107 |
108 | View helpers are automatically shared with layout. In this way
109 | you can creates global helpers during the bootstrap and interact with
110 | those helpers at action time.
111 |
112 | Pay attention that those helpers are copied. Use `static` scope for
113 | share variables.
114 |
115 | .. code-block:: php
116 | :linenos:
117 |
118 | bootstrap("layout", function(){
120 | $layout = new Layout();
121 | $layout->addViewPath(__DIR__ . '/../layouts');
122 |
123 |
124 | return $layout;
125 | });
126 |
127 | $app->bootstrap("view", function(){
128 | $view = new View();
129 | $view->addViewPath(__DIR__ . '/../views');
130 |
131 | $view->addHelper("title", function($part = false){
132 | static $parts = array();
133 | static $delimiter = ' :: ';
134 |
135 | return ($part === false) ? "".implode($delimiter, $parts)." " : $parts[] = $part;
136 | });
137 |
138 | return $view;
139 | });
140 |
141 | From a view you can call the `title()` helper and it appends parts of you
142 | page title.
143 |
144 | Escapes
145 | -------
146 |
147 | Escape is a default view helper. You can escape variables using the
148 | `escape()` view helper.
149 |
150 | .. code-block:: php
151 | :linenos:
152 |
153 | escape("Ciao -->"); // Ciao -->
155 |
156 | Partials view
157 | -------------
158 |
159 | Partials view are useful for render section of your view separately. In
160 | `simple-mvc` partials are view helpers.
161 |
162 | .. code-block:: html
163 | :linenos:
164 |
165 |
166 |
167 |
168 | partial("/path/to/view.phtml", array('title' => $this->title));?>
169 |
170 |
171 |
172 | The partial view `/path/to/view.phtml` are located at `view` path.
173 |
174 | .. code-block:: php
175 | :linenos:
176 |
177 |
178 | title; ?>
179 |
180 | Multiple view scripts paths
181 | ---------------------------
182 |
183 | `simple-mvc` support multiple views scripts paths. In other words you can specify
184 | a single mount point `/path/to/views` after that you can add anther views script path,
185 | this mean that the `simple-mvc` search for a view previously into the second views path
186 | and if it is missing looks for that into the first paths. View paths are threated as
187 | a stack, the latest pushed is the first used.
188 |
189 | During your bootstrap add more view paths
190 |
191 | .. code-block:: php
192 | :linenos:
193 |
194 | $app->bootstrap('view', function(){
195 | $view = new View();
196 | $view->addViewPath(__DIR__ . '/../views');
197 | $view->addViewPath(__DIR__ . '/../views-rewrite');
198 |
199 | return $view;
200 | });
201 |
202 | If you have a view named `name.phtml` into `views` folder and now you create the view
203 | named `name.phtml` into `views-rewrite` this one is used instead the original file in
204 | `views` folder.
205 |
206 | Partials and multiple view scripts paths
207 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
208 |
209 | ***Partial views follow the rewrite path strategy***. If you add the partial
210 | view into a rewrite view folder, this view script is choosen instead
211 | the original partial script.
212 |
213 | .. code-block:: php
214 | :linenos:
215 |
216 | partial("my-helper.phtml", array('ciao' => 'hello'))?>
217 |
218 | If `my-helper.phtml` is found in a rewrite point this view is used instead
219 | the original view script.
220 |
221 | The end.
222 |
--------------------------------------------------------------------------------
/tests/RouterTest.php:
--------------------------------------------------------------------------------
1 | _object = new Router();
16 | }
17 |
18 | public function testDefaultRouting()
19 | {
20 | $request = new Request();
21 | $request->setUri("/controller/action");
22 | $route = $this->_object->match($request);
23 |
24 | $this->assertEquals("Controller", $route->getControllerName());
25 | $this->assertEquals("action", $route->getActionName());
26 | }
27 |
28 | public function testDefaultRoutingComplexNaming()
29 | {
30 | $request = new Request();
31 | $request->setUri("/controller-name/action-name-hello");
32 | $route = $this->_object->match($request);
33 |
34 | $this->assertEquals("ControllerName", $route->getControllerName());
35 | $this->assertEquals("actionNameHello", $route->getActionName());
36 | }
37 |
38 | public function testChain()
39 | {
40 | $request = new Request();
41 | $request->setHostname("t.test.local");
42 | $request->setUri("/");
43 |
44 | $router = new Router();
45 | $hostnameRouter = new HostnameRouter("t.test.local");
46 | $hostnameRouter->addChild("hello", new StaticRouter("/", "Hello", "world"));
47 |
48 | $router->addChild("hostname", $hostnameRouter);
49 |
50 | $route = $router->match($request);
51 |
52 | $this->assertEquals("Hello", $route->getControllerName());
53 | }
54 |
55 | public function testSlashExplode()
56 | {
57 | $routeObj = $this->_object->match(new Request("/"));
58 |
59 | $route = $routeObj->getRoute();
60 | $params = $routeObj->getParams();
61 |
62 | $this->assertEquals("Index", $route["controller"]);
63 | $this->assertEquals("index", $route["action"]);
64 |
65 | $this->assertInternalType("array", $params);
66 | $this->assertSame(0, count($params));
67 | }
68 |
69 | public function testOnlyControllerExplode()
70 | {
71 | $routeObj = $this->_object->match(new Request("/home"));
72 |
73 | $route = $routeObj->getRoute();
74 | $params = $routeObj->getParams();
75 |
76 | $this->assertEquals("Home", $route["controller"]);
77 | $this->assertEquals("index", $route["action"]);
78 |
79 | $this->assertInternalType("array", $params);
80 | $this->assertSame(0, count($params));
81 | }
82 |
83 | public function testControllerActionExplode()
84 | {
85 | $routeObj = $this->_object->match(new Request("/admin/home"));
86 |
87 | $route = $routeObj->getRoute();
88 | $params = $routeObj->getParams();
89 |
90 | $this->assertEquals("Admin", $route["controller"]);
91 | $this->assertEquals("home", $route["action"]);
92 |
93 | $this->assertInternalType("array", $params);
94 | $this->assertSame(0, count($params));
95 | }
96 |
97 | public function testParamsControllerActionExplode()
98 | {
99 | $r = new Request("/walk/on/area/bar/status/inlove");
100 | $routeObj = $this->_object->match($r);
101 |
102 | $route = $routeObj->getRoute();
103 | $params = $r->getParams();
104 |
105 | $this->assertEquals("Walk", $route["controller"]);
106 | $this->assertEquals("on", $route["action"]);
107 |
108 | $this->assertInternalType("array", $params);
109 | $this->assertSame(2, count($params));
110 |
111 | $keys = array_keys($params);
112 | $this->assertEquals("area", $keys[0]);
113 | $this->assertEquals("status", $keys[1]);
114 |
115 | $this->assertEquals("bar", $params["area"]);
116 | $this->assertEquals("inlove", $params["status"]);
117 | }
118 |
119 | public function testUnbalancedParamsExplode()
120 | {
121 | $r = new Request("/walk/on/area/bar/status");
122 | $routeObj = $this->_object->match($r);
123 |
124 | $route = $routeObj->getRoute();
125 | $params = $r->getParams();
126 |
127 | $this->assertEquals("Walk", $route["controller"]);
128 | $this->assertEquals("on", $route["action"]);
129 |
130 | $this->assertInternalType("array", $params);
131 | $this->assertSame(1, count($params));
132 |
133 | $keys = array_keys($params);
134 | $this->assertEquals("area", $keys[0]);
135 |
136 | $this->assertEquals("bar", $params["area"]);
137 | }
138 |
139 | public function testComplexControllerName()
140 | {
141 | $r = new Request("/walk-on-files/hello-sunny-day/param/ok-this");
142 | $routeObj = $this->_object->match($r);
143 |
144 | $route = $routeObj->getRoute();
145 | $params = $r->getParams();
146 |
147 | $this->assertEquals("WalkOnFiles", $route["controller"]);
148 | $this->assertEquals("helloSunnyDay", $route["action"]);
149 |
150 | $params = $r->getParams();
151 | $this->assertEquals("ok-this", $params["param"]);
152 | }
153 |
154 | public function testClearLongGetParams()
155 | {
156 | $uri = '/?hello=world&titti=totti';
157 | $route = $this->_object->match(new Request($uri));
158 | $this->assertEquals("Index", $route->getControllerName());
159 | $this->assertEquals("index", $route->getActionName());
160 | }
161 |
162 | public function testClearGet2Params()
163 | {
164 | $uri = '/account?hello=world';
165 | $route = $this->_object->match(new Request($uri));
166 | $this->assertEquals("Account", $route->getControllerName());
167 | $this->assertEquals("index", $route->getActionName());
168 | }
169 |
170 | public function testClearGet3Params()
171 | {
172 | $uri = '/admin/account-super?hello=world';
173 | $route = $this->_object->match(new Request($uri));
174 | $this->assertEquals("Admin", $route->getControllerName());
175 | $this->assertEquals("accountSuper", $route->getActionName());
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/tests/ViewTest.php:
--------------------------------------------------------------------------------
1 | object = new View;
21 | }
22 |
23 | /**
24 | * Tears down the fixture, for example, closes a network connection.
25 | * This method is called after a test is executed.
26 | */
27 | protected function tearDown()
28 | {
29 | }
30 |
31 | /**
32 | * @covers View::__set
33 | * @covers View::__get
34 | */
35 | public function testSetAndGet()
36 | {
37 | $this->object->string = "ok";
38 | $this->assertSame("ok", $this->object->string);
39 |
40 | $this->object->integer = 100;
41 | $this->assertSame(100, $this->object->integer);
42 |
43 | $arr = array();
44 | $arr["ciao"] = "hello";
45 | $this->object->arr = $arr;
46 |
47 | $this->assertInternalType("array", $this->object->arr);
48 | $this->assertEquals(1, count($this->object->arr));
49 | $this->assertEquals("hello", $this->object->arr["ciao"]);
50 |
51 | $this->assertSame($arr, $this->object->arr);
52 |
53 | $obj = new stdClass();
54 | $obj->value = "ciao";
55 |
56 | $this->object->obj = $obj;
57 |
58 | $this->assertInstanceOf("stdClass", $this->object->obj);
59 | $this->assertSame($obj, $this->object->obj);
60 | }
61 |
62 | /**
63 | * @covers View::setViewPath
64 | */
65 | public function testSetViewPath()
66 | {
67 | // Remove the following lines when you implement this test.
68 | $this->object->setViewPath(dirname(__FILE__));
69 |
70 | $this->assertEquals(dirname(__FILE__), $this->object->getViewPath());
71 | }
72 |
73 | /**
74 | * @expectedException RuntimeException
75 | */
76 | public function testSetInvalidViewPath()
77 | {
78 | $this->object->setViewPath(dirname(__FILE__) . '/ViewTest.php');
79 | }
80 |
81 | /**
82 | * @expectedException RuntimeException
83 | */
84 | public function testSetMissingViewPath()
85 | {
86 | $this->object->setViewPath(dirname(__FILE__) . '/hidden-views');
87 | }
88 |
89 | /**
90 | * @covers View::render
91 | */
92 | public function testRender()
93 | {
94 | $this->object->setViewPath(dirname(__FILE__) . '/views');
95 | $this->object->value = "ciao-mondo";
96 | $text = $this->object->render("view-test.phtml");
97 |
98 | $this->assertEquals("ciao-mondo
", $text);
99 | }
100 |
101 | public function testMixData()
102 | {
103 | $this->object->setViewPath(dirname(__FILE__) . '/views');
104 | $this->object->value = "hello";
105 | $exec = $this->object->render("view-mix-test.phtml", array("value2" => 'hello'));
106 |
107 | $this->assertEquals("hello
hello
", $exec);
108 | }
109 |
110 | /**
111 | * @expectedException RuntimeException
112 | */
113 | public function testMixNonArrayData()
114 | {
115 | $this->object->setViewPath(dirname(__FILE__) . '/views');
116 | $this->object->value = "hello";
117 | $exec = $this->object->render("view-test.phtml", 'hello');
118 | }
119 |
120 | /**
121 | * @expectedException RuntimeException
122 | */
123 | public function testMissingTemplate()
124 | {
125 | $exec = $this->object->render("missing-view-test.phtml");
126 | }
127 |
128 | public function testEmptyGet()
129 | {
130 | $false = $this->object->missingKey;
131 |
132 | $this->assertFalse($false);
133 | }
134 |
135 | public function testGetViewPath()
136 | {
137 | $this->object->setViewPath(dirname(__FILE__));
138 |
139 | $path = $this->object->getViewPath();
140 |
141 | $this->assertEquals(dirname(__FILE__), $path);
142 | }
143 |
144 | public function testViewParamEscape()
145 | {
146 | $ret = $this->object->escape("ciao
");
147 | $this->assertEquals("<p>ciao</p>", $ret);
148 | }
149 |
150 | public function testViewChars()
151 | {
152 | $ret = $this->object->p = "èàòùìç@";
153 | $this->assertEquals("èàòùìç@", $this->object->p);
154 | $this->assertEquals("èàòùìç@", $this->object->escape("èàòùìç@"));
155 | $this->assertEquals(""", $this->object->escape("\"", ENT_QUOTES));
156 | $this->assertEquals("\"'", $this->object->escape("\"'", ENT_NOQUOTES));
157 | }
158 |
159 | public function testViewHelpers()
160 | {
161 | $this->object->addHelper("example", function(){
162 | return "walter";
163 | });
164 |
165 | $this->assertEquals("walter", $this->object->example());
166 | }
167 |
168 | public function testViewHelpersOneParam()
169 | {
170 | $this->object->addHelper("example", function($param){
171 | return "walter-{$param}";
172 | });
173 |
174 | $this->assertEquals("walter-stringa", $this->object->example("stringa"));
175 | }
176 |
177 | public function testViewHelpersMultipleParams()
178 | {
179 | $this->object->addHelper("example", function($param, $app, $def){
180 | return "walter-{$param}-{$def}-{$app}";
181 | });
182 |
183 | $this->assertEquals("walter-vg-#!-_9", $this->object->example("vg", "_9", "#!"));
184 | }
185 |
186 | public function testCloneView()
187 | {
188 | $v = new View();
189 | $v->setViewPath(__DIR__ . "/views");
190 | $v2 = $v->cloneThis();
191 |
192 | $this->assertNotSame($v, $v2);
193 | $v2->value = "hello";
194 | $this->assertEquals("hello
", $v2->render("view-test.phtml"));
195 | }
196 |
197 | /**
198 | * @expectedException RuntimeException
199 | */
200 | public function testMissingHelperView()
201 | {
202 | $this->object->now();
203 | }
204 |
205 | public function testPartialViewHelper()
206 | {
207 | $v = new View();
208 | $v->setViewPath(__DIR__ . '/views');
209 |
210 | $html = $v->render("base.phtml");
211 | $this->assertEquals("", $html);
212 | }
213 |
214 | public function testRewritePoint()
215 | {
216 | $v = new View();
217 | $v->addViewPath(__DIR__ . '/views');
218 | $v->addViewPath(__DIR__ . '/views-rewrite');
219 |
220 | $html = $v->render("base.phtml");
221 | $this->assertEquals("REWRITED!
", $html);
222 | }
223 |
224 | /**
225 | * @expectedException RuntimeException
226 | */
227 | public function testWrongViewPath()
228 | {
229 | $this->object->addViewPath(__DIR__ . '/missing-point');
230 | }
231 |
232 | public function testMissingRewrite()
233 | {
234 | $v = new View();
235 | $v->addViewPath(__DIR__ . '/views');
236 | $v->addViewPath(__DIR__ . '/views-rewrite');
237 |
238 | $html = $v->render("only-base.phtml");
239 | $this->assertEquals("Only base ", $html);
240 | }
241 | }
242 |
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # simple-mvc documentation build configuration file, created by
4 | # sphinx-quickstart on Sat Sep 8 12:59:34 2012.
5 | #
6 | # This file is execfile()d with the current directory set to its containing dir.
7 | #
8 | # Note that not all possible configuration values are present in this
9 | # autogenerated file.
10 | #
11 | # All configuration values have a default; values that are commented out
12 | # serve to show the default.
13 |
14 | import sys, os
15 |
16 | # If extensions (or modules to document with autodoc) are in another directory,
17 | # add these directories to sys.path here. If the directory is relative to the
18 | # documentation root, use os.path.abspath to make it absolute, like shown here.
19 | #sys.path.insert(0, os.path.abspath('.'))
20 |
21 | # -- General configuration -----------------------------------------------------
22 |
23 | # If your documentation needs a minimal Sphinx version, state it here.
24 | #needs_sphinx = '1.0'
25 |
26 | # Add any Sphinx extension module names here, as strings. They can be extensions
27 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
28 | extensions = []
29 |
30 | # Add any paths that contain templates here, relative to this directory.
31 | templates_path = ['_templates']
32 |
33 | # The suffix of source filenames.
34 | source_suffix = '.rst'
35 |
36 | # The encoding of source files.
37 | #source_encoding = 'utf-8-sig'
38 |
39 | # The master toctree document.
40 | master_doc = 'index'
41 |
42 | # General information about the project.
43 | project = u'simple-mvc'
44 | copyright = u'2012, Walter Dal Mut'
45 |
46 | # The version info for the project you're documenting, acts as replacement for
47 | # |version| and |release|, also used in various other places throughout the
48 | # built documents.
49 | #
50 | # The short X.Y version.
51 | version = '0.1'
52 | # The full version, including alpha/beta/rc tags.
53 | release = '0.1.0'
54 |
55 | # The language for content autogenerated by Sphinx. Refer to documentation
56 | # for a list of supported languages.
57 | #language = None
58 |
59 | # There are two options for replacing |today|: either, you set today to some
60 | # non-false value, then it is used:
61 | #today = ''
62 | # Else, today_fmt is used as the format for a strftime call.
63 | #today_fmt = '%B %d, %Y'
64 |
65 | # List of patterns, relative to source directory, that match files and
66 | # directories to ignore when looking for source files.
67 | exclude_patterns = ['_build']
68 |
69 | # The reST default role (used for this markup: `text`) to use for all documents.
70 | #default_role = None
71 |
72 | # If true, '()' will be appended to :func: etc. cross-reference text.
73 | #add_function_parentheses = True
74 |
75 | # If true, the current module name will be prepended to all description
76 | # unit titles (such as .. function::).
77 | #add_module_names = True
78 |
79 | # If true, sectionauthor and moduleauthor directives will be shown in the
80 | # output. They are ignored by default.
81 | #show_authors = False
82 |
83 | # The name of the Pygments (syntax highlighting) style to use.
84 | pygments_style = 'sphinx'
85 |
86 | # A list of ignored prefixes for module index sorting.
87 | #modindex_common_prefix = []
88 |
89 |
90 | # -- Options for HTML output ---------------------------------------------------
91 |
92 | # The theme to use for HTML and HTML Help pages. See the documentation for
93 | # a list of builtin themes.
94 | html_theme = 'default'
95 |
96 | # Theme options are theme-specific and customize the look and feel of a theme
97 | # further. For a list of options available for each theme, see the
98 | # documentation.
99 | #html_theme_options = {}
100 |
101 | # Add any paths that contain custom themes here, relative to this directory.
102 | #html_theme_path = []
103 |
104 | # The name for this set of Sphinx documents. If None, it defaults to
105 | # " v documentation".
106 | #html_title = None
107 |
108 | # A shorter title for the navigation bar. Default is the same as html_title.
109 | #html_short_title = None
110 |
111 | # The name of an image file (relative to this directory) to place at the top
112 | # of the sidebar.
113 | #html_logo = None
114 |
115 | # The name of an image file (within the static path) to use as favicon of the
116 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
117 | # pixels large.
118 | #html_favicon = None
119 |
120 | # Add any paths that contain custom static files (such as style sheets) here,
121 | # relative to this directory. They are copied after the builtin static files,
122 | # so a file named "default.css" will overwrite the builtin "default.css".
123 | html_static_path = ['_static']
124 |
125 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
126 | # using the given strftime format.
127 | #html_last_updated_fmt = '%b %d, %Y'
128 |
129 | # If true, SmartyPants will be used to convert quotes and dashes to
130 | # typographically correct entities.
131 | #html_use_smartypants = True
132 |
133 | # Custom sidebar templates, maps document names to template names.
134 | #html_sidebars = {}
135 |
136 | # Additional templates that should be rendered to pages, maps page names to
137 | # template names.
138 | #html_additional_pages = {}
139 |
140 | # If false, no module index is generated.
141 | #html_domain_indices = True
142 |
143 | # If false, no index is generated.
144 | #html_use_index = True
145 |
146 | # If true, the index is split into individual pages for each letter.
147 | #html_split_index = False
148 |
149 | # If true, links to the reST sources are added to the pages.
150 | #html_show_sourcelink = True
151 |
152 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
153 | #html_show_sphinx = True
154 |
155 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
156 | #html_show_copyright = True
157 |
158 | # If true, an OpenSearch description file will be output, and all pages will
159 | # contain a tag referring to it. The value of this option must be the
160 | # base URL from which the finished HTML is served.
161 | #html_use_opensearch = ''
162 |
163 | # This is the file name suffix for HTML files (e.g. ".xhtml").
164 | #html_file_suffix = None
165 |
166 | # Output file base name for HTML help builder.
167 | htmlhelp_basename = 'simple-mvcdoc'
168 |
169 |
170 | # -- Options for LaTeX output --------------------------------------------------
171 |
172 | latex_elements = {
173 | # The paper size ('letterpaper' or 'a4paper').
174 | #'papersize': 'letterpaper',
175 |
176 | # The font size ('10pt', '11pt' or '12pt').
177 | #'pointsize': '10pt',
178 |
179 | # Additional stuff for the LaTeX preamble.
180 | #'preamble': '',
181 | }
182 |
183 | # Grouping the document tree into LaTeX files. List of tuples
184 | # (source start file, target name, title, author, documentclass [howto/manual]).
185 | latex_documents = [
186 | ('index', 'simple-mvc.tex', u'simple-mvc Documentation',
187 | u'Walter Dal Mut', 'manual'),
188 | ]
189 |
190 | # The name of an image file (relative to this directory) to place at the top of
191 | # the title page.
192 | #latex_logo = None
193 |
194 | # For "manual" documents, if this is true, then toplevel headings are parts,
195 | # not chapters.
196 | #latex_use_parts = False
197 |
198 | # If true, show page references after internal links.
199 | #latex_show_pagerefs = False
200 |
201 | # If true, show URL addresses after external links.
202 | #latex_show_urls = False
203 |
204 | # Documents to append as an appendix to all manuals.
205 | #latex_appendices = []
206 |
207 | # If false, no module index is generated.
208 | #latex_domain_indices = True
209 |
210 |
211 | # -- Options for manual page output --------------------------------------------
212 |
213 | # One entry per manual page. List of tuples
214 | # (source start file, name, description, authors, manual section).
215 | man_pages = [
216 | ('index', 'simple-mvc', u'simple-mvc Documentation',
217 | [u'Walter Dal Mut'], 1)
218 | ]
219 |
220 | # If true, show URL addresses after external links.
221 | #man_show_urls = False
222 |
223 |
224 | # -- Options for Texinfo output ------------------------------------------------
225 |
226 | # Grouping the document tree into Texinfo files. List of tuples
227 | # (source start file, target name, title, author,
228 | # dir menu entry, description, category)
229 | texinfo_documents = [
230 | ('index', 'simple-mvc', u'simple-mvc Documentation',
231 | u'Walter Dal Mut', 'simple-mvc', 'One line description of project.',
232 | 'Miscellaneous'),
233 | ]
234 |
235 | # Documents to append as an appendix to all manuals.
236 | #texinfo_appendices = []
237 |
238 | # If false, no module index is generated.
239 | #texinfo_domain_indices = True
240 |
241 | # How to display URL addresses: 'footnote', 'no', or 'inline'.
242 | #texinfo_show_urls = 'footnote'
243 |
--------------------------------------------------------------------------------
/tests/ApplicationTest.php:
--------------------------------------------------------------------------------
1 | object = $this->getMock(
23 | "Application",
24 | array('sendHeaders')
25 | );
26 | $this->object->setControllerPath(__DIR__ . '/controllers');
27 | $this->object
28 | ->expects($this->any())
29 | ->method("sendHeaders")
30 | ->will($this->returnValue(null));
31 | }
32 |
33 | /**
34 | * Tears down the fixture, for example, closes a network connection.
35 | * This method is called after a test is executed.
36 | */
37 | protected function tearDown()
38 | {
39 | }
40 |
41 | public function testDefaultBootstrapInstance()
42 | {
43 | $this->assertInstanceOf("Bootstrap", $this->object->getBootstrap());
44 | }
45 |
46 | /**
47 | * @covers Application::bootstrap
48 | * @covers Application::getBootstrap
49 | */
50 | public function testBootstrap()
51 | {
52 | $this->object->bootstrap("hello", function(){return "ciao";});
53 | $boot = $this->object->getBootstrap()->getResource("hello");
54 |
55 | $this->assertEquals($boot, "ciao");
56 | }
57 |
58 | /**
59 | * Resources must bootstrap onetime
60 | *
61 | * @covers Application::getBootstrap
62 | */
63 | public function testGetMultipleTimes()
64 | {
65 | $this->object->bootstrap("hello", function(){
66 | return new View();
67 | });
68 | $boot = $this->object->getBootstrap()->getResource("hello");
69 | $boot2 = $this->object->getBootstrap()->getResource("hello");
70 |
71 | $this->assertSame($boot, $boot2);
72 | }
73 |
74 | public function testSetGetControllerPath()
75 | {
76 | $this->object->setControllerPath(__DIR__);
77 |
78 | $this->assertEquals(__DIR__, $this->object->getControllerPath());
79 | }
80 |
81 | public function testSetGetEventManager()
82 | {
83 | $mng = new EventManager();
84 | $this->object->setEventManager($mng);
85 |
86 | $this->assertSame($mng, $this->object->getEventManager());
87 | }
88 |
89 | /**
90 | * @expectedException RuntimeException
91 | */
92 | public function testBootstrapNotCallable()
93 | {
94 | $this->object->bootstrap("up", "not-callable");
95 | }
96 |
97 | public function testMissingLayout()
98 | {
99 | $request = new Request();
100 | $request->setUri("/error/error");
101 |
102 | $this->object->bootstrap("view", function(){
103 | $v = new View();
104 | $v->setViewPath(__DIR__ . '/views');
105 |
106 | return $v;
107 | });
108 | $this->object->setControllerPath(__DIR__ . '/controllers');
109 |
110 | ob_start();
111 | $this->object->run($request);
112 | $content = ob_get_contents();
113 | ob_end_clean();
114 |
115 | $this->assertEquals("--> error action <--", $content);
116 |
117 | }
118 |
119 | public function testErrorPages()
120 | {
121 | $request = new Request();
122 | $request->setUri("/invalid/controller");
123 |
124 | ob_start();
125 | $this->object->run($request);
126 | $errorPage = ob_get_contents();
127 | ob_end_clean();
128 |
129 | $this->assertEquals("--> error action <--", $errorPage);
130 | }
131 |
132 | public function testInitAction()
133 | {
134 | ob_start();
135 | $this->object->run(new Request("init/index"));
136 | $initOutput = ob_get_contents();
137 | ob_end_clean();
138 |
139 | $this->assertEquals("<-- init -->", $initOutput);
140 | }
141 |
142 | public function testPreDispatchHook()
143 | {
144 | $app = '';
145 | $this->object->getEventManager()->subscribe("pre.dispatch", function($r, $app){
146 | $r->setControllerName("admin");
147 | $r->setActionName("login");
148 | });
149 |
150 | ob_start();
151 | $this->object->run(new Request("/init/index"));
152 | $adminOutput = ob_get_contents();
153 | ob_end_clean();
154 |
155 | $this->assertEquals("<-- admin login -->", $adminOutput);
156 | }
157 |
158 | public function testThenMethod()
159 | {
160 | ob_start();
161 | $this->object->run(new Request("/then/first"));
162 | $thenOutput = ob_get_contents();
163 | ob_end_clean();
164 |
165 | $this->assertEquals("first-><-second", $thenOutput);
166 | }
167 |
168 | /**
169 | * @expectedException RuntimeException
170 | */
171 | public function testMissingAction()
172 | {
173 | $this->object->setControllerPath(__DIR__);
174 | $this->object->run(new Request("/admin/missing-action"));
175 | }
176 |
177 | public function testMissingEventManager()
178 | {
179 | $app = new Application();
180 | $eventManager = $app->getEventManager();
181 | $this->assertInstanceOf("EventManager", $eventManager);
182 | }
183 |
184 | public function testLayout()
185 | {
186 | $this->object->bootstrap("layout", function(){
187 | $l = new Layout();
188 | $l->setScriptName("layout.phtml");
189 | $l->setViewPath(__DIR__ . '/layouts');
190 | return $l;
191 | });
192 |
193 | ob_start();
194 | $this->object->run(new Request("/init/index"));
195 | $content = ob_get_contents();
196 | ob_end_clean();
197 |
198 | $this->assertEquals("<-- init -->", $content);
199 | }
200 |
201 | public function testLayoutViewHelpersPass()
202 | {
203 |
204 | $this->object->bootstrap('layout', function(){
205 | $l = new Layout();
206 | $l->setScriptName("title-helper.phtml");
207 | $l->setViewPath(__DIR__ . '/layouts');
208 |
209 |
210 | return $l;
211 | });
212 |
213 | $this->object->bootstrap('view', function(){
214 | $v = new View();
215 | $v->setViewPath(__DIR__ . '/views');
216 |
217 | $v->addHelper("title", function($part = false){
218 | static $parts = array();
219 | static $delimiter = ' :: ';
220 |
221 | return ($part === false) ? implode($delimiter, $parts) : $parts[] = $part;
222 | });
223 |
224 | return $v;
225 | });
226 |
227 | ob_start();
228 | $this->object->run(new Request("/general/title-helper"));
229 | $content = ob_get_contents();
230 | ob_end_clean();
231 |
232 | $this->assertEquals("the title helper :: second ", $content);
233 | }
234 |
235 | public function testEmptyPullDrivenRequest()
236 | {
237 | $this->object->bootstrap("view", function(){
238 | $v = new View();
239 | $v->setViewPath(__DIR__ . '/views');
240 |
241 | return $v;
242 | });
243 |
244 | ob_start();
245 | $this->object->run(new Request("/general/pull-driven"));
246 | $content = ob_get_contents();
247 | ob_end_clean();
248 |
249 | $this->assertEquals("Pull-driven experience
", $content);
250 | }
251 |
252 | public function testCompletelyMissingPullDrivenRequest()
253 | {
254 | $this->object->setControllerPath(null);
255 | $this->object->bootstrap("view", function(){
256 | $v = new View();
257 | $v->setViewPath(__DIR__ . '/views');
258 |
259 | return $v;
260 | });
261 |
262 | ob_start();
263 | $this->object->run(new Request("/pull/driven"));
264 | $content = ob_get_contents();
265 | ob_end_clean();
266 |
267 | $this->assertEquals("Complete pull driven ", $content);
268 | }
269 |
270 | public function testCompletelyMissingPullWithDataDrivenRequest()
271 | {
272 | $this->object->bootstrap("view", function(){
273 | $v = new View();
274 | $v->setViewPath(__DIR__ . '/views');
275 |
276 | return $v;
277 | });
278 |
279 | ob_start();
280 | $this->object->run(new Request("/pull/driven-data"));
281 | $content = ob_get_contents();
282 | ob_end_clean();
283 |
284 | $this->assertEquals("Controller Data ", $content);
285 | }
286 |
287 | public function testMissingControllerPull()
288 | {
289 | $this->object->bootstrap("view", function(){
290 | $v = new View();
291 | $v->setViewPath(__DIR__ . '/views');
292 |
293 | return $v;
294 | });
295 |
296 | ob_start();
297 | $this->object->run(new Request("/pull/missing-pull"));
298 | $content = ob_get_contents();
299 | ob_end_clean();
300 |
301 | $this->assertEquals("--> error action <--", $content);
302 | }
303 |
304 | public function testMissingActionPull()
305 | {
306 | $this->object->bootstrap("view", function(){
307 | $v = new View();
308 | $v->setViewPath(__DIR__ . '/views');
309 |
310 | return $v;
311 | });
312 |
313 | ob_start();
314 | $this->object->run(new Request("/pull/missing-pull-action"));
315 | $content = ob_get_contents();
316 | ob_end_clean();
317 |
318 | $this->assertEquals("--> error action <--", $content);
319 | }
320 |
321 | public function testViewSwitch()
322 | {
323 | $this->object->bootstrap("view", function(){
324 | $v = new View();
325 | $v->setViewPath(__DIR__ . '/views');
326 |
327 | return $v;
328 | });
329 |
330 | ob_start();
331 | $this->object->run(new Request("/general/a"));
332 | $content = ob_get_contents();
333 | ob_end_clean();
334 |
335 | $this->assertEquals("This is B", $content);
336 | }
337 |
338 | public function testViewRewrited()
339 | {
340 | $this->object->bootstrap("view", function(){
341 | $v = new View();
342 | $v->addViewPath(__DIR__ . '/views');
343 | $v->addViewPath(__DIR__ . '/views-rewrite');
344 |
345 | return $v;
346 | });
347 |
348 | ob_start();
349 | $this->object->run(new Request("/general/c"));
350 | $content = ob_get_contents();
351 | ob_end_clean();
352 |
353 | $this->assertEquals("This is C but rewrited", $content);
354 | }
355 |
356 | public function testViewPullRewrited()
357 | {
358 | $this->object->bootstrap("view", function(){
359 | $v = new View();
360 | $v->addViewPath(__DIR__ . '/views');
361 | $v->addViewPath(__DIR__ . '/views-rewrite');
362 |
363 | return $v;
364 | });
365 |
366 | ob_start();
367 | $this->object->run(new Request("/general/d"));
368 | $content = ob_get_contents();
369 | ob_end_clean();
370 |
371 | $this->assertEquals("This is D but rewrited", $content);
372 | }
373 |
374 | public function testPartialViewRewrite()
375 | {
376 | $this->object->bootstrap("view", function(){
377 | $v = new View();
378 | $v->addViewPath(__DIR__ . '/views');
379 | $v->addViewPath(__DIR__ . '/views-rewrite');
380 |
381 | return $v;
382 | });
383 |
384 | ob_start();
385 | $this->object->run(new Request("/general/partial-eg"));
386 | $content = ob_get_contents();
387 | ob_end_clean();
388 |
389 | $this->assertEquals('ciao ', $content);
390 | }
391 |
392 |
393 |
394 | public function testBufferOutPullRequest()
395 | {
396 | $this->object->bootstrap("view", function(){
397 | $v = new View();
398 | $v->addViewPath(__DIR__ . '/views');
399 |
400 | return $v;
401 | });
402 |
403 | ob_start();
404 | $this->object->run(new Request("/pull/buffer-out"));
405 | $content = ob_get_contents();
406 | ob_end_clean();
407 |
408 | $this->assertEquals('ret from out
', $content);
409 | }
410 |
411 | public function testLoopStartupHook()
412 | {
413 | $loopStartup = 0;
414 | $this->object->getEventManager()->subscribe(
415 | "loop.startup", function ($app) use (&$loopStartup){
416 |
417 | $loopStartup += 1;
418 | });
419 |
420 | ob_start();
421 | $this->object->run(new Request("/"));
422 | ob_end_clean();
423 |
424 | $this->assertSame(1, $loopStartup);
425 | }
426 |
427 | public function testLoopShutdownHook()
428 | {
429 | $shutdown = 0;
430 | $this->object->getEventManager()->subscribe(
431 | "loop.shutdown", function ($app) use (&$shutdown) {
432 | $shutdown += 1;
433 | }
434 | );
435 |
436 | ob_start();
437 | $this->object->run(new Request("/"));
438 | ob_end_clean();
439 | $this->assertSame(1, $shutdown);
440 | }
441 |
442 | public function testPrePostDispatch()
443 | {
444 | $preDispatch = 0;
445 | $postDispatch = 0;
446 |
447 | $this->object->getEventManager()->subscribe(
448 | "pre.dispatch", function ($router, $app) use (&$preDispatch) {
449 | ++$preDispatch;
450 | var_dump(1);
451 | }
452 | );
453 |
454 | $this->object->getEventManager()->subscribe(
455 | "post.dispatch", function ($app) use (&$postDispatch) {
456 | ++$postDispatch;
457 | }
458 | );
459 |
460 | ob_start();
461 | $this->object->run(new Request("/admin/login"));
462 | ob_end_clean();
463 |
464 | $this->assertSame(1, $preDispatch);
465 | $this->assertSame(1, $postDispatch);
466 | }
467 |
468 | public function testMultiplePrePostDispatch()
469 | {
470 | $preDispatch = 0;
471 | $postDispatch = 0;
472 |
473 | $this->object->getEventManager()->subscribe(
474 | "pre.dispatch", function ($router, $app) use (&$preDispatch) {
475 | $preDispatch += 1;
476 | }
477 | );
478 |
479 | $this->object->getEventManager()->subscribe(
480 | "post.dispatch", function ($app) use (&$postDispatch) {
481 | ++$postDispatch;
482 | }
483 | );
484 |
485 | ob_start();
486 | $this->object->run(new Request("/then/first"));
487 | ob_end_clean();
488 |
489 | $this->assertSame(2, $preDispatch);
490 | $this->assertSame(2, $postDispatch);
491 | }
492 |
493 | public function testDisableLayout()
494 | {
495 | $this->object->bootstrap("view", function(){
496 | $v = new View();
497 | $v->addViewPath(__DIR__ . '/views');
498 |
499 | return $v;
500 | });
501 |
502 |
503 | $this->object->bootstrap("layout", function(){
504 | $v = new Layout();
505 | $v->addViewPath(__DIR__ . '/layouts');
506 | $v->setScriptName("layout");
507 |
508 | return $v;
509 | });
510 |
511 | ob_start();
512 | $this->object->run(new Request("/general/disable-layout"));
513 | $content = ob_get_contents();
514 | ob_end_clean();
515 |
516 | $this->assertEquals("Only this view...", trim($content));
517 | }
518 | }
519 |
--------------------------------------------------------------------------------