Hack n' slash rework.
This commit is contained in:
parent
73a133097d
commit
be507e0d4c
20 changed files with 1390 additions and 507 deletions
478
src/App.php
478
src/App.php
|
|
@ -2,226 +2,144 @@
|
|||
|
||||
namespace Benzine;
|
||||
|
||||
use Benzine\ORM\Laminator;
|
||||
use Benzine\Services\ConfigurationService;
|
||||
use Benzine\Services\EnvironmentService;
|
||||
use Benzine\Services\SessionService;
|
||||
use Benzine\Twig\Extensions;
|
||||
use Cache\Adapter\Apc\ApcCachePool;
|
||||
use Cache\Adapter\Apcu\ApcuCachePool;
|
||||
use Cache\Adapter\Chain\CachePoolChain;
|
||||
use Cache\Adapter\PHPArray\ArrayCachePool;
|
||||
use Cache\Adapter\Redis\RedisCachePool;
|
||||
use DebugBar\Bridge\MonologCollector;
|
||||
use DebugBar\DebugBar;
|
||||
use DebugBar\StandardDebugBar;
|
||||
use DI\Container;
|
||||
use DI\ContainerBuilder;
|
||||
use Faker\Factory as FakerFactory;
|
||||
use Faker\Provider;
|
||||
use Monolog\Formatter\LineFormatter;
|
||||
use Monolog\Handler\ErrorLogHandler;
|
||||
use Monolog\Logger;
|
||||
use Monolog\Processor\PsrLogMessageProcessor;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use SebastianBergmann\Diff\Differ;
|
||||
use Slim;
|
||||
|
||||
use Slim\Factory\AppFactory;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
use Twig;
|
||||
use Twig\Loader\FilesystemLoader;
|
||||
|
||||
class App
|
||||
{
|
||||
public const DEFAULT_TIMEZONE = 'Europe/London';
|
||||
|
||||
/** @var App */
|
||||
public static $instance;
|
||||
private static bool $isInitialised = false;
|
||||
public static App $instance;
|
||||
|
||||
/** @var ConfigurationService */
|
||||
protected $configuration;
|
||||
/** @var \Slim\App */
|
||||
protected $app;
|
||||
/** @var Container\Container */
|
||||
protected $container;
|
||||
/** @var Log\Logger */
|
||||
protected $logger;
|
||||
protected EnvironmentService $environmentService;
|
||||
protected ConfigurationService $configurationService;
|
||||
protected \Slim\App $app;
|
||||
protected Logger $logger;
|
||||
protected bool $isSessionsEnabled = true;
|
||||
protected array $routePaths = [];
|
||||
protected array $viewPaths = [];
|
||||
protected bool $interrogateControllersComplete = false;
|
||||
|
||||
protected $isSessionsEnabled = true;
|
||||
|
||||
protected $containerAliases = [
|
||||
'view' => Slim\Views\Twig::class,
|
||||
'DatabaseInstance' => DbConfig::class,
|
||||
'Differ' => Differ::class,
|
||||
'HttpClient' => \GuzzleHttp\Client::class,
|
||||
'Faker' => \Faker\Generator::class,
|
||||
'Environment' => EnvironmentService::class,
|
||||
'Redis' => Redis\Redis::class,
|
||||
'Monolog' => Log\Logger::class,
|
||||
'Gone\AppCore\Logger' => Log\Logger::class,
|
||||
'Cache' => CachePoolChain::class,
|
||||
];
|
||||
|
||||
protected $routePaths = [];
|
||||
|
||||
protected $viewPaths = [];
|
||||
|
||||
protected $optionsDefaults = [];
|
||||
|
||||
protected $interrogateControllersComplete = false;
|
||||
|
||||
public function __construct($options = [])
|
||||
public function __construct()
|
||||
{
|
||||
$this->configuration = new Configuration();
|
||||
# $this->environmentService = new EnvironmentService();
|
||||
# $this->configurationService = new ConfigurationService($this->environmentService);
|
||||
|
||||
// Configure Dependency Injector
|
||||
$container = $this->setupContainer();
|
||||
AppFactory::setContainer($container);
|
||||
|
||||
// Configure Router
|
||||
$this->routePaths = [
|
||||
$this->configuration->get(Configuration::KEY_APP_ROOT).'/src/Routes.php',
|
||||
$this->configuration->get(Configuration::KEY_APP_ROOT).'/src/RoutesExtra.php',
|
||||
APP_ROOT.'/src/Routes.php',
|
||||
APP_ROOT.'/src/RoutesExtra.php',
|
||||
];
|
||||
|
||||
$options = array_merge($this->optionsDefaults, $options);
|
||||
$this->setup($container);
|
||||
|
||||
// Configure Slim
|
||||
$this->app = AppFactory::create();
|
||||
$this->app->add(Slim\Views\TwigMiddleware::createFromContainer($this->app));
|
||||
$this->app->addRoutingMiddleware();
|
||||
$errorMiddleware = $this->app->addErrorMiddleware(true, true, true);
|
||||
|
||||
if (isset($options['config'])) {
|
||||
if (is_string($options['config'])) {
|
||||
$configRealpath = $options['config'];
|
||||
if (!file_exists($configRealpath)) {
|
||||
throw new Exceptions\BenzineConfigurationException("Cant find {$configRealpath}.");
|
||||
}
|
||||
$this->configuration->configureFromYaml($options['config']);
|
||||
}
|
||||
}
|
||||
$this->setup();
|
||||
}
|
||||
|
||||
public function setup(): void
|
||||
protected function setup(ContainerInterface $container): void
|
||||
{
|
||||
// Create Slim app
|
||||
$this->app = new \Slim\App(
|
||||
new Container\Container([
|
||||
'settings' => [
|
||||
'debug' => $this->getConfiguration()->get(Configuration::KEY_DEBUG_ENABLE),
|
||||
'displayErrorDetails' => $this->getConfiguration()->get(Configuration::KEY_DEBUG_ENABLE),
|
||||
'determineRouteBeforeAppMiddleware' => true,
|
||||
],
|
||||
])
|
||||
);
|
||||
|
||||
// Fetch DI Container
|
||||
$this->container = $this->app->getContainer();
|
||||
// @todo remove this depenency on getting the container from Slim.
|
||||
//$this->container = new Container\Container();
|
||||
|
||||
$this->populateContainerAliases($this->container);
|
||||
|
||||
$this->setupDependencies();
|
||||
|
||||
$this->logger = $this->getContainer()->get(Log\Logger::class);
|
||||
|
||||
if (file_exists($this->configuration->get(Configuration::KEY_APP_ROOT).'/src/AppContainer.php')) {
|
||||
require $this->configuration->get(Configuration::KEY_APP_ROOT).'/src/AppContainer.php';
|
||||
}
|
||||
if (file_exists($this->configuration->get(Configuration::KEY_APP_ROOT).'/src/AppContainerExtra.php')) {
|
||||
require $this->configuration->get(Configuration::KEY_APP_ROOT).'/src/AppContainerExtra.php';
|
||||
}
|
||||
|
||||
$this->addRoutePathsRecursively($this->configuration->get(Configuration::KEY_APP_ROOT).'/src/Routes');
|
||||
$this->logger = $container->get(Logger::class);
|
||||
|
||||
if ('cli' != php_sapi_name() && $this->isSessionsEnabled) {
|
||||
$session = $this->getContainer()->get(Session\Session::class);
|
||||
$session = $container->get(SessionService::class);
|
||||
}
|
||||
|
||||
$this->setupMiddlewares();
|
||||
|
||||
if (class_exists(Controllers\Controller::class)) {
|
||||
$this->addViewPath($this->getContainer()->get(Controllers\Controller::class)->getViewsPath());
|
||||
if (file_exists($this->configuration->get(Configuration::KEY_APP_ROOT).'/views/')) {
|
||||
$this->addViewPath($this->configuration->get(Configuration::KEY_APP_ROOT).'/views/');
|
||||
}
|
||||
if (file_exists($this->configuration->get(Configuration::KEY_APP_ROOT).'/src/Views')) {
|
||||
$this->addViewPath($this->configuration->get(Configuration::KEY_APP_ROOT).'/src/Views');
|
||||
}
|
||||
|
||||
$this->interrogateControllers();
|
||||
}
|
||||
$this->setupMiddlewares($container);
|
||||
$this->viewPaths[] = APP_ROOT.'/views/';
|
||||
$this->viewPaths[] = APP_ROOT.'/src/Views/';
|
||||
$this->interrogateControllers();
|
||||
}
|
||||
|
||||
public function getConfiguration(): Configuration
|
||||
public function setupContainer(): Container
|
||||
{
|
||||
return $this->configuration;
|
||||
}
|
||||
$container =
|
||||
(new ContainerBuilder())
|
||||
->useAutowiring(true)
|
||||
->useAnnotations(true)
|
||||
#->enableCompilation(APP_ROOT . "/cache")
|
||||
#->writeProxiesToFile(true, APP_ROOT . "/cache/injection-proxies")
|
||||
->build();
|
||||
|
||||
public function getLogger(): Log\Logger
|
||||
{
|
||||
return $this->logger;
|
||||
}
|
||||
|
||||
public function setupDependencies(): void
|
||||
{
|
||||
// add PSR-15 support shim
|
||||
$this->container['callableResolver'] = function ($container) {
|
||||
return new \Bnf\Slim3Psr15\CallableResolver($container);
|
||||
};
|
||||
|
||||
// Register Twig View helper
|
||||
$this->container[Slim\Views\Twig::class] = function ($c) {
|
||||
$container->set(Slim\Views\Twig::class, function(ContainerInterface $container) {
|
||||
foreach ($this->viewPaths as $i => $viewLocation) {
|
||||
if (!file_exists($viewLocation) || !is_dir($viewLocation)) {
|
||||
unset($this->viewPaths[$i]);
|
||||
}
|
||||
}
|
||||
$settings = ['cache' => 'cache/twig'];
|
||||
$loader = new FilesystemLoader();
|
||||
|
||||
$view = new \Slim\Views\Twig(
|
||||
$this->viewPaths,
|
||||
[
|
||||
'cache' => false,
|
||||
'debug' => true,
|
||||
]
|
||||
);
|
||||
|
||||
// Instantiate and add Slim specific extension
|
||||
$view->addExtension(
|
||||
new Slim\Views\TwigExtension(
|
||||
$c['router'],
|
||||
$c['request']->getUri()
|
||||
)
|
||||
);
|
||||
|
||||
$view->addExtension(new Extensions\ArrayUniqueTwigExtension());
|
||||
$view->addExtension(new Extensions\FilterAlphanumericOnlyTwigExtension());
|
||||
|
||||
// Add coding string transform filters (ie: camel_case to StudlyCaps)
|
||||
$view->addExtension(new Extensions\TransformExtension());
|
||||
|
||||
// Add pluralisation/depluralisation support with singularize/pluralize filters
|
||||
$view->addExtension(new Extensions\InflectionExtension());
|
||||
|
||||
// Added Twig_Extension_Debug to enable twig dump() etc.
|
||||
$view->addExtension(new \Twig_Extension_Debug());
|
||||
$view->addExtension(new \Twig_Extensions_Extension_Date());
|
||||
$view->addExtension(new \Twig_Extensions_Extension_Text());
|
||||
|
||||
$view->offsetSet('app_name', $this->configuration->get(Configuration::KEY_APP_NAME));
|
||||
$view->offsetSet('year', date('Y'));
|
||||
|
||||
return $view;
|
||||
};
|
||||
|
||||
$this->container[Configuration::class] = function (Slim\Container $c) {
|
||||
$benzineYamlFile = '/app/.benzine.yml'; // @todo this shouldn't be hardcoded into /app
|
||||
return Configuration::InitFromFile($benzineYamlFile);
|
||||
};
|
||||
|
||||
$this->container[Db::class] = function (Slim\Container $c) {
|
||||
return new Db($c->get(DatabaseConfig::class));
|
||||
};
|
||||
|
||||
$this->container[DatabaseConfig::class] = function (Slim\Container $c) {
|
||||
/** @var Configuration $configuration */
|
||||
$configuration = $c->get(Configuration::class);
|
||||
$dbConfig = new DatabaseConfig();
|
||||
foreach ($configuration->getArray('benzine/databases') as $dbName => $database) {
|
||||
$dbConfig->set($dbName, [
|
||||
'driver' => DatabaseConfig::DbTypeToDriver($database['type']),
|
||||
'hostname' => gethostbyname($database['host']),
|
||||
'port' => $database['port'] ?? DatabaseConfig::DbTypeToDefaultPort($database['type']),
|
||||
'username' => $database['username'],
|
||||
'password' => $database['password'],
|
||||
'database' => $database['database'],
|
||||
'charset' => $database['charset'] ?? 'UTF8',
|
||||
]);
|
||||
foreach ($this->viewPaths as $path) {
|
||||
$loader->addPath($path);
|
||||
}
|
||||
|
||||
return $dbConfig;
|
||||
};
|
||||
$twig = new Slim\Views\Twig($loader, $settings);
|
||||
|
||||
$this->container[\Faker\Generator::class] = function (Slim\Container $c) {
|
||||
$twig->addExtension(new Extensions\ArrayUniqueTwigExtension());
|
||||
$twig->addExtension(new Extensions\FilterAlphanumericOnlyTwigExtension());
|
||||
|
||||
// Add coding string transform filters (ie: camel_case to StudlyCaps)
|
||||
$twig->addExtension(new Extensions\TransformExtension());
|
||||
|
||||
// Add pluralisation/depluralisation support with singularize/pluralize filters
|
||||
$twig->addExtension(new Extensions\InflectionExtension());
|
||||
|
||||
// Added Twig_Extension_Debug to enable twig dump() etc.
|
||||
$twig->addExtension(new Twig\Extension\DebugExtension());
|
||||
|
||||
$twig->offsetSet('app_name', APP_NAME);
|
||||
$twig->offsetSet('year', date('Y'));
|
||||
|
||||
return $twig;
|
||||
});
|
||||
$container->set('view', function(ContainerInterface $container){
|
||||
return $container->get(Slim\Views\Twig::class);
|
||||
});
|
||||
|
||||
$container->set(EnvironmentService::class, function(ContainerInterface $container){
|
||||
return new EnvironmentService();
|
||||
});
|
||||
|
||||
$container->set(ConfigurationService::class, function(ContainerInterface $container){
|
||||
return new ConfigurationService($container->get(EnvironmentService::class));
|
||||
});
|
||||
$container->set(\Faker\Generator::class, function(ContainerInterface $c) {
|
||||
$faker = FakerFactory::create();
|
||||
$faker->addProvider(new Provider\Base($faker));
|
||||
$faker->addProvider(new Provider\DateTime($faker));
|
||||
|
|
@ -234,48 +152,8 @@ class App
|
|||
$faker->addProvider(new Provider\en_US\Company($faker));
|
||||
|
||||
return $faker;
|
||||
};
|
||||
|
||||
$this->container[\GuzzleHttp\Client::class] = function (Slim\Container $c) {
|
||||
return new \GuzzleHttp\Client([
|
||||
// You can set any number of default request options.
|
||||
'timeout' => 2.0,
|
||||
]);
|
||||
};
|
||||
|
||||
$this->container[Services\EnvironmentService::class] = function (Slim\Container $c) {
|
||||
return new Services\EnvironmentService();
|
||||
};
|
||||
|
||||
$this->container[Predis::class] = function (Slim\Container $c) {
|
||||
/** @var EnvironmentService $environmentService */
|
||||
$environmentService = $c->get(EnvironmentService::class);
|
||||
if ($environmentService->isSet('REDIS_HOST')) {
|
||||
$redisMasterHosts = explode(',', $environmentService->get('REDIS_HOST'));
|
||||
}
|
||||
if ($environmentService->isSet('REDIS_HOST_MASTER')) {
|
||||
$redisMasterHosts = explode(',', $environmentService->get('REDIS_HOST_MASTER'));
|
||||
}
|
||||
if ($environmentService->isSet('REDIS_HOST_SLAVE')) {
|
||||
$redisSlaveHosts = explode(',', $environmentService->get('REDIS_HOST_SLAVE'));
|
||||
}
|
||||
|
||||
$options = [];
|
||||
|
||||
$options['profile'] = function ($options) {
|
||||
$profile = $options->getDefault('profile');
|
||||
$profile->defineCommand('setifhigher', SetIfHigherLuaScript::class);
|
||||
|
||||
return $profile;
|
||||
};
|
||||
|
||||
return new Predis(
|
||||
$redisMasterHosts[0],
|
||||
$options
|
||||
);
|
||||
};
|
||||
|
||||
$this->container[CachePoolChain::class] = function (Slim\Container $c) {
|
||||
});
|
||||
$container->set(CachePoolChain::class, function(ContainerInterface $c) {
|
||||
$caches = [];
|
||||
|
||||
// If apc/apcu present, add it to the pool
|
||||
|
|
@ -286,13 +164,14 @@ class App
|
|||
}
|
||||
|
||||
// If Redis is configured, add it to the pool.
|
||||
$caches[] = new PredisCachePool($c->get(Redis\Redis::class));
|
||||
$caches[] = new RedisCachePool($c->get(\Redis::class));
|
||||
$caches[] = new ArrayCachePool();
|
||||
|
||||
return new CachePoolChain($caches);
|
||||
};
|
||||
});
|
||||
|
||||
$container->set('MonologFormatter', function(ContainerInterface $c) {
|
||||
|
||||
$this->container['MonologFormatter'] = function (Slim\Container $c) {
|
||||
/** @var Services\EnvironmentService $environment */
|
||||
$environment = $c->get(Services\EnvironmentService::class);
|
||||
|
||||
|
|
@ -301,55 +180,63 @@ class App
|
|||
// the default output format is "[%datetime%] %channel%.%level_name%: %message% %context% %extra%"
|
||||
$environment->get('MONOLOG_FORMAT', '[%datetime%] %channel%.%level_name%: %message% %context% %extra%')."\n",
|
||||
'Y n j, g:i a'
|
||||
)
|
||||
;
|
||||
};
|
||||
);
|
||||
});
|
||||
|
||||
$this->container[\Monolog\Logger::class] = function (Slim\Container $c) {
|
||||
/** @var Configuration $configuration */
|
||||
$configuration = $c->get(Configuration::class);
|
||||
$appName = $configuration->get(Configuration::KEY_APP_NAME);
|
||||
$container->set(Logger::class, function(ContainerInterface $c) {
|
||||
/** @var ConfigurationService $configuration */
|
||||
$configuration = $c->get(ConfigurationService::class);
|
||||
|
||||
$monolog = new \Monolog\Logger($appName);
|
||||
|
||||
$monolog->pushHandler(new \Monolog\Handler\ErrorLogHandler(), \Monolog\Logger::DEBUG);
|
||||
$monolog = new Logger($configuration->get(ConfigurationService::KEY_APP_NAME));
|
||||
$monolog->pushHandler(new ErrorLogHandler(), Logger::DEBUG);
|
||||
$monolog->pushProcessor(new PsrLogMessageProcessor());
|
||||
|
||||
return $monolog;
|
||||
};
|
||||
});
|
||||
|
||||
$this->container[DebugBar::class] = function (Slim\Container $container) {
|
||||
$container->set(DebugBar::class, function(ContainerInterface $container) {
|
||||
$debugBar = new StandardDebugBar();
|
||||
/** @var Logger $logger */
|
||||
$logger = $container->get(Log\Logger::class);
|
||||
/** @var \Monolog\Logger $monolog */
|
||||
$monolog = $logger->getMonolog();
|
||||
$debugBar->addCollector(new MonologCollector($monolog));
|
||||
$logger = $container->get(Logger::class);
|
||||
$debugBar->addCollector(new MonologCollector($logger));
|
||||
|
||||
return $debugBar;
|
||||
};
|
||||
});
|
||||
|
||||
$this->container[\Middlewares\Debugbar::class] = function (Slim\Container $container) {
|
||||
$container->set(\Middlewares\Debugbar::class, function(ContainerInterface $container) {
|
||||
$debugBar = $container->get(DebugBar::class);
|
||||
|
||||
return new \Middlewares\Debugbar($debugBar);
|
||||
};
|
||||
});
|
||||
|
||||
$this->container[Session\Session::class] = function (Slim\Container $container) {
|
||||
return Session\Session::start($container->get(Redis\Redis::class));
|
||||
};
|
||||
$container->set(\Redis::class, function(ContainerInterface $container){
|
||||
$environmentService = $container->get(EnvironmentService::class);
|
||||
|
||||
$this->container[Differ::class] = function (Slim\Container $container) {
|
||||
return new Differ();
|
||||
};
|
||||
$redis = new \Redis();
|
||||
$redis->connect(
|
||||
$environmentService->get('REDIS_HOST', 'redis'),
|
||||
$environmentService->get('REDIS_PORT', 6379)
|
||||
);
|
||||
|
||||
$this->container[Profiler\Profiler::class] = function (Slim\Container $container) {
|
||||
return new Profiler\Profiler($container->get(Log\Logger::class));
|
||||
};
|
||||
return $redis;
|
||||
});
|
||||
|
||||
$container->set(SessionService::class, function(ContainerInterface $container){
|
||||
return new SessionService(
|
||||
$container->get(\Redis::class)
|
||||
);
|
||||
});
|
||||
|
||||
$container->set(Laminator::class, function(ContainerInterface $container){
|
||||
return new Laminator(
|
||||
APP_ROOT,
|
||||
$container->get(ConfigurationService::class)
|
||||
);
|
||||
});
|
||||
|
||||
/** @var Services\EnvironmentService $environmentService */
|
||||
$environmentService = $this->getContainer()->get(Services\EnvironmentService::class);
|
||||
if ($environmentService->isSet('TIMEZONE')) {
|
||||
$environmentService = $container->get(Services\EnvironmentService::class);
|
||||
if ($environmentService->has('TIMEZONE')) {
|
||||
date_default_timezone_set($environmentService->get('TIMEZONE'));
|
||||
} elseif (file_exists('/etc/timezone')) {
|
||||
date_default_timezone_set(trim(file_get_contents('/etc/timezone')));
|
||||
|
|
@ -357,23 +244,23 @@ class App
|
|||
date_default_timezone_set(self::DEFAULT_TIMEZONE);
|
||||
}
|
||||
|
||||
$debugBar = $this->getContainer()->get(DebugBar::class);
|
||||
$debugBar = $container->get(DebugBar::class);
|
||||
|
||||
return $container;
|
||||
}
|
||||
|
||||
public function setupMiddlewares(): void
|
||||
public function setupMiddlewares(ContainerInterface $container): void
|
||||
{
|
||||
// Middlewares
|
||||
//$this->app->add($this->container->get(Middleware\EnvironmentHeadersOnResponse::class));
|
||||
//#$this->app->add($this->container->get(\Middlewares\ContentType(["text/html", "application/json"])));
|
||||
$this->app->add($this->container->get(\Middlewares\Debugbar::class));
|
||||
//#$this->app->add($this->container->get(\Middlewares\Geolocation::class));
|
||||
//$this->app->add($this->container->get(\Middlewares\TrailingSlash::class));
|
||||
//$this->app->add($this->container->get(Middleware\JSONResponseLinter::class));
|
||||
//$this->app->add($this->container->get(\Middlewares\Whoops::class));
|
||||
//$this->app->add($this->container->get(\Middlewares\CssMinifier::class));
|
||||
//$this->app->add($this->container->get(\Middlewares\JsMinifier::class));
|
||||
//$this->app->add($this->container->get(\Middlewares\HtmlMinifier::class));
|
||||
//$this->app->add($this->container->get(\Middlewares\GzipEncoder::class));
|
||||
#$this->app->add($container->get(Middleware\EnvironmentHeadersOnResponse::class));
|
||||
#$this->app->add($container->get(\Middlewares\ContentLength::class));
|
||||
#$this->app->add($container->get(\Middlewares\Debugbar::class));
|
||||
#$this->app->add($container->get(\Middlewares\Geolocation::class));
|
||||
#$this->app->add($container->get(\Middlewares\TrailingSlash::class));
|
||||
#$this->app->add($container->get(Middleware\JSONResponseLinter::class));
|
||||
#$this->app->add($container->get(\Middlewares\Whoops::class));
|
||||
#$this->app->add($container->get(\Middlewares\Minifier::class));
|
||||
#$this->app->add($container->get(\Middlewares\GzipEncoder::class));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -383,32 +270,14 @@ class App
|
|||
*/
|
||||
public static function Instance(array $options = [])
|
||||
{
|
||||
if (!self::$instance) {
|
||||
if (!self::$isInitialised) {
|
||||
$calledClass = get_called_class();
|
||||
self::$instance = new $calledClass($options);
|
||||
}
|
||||
|
||||
$expectedClass = self::$instance->getConfiguration()->get(Configuration::KEY_CLASS);
|
||||
if (get_class(self::$instance) != $expectedClass) {
|
||||
self::$instance = new $expectedClass($options);
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Container\Container
|
||||
*/
|
||||
public static function Container()
|
||||
{
|
||||
return self::Instance()->getContainer();
|
||||
}
|
||||
|
||||
public function getContainer(): Container\Container
|
||||
{
|
||||
return $this->container;
|
||||
}
|
||||
|
||||
public function getApp()
|
||||
{
|
||||
return $this->app;
|
||||
|
|
@ -430,30 +299,6 @@ class App
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $directory
|
||||
*
|
||||
* @return int number of Paths added
|
||||
*/
|
||||
public function addRoutePathsRecursively($directory)
|
||||
{
|
||||
$count = 0;
|
||||
if (file_exists($directory)) {
|
||||
foreach (new \DirectoryIterator($directory) as $file) {
|
||||
if (!$file->isDot()) {
|
||||
if ($file->isFile() && 'php' == $file->getExtension()) {
|
||||
$this->addRoutePath($file->getRealPath());
|
||||
++$count;
|
||||
} elseif ($file->isDir()) {
|
||||
$count += $this->addRoutePathsRecursively($file->getRealPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
public function addViewPath($path)
|
||||
{
|
||||
if (file_exists($path)) {
|
||||
|
|
@ -471,17 +316,6 @@ class App
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function populateContainerAliases(&$container)
|
||||
{
|
||||
foreach ($this->containerAliases as $alias => $class) {
|
||||
if ($alias != $class) {
|
||||
$container[$alias] = function (Slim\Container $c) use ($class) {
|
||||
return $c->get($class);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function Log(int $level = Logger::DEBUG, $message)
|
||||
{
|
||||
return self::Instance()
|
||||
|
|
@ -542,9 +376,9 @@ class App
|
|||
$environmentService->rebuildEnvironmentVariables();
|
||||
}
|
||||
|
||||
public function runHttp(): ResponseInterface
|
||||
public function runHttp(): void
|
||||
{
|
||||
return $this->app->run();
|
||||
$this->app->run();
|
||||
}
|
||||
|
||||
protected function interrogateControllers()
|
||||
|
|
@ -555,16 +389,16 @@ class App
|
|||
$this->interrogateControllersComplete = true;
|
||||
|
||||
$controllerPaths = [
|
||||
$this->getConfiguration()->get(Configuration::KEY_APP_ROOT).'/src/Controllers',
|
||||
$this->getConfiguration()->get(Configuration::KEY_APP_ROOT).'/vendor/benzine/benzine-controllers/src',
|
||||
APP_ROOT . '/src/Controllers',
|
||||
];
|
||||
|
||||
foreach ($controllerPaths as $controllerPath) {
|
||||
//$this->logger->debug("Route Discovery - {$controllerPath}");
|
||||
if (file_exists($controllerPath)) {
|
||||
foreach (new \DirectoryIterator($controllerPath) as $controllerFile) {
|
||||
if (!$controllerFile->isDot() && $controllerFile->isFile() && $controllerFile->isReadable()) {
|
||||
//$this->logger->debug(" > {$controllerFile->getPathname()}");
|
||||
$appClass = new \ReflectionClass($this->getConfiguration()->get(Configuration::KEY_CLASS));
|
||||
$appClass = new \ReflectionClass(get_called_class());
|
||||
$expectedClasses = [
|
||||
$appClass->getNamespaceName().'\\Controllers\\'.str_replace('.php', '', $controllerFile->getFilename()),
|
||||
'⌬\\Controllers\\'.str_replace('.php', '', $controllerFile->getFilename()),
|
||||
|
|
@ -576,7 +410,7 @@ class App
|
|||
if (!$rc->isAbstract()) {
|
||||
foreach ($rc->getMethods() as $method) {
|
||||
/** @var \ReflectionMethod $method */
|
||||
if (1 == 1 || ResponseInterface::class == ($method->getReturnType() instanceof \ReflectionType ? $method->getReturnType()->getName() : null)) {
|
||||
if (true || ResponseInterface::class == ($method->getReturnType() instanceof \ReflectionType ? $method->getReturnType()->getName() : null)) {
|
||||
$docBlock = $method->getDocComment();
|
||||
foreach (explode("\n", $docBlock) as $docBlockRow) {
|
||||
if (false === stripos($docBlockRow, '@route')) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace ⌬\Exceptions;
|
||||
namespace Benzine\Exceptions;
|
||||
|
||||
class BenzineConfigurationException extends BenzineException
|
||||
class BenzineConfigurationException extends Exception
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace ⌬\Exceptions;
|
||||
namespace Benzine\Exceptions;
|
||||
|
||||
class BenzineException extends \Exception
|
||||
class Exception extends \Exception
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace ⌬\Exceptions;
|
||||
namespace Benzine\Exceptions;
|
||||
|
||||
class DbConfigException extends BenzineException
|
||||
class DbConfigException extends Exception
|
||||
{
|
||||
}
|
||||
|
|
|
|||
178
src/Middlewares/AccessRequirements.php
Normal file
178
src/Middlewares/AccessRequirements.php
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
<?php
|
||||
|
||||
namespace Benzine\Middleware;
|
||||
|
||||
use Faker\Factory;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
use Slim\Route;
|
||||
|
||||
class AccessRequirements
|
||||
{
|
||||
private $defaultRequirements = '';
|
||||
private $defaultVar = 'User';
|
||||
|
||||
private $defaultRejectStatus = 401;
|
||||
private $defaultRejectBody = 'Unauthorized';
|
||||
|
||||
private $rejectMap = [];
|
||||
|
||||
private $hasSession = false;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
if (class_exists(\⌬\Session\Session::class, false)) {
|
||||
$this->hasSession = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function __invoke(Request $request, Response $response, $next)
|
||||
{
|
||||
if ($this->hasSession) {
|
||||
/** @var \Slim\Route $route */
|
||||
$route = $request->getAttribute('route');
|
||||
if ($route) {
|
||||
$requirements = $this->getRequirementsFromRoute($route);
|
||||
$var = $this->getVarFromRoute($route);
|
||||
if (!empty($requirements)) {
|
||||
if ($var) {
|
||||
foreach ($requirements as $requirement) {
|
||||
if (!$this->testRequirement($requirement, $var)) {
|
||||
return $this->rejectRequest($request, $response, $requirement);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return $this->rejectRequest($request, $response, 'NO_VAR');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $next($request, $response);
|
||||
}
|
||||
|
||||
public static function Factory(): AccessRequirements
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
public function setDefaultRequirements($requirements = []): AccessRequirements
|
||||
{
|
||||
if (is_array($requirements)) {
|
||||
$requirements = $this->cleanRequirementsArray($requirements);
|
||||
$requirements = implode(',', $requirements);
|
||||
}
|
||||
$this->defaultRequirements = $requirements;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDefaultVar(string $var): AccessRequirements
|
||||
{
|
||||
$this->defaultVar = $var;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|string $requirements
|
||||
* @param int $status
|
||||
* @param string $body
|
||||
*
|
||||
* @return AccessRequirements
|
||||
*/
|
||||
public function mapRejection($requirements, int $status, string $body): AccessRequirements
|
||||
{
|
||||
if (!is_array($requirements)) {
|
||||
$requirements = [$requirements];
|
||||
}
|
||||
|
||||
foreach ($requirements as $requirement) {
|
||||
$this->rejectMap[strtoupper($requirement)] = [
|
||||
'status' => $status,
|
||||
'body' => $body,
|
||||
];
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function getRejection(string $requirement): array
|
||||
{
|
||||
return $this->rejectMap[strtoupper($requirement)] ?? [];
|
||||
}
|
||||
|
||||
private function cleanRequirementsArray($requirements)
|
||||
{
|
||||
$requirements = array_map('trim', $requirements);
|
||||
$requirements = array_unique($requirements);
|
||||
|
||||
return array_filter($requirements);
|
||||
}
|
||||
|
||||
private function rejectRequest(Request $request, Response $response, string $requirement)
|
||||
{
|
||||
$rejection = $this->getRejection($requirement);
|
||||
$status = $rejection['status'] ?? $this->defaultRejectStatus;
|
||||
$body = $rejection['body'] ?? $this->defaultRejectBody;
|
||||
if ($status >= 300 && $status < 400) {
|
||||
return $response->withRedirect($body);
|
||||
}
|
||||
|
||||
return $response->withStatus($status)->write($body);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Route $route
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
private function getRequirementsFromRoute(Route $route): array
|
||||
{
|
||||
$requirements = $route->getArgument('_accessRequirements') ?? $this->defaultRequirements;
|
||||
$requirements = explode(',', $requirements);
|
||||
|
||||
$plus = array_search('+', $requirements, true);
|
||||
if (false !== $plus) {
|
||||
unset($requirements[$plus]);
|
||||
$requirements = array_merge($this->defaultRequirements, $requirements);
|
||||
}
|
||||
|
||||
return $this->cleanRequirementsArray($requirements);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Route $route
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function getVarFromRoute(Route $route)
|
||||
{
|
||||
$varname = $route->getArgument('_accessVar') ?? $this->defaultVar;
|
||||
|
||||
return \⌬\Session\Session::get($varname);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $requirement
|
||||
* @param $var
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function testRequirement(string $requirement, $var): bool
|
||||
{
|
||||
$_requirement = $requirement;
|
||||
$not = false;
|
||||
if (0 === strpos($_requirement, '!')) {
|
||||
$_requirement = ltrim($_requirement, '!');
|
||||
$not = true;
|
||||
}
|
||||
$methodName = 'is'.ucfirst($_requirement);
|
||||
$result = $var->{$methodName}();
|
||||
if ($not) {
|
||||
$result = !$result;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
60
src/Middlewares/CORSHeadersOnResponse.php
Normal file
60
src/Middlewares/CORSHeadersOnResponse.php
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace Benzine\Middleware;
|
||||
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
|
||||
class CORSHeadersOnResponse
|
||||
{
|
||||
private $domainRegexs = '';
|
||||
|
||||
public function __construct(array $domainRegexs)
|
||||
{
|
||||
if (!is_array($domainRegexs)) {
|
||||
if (is_string($domainRegexs)) {
|
||||
$domainRegexs = [$domainRegexs];
|
||||
} else {
|
||||
$domainRegexs = null;
|
||||
}
|
||||
} else {
|
||||
foreach ($domainRegexs as $regex) {
|
||||
if (!is_string($regex)) {
|
||||
$domainRegexs = null;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (empty($domainRegexs)) {
|
||||
throw new \Exception('Invalid domainRegex for CORS Middleware. Expected string or array');
|
||||
}
|
||||
$this->domainRegexs = $domainRegexs;
|
||||
}
|
||||
|
||||
public function __invoke(Request $request, Response $response, $next)
|
||||
{
|
||||
/** @var Response $response */
|
||||
$response = $next($request, $response);
|
||||
if (!empty($request->getHeader('HTTP_ORIGIN')[0])) {
|
||||
$origin = $request->getHeader('HTTP_ORIGIN')[0];
|
||||
$pass = false;
|
||||
foreach ($this->domainRegexs as $regex) {
|
||||
if (preg_match($regex, $origin)) {
|
||||
$pass = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($pass) {
|
||||
return $response
|
||||
->withHeader('Access-Control-Allow-Origin', $origin)
|
||||
->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization, Ticket')
|
||||
->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS')
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
104
src/Middlewares/EnvironmentHeadersOnResponse.php
Normal file
104
src/Middlewares/EnvironmentHeadersOnResponse.php
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
|
||||
namespace Benzine\Middleware;
|
||||
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
use Benzine\Configuration;
|
||||
use Benzine\ORM\Profiler;
|
||||
use Benzine\Traits\InlineCssTrait;
|
||||
use Benzine\⌬;
|
||||
|
||||
class EnvironmentHeadersOnResponse
|
||||
{
|
||||
use InlineCssTrait;
|
||||
|
||||
protected $apiExplorerEnabled = true;
|
||||
|
||||
/** @var Configuration\Configuration */
|
||||
protected $configuration;
|
||||
/** @var Profiler\Profiler */
|
||||
protected $profiler;
|
||||
|
||||
public function __construct(
|
||||
Configuration\Configuration $configuration,
|
||||
Profiler\Profiler $profiler
|
||||
) {
|
||||
$this->configuration = $configuration;
|
||||
$this->profiler = $profiler;
|
||||
}
|
||||
|
||||
public function __invoke(Request $request, Response $response, $next)
|
||||
{
|
||||
/** @var Response $response */
|
||||
$response = $next($request, $response);
|
||||
if (isset($response->getHeader('Content-Type')[0])
|
||||
and false !== stripos($response->getHeader('Content-Type')[0], 'application/json')
|
||||
) {
|
||||
$body = $response->getBody();
|
||||
$body->rewind();
|
||||
|
||||
$json = json_decode($body->getContents(), true);
|
||||
|
||||
$gitVersion = null;
|
||||
if (file_exists($this->configuration->get(Configuration\Configuration::KEY_APP_ROOT).'/version.txt')) {
|
||||
$gitVersion = trim(file_get_contents($this->configuration->get(Configuration\Configuration::KEY_APP_ROOT).'/version.txt'));
|
||||
$gitVersion = explode(' ', $gitVersion, 2);
|
||||
$gitVersion = reset($gitVersion);
|
||||
}
|
||||
|
||||
$json['Extra'] = array_filter([
|
||||
'_Warning' => 'Do not depend on any variables inside this block - This is for debug only!',
|
||||
'Hostname' => gethostname(),
|
||||
'DebugEnabled' => defined('DEBUG') && DEBUG ? 'Yes' : 'No',
|
||||
'GitVersion' => defined('DEBUG') && DEBUG ? $gitVersion : null,
|
||||
'Time' => defined('DEBUG') && DEBUG ? [
|
||||
'TimeZone' => date_default_timezone_get(),
|
||||
'CurrentTime' => [
|
||||
'Human' => date('Y-m-d H:i:s'),
|
||||
'Epoch' => time(),
|
||||
],
|
||||
'Exec' => number_format(microtime(true) - $this->configuration->get(Configuration\Configuration::KEY_APP_START), 4).' sec',
|
||||
] : null,
|
||||
'Memory' => defined('DEBUG') && DEBUG ? [
|
||||
'Used' => number_format(memory_get_usage(false) / 1024 / 1024, 2).'MB',
|
||||
'Allocated' => number_format(memory_get_usage(true) / 1024 / 1024, 2).'MB',
|
||||
'Limit' => ini_get('memory_limit'),
|
||||
] : null,
|
||||
'SQL' => defined('DEBUG') && DEBUG ? $this->profiler->getQueriesArray() : null,
|
||||
'API' => defined('DEBUG') && DEBUG && class_exists('\Gone\SDK\Common\Profiler') ? \Gone\SDK\Common\Profiler::debugArray() : null,
|
||||
]);
|
||||
|
||||
if (isset($json['Status'])) {
|
||||
if ('okay' != strtolower($json['Status'])) {
|
||||
$response = $response->withStatus(400);
|
||||
} else {
|
||||
$response = $response->withStatus(200);
|
||||
}
|
||||
}
|
||||
|
||||
if (($request->hasHeader('Content-type') && false !== stripos($request->getHeader('Content-type')[0], 'application/json')) ||
|
||||
($request->hasHeader('Accept') && false !== stripos($request->getHeader('Accept')[0], 'application/json')) ||
|
||||
false === $this->apiExplorerEnabled
|
||||
) {
|
||||
$response = $response->withJson($json, null, JSON_PRETTY_PRINT);
|
||||
} else {
|
||||
/** @var Twig $twig */
|
||||
$twig = ⌬::Container()->get('view');
|
||||
$response->getBody()->rewind();
|
||||
$response = $twig->render($response, 'api/explorer.html.twig', [
|
||||
'page_name' => 'API Explorer',
|
||||
'json' => $json,
|
||||
'json_pretty_printed_rows' => explode("\n", json_encode($json, JSON_PRETTY_PRINT)),
|
||||
'inline_css' => $this->renderInlineCss([
|
||||
$this->configuration->get(Configuration\Configuration::KEY_APP_ROOT).'/vendor/benzine/benzine-http-assets/css/reset.css',
|
||||
$this->configuration->get(Configuration\Configuration::KEY_APP_ROOT).'/vendor/benzine/benzine-http-assets/css/api-explorer.css',
|
||||
]),
|
||||
]);
|
||||
$response = $response->withHeader('Content-type', 'text/html');
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
22
src/Middlewares/ForceSSLMiddleware.php
Normal file
22
src/Middlewares/ForceSSLMiddleware.php
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace Benzine\Middleware;
|
||||
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
|
||||
class ForceSSLMiddleware
|
||||
{
|
||||
public function __invoke(Request $request, Response $response, $next)
|
||||
{
|
||||
// @var Response $response
|
||||
if ('80' == $request->getServerParam('SERVER_PORT')
|
||||
&& 'https' != $request->getServerParam('HTTP_X_FORWARDED_PROTO')
|
||||
&& 'yes' == strtolower($request->getServerParam('FORCE_HTTPS'))
|
||||
) {
|
||||
return $response->withRedirect('https://'.$request->getServerParam('HTTP_HOST').'/'.ltrim($request->getServerParam('REQUEST_URI'), '/'));
|
||||
}
|
||||
|
||||
return $next($request, $response);
|
||||
}
|
||||
}
|
||||
66
src/Middlewares/JSONResponseLinter.php
Normal file
66
src/Middlewares/JSONResponseLinter.php
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
namespace Benzine\Middleware;
|
||||
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
|
||||
class JSONResponseLinter
|
||||
{
|
||||
public function __invoke(Request $request, Response $response, $next)
|
||||
{
|
||||
$encodingOptions = 0;
|
||||
$jsonCompact = $request->hasHeader('CompactJson');
|
||||
|
||||
if (!$jsonCompact) {
|
||||
$encodingOptions = JSON_PRETTY_PRINT;
|
||||
}
|
||||
|
||||
try {
|
||||
$response = $next($request, $response);
|
||||
$jsonMode = (isset($response->getHeader('Content-Type')[0]) and false !== stripos($response->getHeader('Content-Type')[0], 'application/json'));
|
||||
if ($jsonMode) {
|
||||
$body = $response->getBody();
|
||||
$body->rewind();
|
||||
$json = json_decode($body->getContents(), true);
|
||||
if ($jsonCompact) {
|
||||
if (isset($json['Extra'])) {
|
||||
unset($json['Extra']);
|
||||
}
|
||||
}
|
||||
if (isset($json['Status'])) {
|
||||
$json['Status'] = ucfirst(strtolower($json['Status']));
|
||||
}
|
||||
$response = $response->withJson($json, null, $encodingOptions);
|
||||
}
|
||||
|
||||
return $response;
|
||||
} catch (\Exception $exception) {
|
||||
$trace = explode("\n", $exception->getTraceAsString());
|
||||
array_walk($trace, function (&$elem) {
|
||||
$pieces = explode(' ', $elem, 2);
|
||||
$elem = $pieces[1];
|
||||
$highlightLocations = [
|
||||
'/app/src/',
|
||||
'/app/tests/',
|
||||
];
|
||||
foreach ($highlightLocations as $highlightLocation) {
|
||||
if (substr($elem, 0, strlen($highlightLocation)) == $highlightLocation) {
|
||||
$elem = "*** {$elem}";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return $response->withJson(
|
||||
[
|
||||
'Status' => 'Fail',
|
||||
'Exception' => get_class($exception),
|
||||
'Reason' => $exception->getMessage(),
|
||||
'Trace' => $trace,
|
||||
],
|
||||
500,
|
||||
$encodingOptions
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
427
src/Router/Route.php
Normal file
427
src/Router/Route.php
Normal file
|
|
@ -0,0 +1,427 @@
|
|||
<?php
|
||||
|
||||
namespace Benzine\Router;
|
||||
|
||||
use Slim\App;
|
||||
|
||||
class Route
|
||||
{
|
||||
public const ACCESS_PUBLIC = 'public';
|
||||
public const ACCESS_PRIVATE = 'private';
|
||||
|
||||
public const ARGUMENT_ACCESS = '_access';
|
||||
|
||||
protected $name;
|
||||
protected $callback;
|
||||
protected $SDKClass;
|
||||
protected $SDKFunction;
|
||||
protected $SDKTemplate = 'callback';
|
||||
protected $routerPattern;
|
||||
protected $httpEndpoint;
|
||||
protected $httpMethod = 'GET';
|
||||
protected $weight = 0;
|
||||
protected $singular;
|
||||
protected $plural;
|
||||
protected $properties;
|
||||
protected $propertyData = [];
|
||||
protected $propertyOptions;
|
||||
protected $exampleEntity;
|
||||
protected $exampleEntityFinderFunction;
|
||||
protected $callbackProperties = [];
|
||||
protected $arguments = [
|
||||
self::ARGUMENT_ACCESS => self::ACCESS_PUBLIC,
|
||||
];
|
||||
|
||||
public static function Factory(): Route
|
||||
{
|
||||
return new Route();
|
||||
}
|
||||
|
||||
public function getCallbackProperties(): array
|
||||
{
|
||||
return $this->callbackProperties;
|
||||
}
|
||||
|
||||
public function setCallbackProperties(array $callbackProperties): Route
|
||||
{
|
||||
$this->callbackProperties = [];
|
||||
foreach ($callbackProperties as $name => $property) {
|
||||
$this->populateCallbackProperty($name, $property);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param null $default
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addCallbackProperty(string $name, bool $mandatory = false, $default = null)
|
||||
{
|
||||
return $this->populateCallbackProperty($name, [
|
||||
'isMandatory' => $mandatory,
|
||||
'default' => $default,
|
||||
]);
|
||||
}
|
||||
|
||||
public function populateCallbackProperty(string $name, array $property)
|
||||
{
|
||||
$property['name'] = $name;
|
||||
$this->callbackProperties[$name] = array_merge(
|
||||
[
|
||||
'in' => null,
|
||||
'description' => null,
|
||||
'isMandatory' => null,
|
||||
'default' => null,
|
||||
'type' => null,
|
||||
'examples' => [],
|
||||
],
|
||||
$property
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSDKTemplate(): string
|
||||
{
|
||||
return $this->SDKTemplate;
|
||||
}
|
||||
|
||||
public function setSDKTemplate(string $SDKTemplate): Route
|
||||
{
|
||||
$this->SDKTemplate = $SDKTemplate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUniqueIdentifier()
|
||||
{
|
||||
return implode(
|
||||
'::',
|
||||
[
|
||||
$this->getRouterPattern(),
|
||||
$this->getHttpMethod(),
|
||||
"Weight={$this->getWeight()}",
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getHttpMethod()
|
||||
{
|
||||
return $this->httpMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $httpMethod
|
||||
*/
|
||||
public function setHttpMethod($httpMethod): Route
|
||||
{
|
||||
$this->httpMethod = $httpMethod;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getWeight(): int
|
||||
{
|
||||
return $this->weight;
|
||||
}
|
||||
|
||||
public function setWeight(int $weight): Route
|
||||
{
|
||||
$this->weight = $weight;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRouterPattern()
|
||||
{
|
||||
return $this->routerPattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $routerPattern
|
||||
*/
|
||||
public function setRouterPattern($routerPattern): Route
|
||||
{
|
||||
$this->routerPattern = $routerPattern;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setExampleEntityFindFunction(callable $finderFunction): Route
|
||||
{
|
||||
$this->exampleEntityFinderFunction = $finderFunction;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getExampleEntity()
|
||||
{
|
||||
if (!$this->exampleEntity && $this->exampleEntityFinderFunction) {
|
||||
$function = $this->exampleEntityFinderFunction;
|
||||
$this->exampleEntity = $function();
|
||||
}
|
||||
|
||||
return $this->exampleEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $exampleEntity
|
||||
*/
|
||||
public function setExampleEntity($exampleEntity): Route
|
||||
{
|
||||
$this->exampleEntity = $exampleEntity;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $name
|
||||
*/
|
||||
public function setName($name): Route
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getSDKClass()
|
||||
{
|
||||
return $this->SDKClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $SDKClass
|
||||
*/
|
||||
public function setSDKClass($SDKClass): Route
|
||||
{
|
||||
$this->SDKClass = $SDKClass;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getSDKFunction()
|
||||
{
|
||||
return $this->SDKFunction;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $function
|
||||
*/
|
||||
public function setSDKFunction($function): Route
|
||||
{
|
||||
$this->SDKFunction = $function;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getSingular()
|
||||
{
|
||||
return $this->singular;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $singular
|
||||
*/
|
||||
public function setSingular($singular): Route
|
||||
{
|
||||
$this->singular = $singular;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getPlural()
|
||||
{
|
||||
return $this->plural;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $plural
|
||||
*/
|
||||
public function setPlural($plural): Route
|
||||
{
|
||||
$this->plural = $plural;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPropertyData()
|
||||
{
|
||||
return $this->propertyData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getProperties()
|
||||
{
|
||||
return $this->properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $properties
|
||||
*/
|
||||
public function setProperties($properties): Route
|
||||
{
|
||||
$this->properties = [];
|
||||
foreach ($properties as $name => $type) {
|
||||
if (is_numeric($name)) {
|
||||
$this->properties[] = $type;
|
||||
} else {
|
||||
$this->properties[] = $name;
|
||||
$this->propertyData[$name]['type'] = $type;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getPropertyOptions()
|
||||
{
|
||||
return $this->propertyOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $propertyOptions
|
||||
*
|
||||
* @return Route
|
||||
*/
|
||||
public function setPropertyOptions($propertyOptions)
|
||||
{
|
||||
$this->propertyOptions = [];
|
||||
foreach ($propertyOptions as $name => $options) {
|
||||
$this->propertyOptions[$name] = $options;
|
||||
$this->propertyData[$name]['options'] = $options;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function populateRoute(App $app): App
|
||||
{
|
||||
//echo "Populating: {$this->getHttpMethod()} {$this->getRouterPattern()}\n";
|
||||
$mapping = $app->map(
|
||||
[$this->getHttpMethod()],
|
||||
$this->getRouterPattern(),
|
||||
$this->getCallback()
|
||||
);
|
||||
|
||||
$mapping->setName($this->getName() ? $this->getName() : 'Unnamed Route');
|
||||
|
||||
foreach ($this->arguments as $key => $value) {
|
||||
$mapping->setArgument($key, $value);
|
||||
}
|
||||
|
||||
return $app;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCallback()
|
||||
{
|
||||
return $this->callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $callback
|
||||
*/
|
||||
public function setCallback($callback): Route
|
||||
{
|
||||
$this->callback = $callback;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getHttpEndpoint()
|
||||
{
|
||||
return $this->httpEndpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $httpEndpoint
|
||||
*/
|
||||
public function setHttpEndpoint($httpEndpoint): Route
|
||||
{
|
||||
$this->httpEndpoint = $httpEndpoint;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAccess()
|
||||
{
|
||||
return $this->getArgument(self::ARGUMENT_ACCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $access
|
||||
*/
|
||||
public function setAccess($access = self::ACCESS_PUBLIC): Route
|
||||
{
|
||||
return $this->setArgument(self::ARGUMENT_ACCESS, $access);
|
||||
}
|
||||
|
||||
public function getArgument(string $argument)
|
||||
{
|
||||
$argument = $this->prefixArgumentKey($argument);
|
||||
|
||||
return $this->arguments[$argument] ?? null;
|
||||
}
|
||||
|
||||
public function setArgument(string $argument, $value): Route
|
||||
{
|
||||
$argument = $this->prefixArgumentKey($argument);
|
||||
$this->arguments[$argument] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function prefixArgumentKey(string $key)
|
||||
{
|
||||
if (0 !== strpos($key, '_')) {
|
||||
$key = "_{$key}";
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
78
src/Router/Router.php
Normal file
78
src/Router/Router.php
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
namespace Benzine\Router;
|
||||
|
||||
use Slim\App;
|
||||
use Monolog\Logger;
|
||||
|
||||
class Router
|
||||
{
|
||||
protected static $instance;
|
||||
|
||||
/** @var Route[] */
|
||||
protected $routes = [];
|
||||
|
||||
/** @var Logger */
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* @return Router
|
||||
*/
|
||||
public static function Instance()
|
||||
{
|
||||
if (!self::$instance instanceof Router) {
|
||||
self::$instance = new Router();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function weighRoutes(): Router
|
||||
{
|
||||
$allocatedRoutes = [];
|
||||
if (is_array($this->routes) && count($this->routes) > 0) {
|
||||
uasort($this->routes, function (Route $a, Route $b) {
|
||||
return $a->getWeight() > $b->getWeight();
|
||||
});
|
||||
|
||||
foreach ($this->routes as $index => $route) {
|
||||
if (!isset($allocatedRoutes[$route->getHttpMethod().$route->getRouterPattern()])) {
|
||||
$allocatedRoutes[$route->getHttpMethod().$route->getRouterPattern()] = true;
|
||||
} else {
|
||||
unset($this->routes[$index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function populateRoutes(App $app)
|
||||
{
|
||||
$this->weighRoutes();
|
||||
if (count($this->routes) > 0) {
|
||||
foreach ($this->routes as $route) {
|
||||
$app = $route->populateRoute($app);
|
||||
}
|
||||
}
|
||||
|
||||
return $app;
|
||||
}
|
||||
|
||||
public function addRoute(Route $route)
|
||||
{
|
||||
$this->routes[$route->getUniqueIdentifier()] = $route;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Route[]
|
||||
*/
|
||||
public function getRoutes()
|
||||
{
|
||||
ksort($this->routes);
|
||||
|
||||
return $this->routes;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,75 +2,60 @@
|
|||
|
||||
namespace Benzine\Services;
|
||||
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class ConfigurationService
|
||||
{
|
||||
public function __construct(array $config = null)
|
||||
{
|
||||
$this->config = $config;
|
||||
public const KEY_APP_NAME = 'application/name';
|
||||
public const KEY_APP_ROOT = 'application/root';
|
||||
public const KEY_CLASS = 'application/class';
|
||||
public const KEY_DEBUG_ENABLE = 'application/debug';
|
||||
public const KEY_DEFAULT_ACCESS = 'application/default_access';
|
||||
public const KEY_TIMEZONE = 'application/timezone';
|
||||
public const KEY_LOCALE = 'application/locale';
|
||||
public const KEY_SESSION_ENABLED = 'application/session_enabled';
|
||||
public const KEY_LOG_FORMAT_DATE = 'logging/format_date';
|
||||
public const KEY_LOG_FORMAT_MESSAGE = 'logging/format_message';
|
||||
|
||||
if (null === $this->config) {
|
||||
$this->config = [
|
||||
'benzine' => [
|
||||
'application' => [
|
||||
'name' => 'Benzine App',
|
||||
'debug' => false,
|
||||
'default_access' => 'public',
|
||||
'timezone' => 'UTC',
|
||||
'locale' => 'en_US.UTF-8',
|
||||
'session_enabled' => true,
|
||||
'time_start' => microtime(true),
|
||||
],
|
||||
'logging' => [
|
||||
'format_date' => 'Y-m-d H:i:s',
|
||||
'format_message' => '%datetime% > %level_name% > %message% %context% %extra%',
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->findConfigs();
|
||||
protected EnvironmentService $environmentService;
|
||||
protected string $appRoot;
|
||||
protected array $config;
|
||||
|
||||
public function __construct(EnvironmentService $environmentService)
|
||||
{
|
||||
$this->environmentService = $environmentService;
|
||||
$this->findConfig();
|
||||
$this->setupDefines();
|
||||
}
|
||||
|
||||
protected function setupDefines() : void {
|
||||
define("APP_ROOT", $this->appRoot);
|
||||
define("APP_NAME", $this->get('application/name'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate .benzine.yml
|
||||
* @param string|null $path
|
||||
*/
|
||||
protected function findConfig(string $path = null) : bool {
|
||||
if(!$path){
|
||||
$path = getcwd();
|
||||
//$path = dirname($this->environmentService->get('SCRIPT_FILENAME'));
|
||||
}
|
||||
if(!file_exists($path . "/.benzine.yml")){
|
||||
$currentDirElem = explode(DIRECTORY_SEPARATOR, $path);
|
||||
array_pop($currentDirElem);
|
||||
$parentPath = implode(DIRECTORY_SEPARATOR, $currentDirElem);
|
||||
return $this->findConfig($parentPath);
|
||||
}
|
||||
|
||||
$this->handleEnvvars();
|
||||
$this->config = Yaml::parseFile($path . "/.benzine.yml");
|
||||
$this->appRoot = $path;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function __toArray(): array
|
||||
{
|
||||
return $this->arrayFlatten($this->config);
|
||||
}
|
||||
|
||||
public static function Init(array $config): Configuration
|
||||
{
|
||||
return new Configuration($config);
|
||||
}
|
||||
|
||||
public static function InitFromFile(string $filePath): Configuration
|
||||
{
|
||||
return self::Init(Yaml::parseFile($filePath));
|
||||
}
|
||||
|
||||
public function dump(): array
|
||||
{
|
||||
return $this->__toArray();
|
||||
}
|
||||
|
||||
public function set(string $key, $value): self
|
||||
{
|
||||
$scope = &$this->config;
|
||||
foreach (explode('/', strtolower($key)) as $keyBit) {
|
||||
$scope = &$scope[$keyBit];
|
||||
}
|
||||
$scope = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function has(string $key): bool
|
||||
{
|
||||
return false != $this->get($key, false)
|
||||
|| (is_array($this->getArray($key)) && count($this->getArray($key)) >= 1);
|
||||
}
|
||||
|
||||
public function get(string $key, $defaultValue = null)
|
||||
{
|
||||
public function get(string $key, string $defaultValue = null){
|
||||
$scope = $this->config;
|
||||
foreach (explode('/', strtolower($key)) as $keyBit) {
|
||||
$scope = &$scope[$keyBit];
|
||||
|
|
@ -86,121 +71,4 @@ class ConfigurationService
|
|||
|
||||
return trim($scope);
|
||||
}
|
||||
|
||||
public function getArray(string $key)
|
||||
{
|
||||
$scope = $this->config;
|
||||
foreach (explode('/', strtolower($key)) as $keyBit) {
|
||||
$scope = &$scope[$keyBit];
|
||||
}
|
||||
|
||||
return $scope;
|
||||
}
|
||||
|
||||
public function defineAs(string $defineTarget, string $key): self
|
||||
{
|
||||
if (!defined($defineTarget)) {
|
||||
define($defineTarget, $this->get($key));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function handleEnvvars(): void
|
||||
{
|
||||
$envvars = array_merge($_SERVER, $_ENV);
|
||||
|
||||
array_walk_recursive($this->config, function (&$value, $key) use ($envvars) {
|
||||
foreach ($envvars as $envvar => $envvarValue) {
|
||||
if (is_array($envvarValue)) {
|
||||
continue;
|
||||
}
|
||||
$value = str_replace("\${$envvar}", $envvarValue, $value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function findConfigs($currentDir = null): void
|
||||
{
|
||||
if (null == $currentDir) {
|
||||
$currentDir = dirname(realpath($_SERVER['SCRIPT_FILENAME']));
|
||||
}
|
||||
|
||||
if (file_exists($currentDir.'/.benzine.yml')) {
|
||||
$this->configureFromYaml($currentDir.'/.benzine.yml');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$currentDirElem = explode(DIRECTORY_SEPARATOR, $currentDir);
|
||||
array_pop($currentDirElem);
|
||||
$this->findConfigs(implode(DIRECTORY_SEPARATOR, $currentDirElem));
|
||||
}
|
||||
|
||||
public function configureFromYaml(string $file): self
|
||||
{
|
||||
$this->set('benzine/application/root', realpath(dirname($file)));
|
||||
//\Kint::dump($this->config, Yaml::parseFile($file));
|
||||
$this->config = array_merge_recursive($this->config, Yaml::parseFile($file));
|
||||
//$this->ksortRecursive($this->config);
|
||||
$this->process();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function process(): void
|
||||
{
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', true == $this->get('benzine/application/debug'));
|
||||
ini_set('display_startup_errors', true == $this->get('benzine/application/debug'));
|
||||
date_default_timezone_set($this->get('benzine/application/timezone', 'UTC'));
|
||||
setlocale(LC_ALL, $this->get('benzine/application/locale'));
|
||||
|
||||
$this
|
||||
->defineAs('DEBUG', self::KEY_DEBUG_ENABLE)
|
||||
->defineAs('APP_START', self::KEY_APP_START)
|
||||
->defineAs('APP_ROOT', self::KEY_APP_ROOT)
|
||||
->defineAs('DEFAULT_ROUTE_ACCESS_MODE', self::KEY_DEFAULT_ACCESS)
|
||||
;
|
||||
}
|
||||
|
||||
public function getDatabases(): DatabaseConfig
|
||||
{
|
||||
$dbConfig = new DatabaseConfig();
|
||||
foreach ($this->config['benzine']['databases'] as $name => $config) {
|
||||
$dbConfig->set($name, [
|
||||
'driver' => DatabaseConfig::DbTypeToDriver($config['type']),
|
||||
'hostname' => $config['host'],
|
||||
'port' => $config['port'] ?? DatabaseConfig::DbTypeToDefaultPort($config['type']),
|
||||
'username' => $config['username'] ?? null,
|
||||
'password' => $config['password'] ?? null,
|
||||
'database' => $config['database'],
|
||||
]);
|
||||
}
|
||||
|
||||
return $dbConfig;
|
||||
}
|
||||
|
||||
public function getNamespace(): string
|
||||
{
|
||||
return $this->config['benzine']['application']['namespace']
|
||||
?? $this->config['benzine']['application']['name'];
|
||||
}
|
||||
|
||||
public function getAppName(): string
|
||||
{
|
||||
return $this->config['benzine']['application']['name'];
|
||||
}
|
||||
|
||||
public function getAppContainer(): string
|
||||
{
|
||||
return $this->config['benzine']['application']['app_container_class']
|
||||
?? ⌬\⌬::class;
|
||||
}
|
||||
|
||||
public function getLaminatorTemplates(): array
|
||||
{
|
||||
return $this->config['benzine']['laminator']['templates']
|
||||
?? ['Models', 'Services', 'Controllers', 'Endpoints', 'Routes'];
|
||||
}
|
||||
}
|
||||
36
src/Services/EnvironmentService.php
Normal file
36
src/Services/EnvironmentService.php
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace Benzine\Services;
|
||||
|
||||
class EnvironmentService
|
||||
{
|
||||
private array $environmentVariables;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->environmentVariables = array_merge($_SERVER, $_ENV);
|
||||
ksort($this->environmentVariables);
|
||||
}
|
||||
|
||||
public function has(string $key) : bool {
|
||||
return $this->get($key) !== null;
|
||||
}
|
||||
|
||||
public function get(string $key, string $default = null){
|
||||
if(isset($this->environmentVariables[$key])){
|
||||
return $this->environmentVariables[$key];
|
||||
}
|
||||
return $default;
|
||||
}
|
||||
|
||||
public function set(string $key, string $value) : self {
|
||||
$this->environmentVariables[$key] = $value;
|
||||
ksort($this->environmentVariables);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function delete(string $key) : self {
|
||||
unset($this->environmentVariables[$key]);
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
10
src/Services/SessionService.php
Normal file
10
src/Services/SessionService.php
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace Benzine\Services;
|
||||
|
||||
class SessionService
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
}
|
||||
34
src/Twig/Extensions/ArrayUniqueTwigExtension.php
Normal file
34
src/Twig/Extensions/ArrayUniqueTwigExtension.php
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace Benzine\Twig\Extensions;
|
||||
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFilter;
|
||||
|
||||
class ArrayUniqueTwigExtension extends AbstractExtension
|
||||
{
|
||||
public function getName()
|
||||
{
|
||||
return 'ArrayUnique Twig Extension';
|
||||
}
|
||||
|
||||
public function getFilters()
|
||||
{
|
||||
$filters = [];
|
||||
$methods = ['unique'];
|
||||
foreach ($methods as $method) {
|
||||
$filters[$method] = new TwigFilter($method, [$this, $method]);
|
||||
}
|
||||
|
||||
return $filters;
|
||||
}
|
||||
|
||||
public function unique($array)
|
||||
{
|
||||
if (is_array($array)) {
|
||||
return array_unique($array, SORT_REGULAR);
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
29
src/Twig/Extensions/ArrayValuesTwigExtension.php
Normal file
29
src/Twig/Extensions/ArrayValuesTwigExtension.php
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace Benzine\Twig\Extensions;
|
||||
|
||||
use Twig\Extension\AbstractExtension;
|
||||
|
||||
class ArrayValuesTwigExtension extends AbstractExtension
|
||||
{
|
||||
public function getName()
|
||||
{
|
||||
return 'Array_Values Twig Extension';
|
||||
}
|
||||
|
||||
public function getFilters()
|
||||
{
|
||||
$filters = [];
|
||||
$methods = ['values'];
|
||||
foreach ($methods as $method) {
|
||||
$filters[$method] = new \Twig_Filter($method, [$this, $method]);
|
||||
}
|
||||
|
||||
return $filters;
|
||||
}
|
||||
|
||||
public function values($array)
|
||||
{
|
||||
return array_values($array);
|
||||
}
|
||||
}
|
||||
29
src/Twig/Extensions/FilterAlphanumericOnlyTwigExtension.php
Normal file
29
src/Twig/Extensions/FilterAlphanumericOnlyTwigExtension.php
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace Benzine\Twig\Extensions;
|
||||
|
||||
use Twig\Extension\AbstractExtension;
|
||||
|
||||
class FilterAlphanumericOnlyTwigExtension extends AbstractExtension
|
||||
{
|
||||
public function getName()
|
||||
{
|
||||
return 'Filter Alphanumeric Only Twig Extension';
|
||||
}
|
||||
|
||||
public function getFilters()
|
||||
{
|
||||
$filters = [];
|
||||
$methods = ['filteralphaonly'];
|
||||
foreach ($methods as $method) {
|
||||
$filters[$method] = new \Twig_Filter($method, [$this, $method]);
|
||||
}
|
||||
|
||||
return $filters;
|
||||
}
|
||||
|
||||
public function filteralphaonly($string)
|
||||
{
|
||||
return preg_replace('/[^a-z0-9_]+/i', '', $string);
|
||||
}
|
||||
}
|
||||
27
src/Twig/Extensions/InflectionExtension.php
Normal file
27
src/Twig/Extensions/InflectionExtension.php
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Benzine\Twig\Extensions;
|
||||
|
||||
use Gone\Inflection\Inflect;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
|
||||
class InflectionExtension extends AbstractExtension
|
||||
{
|
||||
public function getFilters()
|
||||
{
|
||||
$filters = [];
|
||||
$filters['pluralize'] = new \Twig_SimpleFilter('pluralize', function ($word) {
|
||||
return Inflect::pluralize($word);
|
||||
});
|
||||
$filters['singularize'] = new \Twig_SimpleFilter('singularize', function ($word) {
|
||||
return Inflect::singularize($word);
|
||||
});
|
||||
|
||||
return $filters;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'inflection_extension';
|
||||
}
|
||||
}
|
||||
74
src/Twig/Extensions/TransformExtension.php
Normal file
74
src/Twig/Extensions/TransformExtension.php
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
namespace Benzine\Twig\Extensions;
|
||||
|
||||
use Benzine\Twig\Extensions\TransformExtensionException;
|
||||
use Camel\CaseTransformer;
|
||||
use Camel\Format;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
|
||||
class TransformExtension extends AbstractExtension
|
||||
{
|
||||
private $transformers = [
|
||||
'Camel',
|
||||
'ScreamingSnake',
|
||||
'Snake',
|
||||
'Spinal',
|
||||
'Studly',
|
||||
];
|
||||
|
||||
public function getFilters()
|
||||
{
|
||||
$filters = [];
|
||||
foreach ($this->transformers as $fromTransformer) {
|
||||
foreach ($this->transformers as $toTransformer) {
|
||||
$name = 'transform_'.strtolower($fromTransformer).'_to_'.strtolower($toTransformer);
|
||||
$context = $this;
|
||||
$filters[$name] =
|
||||
new \Twig_SimpleFilter($name, function ($word) use ($context, $fromTransformer, $toTransformer) {
|
||||
return $context->transform($word, $fromTransformer, $toTransformer);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return $filters;
|
||||
}
|
||||
|
||||
public function transform($string, $from, $to)
|
||||
{
|
||||
$fromTransformer = $this->getTransformer($from);
|
||||
$toTransformer = $this->getTransformer($to);
|
||||
|
||||
$transformer = new CaseTransformer($fromTransformer, $toTransformer);
|
||||
|
||||
return $transformer->transform($string);
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'transform_extension';
|
||||
}
|
||||
|
||||
protected function getTransformer($name)
|
||||
{
|
||||
switch (strtolower($name)) {
|
||||
case 'camel':
|
||||
case 'camelcase':
|
||||
return new Format\CamelCase();
|
||||
case 'screaming':
|
||||
case 'screamingsnake':
|
||||
case 'screamingsnakecase':
|
||||
return new Format\ScreamingSnakeCase();
|
||||
case 'snake':
|
||||
case 'snakecase':
|
||||
return new Format\SnakeCase();
|
||||
case 'spinal':
|
||||
case 'spinalcase':
|
||||
return new Format\SpinalCase();
|
||||
case 'studly':
|
||||
return new Format\StudlyCaps();
|
||||
default:
|
||||
throw new TransformExtensionException("Unknown transformer: \"{$name}\".");
|
||||
}
|
||||
}
|
||||
}
|
||||
7
src/Twig/Extensions/TransformExtensionException.php
Normal file
7
src/Twig/Extensions/TransformExtensionException.php
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace Benzine\Twig\Extensions;
|
||||
|
||||
final class TransformExtensionException extends \Exception
|
||||
{
|
||||
}
|
||||
Loading…
Reference in a new issue