* initial commit
This commit is contained in:
commit
ff2721f67c
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
.idea
|
||||
composer.phar
|
||||
|
||||
clover.xml
|
||||
coveralls-upload.json
|
||||
phpunit.xml
|
||||
vendor/
|
||||
_local/
|
||||
28
LICENSE.md
Normal file
28
LICENSE.md
Normal file
@ -0,0 +1,28 @@
|
||||
Copyright (c) 2015-2018, Zend Technologies USA, Inc.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
- Neither the name of Zend Technologies USA, Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
150
README.md
Normal file
150
README.md
Normal file
@ -0,0 +1,150 @@
|
||||
# Expressive Skeleton and Installer
|
||||
|
||||
[](https://secure.travis-ci.org/zendframework/zend-expressive-skeleton)
|
||||
[](https://coveralls.io/github/zendframework/zend-expressive-skeleton?branch=master)
|
||||
|
||||
*Begin developing PSR-15 middleware applications in seconds!*
|
||||
|
||||
[zend-expressive](https://github.com/zendframework/zend-expressive) builds on
|
||||
[zend-stratigility](https://github.com/zendframework/zend-stratigility) to
|
||||
provide a minimalist PSR-15 middleware framework for PHP with routing, DI
|
||||
container, optional templating, and optional error handling capabilities.
|
||||
|
||||
This installer will setup a skeleton application based on zend-expressive by
|
||||
choosing optional packages based on user input as demonstrated in the following
|
||||
screenshot:
|
||||
|
||||

|
||||
|
||||
The user selected packages are saved into `composer.json` so that everyone else
|
||||
working on the project have the same packages installed. Configuration files and
|
||||
templates are prepared for first use. The installer command is removed from
|
||||
`composer.json` after setup succeeded, and all installer related files are
|
||||
removed.
|
||||
|
||||
## Getting Started
|
||||
|
||||
Start your new Expressive project with composer:
|
||||
|
||||
```bash
|
||||
$ composer create-project zendframework/zend-expressive-skeleton <project-path>
|
||||
```
|
||||
|
||||
After choosing and installing the packages you want, go to the
|
||||
`<project-path>` and start PHP's built-in web server to verify installation:
|
||||
|
||||
```bash
|
||||
$ composer run --timeout=0 serve
|
||||
```
|
||||
|
||||
You can then browse to http://localhost:8080.
|
||||
|
||||
> ### Linux users
|
||||
>
|
||||
> On PHP versions prior to 7.1.14 and 7.2.2, this command might not work as
|
||||
> expected due to a bug in PHP that only affects linux environments. In such
|
||||
> scenarios, you will need to start the [built-in web
|
||||
> server](http://php.net/manual/en/features.commandline.webserver.php) yourself,
|
||||
> using the following command:
|
||||
>
|
||||
> ```bash
|
||||
> $ php -S 0.0.0.0:8080 -t public/ public/index.php
|
||||
> ```
|
||||
|
||||
> ### Setting a timeout
|
||||
>
|
||||
> Composer commands time out after 300 seconds (5 minutes). On Linux-based
|
||||
> systems, the `php -S` command that `composer serve` spawns continues running
|
||||
> as a background process, but on other systems halts when the timeout occurs.
|
||||
>
|
||||
> As such, we recommend running the `serve` script using a timeout. This can
|
||||
> be done by using `composer run` to execute the `serve` script, with a
|
||||
> `--timeout` option. When set to `0`, as in the previous example, no timeout
|
||||
> will be used, and it will run until you cancel the process (usually via
|
||||
> `Ctrl-C`). Alternately, you can specify a finite timeout; as an example,
|
||||
> the following will extend the timeout to a full day:
|
||||
>
|
||||
> ```bash
|
||||
> $ composer run --timeout=86400 serve
|
||||
> ```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the installer fails during the ``composer create-project`` phase, please go
|
||||
through the following list before opening a new issue. Most issues we have seen
|
||||
so far can be solved by `self-update` and `clear-cache`.
|
||||
|
||||
1. Be sure to work with the latest version of composer by running `composer self-update`.
|
||||
2. Try clearing Composer's cache by running `composer clear-cache`.
|
||||
|
||||
If neither of the above help, you might face more serious issues:
|
||||
|
||||
- Info about the [zlib_decode error](https://github.com/composer/composer/issues/4121).
|
||||
- Info and solutions for [composer degraded mode](https://getcomposer.org/doc/articles/troubleshooting.md#degraded-mode).
|
||||
|
||||
## Application Development Mode Tool
|
||||
|
||||
This skeleton comes with [zf-development-mode](https://github.com/zfcampus/zf-development-mode).
|
||||
It provides a composer script to allow you to enable and disable development mode.
|
||||
|
||||
### To enable development mode
|
||||
|
||||
**Note:** Do NOT run development mode on your production server!
|
||||
|
||||
```bash
|
||||
$ composer development-enable
|
||||
```
|
||||
|
||||
**Note:** Enabling development mode will also clear your configuration cache, to
|
||||
allow safely updating dependencies and ensuring any new configuration is picked
|
||||
up by your application.
|
||||
|
||||
### To disable development mode
|
||||
|
||||
```bash
|
||||
$ composer development-disable
|
||||
```
|
||||
|
||||
### Development mode status
|
||||
|
||||
```bash
|
||||
$ composer development-status
|
||||
```
|
||||
|
||||
## Configuration caching
|
||||
|
||||
By default, the skeleton will create a configuration cache in
|
||||
`data/config-cache.php`. When in development mode, the configuration cache is
|
||||
disabled, and switching in and out of development mode will remove the
|
||||
configuration cache.
|
||||
|
||||
You may need to clear the configuration cache in production when deploying if
|
||||
you deploy to the same directory. You may do so using the following:
|
||||
|
||||
```bash
|
||||
$ composer clear-config-cache
|
||||
```
|
||||
|
||||
You may also change the location of the configuration cache itself by editing
|
||||
the `config/config.php` file and changing the `config_cache_path` entry of the
|
||||
local `$cacheConfig` variable.
|
||||
|
||||
## Skeleton Development
|
||||
|
||||
This section applies only if you cloned this repo with `git clone`, not when you
|
||||
installed expressive with `composer create-project ...`.
|
||||
|
||||
If you want to run tests against the installer, you need to clone this repo and
|
||||
setup all dependencies with composer. Make sure you **prevent composer running
|
||||
scripts** with `--no-scripts`, otherwise it will remove the installer and all
|
||||
tests.
|
||||
|
||||
```bash
|
||||
$ composer update --no-scripts
|
||||
$ composer test
|
||||
```
|
||||
|
||||
Please note that the installer tests remove installed config files and templates
|
||||
before and after running the tests.
|
||||
|
||||
Before contributing read [the contributing guide](docs/CONTRIBUTING.md).
|
||||
48
bin/clear-config-cache.php
Normal file
48
bin/clear-config-cache.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/**
|
||||
* Script for clearing the configuration cache.
|
||||
*
|
||||
* Can also be invoked as `composer clear-config-cache`.
|
||||
*
|
||||
* @see https://github.com/zendframework/zend-expressive-skeleton for the canonical source repository
|
||||
* @copyright Copyright (c) 2017 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license https://github.com/zendframework/zend-expressive-skeleton/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
chdir(__DIR__ . '/../');
|
||||
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
$config = include 'config/config.php';
|
||||
|
||||
if (! isset($config['config_cache_path'])) {
|
||||
echo "No configuration cache path found" . PHP_EOL;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (! file_exists($config['config_cache_path'])) {
|
||||
printf(
|
||||
"Configured config cache file '%s' not found%s",
|
||||
$config['config_cache_path'],
|
||||
PHP_EOL
|
||||
);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (false === unlink($config['config_cache_path'])) {
|
||||
printf(
|
||||
"Error removing config cache file '%s'%s",
|
||||
$config['config_cache_path'],
|
||||
PHP_EOL
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf(
|
||||
"Removed configured config cache file '%s'%s",
|
||||
$config['config_cache_path'],
|
||||
PHP_EOL
|
||||
);
|
||||
exit(0);
|
||||
92
composer.json
Normal file
92
composer.json
Normal file
@ -0,0 +1,92 @@
|
||||
{
|
||||
"name": "zendframework/zend-expressive-skeleton",
|
||||
"description": "Zend expressive skeleton. Begin developing PSR-15 middleware applications in seconds!",
|
||||
"type": "project",
|
||||
"homepage": "https://github.com/zendframework/zend-expressive-skeleton",
|
||||
"license": "BSD-3-Clause",
|
||||
"keywords": [
|
||||
"skeleton",
|
||||
"middleware",
|
||||
"psr",
|
||||
"psr-7",
|
||||
"psr-11",
|
||||
"psr-15",
|
||||
"zf",
|
||||
"zendframework",
|
||||
"zend-expressive"
|
||||
],
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
},
|
||||
"extra": {
|
||||
"zf": {
|
||||
"component-whitelist": [
|
||||
"zendframework/zend-expressive",
|
||||
"zendframework/zend-expressive-helpers",
|
||||
"zendframework/zend-expressive-router",
|
||||
"zendframework/zend-httphandlerrunner",
|
||||
"zendframework/zend-expressive-fastroute",
|
||||
"zendframework/zend-expressive-platesrenderer"
|
||||
]
|
||||
}
|
||||
},
|
||||
"support": {
|
||||
"issues": "https://github.com/zendframework/zend-expressive-skeleton/issues",
|
||||
"source": "https://github.com/zendframework/zend-expressive-skeleton",
|
||||
"rss": "https://github.com/zendframework/zend-expressive-skeleton/releases.atom",
|
||||
"slack": "https://zendframework-slack.herokuapp.com",
|
||||
"forum": "https://discourse.zendframework.com/c/questions/expressive"
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1",
|
||||
"knplabs/knp-menu": "^2.3",
|
||||
"roave/security-advisories": "dev-master",
|
||||
"zendframework/zend-component-installer": "^2.1.1",
|
||||
"zendframework/zend-config-aggregator": "^1.0",
|
||||
"zendframework/zend-diactoros": "^1.7.1",
|
||||
"zendframework/zend-expressive": "^3.0.1",
|
||||
"zendframework/zend-expressive-fastroute": "^3.0",
|
||||
"zendframework/zend-expressive-helpers": "^5.0",
|
||||
"zendframework/zend-expressive-platesrenderer": "^2.0",
|
||||
"zendframework/zend-servicemanager": "^3.3",
|
||||
"zendframework/zend-stdlib": "^3.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.0.1",
|
||||
"squizlabs/php_codesniffer": "^2.9.1",
|
||||
"zendframework/zend-expressive-tooling": "^1.0",
|
||||
"zfcampus/zf-development-mode": "^3.1",
|
||||
"filp/whoops": "^2.1.12"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\": "src/App/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"AppTest\\": "test/AppTest/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"post-create-project-cmd": [
|
||||
"@development-enable"
|
||||
],
|
||||
"development-disable": "zf-development-mode disable",
|
||||
"development-enable": "zf-development-mode enable",
|
||||
"development-status": "zf-development-mode status",
|
||||
"expressive": "expressive --ansi",
|
||||
"check": [
|
||||
"@cs-check",
|
||||
"@test",
|
||||
"@analyze"
|
||||
],
|
||||
"analyze": "phpstan analyze -l max -c ./phpstan.installer.neon ./src ./config",
|
||||
"clear-config-cache": "php bin/clear-config-cache.php",
|
||||
"cs-check": "phpcs",
|
||||
"cs-fix": "phpcbf",
|
||||
"serve": "php -S 0.0.0.0:8080 -t public/",
|
||||
"test": "phpunit --colors=always",
|
||||
"test-coverage": "phpunit --colors=always --coverage-clover clover.xml"
|
||||
}
|
||||
}
|
||||
3564
composer.lock
generated
Normal file
3564
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
1
config/.gitignore
vendored
Normal file
1
config/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
development.config.php
|
||||
2
config/autoload/.gitignore
vendored
Normal file
2
config/autoload/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
local.php
|
||||
*.local.php
|
||||
26
config/autoload/dependencies.global.php
Normal file
26
config/autoload/dependencies.global.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
// Provides application-wide services.
|
||||
// We recommend using fully-qualified class names whenever possible as
|
||||
// service names.
|
||||
'dependencies' => [
|
||||
// Use 'aliases' to alias a service name to another service. The
|
||||
// key is the alias name, the value is the service to which it points.
|
||||
'aliases' => [
|
||||
// Fully\Qualified\ClassOrInterfaceName::class => Fully\Qualified\ClassName::class,
|
||||
],
|
||||
// Use 'invokables' for constructor-less services, or services that do
|
||||
// not require arguments to the constructor. Map a service name to the
|
||||
// class name.
|
||||
'invokables' => [
|
||||
// Fully\Qualified\InterfaceName::class => Fully\Qualified\ClassName::class,
|
||||
],
|
||||
// Use 'factories' for services provided by callbacks/factory classes.
|
||||
'factories' => [
|
||||
// Fully\Qualified\ClassName::class => Fully\Qualified\FactoryName::class,
|
||||
],
|
||||
],
|
||||
];
|
||||
35
config/autoload/development.local.php.dist
Normal file
35
config/autoload/development.local.php.dist
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/**
|
||||
* Development-only configuration.
|
||||
*
|
||||
* Put settings you want enabled when under development mode in this file, and
|
||||
* check it into your repository.
|
||||
*
|
||||
* Developers on your team will then automatically enable them by calling on
|
||||
* `composer development-enable`.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Zend\Expressive\Container;
|
||||
use Zend\Expressive\Middleware\ErrorResponseGenerator;
|
||||
|
||||
return [
|
||||
'dependencies' => [
|
||||
'invokables' => [
|
||||
],
|
||||
'factories' => [
|
||||
ErrorResponseGenerator::class => Container\WhoopsErrorResponseGeneratorFactory::class,
|
||||
'Zend\Expressive\Whoops' => Container\WhoopsFactory::class,
|
||||
'Zend\Expressive\WhoopsPageHandler' => Container\WhoopsPageHandlerFactory::class,
|
||||
],
|
||||
],
|
||||
|
||||
'whoops' => [
|
||||
'json_exceptions' => [
|
||||
'display' => true,
|
||||
'show_trace' => true,
|
||||
'ajax_only' => true,
|
||||
],
|
||||
],
|
||||
];
|
||||
12
config/autoload/local.php.dist
Normal file
12
config/autoload/local.php.dist
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
/**
|
||||
* Local configuration.
|
||||
*
|
||||
* Copy this file to `local.php` and change its settings as required.
|
||||
* `local.php` is ignored by git and safe to use for local and sensitive data like usernames and passwords.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
];
|
||||
11
config/autoload/navigation.global.php
Normal file
11
config/autoload/navigation.global.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'plates' => [
|
||||
'extensions' => [
|
||||
App\Plates\NavigationExtension::class,
|
||||
]
|
||||
],
|
||||
];
|
||||
25
config/autoload/zend-expressive.global.php
Normal file
25
config/autoload/zend-expressive.global.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Zend\ConfigAggregator\ConfigAggregator;
|
||||
|
||||
return [
|
||||
// Toggle the configuration cache. Set this to boolean false, or remove the
|
||||
// directive, to disable configuration caching. Toggling development mode
|
||||
// will also disable it by default; clear the configuration cache using
|
||||
// `composer clear-config-cache`.
|
||||
ConfigAggregator::ENABLE_CACHE => true,
|
||||
|
||||
// Enable debugging; typically used to provide debugging information within templates.
|
||||
'debug' => false,
|
||||
|
||||
'zend-expressive' => [
|
||||
// Provide templates for the error handling middleware to use when
|
||||
// generating responses.
|
||||
'error_handler' => [
|
||||
'template_404' => 'error::404',
|
||||
'template_error' => 'error::error',
|
||||
],
|
||||
],
|
||||
];
|
||||
41
config/config.php
Normal file
41
config/config.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Zend\ConfigAggregator\ArrayProvider;
|
||||
use Zend\ConfigAggregator\ConfigAggregator;
|
||||
use Zend\ConfigAggregator\PhpFileProvider;
|
||||
|
||||
// To enable or disable caching, set the `ConfigAggregator::ENABLE_CACHE` boolean in
|
||||
// `config/autoload/local.php`.
|
||||
$cacheConfig = [
|
||||
'config_cache_path' => 'data/cache/config-cache.php',
|
||||
];
|
||||
|
||||
$aggregator = new ConfigAggregator([
|
||||
\Zend\Expressive\Router\FastRouteRouter\ConfigProvider::class,
|
||||
\Zend\HttpHandlerRunner\ConfigProvider::class,
|
||||
\Zend\Expressive\Plates\ConfigProvider::class,
|
||||
// Include cache configuration
|
||||
new ArrayProvider($cacheConfig),
|
||||
|
||||
\Zend\Expressive\Helper\ConfigProvider::class,
|
||||
\Zend\Expressive\ConfigProvider::class,
|
||||
\Zend\Expressive\Router\ConfigProvider::class,
|
||||
|
||||
// Default App module config
|
||||
App\ConfigProvider::class,
|
||||
|
||||
// Load application config in a pre-defined order in such a way that local settings
|
||||
// overwrite global settings. (Loaded as first to last):
|
||||
// - `global.php`
|
||||
// - `*.global.php`
|
||||
// - `local.php`
|
||||
// - `*.local.php`
|
||||
new PhpFileProvider(realpath(__DIR__) . '/autoload/{{,*.}global,{,*.}local}.php'),
|
||||
|
||||
// Load development config if it exists
|
||||
new PhpFileProvider(realpath(__DIR__) . '/development.config.php'),
|
||||
], $cacheConfig['config_cache_path']);
|
||||
|
||||
return $aggregator->getMergedConfig();
|
||||
14
config/container.php
Normal file
14
config/container.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Zend\ServiceManager\ServiceManager;
|
||||
|
||||
// Load configuration
|
||||
$config = require __DIR__ . '/config.php';
|
||||
|
||||
$dependencies = $config['dependencies'];
|
||||
$dependencies['services']['config'] = $config;
|
||||
|
||||
// Build container
|
||||
return new ServiceManager($dependencies);
|
||||
30
config/development.config.php.dist
Normal file
30
config/development.config.php.dist
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/**
|
||||
* File required to allow enablement of development mode.
|
||||
*
|
||||
* For use with the zf-development-mode tool.
|
||||
*
|
||||
* Usage:
|
||||
* $ composer development-disable
|
||||
* $ composer development-enable
|
||||
* $ composer development-status
|
||||
*
|
||||
* DO NOT MODIFY THIS FILE.
|
||||
*
|
||||
* Provide your own development-mode settings by editing the file
|
||||
* `config/autoload/development.local.php.dist`.
|
||||
*
|
||||
* Because this file is aggregated last, it simply ensures:
|
||||
*
|
||||
* - The `debug` flag is _enabled_.
|
||||
* - Configuration caching is _disabled_.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Zend\ConfigAggregator\ConfigAggregator;
|
||||
|
||||
return [
|
||||
'debug' => true,
|
||||
ConfigAggregator::ENABLE_CACHE => false,
|
||||
];
|
||||
76
config/pipeline.php
Normal file
76
config/pipeline.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Zend\Expressive\Application;
|
||||
use Zend\Expressive\Handler\NotFoundHandler;
|
||||
use Zend\Expressive\Helper\ServerUrlMiddleware;
|
||||
use Zend\Expressive\Helper\UrlHelperMiddleware;
|
||||
use Zend\Expressive\MiddlewareFactory;
|
||||
use Zend\Expressive\Router\Middleware\DispatchMiddleware;
|
||||
use Zend\Expressive\Router\Middleware\ImplicitHeadMiddleware;
|
||||
use Zend\Expressive\Router\Middleware\ImplicitOptionsMiddleware;
|
||||
use Zend\Expressive\Router\Middleware\MethodNotAllowedMiddleware;
|
||||
use Zend\Expressive\Router\Middleware\RouteMiddleware;
|
||||
use Zend\Stratigility\Middleware\ErrorHandler;
|
||||
|
||||
/**
|
||||
* Setup middleware pipeline:
|
||||
*/
|
||||
return function (Application $app, MiddlewareFactory $factory, ContainerInterface $container) : void {
|
||||
// The error handler should be the first (most outer) middleware to catch
|
||||
// all Exceptions.
|
||||
$app->pipe(ErrorHandler::class);
|
||||
$app->pipe(ServerUrlMiddleware::class);
|
||||
|
||||
// Pipe more middleware here that you want to execute on every request:
|
||||
// - bootstrapping
|
||||
// - pre-conditions
|
||||
// - modifications to outgoing responses
|
||||
//
|
||||
// Piped Middleware may be either callables or service names. Middleware may
|
||||
// also be passed as an array; each item in the array must resolve to
|
||||
// middleware eventually (i.e., callable or service name).
|
||||
//
|
||||
// Middleware can be attached to specific paths, allowing you to mix and match
|
||||
// applications under a common domain. The handlers in each middleware
|
||||
// attached this way will see a URI with the matched path segment removed.
|
||||
//
|
||||
// i.e., path of "/api/member/profile" only passes "/member/profile" to $apiMiddleware
|
||||
// - $app->pipe('/api', $apiMiddleware);
|
||||
// - $app->pipe('/docs', $apiDocMiddleware);
|
||||
// - $app->pipe('/files', $filesMiddleware);
|
||||
|
||||
// Register the routing middleware in the middleware pipeline.
|
||||
// This middleware registers the Zend\Expressive\Router\RouteResult request attribute.
|
||||
$app->pipe(RouteMiddleware::class);
|
||||
|
||||
// The following handle routing failures for common conditions:
|
||||
// - HEAD request but no routes answer that method
|
||||
// - OPTIONS request but no routes answer that method
|
||||
// - method not allowed
|
||||
// Order here matters; the MethodNotAllowedMiddleware should be placed
|
||||
// after the Implicit*Middleware.
|
||||
$app->pipe(ImplicitHeadMiddleware::class);
|
||||
$app->pipe(ImplicitOptionsMiddleware::class);
|
||||
$app->pipe(MethodNotAllowedMiddleware::class);
|
||||
|
||||
// Seed the UrlHelper with the routing results:
|
||||
$app->pipe(UrlHelperMiddleware::class);
|
||||
|
||||
// Add more middleware here that needs to introspect the routing results; this
|
||||
// might include:
|
||||
//
|
||||
// - route-based authentication
|
||||
// - route-based validation
|
||||
// - etc.
|
||||
|
||||
// Register the dispatch middleware in the middleware pipeline
|
||||
$app->pipe(DispatchMiddleware::class);
|
||||
|
||||
// At this point, if no Response is returned by any middleware, the
|
||||
// NotFoundHandler kicks in; alternately, you can provide other fallback
|
||||
// middleware to execute.
|
||||
$app->pipe(NotFoundHandler::class);
|
||||
};
|
||||
48
config/routes.php
Normal file
48
config/routes.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Zend\Expressive\Application;
|
||||
use Zend\Expressive\MiddlewareFactory;
|
||||
|
||||
/**
|
||||
* Setup routes with a single request method:
|
||||
*
|
||||
* $app->get('/', App\Handler\HomePageHandler::class, 'home');
|
||||
* $app->post('/album', App\Handler\AlbumCreateHandler::class, 'album.create');
|
||||
* $app->put('/album/:id', App\Handler\AlbumUpdateHandler::class, 'album.put');
|
||||
* $app->patch('/album/:id', App\Handler\AlbumUpdateHandler::class, 'album.patch');
|
||||
* $app->delete('/album/:id', App\Handler\AlbumDeleteHandler::class, 'album.delete');
|
||||
*
|
||||
* Or with multiple request methods:
|
||||
*
|
||||
* $app->route('/contact', App\Handler\ContactHandler::class, ['GET', 'POST', ...], 'contact');
|
||||
*
|
||||
* Or handling all request methods:
|
||||
*
|
||||
* $app->route('/contact', App\Handler\ContactHandler::class)->setName('contact');
|
||||
*
|
||||
* or:
|
||||
*
|
||||
* $app->route(
|
||||
* '/contact',
|
||||
* App\Handler\ContactHandler::class,
|
||||
* Zend\Expressive\Router\Route::HTTP_METHOD_ANY,
|
||||
* 'contact'
|
||||
* );
|
||||
*/
|
||||
return function (Application $app, MiddlewareFactory $factory, ContainerInterface $container) : void {
|
||||
$app->get('/', App\Handler\HomePageHandler::class, 'home');
|
||||
$app->get('/api/ping', App\Handler\PingHandler::class, 'api.ping');
|
||||
|
||||
$app->get('/the-prize', App\Handler\HomePageHandler::class, 'the-prize');
|
||||
$app->get('/the-prize/background-and-purpose', App\Handler\HomePageHandler::class, 'the-prize.bg');
|
||||
$app->get('/the-prize/description-and-values', App\Handler\HomePageHandler::class, 'the-prize.desc');
|
||||
$app->get('/the-prize/aspect-for-selection', App\Handler\HomePageHandler::class, 'the-prize.aspect');
|
||||
$app->get('/the-prize/gran-prize-award-events', App\Handler\HomePageHandler::class, 'the-prize.events');
|
||||
|
||||
$app->get('/judges', App\Handler\HomePageHandler::class, 'judges');
|
||||
$app->get('/awardees', App\Handler\HomePageHandler::class, 'awardees');
|
||||
$app->get('/awardees/{year:\d+}', App\Handler\HomePageHandler::class, 'awardees-by-year');
|
||||
};
|
||||
4
data/.gitignore
vendored
Normal file
4
data/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
*
|
||||
!cache
|
||||
!cache/.gitkeep
|
||||
!.gitignore
|
||||
0
data/cache/.gitkeep
vendored
Normal file
0
data/cache/.gitkeep
vendored
Normal file
20
phpcs.xml.dist
Normal file
20
phpcs.xml.dist
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0"?>
|
||||
<ruleset name="Expressive Skeleton coding standard">
|
||||
<description>Expressive Skeleton coding standard</description>
|
||||
|
||||
<!-- display progress -->
|
||||
<arg value="p"/>
|
||||
<arg name="colors"/>
|
||||
|
||||
<!-- inherit rules from: -->
|
||||
<rule ref="PSR2"/>
|
||||
<rule ref="Generic.Arrays.DisallowLongArraySyntax"/>
|
||||
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace">
|
||||
<properties>
|
||||
<property name="ignoreBlankLines" value="false"/>
|
||||
</properties>
|
||||
</rule>
|
||||
|
||||
<!-- Paths to check -->
|
||||
<file>src</file>
|
||||
</ruleset>
|
||||
17
phpunit.xml.dist
Normal file
17
phpunit.xml.dist
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true">
|
||||
<testsuites>
|
||||
<testsuite name="App\\Tests">
|
||||
<directory>./test</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">./src</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
19
public/.htaccess
Normal file
19
public/.htaccess
Normal file
@ -0,0 +1,19 @@
|
||||
RewriteEngine On
|
||||
# The following rule allows authentication to work with fast-cgi
|
||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
# The following rule tells Apache that if the requested filename
|
||||
# exists, simply serve it.
|
||||
RewriteCond %{REQUEST_FILENAME} -s [OR]
|
||||
RewriteCond %{REQUEST_FILENAME} -l [OR]
|
||||
RewriteCond %{REQUEST_FILENAME} -d
|
||||
RewriteRule ^.*$ - [NC,L]
|
||||
|
||||
# The following rewrites all other queries to index.php. The
|
||||
# condition ensures that if you are using Apache aliases to do
|
||||
# mass virtual hosting, the base path will be prepended to
|
||||
# allow proper resolution of the index.php file; it will work
|
||||
# in non-aliased environments as well, providing a safe, one-size
|
||||
# fits all solution.
|
||||
RewriteCond %{REQUEST_URI}::$1 ^(/.+)(.+)::\2$
|
||||
RewriteRule ^(.*) - [E=BASE:%1]
|
||||
RewriteRule ^(.*)$ %{ENV:BASE}index.php [NC,L]
|
||||
30
public/index.php
Normal file
30
public/index.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
// Delegate static file requests back to the PHP built-in webserver
|
||||
if (PHP_SAPI === 'cli-server' && $_SERVER['SCRIPT_FILENAME'] !== __FILE__) {
|
||||
return false;
|
||||
}
|
||||
|
||||
chdir(dirname(__DIR__));
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
/**
|
||||
* Self-called anonymous function that creates its own scope and keep the global namespace clean.
|
||||
*/
|
||||
(function () {
|
||||
/** @var \Psr\Container\ContainerInterface $container */
|
||||
$container = require 'config/container.php';
|
||||
|
||||
/** @var \Zend\Expressive\Application $app */
|
||||
$app = $container->get(\Zend\Expressive\Application::class);
|
||||
$factory = $container->get(\Zend\Expressive\MiddlewareFactory::class);
|
||||
|
||||
// Execute programmatic/declarative middleware pipeline and routing
|
||||
// configuration statements
|
||||
(require 'config/pipeline.php')($app, $factory, $container);
|
||||
(require 'config/routes.php')($app, $factory, $container);
|
||||
|
||||
$app->run();
|
||||
})();
|
||||
131
public/styles/main.css
Normal file
131
public/styles/main.css
Normal file
@ -0,0 +1,131 @@
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.app-container {
|
||||
margin: auto;
|
||||
display: grid;
|
||||
grid-column-gap: 10px;
|
||||
grid-row-gap: 10px;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.content-container {
|
||||
grid-area: content;
|
||||
display: grid;
|
||||
grid-template-columns: auto;
|
||||
grid-template-rows: 200px auto;
|
||||
grid-template-areas:
|
||||
"title"
|
||||
"main";
|
||||
}
|
||||
|
||||
.content-container article,
|
||||
.content-block section {
|
||||
border-left: 1px solid #003b71;
|
||||
}
|
||||
|
||||
.content-block {
|
||||
grid-area: content;
|
||||
}
|
||||
|
||||
/* mobile */
|
||||
@media only screen and (max-width: 767px) {
|
||||
.app-container {
|
||||
grid-template-columns: auto;
|
||||
grid-template-rows: 100px auto auto auto 70px;
|
||||
grid-template-areas: "header" "nav" "content" "sidebar" "footer";
|
||||
}
|
||||
}
|
||||
|
||||
/* tablet */
|
||||
@media only screen and (min-width: 768px) and (max-width: 991px) {
|
||||
.app-container {
|
||||
grid-template-columns: 200px 600px 400px;
|
||||
grid-template-rows: 100px auto 70px;
|
||||
grid-template-areas:
|
||||
"header header header"
|
||||
"nav content sidebar"
|
||||
"footer footer footer";
|
||||
}
|
||||
}
|
||||
|
||||
/* monitor */
|
||||
@media only screen and (min-width: 992px) {
|
||||
.app-container {
|
||||
grid-template-columns: auto 200px 600px 400px auto;
|
||||
grid-template-rows: 100px auto 70px;
|
||||
grid-template-areas:
|
||||
". header header header ."
|
||||
". nav content sidebar ."
|
||||
"footer footer footer footer footer";
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
margin: auto;
|
||||
width: 1220px;
|
||||
}
|
||||
}
|
||||
|
||||
header.main-header {
|
||||
grid-area: header;
|
||||
}
|
||||
|
||||
nav.main-nav {
|
||||
grid-area: nav;
|
||||
}
|
||||
|
||||
section.profile-title {
|
||||
grid-area: title;
|
||||
}
|
||||
|
||||
article.profile {
|
||||
grid-area: main;
|
||||
}
|
||||
|
||||
aside.profile-image {
|
||||
grid-area: sidebar;
|
||||
}
|
||||
|
||||
footer.main-footer {
|
||||
grid-area: footer;
|
||||
}
|
||||
|
||||
footer {
|
||||
color: #fffffd;
|
||||
background-color: #003b71;
|
||||
}
|
||||
|
||||
nav ul {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
nav li > ul {
|
||||
display: none;
|
||||
}
|
||||
|
||||
nav li.current > ul,
|
||||
nav li.current_ancestor > ul {
|
||||
display: block;
|
||||
}
|
||||
|
||||
nav > ul > li {
|
||||
font-variant: small-caps;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
nav ul.menu_level_1 {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
nav ul.menu_level_1 > li {
|
||||
font-variant: initial;
|
||||
display: block;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
nav ul.menu_level_1 > li.current {
|
||||
border-left: 5px solid #003b71;
|
||||
}
|
||||
341
public/styles/normalize.css
vendored
Normal file
341
public/styles/normalize.css
vendored
Normal file
@ -0,0 +1,341 @@
|
||||
/*! normalize.css v8.0.0 | MIT License | github.com/necolas/normalize.css */
|
||||
|
||||
/* Document
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Correct the line height in all browsers.
|
||||
* 2. Prevent adjustments of font size after orientation changes in iOS.
|
||||
*/
|
||||
|
||||
html {
|
||||
line-height: 1.15; /* 1 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
}
|
||||
|
||||
/* Sections
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the margin in all browsers.
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the font size and margin on `h1` elements within `section` and
|
||||
* `article` contexts in Chrome, Firefox, and Safari.
|
||||
*/
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
|
||||
/* Grouping content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in Firefox.
|
||||
* 2. Show the overflow in Edge and IE.
|
||||
*/
|
||||
|
||||
hr {
|
||||
box-sizing: content-box; /* 1 */
|
||||
height: 0; /* 1 */
|
||||
overflow: visible; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
pre {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/* Text-level semantics
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the gray background on active links in IE 10.
|
||||
*/
|
||||
|
||||
a {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Remove the bottom border in Chrome 57-
|
||||
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||
*/
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: none; /* 1 */
|
||||
text-decoration: underline; /* 2 */
|
||||
text-decoration: underline dotted; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||
* all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
/* Embedded content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the border on images inside links in IE 10.
|
||||
*/
|
||||
|
||||
img {
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
/* Forms
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Change the font styles in all browsers.
|
||||
* 2. Remove the margin in Firefox and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit; /* 1 */
|
||||
font-size: 100%; /* 1 */
|
||||
line-height: 1.15; /* 1 */
|
||||
margin: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the overflow in IE.
|
||||
* 1. Show the overflow in Edge.
|
||||
*/
|
||||
|
||||
button,
|
||||
input { /* 1 */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||
* 1. Remove the inheritance of text transform in Firefox.
|
||||
*/
|
||||
|
||||
button,
|
||||
select { /* 1 */
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the inability to style clickable types in iOS and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
[type="button"],
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner border and padding in Firefox.
|
||||
*/
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
border-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the focus styles unset by the previous rule.
|
||||
*/
|
||||
|
||||
button:-moz-focusring,
|
||||
[type="button"]:-moz-focusring,
|
||||
[type="reset"]:-moz-focusring,
|
||||
[type="submit"]:-moz-focusring {
|
||||
outline: 1px dotted ButtonText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the padding in Firefox.
|
||||
*/
|
||||
|
||||
fieldset {
|
||||
padding: 0.35em 0.75em 0.625em;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the text wrapping in Edge and IE.
|
||||
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||
* 3. Remove the padding so developers are not caught out when they zero out
|
||||
* `fieldset` elements in all browsers.
|
||||
*/
|
||||
|
||||
legend {
|
||||
box-sizing: border-box; /* 1 */
|
||||
color: inherit; /* 2 */
|
||||
display: table; /* 1 */
|
||||
max-width: 100%; /* 1 */
|
||||
padding: 0; /* 3 */
|
||||
white-space: normal; /* 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||
*/
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the default vertical scrollbar in IE 10+.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in IE 10.
|
||||
* 2. Remove the padding in IE 10.
|
||||
*/
|
||||
|
||||
[type="checkbox"],
|
||||
[type="radio"] {
|
||||
box-sizing: border-box; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||
*/
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the odd appearance in Chrome and Safari.
|
||||
* 2. Correct the outline style in Safari.
|
||||
*/
|
||||
|
||||
[type="search"] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
outline-offset: -2px; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner padding in Chrome and Safari on macOS.
|
||||
*/
|
||||
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||
* 2. Change font properties to `inherit` in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button; /* 1 */
|
||||
font: inherit; /* 2 */
|
||||
}
|
||||
|
||||
/* Interactive
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Add the correct display in Edge, IE 10+, and Firefox.
|
||||
*/
|
||||
|
||||
details {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the correct display in all browsers.
|
||||
*/
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
/* Misc
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10+.
|
||||
*/
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10.
|
||||
*/
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
59
src/App/ConfigProvider.php
Normal file
59
src/App/ConfigProvider.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App;
|
||||
|
||||
/**
|
||||
* The configuration provider for the App module
|
||||
*
|
||||
* @see https://docs.zendframework.com/zend-component-installer/
|
||||
*/
|
||||
class ConfigProvider
|
||||
{
|
||||
/**
|
||||
* Returns the configuration array
|
||||
*
|
||||
* To add a bit of a structure, each section is defined in a separate
|
||||
* method which returns an array with its configuration.
|
||||
*
|
||||
*/
|
||||
public function __invoke() : array
|
||||
{
|
||||
return [
|
||||
'dependencies' => $this->getDependencies(),
|
||||
'templates' => $this->getTemplates(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the container dependencies
|
||||
*/
|
||||
public function getDependencies() : array
|
||||
{
|
||||
return [
|
||||
'invokables' => [
|
||||
Handler\PingHandler::class => Handler\PingHandler::class,
|
||||
],
|
||||
'factories' => [
|
||||
Handler\HomePageHandler::class => Handler\HomePageHandlerFactory::class,
|
||||
|
||||
Plates\NavigationExtension::class => Plates\NavigationExtensionFactory::class,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the templates configuration
|
||||
*/
|
||||
public function getTemplates() : array
|
||||
{
|
||||
return [
|
||||
'paths' => [
|
||||
'app' => ['templates/app'],
|
||||
'error' => ['templates/error'],
|
||||
'layout' => ['templates/layout'],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
94
src/App/Handler/HomePageHandler.php
Normal file
94
src/App/Handler/HomePageHandler.php
Normal file
@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Handler;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Zend\Diactoros\Response\HtmlResponse;
|
||||
use Zend\Diactoros\Response\JsonResponse;
|
||||
use Zend\Expressive\Plates\PlatesRenderer;
|
||||
use Zend\Expressive\Router;
|
||||
use Zend\Expressive\Template;
|
||||
use Zend\Expressive\Twig\TwigRenderer;
|
||||
use Zend\Expressive\ZendView\ZendViewRenderer;
|
||||
|
||||
class HomePageHandler implements RequestHandlerInterface
|
||||
{
|
||||
private $containerName;
|
||||
|
||||
private $router;
|
||||
|
||||
private $template;
|
||||
|
||||
public function __construct(
|
||||
Router\RouterInterface $router,
|
||||
Template\TemplateRendererInterface $template = null,
|
||||
string $containerName
|
||||
) {
|
||||
$this->router = $router;
|
||||
$this->template = $template;
|
||||
$this->containerName = $containerName;
|
||||
}
|
||||
|
||||
public function handle(ServerRequestInterface $request) : ResponseInterface
|
||||
{
|
||||
if (! $this->template) {
|
||||
return new JsonResponse([
|
||||
'welcome' => 'Congratulations! You have installed the zend-expressive skeleton application.',
|
||||
'docsUrl' => 'https://docs.zendframework.com/zend-expressive/',
|
||||
]);
|
||||
}
|
||||
|
||||
$data = [];
|
||||
|
||||
switch ($this->containerName) {
|
||||
case 'Aura\Di\Container':
|
||||
$data['containerName'] = 'Aura.Di';
|
||||
$data['containerDocs'] = 'http://auraphp.com/packages/2.x/Di.html';
|
||||
break;
|
||||
case 'Pimple\Container':
|
||||
$data['containerName'] = 'Pimple';
|
||||
$data['containerDocs'] = 'https://pimple.symfony.com/';
|
||||
break;
|
||||
case 'Zend\ServiceManager\ServiceManager':
|
||||
$data['containerName'] = 'Zend Servicemanager';
|
||||
$data['containerDocs'] = 'https://docs.zendframework.com/zend-servicemanager/';
|
||||
break;
|
||||
case 'Auryn\Injector':
|
||||
$data['containerName'] = 'Auryn';
|
||||
$data['containerDocs'] = 'https://github.com/rdlowrey/Auryn';
|
||||
break;
|
||||
case 'Symfony\Component\DependencyInjection\ContainerBuilder':
|
||||
$data['containerName'] = 'Symfony DI Container';
|
||||
$data['containerDocs'] = 'https://symfony.com/doc/current/service_container.html';
|
||||
break;
|
||||
}
|
||||
|
||||
if ($this->router instanceof Router\AuraRouter) {
|
||||
$data['routerName'] = 'Aura.Router';
|
||||
$data['routerDocs'] = 'http://auraphp.com/packages/2.x/Router.html';
|
||||
} elseif ($this->router instanceof Router\FastRouteRouter) {
|
||||
$data['routerName'] = 'FastRoute';
|
||||
$data['routerDocs'] = 'https://github.com/nikic/FastRoute';
|
||||
} elseif ($this->router instanceof Router\ZendRouter) {
|
||||
$data['routerName'] = 'Zend Router';
|
||||
$data['routerDocs'] = 'https://docs.zendframework.com/zend-router/';
|
||||
}
|
||||
|
||||
if ($this->template instanceof PlatesRenderer) {
|
||||
$data['templateName'] = 'Plates';
|
||||
$data['templateDocs'] = 'http://platesphp.com/';
|
||||
} elseif ($this->template instanceof TwigRenderer) {
|
||||
$data['templateName'] = 'Twig';
|
||||
$data['templateDocs'] = 'http://twig.sensiolabs.org/documentation';
|
||||
} elseif ($this->template instanceof ZendViewRenderer) {
|
||||
$data['templateName'] = 'Zend View';
|
||||
$data['templateDocs'] = 'https://docs.zendframework.com/zend-view/';
|
||||
}
|
||||
|
||||
return new HtmlResponse($this->template->render('app::home-page', $data));
|
||||
}
|
||||
}
|
||||
23
src/App/Handler/HomePageHandlerFactory.php
Normal file
23
src/App/Handler/HomePageHandlerFactory.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Handler;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Zend\Expressive\Router\RouterInterface;
|
||||
use Zend\Expressive\Template\TemplateRendererInterface;
|
||||
|
||||
class HomePageHandlerFactory
|
||||
{
|
||||
public function __invoke(ContainerInterface $container) : RequestHandlerInterface
|
||||
{
|
||||
$router = $container->get(RouterInterface::class);
|
||||
$template = $container->has(TemplateRendererInterface::class)
|
||||
? $container->get(TemplateRendererInterface::class)
|
||||
: null;
|
||||
|
||||
return new HomePageHandler($router, $template, get_class($container));
|
||||
}
|
||||
}
|
||||
18
src/App/Handler/PingHandler.php
Normal file
18
src/App/Handler/PingHandler.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Handler;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Zend\Diactoros\Response\JsonResponse;
|
||||
|
||||
class PingHandler implements RequestHandlerInterface
|
||||
{
|
||||
public function handle(ServerRequestInterface $request) : ResponseInterface
|
||||
{
|
||||
return new JsonResponse(['ack' => time()]);
|
||||
}
|
||||
}
|
||||
102
src/App/Plates/NavigationExtension.php
Normal file
102
src/App/Plates/NavigationExtension.php
Normal file
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Plates;
|
||||
|
||||
use Knp\Menu\ItemInterface;
|
||||
use Knp\Menu\Matcher\Matcher;
|
||||
use Knp\Menu\Matcher\Voter\UriVoter;
|
||||
use Knp\Menu\Matcher\Voter\VoterInterface;
|
||||
use Knp\Menu\MenuFactory;
|
||||
use Knp\Menu\MenuItem;
|
||||
use Knp\Menu\Renderer\ListRenderer;
|
||||
use League\Plates\Engine;
|
||||
use League\Plates\Extension\ExtensionInterface;
|
||||
use Zend\Expressive\Router\RouterInterface;
|
||||
|
||||
class NavigationExtension implements ExtensionInterface
|
||||
{
|
||||
/** @var RouterInterface */
|
||||
private $router;
|
||||
|
||||
/** @var MenuItem */
|
||||
private $menu;
|
||||
|
||||
public function __construct(RouterInterface $router)
|
||||
{
|
||||
$this->router = $router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register functions with the Plates engine.
|
||||
*
|
||||
* Registers:
|
||||
*
|
||||
* - url($route = null, array $params = []) : string
|
||||
* - serverurl($path = null) : string
|
||||
*
|
||||
* @param Engine $engine
|
||||
*/
|
||||
public function register(Engine $engine): void
|
||||
{
|
||||
$engine->registerFunction('navigation', [$this, 'generateNavigation']);
|
||||
}
|
||||
|
||||
public function generateNavigation(): string
|
||||
{
|
||||
$this->initMainMenu();
|
||||
$matcher = new Matcher([
|
||||
new UriVoter($_SERVER['REQUEST_URI'])
|
||||
]);
|
||||
$renderer = new ListRenderer($matcher);
|
||||
return $renderer->render($this->menu);
|
||||
}
|
||||
|
||||
private function initMainMenu()
|
||||
{
|
||||
$factory = new MenuFactory();
|
||||
$this->menu = $factory->createItem("Main-menu");
|
||||
|
||||
$prizeMenu = $this->menu->addChild("The prize", [
|
||||
'uri' => $this->getUriFromRouter('the-prize')
|
||||
]);
|
||||
$prizeMenu->addChild("Background and Purpose", [
|
||||
'uri' => $this->getUriFromRouter('the-prize.bg')
|
||||
]);
|
||||
$prizeMenu->addChild("Description and Values", [
|
||||
'uri' => $this->getUriFromRouter('the-prize.desc')
|
||||
]);
|
||||
$prizeMenu->addChild("Aspect for Selection", [
|
||||
'uri' => $this->getUriFromRouter('the-prize.aspect')
|
||||
]);
|
||||
$prizeMenu->addChild("Gran Prize Award and Events", [
|
||||
'uri' => $this->getUriFromRouter('the-prize.events')
|
||||
]);
|
||||
|
||||
$this->menu->addChild("Judges", [
|
||||
'uri' => $this->getUriFromRouter('judges')
|
||||
]);
|
||||
|
||||
$awardeesMenu = $this->menu->addChild("Awardees", [
|
||||
'uri' => $this->getUriFromRouter('awardees')
|
||||
]);
|
||||
$this->populateAwardeesSubmenu($awardeesMenu);
|
||||
}
|
||||
|
||||
private function getUriFromRouter(string $name, $param = []): string
|
||||
{
|
||||
return $this->router->generateUri($name, $param);
|
||||
}
|
||||
|
||||
private function populateAwardeesSubmenu(ItemInterface $awardeesMenu)
|
||||
{
|
||||
$year = (int)date("Y");
|
||||
|
||||
for ($i = $year; $i > 2012; $i--) {
|
||||
$awardeesMenu->addChild($i, [
|
||||
'uri' => $this->getUriFromRouter('awardees-by-year', ['year' => $i])
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
26
src/App/Plates/NavigationExtensionFactory.php
Normal file
26
src/App/Plates/NavigationExtensionFactory.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Plates;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Zend\Expressive\Plates\Exception\MissingHelperException;
|
||||
use Zend\Expressive\Router\RouterInterface;
|
||||
|
||||
class NavigationExtensionFactory
|
||||
{
|
||||
public function __invoke(ContainerInterface $container) : NavigationExtension
|
||||
{
|
||||
if (! $container->has(RouterInterface::class)) {
|
||||
throw new MissingHelperException(sprintf(
|
||||
'%s requires that the %s service be present; not found',
|
||||
NavigationExtension::class,
|
||||
RouterInterface::class
|
||||
));
|
||||
}
|
||||
|
||||
$router = $container->get(RouterInterface::class);
|
||||
return new NavigationExtension($router);
|
||||
}
|
||||
}
|
||||
15
templates/app/home-page.phtml
Normal file
15
templates/app/home-page.phtml
Normal file
@ -0,0 +1,15 @@
|
||||
<?php $this->layout('layout::default', ['title' => 'Home']) ?>
|
||||
<section class="content-container">
|
||||
<section class="profile-title">
|
||||
|
||||
</section>
|
||||
<article class="profile">
|
||||
<section class="profile-image">
|
||||
|
||||
</section>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<aside class="profile-image">
|
||||
|
||||
</aside>
|
||||
9
templates/error/404.phtml
Normal file
9
templates/error/404.phtml
Normal file
@ -0,0 +1,9 @@
|
||||
<?php $this->layout('layout::default', ['title' => '404 Not Found']) ?>
|
||||
|
||||
<h1>Oops!</h1>
|
||||
<h2>This is awkward.</h2>
|
||||
<p>We encountered a 404 Not Found error.</p>
|
||||
<p>
|
||||
You are looking for something that doesn't exist or may have moved. Check out one of the links on this page
|
||||
or head back to <a href="/">Home</a>.
|
||||
</p>
|
||||
11
templates/error/error.phtml
Normal file
11
templates/error/error.phtml
Normal file
@ -0,0 +1,11 @@
|
||||
<?php $this->layout('layout::default', ['title' => sprintf('%d %s', $status, $reason)]) ?>
|
||||
|
||||
<h1>Oops!</h1>
|
||||
<h2>This is awkward.</h2>
|
||||
<p>We encountered a <?=$this->e($status)?> <?=$this->e($reason)?> error.</p>
|
||||
<?php if ($status == 404) : ?>
|
||||
<p>
|
||||
You are looking for something that doesn't exist or may have moved. Check out one of the links on this page
|
||||
or head back to <a href="/">Home</a>.
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
36
templates/layout/default.phtml
Normal file
36
templates/layout/default.phtml
Normal file
@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/>
|
||||
<title><?= $this->e($title) ?> - zend-expressive</title>
|
||||
<link rel="shortcut icon" href="https://framework.zend.com/ico/favicon.ico"/>
|
||||
<link rel="stylesheet" href="<?= $this->serverurl('/styles/normalize.css') ?>"/>
|
||||
<link rel="stylesheet" href="<?= $this->serverurl('/styles/main.css') ?>"/>
|
||||
<?= $this->section('stylesheets') ?>
|
||||
</head>
|
||||
<body>
|
||||
<div class="app-container">
|
||||
<header class="main-header">
|
||||
<span>Gran Prize</span>
|
||||
<span>
|
||||
Interdisciplinary
|
||||
Innovative
|
||||
Award
|
||||
</span>
|
||||
GP logo
|
||||
</header>
|
||||
<nav class="main-nav"><?=$this->navigation()?></nav>
|
||||
<?= $this->section('content') ?>
|
||||
<footer class="main-footer">
|
||||
<div class="footer-content">
|
||||
Sponsored by:
|
||||
SIGMA
|
||||
ERICSSON
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
<?= $this->section('javascript') ?>
|
||||
</body>
|
||||
</html>
|
||||
52
test/AppTest/Handler/HomePageHandlerFactoryTest.php
Normal file
52
test/AppTest/Handler/HomePageHandlerFactoryTest.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace AppTest\Handler;
|
||||
|
||||
use App\Handler\HomePageHandler;
|
||||
use App\Handler\HomePageHandlerFactory;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Zend\Expressive\Router\RouterInterface;
|
||||
use Zend\Expressive\Template\TemplateRendererInterface;
|
||||
|
||||
class HomePageHandlerFactoryTest extends TestCase
|
||||
{
|
||||
/** @var ContainerInterface|ObjectProphecy */
|
||||
protected $container;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->container = $this->prophesize(ContainerInterface::class);
|
||||
$router = $this->prophesize(RouterInterface::class);
|
||||
|
||||
$this->container->get(RouterInterface::class)->willReturn($router);
|
||||
}
|
||||
|
||||
public function testFactoryWithoutTemplate()
|
||||
{
|
||||
$factory = new HomePageHandlerFactory();
|
||||
$this->container->has(TemplateRendererInterface::class)->willReturn(false);
|
||||
|
||||
$this->assertInstanceOf(HomePageHandlerFactory::class, $factory);
|
||||
|
||||
$homePage = $factory($this->container->reveal(), null, get_class($this->container->reveal()));
|
||||
|
||||
$this->assertInstanceOf(HomePageHandler::class, $homePage);
|
||||
}
|
||||
|
||||
public function testFactoryWithTemplate()
|
||||
{
|
||||
$this->container->has(TemplateRendererInterface::class)->willReturn(true);
|
||||
$this->container
|
||||
->get(TemplateRendererInterface::class)
|
||||
->willReturn($this->prophesize(TemplateRendererInterface::class));
|
||||
|
||||
$factory = new HomePageHandlerFactory();
|
||||
|
||||
$homePage = $factory($this->container->reveal(), null, get_class($this->container->reveal()));
|
||||
|
||||
$this->assertInstanceOf(HomePageHandler::class, $homePage);
|
||||
}
|
||||
}
|
||||
64
test/AppTest/Handler/HomePageHandlerTest.php
Normal file
64
test/AppTest/Handler/HomePageHandlerTest.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace AppTest\Handler;
|
||||
|
||||
use App\Handler\HomePageHandler;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Prophecy\Argument;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Zend\Diactoros\Response\HtmlResponse;
|
||||
use Zend\Diactoros\Response\JsonResponse;
|
||||
use Zend\Expressive\Router\RouterInterface;
|
||||
use Zend\Expressive\Template\TemplateRendererInterface;
|
||||
|
||||
class HomePageHandlerTest extends TestCase
|
||||
{
|
||||
/** @var ContainerInterface|ObjectProphecy */
|
||||
protected $container;
|
||||
|
||||
/** @var RouterInterface|ObjectProphecy */
|
||||
protected $router;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->container = $this->prophesize(ContainerInterface::class);
|
||||
$this->router = $this->prophesize(RouterInterface::class);
|
||||
}
|
||||
|
||||
public function testReturnsJsonResponseWhenNoTemplateRendererProvided()
|
||||
{
|
||||
$homePage = new HomePageHandler(
|
||||
$this->router->reveal(),
|
||||
null,
|
||||
get_class($this->container->reveal())
|
||||
);
|
||||
$response = $homePage->handle(
|
||||
$this->prophesize(ServerRequestInterface::class)->reveal()
|
||||
);
|
||||
|
||||
$this->assertInstanceOf(JsonResponse::class, $response);
|
||||
}
|
||||
|
||||
public function testReturnsHtmlResponseWhenTemplateRendererProvided()
|
||||
{
|
||||
$renderer = $this->prophesize(TemplateRendererInterface::class);
|
||||
$renderer
|
||||
->render('app::home-page', Argument::type('array'))
|
||||
->willReturn('');
|
||||
|
||||
$homePage = new HomePageHandler(
|
||||
$this->router->reveal(),
|
||||
$renderer->reveal(),
|
||||
get_class($this->container->reveal())
|
||||
);
|
||||
|
||||
$response = $homePage->handle(
|
||||
$this->prophesize(ServerRequestInterface::class)->reveal()
|
||||
);
|
||||
|
||||
$this->assertInstanceOf(HtmlResponse::class, $response);
|
||||
}
|
||||
}
|
||||
26
test/AppTest/Handler/PingHandlerTest.php
Normal file
26
test/AppTest/Handler/PingHandlerTest.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace AppTest\Handler;
|
||||
|
||||
use App\Handler\PingHandler;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Zend\Diactoros\Response\JsonResponse;
|
||||
|
||||
class PingHandlerTest extends TestCase
|
||||
{
|
||||
public function testResponse()
|
||||
{
|
||||
$pingHandler = new PingHandler();
|
||||
$response = $pingHandler->handle(
|
||||
$this->prophesize(ServerRequestInterface::class)->reveal()
|
||||
);
|
||||
|
||||
$json = json_decode((string) $response->getBody());
|
||||
|
||||
$this->assertInstanceOf(JsonResponse::class, $response);
|
||||
$this->assertTrue(isset($json->ack));
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user