first commit

This commit is contained in:
Sampanna Rimal
2024-08-27 17:48:06 +05:45
commit 53c0140f58
10839 changed files with 1125847 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
<?php
/*
* This file is part of the PHPFlasher package.
* (c) Younes KHOUBZA <younes.khoubza@gmail.com>
*/
namespace Flasher\Prime\Response\Presenter;
use Flasher\Prime\Response\Response;
final class ArrayPresenter implements PresenterInterface
{
/**
* {@inheritdoc}
*/
public function render(Response $response)
{
return $response->toArray();
}
}

View File

@@ -0,0 +1,129 @@
<?php
/*
* This file is part of the PHPFlasher package.
* (c) Younes KHOUBZA <younes.khoubza@gmail.com>
*/
namespace Flasher\Prime\Response\Presenter;
use Flasher\Prime\Response\Response;
final class HtmlPresenter implements PresenterInterface
{
const FLASHER_FLASH_BAG_PLACE_HOLDER = 'FLASHER_FLASH_BAG_PLACE_HOLDER';
const HEAD_END_PLACE_HOLDER = '</head>';
const BODY_END_PLACE_HOLDER = '</body>';
/**
* {@inheritdoc}
*/
public function render(Response $response)
{
$options = json_encode($response->toArray(true));
$context = $response->getContext();
if (isset($context['envelopes_only']) && true === $context['envelopes_only']) {
return $options;
}
$rootScript = $response->getRootScript();
$placeHolder = self::FLASHER_FLASH_BAG_PLACE_HOLDER;
$livewireListener = $this->getLivewireListenerScript();
return <<<JAVASCRIPT
<script type="text/javascript" class="flasher-js">
(function() {
var rootScript = '{$rootScript}';
var {$placeHolder} = {};
var options = mergeOptions({$options}, {$placeHolder});
function mergeOptions(first, second) {
return {
context: merge(first.context || {}, second.context || {}),
envelopes: merge(first.envelopes || [], second.envelopes || []),
options: merge(first.options || {}, second.options || {}),
scripts: merge(first.scripts || [], second.scripts || []),
styles: merge(first.styles || [], second.styles || []),
};
}
function merge(first, second) {
if (Array.isArray(first) && Array.isArray(second)) {
return first.concat(second).filter(function(item, index, array) {
return array.indexOf(item) === index;
});
}
return Object.assign({}, first, second);
}
function renderOptions(options) {
if(!window.hasOwnProperty('flasher')) {
console.error('Flasher is not loaded');
return;
}
requestAnimationFrame(function () {
window.flasher.render(options);
});
}
function render(options) {
if ('loading' !== document.readyState) {
renderOptions(options);
return;
}
document.addEventListener('DOMContentLoaded', function() {
renderOptions(options);
});
}
if (1 === document.querySelectorAll('script.flasher-js').length) {
document.addEventListener('flasher:render', function (event) {
render(event.detail);
});
{$livewireListener}
}
if (window.hasOwnProperty('flasher') || !rootScript || document.querySelector('script[src="' + rootScript + '"]')) {
render(options);
} else {
var tag = document.createElement('script');
tag.setAttribute('src', rootScript);
tag.setAttribute('type', 'text/javascript');
tag.onload = function () {
render(options);
};
document.head.appendChild(tag);
}
})();
</script>
JAVASCRIPT;
}
/**
* Generate the script for Livewire event handling.
*
* @return string
*/
private function getLivewireListenerScript()
{
if (!class_exists('Livewire\LivewireManager')) {
return '';
}
return <<<JAVASCRIPT
document.addEventListener('livewire:navigating', function () {
var elements = document.querySelectorAll('.fl-no-cache');
for (var i = 0; i < elements.length; i++) {
elements[i].remove();
}
});
JAVASCRIPT;
}
}

View File

@@ -0,0 +1,18 @@
<?php
/*
* This file is part of the PHPFlasher package.
* (c) Younes KHOUBZA <younes.khoubza@gmail.com>
*/
namespace Flasher\Prime\Response\Presenter;
use Flasher\Prime\Response\Response;
interface PresenterInterface
{
/**
* @return mixed
*/
public function render(Response $response);
}

View File

@@ -0,0 +1,241 @@
<?php
/*
* This file is part of the PHPFlasher package.
* (c) Younes KHOUBZA <younes.khoubza@gmail.com>
*/
namespace Flasher\Prime\Response\Resource;
use Flasher\Prime\Config\Config;
use Flasher\Prime\Config\ConfigInterface;
use Flasher\Prime\Notification\Envelope;
use Flasher\Prime\Response\Response;
use Flasher\Prime\Stamp\HandlerStamp;
use Flasher\Prime\Stamp\ViewStamp;
use Flasher\Prime\Template\TemplateEngineInterface;
final class ResourceManager implements ResourceManagerInterface
{
/**
* @var ConfigInterface
*/
private $config;
/**
* @var TemplateEngineInterface|null
*/
private $templateEngine;
/**
* @var array<string, string[]>
*/
private $scripts = array();
/**
* @var array<string, string[]>
*/
private $styles = array();
/**
* @var array<string, array<string, mixed>>
*/
private $options = array();
public function __construct(ConfigInterface $config = null, TemplateEngineInterface $templateEngine = null)
{
$this->config = $config ?: new Config();
$this->templateEngine = $templateEngine;
}
/**
* {@inheritdoc}
*/
public function buildResponse(Response $response)
{
$rootScript = $this->selectAssets($this->config->get('root_script'));
$rootScript = is_string($rootScript) ? $rootScript : null;
$response->setRootScript($rootScript);
$handlers = array();
foreach ($response->getEnvelopes() as $envelope) {
$handler = $this->resolveHandler($envelope);
if (null === $handler || \in_array($handler, $handlers, true)) {
continue;
}
$handlers[] = $handler;
$this->populateResponse($response, $handler);
}
return $response;
}
/**
* {@inheritdoc}
*/
public function addScripts($handler, array $scripts)
{
$this->scripts[$handler] = $scripts;
}
/**
* {@inheritdoc}
*/
public function addStyles($handler, array $styles)
{
$this->styles[$handler] = $styles;
}
/**
* {@inheritdoc}
*/
public function addOptions($handler, array $options)
{
$this->options[$handler] = $options;
}
/**
* @return ConfigInterface
*/
public function getConfig()
{
return $this->config;
}
/**
* @param mixed $assets
*
* @return string[]|string
*/
private function selectAssets($assets)
{
$use = $this->config->get('use_cdn', true) ? 'cdn' : 'local';
return is_array($assets) && array_key_exists($use, $assets) ? $assets[$use] : $assets;
}
/**
* @return string|null
*/
private function resolveHandler(Envelope $envelope)
{
$handlerStamp = $envelope->get('Flasher\Prime\Stamp\HandlerStamp');
if (!$handlerStamp instanceof HandlerStamp) {
return null;
}
$handler = $handlerStamp->getHandler();
if ('flasher' !== $handler && 0 !== strpos($handler, 'theme.')) {
return $handler;
}
$theme = $this->getTheme($handler);
if (null === $theme) {
return $handler;
}
if (!isset($this->scripts[$handler])) {
$scripts = $this->config->get('themes.'.$theme.'.scripts', array());
$this->addScripts('theme.'.$theme, (array) $scripts);
}
if (!isset($this->styles[$handler])) {
$styles = $this->config->get('themes.'.$theme.'.styles', array());
$this->addStyles('theme.'.$theme, (array) $styles);
}
if (!isset($this->options[$handler])) {
$options = $this->config->get('themes.'.$theme.'.options', array());
$this->addOptions('theme.'.$theme, $options);
}
if ('flasher' === $theme) {
$scripts = $this->config->get('scripts', array());
if (isset($this->scripts['theme.flasher'])) {
$scripts = array_merge($this->scripts['theme.flasher'], $scripts);
}
if (isset($this->scripts['flasher'])) {
$scripts = array_merge($this->scripts['flasher'], $scripts);
}
$this->addScripts('theme.flasher', (array) $scripts);
$styles = $this->config->get('styles', array());
if (isset($this->styles['theme.flasher'])) {
$styles = array_merge($this->styles['theme.flasher'], $styles);
}
if (isset($this->scripts['flasher'])) {
$styles = array_merge($this->styles['flasher'], $styles);
}
$this->addStyles('theme.flasher', (array) $styles);
$options = $this->config->get('options', array());
if (isset($this->options['theme.flasher'])) {
$options = array_merge($this->options['theme.flasher'], $options);
}
if (isset($this->options['flasher'])) {
$options = array_merge($this->options['flasher'], $options);
}
$this->addOptions('theme.flasher', $options);
}
/** @var string|null $view */
$view = $this->config->get('themes.'.$theme.'.view');
if (null === $view || null === $this->templateEngine) {
return 'theme.'.$theme;
}
$compiled = $this->templateEngine->render($view, array('envelope' => $envelope));
$envelope->withStamp(new ViewStamp($compiled));
return 'theme.'.$theme;
}
/**
* @param string $handler
*
* @return string|null
*/
private function getTheme($handler)
{
if ('flasher' === $handler) {
return 'flasher';
}
if (0 === strpos($handler, 'theme.')) {
return substr($handler, \strlen('theme.'));
}
return null;
}
/**
* @param string $handler
*
* @return void
*/
private function populateResponse(Response $response, $handler)
{
if (isset($this->scripts[$handler])) {
$scripts = $this->selectAssets($this->scripts[$handler]);
$scripts = is_array($scripts) ? $scripts : array();
$response->addScripts($scripts);
}
if (isset($this->styles[$handler])) {
$styles = $this->selectAssets($this->styles[$handler]);
$styles = is_array($styles) ? $styles : array();
$response->addStyles($styles);
}
if (isset($this->options[$handler])) {
$response->addOptions($handler, $this->options[$handler]);
}
}
}

View File

@@ -0,0 +1,42 @@
<?php
/*
* This file is part of the PHPFlasher package.
* (c) Younes KHOUBZA <younes.khoubza@gmail.com>
*/
namespace Flasher\Prime\Response\Resource;
use Flasher\Prime\Response\Response;
interface ResourceManagerInterface
{
/**
* @return Response
*/
public function buildResponse(Response $response);
/**
* @param string $handler
* @param string[] $scripts
*
* @return void
*/
public function addScripts($handler, array $scripts);
/**
* @param string $handler
* @param string[] $styles
*
* @return void
*/
public function addStyles($handler, array $styles);
/**
* @param string $handler
* @param array<string, mixed> $options
*
* @return void
*/
public function addOptions($handler, array $options);
}

View File

@@ -0,0 +1,167 @@
<?php
/*
* This file is part of the PHPFlasher package.
* (c) Younes KHOUBZA <younes.khoubza@gmail.com>
*/
namespace Flasher\Prime\Response;
use Flasher\Prime\Notification\Envelope;
final class Response
{
/**
* @var Envelope[]
*/
private $envelopes;
/**
* @var string|null
*/
private $rootScript;
/**
* @var string[]
*/
private $scripts = array();
/**
* @var string[]
*/
private $styles = array();
/**
* @var array<string, array<string, mixed>>
*/
private $options = array();
/**
* @var array<string, mixed>
*/
private $context;
/**
* @param Envelope[] $envelopes
* @param array<string, mixed> $context
*/
public function __construct(array $envelopes, array $context)
{
$this->envelopes = $envelopes;
$this->context = $context;
}
/**
* @param string[] $scripts
*
* @return void
*/
public function addScripts(array $scripts)
{
$this->scripts = array_merge($this->scripts, $scripts);
}
/**
* @param string[] $styles
*
* @return void
*/
public function addStyles(array $styles)
{
$this->styles = array_merge($this->styles, $styles);
}
/**
* @param string $alias
* @param array<string, mixed> $options
*
* @return void
*/
public function addOptions($alias, array $options)
{
$this->options[$alias] = $options;
}
/**
* @return Envelope[]
*/
public function getEnvelopes()
{
return $this->envelopes;
}
/**
* @return string|null
*/
public function getRootScript()
{
return $this->rootScript;
}
/**
* @param string|null $rootScript
*
* @return void
*/
public function setRootScript($rootScript)
{
$this->rootScript = $rootScript;
}
/**
* @return string[]
*/
public function getStyles()
{
return array_values(array_filter(array_unique($this->styles)));
}
/**
* @return string[]
*/
public function getScripts()
{
return array_values(array_filter(array_unique($this->scripts)));
}
/**
* @return array<string, array<string, mixed>>
*/
public function getOptions()
{
return $this->options;
}
/**
* @return array<string, mixed>
*/
public function getContext()
{
return $this->context;
}
/**
* @param mixed $filter
*
* @return array<string, mixed>
*/
public function toArray($filter = false)
{
$envelopes = array_map(function (Envelope $envelope) {
return $envelope->toArray();
}, $this->getEnvelopes());
$response = array(
'envelopes' => $envelopes,
'scripts' => $this->getScripts(),
'styles' => $this->getStyles(),
'options' => $this->getOptions(),
);
if (false === $filter) {
return $response;
}
return array_filter($response);
}
}

View File

@@ -0,0 +1,113 @@
<?php
/*
* This file is part of the PHPFlasher package.
* (c) Younes KHOUBZA <younes.khoubza@gmail.com>
*/
namespace Flasher\Prime\Response;
use Flasher\Prime\EventDispatcher\Event\PresentationEvent;
use Flasher\Prime\EventDispatcher\Event\ResponseEvent;
use Flasher\Prime\EventDispatcher\EventDispatcher;
use Flasher\Prime\EventDispatcher\EventDispatcherInterface;
use Flasher\Prime\Notification\Envelope;
use Flasher\Prime\Response\Presenter\ArrayPresenter;
use Flasher\Prime\Response\Presenter\HtmlPresenter;
use Flasher\Prime\Response\Presenter\PresenterInterface;
use Flasher\Prime\Response\Resource\ResourceManager;
use Flasher\Prime\Response\Resource\ResourceManagerInterface;
use Flasher\Prime\Storage\StorageManager;
use Flasher\Prime\Storage\StorageManagerInterface;
final class ResponseManager implements ResponseManagerInterface
{
/**
* @var array<string, callable|PresenterInterface>
*/
private $presenters;
/**
* @var ResourceManagerInterface
*/
private $resourceManager;
/**
* @var StorageManagerInterface
*/
private $storageManager;
/**
* @var EventDispatcherInterface
*/
private $eventDispatcher;
public function __construct(ResourceManagerInterface $resourceManager = null, StorageManagerInterface $storageManager = null, EventDispatcherInterface $eventDispatcher = null)
{
$this->resourceManager = $resourceManager ?: new ResourceManager();
$this->storageManager = $storageManager ?: new StorageManager();
$this->eventDispatcher = $eventDispatcher ?: new EventDispatcher();
$this->addPresenter('html', new HtmlPresenter());
$this->addPresenter('json', new ArrayPresenter());
$this->addPresenter('array', new ArrayPresenter());
}
/**
* {@inheritdoc}
*/
public function render(array $criteria = array(), $presenter = 'html', array $context = array())
{
$envelopes = $this->storageManager->filter($criteria);
$this->storageManager->remove($envelopes);
$event = new PresentationEvent($envelopes, $context);
$this->eventDispatcher->dispatch($event);
$response = $this->createResponse($event->getEnvelopes(), $context);
$response = $this->createPresenter($presenter)->render($response);
$event = new ResponseEvent($response, $presenter);
$this->eventDispatcher->dispatch($event);
return $event->getResponse();
}
/**
* {@inheritdoc}
*/
public function addPresenter($alias, $presenter)
{
$this->presenters[$alias] = $presenter;
}
/**
* @param string $alias
*
* @return PresenterInterface
*/
private function createPresenter($alias)
{
if (!isset($this->presenters[$alias])) {
throw new \InvalidArgumentException(sprintf('Presenter [%s] not supported.', $alias));
}
$presenter = $this->presenters[$alias];
return \is_callable($presenter) ? $presenter() : $presenter;
}
/**
* @param Envelope[] $envelopes
* @param mixed[] $context
*
* @return Response
*/
private function createResponse($envelopes, $context)
{
$response = new Response($envelopes, $context);
return $this->resourceManager->buildResponse($response);
}
}

View File

@@ -0,0 +1,30 @@
<?php
/*
* This file is part of the PHPFlasher package.
* (c) Younes KHOUBZA <younes.khoubza@gmail.com>
*/
namespace Flasher\Prime\Response;
use Flasher\Prime\Response\Presenter\PresenterInterface;
interface ResponseManagerInterface
{
/**
* @param mixed[] $criteria
* @param string $presenter
* @param mixed[] $context
*
* @return mixed
*/
public function render(array $criteria = array(), $presenter = 'html', array $context = array());
/**
* @param string $alias
* @param callable|PresenterInterface $presenter
*
* @return void
*/
public function addPresenter($alias, $presenter);
}