├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── build.xml
├── composer.json
├── coverage-checker.php
├── phpdox.xml.dist
├── phpmd.xml.dist
├── phpunit.xml.dist
├── src
└── ZeffMu
│ ├── App.php
│ ├── ClosureController.php
│ └── Module.php
└── tests
├── .gitignore
└── ZeffMuTest
├── AppFunctionalTest.php
└── AppInitTest.php
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor
2 | composer.phar
3 | composer.lock
4 | .idea
5 | phpunit.xml
6 | build
7 | nbproject
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 5.5
5 | - 5.6
6 |
7 | before_script:
8 | - composer self-update
9 | - composer install --prefer-source
10 | - wget http://cs.sensiolabs.org/get/php-cs-fixer.phar
11 |
12 | script:
13 | - ./vendor/bin/phpunit
14 | - php coverage-checker.php build/logs/clover.xml 1
15 | - output=$(php php-cs-fixer.phar fix -v --dry-run --level=psr2 ./src/); if [[ $output ]]; then while read -r line; do echo -e "\e[00;31m$line\e[00m"; done <<< "$output"; false; fi;
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012-2013 Marco Pivetta, Kathryn Reeve - ZeffMu
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
5 | the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
6 | to permit 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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ZeffMu - a micro framework built on ZF2
2 |
3 | Zeff - The effective nuclear charge (often symbolize as Zeff or Z*) is the net positive charge experienced by an electron in a multi-electron atom.
4 |
5 | Mu - Micron or small
6 |
7 | Zeff is also a contraction of the British English pronounced Zed Eff (ZF)
8 |
9 | --------------------------------------------------------------------------------------------------
10 |
11 | This project is a simple example of how ZF2 could be used to build a
12 | really simple micro-framework. It looks exactly like
13 | [silex](http://silex.sensiolabs.org/), but its core is basically a
14 | [`ServiceManager`](http://framework.zend.com/manual/2.0/en/modules/zend.service-manager.intro.html).
15 |
16 | This allows you to have simple closures returning output as complex
17 | architectures involving services and more advanced components, such as
18 | ZF2's [ModuleManager](http://framework.zend.com/manual/2.0/en/modules/zend.module-manager.intro.html)
19 | or [Doctrine2 ORM](http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/index.html).
20 |
21 | ## Important Notes
22 |
23 | Please note that this is a project started for fun and to see how similar/different the architectures of Silex and ZF2 are.
24 |
25 | We are working on new features and fixes for various things
26 |
27 | We are looking to have preliminary stableness for each Milestone, starting with Milestone v0.0.5 .
28 | Until this 1st milestone has been reached master is to be considered unstable and volatile.
29 |
30 | ## Installation:
31 |
32 | In a project with a `composer.json` file, type following in your console.
33 |
34 | ```sh
35 | $ composer require binarykitten/zeffmu
36 | ```
37 |
38 | You can type `*` as a required version.
39 |
40 | ## Usage:
41 |
42 | In your `public/index.php` file (assuming `public` is your webroot), define
43 | following:
44 |
45 | ```php
46 | route('/', function() {
53 | return 'HEllo!';
54 | })
55 | ->route('/hello', function() {
56 | return 'Hi!';
57 | })
58 | ->route('/hello/:name', function($params) use ($app) {
59 | return 'Hello ' . $params['name'];
60 | })
61 | ->route('/hello/:name/:surname', function($params) use ($app) {
62 | return 'Hello, Mr. ' . $params['surname'] . ', or shall I call you ' . $params['name'] . '?';
63 | })
64 | ->run();
65 | ```
66 |
67 | ## Advantages
68 | * Since the application is a fully functional ZF application, you could also return view
69 | models in your closures, thus having templates rendered.
70 | * You can attach listeners to events like in a standard ZF2 application
71 | * You can fetch services like in a standard MVC application (i.e. `$app->getService('db')` or `$app->getServiceLocator()->get('db')`)
72 | * You can load modules and have any module functionality as in typical ZF2 applications
73 |
74 | ## Limitations (for now)
75 |
76 | * ZeffMu will currently route all HTTP requests, regardless of the HTTP method.
77 | * It does not support things such as filtering output strings natively
78 | * It does not support setting controllers or retrieving them from the internal service
79 | locator (since that would require naming the controllers and basically ending up with
80 | ZF2's MVC). Routing and dispatching is also quite different from ZF2. A fallback may be
81 | interesting.
82 | * It does not support registering routes other than `Zend\Mvc\Router\Http\Part`
83 | * As a default, it has all the service provided in a default ZF2 application
84 | * Cannot define a parameter named "controller" in route matches, since it is reserved
85 | * Assembling routes does not yet work
86 | * Helper methods (view helpers/controller plugins) utilities are not yet accessible in a
87 | simple way from the application object. Some simple shortcuts may help.
88 |
89 | ## Advanced usage
90 |
91 | * TBD: services
92 | * TBD: events
93 |
94 | ## Build status
95 |
96 | [](undefined)
97 |
--------------------------------------------------------------------------------
/build.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "binarykitten/zeffmu",
3 | "description": "Zend Framework 2 based Micro Framework",
4 | "type": "library",
5 | "license": "MIT",
6 | "keywords": [
7 | "framework",
8 | "micro",
9 | "zf2"
10 | ],
11 | "homepage": "https://github.com/binarykitten/ZeffMu",
12 | "authors": [
13 | {
14 | "name": "Marco Pivetta",
15 | "email": "ocramius@gmail.com",
16 | "homepage": "http://marco-pivetta.com/",
17 | "role": "Developer"
18 | },
19 | {
20 | "name": "Kathryn Reeve",
21 | "email": "kathryn@binarykitten.com",
22 | "homepage": "http://binarykitten.com/",
23 | "role": "Developer"
24 | }
25 | ],
26 | "require": {
27 | "php": ">=5.5",
28 | "zendframework/zend-mvc": "~2.6.0",
29 | "zendframework/zend-modulemanager": "~2.6.1",
30 | "zendframework/zend-loader": "~2.5.0",
31 | "zendframework/zend-view": "~2.5.0",
32 | "zendframework/zend-serializer": "~2.5.0",
33 | "zendframework/zend-log": "~2.5.0",
34 | "zendframework/zend-i18n": "~2.5.0",
35 | "zendframework/zend-console": "~2.5.0",
36 | "zendframework/zend-http": "~2.5.0"
37 | },
38 | "require-dev": {
39 | "phpunit/phpunit": "~4.0|~5.0"
40 | },
41 | "autoload": {
42 | "psr-0": {
43 | "ZeffMu": "src/"
44 | }
45 | }
46 | }
47 |
48 |
--------------------------------------------------------------------------------
/coverage-checker.php:
--------------------------------------------------------------------------------
1 | .
18 | */
19 |
20 | /**
21 | * Code coverage checker. Analyzes a given `clover.xml` report produced
22 | * by PHPUnit and checks if coverage fits expected ratio
23 | *
24 | * Usage:
25 | * php coverage-checker
26 | *
27 | * @author Marco Pivetta
28 | */
29 |
30 | $inputFile = $argv[1];
31 | $percentage = min(100, max(0, (int) $argv[2]));
32 |
33 | if (!file_exists($inputFile)) {
34 | throw new InvalidArgumentException('Invalid input file provided');
35 | }
36 |
37 | if (!$percentage) {
38 | throw new InvalidArgumentException('An integer checked percentage must be given as second parameter');
39 | }
40 |
41 | $xml = new SimpleXMLElement(file_get_contents($inputFile));
42 | /* @var $metrics SimpleXMLElement[] */
43 | $metrics = $xml->xpath('//metrics');
44 |
45 | $totalElements = 0;
46 | $checkedElements = 0;
47 |
48 | foreach ($metrics as $metric) {
49 | $totalElements += (int) $metric['elements'];
50 | $checkedElements += (int) $metric['coveredelements'];
51 | }
52 |
53 | $coverage = round(($checkedElements / $totalElements) * 100);
54 |
55 | if ($coverage < $percentage) {
56 | echo 'Code coverage is ' . $coverage . '%, which is below the accepted ' . $percentage . '%' . PHP_EOL;
57 | exit(1);
58 | }
59 |
60 | echo 'Code coverage is ' . $coverage . '% - OK!' . PHP_EOL;
61 |
--------------------------------------------------------------------------------
/phpdox.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/phpmd.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 | 1
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 | ./tests
16 |
17 |
18 |
19 | ./src
20 |
21 |
22 |
23 |
32 |
36 |
41 |
42 |
--------------------------------------------------------------------------------
/src/ZeffMu/App.php:
--------------------------------------------------------------------------------
1 | .
18 | */
19 |
20 | namespace ZeffMu;
21 |
22 | use ZeffMu\ClosureController;
23 | use Zend\Mvc\Application as ZfApplication;
24 | use Zend\Mvc\Router\RouteInterface;
25 | use Zend\Stdlib\ArrayUtils;
26 |
27 | /**
28 | * Zend Framework 2 based micro-framework application
29 | *
30 | * @license MIT
31 | * @author Marco Pivetta
32 | * @author Kathryn Reeve
33 | */
34 | class App extends ZfApplication
35 | {
36 | /**
37 | * @param string|RouteInterface $route
38 | * @param Closure|String $controller
39 | *
40 | * @return $this
41 | */
42 | public function route($route, $controller)
43 | {
44 | $sm = $this->getServiceManager();
45 | /* @var $cpm \Zend\Mvc\Controller\ControllerManager */
46 | $cpm = $sm->get('ControllerLoader');
47 |
48 | if ($controller instanceof \Closure) {
49 | $wrappedController = new ClosureController($controller);
50 | $controller = "ZeffMu\\Controllers\\" . md5($route);
51 | $cpm->setFactory(
52 | $controller,
53 | function () use ($wrappedController) {
54 | return $wrappedController;
55 | }
56 | );
57 | }
58 |
59 | $sm
60 | ->get('Router')
61 | ->addRoute(
62 | $route,
63 | array(
64 | 'type' => 'Zend\Mvc\Router\Http\Segment',
65 | 'options' => array(
66 | 'route' => $route,
67 | 'defaults' => array(
68 | 'controller' => $controller,
69 | ),
70 | ),
71 | )
72 | );
73 |
74 | return $this;
75 | }
76 |
77 | /**
78 | * {@inheritDoc}
79 | * @return self
80 | */
81 | public static function init($configuration = array())
82 | {
83 |
84 | $defaults = array(
85 | 'module_listener_options' => array(),
86 | 'modules' => array(),
87 | 'service_manager' => array(),
88 | );
89 |
90 | $configuration = ArrayUtils::merge($defaults, $configuration);
91 |
92 | $configuration['modules'][] = 'ZeffMu';
93 |
94 | return parent::init($configuration);
95 | }
96 |
97 | /**
98 | * @param $service
99 | *
100 | * @return array|object
101 | */
102 | public function getService($service)
103 | {
104 | return $this->getServiceManager()->get($service);
105 | }
106 |
107 | /**
108 | * Launch the app
109 | */
110 | public function __invoke()
111 | {
112 | $this->run();
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/ZeffMu/ClosureController.php:
--------------------------------------------------------------------------------
1 | .
19 | */
20 |
21 | namespace ZeffMu;
22 |
23 | use Closure;
24 | use Zend\Mvc\Controller\AbstractController;
25 | use Zend\Mvc\MvcEvent;
26 | use Zend\View\Model\ViewModel;
27 |
28 | /**
29 | * Description of ClosureController
30 | *
31 | * @author Kathryn Reeve
32 | */
33 | class ClosureController extends AbstractController
34 | {
35 |
36 | /**
37 | * The closure we are wrapping
38 | * @var Closure $closure
39 | */
40 | protected $closure = null;
41 |
42 | /**
43 | * Constructor
44 | *
45 | * @param Closure $closure
46 | */
47 | public function __construct(Closure $closure)
48 | {
49 | $this->closure = $closure;
50 | }
51 |
52 | /**
53 | *
54 | * @param \Zend\Mvc\MvcEvent $e
55 | *
56 | * @return misc
57 | */
58 | public function onDispatch(MvcEvent $e)
59 | {
60 | $routeMatch = $e->getRouteMatch();
61 | $application = $e->getApplication();
62 | $request = $e->getRequest();
63 | $response = $application->getResponse();
64 |
65 | $closure = $this->closure->bindTo($this);
66 |
67 | $result = $closure(
68 | $routeMatch->getParams(), $request, $response
69 | );
70 |
71 | if (is_array($result)) {
72 | $result = new ViewModel($result);
73 | $result->setTemplate($template);
74 | } elseif ( ! ( $result instanceof ViewModel )) {
75 | $response->setContent($result);
76 |
77 | return $response;
78 | }
79 |
80 | $e->setResult($result);
81 |
82 | return $result;
83 | }
84 |
85 | }
86 |
--------------------------------------------------------------------------------
/src/ZeffMu/Module.php:
--------------------------------------------------------------------------------
1 | .
18 | */
19 |
20 | namespace ZeffMu;
21 |
22 | use ZeffMu\App;
23 | use Zend\ModuleManager\Feature\ServiceProviderInterface;
24 | use Zend\Mvc\Router\Http\TreeRouteStack as HttpRouter;
25 | use Zend\ServiceManager\ServiceLocatorInterface;
26 |
27 | class Module implements ServiceProviderInterface
28 | {
29 | /**
30 | * {@inheritDoc}
31 | */
32 | public function getServiceConfig()
33 | {
34 | return array(
35 | 'factories' => array(
36 | 'Application' => function (ServiceLocatorInterface $sl) {
37 | return new App($sl->get('Config'), $sl);
38 | },
39 | 'Router' => function () {
40 | return HttpRouter::factory(array());
41 | },
42 | 'HttpRouter' => function (ServiceLocatorInterface $sl) {
43 | return $sl->get('Router');
44 | }
45 | ),
46 | );
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/tests/.gitignore:
--------------------------------------------------------------------------------
1 | TestConfiguration.php
2 |
--------------------------------------------------------------------------------
/tests/ZeffMuTest/AppFunctionalTest.php:
--------------------------------------------------------------------------------
1 | .
18 | */
19 |
20 | namespace ZeffMuTest;
21 |
22 | use PHPUnit_Framework_TestCase;
23 | use ZeffMu\App;
24 | use Zend\Console\Console;
25 | use Zend\EventManager\EventInterface;
26 | use Zend\Http\Request;
27 | use Zend\Mvc\MvcEvent;
28 | use Zend\Stdlib\RequestInterface;
29 | use Zend\Stdlib\ResponseInterface;
30 |
31 | /**
32 | * Functional test demonstrating the features of a ZeffMu application
33 | *
34 | * @license MIT
35 | * @author Marco Pivetta
36 | */
37 | class AppFunctionalTest extends PHPUnit_Framework_TestCase
38 | {
39 | public function testSimpleClosureController()
40 | {
41 | Console::overrideIsConsole(false);
42 |
43 | $app = App::init();
44 | $test = $this;
45 | $appRequest = new Request();
46 | $outerTestResponse = null;
47 |
48 | $appRequest->setUri('http://localhost/test/blah');
49 | $app->getMvcEvent()->setRequest($appRequest);
50 |
51 | $app->route(
52 | '/test/:param1',
53 | function (array $params, RequestInterface $req, ResponseInterface $res)
54 | use ($test, $appRequest, &$outerTestResponse) {
55 | $test->assertArrayHasKey('param1', $params);
56 | $test->assertSame($appRequest, $req);
57 |
58 | $outerTestResponse = $res;
59 |
60 | return 'Hello world!';
61 | }
62 | );
63 |
64 | // overriding send response listener
65 | $app->getEventManager()->attach(
66 | MvcEvent::EVENT_FINISH,
67 | function (EventInterface $e) {
68 | $e->stopPropagation();
69 | },
70 | 1000
71 | );
72 |
73 | $appRunReturn = $app->run();
74 | $this->assertSame($app, $appRunReturn);
75 | $this->assertSame($app->getResponse(), $outerTestResponse);
76 | $this->assertSame('Hello world!', $app->getResponse()->getContent());
77 | }
78 |
79 | /**
80 | * @requires PHP 5.4
81 | */
82 | public function testClosureThisIsControllerInstance()
83 | {
84 | Console::overrideIsConsole(false);
85 |
86 | $app = App::init();
87 | $test = $this;
88 | $appRequest = new Request();
89 |
90 | $appRequest->setUri('http://localhost/test/blah');
91 | $app->getMvcEvent()->setRequest($appRequest);
92 |
93 | $app->route(
94 | '/test/:param1',
95 | function () use ($test) {
96 | $test->assertInstanceOf('ZeffMu\ClosureController', $this);
97 | return 'test';
98 | }
99 | );
100 |
101 | // overriding send response listener
102 | $app->getEventManager()->attach(
103 | MvcEvent::EVENT_FINISH,
104 | function (EventInterface $e) {
105 | $e->stopPropagation();
106 | },
107 | 1000
108 | );
109 |
110 | $app->run();
111 | }
112 |
113 | /**
114 | * @group #24
115 | *
116 | * @requires PHP 5.4
117 | */
118 | public function testControllerPluginsAreCorrectlyInjectedInFactories()
119 | {
120 | $helper = $this->getMock(
121 | 'Zend\Mvc\Controller\Plugin\PluginInterface',
122 | array('setController', 'getController', '__invoke')
123 | );
124 |
125 | $request = new Request();
126 | $app = App::init();
127 | /* @var $plugins \Zend\ServiceManager\AbstractPluginManager */
128 | $plugins = $app->getServiceManager()->get('ControllerPluginManager');
129 |
130 | $request->setUri('http://localhost/something');
131 | $app->getMvcEvent()->setRequest($request);
132 |
133 | $plugins->setService('foo', $helper);
134 |
135 | $app->route('/something', function () {
136 | $this->foo('bar');
137 | });
138 |
139 | $helper->expects(self::once())->method('__invoke')->with('bar');
140 |
141 | // overriding send response listener
142 | $app->getEventManager()->attach(
143 | MvcEvent::EVENT_FINISH,
144 | function (EventInterface $e) {
145 | $e->stopPropagation();
146 | },
147 | 1000
148 | );
149 |
150 | $app->run();
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/tests/ZeffMuTest/AppInitTest.php:
--------------------------------------------------------------------------------
1 | .
18 | */
19 |
20 | namespace ZeffMuTest;
21 |
22 | use PHPUnit_Framework_TestCase;
23 | use ZeffMu\App;
24 |
25 | use Zend\Console\Console;
26 | use Zend\EventManager\EventInterface;
27 | use Zend\Http\Request;
28 | use Zend\Mvc\MvcEvent;
29 | use Zend\Stdlib\RequestInterface;
30 | use Zend\Stdlib\ResponseInterface;
31 |
32 | /**
33 | * Application initailize tests to cover the start up and routing
34 | *
35 | * @license MIT
36 | * @author Marco Pivetta
37 | * @author Kathryn Reeve
38 | */
39 | class AppInitTest extends PHPUnit_Framework_TestCase
40 | {
41 | public function testAppReturnedByInit()
42 | {
43 | $app = App::init();
44 | $this->assertInstanceOf('ZeffMu\App', $app);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------