* uplift to latest expressive
This commit is contained in:
parent
b5c307166e
commit
8c5b58acd1
139
README.md
139
README.md
@ -1 +1,138 @@
|
||||
# Skies proxy api
|
||||
# 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-7 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-7 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.
|
||||
|
||||
> ### 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](CONTRIBUTING.md).
|
||||
|
||||
46
bin/clear-config-cache.php
Normal file
46
bin/clear-config-cache.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
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);
|
||||
@ -4,21 +4,30 @@
|
||||
"type": "project",
|
||||
"homepage": "https://github.com/zendframework/zend-expressive-skeleton",
|
||||
"license": "BSD-3-Clause",
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.6 || ^7.0",
|
||||
"php": "^7.1",
|
||||
"doctrine/common": "2.9.x-dev",
|
||||
"guzzlehttp/guzzle": "6.3.0",
|
||||
"http-interop/http-middleware": "^0.4.1",
|
||||
"roave/security-advisories": "dev-master",
|
||||
"zendframework/zend-expressive": "^1.0",
|
||||
"zendframework/zend-expressive-helpers": "^2.0",
|
||||
"zendframework/zend-stdlib": "^2.7 || ^3.0",
|
||||
"zendframework/zend-expressive-fastroute": "^1.0",
|
||||
"zendframework/zend-servicemanager": "^2.7.3 || ^3.0",
|
||||
"zendframework/zend-http": "^2.5",
|
||||
"symfony/css-selector": "^3.2"
|
||||
"zendframework/zend-component-installer": "^1.0",
|
||||
"zendframework/zend-config-aggregator": "^1.0",
|
||||
"zendframework/zend-dom": "2.6.0",
|
||||
"zendframework/zend-expressive": "^2.0.5",
|
||||
"zendframework/zend-expressive-fastroute": "^2.0",
|
||||
"zendframework/zend-expressive-helpers": "^4.0",
|
||||
"zendframework/zend-json": "3.0.0",
|
||||
"zendframework/zend-servicemanager": "^3.3",
|
||||
"zendframework/zend-stdlib": "^3.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.8",
|
||||
"squizlabs/php_codesniffer": "^2.3",
|
||||
"filp/whoops": "^1.1 || ^2.0"
|
||||
"phpunit/phpunit": "^6.0.8 || ^5.7.15",
|
||||
"squizlabs/php_codesniffer": "^2.8.1",
|
||||
"zfcampus/zf-development-mode": "^3.1",
|
||||
"filp/whoops": "^2.1.7"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@ -31,10 +40,17 @@
|
||||
}
|
||||
},
|
||||
"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",
|
||||
"check": [
|
||||
"@cs-check",
|
||||
"@test"
|
||||
],
|
||||
"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 public/index.php",
|
||||
|
||||
2280
composer.lock
generated
2280
composer.lock
generated
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
|
||||
@ -1,13 +1,21 @@
|
||||
<?php
|
||||
|
||||
use Zend\Expressive\Application;
|
||||
use Zend\Expressive\Container\ApplicationFactory;
|
||||
use Zend\Expressive\Container;
|
||||
use Zend\Expressive\Delegate;
|
||||
use Zend\Expressive\Helper;
|
||||
use Zend\Expressive\Middleware;
|
||||
|
||||
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' => [
|
||||
'Zend\Expressive\Delegate\DefaultDelegate' => Delegate\NotFoundDelegate::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.
|
||||
@ -16,10 +24,16 @@ return [
|
||||
Helper\ServerUrlHelper::class => Helper\ServerUrlHelper::class,
|
||||
],
|
||||
// Use 'factories' for services provided by callbacks/factory classes.
|
||||
'factories' => [
|
||||
Application::class => ApplicationFactory::class,
|
||||
Helper\UrlHelper::class => Helper\UrlHelperFactory::class,
|
||||
\App\Service\SkiesService::class => \App\Service\SkiesServiceFactory::class,
|
||||
'factories' => [
|
||||
Application::class => Container\ApplicationFactory::class,
|
||||
Delegate\NotFoundDelegate::class => Container\NotFoundDelegateFactory::class,
|
||||
Helper\ServerUrlMiddleware::class => Helper\ServerUrlMiddlewareFactory::class,
|
||||
Helper\UrlHelper::class => Helper\UrlHelperFactory::class,
|
||||
Helper\UrlHelperMiddleware::class => Helper\UrlHelperMiddlewareFactory::class,
|
||||
|
||||
Zend\Stratigility\Middleware\ErrorHandler::class => Container\ErrorHandlerFactory::class,
|
||||
Middleware\ErrorResponseGenerator::class => Container\ErrorResponseGeneratorFactory::class,
|
||||
Middleware\NotFoundHandler::class => Container\NotFoundHandlerFactory::class,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
34
config/autoload/development.local.php.dist
Normal file
34
config/autoload/development.local.php.dist
Normal file
@ -0,0 +1,34 @@
|
||||
<?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`.
|
||||
*/
|
||||
|
||||
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,
|
||||
],
|
||||
],
|
||||
];
|
||||
@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'authKey' => '',
|
||||
'debug' => true,
|
||||
'config_cache_enabled' => false,
|
||||
];
|
||||
11
config/autoload/local.php.dist
Normal file
11
config/autoload/local.php.dist
Normal file
@ -0,0 +1,11 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
return [
|
||||
];
|
||||
@ -1,69 +0,0 @@
|
||||
<?php
|
||||
use Zend\Expressive\Container\ApplicationFactory;
|
||||
use Zend\Expressive\Helper;
|
||||
|
||||
return [
|
||||
'dependencies' => [
|
||||
'factories' => [
|
||||
Helper\ServerUrlMiddleware::class => Helper\ServerUrlMiddlewareFactory::class,
|
||||
Helper\UrlHelperMiddleware::class => Helper\UrlHelperMiddlewareFactory::class,
|
||||
],
|
||||
],
|
||||
// This can be used to seed pre- and/or post-routing middleware
|
||||
'middleware_pipeline' => [
|
||||
// An array of middleware to register. Each item is of the following
|
||||
// specification:
|
||||
//
|
||||
// [
|
||||
// Required:
|
||||
// 'middleware' => 'Name or array of names of middleware services and/or callables',
|
||||
// Optional:
|
||||
// 'path' => '/path/to/match', // string; literal path prefix to match
|
||||
// // middleware will not execute
|
||||
// // if path does not match!
|
||||
// 'error' => true, // boolean; true for error middleware
|
||||
// 'priority' => 1, // int; higher values == register early;
|
||||
// // lower/negative == register last;
|
||||
// // default is 1, if none is provided.
|
||||
// ],
|
||||
//
|
||||
// While the ApplicationFactory ignores the keys associated with
|
||||
// specifications, they can be used to allow merging related values
|
||||
// defined in multiple configuration files/locations. This file defines
|
||||
// some conventional keys for middleware to execute early, routing
|
||||
// middleware, and error middleware.
|
||||
'always' => [
|
||||
'middleware' => [
|
||||
// Add more middleware here that you want to execute on
|
||||
// every request:
|
||||
// - bootstrapping
|
||||
// - pre-conditions
|
||||
// - modifications to outgoing responses
|
||||
Helper\ServerUrlMiddleware::class,
|
||||
],
|
||||
'priority' => 10000,
|
||||
],
|
||||
|
||||
'routing' => [
|
||||
'middleware' => [
|
||||
ApplicationFactory::ROUTING_MIDDLEWARE,
|
||||
Helper\UrlHelperMiddleware::class,
|
||||
// Add more middleware here that needs to introspect the routing
|
||||
// results; this might include:
|
||||
// - route-based authentication
|
||||
// - route-based validation
|
||||
// - etc.
|
||||
ApplicationFactory::DISPATCH_MIDDLEWARE,
|
||||
],
|
||||
'priority' => 1,
|
||||
],
|
||||
|
||||
'error' => [
|
||||
'middleware' => [
|
||||
// Add error middleware here.
|
||||
],
|
||||
'error' => true,
|
||||
'priority' => -10000,
|
||||
],
|
||||
],
|
||||
];
|
||||
12
config/autoload/router.global.php
Normal file
12
config/autoload/router.global.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
use Zend\Expressive\Router\FastRouteRouter;
|
||||
use Zend\Expressive\Router\RouterInterface;
|
||||
|
||||
return [
|
||||
'dependencies' => [
|
||||
'invokables' => [
|
||||
RouterInterface::class => FastRouteRouter::class,
|
||||
],
|
||||
],
|
||||
];
|
||||
@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'dependencies' => [
|
||||
'invokables' => [
|
||||
Zend\Expressive\Router\RouterInterface::class => Zend\Expressive\Router\FastRouteRouter::class,
|
||||
App\Action\PingAction::class => App\Action\PingAction::class,
|
||||
],
|
||||
'factories' => [
|
||||
App\Action\HomePageAction::class => App\Action\HomePageFactory::class,
|
||||
App\Action\ActivityAction::class => App\Action\ActivityFactory::class,
|
||||
],
|
||||
],
|
||||
|
||||
'routes' => [
|
||||
[
|
||||
'name' => 'home',
|
||||
'path' => '/',
|
||||
'middleware' => App\Action\HomePageAction::class,
|
||||
'allowed_methods' => ['GET'],
|
||||
],
|
||||
[
|
||||
'name' => 'api.ping',
|
||||
'path' => '/api/ping',
|
||||
'middleware' => App\Action\PingAction::class,
|
||||
'allowed_methods' => ['GET'],
|
||||
],
|
||||
[
|
||||
'name' => 'api.activity',
|
||||
'path' => '/api/activity',
|
||||
'middleware' => App\Action\ActivityAction::class,
|
||||
'allowed_methods' => ['GET', 'POST', 'DELETE', 'OPTIONS'],
|
||||
],
|
||||
[
|
||||
'name' => 'api.activity.id',
|
||||
'path' => '/api/activity/{id:\d+}',
|
||||
'middleware' => App\Action\ActivityAction::class,
|
||||
'allowed_methods' => ['GET', 'PUT', 'DELETE', 'OPTIONS'],
|
||||
],
|
||||
],
|
||||
];
|
||||
@ -1,11 +1,24 @@
|
||||
<?php
|
||||
|
||||
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,
|
||||
|
||||
'config_cache_enabled' => false,
|
||||
|
||||
'zend-expressive' => [
|
||||
// Enable programmatic pipeline: Any `middleware_pipeline` or `routes`
|
||||
// configuration will be ignored when creating the `Application` instance.
|
||||
'programmatic_pipeline' => true,
|
||||
|
||||
// Provide templates for the error handling middleware to use when
|
||||
// generating responses.
|
||||
'error_handler' => [
|
||||
'template_404' => 'error::404',
|
||||
'template_error' => 'error::error',
|
||||
|
||||
@ -1,35 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Zend\Stdlib\ArrayUtils;
|
||||
use Zend\Stdlib\Glob;
|
||||
use Zend\ConfigAggregator\ArrayProvider;
|
||||
use Zend\ConfigAggregator\ConfigAggregator;
|
||||
use Zend\ConfigAggregator\PhpFileProvider;
|
||||
|
||||
/**
|
||||
* Configuration files are loaded in a specific order. First ``global.php``, then ``*.global.php``.
|
||||
* then ``local.php`` and finally ``*.local.php``. This way local settings overwrite global settings.
|
||||
*
|
||||
* The configuration can be cached. This can be done by setting ``config_cache_enabled`` to ``true``.
|
||||
*
|
||||
* Obviously, if you use closures in your config you can't cache it.
|
||||
*/
|
||||
// To enable or disable caching, set the `ConfigAggregator::ENABLE_CACHE` boolean in
|
||||
// `config/autoload/local.php`.
|
||||
$cacheConfig = [
|
||||
'config_cache_path' => 'data/config-cache.php',
|
||||
];
|
||||
|
||||
$cachedConfigFile = 'data/cache/app_config.php';
|
||||
$aggregator = new ConfigAggregator([
|
||||
// Include cache configuration
|
||||
new ArrayProvider($cacheConfig),
|
||||
|
||||
$config = [];
|
||||
if (is_file($cachedConfigFile)) {
|
||||
// Try to load the cached config
|
||||
$config = include $cachedConfigFile;
|
||||
} else {
|
||||
// Load configuration from autoload path
|
||||
foreach (Glob::glob('config/autoload/{{,*.}global,{,*.}local}.php', Glob::GLOB_BRACE) as $file) {
|
||||
$config = ArrayUtils::merge($config, include $file);
|
||||
}
|
||||
// Default App module config
|
||||
App\ConfigProvider::class,
|
||||
|
||||
// Cache config if enabled
|
||||
if (isset($config['config_cache_enabled']) && $config['config_cache_enabled'] === true) {
|
||||
file_put_contents($cachedConfigFile, '<?php return ' . var_export($config, true) . ';');
|
||||
}
|
||||
}
|
||||
// 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('config/autoload/{{,*.}global,{,*.}local}.php'),
|
||||
|
||||
// Return an ArrayObject so we can inject the config as a service in Aura.Di
|
||||
// and still use array checks like ``is_array``.
|
||||
return new ArrayObject($config, ArrayObject::ARRAY_AS_PROPS);
|
||||
// Load development config if it exists
|
||||
new PhpFileProvider('config/development.config.php'),
|
||||
], $cacheConfig['config_cache_path']);
|
||||
|
||||
return $aggregator->getMergedConfig();
|
||||
|
||||
29
config/development.config.php.dist
Normal file
29
config/development.config.php.dist
Normal file
@ -0,0 +1,29 @@
|
||||
<?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_.
|
||||
*/
|
||||
|
||||
use Zend\ConfigAggregator\ConfigAggregator;
|
||||
|
||||
return [
|
||||
'debug' => true,
|
||||
ConfigAggregator::ENABLE_CACHE => false,
|
||||
];
|
||||
55
config/pipeline.php
Normal file
55
config/pipeline.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
use Zend\Expressive\Helper\ServerUrlMiddleware;
|
||||
use Zend\Expressive\Helper\UrlHelperMiddleware;
|
||||
use Zend\Expressive\Middleware\ImplicitHeadMiddleware;
|
||||
use Zend\Expressive\Middleware\ImplicitOptionsMiddleware;
|
||||
use Zend\Expressive\Middleware\NotFoundHandler;
|
||||
use Zend\Stratigility\Middleware\ErrorHandler;
|
||||
|
||||
/**
|
||||
* Setup middleware pipeline:
|
||||
*/
|
||||
|
||||
// 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!!!
|
||||
//
|
||||
// - $app->pipe('/api', $apiMiddleware);
|
||||
// - $app->pipe('/docs', $apiDocMiddleware);
|
||||
// - $app->pipe('/files', $filesMiddleware);
|
||||
|
||||
// Register the routing middleware in the middleware pipeline
|
||||
$app->pipeRoutingMiddleware();
|
||||
$app->pipe(ImplicitHeadMiddleware::class);
|
||||
$app->pipe(ImplicitOptionsMiddleware::class);
|
||||
$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->pipeDispatchMiddleware();
|
||||
|
||||
// At this point, if no Response is return by any middleware, the
|
||||
// NotFoundHandler kicks in; alternately, you can provide other fallback
|
||||
// middleware to execute.
|
||||
$app->pipe(NotFoundHandler::class);
|
||||
34
config/routes.php
Normal file
34
config/routes.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
/**
|
||||
* Setup routes with a single request method:
|
||||
*
|
||||
* $app->get('/', App\Action\HomePageAction::class, 'home');
|
||||
* $app->post('/album', App\Action\AlbumCreateAction::class, 'album.create');
|
||||
* $app->put('/album/:id', App\Action\AlbumUpdateAction::class, 'album.put');
|
||||
* $app->patch('/album/:id', App\Action\AlbumUpdateAction::class, 'album.patch');
|
||||
* $app->delete('/album/:id', App\Action\AlbumDeleteAction::class, 'album.delete');
|
||||
*
|
||||
* Or with multiple request methods:
|
||||
*
|
||||
* $app->route('/contact', App\Action\ContactAction::class, ['GET', 'POST', ...], 'contact');
|
||||
*
|
||||
* Or handling all request methods:
|
||||
*
|
||||
* $app->route('/contact', App\Action\ContactAction::class)->setName('contact');
|
||||
*
|
||||
* or:
|
||||
*
|
||||
* $app->route(
|
||||
* '/contact',
|
||||
* App\Action\ContactAction::class,
|
||||
* Zend\Expressive\Router\Route::HTTP_METHOD_ANY,
|
||||
* 'contact'
|
||||
* );
|
||||
*/
|
||||
|
||||
$app->get('/', App\Action\HomePageAction::class, 'home');
|
||||
$app->get('/api/ping', App\Action\PingAction::class, 'api.ping');
|
||||
|
||||
$app->get('/api/activity[/{id:\d+}]', App\Action\ActivityAction::class, 'api.activity.get');
|
||||
$app->get('/api/activity/signup/{id:\d+}', App\Action\ActivitySignupAction::class, 'api.activity.signup');
|
||||
$app->get('/api/activity/signoff/{id:\d+}', App\Action\ActivitySignoffAction::class, 'api.activity.signoff');
|
||||
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0"?>
|
||||
<ruleset name="Zend Framework coding standard">
|
||||
<description>Zend Framework coding standard</description>
|
||||
<ruleset name="Expressive Skeleton coding standard">
|
||||
<description>Expressive Skeleton coding standard</description>
|
||||
|
||||
<!-- display progress -->
|
||||
<arg value="p"/>
|
||||
@ -10,9 +10,20 @@ if (php_sapi_name() === 'cli-server'
|
||||
chdir(dirname(__DIR__));
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
/** @var \Interop\Container\ContainerInterface $container */
|
||||
$container = require 'config/container.php';
|
||||
/**
|
||||
* Self-called anonymous function that creates its own scope and keep the global namespace clean.
|
||||
*/
|
||||
call_user_func(function () {
|
||||
/** @var \Interop\Container\ContainerInterface $container */
|
||||
$container = require 'config/container.php';
|
||||
|
||||
/** @var \Zend\Expressive\Application $app */
|
||||
$app = $container->get(\Zend\Expressive\Application::class);
|
||||
$app->run();
|
||||
/** @var \Zend\Expressive\Application $app */
|
||||
$app = $container->get(\Zend\Expressive\Application::class);
|
||||
|
||||
// Import programmatic/declarative middleware pipeline and routing
|
||||
// configuration statements
|
||||
require 'config/pipeline.php';
|
||||
require 'config/routes.php';
|
||||
|
||||
$app->run();
|
||||
});
|
||||
|
||||
@ -2,16 +2,17 @@
|
||||
|
||||
namespace App\Action;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||
use Interop\Http\ServerMiddleware\MiddlewareInterface as ServerMiddlewareInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Zend\Diactoros\Response\JsonResponse;
|
||||
use Zend\Stratigility\MiddlewareInterface;
|
||||
use Zend\Json\Json;
|
||||
|
||||
abstract class AbstractAction implements MiddlewareInterface
|
||||
abstract class AbstractAction implements ServerMiddlewareInterface
|
||||
{
|
||||
const IDENTIFIER_NAME = 'id';
|
||||
|
||||
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
||||
public function process(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||
{
|
||||
$requestMethod = strtoupper($request->getMethod());
|
||||
$id = $request->getAttribute(static::IDENTIFIER_NAME);
|
||||
@ -19,68 +20,68 @@ abstract class AbstractAction implements MiddlewareInterface
|
||||
switch ($requestMethod) {
|
||||
case 'GET':
|
||||
return isset($id)
|
||||
? $this->get($request, $response, $next)
|
||||
: $this->getList($request, $response, $next);
|
||||
? $this->get($request, $delegate)
|
||||
: $this->getList($request, $delegate);
|
||||
case 'POST':
|
||||
return $this->create($request, $response, $next);
|
||||
return $this->create($request, $delegate);
|
||||
case 'PUT':
|
||||
return $this->update($request, $response, $next);
|
||||
return $this->update($request, $delegate);
|
||||
case 'DELETE':
|
||||
return isset($id)
|
||||
? $this->delete($request, $response, $next)
|
||||
: $this->deleteList($request, $response, $next);
|
||||
? $this->delete($request, $delegate)
|
||||
: $this->deleteList($request, $delegate);
|
||||
case 'HEAD':
|
||||
return $this->head($request, $response, $next);
|
||||
return $this->head($request, $delegate);
|
||||
case 'OPTIONS':
|
||||
return $this->options($request, $response, $next);
|
||||
return $this->options($request, $delegate);
|
||||
case 'PATCH':
|
||||
return $this->patch($request, $response, $next);
|
||||
return $this->patch($request, $delegate);
|
||||
default:
|
||||
return $next($request, $response);
|
||||
return $delegate->process($request);
|
||||
}
|
||||
}
|
||||
|
||||
public function get(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
||||
public function get(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||
{
|
||||
return $this->createResponse(['content' => 'Method not allowed'], 405);
|
||||
}
|
||||
|
||||
public function getList(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
||||
public function getList(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||
{
|
||||
return $this->createResponse(['content' => 'Method not allowed'], 405);
|
||||
}
|
||||
|
||||
public function create(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
||||
public function create(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||
{
|
||||
return $this->createResponse(['content' => 'Method not allowed'], 405);
|
||||
}
|
||||
|
||||
public function update(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
||||
public function update(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||
{
|
||||
return $this->createResponse(['content' => 'Method not allowed'], 405);
|
||||
}
|
||||
|
||||
public function delete(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
||||
public function delete(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||
{
|
||||
return $this->createResponse(['content' => 'Method not allowed'], 405);
|
||||
}
|
||||
|
||||
public function deleteList(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
||||
public function deleteList(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||
{
|
||||
return $this->createResponse(['content' => 'Method not allowed'], 405);
|
||||
}
|
||||
|
||||
public function head(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
||||
public function head(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||
{
|
||||
return $this->createResponse(['content' => 'Method not allowed'], 405);
|
||||
}
|
||||
|
||||
public function options(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
||||
public function options(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||
{
|
||||
return $this->createResponse(['content' => 'Method not allowed'], 405);
|
||||
}
|
||||
|
||||
public function patch(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
||||
public function patch(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||
{
|
||||
return $this->createResponse(['content' => 'Method not allowed'], 405);
|
||||
}
|
||||
@ -89,4 +90,69 @@ abstract class AbstractAction implements MiddlewareInterface
|
||||
{
|
||||
return new JsonResponse($data, $status);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return array|object
|
||||
*/
|
||||
public function getRequestData(ServerRequestInterface $request)
|
||||
{
|
||||
$body = $request->getParsedBody();
|
||||
|
||||
if (!empty($body)) {
|
||||
return $body;
|
||||
}
|
||||
|
||||
return $this->parseRequestData(
|
||||
$request->getBody()->getContents(),
|
||||
$request->getHeaderLine('content-type')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $contentType
|
||||
* @return mixed
|
||||
*/
|
||||
private function parseRequestData($input, $contentType)
|
||||
{
|
||||
$contentTypeParts = preg_split('/\s*[;,]\s*/', $contentType);
|
||||
$parser = $this->returnParserContentType($contentTypeParts[0]);
|
||||
|
||||
return $parser($input);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $contentType
|
||||
* @return callable
|
||||
*/
|
||||
private function returnParserContentType(string $contentType)
|
||||
{
|
||||
if ($contentType === 'application/x-www-form-urlencoded') {
|
||||
return function ($input) {
|
||||
parse_str($input, $data);
|
||||
return $data;
|
||||
};
|
||||
} elseif ($contentType === 'application/json') {
|
||||
return function ($input) {
|
||||
$jsonDecoder = new Json();
|
||||
try {
|
||||
return $jsonDecoder->decode($input, Json::TYPE_ARRAY);
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
} elseif ($contentType === 'multipart/form-data') {
|
||||
return function ($input) {
|
||||
return $input;
|
||||
};
|
||||
}
|
||||
|
||||
return function ($input) {
|
||||
return $input;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,33 +2,34 @@
|
||||
|
||||
namespace App\Action;
|
||||
|
||||
use App\Service\SkiesService;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use App\Service\SkiesClientService;
|
||||
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Zend\Diactoros\Response\JsonResponse;
|
||||
use Zend\Diactoros\Response\TextResponse;
|
||||
|
||||
class ActivityAction extends AbstractAction
|
||||
{
|
||||
private $skiesService;
|
||||
/**
|
||||
* @var SkiesClientService
|
||||
*/
|
||||
private $skiesClient;
|
||||
|
||||
public function __construct(SkiesService $skiesService)
|
||||
public function __construct(SkiesClientService $skiesClient)
|
||||
{
|
||||
$this->skiesService = $skiesService;
|
||||
$this->skiesClient = $skiesClient;
|
||||
}
|
||||
|
||||
public function getList(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
||||
public function getList(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||
{
|
||||
return new JsonResponse($this->skiesService->getActivityList());
|
||||
$authHeader = $request->getHeaderLine("x-passthru-auth");
|
||||
return new JsonResponse($this->skiesClient->setAuthHeader($authHeader)->getActivities());
|
||||
}
|
||||
|
||||
public function get(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
||||
public function get(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||
{
|
||||
$id = $request->getAttribute(self::IDENTIFIER_NAME);
|
||||
return new JsonResponse($this->skiesService->getActivity($id));
|
||||
}
|
||||
|
||||
public function options(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
||||
{
|
||||
return new JsonResponse(true);
|
||||
$authHeader = $request->getHeaderLine("x-passthru-auth");
|
||||
return new JsonResponse($this->skiesClient->setAuthHeader($authHeader)->getActivity($id));
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,14 +2,14 @@
|
||||
|
||||
namespace App\Action;
|
||||
|
||||
use App\Service\SkiesService;
|
||||
use App\Service\SkiesClientService;
|
||||
use Interop\Container\ContainerInterface;
|
||||
|
||||
class ActivityFactory
|
||||
{
|
||||
public function __invoke(ContainerInterface $container)
|
||||
{
|
||||
$skiesService = $container->get(SkiesService::class);
|
||||
return new ActivityAction($skiesService);
|
||||
$skiesClient = $container->get(SkiesClientService::class);
|
||||
return new ActivityAction($skiesClient);
|
||||
}
|
||||
}
|
||||
|
||||
29
src/App/Action/ActivitySignoffAction.php
Normal file
29
src/App/Action/ActivitySignoffAction.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Action;
|
||||
|
||||
use App\Service\SkiesClientService;
|
||||
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||
use Interop\Http\ServerMiddleware\MiddlewareInterface as ServerMiddlewareInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Zend\Diactoros\Response\JsonResponse;
|
||||
|
||||
class ActivitySignoffAction implements ServerMiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* @var SkiesClientService
|
||||
*/
|
||||
private $skiesClient;
|
||||
|
||||
public function __construct(SkiesClientService $skiesClient)
|
||||
{
|
||||
$this->skiesClient = $skiesClient;
|
||||
}
|
||||
|
||||
public function process(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||
{
|
||||
$authHeader = $request->getHeaderLine("x-passthru-auth");
|
||||
$id = $request->getAttribute("id");
|
||||
return new JsonResponse($this->skiesClient->setAuthHeader($authHeader)->signOffActivity($id));
|
||||
}
|
||||
}
|
||||
15
src/App/Action/ActivitySignoffFactory.php
Normal file
15
src/App/Action/ActivitySignoffFactory.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Action;
|
||||
|
||||
use App\Service\SkiesClientService;
|
||||
use Interop\Container\ContainerInterface;
|
||||
|
||||
class ActivitySignoffFactory
|
||||
{
|
||||
public function __invoke(ContainerInterface $container)
|
||||
{
|
||||
$skiesClient = $container->get(SkiesClientService::class);
|
||||
return new ActivitySignoffAction($skiesClient);
|
||||
}
|
||||
}
|
||||
29
src/App/Action/ActivitySignupAction.php
Normal file
29
src/App/Action/ActivitySignupAction.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Action;
|
||||
|
||||
use App\Service\SkiesClientService;
|
||||
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||
use Interop\Http\ServerMiddleware\MiddlewareInterface as ServerMiddlewareInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Zend\Diactoros\Response\JsonResponse;
|
||||
|
||||
class ActivitySignupAction implements ServerMiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* @var SkiesClientService
|
||||
*/
|
||||
private $skiesClient;
|
||||
|
||||
public function __construct(SkiesClientService $skiesClient)
|
||||
{
|
||||
$this->skiesClient = $skiesClient;
|
||||
}
|
||||
|
||||
public function process(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||
{
|
||||
$authHeader = $request->getHeaderLine("x-passthru-auth");
|
||||
$id = $request->getAttribute("id");
|
||||
return new JsonResponse($this->skiesClient->setAuthHeader($authHeader)->signUpActivity($id));
|
||||
}
|
||||
}
|
||||
15
src/App/Action/ActivitySignupFactory.php
Normal file
15
src/App/Action/ActivitySignupFactory.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Action;
|
||||
|
||||
use App\Service\SkiesClientService;
|
||||
use Interop\Container\ContainerInterface;
|
||||
|
||||
class ActivitySignupFactory
|
||||
{
|
||||
public function __invoke(ContainerInterface $container)
|
||||
{
|
||||
$skiesClient = $container->get(SkiesClientService::class);
|
||||
return new ActivitySignupAction($skiesClient);
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,8 @@
|
||||
|
||||
namespace App\Action;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||
use Interop\Http\ServerMiddleware\MiddlewareInterface as ServerMiddlewareInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Zend\Diactoros\Response\HtmlResponse;
|
||||
use Zend\Diactoros\Response\JsonResponse;
|
||||
@ -12,7 +13,7 @@ use Zend\Expressive\Plates\PlatesRenderer;
|
||||
use Zend\Expressive\Twig\TwigRenderer;
|
||||
use Zend\Expressive\ZendView\ZendViewRenderer;
|
||||
|
||||
class HomePageAction
|
||||
class HomePageAction implements ServerMiddlewareInterface
|
||||
{
|
||||
private $router;
|
||||
|
||||
@ -24,8 +25,15 @@ class HomePageAction
|
||||
$this->template = $template;
|
||||
}
|
||||
|
||||
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
||||
public function process(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||
{
|
||||
if (! $this->template) {
|
||||
return new JsonResponse([
|
||||
'welcome' => 'Congratulations! You have installed the zend-expressive skeleton application.',
|
||||
'docsUrl' => 'https://docs.zendframework.com/zend-expressive/',
|
||||
]);
|
||||
}
|
||||
|
||||
$data = [];
|
||||
|
||||
if ($this->router instanceof Router\AuraRouter) {
|
||||
@ -36,7 +44,7 @@ class HomePageAction
|
||||
$data['routerDocs'] = 'https://github.com/nikic/FastRoute';
|
||||
} elseif ($this->router instanceof Router\ZendRouter) {
|
||||
$data['routerName'] = 'Zend Router';
|
||||
$data['routerDocs'] = 'http://framework.zend.com/manual/current/en/modules/zend.mvc.routing.html';
|
||||
$data['routerDocs'] = 'https://docs.zendframework.com/zend-router/';
|
||||
}
|
||||
|
||||
if ($this->template instanceof PlatesRenderer) {
|
||||
@ -47,14 +55,7 @@ class HomePageAction
|
||||
$data['templateDocs'] = 'http://twig.sensiolabs.org/documentation';
|
||||
} elseif ($this->template instanceof ZendViewRenderer) {
|
||||
$data['templateName'] = 'Zend View';
|
||||
$data['templateDocs'] = 'http://framework.zend.com/manual/current/en/modules/zend.view.quick-start.html';
|
||||
}
|
||||
|
||||
if (!$this->template) {
|
||||
return new JsonResponse([
|
||||
'welcome' => 'Congratulations! You have installed the zend-expressive skeleton application.',
|
||||
'docsUrl' => 'zend-expressive.readthedocs.org',
|
||||
]);
|
||||
$data['templateDocs'] = 'https://docs.zendframework.com/zend-view/';
|
||||
}
|
||||
|
||||
return new HtmlResponse($this->template->render('app::home-page', $data));
|
||||
|
||||
@ -11,7 +11,7 @@ class HomePageFactory
|
||||
public function __invoke(ContainerInterface $container)
|
||||
{
|
||||
$router = $container->get(RouterInterface::class);
|
||||
$template = ($container->has(TemplateRendererInterface::class))
|
||||
$template = $container->has(TemplateRendererInterface::class)
|
||||
? $container->get(TemplateRendererInterface::class)
|
||||
: null;
|
||||
|
||||
|
||||
18
src/App/Action/NewsAction.php
Normal file
18
src/App/Action/NewsAction.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Action;
|
||||
|
||||
use App\Service\SkiesClientService;
|
||||
|
||||
class NewsAction
|
||||
{
|
||||
/**
|
||||
* @var SkiesClientService
|
||||
*/
|
||||
private $skiesClient;
|
||||
|
||||
public function __construct(SkiesClientService $skiesClient)
|
||||
{
|
||||
$this->skiesClient = $skiesClient;
|
||||
}
|
||||
}
|
||||
15
src/App/Action/NewsFactory.php
Normal file
15
src/App/Action/NewsFactory.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Action;
|
||||
|
||||
use App\Service\SkiesClientService;
|
||||
use Interop\Container\ContainerInterface;
|
||||
|
||||
class NewsFactory
|
||||
{
|
||||
public function __invoke(ContainerInterface $container)
|
||||
{
|
||||
$skiesClient = $container->get(SkiesClientService::class);
|
||||
return new NewsAction($skiesClient);
|
||||
}
|
||||
}
|
||||
@ -2,16 +2,15 @@
|
||||
|
||||
namespace App\Action;
|
||||
|
||||
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||
use Interop\Http\ServerMiddleware\MiddlewareInterface as ServerMiddlewareInterface;
|
||||
use Zend\Diactoros\Response\JsonResponse;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
class PingAction
|
||||
class PingAction implements ServerMiddlewareInterface
|
||||
{
|
||||
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
||||
public function process(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||
{
|
||||
return new JsonResponse([
|
||||
'ack' => time(),
|
||||
]);
|
||||
return new JsonResponse(['ack' => time()]);
|
||||
}
|
||||
}
|
||||
|
||||
66
src/App/ConfigProvider.php
Normal file
66
src/App/ConfigProvider.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
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.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __invoke()
|
||||
{
|
||||
return [
|
||||
'dependencies' => $this->getDependencies(),
|
||||
'templates' => $this->getTemplates(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the container dependencies
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDependencies()
|
||||
{
|
||||
return [
|
||||
'invokables' => [
|
||||
Action\PingAction::class => Action\PingAction::class,
|
||||
],
|
||||
'factories' => [
|
||||
Action\HomePageAction::class => Action\HomePageFactory::class,
|
||||
Action\ActivityAction::class => Action\ActivityFactory::class,
|
||||
Action\ActivitySignupAction::class => Action\ActivitySignupFactory::class,
|
||||
Action\ActivitySignoffAction::class => Action\ActivitySignoffFactory::class,
|
||||
Action\NewsAction::class => Action\NewsFactory::class,
|
||||
|
||||
Service\SkiesClientService::class => Service\SkiesClientServiceFactory::class,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the templates configuration
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTemplates()
|
||||
{
|
||||
return [
|
||||
'paths' => [
|
||||
'app' => ['templates/app'],
|
||||
'error' => ['templates/error'],
|
||||
'layout' => ['templates/layout'],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
305
src/App/Entity/Activity.php
Normal file
305
src/App/Entity/Activity.php
Normal file
@ -0,0 +1,305 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
||||
class Activity implements \JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*/
|
||||
private $date;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $description;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*/
|
||||
private $finalEntry;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $accountable;
|
||||
|
||||
/**
|
||||
* @var ArrayCollection|Comment[]
|
||||
*/
|
||||
private $comments;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $signedUsers;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $isSignedup = false;
|
||||
|
||||
/**
|
||||
* Can sign up or sign off activity
|
||||
* @var bool
|
||||
*/
|
||||
private $canChangeSignup = false;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->comments = new ArrayCollection();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getId(): int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @return Activity
|
||||
*/
|
||||
public function setId(int $id)
|
||||
{
|
||||
$this->id = $id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): ?string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return Activity
|
||||
*/
|
||||
public function setName(?string $name): Activity
|
||||
{
|
||||
$this->name = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getDate(): ?\DateTime
|
||||
{
|
||||
return $this->date;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTime $date
|
||||
* @return Activity
|
||||
*/
|
||||
public function setDate(?\DateTime $date): Activity
|
||||
{
|
||||
$this->date = $date;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription(): ?string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $description
|
||||
* @return Activity
|
||||
*/
|
||||
public function setDescription(?string $description): Activity
|
||||
{
|
||||
$this->description = $description;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getFinalEntry(): ?\DateTime
|
||||
{
|
||||
return $this->finalEntry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTime $finalEntry
|
||||
* @return Activity
|
||||
*/
|
||||
public function setFinalEntry(\DateTime $finalEntry): Activity
|
||||
{
|
||||
$this->finalEntry = $finalEntry;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAccountable(): ?string
|
||||
{
|
||||
return $this->accountable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accountable
|
||||
* @return Activity
|
||||
*/
|
||||
public function setAccountable(string $accountable): Activity
|
||||
{
|
||||
$this->accountable = $accountable;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Comment[]|ArrayCollection
|
||||
*/
|
||||
public function getComments()
|
||||
{
|
||||
return $this->comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Comment[]|ArrayCollection $comments
|
||||
* @return Activity
|
||||
*/
|
||||
public function setComments($comments): Activity
|
||||
{
|
||||
$this->comments = $comments;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Comment $comment
|
||||
* @return Activity
|
||||
*/
|
||||
public function addComment(Comment $comment): Activity
|
||||
{
|
||||
if(!$this->comments->contains($comment)) {
|
||||
$this->comments->add($comment);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Comment $comment
|
||||
* @return Activity
|
||||
*/
|
||||
public function removeComment(Comment $comment): Activity
|
||||
{
|
||||
if($this->comments->contains($comment)) {
|
||||
$this->comments->removeElement($comment);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getSignedUsers(): ?array
|
||||
{
|
||||
return $this->signedUsers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $signedUsers
|
||||
* @return Activity
|
||||
*/
|
||||
public function setSignedUsers(array $signedUsers): Activity
|
||||
{
|
||||
$this->signedUsers = $signedUsers;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $signed
|
||||
* @return Activity
|
||||
*/
|
||||
public function addSignedUser(string $signed): Activity
|
||||
{
|
||||
$this->signedUsers[] = $signed;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isSignedup(): bool
|
||||
{
|
||||
return $this->isSignedup;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $isSignedup
|
||||
* @return Activity
|
||||
*/
|
||||
public function setIsSignedup(bool $isSignedup): Activity
|
||||
{
|
||||
$this->isSignedup = $isSignedup;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isCanChangeSignup(): bool
|
||||
{
|
||||
return $this->canChangeSignup;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $canChangeSignup
|
||||
* @return Activity
|
||||
*/
|
||||
public function setCanChangeSignup(bool $canChangeSignup): Activity
|
||||
{
|
||||
$this->canChangeSignup = $canChangeSignup;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify data which should be serialized to JSON
|
||||
* @link http://php.net/manual/en/jsonserializable.jsonserialize.php
|
||||
* @return mixed data which can be serialized by <b>json_encode</b>,
|
||||
* which is a value of any type other than a resource.
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return [
|
||||
'id' => $this->getId(),
|
||||
'name' => $this->getName(),
|
||||
'date' => $this->getDate()
|
||||
? $this->getDate()->format("Y-m-d H:i:s")
|
||||
: null,
|
||||
'description' => $this->getDescription(),
|
||||
'finalEntry' => $this->getFinalEntry()
|
||||
? $this->getFinalEntry()->format("Y-m-d H:i:s")
|
||||
: null,
|
||||
'accountable' => $this->getAccountable(),
|
||||
'comments' => $this->getComments()->getValues(),
|
||||
'signedUsers' => $this->getSignedUsers(),
|
||||
'canChangeSignup' => $this->isCanChangeSignup(),
|
||||
'isSignedUp' => $this->isSignedup()
|
||||
];
|
||||
}
|
||||
}
|
||||
93
src/App/Entity/Comment.php
Normal file
93
src/App/Entity/Comment.php
Normal file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
class Comment implements \JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $text;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*/
|
||||
private $createdAt;
|
||||
|
||||
/**
|
||||
* @var User
|
||||
*/
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getText(): string
|
||||
{
|
||||
return $this->text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $text
|
||||
* @return Comment
|
||||
*/
|
||||
public function setText(string $text): Comment
|
||||
{
|
||||
$this->text = $text;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getCreatedAt(): \DateTime
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTime $createdAt
|
||||
* @return Comment
|
||||
*/
|
||||
public function setCreatedAt(\DateTime $createdAt): Comment
|
||||
{
|
||||
$this->createdAt = $createdAt;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return User
|
||||
*/
|
||||
public function getUser(): User
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @return Comment
|
||||
*/
|
||||
public function setUser(User $user): Comment
|
||||
{
|
||||
$this->user = $user;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify data which should be serialized to JSON
|
||||
* @link http://php.net/manual/en/jsonserializable.jsonserialize.php
|
||||
* @return mixed data which can be serialized by <b>json_encode</b>,
|
||||
* which is a value of any type other than a resource.
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return [
|
||||
'text' => $this->getText(),
|
||||
'user' => $this->getUser(),
|
||||
'createdAt' => $this->getCreatedAt()
|
||||
? $this->getCreatedAt()->format("Y-m-d H:i:s")
|
||||
: null,
|
||||
];
|
||||
}
|
||||
}
|
||||
8
src/App/Entity/News.php
Normal file
8
src/App/Entity/News.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
class News
|
||||
{
|
||||
|
||||
}
|
||||
67
src/App/Entity/User.php
Normal file
67
src/App/Entity/User.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
class User implements \JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $username;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $displayName;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUsername(): string
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $username
|
||||
* @return User
|
||||
*/
|
||||
public function setUsername(string $username): User
|
||||
{
|
||||
$this->username = $username;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDisplayName(): string
|
||||
{
|
||||
return $this->displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $displayName
|
||||
* @return User
|
||||
*/
|
||||
public function setDisplayName(string $displayName): User
|
||||
{
|
||||
$this->displayName = $displayName;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify data which should be serialized to JSON
|
||||
* @link http://php.net/manual/en/jsonserializable.jsonserialize.php
|
||||
* @return mixed data which can be serialized by <b>json_encode</b>,
|
||||
* which is a value of any type other than a resource.
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return [
|
||||
'username' => $this->getUsername(),
|
||||
'displayName' => $this->getDisplayName(),
|
||||
];
|
||||
}
|
||||
}
|
||||
397
src/App/Service/SkiesClientService.php
Normal file
397
src/App/Service/SkiesClientService.php
Normal file
@ -0,0 +1,397 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Entity\Activity;
|
||||
use App\Entity\Comment;
|
||||
use App\Entity\User;
|
||||
use GuzzleHttp\Client;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Zend\Dom\Document;
|
||||
use Zend\Expressive\Exception\MissingDependencyException;
|
||||
|
||||
class SkiesClientService
|
||||
{
|
||||
const SKIES_MAIN_URL = "https://skies.sigmatechnology.se/main.asp";
|
||||
const SKIES_PROFILE_URL = "https://skies.sigmatechnology.se/main.asp?rID=1&alt=2&username=%s";
|
||||
const SKIES_ACTIVITIES_URL = "https://skies.sigmatechnology.se/main.asp?rID=2";
|
||||
const SKIES_ACTIVITY_URL = "https://skies.sigmatechnology.se/main.asp?rID=2&alt=1&aktID=%s";
|
||||
const SKIES_ACTIVITY_SIGNUP_URL = "https://skies.sigmatechnology.se/main.asp?rID=2&alt=1&aktID=%s&doJoin=1";
|
||||
const SKIES_ACTIVITY_SIGNOFF_URL = "https://skies.sigmatechnology.se/main.asp?rID=2&alt=1&aktID=%s&doCancel=1&user=%s";
|
||||
|
||||
/**
|
||||
* @var Client
|
||||
*/
|
||||
private $client;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $authHeader = null;
|
||||
|
||||
public function __construct(Client $client)
|
||||
{
|
||||
$this->client = $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $authHeader
|
||||
* @return SkiesClientService
|
||||
*/
|
||||
public function setAuthHeader(string $authHeader): SkiesClientService
|
||||
{
|
||||
$this->authHeader = $authHeader;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNews()
|
||||
{
|
||||
$response = $this->doSkiesRequest('GET', self::SKIES_MAIN_URL);
|
||||
$htmlBody = $response->getBody();
|
||||
return $this->parseMainPage($htmlBody);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Activity[]
|
||||
*/
|
||||
public function getActivities()
|
||||
{
|
||||
$response = $this->doSkiesRequest('GET', self::SKIES_ACTIVITIES_URL);
|
||||
$htmlBody = $response->getBody();
|
||||
return $this->parseActivitiesPage($htmlBody);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $htmlBody
|
||||
* @return Activity[]
|
||||
*/
|
||||
private function parseActivitiesPage(string $htmlBody)
|
||||
{
|
||||
$domDocument = new Document($htmlBody);
|
||||
$eventNodes = Document\Query::execute(
|
||||
"div.container div.row div.box > table tr",
|
||||
$domDocument,
|
||||
Document\Query::TYPE_CSS
|
||||
);
|
||||
|
||||
$activities = [];
|
||||
|
||||
for ($i = 1; $i < $eventNodes->count(); $i++) {
|
||||
$eventRow = $eventNodes[$i];
|
||||
$rowCells = Document\Query::execute(
|
||||
"./td",
|
||||
$domDocument,
|
||||
Document\Query::TYPE_XPATH,
|
||||
$eventRow
|
||||
);
|
||||
|
||||
$href = $rowCells[0]->childNodes->item(0)->getAttribute("href");
|
||||
$queryString = parse_url($href, PHP_URL_QUERY);
|
||||
parse_str($queryString, $queryParams);
|
||||
$activities[] = $this->getActivity($queryParams["aktID"]);
|
||||
}
|
||||
|
||||
return $activities;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @return Activity
|
||||
*/
|
||||
public function getActivity(int $id): Activity
|
||||
{
|
||||
$response = $this->doSkiesRequest('GET', sprintf(self::SKIES_ACTIVITY_URL, $id));
|
||||
$htmlBody = $response->getBody();
|
||||
return $this->parseActivityPage($htmlBody, $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @return Activity
|
||||
*/
|
||||
public function signUpActivity(int $id): Activity
|
||||
{
|
||||
$this->doSkiesRequest('POST', sprintf(self::SKIES_ACTIVITY_SIGNUP_URL, $id), [
|
||||
'form_params' => [
|
||||
'user' => $this->getUsername(),
|
||||
],
|
||||
]);
|
||||
return $this->getActivity($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @return Activity
|
||||
*/
|
||||
public function signOffActivity(int $id): Activity
|
||||
{
|
||||
$username = $this->getUsername();
|
||||
$this->doSkiesRequest('GET', sprintf(self::SKIES_ACTIVITY_SIGNOFF_URL, $id, $username));
|
||||
return $this->getActivity($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $htmlBody
|
||||
* @param int $id
|
||||
* @return Activity
|
||||
*/
|
||||
private function parseActivityPage(string $htmlBody, int $id): Activity
|
||||
{
|
||||
$domDocument = new Document($htmlBody);
|
||||
/** Activity body */
|
||||
$activityNode = Document\Query::execute(
|
||||
"div.col-md-10 div.box",
|
||||
$domDocument,
|
||||
Document\Query::TYPE_CSS
|
||||
);
|
||||
|
||||
/** Activity name */
|
||||
$h5Nodes = Document\Query::execute(
|
||||
"./h5",
|
||||
$domDocument,
|
||||
Document\Query::TYPE_XPATH,
|
||||
$activityNode[0]
|
||||
);
|
||||
|
||||
$divNodes = Document\Query::execute(
|
||||
".//div",
|
||||
$domDocument,
|
||||
Document\Query::TYPE_XPATH,
|
||||
$activityNode[0]
|
||||
);
|
||||
|
||||
/** Comment block */
|
||||
$commentNodes = Document\Query::execute(
|
||||
'.//div[@class="onecomment"]',
|
||||
$domDocument,
|
||||
Document\Query::TYPE_XPATH,
|
||||
$activityNode[0]
|
||||
);
|
||||
|
||||
/** Signed users block */
|
||||
$signedUserParagraph = Document\Query::execute(
|
||||
'//div[@class="portlet"]//div[@class="paragraph"]/text()',
|
||||
$domDocument,
|
||||
Document\Query::TYPE_XPATH
|
||||
);
|
||||
|
||||
preg_match("#Date:.*?([0-9]{4}-[0-9]{2}-[0-9]{2}).*?Time: ([0-9]{1,2}:[0-9]{2}).*?Final entry day:.*?([0-9]{4}-[0-9]{2}-[0-9]{2}).*?Accountable: (.*?) / ([0-9 +]*)#msi", $divNodes[1]->textContent, $matches);
|
||||
|
||||
$signedUsers = $this->parseActivitySignedUsers($signedUserParagraph);
|
||||
$isSignedUp = in_array($this->getDisplayName($this->getUsername()), $signedUsers);
|
||||
$canChangeSignup = $isSignedUp
|
||||
? $this->canSignOff($domDocument, $activityNode[0])
|
||||
: $this->canSignup($domDocument, $activityNode[0]);
|
||||
|
||||
$activity = new Activity();
|
||||
$activity->setId($id)
|
||||
->setName($this->clearTextNode($h5Nodes[0]->textContent))
|
||||
->setDescription($this->parseActivityDescription($activityNode[0]))
|
||||
->setDate(new \DateTime(sprintf("%s %s", $matches[1], $matches[2])))
|
||||
->setFinalEntry(new \DateTime($matches[3]))
|
||||
->setAccountable(str_replace(
|
||||
" ",
|
||||
" ",
|
||||
$matches[5]
|
||||
? sprintf("%s (%s)", $matches[4], $matches[5])
|
||||
: $matches[4])
|
||||
)
|
||||
->setSignedUsers($signedUsers)
|
||||
->setIsSignedup($isSignedUp)
|
||||
->setCanChangeSignup($canChangeSignup);
|
||||
$this->parseActivityComments($commentNodes, $activity);
|
||||
|
||||
return $activity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DOMNode $element
|
||||
* @return null|string
|
||||
*/
|
||||
private function parseActivityDescription(\DOMNode $element): ?string
|
||||
{
|
||||
$description = "";
|
||||
$isContent = false;
|
||||
/** @var \DOMElement $childNode */
|
||||
foreach ($element->childNodes as $childNode) {
|
||||
if ($childNode->nodeName == "hr") {
|
||||
$isContent = true;
|
||||
}
|
||||
if ($childNode->nodeName == "form") {
|
||||
break;
|
||||
}
|
||||
if ($childNode->nodeName == "span") {
|
||||
break;
|
||||
}
|
||||
if ($childNode->nodeName == "script") {
|
||||
break;
|
||||
}
|
||||
if ($isContent) {
|
||||
$description .= $childNode->nodeName == "br"
|
||||
? "\n"
|
||||
: rtrim($childNode->textContent);
|
||||
}
|
||||
}
|
||||
return $this->clearTextNode($description);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Document\NodeList $commentElements
|
||||
* @param Activity $activity
|
||||
*/
|
||||
private function parseActivityComments(Document\NodeList $commentElements, Activity $activity)
|
||||
{
|
||||
/** @var \DOMElement $commentElement */
|
||||
foreach ($commentElements as $commentElement) {
|
||||
$divElements = $commentElement->getElementsByTagName("div");
|
||||
|
||||
$queryString = parse_url(
|
||||
$commentElement->getElementsByTagName("a")->item(0)->getAttribute("href"),
|
||||
PHP_URL_QUERY
|
||||
);
|
||||
parse_str($queryString, $queryParams);
|
||||
preg_match(
|
||||
"#(.*)\s/\s([0-9]{4}-[0-9]{2}-[0-9]{2})\s([0-9]{1,2}:[0-9]{1,2})#msi",
|
||||
str_replace(" ", " ", $divElements->item(2)->textContent),
|
||||
$matches
|
||||
);
|
||||
|
||||
$user = new User();
|
||||
$user->setDisplayName($matches[1])
|
||||
->setUsername($queryParams['username']);
|
||||
|
||||
$comment = new Comment();
|
||||
$comment
|
||||
->setText($divElements->item(1)->textContent)
|
||||
->setUser($user)
|
||||
->setCreatedAt(new \DateTime(sprintf(
|
||||
"%s %s",
|
||||
$matches[2],
|
||||
$matches[3]
|
||||
)));
|
||||
$activity->addComment($comment);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Document\NodeList $usersBlockText
|
||||
* @return array
|
||||
* @todo remove unsign button if it is present
|
||||
*/
|
||||
private function parseActivitySignedUsers(Document\NodeList $usersBlockText): array
|
||||
{
|
||||
$signed = [];
|
||||
for ($i = 1; $i < $usersBlockText->count(); $i++) {
|
||||
preg_match(
|
||||
"#[0-9]+\.[^\p{L}]([\p{L}\s]+)#msiu",
|
||||
$usersBlockText[$i]->textContent,
|
||||
$usernameMatch
|
||||
);
|
||||
$signed[] = $usernameMatch[1];
|
||||
}
|
||||
$collator = new \Collator("hu_HU");
|
||||
$collator->sort($signed);
|
||||
return $signed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can sign off if there is no red span inside the node
|
||||
* @param Document $domDocument
|
||||
* @param \DOMNode $domNode
|
||||
* @return bool
|
||||
*/
|
||||
private function canSignOff(Document $domDocument, \DOMNode $domNode): bool
|
||||
{
|
||||
$redSpanNodes = Document\Query::execute(
|
||||
'./span[@class="red"]',
|
||||
$domDocument,
|
||||
Document\Query::TYPE_XPATH,
|
||||
$domNode
|
||||
);
|
||||
return $redSpanNodes->count() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can sign up if there is a form direct descendant
|
||||
* @param Document $domDocument
|
||||
* @param \DOMNode $domNode
|
||||
* @return bool
|
||||
*/
|
||||
private function canSignUp(Document $domDocument, \DOMNode $domNode): bool
|
||||
{
|
||||
$formNodes = Document\Query::execute(
|
||||
"./form",
|
||||
$domDocument,
|
||||
Document\Query::TYPE_XPATH,
|
||||
$domNode
|
||||
);
|
||||
return $formNodes->count() == 1;
|
||||
}
|
||||
|
||||
private function parseMainPage(string $htmlBody)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private function getDisplayName(string $username): string
|
||||
{
|
||||
$response = $this->doSkiesRequest("GET", sprintf(self::SKIES_PROFILE_URL, $username));
|
||||
$profilePage = $response->getBody();
|
||||
|
||||
$domDocument = new Document($profilePage);
|
||||
$h1Nodes = Document\Query::execute(
|
||||
'//div[@class="box"]/h1',
|
||||
$domDocument,
|
||||
Document\Query::TYPE_XPATH
|
||||
);
|
||||
return $this->clearTextNode($h1Nodes[0]->textContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear junk from text nodes
|
||||
*
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
private function clearTextNode(string $text): string
|
||||
{
|
||||
$text = str_replace(" ", " ", $text);
|
||||
$text = str_replace("", "-", $text);
|
||||
$text = preg_replace("#[ \t]+#msiu", " ", $text);
|
||||
return trim($text, " \t\n\r\0\x0B" . chr(0xC2) . chr(0xA0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Do an http request adding the Authorization header
|
||||
*
|
||||
* @param string $method
|
||||
* @param string $url
|
||||
* @param array $options
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
private function doSkiesRequest(string $method, string $url, $options = []): ResponseInterface
|
||||
{
|
||||
if ($this->authHeader == null) {
|
||||
throw new MissingDependencyException("X-Passthru-Auth header is missing");
|
||||
}
|
||||
return $this->client
|
||||
->request($method, $url, [
|
||||
'headers' => [
|
||||
'Authorization' => "Basic {$this->authHeader}",
|
||||
]
|
||||
] + $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function getUsername(): string
|
||||
{
|
||||
if (null == $this->authHeader) {
|
||||
throw new MissingDependencyException("X-Passthru-Auth header is missing");
|
||||
}
|
||||
$decodedHeader = base64_decode($this->authHeader);
|
||||
list($username) = explode(":", $decodedHeader);
|
||||
return $username;
|
||||
}
|
||||
}
|
||||
17
src/App/Service/SkiesClientServiceFactory.php
Normal file
17
src/App/Service/SkiesClientServiceFactory.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use Interop\Container\ContainerInterface;
|
||||
|
||||
class SkiesClientServiceFactory
|
||||
{
|
||||
public function __invoke(ContainerInterface $container)
|
||||
{
|
||||
$httpClient = new Client([
|
||||
'cookies' => true,
|
||||
]);
|
||||
return new SkiesClientService($httpClient);
|
||||
}
|
||||
}
|
||||
@ -1,187 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use Interop\Container\ContainerInterface;
|
||||
use Symfony\Component\CssSelector\CssSelectorConverter;
|
||||
use Zend\Http\Client;
|
||||
|
||||
/**
|
||||
* Class SkiesService
|
||||
*
|
||||
* @package App\Service
|
||||
* @todo handle errors in http response
|
||||
*/
|
||||
class SkiesService
|
||||
{
|
||||
const BASE_URI = 'http://skies.sigmatechnology.se/';
|
||||
|
||||
/**
|
||||
* @var Client
|
||||
*/
|
||||
private $httpClient;
|
||||
/**
|
||||
* @var ContainerInterface
|
||||
*/
|
||||
private $container;
|
||||
|
||||
public function __construct(ContainerInterface $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->httpClient = new Client();
|
||||
$headers = $this->httpClient->getRequest()->getHeaders();
|
||||
$headers->addHeaderLine('Authorization', 'Basic ' . $this->getAuthToken());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all activities
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getActivityList()
|
||||
{
|
||||
$this->httpClient->setUri(self::BASE_URI . "main.asp?rID=2");
|
||||
$skiesResponse = $this->httpClient->send();
|
||||
$skiesHtmlBody = $skiesResponse->getBody();
|
||||
|
||||
$cssToXpathConverter = new CssSelectorConverter();
|
||||
$xpathQuery = $cssToXpathConverter->toXPath('html > body > div > div.row > div.col-md-10 > div.row > div.col-md-9 > div.box > table.table-striped > tr');
|
||||
|
||||
$doc = new \DOMDocument();
|
||||
$doc->loadHTML($skiesHtmlBody);
|
||||
|
||||
$xpath = new \DOMXPath($doc);
|
||||
$elements = $xpath->query($xpathQuery);
|
||||
|
||||
$parsed = [];
|
||||
/** @var \DOMNode $domElement */
|
||||
foreach ($elements as $domElement) {
|
||||
if($domElement->childNodes->item(0)->nodeName != 'td') {
|
||||
continue;
|
||||
}
|
||||
$url = $domElement->childNodes->item(0)->childNodes->item(0)->attributes->getNamedItem('href')->textContent;
|
||||
preg_match("/aktid=([0-9]+)/msi", $url, $match);
|
||||
$parsed[] = [
|
||||
'id' => (int)$match[1],
|
||||
'label' => $domElement->childNodes->item(0)->childNodes->item(0)->textContent,
|
||||
'date' => $domElement->childNodes->item(2)->textContent,
|
||||
'time' => trim($domElement->childNodes->item(3)->textContent, " \t\n\r\0\x0B\xc2\xa0"),
|
||||
];
|
||||
}
|
||||
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single activity
|
||||
*
|
||||
* @param int $id
|
||||
* @return array
|
||||
*/
|
||||
public function getActivity(int $id)
|
||||
{
|
||||
$this->httpClient->setUri(self::BASE_URI . "main.asp?rID=2&alt=1&aktID=" . $id);
|
||||
$skiesResponse = $this->httpClient->send();
|
||||
$skiesHtmlBody = $skiesResponse->getBody();
|
||||
|
||||
$cssToXpathConverter = new CssSelectorConverter();
|
||||
$xpathQuery = $cssToXpathConverter->toXPath('div.container > div.row > div.col-md-10 > div.row > div.col-md-9 > div.box');
|
||||
$xpathCommentsQuery = $cssToXpathConverter->toXPath('div.container div.paragraph > div.onecomment');
|
||||
|
||||
$doc = new \DOMDocument();
|
||||
$doc->loadHTML($skiesHtmlBody);
|
||||
|
||||
$xpath = new \DOMXPath($doc);
|
||||
$elements = $xpath->query($xpathQuery);
|
||||
|
||||
$h5Elements = $elements->item(0)->getElementsByTagName('h5');
|
||||
$title = $h5Elements->item(0)->textContent;
|
||||
|
||||
$detailDivElements = $elements->item(0)->getElementsByTagName('div');
|
||||
$commentElements = $xpath->query($xpathCommentsQuery);
|
||||
|
||||
$eventDetails = $this->parseActivityDetails($detailDivElements->item(1)->textContent);
|
||||
$eventDescription = $this->parseActivityDescription($elements->item(0));
|
||||
$eventComments = $this->parseActivityComments($commentElements);
|
||||
|
||||
return [
|
||||
'title' => $title,
|
||||
'details' => $eventDetails,
|
||||
'description' => $eventDescription,
|
||||
'comments' => $eventComments,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|string
|
||||
* @todo real auth token from whatever...
|
||||
*/
|
||||
private function getAuthToken(): ?string
|
||||
{
|
||||
$config = $this->container->get('config');
|
||||
return $config['authKey'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the activity information details returning the date, time finaly entry and the person accountable
|
||||
*
|
||||
* @param string $divText
|
||||
* @return array|null
|
||||
*/
|
||||
private function parseActivityDetails(string $divText): ?array
|
||||
{
|
||||
preg_match("#Date:.*?([0-9]{4}-[0-9]{2}-[0-9]{2}).*?Time: ([0-9]{1,2}:[0-9]{2}).*?Final entry day:.*?([0-9]{4}-[0-9]{2}-[0-9]{2}).*?Accountable: (.*?) / ([0-9 +]*)#msi", $divText, $matches);
|
||||
return [
|
||||
'date' => $matches[1],
|
||||
'time' => $matches[2],
|
||||
'finalEntry' => $matches[3],
|
||||
'accountable' => str_replace(" ", " ", $matches[5] ? sprintf("%s (%s)", $matches[4], $matches[5]) : $matches[4]),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses comment block. Comment section is pretty much unformatted text
|
||||
* between a <hr> tag and a <form> tag in the passed $element
|
||||
*
|
||||
* @param \DOMElement $element
|
||||
* @return null|string
|
||||
*/
|
||||
private function parseActivityDescription(\DOMElement $element): ?string
|
||||
{
|
||||
$description = "";
|
||||
$isContent = false;
|
||||
/** @var \DOMElement $childNode */
|
||||
foreach($element->childNodes as $childNode) {
|
||||
if ($childNode->nodeName == "hr") { $isContent = true; }
|
||||
if ($childNode->nodeName == "form") { break; }
|
||||
if ($isContent) {
|
||||
$description .= $childNode->nodeName == "br"
|
||||
? "\n"
|
||||
: rtrim($childNode->textContent);
|
||||
}
|
||||
}
|
||||
return trim($description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse $commentElements
|
||||
*
|
||||
* @param \DOMNodeList $commentElements
|
||||
* @return array|null
|
||||
*/
|
||||
private function parseActivityComments(\DOMNodeList $commentElements): ?array
|
||||
{
|
||||
$comments = [];
|
||||
/** @var \DOMElement $commentElement */
|
||||
foreach($commentElements as $commentElement) {
|
||||
preg_match("#(.*)\s/\s([0-9]{4}-[0-9]{2}-[0-9]{2})\s([0-9]{1,2}:[0-9]{1,2})#msi", str_replace(" "," ", $commentElement->getElementsByTagName("div")->item(2)->textContent), $matches);
|
||||
$comments[] = [
|
||||
'comment' => $commentElement->getElementsByTagName("div")->item(1)->textContent,
|
||||
'user' => $matches[1],
|
||||
'date' => $matches[2],
|
||||
'time' => $matches[3],
|
||||
];
|
||||
}
|
||||
return $comments;
|
||||
}
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use Interop\Container\ContainerInterface;
|
||||
|
||||
class SkiesServiceFactory
|
||||
{
|
||||
public function __invoke(ContainerInterface $container)
|
||||
{
|
||||
return new SkiesService($container);
|
||||
}
|
||||
}
|
||||
@ -3,11 +3,16 @@
|
||||
namespace AppTest\Action;
|
||||
|
||||
use App\Action\HomePageAction;
|
||||
use Zend\Diactoros\Response;
|
||||
use Zend\Diactoros\ServerRequest;
|
||||
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Prophecy\Argument;
|
||||
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 HomePageActionTest extends \PHPUnit_Framework_TestCase
|
||||
class HomePageActionTest extends TestCase
|
||||
{
|
||||
/** @var RouterInterface */
|
||||
protected $router;
|
||||
@ -17,12 +22,31 @@ class HomePageActionTest extends \PHPUnit_Framework_TestCase
|
||||
$this->router = $this->prophesize(RouterInterface::class);
|
||||
}
|
||||
|
||||
public function testResponse()
|
||||
public function testReturnsJsonResponseWhenNoTemplateRendererProvided()
|
||||
{
|
||||
$homePage = new HomePageAction($this->router->reveal(), null);
|
||||
$response = $homePage(new ServerRequest(['/']), new Response(), function () {
|
||||
});
|
||||
$response = $homePage->process(
|
||||
$this->prophesize(ServerRequestInterface::class)->reveal(),
|
||||
$this->prophesize(DelegateInterface::class)->reveal()
|
||||
);
|
||||
|
||||
$this->assertTrue($response instanceof Response);
|
||||
$this->assertInstanceOf(JsonResponse::class, $response);
|
||||
}
|
||||
|
||||
public function testReturnsHtmlResponseWhenTemplateRendererProvided()
|
||||
{
|
||||
$renderer = $this->prophesize(TemplateRendererInterface::class);
|
||||
$renderer
|
||||
->render('app::home-page', Argument::type('array'))
|
||||
->willReturn('');
|
||||
|
||||
$homePage = new HomePageAction($this->router->reveal(), $renderer->reveal());
|
||||
|
||||
$response = $homePage->process(
|
||||
$this->prophesize(ServerRequestInterface::class)->reveal(),
|
||||
$this->prophesize(DelegateInterface::class)->reveal()
|
||||
);
|
||||
|
||||
$this->assertInstanceOf(HtmlResponse::class, $response);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,10 +5,11 @@ namespace AppTest\Action;
|
||||
use App\Action\HomePageAction;
|
||||
use App\Action\HomePageFactory;
|
||||
use Interop\Container\ContainerInterface;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Zend\Expressive\Router\RouterInterface;
|
||||
use Zend\Expressive\Template\TemplateRendererInterface;
|
||||
|
||||
class HomePageFactoryTest extends \PHPUnit_Framework_TestCase
|
||||
class HomePageFactoryTest extends TestCase
|
||||
{
|
||||
/** @var ContainerInterface */
|
||||
protected $container;
|
||||
@ -26,11 +27,11 @@ class HomePageFactoryTest extends \PHPUnit_Framework_TestCase
|
||||
$factory = new HomePageFactory();
|
||||
$this->container->has(TemplateRendererInterface::class)->willReturn(false);
|
||||
|
||||
$this->assertTrue($factory instanceof HomePageFactory);
|
||||
$this->assertInstanceOf(HomePageFactory::class, $factory);
|
||||
|
||||
$homePage = $factory($this->container->reveal());
|
||||
|
||||
$this->assertTrue($homePage instanceof HomePageAction);
|
||||
$this->assertInstanceOf(HomePageAction::class, $homePage);
|
||||
}
|
||||
|
||||
public function testFactoryWithTemplate()
|
||||
@ -41,10 +42,10 @@ class HomePageFactoryTest extends \PHPUnit_Framework_TestCase
|
||||
->get(TemplateRendererInterface::class)
|
||||
->willReturn($this->prophesize(TemplateRendererInterface::class));
|
||||
|
||||
$this->assertTrue($factory instanceof HomePageFactory);
|
||||
$this->assertInstanceOf(HomePageFactory::class, $factory);
|
||||
|
||||
$homePage = $factory($this->container->reveal());
|
||||
|
||||
$this->assertTrue($homePage instanceof HomePageAction);
|
||||
$this->assertInstanceOf(HomePageAction::class, $homePage);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,20 +3,24 @@
|
||||
namespace AppTest\Action;
|
||||
|
||||
use App\Action\PingAction;
|
||||
use Zend\Diactoros\Response;
|
||||
use Zend\Diactoros\ServerRequest;
|
||||
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Zend\Diactoros\Response\JsonResponse;
|
||||
|
||||
class PingActionTest extends \PHPUnit_Framework_TestCase
|
||||
class PingActionTest extends TestCase
|
||||
{
|
||||
public function testResponse()
|
||||
{
|
||||
$pingAction = new PingAction();
|
||||
$response = $pingAction(new ServerRequest(['/']), new Response(), function () {
|
||||
});
|
||||
$response = $pingAction->process(
|
||||
$this->prophesize(ServerRequestInterface::class)->reveal(),
|
||||
$this->prophesize(DelegateInterface::class)->reveal()
|
||||
);
|
||||
|
||||
$json = json_decode((string) $response->getBody());
|
||||
|
||||
$this->assertTrue($response instanceof Response);
|
||||
$this->assertTrue($response instanceof Response\JsonResponse);
|
||||
$this->assertInstanceOf(JsonResponse::class, $response);
|
||||
$this->assertTrue(isset($json->ack));
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user