Skip to content

Instantly share code, notes, and snippets.

@geerteltink
Created June 22, 2017 14:35
Show Gist options
  • Save geerteltink/9a0f13b759e4581f4e69571fda764c7f to your computer and use it in GitHub Desktop.
Save geerteltink/9a0f13b759e4581f4e69571fda764c7f to your computer and use it in GitHub Desktop.
<?php
namespace App\Application\Console;
class CreateMiddleware
{
/**
* @var string Template for middleware class.
*/
const MIDDLEWARE_SKELETON = <<< 'EOS'
<?php
declare(strict_types=1);
namespace %namespace%;
use Interop\Http\ServerMiddleware\DelegateInterface;
use Interop\Http\ServerMiddleware\MiddlewareInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
class %class% implements MiddlewareInterface
{
public function __construct()
{
}
public function process(ServerRequestInterface $request, DelegateInterface $delegate): ResponseInterface
{
// Do something (with the request) first
// Call the next middleware and wait for the response
$response = $delegate->process($request);
// Do something (with the response) before returning the response
// Return the response
return $response;
}
}
EOS;
/**
* @var string Template for middleware class.
*/
const ACTION_SKELETON = <<< 'EOS'
<?php
declare(strict_types=1);
namespace %namespace%;
use App\Infrastructure\View\StreamRenderer;
use Interop\Http\ServerMiddleware\DelegateInterface;
use Interop\Http\ServerMiddleware\MiddlewareInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Diactoros\Response\HtmlResponse;
class %class% implements MiddlewareInterface
{
/**
* @var StreamRenderer
*/
private $renderer;
public function __construct(StreamRenderer $renderer)
{
$this->renderer = $renderer;
}
public function process(ServerRequestInterface $request, DelegateInterface $delegate): ResponseInterface
{
return new HtmlResponse($this->renderer->render($request, '%path%::%template%'));
}
}
EOS;
/**
* @var string Template for middleware class.
*/
const API_SKELETON = <<< 'EOS'
<?php
declare(strict_types=1);
namespace %namespace%;
use Interop\Http\ServerMiddleware\DelegateInterface;
use Interop\Http\ServerMiddleware\MiddlewareInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Diactoros\Response\JsonResponse;
class %class% implements MiddlewareInterface
{
public function __construct()
{
}
public function process(ServerRequestInterface $request, DelegateInterface $delegate): ResponseInterface
{
return new JsonResponse([]);
}
}
EOS;
const TEMPLATE_SKELETON = <<< 'EOS'
<?php $this->layout('layout::%path%', ['title' => '%template%']); ?>
<h1>%template%</h1>
EOS;
/**
* @param string $class
* @param string|null $projectRoot
*
* @return string
* @throws CreateMiddlewareException
*/
public function process($class, $projectRoot = null): string
{
$templatePath = $templateNamespace = $templateName = null;
$projectRoot = $projectRoot ?: getcwd();
$path = $this->getClassPath($class, $projectRoot);
$templateData = $this->getTemplateNamespaceAndName($class);
[$namespace, $class] = $this->getNamespaceAndClass($class);
$api = strpos($namespace, '\\Api');
// Middleware
$skeleton = self::MIDDLEWARE_SKELETON;
// HtmlResponse Action
if ($api === false && substr($class, -6) === 'Action') {
$skeleton = self::ACTION_SKELETON;
[$templateNamespace, $templatePath, $templateName] = $templateData;
}
// JsonResponse API Action
if ($api !== false) {
$skeleton = self::API_SKELETON;
}
$content = str_replace(
['%namespace%', '%class%', '%path%', '%template%'],
[$namespace, $class, $templateNamespace, $templatePath . $templateName],
$skeleton
);
if (is_file($path)) {
throw CreateMiddlewareException::unableToCreatePath($path, $class);
}
// Create middleware class
file_put_contents($path, $content);
if ($templatePath !== null) {
// Create template
$content = str_replace(
['%namespace%', '%class%', '%path%', '%template%'],
[$namespace, $class, $templateNamespace, $templatePath . $templateName],
self::TEMPLATE_SKELETON
);
file_put_contents(sprintf(
'resources/templates/%s/%s%s.phtml',
$templateNamespace,
$templatePath,
$templateName
), $content);
}
return $path;
}
/**
* @param string $class
* @param string $projectRoot
*
* @return string
* @throws CreateMiddlewareException
*/
private function getClassPath($class, $projectRoot): string
{
$autoloaders = $this->getComposerAutoloaders($projectRoot);
[$namespace, $path] = $this->discoverNamespaceAndPath($class, $autoloaders);
// Absolute path to namespace
$path = implode([$projectRoot, DIRECTORY_SEPARATOR, $path]);
$parts = explode('\\', $class);
$className = array_pop($parts);
// Create absolute path to subnamespace, if required
//$nsParts = explode('\\', trim($namespace, '\\'));
$subNsParts = array_slice($parts, substr_count(trim($namespace, '\\'), '\\') + 1);
if (0 < count($subNsParts)) {
$subNsPath = implode(DIRECTORY_SEPARATOR, $subNsParts);
$path = implode([$path, DIRECTORY_SEPARATOR, $subNsPath]);
}
// Create path if it does not exist
if (! @mkdir($path, 0755, true) && ! is_dir($path)) {
throw CreateMiddlewareException::unableToCreatePath($path, $class);
}
return $path . DIRECTORY_SEPARATOR . $className . '.php';
}
/**
* @param string $projectRoot
*
* @return array Associative array of namespace/path pairs
* @throws CreateMiddlewareException
*/
private function getComposerAutoloaders($projectRoot): array
{
$composerPath = sprintf('%s/composer.json', $projectRoot);
if (! file_exists($composerPath)) {
throw CreateMiddlewareException::missingComposerJson();
}
$composer = json_decode(file_get_contents($composerPath), true);
if (json_last_error() !== \JSON_ERROR_NONE) {
throw CreateMiddlewareException::invalidComposerJson(json_last_error_msg());
}
if (! isset($composer['autoload']['psr-4'])) {
throw CreateMiddlewareException::missingComposerAutoloaders();
}
if (! is_array($composer['autoload']['psr-4'])) {
throw CreateMiddlewareException::missingComposerAutoloaders();
}
return $composer['autoload']['psr-4'];
}
/**
* @param string $class
* @param array $autoloaders
*
* @return array [namespace, path]
* @throws CreateMiddlewareException
*/
private function discoverNamespaceAndPath($class, array $autoloaders): array
{
$discoveredPath = false;
foreach ($autoloaders as $namespace => $path) {
if (0 === strpos($class, $namespace)) {
$path = trim(
str_replace(
['/', '\\'],
DIRECTORY_SEPARATOR,
$path
),
DIRECTORY_SEPARATOR
);
return [$namespace, $path];
}
}
throw CreateMiddlewareException::autoloaderNotFound($class);
}
/**
* @param string $class
*
* @return array [namespace, class]
*/
private function getNamespaceAndClass($class): array
{
$parts = explode('\\', $class);
$className = array_pop($parts);
$namespace = implode('\\', $parts);
return [$namespace, $className];
}
private function getTemplateNamespaceAndName($class): array
{
$parts = explode('\\', $class);
if ($parts[0] = 'App') {
unset($parts[0]);
}
if ($parts[1] = 'Application') {
unset($parts[1]);
}
$namespace = strtolower(array_shift($parts));
$template = strtolower(array_pop($parts));
$template = preg_replace('/action$/', '', $template);
$path = sprintf('%s/', strtolower(implode('/', $parts)));
$realPath = sprintf('resources/templates/%s/%s/', $namespace, strtolower(implode('/', $parts)));
// Create path if it does not exist
if (! @mkdir($realPath, 0755, true) && ! is_dir($realPath)) {
throw CreateMiddlewareException::unableToCreatePath($realPath, $class);
}
return [$namespace, $path, $template];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment