initial commit
This commit is contained in:
935
src/Application.php
Normal file
935
src/Application.php
Normal file
@@ -0,0 +1,935 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*
|
||||
* This file heavily inspired by Roots\Acorn and Illuminate\Foundation.
|
||||
* Original author: Roots/Laravel
|
||||
* Original source: https://github.com/roots/acorn/blob/main/src/Roots/Acorn/
|
||||
*/
|
||||
|
||||
namespace Webshr\Core;
|
||||
|
||||
use BadMethodCallException;
|
||||
use Webshr\Core\Support\Arriable;
|
||||
use Webshr\Core\Utility\Encryption;
|
||||
use Webshr\Core\Utility\Hash;
|
||||
use Webshr\Core\Assets\Manager as Assets_Manager;
|
||||
use Webshr\Core\Modules\Manager as Modules_Manager;
|
||||
use Webshr\Core\Support\Traits\Aliases as Aliases_Trait;
|
||||
use Webshr\Core\Support\Traits\Application as App_Trait;
|
||||
use Webshr\Core\Contracts\Application as Application_Interface;
|
||||
|
||||
use function Webshr\Core\Filesystem\join_paths;
|
||||
|
||||
/**
|
||||
* Main communication channel with the theme.
|
||||
*/
|
||||
class Application implements Application_Interface
|
||||
{
|
||||
use Aliases_Trait;
|
||||
use App_Trait;
|
||||
|
||||
/**
|
||||
* The core framework version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
|
||||
|
||||
public const VERSION = '1.1.0';
|
||||
/**
|
||||
* The base path for the application.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $base_path;
|
||||
/**
|
||||
* The custom language file path defined by the developer.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $lang_path;
|
||||
/**
|
||||
* The custom environment file path defined by the developer.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $env_path;
|
||||
/**
|
||||
* The custom manifests defined by the developer.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $manifests;
|
||||
/**
|
||||
* The custom default manifest file defined by the developer.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $default_manifest;
|
||||
/**
|
||||
* The custom language file path defined by the developer.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $config_path;
|
||||
/**
|
||||
* Paths to be used by the theme.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $paths = [];
|
||||
/**
|
||||
* Associative array of all core theme configs.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $config = [];
|
||||
/**
|
||||
* App instance.
|
||||
*
|
||||
* @var Application
|
||||
*/
|
||||
protected static $instance;
|
||||
/**
|
||||
* Assets_Manager.
|
||||
*
|
||||
* @var Assets_Manager
|
||||
*/
|
||||
protected $assets_manager;
|
||||
/**
|
||||
* The Module Manager instance.
|
||||
*
|
||||
* @var Modules_Manager
|
||||
*/
|
||||
public $module_manager;
|
||||
/**
|
||||
* Indicates if the application has "booted".
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $booted = false;
|
||||
/**
|
||||
* Indicates if the application has been bootstrapped before.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $has_been_bootstrapped = false;
|
||||
/**
|
||||
* The array of booting callbacks.
|
||||
*
|
||||
* @var callable[]
|
||||
*/
|
||||
protected $booting_callbacks = [];
|
||||
/**
|
||||
* The array of booted callbacks.
|
||||
*
|
||||
* @var callable[]
|
||||
*/
|
||||
protected $booted_callbacks = [];
|
||||
/**
|
||||
* Associative array of all core theme providers.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $managers = [];
|
||||
/**
|
||||
* Associative array of all core theme modules
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $modules = [];
|
||||
/**
|
||||
* Associative array of all core encryption cipher and keys
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $encryption = [];
|
||||
/**
|
||||
* The names of the loaded modules.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $registered_modules = [];
|
||||
/**
|
||||
* The names of the loaded modules.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $loaded_modules = [];
|
||||
/**
|
||||
* The deferred services and their modules.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $deferred_services = [];
|
||||
/**
|
||||
* The application bindings.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $bindings = [];
|
||||
/**
|
||||
* Constructor to initialize the app instance.
|
||||
*
|
||||
* @param Application $instance
|
||||
*/
|
||||
public function __construct($encryption = null, $base_path = null, $paths = null, $default_manifest = null, $manifests = null, $modules = null)
|
||||
{
|
||||
|
||||
if ($encryption) {
|
||||
$this->use_encryption((array) $encryption);
|
||||
}
|
||||
|
||||
if ($base_path) {
|
||||
$this->base_path = rtrim($base_path, '\/');
|
||||
}
|
||||
|
||||
if ($paths) {
|
||||
$this->use_paths((array) $paths);
|
||||
}
|
||||
|
||||
if ($default_manifest) {
|
||||
$this->use_default_manifest((string) $default_manifest);
|
||||
}
|
||||
|
||||
if ($manifests) {
|
||||
$this->use_manifests((array) $manifests);
|
||||
}
|
||||
|
||||
if ($modules) {
|
||||
$this->use_modules((array) $modules);
|
||||
}
|
||||
|
||||
$this->register_core_bindings();
|
||||
$this->register_core_aliases();
|
||||
$this->register_core_encryption();
|
||||
$this->register_core_managers();
|
||||
$this->register_core_helpers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a new theme instance.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @return static
|
||||
*/
|
||||
public function make($abstract, array $parameters = [])
|
||||
{
|
||||
return $this->resolve($abstract, $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Application instance.
|
||||
*
|
||||
* @param Application $instance
|
||||
*/
|
||||
public static function set_instance(Application $instance): void
|
||||
{
|
||||
static::$instance = $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the globally available instance of the theme.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function get_instance()
|
||||
{
|
||||
if (is_null(static::$instance)) {
|
||||
static::$instance = new static();
|
||||
}
|
||||
|
||||
return static::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Boot the application's modules.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
if ($this->is_booted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Once the application has booted we will also fire some "booted" callbacks
|
||||
// for any listeners that need to do work after this initial booting gets
|
||||
// finished. This is useful when ordering the boot-up processes we run.
|
||||
$this->fire_app_callbacks($this->booting_callbacks);
|
||||
// Register assets
|
||||
$this->register_assets_config();
|
||||
$this->register_modules();
|
||||
$this->boot_modules();
|
||||
$this->fire_app_callbacks($this->booted_callbacks);
|
||||
$this->booted = true;
|
||||
// Set the bootstrapped flag
|
||||
$this->has_been_bootstrapped = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the encryption configuration.
|
||||
*
|
||||
* @param array $encryption
|
||||
* @return $this
|
||||
*/
|
||||
public function use_encryption(array $encryption)
|
||||
{
|
||||
if (isset($encryption['cipher'])) {
|
||||
$this->instance('cipher', $encryption['cipher']);
|
||||
}
|
||||
|
||||
if (isset($encryption['key'])) {
|
||||
$this->instance('key', $encryption['key']);
|
||||
}
|
||||
|
||||
if (isset($encryption['previous_keys'])) {
|
||||
$this->instance('previous_keys', $encryption['previous_keys']);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set paths that are configurable by the developer.
|
||||
*
|
||||
* Supported path types:
|
||||
* - app
|
||||
* - bootstrap
|
||||
* - config
|
||||
* - lang
|
||||
* - resources
|
||||
* - storage
|
||||
* - env
|
||||
*
|
||||
* @param array $path
|
||||
* @return $this
|
||||
*/
|
||||
public function use_paths(array $paths)
|
||||
{
|
||||
$supported_paths = [
|
||||
'base',
|
||||
'app',
|
||||
'config',
|
||||
'resources',
|
||||
'storage',
|
||||
'bootstrap',
|
||||
'env',
|
||||
];
|
||||
foreach ($paths as $path_type => $path) {
|
||||
$path = rtrim($path, '\\/');
|
||||
if (! in_array($path_type, $supported_paths)) {
|
||||
throw new \Exception("The {$path_type} path type is not supported.");
|
||||
}
|
||||
|
||||
$this->paths[$path_type] = $path;
|
||||
}
|
||||
|
||||
$this->bind_paths();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind all of the application paths.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function bind_paths()
|
||||
{
|
||||
foreach ($this->paths as $key => $path) {
|
||||
$this->instance("path.{$key}", $path);
|
||||
}
|
||||
|
||||
$this->use_lang_path(is_dir($directory = $this->paths['resources'] . '/lang')
|
||||
? $directory
|
||||
: $this->paths['base'] . '/lang');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default manifest
|
||||
*
|
||||
* @param string $manifest
|
||||
* @return $this
|
||||
*/
|
||||
public function use_default_manifest(string $manifest)
|
||||
{
|
||||
if (! is_string($manifest)) {
|
||||
throw new \Exception("The default manifest '{$manifest}' does not exist in the manifests array.");
|
||||
}
|
||||
$this->default_manifest = $manifest;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set manifests types that are configurable by the developer.
|
||||
*
|
||||
* Supported manifest types:
|
||||
* - path
|
||||
* - url
|
||||
* - assets
|
||||
* - bundles
|
||||
*
|
||||
* @param array $manifests
|
||||
* @return $this
|
||||
*/
|
||||
public function use_manifests(array $manifests)
|
||||
{
|
||||
$supported_manifest_keys = [
|
||||
'path',
|
||||
'url',
|
||||
'assets',
|
||||
'bundles',
|
||||
];
|
||||
foreach ($manifests as $key => $manifest) {
|
||||
foreach ($manifest as $manifest => $value) {
|
||||
if (! in_array($manifest, $supported_manifest_keys)) {
|
||||
throw new \Exception("The {$manifest} path type is not supported.");
|
||||
}
|
||||
|
||||
$this->manifests[$key][$manifest] = rtrim($value, '\\/');
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register assets
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function register_assets_config()
|
||||
{
|
||||
if (! isset($this->assets_manager)) {
|
||||
throw new \Exception("Assets manager is not initialized.");
|
||||
}
|
||||
|
||||
$this->register_default_manifest();
|
||||
$this->register_manifests();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the default manifest.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function register_default_manifest()
|
||||
{
|
||||
if (! isset($this->default_manifest)) {
|
||||
throw new \Exception("Default manifest key '{$this->default_manifest}' is not set.");
|
||||
}
|
||||
|
||||
$key = $this->default_manifest;
|
||||
$manifest = $this->manifests[$key];
|
||||
// Check if the default manifest is already bound
|
||||
if (! isset($this->bindings['assets.manifest'])) {
|
||||
// Set the default manifest instance
|
||||
$manifest_instace = $this->assets_manager->manifest($key, $manifest);
|
||||
// Bind the Manifest instance
|
||||
$this->instance('assets.manifest', $manifest_instace);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register all manifests.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function register_manifests()
|
||||
{
|
||||
foreach ($this->manifests as $key => $manifest) {
|
||||
$this->assets_manager->manifest($key, $manifest);
|
||||
}
|
||||
|
||||
$this->instance('assets', $this->assets_manager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the directory for the environment file.
|
||||
*
|
||||
* @param string $path
|
||||
* @return $this
|
||||
*/
|
||||
public function use_environment_path($path)
|
||||
{
|
||||
$this->env_path = $path;
|
||||
$this->instance('path.env', $path);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the language file directory.
|
||||
*
|
||||
* @param string $path
|
||||
* @return $this
|
||||
*/
|
||||
public function use_lang_path($path)
|
||||
{
|
||||
$this->lang_path = $path;
|
||||
$this->instance('path.lang', $path);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function assets_path($path = '')
|
||||
{
|
||||
return $this->join_paths($this->config_path, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function base_path($path = '')
|
||||
{
|
||||
return $this->join_paths($this->base_path, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function config_path($path = '')
|
||||
{
|
||||
return $this->join_paths($this->config_path, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function lang_path($path = '')
|
||||
{
|
||||
return $this->join_paths($this->config_path, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function manifest_path($path = '')
|
||||
{
|
||||
return $this->join_paths($this->config_path, $path);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Join the given paths together.
|
||||
*
|
||||
* @param string $base_path
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
public function join_paths($base_path, $path = '')
|
||||
{
|
||||
return join_paths($base_path, $path);
|
||||
}
|
||||
|
||||
protected function register_core_encryption()
|
||||
{
|
||||
$this->instance('hash', new Hash());
|
||||
$this->instance('encryption', new Encryption($this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load global helper functions.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function register_core_helpers()
|
||||
{
|
||||
require_once __DIR__ . '/globals.php';
|
||||
require_once __DIR__ . '/helpers.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Register core bindings.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function register_core_bindings()
|
||||
{
|
||||
static::set_instance($this);
|
||||
$this->instance('app', $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register core managers.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function register_core_managers()
|
||||
{
|
||||
$this->assets_manager = new Assets_Manager();
|
||||
$this->module_manager = new Modules_Manager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the modules that are configurable by the developer.
|
||||
*
|
||||
* @param array $modules
|
||||
* @return $this
|
||||
*/
|
||||
public function use_modules(array $modules)
|
||||
{
|
||||
foreach ($modules as $module_name => $module_class) {
|
||||
if (! class_exists($module_class)) {
|
||||
throw new \Exception("The module class {$module_class} does not exist.");
|
||||
}
|
||||
|
||||
$this->modules[$module_name] = $module_class;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register modules
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function register_modules()
|
||||
{
|
||||
if (! isset($this->module_manager)) {
|
||||
throw new \Exception("Modules manager is not initialized.");
|
||||
}
|
||||
|
||||
foreach ($this->modules as $key => $module) {
|
||||
$this->module_manager->register($key, new $module());
|
||||
$this->registered_modules[$key] = $module;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a module with the application.
|
||||
*
|
||||
* @param \Webshr\Core\Module|string $module
|
||||
* @param bool $force
|
||||
*/
|
||||
public function boot_modules()
|
||||
{
|
||||
foreach ($this->module_manager->modules() as $key => $module) {
|
||||
// Check if the module is already loaded
|
||||
if (isset($this->loaded_modules[$key])) {
|
||||
continue;
|
||||
// Skip already loaded modules
|
||||
}
|
||||
|
||||
// Boot the module
|
||||
$this->boot_module($module);
|
||||
$this->loaded_modules[$key] = $module;
|
||||
// Register the module if it can be registered
|
||||
if ($module->can_register()) {
|
||||
$module->register();
|
||||
}
|
||||
|
||||
// If the application has already booted, call the boot method on the module
|
||||
if ($this->is_booted()) {
|
||||
$this->boot_module($module);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Boot the given module.
|
||||
*
|
||||
* @param \Webshr\Core\Module $module
|
||||
* @return void
|
||||
*/
|
||||
protected function boot_module(Module $module)
|
||||
{
|
||||
$module->call_booting_callbacks();
|
||||
if (method_exists($module, 'boot')) {
|
||||
$this->call([$module, 'boot']);
|
||||
}
|
||||
|
||||
$module->call_booted_callbacks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the registered service provider instance if it exists.
|
||||
*
|
||||
* @param \Webshr\Core\Module|string $module
|
||||
* @return \Webshr\Core\Module|null
|
||||
*/
|
||||
public function get_module($module)
|
||||
{
|
||||
return array_values($this->get_modules($module))[0] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the registered service provider instances if any exist.
|
||||
*
|
||||
* @param \Webshr\Core\Module|string $module
|
||||
* @return array
|
||||
*/
|
||||
public function get_modules($module)
|
||||
{
|
||||
$name = is_string($module) ? $module : get_class($module);
|
||||
return Arriable::where($this->modules, fn($value) => $value instanceof $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a module instance from the class name.
|
||||
*
|
||||
* @param string $module
|
||||
* @return \Webshr\Core\Modules\Contracts\Module
|
||||
*/
|
||||
public function resolve_module($module)
|
||||
{
|
||||
return $this->module_manager->module($module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the given module as registered.
|
||||
*
|
||||
* @param \Webshr\Core\Module $module
|
||||
* @return void
|
||||
*/
|
||||
protected function mark_as_registered($module)
|
||||
{
|
||||
$this->modules[] = $module;
|
||||
$this->loaded_modules[get_class($module)] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the core class aliases in the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function register_core_aliases()
|
||||
{
|
||||
$this->alias('app', self::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new boot listener.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return void
|
||||
*/
|
||||
public function booting($callback)
|
||||
{
|
||||
$this->booting_callbacks[] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new "booted" listener.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return void
|
||||
*/
|
||||
public function booted($callback)
|
||||
{
|
||||
$this->booted_callbacks[] = $callback;
|
||||
if ($this->is_booted()) {
|
||||
$callback($this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the application has been booted.
|
||||
*
|
||||
* This method returns the status of the application's boot process.
|
||||
*
|
||||
* @return bool True if the application is booted, false otherwise.
|
||||
*/
|
||||
public function is_booted()
|
||||
{
|
||||
return $this->booted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the booting callbacks for the application.
|
||||
*
|
||||
* @param callable[] $callbacks
|
||||
* @return void
|
||||
*/
|
||||
protected function fire_app_callbacks(array &$callbacks)
|
||||
{
|
||||
$index = 0;
|
||||
while ($index < count($callbacks)) {
|
||||
$callbacks[$index]($this);
|
||||
$index++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the application has been bootstrapped before.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has_been_bootstrapped()
|
||||
{
|
||||
return $this->has_been_bootstrapped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an object or value from the application container.
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function get(string $key)
|
||||
{
|
||||
return $this->bindings[$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all bindings.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function bindings(): array
|
||||
{
|
||||
return $this->bindings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function version()
|
||||
{
|
||||
return self::VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a value or object to a key in the application container.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function instance(string $key, $value)
|
||||
{
|
||||
$this->bindings[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a shared binding in the application.
|
||||
*
|
||||
* @param string $abstract
|
||||
* @param \Closure|string|null $concrete
|
||||
* @return void
|
||||
*/
|
||||
public function singleton($abstract, $concrete = null)
|
||||
{
|
||||
// If no concrete implementation is provided, use the abstract as the concrete
|
||||
if (is_null($concrete)) {
|
||||
$concrete = $abstract;
|
||||
}
|
||||
|
||||
// Register a closure that resolves the singleton instance
|
||||
$this->bindings[$abstract] = function () use ($abstract, $concrete) {
|
||||
|
||||
// Check if the instance already exists
|
||||
if (! isset($this->bindings["instances"][$abstract])) {
|
||||
// Resolve the instance and store it
|
||||
$this->bindings["instances"][$abstract] = $this->resolve($concrete);
|
||||
}
|
||||
|
||||
// Return the stored instance
|
||||
return $this->bindings["instances"][$abstract];
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the given Closure / class@method and inject its dependencies.
|
||||
*
|
||||
* @param callable|string $callback
|
||||
* @param array $parameters
|
||||
* @param string|null $default_method
|
||||
* @return mixed
|
||||
*/
|
||||
public function call($callback, array $parameters = [], $default_method = null)
|
||||
{
|
||||
if (is_string($callback)) {
|
||||
// Handle the case where the callback is a string in the format 'Class@method'
|
||||
if (strpos($callback, '@') !== false) {
|
||||
list($class, $method) = explode('@', $callback);
|
||||
$callback = [new $class(), $method];
|
||||
} elseif ($default_method) {
|
||||
$callback = [new $callback(), $default_method];
|
||||
} else {
|
||||
$callback = new $callback();
|
||||
}
|
||||
}
|
||||
|
||||
return call_user_func_array($callback, $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a value or object from the application
|
||||
*
|
||||
* @param string $key The key to resolve.
|
||||
* @param array $args The arguments to pass to the resolved service.
|
||||
* @return mixed|null The resolved service or null if not found.
|
||||
*/
|
||||
public function resolve($abstract, $parameters = []): mixed
|
||||
{
|
||||
// Get the alias
|
||||
if (! is_string($abstract)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if the binding exists
|
||||
if (isset($this->bindings[$abstract])) {
|
||||
$binding = $this->bindings[$abstract];
|
||||
// If the module is a class name (string), instantiate it
|
||||
if (is_string($binding) && class_exists($binding)) {
|
||||
return new $binding();
|
||||
// or resolve via a DI container if needed
|
||||
}
|
||||
|
||||
// If the module is a callable or has a callback, invoke it
|
||||
if (is_callable($binding)) {
|
||||
return call_user_func($binding);
|
||||
}
|
||||
|
||||
if (is_array($binding) && isset($binding['callback']) && is_callable($binding['callback'])) {
|
||||
return call_user_func($binding['callback']);
|
||||
}
|
||||
// Otherwise, return the module directly
|
||||
return $binding;
|
||||
}
|
||||
|
||||
return null;
|
||||
// Return null if the service is not found
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic call method.
|
||||
*
|
||||
* Will proxy to the theme method $method, unless it is not available, in which case an exception will be thrown.
|
||||
*
|
||||
* @param string $method Template tag name.
|
||||
* @param array $args Template tag arguments.
|
||||
* @return mixed Template tag result, or null if theme method only outputs markup.
|
||||
*
|
||||
* @throws BadMethodCallException Thrown if the theme method does not exist.
|
||||
*/
|
||||
public function __call(string $method, array $args): mixed
|
||||
{
|
||||
$resolved = $this->resolve($method);
|
||||
if (! $resolved) {
|
||||
throw new BadMethodCallException(sprintf(__('The method %s does not exist.', 'webshr'), 'app()->' . $method . '()'),);
|
||||
}
|
||||
|
||||
// If the resolved service is an invokable object, call it
|
||||
if (is_object($resolved) && is_callable($resolved)) {
|
||||
return call_user_func_array($resolved, $args);
|
||||
}
|
||||
|
||||
// If the resolved service is an object (but not invokable), return the object itself
|
||||
if (is_object($resolved)) {
|
||||
return $resolved;
|
||||
}
|
||||
|
||||
// Otherwise, call the resolved service as a function
|
||||
return call_user_func_array($resolved, $args);
|
||||
}
|
||||
}
|
||||
226
src/Assets/Asset/Asset.php
Normal file
226
src/Assets/Asset/Asset.php
Normal file
@@ -0,0 +1,226 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Assets\Asset;
|
||||
|
||||
use Webshr\Core\Assets\Contracts\Asset as Asset_Interface;
|
||||
use Webshr\Core\Assets\Contracts\Asset_Meta as Meta_Interface;
|
||||
use Webshr\Core\Filesystem\Filesystem;
|
||||
use SplFileInfo;
|
||||
|
||||
class Asset implements Asset_Interface, Meta_Interface
|
||||
{
|
||||
/**
|
||||
* The local asset path.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
/**
|
||||
* The remote asset URI.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $uri;
|
||||
/**
|
||||
* The asset MIME content type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
/**
|
||||
* The asset base64-encoded contents.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $base64;
|
||||
/**
|
||||
* The asset data URL.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $data_url;
|
||||
/**
|
||||
* Get asset from manifest
|
||||
*
|
||||
* @param string $path Local path
|
||||
* @param string $uri Remote URI
|
||||
*/
|
||||
public function __construct(string $path, string $uri)
|
||||
{
|
||||
$this->path = $path;
|
||||
$this->uri = $uri;
|
||||
}
|
||||
|
||||
/** {@inheritdoc} */
|
||||
public function uri(): string
|
||||
{
|
||||
return $this->uri;
|
||||
}
|
||||
|
||||
/** {@inheritdoc} */
|
||||
public function path(): string
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/** {@inheritdoc} */
|
||||
public function exists(): bool
|
||||
{
|
||||
return file_exists($this->path());
|
||||
}
|
||||
|
||||
/** {@inheritdoc} */
|
||||
public function contents(): string
|
||||
{
|
||||
if (! $this->exists()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return file_get_contents($this->path());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the relative path to the asset.
|
||||
*
|
||||
* @param string $basePath Base path to use for relative path.
|
||||
*/
|
||||
public function relative_path(string $basePath): string
|
||||
{
|
||||
$basePath = rtrim($basePath, '/\\') . '/';
|
||||
return (new Filesystem())->get_relative_path($basePath, $this->path());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the base64-encoded contents of the asset.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function base64()
|
||||
{
|
||||
if ($this->base64) {
|
||||
return $this->base64;
|
||||
}
|
||||
|
||||
return $this->base64 = base64_encode($this->contents());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data URL of asset.
|
||||
*
|
||||
* @param string $mediatype MIME content type
|
||||
*/
|
||||
public function data_url(?string $mediatype = null): string
|
||||
{
|
||||
if ($this->data_url) {
|
||||
return $this->data_url;
|
||||
}
|
||||
|
||||
if (! $mediatype) {
|
||||
$mediatype = $this->content_type();
|
||||
}
|
||||
|
||||
return $this->data_url = "data:{$mediatype};base64,{$this->base64()}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data URL of asset.
|
||||
*
|
||||
* @param string $mediatype MIME content type
|
||||
*/
|
||||
public function data_uri(?string $mediatype = null): string
|
||||
{
|
||||
return $this->data_url($mediatype);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the MIME content type.
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
public function content_type()
|
||||
{
|
||||
if ($this->type) {
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
return $this->type = finfo_file(finfo_open(FILEINFO_MIME_TYPE), $this->path());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the MIME content type.
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
public function mime_type()
|
||||
{
|
||||
return $this->content_type();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get SplFileInfo instance of asset.
|
||||
*
|
||||
* @return SplFileInfo
|
||||
*/
|
||||
public function file()
|
||||
{
|
||||
return new SplFileInfo($this->path());
|
||||
}
|
||||
|
||||
/** {@inheritdoc} */
|
||||
public function raw(): mixed
|
||||
{
|
||||
if (!$this->exists()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Start output buffering
|
||||
ob_start();
|
||||
// Include the PHP file
|
||||
include $this->path();
|
||||
// Get the contents of the buffer and clean it
|
||||
$content = ob_get_clean();
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function version()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function dependencies()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function include()
|
||||
{
|
||||
if (!$this->exists()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return include $this->path();
|
||||
}
|
||||
|
||||
/** {@inheritdoc} */
|
||||
public function __toString()
|
||||
{
|
||||
return $this->uri();
|
||||
}
|
||||
}
|
||||
45
src/Assets/Asset/Json_Asset.php
Normal file
45
src/Assets/Asset/Json_Asset.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Assets\Asset;
|
||||
|
||||
use Webshr\Core\Support\Contracts\Arrayable;
|
||||
use Webshr\Core\Support\Contracts\Jsonable;
|
||||
|
||||
class Json_Asset extends Text_Asset implements Arrayable, Jsonable
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function to_json($options = \JSON_UNESCAPED_SLASHES)
|
||||
{
|
||||
return json_encode($this->to_array(), $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function to_array(): array
|
||||
{
|
||||
return (array) $this->decode(JSON_OBJECT_AS_ARRAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode JSON data.
|
||||
*
|
||||
* @param int $options
|
||||
* @param int $depth
|
||||
* @return array|null
|
||||
*/
|
||||
public function decode($options = 0, $depth = 512)
|
||||
{
|
||||
return json_decode($this->contents(), null, $depth, $options);
|
||||
}
|
||||
}
|
||||
66
src/Assets/Asset/Meta_Asset.php
Normal file
66
src/Assets/Asset/Meta_Asset.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Assets\Asset;
|
||||
|
||||
use Webshr\Core\Assets\Contracts\Asset_Meta;
|
||||
|
||||
class Meta_Asset extends Php_Asset implements Asset_Meta
|
||||
{
|
||||
/**
|
||||
* The asset content
|
||||
*/
|
||||
protected $contents;
|
||||
/**
|
||||
* The asset version
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $version;
|
||||
/**
|
||||
* The asset dependencies
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dependencies;
|
||||
/**
|
||||
* Constructor to initialize the asset.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $uri
|
||||
*/
|
||||
public function __construct(string $path, string $uri)
|
||||
{
|
||||
parent::__construct($path, $uri);
|
||||
$this->contents = $this->include_once();
|
||||
$this->version = $this->contents['version'] ?? '';
|
||||
$this->dependencies = $this->contents['dependencies'] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the asset version.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function version()
|
||||
{
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the asset dependencies.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function dependencies(): string
|
||||
{
|
||||
return json_encode($this->dependencies);
|
||||
}
|
||||
}
|
||||
72
src/Assets/Asset/Php_Asset.php
Normal file
72
src/Assets/Asset/Php_Asset.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Assets\Asset;
|
||||
|
||||
use Webshr\Core\Filesystem\Exceptions\File_Not_Found_Exception;
|
||||
|
||||
class Php_Asset extends Asset
|
||||
{
|
||||
/**
|
||||
* Get the returned value of the asset
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function require_once()
|
||||
{
|
||||
$this->assert_exists();
|
||||
return require_once $this->path();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the returned value of the asset
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function require()
|
||||
{
|
||||
$this->assert_exists();
|
||||
return require $this->path();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the returned value of the asset
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function include_once()
|
||||
{
|
||||
$this->assert_exists();
|
||||
return include_once $this->path();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the returned value of the asset
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function include()
|
||||
{
|
||||
$this->assert_exists();
|
||||
return include $this->path();
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the asset exists.
|
||||
*
|
||||
* @throws \Webshr\Core\Filesystem\Exceptions\File_Not_Found_Exception
|
||||
*/
|
||||
protected function assert_exists()
|
||||
{
|
||||
if (! $this->exists()) {
|
||||
throw new File_Not_Found_Exception("Asset [{$this->path()}] not found.");
|
||||
}
|
||||
}
|
||||
}
|
||||
19
src/Assets/Asset/Svg_Asset.php
Normal file
19
src/Assets/Asset/Svg_Asset.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Assets\Asset;
|
||||
|
||||
class Svg_Asset extends Text_Asset
|
||||
{
|
||||
public function content_type()
|
||||
{
|
||||
return 'image/svg+xml';
|
||||
}
|
||||
}
|
||||
84
src/Assets/Asset/Text_Asset.php
Normal file
84
src/Assets/Asset/Text_Asset.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Assets\Asset;
|
||||
|
||||
class Text_Asset extends Asset
|
||||
{
|
||||
/**
|
||||
* Character encoding
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $charset;
|
||||
/**
|
||||
* Get character encoding.
|
||||
*
|
||||
* @param string $fallback Fallback if charset cannot be determined
|
||||
*/
|
||||
public function charset($fallback = 'UTF-8'): string
|
||||
{
|
||||
if ($this->charset) {
|
||||
return $this->charset;
|
||||
}
|
||||
|
||||
if (preg_match('//u', $this->contents())) {
|
||||
return $this->charset = 'UTF-8';
|
||||
}
|
||||
|
||||
if (function_exists('mb_detect_encoding')) {
|
||||
return $this->charset = mb_detect_encoding($this->contents()) ?: $fallback;
|
||||
}
|
||||
|
||||
return $this->charset = $fallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data URL of asset.
|
||||
*
|
||||
* @param string $mediatype MIME content type
|
||||
* @param string $charset Character encoding
|
||||
* @param string $urlencode List of characters to be percent-encoded
|
||||
*/
|
||||
public function data_url(?string $mediatype = null, ?string $charset = null, string $urlencode = '%\'"'): string
|
||||
{
|
||||
if ($this->data_url) {
|
||||
return $this->data_url;
|
||||
}
|
||||
|
||||
if (! $mediatype) {
|
||||
$mediatype = $this->content_type();
|
||||
}
|
||||
|
||||
if (! strstr($mediatype, 'charset')) {
|
||||
$mediatype .= ';charset=' . ($charset ?: $this->charset());
|
||||
}
|
||||
|
||||
$percents = [];
|
||||
foreach (preg_split('//u', $urlencode, -1, PREG_SPLIT_NO_EMPTY) as $char) {
|
||||
$percents[$char] = rawurlencode($char);
|
||||
}
|
||||
|
||||
$data = strtr($this->contents(), $percents);
|
||||
return $this->data_url = "data:{$mediatype},{$data}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data URL of asset.
|
||||
*
|
||||
* @param string $mediatype MIME content type
|
||||
* @param string $charset Character encoding
|
||||
* @param string $urlencode List of characters to be percent-encoded
|
||||
*/
|
||||
public function data_uri(?string $mediatype = null, ?string $charset = null, string $urlencode = '%\'"'): string
|
||||
{
|
||||
return $this->data_url($mediatype, $charset, $urlencode);
|
||||
}
|
||||
}
|
||||
98
src/Assets/Asset_Factory.php
Normal file
98
src/Assets/Asset_Factory.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*
|
||||
* This file was copied from Roots\Acorn.
|
||||
* Original author: Roots
|
||||
* Original source: https://github.com/roots/acorn/blob/main/src/Roots/Acorn/
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Assets;
|
||||
|
||||
use Webshr\Core\Assets\Asset\Asset;
|
||||
use Webshr\Core\Assets\Asset\Json_Asset;
|
||||
use Webshr\Core\Assets\Asset\PHP_Asset;
|
||||
use Webshr\Core\Assets\Asset\Meta_Asset;
|
||||
use Webshr\Core\Assets\Asset\Svg_Asset;
|
||||
use Webshr\Core\Assets\Contracts\Asset as Asset_Interface;
|
||||
|
||||
class Asset_Factory
|
||||
{
|
||||
/**
|
||||
* Create Asset instance.
|
||||
*
|
||||
* @param string $path Local path
|
||||
* @param string $uri Remote URI
|
||||
* @param string $type Asset type
|
||||
*/
|
||||
public static function create(string $path, string $uri, ?string $type = null): Asset_Interface
|
||||
{
|
||||
if (! $type) {
|
||||
$type = pathinfo($path, PATHINFO_EXTENSION);
|
||||
}
|
||||
|
||||
if ($type === 'meta') {
|
||||
$path .= '.asset.php';
|
||||
}
|
||||
|
||||
$method = 'create_' . strtolower($type) . '_asset';
|
||||
if (method_exists(self::class, $method)) {
|
||||
return self::{$method}($path, $uri);
|
||||
}
|
||||
|
||||
return self::create_asset($path, $uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an asset to another asset type.
|
||||
*/
|
||||
public static function convert(Asset_Interface $asset, string $type): Asset_Interface
|
||||
{
|
||||
return self::create($asset->path(), $asset->uri(), $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Asset instance.
|
||||
*/
|
||||
protected static function create_asset(string $path, string $uri): Asset
|
||||
{
|
||||
return new Asset($path, $uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Json_Asset instance.
|
||||
*/
|
||||
protected static function create_json_asset(string $path, string $uri): Json_Asset
|
||||
{
|
||||
return new Json_Asset($path, $uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create PHP_Asset instance.
|
||||
*/
|
||||
protected static function create_php_asset(string $path, string $uri): PHP_Asset
|
||||
{
|
||||
return new PHP_Asset($path, $uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Meta_Asset instance.
|
||||
*/
|
||||
protected static function create_meta_asset(string $path, string $uri): Meta_Asset
|
||||
{
|
||||
return new Meta_Asset($path, $uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Svg_Asset instance.
|
||||
*/
|
||||
protected static function create_svg_asset(string $path, string $uri): Svg_Asset
|
||||
{
|
||||
return new Svg_Asset($path, $uri);
|
||||
}
|
||||
}
|
||||
216
src/Assets/Bundle.php
Normal file
216
src/Assets/Bundle.php
Normal file
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*
|
||||
* This file was copied from Roots\Acorn.
|
||||
* Original author: Roots
|
||||
* Original source: https://github.com/roots/acorn/blob/main/src/Roots/Acorn/
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Assets;
|
||||
|
||||
use Webshr\Core\Support\Arriable;
|
||||
use Webshr\Core\Assets\Concerns\Conditional;
|
||||
use Webshr\Core\Assets\Concerns\Enqueuable;
|
||||
use Webshr\Core\Assets\Contracts\Bundle as Bundle_Interface;
|
||||
|
||||
class Bundle implements Bundle_Interface
|
||||
{
|
||||
use Conditional;
|
||||
use Enqueuable;
|
||||
|
||||
/**
|
||||
* The bundle ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
|
||||
|
||||
protected $id;
|
||||
/**
|
||||
* The bundle path.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
/**
|
||||
* The bundle URI.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $uri;
|
||||
/**
|
||||
* The bundle runtime.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $runtime;
|
||||
/**
|
||||
* The bundle contents.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $bundle;
|
||||
/**
|
||||
* The bundle runtimes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $runtimes = [];
|
||||
/**
|
||||
* Create a new bundle.
|
||||
*/
|
||||
public function __construct(string $id, array $bundle, string $path, string $uri = '/')
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->path = $path;
|
||||
$this->uri = $uri;
|
||||
$this->bundle = $bundle + ['js' => [], 'mjs' => [], 'css' => []];
|
||||
$this->set_runtime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get CSS files in bundle.
|
||||
*
|
||||
* Optionally pass a function to execute on each CSS file.
|
||||
*
|
||||
* @return array|$this
|
||||
*/
|
||||
public function css(?callable $callable = null)
|
||||
{
|
||||
$styles = $this->conditional ? $this->bundle['css'] : [];
|
||||
if (! $callable) {
|
||||
return $styles;
|
||||
}
|
||||
|
||||
foreach ($styles as $handle => $src) {
|
||||
$callable("{$this->id}/{$handle}", $this->get_url($src));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get JS files in bundle.
|
||||
*
|
||||
* Optionally pass a function to execute on each JS file.
|
||||
*
|
||||
* @return array|$this
|
||||
*/
|
||||
public function js(?callable $callable = null)
|
||||
{
|
||||
$scripts = $this->conditional ? array_merge($this->bundle['js'], $this->bundle['mjs']) : [];
|
||||
if (! $callable) {
|
||||
return $scripts;
|
||||
}
|
||||
|
||||
foreach ($scripts as $handle => $src) {
|
||||
if ($handle === 'runtime') {
|
||||
continue;
|
||||
}
|
||||
$callable("{$this->id}/{$handle}", $this->get_url($src), $this->dependencies());
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bundle dependencies.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function dependencies()
|
||||
{
|
||||
return $this->bundle['dependencies'] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bundle runtime.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function runtime()
|
||||
{
|
||||
return $this->runtime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bundle runtime contents.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function runtime_source()
|
||||
{
|
||||
if (($runtime = $this->runtime()) === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($sauce = self::$runtimes[$runtime] ?? null) {
|
||||
return $sauce;
|
||||
}
|
||||
|
||||
return self::$runtimes[$runtime] = file_get_contents("{$this->path}/{$runtime}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bundle URL.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_url(string $path)
|
||||
{
|
||||
if (parse_url($path, PHP_URL_HOST)) {
|
||||
return $path;
|
||||
}
|
||||
|
||||
$path = ltrim($path, '/');
|
||||
$uri = rtrim($this->uri, '/');
|
||||
return "{$uri}/{$path}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the bundle runtime.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function set_runtime()
|
||||
{
|
||||
if (Arriable::is_assoc($this->bundle['js'])) {
|
||||
$this->runtime = $this->bundle['js']['runtime']
|
||||
?? $this->bundle['js']["runtime~{$this->id}"]
|
||||
?? null;
|
||||
unset($this->bundle['js']['runtime'], $this->bundle['js']["runtime~{$this->id}"]);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->runtime = $this->get_bundle_runtime() ?? $this->get_bundle_runtime('mjs');
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the runtime in a bundle.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function get_bundle_runtime(string $type = 'js')
|
||||
{
|
||||
if (! $this->bundle[$type]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($this->bundle[$type] as $key => $value) {
|
||||
if (! str_contains($value, 'runtime')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
unset($this->bundle[$type][$key]);
|
||||
return $value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
38
src/Assets/Concerns/Conditional.php
Normal file
38
src/Assets/Concerns/Conditional.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*
|
||||
* This file was copied from Roots\Acorn.
|
||||
* Original author: Roots
|
||||
* Original source: https://github.com/roots/acorn/blob/main/src/Roots/Acorn/
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Assets\Concerns;
|
||||
|
||||
trait Conditional
|
||||
{
|
||||
/**
|
||||
* Conditionally load assets.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $conditional = true;
|
||||
/**
|
||||
* Set conditional loading.
|
||||
*
|
||||
* @param bool|callable $conditional
|
||||
* @return $this
|
||||
*/
|
||||
public function when($conditional, ...$args)
|
||||
{
|
||||
$this->conditional = is_callable($conditional)
|
||||
? call_user_func($conditional, $args)
|
||||
: $conditional;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
252
src/Assets/Concerns/Enqueuable.php
Normal file
252
src/Assets/Concerns/Enqueuable.php
Normal file
@@ -0,0 +1,252 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*
|
||||
* This file was copied from Roots\Acorn.
|
||||
* Original author: Roots
|
||||
* Original source: https://github.com/roots/acorn/blob/main/src/Roots/Acorn/
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Assets\Concerns;
|
||||
|
||||
use Webshr\Core\Support\Str;
|
||||
use Webshr\Core\Filesystem\Filesystem;
|
||||
|
||||
trait Enqueuable
|
||||
{
|
||||
/**
|
||||
* Resolved inline sources.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $inlined = [];
|
||||
/**
|
||||
* Get JS files in bundle.
|
||||
*
|
||||
* Optionally pass a function to execute on each JS file.
|
||||
*
|
||||
* @return array|$this
|
||||
*/
|
||||
abstract public function js(?callable $callable = null);
|
||||
/**
|
||||
* Get CSS files in bundle.
|
||||
*
|
||||
* Optionally pass a function to execute on each CSS file.
|
||||
*
|
||||
* @return array|$this
|
||||
*/
|
||||
abstract public function css(?callable $callable = null);
|
||||
abstract public function runtime();
|
||||
abstract public function runtime_source();
|
||||
/**
|
||||
* Enqueue CSS files in WordPress.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function enqueue_css(string $media = 'all', array $dependencies = [])
|
||||
{
|
||||
$this->css(function ($handle, $src) use (&$dependencies, $media) {
|
||||
|
||||
wp_enqueue_style($handle, $src, $dependencies, null, $media);
|
||||
$this->merge_dependencies($dependencies, [$handle]);
|
||||
});
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue JS files in WordPress.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function enqueue_js(bool|array $args = true, array $dependencies = [])
|
||||
{
|
||||
$this->js(function ($handle, $src, $bundleDependencies) use (&$dependencies, $args) {
|
||||
|
||||
$this->merge_dependencies($dependencies, $bundleDependencies);
|
||||
wp_enqueue_script($handle, $src, $dependencies, null, $args);
|
||||
$this->inline_runtime();
|
||||
$this->merge_dependencies($dependencies, [$handle]);
|
||||
});
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue JS and CSS files in WordPress.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function enqueue()
|
||||
{
|
||||
return $this->enqueue_css()->enqueue_js();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add CSS files as editor styles in WordPress.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function editor_styles()
|
||||
{
|
||||
$relative_path = (new Filesystem())->get_relative_path(Str::finish(get_theme_file_path(), '/'), $this->path);
|
||||
$this->css(function ($handle, $src) use ($relative_path) {
|
||||
|
||||
if (! Str::starts_with($src, $this->uri)) {
|
||||
return add_editor_style($src);
|
||||
}
|
||||
|
||||
$style = Str::of($src)
|
||||
->after($this->uri)
|
||||
->ltrim('/')
|
||||
->start("{$relative_path}/")
|
||||
->to_string();
|
||||
add_editor_style($style);
|
||||
});
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dequeue CSS files in WordPress.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function dequeue_css()
|
||||
{
|
||||
$this->css(function ($handle) {
|
||||
|
||||
wp_dequeue_style($handle);
|
||||
});
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dequeue JS files in WordPress.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function dequeue_js()
|
||||
{
|
||||
$this->js(function ($handle) {
|
||||
|
||||
wp_dequeue_script($handle);
|
||||
});
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dequeue JS and CSS files in WordPress.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function dequeue()
|
||||
{
|
||||
return $this->dequeue_css()->dequeue_js();
|
||||
}
|
||||
|
||||
/**
|
||||
* Inline runtime.js in WordPress.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function inline_runtime()
|
||||
{
|
||||
if (! $runtime = $this->runtime()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (isset(self::$inlined[$runtime])) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($contents = $this->runtime_source()) {
|
||||
$this->inline($contents, 'before');
|
||||
}
|
||||
|
||||
self::$inlined[$runtime] = $contents;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an inline script before or after the bundle loads
|
||||
*
|
||||
* @param string $contents
|
||||
* @param string $position
|
||||
* @return $this
|
||||
*/
|
||||
public function inline($contents, $position = 'after')
|
||||
{
|
||||
if (! $handles = array_keys($this->js()->keys()->toArray())) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$handle = "{$this->id}/" . (
|
||||
$position === 'after'
|
||||
? array_pop($handles)
|
||||
: array_shift($handles)
|
||||
);
|
||||
wp_add_inline_script($handle, $contents, $position);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add localization data to be used by the bundle
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $object
|
||||
* @return $this
|
||||
*/
|
||||
public function localize($name, $object)
|
||||
{
|
||||
if (! $handles = $this->js()->keys()->toArray()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$handle = "{$this->id}/{$handles[0]}";
|
||||
wp_localize_script($handle, $name, $object);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add script translations to be used by the bundle
|
||||
*
|
||||
* @param string $domain
|
||||
* @param string $path
|
||||
* @return $this
|
||||
*/
|
||||
public function translate($domain = null, $path = null)
|
||||
{
|
||||
$domain ??= wp_get_theme()->get('TextDomain');
|
||||
$path ??= lang_path();
|
||||
$this->js()->keys()->each(function ($handle) use ($domain, $path) {
|
||||
|
||||
wp_set_script_translations("{$this->id}/{$handle}", $domain, $path);
|
||||
});
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two or more arrays.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function merge_dependencies(array &$dependencies, array ...$moreDependencies)
|
||||
{
|
||||
$dependencies = array_unique(array_merge($dependencies, ...$moreDependencies));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset inlined sources.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function reset_inlined_sources()
|
||||
{
|
||||
self::$inlined = [];
|
||||
}
|
||||
}
|
||||
72
src/Assets/Contracts/Asset.php
Normal file
72
src/Assets/Contracts/Asset.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*
|
||||
* This file was copied from Roots\Acorn.
|
||||
* Original author: Roots
|
||||
* Original source: https://github.com/roots/acorn/blob/main/src/Roots/Acorn/Assets/Contracts/Asset.php
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Assets\Contracts;
|
||||
|
||||
interface Asset
|
||||
{
|
||||
/**
|
||||
* Get the asset's remote URI
|
||||
*
|
||||
* Example: https://example.com/app/themes/sage/dist/styles/a1b2c3.min.css
|
||||
*/
|
||||
public function uri(): string;
|
||||
/**
|
||||
* Get the asset's local path
|
||||
*
|
||||
* Example: /srv/www/example.com/current/web/app/themes/sage/dist/styles/a1b2c3.min.css
|
||||
*/
|
||||
public function path(): string;
|
||||
/**
|
||||
* Check whether the asset exists on the file system
|
||||
*/
|
||||
public function exists(): bool;
|
||||
/**
|
||||
* Get the contents of the asset
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function contents();
|
||||
/**
|
||||
* Get the relative path to the asset.
|
||||
*
|
||||
* @param string $base_path Base path to use for relative path.
|
||||
*/
|
||||
public function relative_path(string $base_path): string;
|
||||
/**
|
||||
* Get data URL of asset.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function data_url();
|
||||
/**
|
||||
* Get asset file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function file();
|
||||
/**
|
||||
* Include asset.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function include();
|
||||
|
||||
/**
|
||||
* Get raw content of asset.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
//public function php ();
|
||||
}
|
||||
27
src/Assets/Contracts/Asset_Meta.php
Normal file
27
src/Assets/Contracts/Asset_Meta.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Assets\Contracts;
|
||||
|
||||
interface Asset_Meta
|
||||
{
|
||||
/**
|
||||
* Get the asset version.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function version();
|
||||
/**
|
||||
* Get the asset dependencies.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function dependencies();
|
||||
}
|
||||
22
src/Assets/Contracts/Bundle.php
Normal file
22
src/Assets/Contracts/Bundle.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*
|
||||
* This file was copied from Roots\Acorn.
|
||||
* Original author: Roots
|
||||
* Original source: https://github.com/roots/acorn/blob/main/src/Roots/Acorn/Assets/Contracts/Bundle.php
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Assets\Contracts;
|
||||
|
||||
interface Bundle
|
||||
{
|
||||
public function css();
|
||||
public function js();
|
||||
public function runtime();
|
||||
}
|
||||
38
src/Assets/Contracts/Manifest.php
Normal file
38
src/Assets/Contracts/Manifest.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*
|
||||
* This file was copied from Roots\Acorn.
|
||||
* Original author: Roots
|
||||
* Original source: https://github.com/roots/acorn/blob/main/src/Roots/Acorn/Assets/Contracts/Manifest.php
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Assets\Contracts;
|
||||
|
||||
interface Manifest
|
||||
{
|
||||
/**
|
||||
* Get an asset object from the Manifest
|
||||
*
|
||||
* @param string $key
|
||||
*/
|
||||
public function asset($key): Asset;
|
||||
/**
|
||||
* Get an asset bundle from the Manifest
|
||||
*
|
||||
* @param string $key
|
||||
*/
|
||||
public function bundle($key): Bundle;
|
||||
|
||||
/**
|
||||
* Get an php file from the Manifest
|
||||
*
|
||||
* @param string $key
|
||||
*/
|
||||
//public function php($key): Php;
|
||||
}
|
||||
21
src/Assets/Contracts/Meta.php
Normal file
21
src/Assets/Contracts/Meta.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Assets\Contracts;
|
||||
|
||||
interface Meta
|
||||
{
|
||||
/**
|
||||
* Get an asset meta object from the Store
|
||||
*
|
||||
* @param string $key
|
||||
*/
|
||||
public function meta($key): Asset_Meta;
|
||||
}
|
||||
21
src/Assets/Contracts/Php.php
Normal file
21
src/Assets/Contracts/Php.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Assets\Contracts;
|
||||
|
||||
interface Php
|
||||
{
|
||||
/**
|
||||
* Get an php file from the Manifest
|
||||
*
|
||||
* @param string $key
|
||||
*/
|
||||
public function php();
|
||||
}
|
||||
11
src/Assets/Exceptions/Binding_Resolution_Exception.php
Normal file
11
src/Assets/Exceptions/Binding_Resolution_Exception.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Webshr\Core\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
|
||||
class Binding_Resolution_Exception extends Exception implements ContainerExceptionInterface
|
||||
{
|
||||
//
|
||||
}
|
||||
10
src/Assets/Exceptions/Bundle_Not_Found_Exception.php
Normal file
10
src/Assets/Exceptions/Bundle_Not_Found_Exception.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Webshr\Core\Assets\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class Bundle_Not_Found_Exception extends Exception
|
||||
{
|
||||
//
|
||||
}
|
||||
10
src/Assets/Exceptions/Manifest_Not_Found_Exception.php
Normal file
10
src/Assets/Exceptions/Manifest_Not_Found_Exception.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Webshr\Core\Assets\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class Manifest_Not_Found_Exception extends Exception
|
||||
{
|
||||
//
|
||||
}
|
||||
10
src/Assets/Exceptions/Meta_Not_Found_Exception.php
Normal file
10
src/Assets/Exceptions/Meta_Not_Found_Exception.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Webshr\Core\Assets\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class Meta_Not_Found_Exception extends Exception
|
||||
{
|
||||
//
|
||||
}
|
||||
60
src/Assets/Exceptions/Skip_Module_Exception.php
Normal file
60
src/Assets/Exceptions/Skip_Module_Exception.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Webshr\Core\Assets\Exceptions;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Throwable;
|
||||
|
||||
class Skip_Module_Exception extends InvalidArgumentException
|
||||
{
|
||||
/**
|
||||
* Create a new exception.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(string $message = '', int $code = 0, ?Throwable $previous = null, string $package = '')
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
|
||||
$this->package = $package;
|
||||
}
|
||||
|
||||
/**
|
||||
* Name of the module's package.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $package;
|
||||
|
||||
/**
|
||||
* Set the name of the module's package.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set_package(string $package)
|
||||
{
|
||||
$this->package = $package;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the module's package.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function package()
|
||||
{
|
||||
return $this->package;
|
||||
}
|
||||
|
||||
/**
|
||||
* Report the exception.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function context()
|
||||
{
|
||||
return [
|
||||
'package' => $this->package(),
|
||||
];
|
||||
}
|
||||
}
|
||||
114
src/Assets/Manager.php
Normal file
114
src/Assets/Manager.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*
|
||||
* This file was copied from Roots\Acorn.
|
||||
* Original author: Roots
|
||||
* Original source: https://github.com/roots/acorn/blob/main/src/Roots/Acorn/
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Assets;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Webshr\Core\Assets\Contracts\Manifest as Manifest_Interface;
|
||||
use Webshr\Core\Assets\Exceptions\Manifest_Not_Found_Exception;
|
||||
|
||||
/**
|
||||
* Manage assets manifests
|
||||
*
|
||||
* @see \Illuminate\Support\Manager
|
||||
* @link https://github.com/illuminate/support/blob/8.x/Manager.php
|
||||
*/
|
||||
class Manager
|
||||
{
|
||||
/**
|
||||
* Resolved manifests
|
||||
*
|
||||
* @var Manifest_Interface[]
|
||||
*/
|
||||
protected $manifests;
|
||||
/**
|
||||
* Assets Config
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $config;
|
||||
/**
|
||||
* Initialize the Asset Manager instance.
|
||||
*
|
||||
* @param array $config
|
||||
*/
|
||||
public function __construct($config = [])
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the given manifest
|
||||
*
|
||||
* @param Manifest $manifest
|
||||
* @return static
|
||||
*/
|
||||
public function register(string $name, Manifest_Interface $manifest): self
|
||||
{
|
||||
$this->manifests[$name] = $manifest;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Manifest
|
||||
*/
|
||||
public function manifest(string $name, ?array $config = null): Manifest_Interface
|
||||
{
|
||||
$manifest = $this->manifests[$name] ?? $this->resolve($name, $config);
|
||||
return $this->manifests[$name] = $manifest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the given manifest.
|
||||
*
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
protected function resolve(string $name, ?array $config): Manifest_Interface
|
||||
{
|
||||
$config = $config ?? $this->get_config($name);
|
||||
if (isset($config['handler'])) {
|
||||
return new $config['handler']($config);
|
||||
}
|
||||
|
||||
$path = $config['path'];
|
||||
$url = $config['url'];
|
||||
$assets = isset($config['assets']) ? $this->get_json_manifest($config['assets']) : [];
|
||||
$bundles = isset($config['bundles']) ? $this->get_json_manifest($config['bundles']) : [];
|
||||
return new Manifest($path, $url, $assets, $bundles);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Opens a JSON manifest file from the local file system
|
||||
*
|
||||
* @param string $json_manifest Path to .json file
|
||||
*/
|
||||
protected function get_json_manifest(string $json_manifest): array
|
||||
{
|
||||
if (! file_exists($json_manifest)) {
|
||||
throw new Manifest_Not_Found_Exception("The asset manifest [{$json_manifest}] cannot be found.");
|
||||
}
|
||||
|
||||
return json_decode(file_get_contents($json_manifest), true) ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the assets manifest configuration.
|
||||
*/
|
||||
protected function get_config(string $name): array
|
||||
{
|
||||
return $this->config['manifests'][$name];
|
||||
}
|
||||
}
|
||||
150
src/Assets/Manifest.php
Normal file
150
src/Assets/Manifest.php
Normal file
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*
|
||||
* This file was copied from Roots\Acorn.
|
||||
* Original author: Roots
|
||||
* Original source: https://github.com/roots/acorn/blob/main/src/Roots/Acorn/
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Assets;
|
||||
|
||||
use Webshr\Core\Support\Str;
|
||||
use Webshr\Core\Assets\Exceptions\Bundle_Not_Found_Exception;
|
||||
use Webshr\Core\Assets\Contracts\Asset as Asset_Interface;
|
||||
use Webshr\Core\Assets\Contracts\Asset_Meta as Asset_Meta_Interface;
|
||||
use Webshr\Core\Assets\Contracts\Bundle as Bundle_Interface;
|
||||
use Webshr\Core\Assets\Contracts\Manifest as Manifest_Interface;
|
||||
|
||||
class Manifest implements Manifest_Interface
|
||||
{
|
||||
/**
|
||||
* The manifest assets.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $assets;
|
||||
/**
|
||||
* The manifest meta assets.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $metas;
|
||||
/**
|
||||
* The manifest bundles.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $bundles;
|
||||
/**
|
||||
* The manifest path.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
/**
|
||||
* The manifest URI.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $uri;
|
||||
/**
|
||||
* Create a new manifest instance.
|
||||
*/
|
||||
public function __construct(string $path, string $uri, array $assets = [], ?array $bundles = null, ?array $metas = null)
|
||||
{
|
||||
$this->path = $path;
|
||||
$this->uri = $uri;
|
||||
$this->bundles = $bundles;
|
||||
$this->metas = $metas;
|
||||
foreach ($assets as $original => $revved) {
|
||||
$this->assets[$this->normalize_relative_path($original)] = $this->normalize_relative_path($revved);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get specified asset.
|
||||
*
|
||||
* @param string $key
|
||||
*/
|
||||
public function asset($key): Asset_Interface
|
||||
{
|
||||
$key = $this->normalize_relative_path($key);
|
||||
$relativePath = $this->assets[$key] ?? $key;
|
||||
$path = Str::before("{$this->path}/{$relativePath}", '?');
|
||||
$uri = "{$this->uri}/{$relativePath}";
|
||||
return Asset_Factory::create($path, $uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get specified asset.
|
||||
*
|
||||
* @param string $key
|
||||
*/
|
||||
public function php($key)
|
||||
{
|
||||
$key = $this->normalize_relative_path($key);
|
||||
$relativePath = $this->assets[$key] ?? $key;
|
||||
$path = Str::before("{$this->path}/{$relativePath}", '?');
|
||||
$uri = "{$this->uri}/{$relativePath}";
|
||||
return Asset_Factory::create($path, $uri, 'php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get specified meta asset.
|
||||
*
|
||||
* @param string $key
|
||||
*/
|
||||
public function meta($key): Asset_Meta_Interface
|
||||
{
|
||||
$key = $this->normalize_relative_path($key);
|
||||
// Check if the meta asset is already cached
|
||||
if (isset($this->metas[$key])) {
|
||||
return $this->metas[$key];
|
||||
}
|
||||
|
||||
$relativePath = $this->assets[$key] ?? $key;
|
||||
$path = Str::before("{$this->path}/{$relativePath}", '?');
|
||||
$uri = "{$this->uri}/{$relativePath}";
|
||||
if (! isset($this->metas[$key])) {
|
||||
$meta = Asset_Factory::create($path, $uri, 'meta');
|
||||
} else {
|
||||
$meta = new Meta($key, $this->metas[$key], $this->path, $this->uri);
|
||||
}
|
||||
|
||||
// Cache the created meta asset
|
||||
$this->metas[$key] = $meta;
|
||||
return $meta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get specified bundles.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @throws \Webshr\Core\Assets\Exceptions\Bundle_Not_Found_Exception
|
||||
*/
|
||||
public function bundle($key): Bundle_Interface
|
||||
{
|
||||
if (! isset($this->bundles[$key])) {
|
||||
throw new Bundle_Not_Found_Exception("Bundle [{$key}] not found in manifest.");
|
||||
}
|
||||
|
||||
return new Bundle($key, $this->bundles[$key], $this->path, $this->uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes to forward slashes and removes leading slash.
|
||||
*/
|
||||
protected function normalize_relative_path(string $path): string
|
||||
{
|
||||
$path = str_replace('\\', '/', $path);
|
||||
$path = preg_replace('%//+%', '/', $path);
|
||||
return ltrim($path, './');
|
||||
}
|
||||
}
|
||||
75
src/Assets/Meta.php
Normal file
75
src/Assets/Meta.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Assets;
|
||||
|
||||
use Webshr\Core\Assets\Contracts\Asset_Meta as Meta_Interface;
|
||||
|
||||
class Meta implements Meta_Interface
|
||||
{
|
||||
//use Conditional, Enqueuable;
|
||||
|
||||
/**
|
||||
* The meta ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
/**
|
||||
* The meta path.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
/**
|
||||
* The meta URI.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $uri;
|
||||
/**
|
||||
* The meta contents.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $meta;
|
||||
/**
|
||||
* Create a new meta.
|
||||
*/
|
||||
public function __construct(string $id, array $meta, string $path, string $uri = '/')
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->path = $path;
|
||||
$this->uri = $uri;
|
||||
$this->meta = $meta;
|
||||
$this->dependencies();
|
||||
$this->version();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the meta dependencies.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function version()
|
||||
{
|
||||
return $this->meta['version'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the meta dependencies.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function dependencies()
|
||||
{
|
||||
return $this->meta['dependencies'] ?? [];
|
||||
}
|
||||
}
|
||||
242
src/Bootloader.php
Normal file
242
src/Bootloader.php
Normal file
@@ -0,0 +1,242 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*
|
||||
* This file heavily inspired by Roots\Acorn.
|
||||
* Original author: Roots
|
||||
* Original source: https://github.com/roots/acorn/blob/main/src/Roots/Acorn/
|
||||
*/
|
||||
|
||||
namespace Webshr\Core;
|
||||
|
||||
use Webshr\Core\Filesystem\Filesystem;
|
||||
use Webshr\Core\Config\Repository;
|
||||
|
||||
class Bootloader
|
||||
{
|
||||
/**
|
||||
* The Bootloader instance.
|
||||
*/
|
||||
protected static $instance;
|
||||
/**
|
||||
* The Application instance.
|
||||
*
|
||||
* @var \Webshr\Core\Application|null
|
||||
*/
|
||||
protected ?Application $app;
|
||||
/**
|
||||
* The Filesystem instance.
|
||||
*
|
||||
* @var \Webshr\Core\Filesystem\Filesystem
|
||||
*/
|
||||
protected Filesystem $files;
|
||||
/**
|
||||
* The configuration repository.
|
||||
*
|
||||
* @var \Webshr\Core\Config\Repository
|
||||
*/
|
||||
protected Repository $config;
|
||||
/**
|
||||
* The application's base path.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected string $base_path = '';
|
||||
/**
|
||||
* The configuration settings.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected array $configss;
|
||||
/**
|
||||
* Create a new bootloader instance.
|
||||
*
|
||||
* @param \Webshr\Core\Application|null $app
|
||||
* @param \Webshr\Core\Filesystem\Filesystem|null $files
|
||||
*/
|
||||
public function __construct(?Application $app = null, ?Filesystem $files = null)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->files = $files ?? new Filesystem();
|
||||
// Load all configuration files into the repository
|
||||
$this->config = new Repository($this->load_configuration_files());
|
||||
static::$instance ??= $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Boot the Application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __invoke(): void
|
||||
{
|
||||
$this->boot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Bootloader instance.
|
||||
*
|
||||
* @param \Webshr\Core\Bootloader|null $bootloader
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function set_instance(?self $bootloader): void
|
||||
{
|
||||
static::$instance = $bootloader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Bootloader instance.
|
||||
*
|
||||
* @param \Webshr\Core\Application|null $app
|
||||
*
|
||||
* @return \Webshr\Core\Bootloader
|
||||
*/
|
||||
public static function get_instance(?Application $app = null): static
|
||||
{
|
||||
return static::$instance ??= new static($app);
|
||||
}
|
||||
|
||||
/**
|
||||
* Boot the Application.
|
||||
*
|
||||
* @param callable|null $callback
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot(?callable $callback = null): void
|
||||
{
|
||||
$this->get_application();
|
||||
if ($callback) {
|
||||
$callback($this->app);
|
||||
}
|
||||
|
||||
if ($this->app->has_been_bootstrapped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->app->boot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize and retrieve the Application instance.
|
||||
*
|
||||
* @return \Webshr\Core\Application
|
||||
*/
|
||||
public function get_application(): Application
|
||||
{
|
||||
$this->app ??= new Application($this->encryption(), $this->base_path(), $this->use_path());
|
||||
$this->app->use_environment_path($this->environment_path());
|
||||
$this->app->use_default_manifest($this->default_manifest());
|
||||
$this->app->use_manifests($this->manifests());
|
||||
$this->app->use_modules($this->modules());
|
||||
return $this->app;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all configuration files.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function load_configuration_files(): array
|
||||
{
|
||||
$config_path = get_template_directory() . '/config';
|
||||
$config_files = $this->files->files($config_path);
|
||||
$config = [];
|
||||
foreach ($config_files as $file) {
|
||||
$key = basename($file, '.php');
|
||||
$config[$key] = require $file;
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the application's encryption values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function encryption(): array
|
||||
{
|
||||
$encryption = [];
|
||||
$encryption['cipher'] = $this->config->get('app.cipher');
|
||||
$encryption['key'] = $this->config->get('app.key');
|
||||
$encryption['previous_keys'] = $this->config->get('app.previous_keys');
|
||||
return $encryption;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the application's base path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function base_path(): string
|
||||
{
|
||||
return $this->config->get('app.paths.base');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the environment file path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function environment_path(): string
|
||||
{
|
||||
return $this->config->get('app.paths.env');
|
||||
}
|
||||
|
||||
/**
|
||||
* Use paths that are configurable by the developer.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function use_path(): array
|
||||
{
|
||||
return $this->config->get('app.paths');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default manifest name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function default_manifest(): string
|
||||
{
|
||||
return $this->config->get('assets.default');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the application's manifests.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function manifests(): array
|
||||
{
|
||||
return $this->config->get('assets.manifests');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the application's modules
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function modules(): array
|
||||
{
|
||||
return $this->config->get('app.modules');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the application's aliases.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function aliases(): array
|
||||
{
|
||||
return $this->config->get('app.aliases');
|
||||
}
|
||||
}
|
||||
109
src/Config/Environment.php
Normal file
109
src/Config/Environment.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*
|
||||
* This file contains a customized adaptation of the Laravel\Illuminate package.
|
||||
* Original author: Laravel
|
||||
* Original source: https://laravel.com/api/master/Illuminate/Foundation.html
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Config;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class Environment
|
||||
{
|
||||
/**
|
||||
* Gets the value of an environment variable.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $default
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get($key, $default = null)
|
||||
{
|
||||
$value = getenv($key);
|
||||
|
||||
if ($value === false) {
|
||||
return \Webshr\Core\value($default);
|
||||
}
|
||||
|
||||
switch (strtolower($value)) {
|
||||
case 'true':
|
||||
case '(true)':
|
||||
return true;
|
||||
case 'false':
|
||||
case '(false)':
|
||||
return false;
|
||||
case 'empty':
|
||||
case '(empty)':
|
||||
return '';
|
||||
case 'null':
|
||||
case '(null)':
|
||||
return null;
|
||||
}
|
||||
|
||||
if (($valueLength = strlen($value)) > 1 && $value[0] === '"' && $value[($valueLength - 1)] === '"') {
|
||||
return substr($value, 1, -1);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of a required environment variable.
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public static function get_or_fail($key)
|
||||
{
|
||||
$value = static::get($key);
|
||||
|
||||
if (is_null($value)) {
|
||||
throw new RuntimeException("Environment variable [{$key}] has no value.");
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an environment variable exists.
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public static function has($key)
|
||||
{
|
||||
return getenv($key) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an environment variable.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public static function set($key, $value)
|
||||
{
|
||||
return putenv("{$key}={$value}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all environment variables.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function all()
|
||||
{
|
||||
return getenv();
|
||||
}
|
||||
}
|
||||
189
src/Config/Repository.php
Normal file
189
src/Config/Repository.php
Normal file
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*
|
||||
* This file contains a customized adaptation of the Laravel\Illuminate package.
|
||||
* Original author: Laravel
|
||||
* Original source: https://laravel.com/api/master/Illuminate/Foundation.html
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Config;
|
||||
|
||||
use ArrayAccess;
|
||||
use Webshr\Core\Contracts\Repository as Config_Interface;
|
||||
use Webshr\Core\Support\Arriable;
|
||||
use Webshr\Core\Support\Traits\Macroable;
|
||||
|
||||
class Repository implements ArrayAccess, Config_Interface
|
||||
{
|
||||
use Macroable;
|
||||
|
||||
/**
|
||||
* All of the configuration items.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
|
||||
|
||||
protected $items = [];
|
||||
/**
|
||||
* Create a new configuration repository.
|
||||
*
|
||||
* @param array $items
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(array $items = [])
|
||||
{
|
||||
$this->items = $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given configuration value exists.
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key)
|
||||
{
|
||||
return Arriable::has($this->items, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the specified configuration value.
|
||||
*
|
||||
* @param array|string $key
|
||||
* @param mixed $default
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($key, $default = null)
|
||||
{
|
||||
if (is_array($key)) {
|
||||
return $this->get_many($key);
|
||||
}
|
||||
|
||||
return Arriable::get($this->items, $key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get many configuration values.
|
||||
*
|
||||
* @param array $keys
|
||||
* @return array
|
||||
*/
|
||||
public function get_many($keys)
|
||||
{
|
||||
$config = [];
|
||||
foreach ($keys as $key => $default) {
|
||||
if (is_numeric($key)) {
|
||||
[$key, $default] = [$default, null];
|
||||
}
|
||||
|
||||
$config[$key] = Arriable::get($this->items, $key, $default);
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a given configuration value.
|
||||
*
|
||||
* @param array|string $key
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function set($key, $value = null)
|
||||
{
|
||||
$keys = is_array($key) ? $key : [$key => $value];
|
||||
foreach ($keys as $key => $value) {
|
||||
Arriable::set($this->items, $key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepend a value onto an array configuration value.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function prepend($key, $value)
|
||||
{
|
||||
$array = $this->get($key, []);
|
||||
array_unshift($array, $value);
|
||||
$this->set($key, $array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a value onto an array configuration value.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function push($key, $value)
|
||||
{
|
||||
$array = $this->get($key, []);
|
||||
$array[] = $value;
|
||||
$this->set($key, $array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the configuration items for the application.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function all()
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given configuration option exists.
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($key): bool
|
||||
{
|
||||
return $this->has($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a configuration option.
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function offsetGet($key): mixed
|
||||
{
|
||||
return $this->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a configuration option.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function offsetSet($key, $value): void
|
||||
{
|
||||
$this->set($key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unset a configuration option.
|
||||
*
|
||||
* @param string $key
|
||||
* @return void
|
||||
*/
|
||||
public function offsetUnset($key): void
|
||||
{
|
||||
$this->set($key, null);
|
||||
}
|
||||
}
|
||||
87
src/Contracts/Application.php
Normal file
87
src/Contracts/Application.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*
|
||||
* This file contains a customized adaptation of the Illuminate\Foundation package.
|
||||
* Original author: Laravel
|
||||
* Original source: https://laravel.com/api/master/Illuminate/Foundation.html
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Contracts;
|
||||
|
||||
interface Application
|
||||
{
|
||||
/**
|
||||
* Get the version number of the application.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function version();
|
||||
|
||||
/**
|
||||
* Get the base path of the installation.
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
public function base_path($path = '');
|
||||
|
||||
/**
|
||||
* Get the path to the application configuration files.
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
public function config_path($path = '');
|
||||
|
||||
/**
|
||||
* Get the path to the language files.
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
public function lang_path($path = '');
|
||||
|
||||
/**
|
||||
* Register a shared binding in the application.
|
||||
*
|
||||
* @param string $abstract
|
||||
* @param \Closure|string|null $concrete
|
||||
* @return void
|
||||
*/
|
||||
public function singleton($abstract, $concrete = null);
|
||||
|
||||
/**
|
||||
* Resolve the given type from the application.
|
||||
*
|
||||
* @param string $abstract
|
||||
* @param array $parameters
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Webshr\Core\Exceptions\Binding_Resolution_Exception
|
||||
*/
|
||||
public function make($abstract, array $parameters = []);
|
||||
|
||||
/**
|
||||
* Call the given Closure / class@method and inject its dependencies.
|
||||
*
|
||||
* @param callable|string $callback
|
||||
* @param array $parameters
|
||||
* @param string|null $default_method
|
||||
* @return mixed
|
||||
*/
|
||||
public function call($callback, array $parameters = [], $default_method = null);
|
||||
|
||||
/**
|
||||
* Get the value of a configuration key.
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function get(string $key);
|
||||
}
|
||||
25
src/Contracts/Deferrable_Module.php
Normal file
25
src/Contracts/Deferrable_Module.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*
|
||||
* This file contains a customized adaptation of the Illuminate\Foundation package.
|
||||
* Original author: Laravel
|
||||
* Original source: https://laravel.com/api/master/Illuminate/Foundation.html
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Contracts;
|
||||
|
||||
interface Deferrable_Module
|
||||
{
|
||||
/**
|
||||
* Get the services provided by the provider.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function provides();
|
||||
}
|
||||
64
src/Contracts/Repository.php
Normal file
64
src/Contracts/Repository.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*
|
||||
* This file contains a customized adaptation of the Illuminate\Foundation package.
|
||||
* Original author: Laravel
|
||||
* Original source: https://laravel.com/api/master/Illuminate/Foundation.html
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Contracts;
|
||||
|
||||
interface Repository
|
||||
{
|
||||
/**
|
||||
* Determine if the given configuration value exists.
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key);
|
||||
/**
|
||||
* Get the specified configuration value.
|
||||
*
|
||||
* @param array|string $key
|
||||
* @param mixed $default
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($key, $default = null);
|
||||
/**
|
||||
* Get all of the configuration items for the application.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function all();
|
||||
/**
|
||||
* Set a given configuration value.
|
||||
*
|
||||
* @param array|string $key
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function set($key, $value = null);
|
||||
/**
|
||||
* Prepend a value onto an array configuration value.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function prepend($key, $value);
|
||||
/**
|
||||
* Push a value onto an array configuration value.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function push($key, $value);
|
||||
}
|
||||
10
src/Filesystem/Exceptions/File_Not_Found_Exception.php
Normal file
10
src/Filesystem/Exceptions/File_Not_Found_Exception.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Webshr\Core\Filesystem\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class File_Not_Found_Exception extends Exception
|
||||
{
|
||||
//
|
||||
}
|
||||
184
src/Filesystem/Filesystem.php
Normal file
184
src/Filesystem/Filesystem.php
Normal file
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*
|
||||
* This file contains a customized adaptation of the Roots\Acorn package.
|
||||
* Original author: Roots
|
||||
* Original source: https://github.com/roots/acorn/blob/main/src/Roots/Acorn/
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Filesystem;
|
||||
|
||||
class Filesystem
|
||||
{
|
||||
/**
|
||||
* Normalizes file path separators
|
||||
*
|
||||
* @param mixed $path
|
||||
* @param string $separator
|
||||
* @return mixed
|
||||
*/
|
||||
public function normalize_path($path, $separator = '/')
|
||||
{
|
||||
return preg_replace('#/+#', $separator, strtr($path, '\\', '/'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the closest file up the directory tree.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $file
|
||||
* @return string|null
|
||||
*/
|
||||
public function closest($path, $file)
|
||||
{
|
||||
$currentDirectory = $path;
|
||||
while ($this->is_readable($currentDirectory)) {
|
||||
if ($this->is_file($filePath = $currentDirectory . DIRECTORY_SEPARATOR . $file)) {
|
||||
return $filePath;
|
||||
}
|
||||
|
||||
$parentDirectory = $this->dirname($currentDirectory);
|
||||
if (empty($parentDirectory) || $parentDirectory === $currentDirectory) {
|
||||
break;
|
||||
}
|
||||
|
||||
$currentDirectory = $parentDirectory;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get relative path of target from specified base
|
||||
*
|
||||
* @param string $base_path
|
||||
* @param string $target_path
|
||||
* @return string
|
||||
*
|
||||
* @copyright Fabien Potencier
|
||||
* @license GPL-3.0-or-later
|
||||
*
|
||||
* @link https://github.com/symfony/routing/blob/v4.1.1/Generator/UrlGenerator.php#L280-L329
|
||||
*/
|
||||
public function get_relative_path($base_path, $target_path)
|
||||
{
|
||||
$base_path = $this->normalize_path($base_path);
|
||||
$target_path = $this->normalize_path($target_path);
|
||||
if ($base_path === $target_path) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$source_dirs = explode('/', ltrim($base_path, '/'));
|
||||
$target_dirs = explode('/', ltrim($target_path, '/'));
|
||||
array_pop($source_dirs);
|
||||
$target_file = array_pop($target_dirs);
|
||||
foreach ($source_dirs as $i => $dir) {
|
||||
if (isset($target_dirs[$i]) && $dir === $target_dirs[$i]) {
|
||||
unset($source_dirs[$i], $target_dirs[$i]);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$target_dirs[] = $target_file;
|
||||
$path = str_repeat('../', count($source_dirs)) . implode('/', $target_dirs);
|
||||
return $path === '' || $path[0] === '/'
|
||||
|| ($colon_pos = strpos($path, ':')) !== false && ($colon_pos < ($slash_pos = strpos($path, '/'))
|
||||
|| $slash_pos === false)
|
||||
? "./$path" : $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure a directory exists.
|
||||
*
|
||||
* @param string $path
|
||||
* @param int $mode
|
||||
* @param bool $recursive
|
||||
* @return void
|
||||
*/
|
||||
public function ensure_directory_exists($path, $mode = 0755, $recursive = true)
|
||||
{
|
||||
if (! $this->is_directory($path)) {
|
||||
$this->make_directory($path, $mode, $recursive);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a directory.
|
||||
*
|
||||
* @param string $path
|
||||
* @param int $mode
|
||||
* @param bool $recursive
|
||||
* @param bool $force
|
||||
* @return bool
|
||||
*/
|
||||
public function make_directory($path, $mode = 0755, $recursive = false, $force = false)
|
||||
{
|
||||
if ($force) {
|
||||
return @mkdir($path, $mode, $recursive);
|
||||
}
|
||||
|
||||
return mkdir($path, $mode, $recursive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the parent directory from a file path.
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
public function dirname($path)
|
||||
{
|
||||
return pathinfo($path, PATHINFO_DIRNAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given path is a directory.
|
||||
*
|
||||
* @param string $directory
|
||||
* @return bool
|
||||
*/
|
||||
public function is_directory($directory)
|
||||
{
|
||||
return is_dir($directory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all files in a directory.
|
||||
*
|
||||
* @param string $directory
|
||||
* @return array
|
||||
*/
|
||||
public function files(string $directory): array
|
||||
{
|
||||
return glob($directory . '/*.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given path is a file.
|
||||
*
|
||||
* @param string $file
|
||||
* @return bool
|
||||
*/
|
||||
public function is_file($file)
|
||||
{
|
||||
return is_file($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given path is readable.
|
||||
*
|
||||
* @param string $path
|
||||
* @return bool
|
||||
*/
|
||||
public function is_readable($path)
|
||||
{
|
||||
return is_readable($path);
|
||||
}
|
||||
}
|
||||
97
src/Filesystem/Loader.php
Normal file
97
src/Filesystem/Loader.php
Normal file
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @version 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Filesystem;
|
||||
|
||||
use DirectoryIterator;
|
||||
|
||||
/**
|
||||
* Provides a set of methods that are used to load files.
|
||||
*/
|
||||
class Loader
|
||||
{
|
||||
/**
|
||||
* Iterates through a directory and executes the provided callback function
|
||||
* on each file or folder in the directory (excluding dot files).
|
||||
*
|
||||
* @since 0.1.0
|
||||
*
|
||||
* @param string $dir Absolute path to the directory.
|
||||
* @param callable $callback The callback function.
|
||||
*
|
||||
* @return array An array of the callback results.
|
||||
*/
|
||||
public function iterate_dir(string $dir, callable $callback): array
|
||||
{
|
||||
$output = [];
|
||||
if (! is_dir($dir)) {
|
||||
return $output;
|
||||
}
|
||||
|
||||
$directory_iterator = new DirectoryIterator($dir);
|
||||
foreach ($directory_iterator as $file) {
|
||||
if ($file->isDot()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$callback_result = call_user_func($callback, $file);
|
||||
$output[] = $callback_result;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively require all files in a specific directory.
|
||||
*
|
||||
* By default, requires all php files in a specific directory once.
|
||||
* Optionally able to specify the files in an array to load in a certain order.
|
||||
* Starting and trailing slashes will be stripped for the directory and all files provided.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*
|
||||
* @param string $dir Directory to search through.
|
||||
* @param array $files Optional array of files to include. If this is set, only the files specified will be loaded.
|
||||
*/
|
||||
public function load(string $dir, array $files = []): void
|
||||
{
|
||||
$dir = trim($dir, '/');
|
||||
if ($files === []) {
|
||||
$dir = get_template_directory() . '/' . $dir;
|
||||
$php_files = [];
|
||||
$this->iterate_dir($dir, function ($file) use (&$php_files): void {
|
||||
|
||||
if ($file->isDir()) {
|
||||
$dir_path = trim(str_replace(get_template_directory(), '', $file->getPathname()), '/');
|
||||
$this->load($dir_path);
|
||||
} elseif ($file->isFile() && $file->getExtension() === 'php') {
|
||||
$file_path = $file->getPathname();
|
||||
$php_files[] = $file_path;
|
||||
}
|
||||
});
|
||||
// Sort files alphabetically.
|
||||
sort($php_files);
|
||||
foreach ($php_files as $php_file) {
|
||||
require_once $php_file;
|
||||
}
|
||||
} else {
|
||||
sort($files);
|
||||
foreach ($files as $file) {
|
||||
$file = $file ?? '';
|
||||
// Fix Passing null to parameter #1 ($string) of type string is deprecated error.
|
||||
$file_path = $dir . '/' . ltrim($file, '/');
|
||||
if (locate_template($file_path, true, true) === '' || locate_template($file_path, true, true) === '0') {
|
||||
trigger_error(sprintf(__('Error locating %s for inclusion', 'webshr'), $file_path), E_USER_ERROR,);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/Filesystem/functions.php
Normal file
25
src/Filesystem/functions.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Webshr\Core\Filesystem;
|
||||
|
||||
if (! function_exists('Webshr\Core\Filesystem\join_paths')) {
|
||||
/**
|
||||
* Join the given paths together.
|
||||
*
|
||||
* @param string|null $base_path
|
||||
* @param string ...$paths
|
||||
* @return string
|
||||
*/
|
||||
function join_paths($base_path, ...$paths)
|
||||
{
|
||||
foreach ($paths as $index => $path) {
|
||||
if (empty($path)) {
|
||||
unset($paths[$index]);
|
||||
} else {
|
||||
$paths[$index] = DIRECTORY_SEPARATOR . ltrim($path, DIRECTORY_SEPARATOR);
|
||||
}
|
||||
}
|
||||
|
||||
return $base_path . implode('', $paths);
|
||||
}
|
||||
}
|
||||
60
src/Module.php
Normal file
60
src/Module.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*/
|
||||
|
||||
namespace Webshr\Core;
|
||||
|
||||
use Webshr\Core\Modules\Contracts\Module as Module_Interface;
|
||||
use Webshr\Core\Modules\Module\Module as Base_Module;
|
||||
|
||||
/**
|
||||
* Module class.
|
||||
*/
|
||||
class Module extends Base_Module implements Module_Interface
|
||||
{
|
||||
/**
|
||||
* Determine if the module can be registered.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_register(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the module in WordPress.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the action hooks for the module.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_actions(): void
|
||||
{
|
||||
// Default implementation can be overridden by child classes
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the filter hooks for the module.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_filters(): void
|
||||
{
|
||||
// Default implementation can be overridden by child classes
|
||||
}
|
||||
}
|
||||
25
src/Modules/Contracts/Deferrable_Module.php
Normal file
25
src/Modules/Contracts/Deferrable_Module.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Theme
|
||||
* @since 1.0.0
|
||||
* @version 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Modules\Contracts;
|
||||
|
||||
/**
|
||||
* Deferrable module interface.
|
||||
*/
|
||||
interface Deferrable_Module
|
||||
{
|
||||
/**
|
||||
* Get the services provided by the module.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function provides();
|
||||
}
|
||||
33
src/Modules/Contracts/Module.php
Normal file
33
src/Modules/Contracts/Module.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Theme
|
||||
* @since 1.0.0
|
||||
* @version 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Modules\Contracts;
|
||||
|
||||
/**
|
||||
* Theme module interface.
|
||||
* Defines the contract that every module must follow.
|
||||
*
|
||||
*/
|
||||
interface Module
|
||||
{
|
||||
/**
|
||||
* Determine whether the module can be registered in the current context.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_register(): bool;
|
||||
/**
|
||||
* Register the module in WordPress.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register(): void;
|
||||
}
|
||||
104
src/Modules/Manager.php
Normal file
104
src/Modules/Manager.php
Normal file
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Theme
|
||||
* @since 1.0.0
|
||||
* @version 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Modules;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Webshr\Core\Modules\Contracts\Module as Module_Interface;
|
||||
|
||||
/**
|
||||
* Theme module manager.
|
||||
* Provides a set of methods to manage theme modules.
|
||||
*/
|
||||
class Manager
|
||||
{
|
||||
/**
|
||||
* Resolved modules.
|
||||
*
|
||||
* @var Module_Interface[]
|
||||
*/
|
||||
protected $modules = [];
|
||||
/**
|
||||
* Assets Config
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $config;
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $modules Associative array of modules.
|
||||
*/
|
||||
public function __construct($config = [])
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the given module
|
||||
*
|
||||
* @param Module_Interface $module
|
||||
* @return static
|
||||
*/
|
||||
public function register(string $name, Module_Interface $module): self
|
||||
{
|
||||
$this->modules[$name] = $module;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Module
|
||||
*
|
||||
*/
|
||||
public function module(string $name, ?array $config = null): Module_Interface
|
||||
{
|
||||
$module = $this->modules[$name] ?? $this->resolve($name, $config);
|
||||
return $this->modules[$name] = $module;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all modules.
|
||||
*/
|
||||
public function modules(): array
|
||||
{
|
||||
return $this->modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the given module.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
protected function resolve(string $name, ?array $config): Module_Interface
|
||||
{
|
||||
$config = $config ?? $this->get_config($name);
|
||||
|
||||
if (isset($config['handler'])) {
|
||||
return new $config['handler']($config);
|
||||
}
|
||||
|
||||
// If there's no handler in the config, use the module class directly from the modules array
|
||||
if (isset($this->config['modules'][$name])) {
|
||||
$moduleClass = $this->config['modules'][$name];
|
||||
return new $moduleClass($this->app ?? null);
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException("Module '{$name}' has no handler defined and could not be resolved.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the modules configuration.
|
||||
*/
|
||||
protected function get_config(string $name): array
|
||||
{
|
||||
return $this->config['modules'][$name];
|
||||
}
|
||||
}
|
||||
155
src/Modules/Module/Module.php
Normal file
155
src/Modules/Module/Module.php
Normal file
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Theme
|
||||
* @since 1.0.0
|
||||
* @version 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Modules\Module;
|
||||
|
||||
use Closure;
|
||||
use Webshr\Core\Modules\Contracts\Module as Module_Interface;
|
||||
use Webshr\Core\Modules\Contracts\Deferrable_Module as Deferrable_Module_Interface;
|
||||
|
||||
/**
|
||||
* Theme module class.
|
||||
* Defines the basic structure for a theme module.
|
||||
*
|
||||
*/
|
||||
abstract class Module implements Module_Interface
|
||||
{
|
||||
/**
|
||||
* The application instance.
|
||||
*
|
||||
* @var \Webshr\Core\Contracts\Application
|
||||
*/
|
||||
protected $app;
|
||||
/**
|
||||
* All of the registered booting callbacks.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $booting_callbacks = [];
|
||||
/**
|
||||
* All of the registered booted callbacks.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $booted_callbacks = [];
|
||||
/**
|
||||
* Whether the module will be a deferred one or not.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $deferred = false;
|
||||
/**
|
||||
* Create a new module instance.
|
||||
*
|
||||
* @param \Webshr\Core\Application $app
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($app)
|
||||
{
|
||||
$this->app = $app;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the module should be registered in the current context.
|
||||
*
|
||||
* Must be implemented by the child module.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function can_register(): bool;
|
||||
/**
|
||||
* Register the module in WordPress.
|
||||
*
|
||||
* Must be implemented by the child module.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
abstract public function register(): void;
|
||||
/**
|
||||
* Register a booting callback to be run before the "boot" method is called.
|
||||
*
|
||||
* @param \Closure $callback
|
||||
* @return void
|
||||
*/
|
||||
public function booting(Closure $callback)
|
||||
{
|
||||
$this->booting_callbacks[] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a booted callback to be run after the "boot" method is called.
|
||||
*
|
||||
* @param \Closure $callback
|
||||
* @return void
|
||||
*/
|
||||
public function booted(Closure $callback)
|
||||
{
|
||||
$this->booted_callbacks[] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the registered booting callbacks.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function call_booting_callbacks()
|
||||
{
|
||||
$index = 0;
|
||||
while ($index < count($this->booting_callbacks)) {
|
||||
$this->app->call($this->booting_callbacks[$index]);
|
||||
$index++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the registered booted callbacks.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function call_booted_callbacks()
|
||||
{
|
||||
$index = 0;
|
||||
while ($index < count($this->booted_callbacks)) {
|
||||
$this->app->call($this->booted_callbacks[$index]);
|
||||
$index++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the services provided by the module.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function provides()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the events that trigger this module to register.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function when()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the module is deferred.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_deferred()
|
||||
{
|
||||
return $this instanceof Deferrable_Module_Interface;
|
||||
}
|
||||
}
|
||||
35
src/Modules/Module_Factory.php
Normal file
35
src/Modules/Module_Factory.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Module Factory class.
|
||||
*
|
||||
* @package Webshr\Theme
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Modules;
|
||||
|
||||
use Webshr\Core\Modules\Contracts\Module as Module_interface;
|
||||
|
||||
class Module_Factory
|
||||
{
|
||||
/**
|
||||
* @var Module_Interface The module instance.
|
||||
*/
|
||||
protected $module;
|
||||
/**
|
||||
* Create Module instance.
|
||||
*
|
||||
* @param string $class Module class name
|
||||
* @return Module_Interface
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function create(string $class): Module_Interface
|
||||
{
|
||||
if (! class_exists($class) || ! is_subclass_of($class, Module_Interface::class)) {
|
||||
throw new \Exception("Module class {$class} must exist and implement Module_Interface.");
|
||||
}
|
||||
|
||||
return new $class();
|
||||
}
|
||||
}
|
||||
536
src/Support/Arriable.php
Normal file
536
src/Support/Arriable.php
Normal file
@@ -0,0 +1,536 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*
|
||||
* This file contains a customized adaptation of the Laravel\Illuminate\Foundation package.
|
||||
* Original author: Laravel
|
||||
* Original source: https://laravel.com/api/master/Illuminate/Foundation.html
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Support;
|
||||
|
||||
use ArrayAccess;
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* A collection of tools dealing with arrays
|
||||
*
|
||||
* @credit (limited version of) illuminate/support
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class Arriable
|
||||
{
|
||||
/**
|
||||
* Converts the given value to an array.
|
||||
*
|
||||
* If the provided value is already an array, it will be returned as-is.
|
||||
* Otherwise, the value will be wrapped in an array.
|
||||
*
|
||||
* @param mixed $value The value to be converted to an array.
|
||||
* @return array The resulting array.
|
||||
*/
|
||||
public static function to_array($value): array
|
||||
{
|
||||
return is_array($value) ? $value : [$value];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given value is array accessible.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public static function accessible($value)
|
||||
{
|
||||
return \is_array($value) || $value instanceof ArrayAccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an element to an array using "dot" notation if it doesn't exist.
|
||||
*
|
||||
* @param array $array
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return array
|
||||
*/
|
||||
public static function add($array, $key, $value)
|
||||
{
|
||||
if (\is_null(static::get($array, $key))) {
|
||||
static::set($array, $key, $value);
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collapse an array of arrays into a single array.
|
||||
*
|
||||
* @param array $array
|
||||
* @return array
|
||||
*/
|
||||
public static function collapse($array)
|
||||
{
|
||||
$results = [];
|
||||
foreach ($array as $values) {
|
||||
if (! \is_array($values)) {
|
||||
continue;
|
||||
}
|
||||
$results = \array_merge($results, $values);
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Divide an array into two arrays. One with keys and the other with values.
|
||||
*
|
||||
* @param array $array
|
||||
* @return array
|
||||
*/
|
||||
public static function divide($array)
|
||||
{
|
||||
return [\array_keys($array), \array_values($array)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Flatten a multi-dimensional associative array with dots.
|
||||
*
|
||||
* @param array $array
|
||||
* @param string $prepend
|
||||
* @return array
|
||||
*/
|
||||
public static function dot($array, $prepend = '')
|
||||
{
|
||||
$results = [];
|
||||
foreach ($array as $key => $value) {
|
||||
if (\is_array($value) && ! empty($value)) {
|
||||
$results = \array_merge($results, static::dot($value, $prepend . $key . '.'));
|
||||
} else {
|
||||
$results[$prepend . $key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the given array except for a specified array of items.
|
||||
*
|
||||
* @param array $array
|
||||
* @param array|string $keys
|
||||
* @return array
|
||||
*/
|
||||
public static function except($array, $keys)
|
||||
{
|
||||
static::forget($array, $keys);
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given key exists in the provided array.
|
||||
*
|
||||
* @param array|ArrayAccess $array
|
||||
* @param string|int $key
|
||||
* @return bool
|
||||
*/
|
||||
public static function exists($array, $key)
|
||||
{
|
||||
if ($array instanceof ArrayAccess) {
|
||||
return $array->offsetExists($key);
|
||||
}
|
||||
|
||||
return \array_key_exists($key, $array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first element in an array passing a given truth test.
|
||||
*
|
||||
* @param array $array
|
||||
* @param callable|null $callback
|
||||
* @param mixed $default
|
||||
* @return mixed
|
||||
*/
|
||||
public static function first($array, $callback = null, $default = null)
|
||||
{
|
||||
if (\is_null($callback)) {
|
||||
if (empty($array)) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
foreach ($array as $item) {
|
||||
return $item;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($array as $key => $value) {
|
||||
if (\call_user_func($callback, $value, $key)) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last element in an array passing a given truth test.
|
||||
*
|
||||
* @param array $array
|
||||
* @param callable|null $callback
|
||||
* @param mixed $default
|
||||
* @return mixed
|
||||
*/
|
||||
public static function last($array, $callback = null, $default = null)
|
||||
{
|
||||
if (\is_null($callback)) {
|
||||
return empty($array) ? $default : \end($array);
|
||||
}
|
||||
|
||||
return static::first(\array_reverse($array, true), $callback, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove one or many array items from a given array using "dot" notation.
|
||||
*
|
||||
* @param array $array
|
||||
* @param array|string $keys
|
||||
* @return void
|
||||
*/
|
||||
public static function forget(&$array, $keys)
|
||||
{
|
||||
$original = &$array;
|
||||
$keys = (array) $keys;
|
||||
if (\count($keys) === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($keys as $key) {
|
||||
// if the exact key exists in the top-level, remove it
|
||||
if (static::exists($array, $key)) {
|
||||
unset($array[$key]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$parts = \explode('.', $key);
|
||||
// clean up before each pass
|
||||
$array = &$original;
|
||||
while (\count($parts) > 1) {
|
||||
$part = \array_shift($parts);
|
||||
if (isset($array[$part]) && \is_array($array[$part])) {
|
||||
$array = &$array[$part];
|
||||
} else {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
unset($array[\array_shift($parts)]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an item from an array using "dot" notation.
|
||||
*
|
||||
* @param array|ArrayAccess $array
|
||||
* @param int|string $key
|
||||
* @param mixed $default
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get($array, $key, $default = null)
|
||||
{
|
||||
if (! static::accessible($array)) {
|
||||
return self::return_default($default);
|
||||
}
|
||||
|
||||
if (static::exists($array, $key)) {
|
||||
return $array[$key];
|
||||
}
|
||||
|
||||
$key = (string) $key;
|
||||
if (false === strpos($key, '.')) {
|
||||
return $array[$key] ?? self::return_default($default);
|
||||
}
|
||||
|
||||
foreach (\explode('.', $key) as $segment) {
|
||||
if (static::accessible($array) && static::exists($array, $segment)) {
|
||||
$array = $array[$segment];
|
||||
} else {
|
||||
return self::return_default($default);
|
||||
}
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an item or items exist in an array using "dot" notation.
|
||||
*
|
||||
* @param array|ArrayAccess $array
|
||||
* @param string|array $keys
|
||||
* @return bool
|
||||
*/
|
||||
public static function has($array, $keys)
|
||||
{
|
||||
if (\is_null($keys)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$keys = (array) $keys;
|
||||
if (! $array) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($keys === []) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$subKeyArray = $array;
|
||||
if (static::exists($array, $key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (\explode('.', $key) as $segment) {
|
||||
if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) {
|
||||
$subKeyArray = $subKeyArray[$segment];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if an array is associative.
|
||||
*
|
||||
* An array is "associative" if it doesn't have sequential numerical keys beginning with zero.
|
||||
*
|
||||
* @param array $array
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_assoc(array $array)
|
||||
{
|
||||
$keys = \array_keys($array);
|
||||
return \array_keys($keys) !== $keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a subset of the items from the given array.
|
||||
*
|
||||
* @param array $array
|
||||
* @param array|string $keys
|
||||
* @return array
|
||||
*/
|
||||
public static function only($array, $keys)
|
||||
{
|
||||
return \array_intersect_key($array, \array_flip((array) $keys));
|
||||
}
|
||||
|
||||
/**
|
||||
* Pluck an array of values from an array.
|
||||
*
|
||||
* @param array $array
|
||||
* @param string|array $value
|
||||
* @param string|array|null $key
|
||||
* @return array
|
||||
*/
|
||||
public static function pluck($array, $value, $key = null)
|
||||
{
|
||||
$results = [];
|
||||
list($value, $key) = static::exploded_pluck_parameters($value, $key);
|
||||
foreach ($array as $item) {
|
||||
$itemValue = static::data_get($item, $value);
|
||||
// If the key is "null", we will just append the value to the array and keep
|
||||
// looping. Otherwise we will key the array using the value of the key we
|
||||
// received from the developer. Then we'll return the final array form.
|
||||
if (\is_null($key)) {
|
||||
$results[] = $itemValue;
|
||||
} else {
|
||||
$itemKey = static::data_get($item, $key);
|
||||
$results[$itemKey] = $itemValue;
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Explode the "value" and "key" arguments passed to "pluck".
|
||||
*
|
||||
* @param string|array $value
|
||||
* @param string|array|null $key
|
||||
* @return array
|
||||
*/
|
||||
protected static function exploded_pluck_parameters($value, $key)
|
||||
{
|
||||
$value = \is_string($value) ? \explode('.', $value) : $value;
|
||||
$key = \is_null($key) || \is_array($key) ? $key : \explode('.', $key);
|
||||
return [$value, $key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Push an item onto the beginning of an array.
|
||||
*
|
||||
* @param array $array
|
||||
* @param mixed $value
|
||||
* @param mixed $key
|
||||
* @return array
|
||||
*/
|
||||
public static function prepend($array, $value, $key = null)
|
||||
{
|
||||
if (\is_null($key)) {
|
||||
\array_unshift($array, $value);
|
||||
} else {
|
||||
$array = [$key => $value] + $array;
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a value from the array, and remove it.
|
||||
*
|
||||
* @param array $array
|
||||
* @param string $key
|
||||
* @param mixed $default
|
||||
* @return mixed
|
||||
*/
|
||||
public static function pull(&$array, $key, $default = null)
|
||||
{
|
||||
$value = static::get($array, $key, $default);
|
||||
static::forget($array, $key);
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an array item to a given value using "dot" notation.
|
||||
*
|
||||
* If no key is given to the method, the entire array will be replaced.
|
||||
*
|
||||
* @param array $array
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return array
|
||||
*/
|
||||
public static function set(&$array, $key, $value)
|
||||
{
|
||||
if (\is_null($key)) {
|
||||
return $array = $value;
|
||||
}
|
||||
|
||||
$keys = \explode('.', $key);
|
||||
while (\count($keys) > 1) {
|
||||
$key = \array_shift($keys);
|
||||
// If the key doesn't exist at this depth, we will just create an empty array
|
||||
// to hold the next value, allowing us to create the arrays to hold final
|
||||
// values at the correct depth. Then we'll keep digging into the array.
|
||||
if (! isset($array[$key]) || ! \is_array($array[$key])) {
|
||||
$array[$key] = [];
|
||||
}
|
||||
|
||||
$array = &$array[$key];
|
||||
}
|
||||
|
||||
$array[\array_shift($keys)] = $value;
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuffle the given array and return the result.
|
||||
*
|
||||
* @param array $array
|
||||
* @return array
|
||||
*/
|
||||
public static function shuffle($array)
|
||||
{
|
||||
\shuffle($array);
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively sort an array by keys and values.
|
||||
*
|
||||
* @param array $array
|
||||
* @return array
|
||||
*/
|
||||
public static function sort_recursive($array)
|
||||
{
|
||||
foreach ($array as &$value) {
|
||||
if (\is_array($value)) {
|
||||
$value = static::sort_recursive($value);
|
||||
}
|
||||
}
|
||||
|
||||
if (static::is_assoc($array)) {
|
||||
\ksort($array);
|
||||
} else {
|
||||
\sort($array);
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an item from an array or object using "dot" notation.
|
||||
*
|
||||
* @param mixed $target
|
||||
* @param string|array $key
|
||||
* @param mixed $default
|
||||
* @return mixed
|
||||
*/
|
||||
public static function data_get($target, $key, $default = null)
|
||||
{
|
||||
if (\is_null($key)) {
|
||||
return $target;
|
||||
}
|
||||
$key = \is_array($key) ? $key : \explode('.', $key);
|
||||
while (! \is_null($segment = \array_shift($key))) {
|
||||
if ($segment === '*') {
|
||||
if (! \is_array($target)) {
|
||||
return $default;
|
||||
}
|
||||
$result = static::pluck($target, $key);
|
||||
return \in_array('*', $key) ? static::collapse($result) : $result;
|
||||
}
|
||||
if (static::accessible($target) && static::exists($target, $segment)) {
|
||||
$target = $target[$segment];
|
||||
} elseif (\is_object($target) && isset($target->{$segment})) {
|
||||
$target = $target->{$segment};
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
return $target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the array using the given callback.
|
||||
*
|
||||
* @param array $array
|
||||
* @param callable $callback
|
||||
* @return array
|
||||
*/
|
||||
public static function where($array, callable $callback)
|
||||
{
|
||||
return array_filter($array, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default value. If the default value is a Closure, it executes the Closure and returns its result.
|
||||
*
|
||||
* @param mixed $default The default value or a Closure that returns the default value.
|
||||
* @return mixed The default value or the result of the Closure execution.
|
||||
*/
|
||||
private static function return_default($default)
|
||||
{
|
||||
if ($default instanceof Closure) {
|
||||
$default = $default();
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
29
src/Support/Contracts/Arrayable.php
Normal file
29
src/Support/Contracts/Arrayable.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*
|
||||
* This file contains a customized adaptation of the Laravel\Illuminate package.
|
||||
* Original author: Laravel
|
||||
* Original source: https://laravel.com/api/master/Illuminate/Foundation.html
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Support\Contracts;
|
||||
|
||||
/**
|
||||
* @template TKey of array-key
|
||||
* @template TValue
|
||||
*/
|
||||
interface Arrayable
|
||||
{
|
||||
/**
|
||||
* Get the instance as an array.
|
||||
*
|
||||
* @return array<TKey, TValue>
|
||||
*/
|
||||
public function to_array();
|
||||
}
|
||||
30
src/Support/Contracts/Jsonable.php
Normal file
30
src/Support/Contracts/Jsonable.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*
|
||||
* This file contains a customized adaptation of the Laravel\Illuminate package.
|
||||
* Original author: Laravel
|
||||
* Original source: https://laravel.com/api/master/Illuminate/Foundation.html
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Support\Contracts;
|
||||
|
||||
/**
|
||||
* @template TKey of array-key
|
||||
* @template TValue
|
||||
*/
|
||||
interface Jsonable
|
||||
{
|
||||
/**
|
||||
* Convert the object to its JSON representation.
|
||||
*
|
||||
* @param int $options
|
||||
* @return string
|
||||
*/
|
||||
public function to_json();
|
||||
}
|
||||
393
src/Support/Str.php
Normal file
393
src/Support/Str.php
Normal file
@@ -0,0 +1,393 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*
|
||||
* This file contains a customized adaptation of the Laravel\Illuminate\Foundation package.
|
||||
* Original author: Laravel
|
||||
* Original source: https://laravel.com/api/master/Illuminate/Foundation.html
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Support;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class Str
|
||||
{
|
||||
/**
|
||||
* Check if a given substring is present within a subject string.
|
||||
*
|
||||
* @param string $string The string to search within.
|
||||
* @param string $substring The substring to search for.
|
||||
* @return bool True if the substring is found within the subject, false otherwise.
|
||||
*/
|
||||
public static function contains(string $string, string $substring): bool
|
||||
{
|
||||
if ('' === $substring) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ('' === $string) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false !== mb_strpos($string, $substring);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the portion of a string before the first occurrence of a given value.
|
||||
*
|
||||
* @param string $string
|
||||
* @param string $substring
|
||||
* @return string
|
||||
*/
|
||||
public static function before(string $string, string $substring): bool|string
|
||||
{
|
||||
if ($substring === '') {
|
||||
return $string;
|
||||
}
|
||||
|
||||
$result = strstr($string, (string) $substring, true);
|
||||
return $result === false ? $string : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given string contains any of the specified substrings.
|
||||
*
|
||||
* @param string $string The string to search within.
|
||||
* @param array $substrings An array of substrings to search for.
|
||||
* @return bool True if any of the substrings are found in the subject string, false otherwise.
|
||||
*/
|
||||
public static function contains_any(string $string, array $substrings): bool
|
||||
{
|
||||
foreach ($substrings as $substring) {
|
||||
if (self::contains($string, $substring)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin a string with a single instance of a given value.
|
||||
*
|
||||
* @param string $value
|
||||
* @param string $prefix
|
||||
* @return string
|
||||
*/
|
||||
public static function start($value, $prefix)
|
||||
{
|
||||
$quoted = preg_quote($prefix, '/');
|
||||
|
||||
return $prefix . preg_replace('/^(?:' . $quoted . ')+/u', '', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a given string starts with a given substring.
|
||||
*
|
||||
* @param string $string
|
||||
* @param string|iterable<string> $substrings
|
||||
* @return bool
|
||||
*/
|
||||
public static function starts_with(string $string, string $substrings): bool
|
||||
{
|
||||
if (! is_iterable($substrings)) {
|
||||
$substrings = [$substrings];
|
||||
}
|
||||
|
||||
foreach ($substrings as $substring) {
|
||||
if ((string) $substring !== '' && str_starts_with($string, $substring)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a given string ends with a given substring.
|
||||
*
|
||||
* @param string $string The string to search in.
|
||||
* @param string $substring The substring to search for.
|
||||
* @return bool True if the subject ends with the substring, false otherwise.
|
||||
*/
|
||||
public static function ends_with(string $string, string $substring): bool
|
||||
{
|
||||
if ('' === $substring) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return substr($string, -strlen($substring)) === $substring;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a given string does not end with a given substring.
|
||||
*
|
||||
* @param string $string The string to check.
|
||||
* @param string $substring The substring to search for at the end of the subject string.
|
||||
* @return bool True if the subject string does not end with the given substring, false otherwise.
|
||||
*/
|
||||
public static function does_not_end_with(string $string, string $substring): bool
|
||||
{
|
||||
return ! self::ends_with($string, $substring);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the remainder of a string after the first occurrence of a given value.
|
||||
*
|
||||
* @param string $subject
|
||||
* @param string $search
|
||||
* @return string
|
||||
*/
|
||||
public static function after(string $subject, string $search): string
|
||||
{
|
||||
return $search === '' ? $subject : array_reverse(explode($search, $subject, 2))[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the portion of the string after the first occurrence of the given search string.
|
||||
*
|
||||
* @param string $string The input string.
|
||||
* @param string $substring The string to search for.
|
||||
* @return string The portion of the string after the first occurrence of the search string.
|
||||
*/
|
||||
public static function after_first(string $string, string $substring): string
|
||||
{
|
||||
return '' === $substring ? $string : array_reverse(explode($substring, $string, 2))[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the portion of the string after the last occurrence of a given substring.
|
||||
*
|
||||
* @param string $string The string to search in.
|
||||
* @param string $substring The substring to search for.
|
||||
* @return string The portion of the string after the last occurrence of the substring.
|
||||
* @throws RuntimeException If the substr function returns false.
|
||||
*/
|
||||
public static function after_last(string $string, string $substring): string
|
||||
{
|
||||
if ('' === $substring) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
$position = strrpos($string, $substring);
|
||||
if (false === $position) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
$res = substr($string, $position + strlen($substring));
|
||||
if (false === $res) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new RuntimeException(sprintf('substr returned false for subject [%s].', $string));
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the substring between the first occurrence of the given "from" and "to" strings.
|
||||
*
|
||||
* Usage: Str::between_first('xayyy', 'x', 'y') => 'a'.
|
||||
*
|
||||
* @param string $string The string to extract the substring from.
|
||||
* @param string $from The starting delimiter string.
|
||||
* @param string $to The ending delimiter string.
|
||||
* @return string The extracted substring. If either "from" or "to" is an empty string, the original subject is returned.
|
||||
*/
|
||||
public static function between_first(string $string, string $from, string $to): string
|
||||
{
|
||||
if ('' === $from) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
if ('' === $to) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
return self::before_first(self::after_first($string, $from), $to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the substring between the last occurrence of the given "from" and "to" strings.
|
||||
*
|
||||
* Usage: Str::between_last('xayyy', 'x', 'y') => 'a'.
|
||||
*
|
||||
* @param string $string The input string from which to extract the substring.
|
||||
* @param string $from The starting delimiter string.
|
||||
* @param string $to The ending delimiter string.
|
||||
* @return string The extracted substring, or the entire `$string` string if
|
||||
* either `$from` or `$to` is empty.
|
||||
*/
|
||||
public static function between_last(string $string, string $from, string $to): string
|
||||
{
|
||||
if ('' === $from) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
if ('' === $to) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
return self::before_last(self::after_first($string, $from), $to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the portion of the string before the first occurrence of the given search string.
|
||||
*
|
||||
* @param string $string The input string to search within.
|
||||
* @param string $substring The string to search for.
|
||||
* @return string The portion of the input string before the first occurrence of the search string,
|
||||
* or the entire input string if the search string is not found or is empty.
|
||||
*/
|
||||
public static function before_first(string $string, string $substring): string
|
||||
{
|
||||
if ('' === $substring) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
$result = strstr($string, $substring, true);
|
||||
return false === $result ? $string : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the portion of the string before the last occurrence of the given substring.
|
||||
*
|
||||
* @param string $string The input string.
|
||||
* @param string $substring The substring to search for.
|
||||
* @return string The portion of the string before the last occurrence of the substring.
|
||||
*/
|
||||
public static function before_last(string $string, string $substring): string
|
||||
{
|
||||
if ('' === $substring) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
$pos = mb_strrpos($string, $substring);
|
||||
if (false === $pos) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
return self::substr($string, 0, $pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the portion of the string specified by the start and length parameters.
|
||||
*
|
||||
* @param string $string The input string.
|
||||
* @param int $start The starting position.
|
||||
* @param int|null $length The length of the substring. If omitted, the substring will extend to the end of the string.
|
||||
* @return string The extracted substring.
|
||||
*/
|
||||
public static function substr(string $string, int $start, int $length = null): string
|
||||
{
|
||||
return mb_substr($string, $start, $length, 'UTF-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* Cap a string with a single instance of a given value.
|
||||
*
|
||||
* @param string $value
|
||||
* @param string $cap
|
||||
* @return string
|
||||
*/
|
||||
public static function finish(string $value, string $cap): string
|
||||
{
|
||||
$quoted = preg_quote($cap, '/');
|
||||
return preg_replace('/(?:' . $quoted . ')+$/u', '', $value) . $cap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new stringable object from the given string.
|
||||
*
|
||||
* @param string $string
|
||||
* @return \Webshr\Core\Support\Stringable
|
||||
*/
|
||||
public static function of(string $string): Stringable
|
||||
{
|
||||
return new Stringable($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a given string matches a given pattern.
|
||||
*
|
||||
* This method checks if the given subject string matches the specified pattern.
|
||||
* If the pattern is an exact match to the subject, it returns true immediately.
|
||||
* Otherwise, it translates asterisks in the pattern into regular expression
|
||||
* wildcards and performs a pattern match against the subject string.
|
||||
*
|
||||
* @param string $string The string to be checked.
|
||||
* @param string $pattern The pattern to match against the subject.
|
||||
* @return bool True if the subject matches the pattern, false otherwise.
|
||||
*/
|
||||
public static function is(string $string, string $pattern): bool
|
||||
{
|
||||
// If the given value is an exact match we can of course return true right
|
||||
// from the beginning. Otherwise, we will translate asterisks and do an
|
||||
// actual pattern match against the two strings to see if they match.
|
||||
if ($pattern === $string) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$pattern = preg_quote($pattern, '#');
|
||||
// Asterisks are translated into zero-or-more regular expression wildcards
|
||||
// to make it convenient to check if the strings starts with the given
|
||||
// pattern such as "library/*", making any string check convenient.
|
||||
$pattern = str_replace('\*', '.*', $pattern);
|
||||
return 1 === preg_match('#^' . $pattern . '\z#u', $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace all occurrences of a substring within a string with another string.
|
||||
*
|
||||
* @param string $string The string to search and replace within.
|
||||
* @param string $substring The substring to search for.
|
||||
* @param string $replace The string to replace the substring with.
|
||||
* @return string The resulting string after the replacements.
|
||||
*/
|
||||
public static function replace_all(string $string, string $substring, string $replace): string
|
||||
{
|
||||
return str_replace($substring, $replace, $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all whitespace from both ends of a string.
|
||||
*
|
||||
* @param string $value
|
||||
* @param string|null $charlist
|
||||
* @return string
|
||||
*/
|
||||
public static function trim($value, $charlist = null)
|
||||
{
|
||||
if ($charlist === null) {
|
||||
$trim_default_characters = " \n\r\t\v\0";
|
||||
|
||||
return preg_replace('~^[\s\x{FEFF}\x{200B}\x{200E}' . $trim_default_characters . ']+|[\s\x{FEFF}\x{200B}\x{200E}' . $trim_default_characters . ']+$~u', '', $value) ?? trim($value);
|
||||
}
|
||||
|
||||
return trim($value, $charlist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all whitespace from the beginning of a string.
|
||||
*
|
||||
* @param string $value
|
||||
* @param string|null $charlist
|
||||
* @return string
|
||||
*/
|
||||
public static function ltrim($value, $charlist = null)
|
||||
{
|
||||
if ($charlist === null) {
|
||||
$trim_default_characters = " \n\r\t\v\0";
|
||||
|
||||
return preg_replace('~^[\s\x{FEFF}\x{200B}\x{200E}' . $trim_default_characters . ']+~u', '', $value) ?? ltrim($value);
|
||||
}
|
||||
|
||||
return ltrim($value, $charlist);
|
||||
}
|
||||
}
|
||||
110
src/Support/Stringable.php
Normal file
110
src/Support/Stringable.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*
|
||||
* This file contains a customized adaptation of the Laravel\Illuminate\Foundation package.
|
||||
* Original author: Laravel
|
||||
* Original source: https://laravel.com/api/master/Illuminate/Foundation.html
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Support;
|
||||
|
||||
class Stringable
|
||||
{
|
||||
/**
|
||||
* The underlying string value.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $value;
|
||||
/**
|
||||
* Create a new instance of the class.
|
||||
*
|
||||
* @param string $value
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($value = '')
|
||||
{
|
||||
$this->value = (string) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the remainder of a string after the first occurrence of a given value.
|
||||
*
|
||||
* @param string $search
|
||||
* @return static
|
||||
*/
|
||||
public function after($search)
|
||||
{
|
||||
return new static(Str::after($this->value, $search));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the remainder of a string after the last occurrence of a given value.
|
||||
*
|
||||
* @param string $search
|
||||
* @return static
|
||||
*/
|
||||
public function after_last($search)
|
||||
{
|
||||
return new static(Str::after_last($this->value, $search));
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim the string of the given characters.
|
||||
*
|
||||
* @param string $characters
|
||||
* @return static
|
||||
*/
|
||||
public function trim($characters = null)
|
||||
{
|
||||
return new static(Str::trim(...array_merge([$this->value], func_get_args())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Left trim the string of the given characters.
|
||||
*
|
||||
* @param string $characters
|
||||
* @return static
|
||||
*/
|
||||
public function ltrim($characters = null)
|
||||
{
|
||||
return new static(Str::ltrim(...array_merge([$this->value], func_get_args())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin a string with a single instance of a given value.
|
||||
*
|
||||
* @param string $prefix
|
||||
* @return static
|
||||
*/
|
||||
public function start($prefix)
|
||||
{
|
||||
return new static(Str::start($this->value, $prefix));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string value.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function to_string()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string value.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
123
src/Support/Traits/Aliases.php
Normal file
123
src/Support/Traits/Aliases.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
namespace Webshr\Core\Support\Traits;
|
||||
|
||||
use Closure;
|
||||
use BadMethodCallException;
|
||||
use Webshr\Core\Support\Arriable;
|
||||
|
||||
/**
|
||||
* Add methods to classes at runtime.
|
||||
* Loosely based on spatie/macroable.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
trait Aliases
|
||||
{
|
||||
/**
|
||||
* Registered aliases.
|
||||
*
|
||||
* @var array<string, array>
|
||||
*/
|
||||
protected $aliases = [];
|
||||
|
||||
/**
|
||||
* Get whether an alias is registered.
|
||||
*
|
||||
* @param string $alias
|
||||
* @return boolean
|
||||
*/
|
||||
public function has_alias($alias)
|
||||
{
|
||||
return isset($this->aliases[$alias]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a registered alias.
|
||||
*
|
||||
* @param string $alias
|
||||
* @return array|null
|
||||
*/
|
||||
public function get_alias($alias)
|
||||
{
|
||||
if (! $this->has_alias($alias)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->aliases[$alias];
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an alias.
|
||||
* Useful when passed the return value of getAlias() to restore
|
||||
* a previously registered alias, for example.
|
||||
*
|
||||
* @param array<string, mixed> $alias
|
||||
* @return void
|
||||
*/
|
||||
public function set_alias($alias)
|
||||
{
|
||||
$name = Arriable::get($alias, 'name');
|
||||
|
||||
$this->aliases[$name] = [
|
||||
'name' => $name,
|
||||
'target' => Arriable::get($alias, 'target'),
|
||||
'method' => Arriable::get($alias, 'method', ''),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an alias.
|
||||
* Identical to setAlias but with a more user-friendly interface.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @param string $alias
|
||||
* @param string|Closure $target
|
||||
* @param string $method
|
||||
* @return void
|
||||
*/
|
||||
public function alias($alias, $target, $method = '')
|
||||
{
|
||||
$this->set_alias([
|
||||
'name' => $alias,
|
||||
'target' => $target,
|
||||
'method' => $method,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call alias if registered.
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $parameters
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($method, $parameters)
|
||||
{
|
||||
if (! $this->has_alias($method)) {
|
||||
throw new BadMethodCallException("Method {$method} does not exist.");
|
||||
}
|
||||
|
||||
$alias = $this->aliases[$method];
|
||||
|
||||
if ($alias['target'] instanceof Closure) {
|
||||
return call_user_func_array($alias['target']->bindTo($this, static::class), $parameters);
|
||||
}
|
||||
|
||||
$target = $this->resolve($alias['target']);
|
||||
|
||||
if (! empty($alias['method'])) {
|
||||
return call_user_func_array([$target, $alias['method']], $parameters);
|
||||
}
|
||||
|
||||
return $target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a dependency from the IoC container.
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed|null
|
||||
*/
|
||||
abstract public function resolve($key);
|
||||
}
|
||||
7
src/Support/Traits/Application.php
Normal file
7
src/Support/Traits/Application.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Webshr\Core\Support\Traits;
|
||||
|
||||
trait Application
|
||||
{
|
||||
}
|
||||
127
src/Support/Traits/Macroable.php
Normal file
127
src/Support/Traits/Macroable.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*
|
||||
* This file contains a customized adaptation of the Laravel\Illuminate package.
|
||||
* Original author: Laravel
|
||||
* Original source: https://laravel.com/api/master/Illuminate/Foundation.html
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Support\Traits;
|
||||
|
||||
use BadMethodCallException;
|
||||
use Closure;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
|
||||
trait Macroable
|
||||
{
|
||||
/**
|
||||
* The registered string macros.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $macros = [];
|
||||
/**
|
||||
* Register a custom macro.
|
||||
*
|
||||
* @param string $name
|
||||
* @param object|callable $macro
|
||||
* @return void
|
||||
*/
|
||||
public static function macro($name, $macro)
|
||||
{
|
||||
static::$macros[$name] = $macro;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mix another object into the class.
|
||||
*
|
||||
* @param object $mixin
|
||||
* @param bool $replace
|
||||
* @return void
|
||||
*
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public static function mixin($mixin, $replace = true)
|
||||
{
|
||||
$methods = (new ReflectionClass($mixin))->getMethods(ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED);
|
||||
foreach ($methods as $method) {
|
||||
if ($replace || ! static::has_macro($method->name)) {
|
||||
static::macro($method->name, $method->invoke($mixin));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if macro is registered.
|
||||
*
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public static function has_macro($name)
|
||||
{
|
||||
return isset(static::$macros[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the existing macros.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function flush_macros()
|
||||
{
|
||||
static::$macros = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically handle calls to the class.
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $parameters
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public static function __callStatic($method, $parameters)
|
||||
{
|
||||
if (! static::has_macro($method)) {
|
||||
throw new BadMethodCallException(sprintf('Method %s::%s does not exist.', static::class, $method,));
|
||||
}
|
||||
|
||||
$macro = static::$macros[$method];
|
||||
if ($macro instanceof Closure) {
|
||||
$macro = $macro->bindTo(null, static::class);
|
||||
}
|
||||
|
||||
return $macro(...$parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically handle calls to the class.
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $parameters
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public function __call($method, $parameters)
|
||||
{
|
||||
if (! static::has_macro($method)) {
|
||||
throw new BadMethodCallException(sprintf('Method %s::%s does not exist.', static::class, $method,));
|
||||
}
|
||||
|
||||
$macro = static::$macros[$method];
|
||||
if ($macro instanceof Closure) {
|
||||
$macro = $macro->bindTo($this, static::class);
|
||||
}
|
||||
|
||||
return $macro(...$parameters);
|
||||
}
|
||||
}
|
||||
55
src/Utility/Contracts/Encrypter.php
Normal file
55
src/Utility/Contracts/Encrypter.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Utility\Contracts;
|
||||
|
||||
use Webshr\Core\Utility\Exceptions\Decrypt_Exception;
|
||||
use Webshr\Core\Utility\Exceptions\Encrypt_Exception;
|
||||
|
||||
interface Encrypter
|
||||
{
|
||||
/**
|
||||
* Encrypts the given data.
|
||||
*
|
||||
* @param string $value The data to be encrypted.
|
||||
* @param bool $serialize Whether to serialize the data.
|
||||
* @return string The encrypted data.
|
||||
* @throws Encrypt_Exception If encryption fails.
|
||||
*/
|
||||
public function encrypt(string $value, bool $serialize = true): string;
|
||||
/**
|
||||
* Decrypts the given data.
|
||||
*
|
||||
* @param string $payload The data to be decrypted.
|
||||
* @param bool $unserialize Whether to unserialize the data.
|
||||
* @return mixed The decrypted data.
|
||||
* @throws Decrypt_Exception If encryption fails.
|
||||
*/
|
||||
public function decrypt(string $payload, bool $unserialize = true): mixed;
|
||||
/**
|
||||
* Get the encryption key that the encrypter is currently using.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_key(): string;
|
||||
/**
|
||||
* Get the current encryption key and all previous encryption keys.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_all_keys(): array;
|
||||
/**
|
||||
* Get the previous encryption keys.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_previous_keys(): array;
|
||||
}
|
||||
31
src/Utility/Contracts/Hasher.php
Normal file
31
src/Utility/Contracts/Hasher.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Utility\Contracts;
|
||||
|
||||
interface Hasher
|
||||
{
|
||||
/**
|
||||
* Create a hash for the given password.
|
||||
*
|
||||
* @param string $password The password to be hashed.
|
||||
* @return string The hashed password.
|
||||
*/
|
||||
public function make($password);
|
||||
/**
|
||||
* Verifies that the given password matches the provided hash.
|
||||
*
|
||||
* @param string $password The plain text password to check.
|
||||
* @param string $hash The hashed password to compare against.
|
||||
* @return bool True if the password matches the hash, false otherwise.
|
||||
*/
|
||||
public function check($password, $hash);
|
||||
}
|
||||
37
src/Utility/Contracts/String_Encrypter.php
Normal file
37
src/Utility/Contracts/String_Encrypter.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Utility\Contracts;
|
||||
|
||||
use Webshr\Core\Utility\Exceptions\Decrypt_Exception;
|
||||
use Webshr\Core\Utility\Exceptions\Encrypt_Exception;
|
||||
|
||||
interface String_Encrypter
|
||||
{
|
||||
/**
|
||||
* Encrypt a string without serialization.
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*
|
||||
* @throws Encrypt_Exception
|
||||
*/
|
||||
public function encrypt_string(string $value);
|
||||
/**
|
||||
* Decrypt the given string without unserialization.
|
||||
*
|
||||
* @param string $payload
|
||||
* @return string
|
||||
*
|
||||
* @throws Decrypt_Exception
|
||||
*/
|
||||
public function decrypt_string(string $payload);
|
||||
}
|
||||
352
src/Utility/Encryption.php
Normal file
352
src/Utility/Encryption.php
Normal file
@@ -0,0 +1,352 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Utility;
|
||||
|
||||
use RuntimeException;
|
||||
use Webshr\Core\Utility\Exceptions\Encrypt_Exception;
|
||||
use Webshr\Core\Utility\Exceptions\Decrypt_Exception;
|
||||
use Webshr\Core\Contracts\Application as Application_Interface;
|
||||
use Webshr\Core\Utility\Contracts\Encrypter as Encrypter_Interface;
|
||||
use Webshr\Core\Utility\Contracts\String_Encrypter as String_Encrypter_Interface;
|
||||
|
||||
class Encryption implements Encrypter_Interface, String_Encrypter_Interface
|
||||
{
|
||||
/**
|
||||
* The encryption key.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected string $key;
|
||||
/**
|
||||
* The previous / legacy encryption keys.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $previous_keys = [];
|
||||
/**
|
||||
* The encryption cipher.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected string $cipher;
|
||||
/**
|
||||
* The supported cipher algorithms and their properties.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $supported_ciphers = [
|
||||
'aes-128-cbc' => ['size' => 16, 'aead' => false],
|
||||
'aes-256-cbc' => ['size' => 32, 'aead' => false],
|
||||
'aes-128-gcm' => ['size' => 16, 'aead' => true],
|
||||
'aes-256-gcm' => ['size' => 32, 'aead' => true],
|
||||
];
|
||||
/**
|
||||
* Initialize the Encryption instance.
|
||||
*
|
||||
* @param Application_Interface $app
|
||||
*/
|
||||
public function __construct(Application_Interface $app)
|
||||
{
|
||||
$key = $app->get('key');
|
||||
$cipher = $app->get('cipher') ?? 'aes-256-cbc';
|
||||
// Ensure the key length matches the expected length for the cipher
|
||||
$key = $this->ensure_key_length($key, $cipher);
|
||||
if (! static::supported($key, $cipher)) {
|
||||
$ciphers = implode(', ', array_keys(self::$supported_ciphers));
|
||||
throw new RuntimeException("Unsupported cipher or incorrect key length. Supported ciphers are: {$ciphers}.");
|
||||
}
|
||||
|
||||
$this->key = $key;
|
||||
$this->cipher = $cipher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the key length matches the expected length for the cipher.
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $cipher
|
||||
* @return string
|
||||
*/
|
||||
private function ensure_key_length(string $key, string $cipher): string
|
||||
{
|
||||
$expected_length = self::$supported_ciphers[strtolower($cipher)]['size'];
|
||||
if (mb_strlen($key, '8bit') !== $expected_length) {
|
||||
// Truncate or hash the key to the required length
|
||||
$key = substr(hash('sha256', $key), 0, $expected_length);
|
||||
}
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given key and cipher combination is valid.
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $cipher
|
||||
* @return bool
|
||||
*/
|
||||
public static function supported($key, $cipher)
|
||||
{
|
||||
if (! isset(self::$supported_ciphers[strtolower($cipher)])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return mb_strlen($key, '8bit') === self::$supported_ciphers[strtolower($cipher)]['size'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new encryption key for the given cipher.
|
||||
*
|
||||
* @param string $cipher
|
||||
* @return string
|
||||
*/
|
||||
public static function generate_key($cipher)
|
||||
{
|
||||
return random_bytes(self::$supported_ciphers[strtolower($cipher)]['size'] ?? 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function encrypt(string $value, $serialize = true): string
|
||||
{
|
||||
if (! extension_loaded('openssl')) {
|
||||
throw new Encrypt_Exception('The OpenSSL extension is not loaded.');
|
||||
}
|
||||
|
||||
$iv = random_bytes(openssl_cipher_iv_length(strtolower($this->cipher)));
|
||||
$value = \openssl_encrypt($serialize ? serialize($value) : $value, strtolower($this->cipher), $this->key, 0, $iv, $tag,);
|
||||
if ($value === false) {
|
||||
throw new Encrypt_Exception('Could not encrypt the data.');
|
||||
}
|
||||
|
||||
$iv = base64_encode($iv);
|
||||
$tag = base64_encode($tag ?? '');
|
||||
$mac = self::$supported_ciphers[strtolower($this->cipher)]['aead']
|
||||
? '' // For AEAD-algorithms, the tag / MAC is returned by openssl_encrypt...
|
||||
: $this->hash($iv, $value, $this->key);
|
||||
$json = json_encode(compact('iv', 'value', 'mac', 'tag'), JSON_UNESCAPED_SLASHES);
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
throw new RuntimeException('Could not encrypt the data.');
|
||||
}
|
||||
|
||||
return base64_encode($json);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function encrypt_string(string $value): string
|
||||
{
|
||||
return $this->encrypt($value, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function decrypt(string $payload, bool $unserialize = true): string
|
||||
{
|
||||
$payload = $this->get_json_payload($payload);
|
||||
$iv = base64_decode($payload['iv']);
|
||||
$this->ensure_tag_is_valid($tag = empty($payload['tag']) ? null : base64_decode($payload['tag']));
|
||||
$found_valid_mac = false;
|
||||
// Here we will decrypt the value. If we are able to successfully decrypt it
|
||||
// we will then unserialize it and return it out to the caller. If we are
|
||||
// unable to decrypt this value we will throw out an exception message.
|
||||
foreach ($this->get_all_keys() as $key) {
|
||||
if (
|
||||
$this->should_validate_mac() &&
|
||||
! ($found_valid_mac = $found_valid_mac || $this->valid_mac_for_key($payload, $key))
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$decrypted = \openssl_decrypt($payload['value'], strtolower($this->cipher), $key, 0, $iv, $tag ?? '');
|
||||
if ($decrypted !== false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->should_validate_mac() && ! $found_valid_mac) {
|
||||
throw new Decrypt_Exception('The MAC is invalid.');
|
||||
}
|
||||
|
||||
if (($decrypted ?? false) === false) {
|
||||
throw new Decrypt_Exception('Could not decrypt the data.');
|
||||
}
|
||||
|
||||
return $unserialize ? unserialize($decrypted) : $decrypted;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function decrypt_string(string $payload): string
|
||||
{
|
||||
return $this->decrypt($payload, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a MAC for the given value.
|
||||
*
|
||||
* @param string $iv
|
||||
* @param mixed $value
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
protected function hash(string $iv, mixed $value, string $key)
|
||||
{
|
||||
return hash_hmac('sha256', $iv . $value, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JSON array from the given payload.
|
||||
*
|
||||
* @param string $payload
|
||||
* @return array
|
||||
*
|
||||
* @throws Decrypt_Exception
|
||||
*/
|
||||
protected function get_json_payload(string $payload): array
|
||||
{
|
||||
if (! is_string($payload)) {
|
||||
throw new Decrypt_Exception('The payload is invalid.');
|
||||
}
|
||||
|
||||
$payload = json_decode(base64_decode($payload), true);
|
||||
// If the payload is not valid JSON or does not have the proper keys set we will
|
||||
// assume it is invalid and bail out of the routine since we will not be able
|
||||
// to decrypt the given value. We'll also check the MAC for this encryption.
|
||||
if (! $this->valid_payload($payload)) {
|
||||
throw new Decrypt_Exception('The payload is invalid.');
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the encryption payload is valid.
|
||||
*
|
||||
* @param mixed $payload
|
||||
* @return bool
|
||||
*/
|
||||
protected function valid_payload(mixed $payload): bool
|
||||
{
|
||||
if (! is_array($payload)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (['iv', 'value', 'mac'] as $item) {
|
||||
if (! isset($payload[$item]) || ! is_string($payload[$item])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($payload['tag']) && ! is_string($payload['tag'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return strlen(base64_decode($payload['iv'], true)) === openssl_cipher_iv_length(strtolower($this->cipher));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the MAC for the given payload is valid for the primary key.
|
||||
*
|
||||
* @param array $payload
|
||||
* @return bool
|
||||
*/
|
||||
protected function valid_mac(array $payload): bool
|
||||
{
|
||||
return $this->valid_mac_for_key($payload, $this->key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the MAC is valid for the given payload and key.
|
||||
*
|
||||
* @param array $payload
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
protected function valid_mac_for_key(array $payload, string $key): bool
|
||||
{
|
||||
return hash_equals($this->hash($payload['iv'], $payload['value'], $key), $payload['mac'],);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the given tag is a valid tag given the selected cipher.
|
||||
*
|
||||
* @param string $tag
|
||||
* @return void
|
||||
*/
|
||||
protected function ensure_tag_is_valid($tag)
|
||||
{
|
||||
if (self::$supported_ciphers[strtolower($this->cipher)]['aead'] && strlen($tag) !== 16) {
|
||||
throw new Decrypt_Exception('Could not decrypt the data.');
|
||||
}
|
||||
|
||||
if (! self::$supported_ciphers[strtolower($this->cipher)]['aead'] && is_string($tag)) {
|
||||
throw new Decrypt_Exception('Unable to use tag because the cipher algorithm does not support AEAD.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we should validate the MAC while decrypting.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function should_validate_mac()
|
||||
{
|
||||
return ! self::$supported_ciphers[strtolower($this->cipher)]['aead'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function get_key(): string
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function get_all_keys(): array
|
||||
{
|
||||
return [$this->key, ...$this->previous_keys];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function get_previous_keys(): array
|
||||
{
|
||||
return $this->previous_keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the previous / legacy encryption keys that should be utilized if decryption fails.
|
||||
*
|
||||
* @param array $keys
|
||||
* @return $this
|
||||
*/
|
||||
public function previous_keys(array $keys)
|
||||
{
|
||||
foreach ($keys as $key) {
|
||||
if (! static::supported($key, $this->cipher)) {
|
||||
$ciphers = implode(', ', array_keys(self::$supported_ciphers));
|
||||
throw new RuntimeException("Unsupported cipher or incorrect key length. Supported ciphers are: {$ciphers}.");
|
||||
}
|
||||
}
|
||||
|
||||
$this->previous_keys = $keys;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
10
src/Utility/Exceptions/Decrypt_Exception.php
Normal file
10
src/Utility/Exceptions/Decrypt_Exception.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Webshr\Core\Utility\Exceptions;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class Decrypt_Exception extends RuntimeException
|
||||
{
|
||||
//
|
||||
}
|
||||
10
src/Utility/Exceptions/Encrypt_Exception.php
Normal file
10
src/Utility/Exceptions/Encrypt_Exception.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Webshr\Core\Utility\Exceptions;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class Encrypt_Exception extends RuntimeException
|
||||
{
|
||||
//
|
||||
}
|
||||
33
src/Utility/Hash.php
Normal file
33
src/Utility/Hash.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Webshr\Core
|
||||
* @since 1.0.0
|
||||
* @author Webshore, H. Liebel
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://webshore.eu/
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Webshr\Core\Utility;
|
||||
|
||||
use Webshr\Core\Utility\Contracts\Hasher as Haser_Interface;
|
||||
|
||||
class Hash implements Haser_Interface
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function make($password)
|
||||
{
|
||||
return password_hash($password, PASSWORD_BCRYPT);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function check($password, $hash)
|
||||
{
|
||||
return password_verify($password, $hash);
|
||||
}
|
||||
}
|
||||
156
src/globals.php
Normal file
156
src/globals.php
Normal file
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
use Webshr\Core\Application;
|
||||
use Webshr\Core\Assets\Bundle;
|
||||
use Webshr\Core\Assets\Asset\Asset;
|
||||
|
||||
if (! function_exists('app')) {
|
||||
/**
|
||||
* Get the available container instance.
|
||||
*
|
||||
* @param string|null $abstract
|
||||
* @param array $parameters
|
||||
* @return \Webshr\Core\Contracts\Application|\Webshr\Core\Application|mixed
|
||||
*/
|
||||
function app($abstract = null, array $parameters = [])
|
||||
{
|
||||
if (is_null($abstract)) {
|
||||
return Application::get_instance();
|
||||
}
|
||||
|
||||
return Application::get_instance()->make($abstract, $parameters);
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('lang_path')) {
|
||||
/**
|
||||
* Get the path to the language folder.
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
function lang_path(string $path = '')
|
||||
{
|
||||
return app()->lang_path($path);
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('app_public_path')) {
|
||||
/**
|
||||
* Get the path to the public folder.
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
function app_public_path(string $path = '')
|
||||
{
|
||||
return app()->public_path($path);
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('app_asset_path')) {
|
||||
/**
|
||||
* Generate an asset path for the application.
|
||||
*
|
||||
* @param string $path
|
||||
* @param bool|null $secure
|
||||
* @return string
|
||||
*/
|
||||
function app_asset_path(string $path, bool|null $secure = null)
|
||||
{
|
||||
return app('url')->asset($path, $secure);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('asset')) {
|
||||
/**
|
||||
* Get an asset instance and return the URL or other methods.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string|null $manifest
|
||||
* @return Asset
|
||||
*/
|
||||
function asset(string $path, ?string $manifest = null): Asset
|
||||
{
|
||||
// Use the core `asset()` from the helpers file
|
||||
return \Webshr\Core\asset($path, $manifest);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('bundle')) {
|
||||
/**
|
||||
* Get a bundle instance and return the methods.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string|null $manifest
|
||||
* @return Bundle
|
||||
*/
|
||||
function bundle(string $path, ?string $manifest = null): Bundle
|
||||
{
|
||||
// Use the core `bundle()` from the helpers file
|
||||
return \Webshr\Core\bundle($path, $manifest);
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('module')) {
|
||||
/**
|
||||
* Return the given module
|
||||
*
|
||||
* @param string $path
|
||||
* @param bool|null $secure
|
||||
* @return string
|
||||
*/
|
||||
function module($name)
|
||||
{
|
||||
return app('modules')->module($name);
|
||||
}
|
||||
}
|
||||
|
||||
/** Deprecated **/
|
||||
if (! function_exists('asset_content')) {
|
||||
/**
|
||||
* Get the asset file content.
|
||||
*
|
||||
* @param string $path
|
||||
* @example '/icons/..svg'
|
||||
* @return void
|
||||
*/
|
||||
|
||||
function asset_content(string $path): void
|
||||
{
|
||||
_doing_it_wrong('asset_content', 'This method has been deprecated in favor of asset()->contents', '0.2.0');
|
||||
echo asset($path)->contents();
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('asset_path')) {
|
||||
/**
|
||||
* Get the asset file path.
|
||||
*
|
||||
* @param string $path
|
||||
* @example '/images/..img'
|
||||
* @return string
|
||||
*/
|
||||
|
||||
function asset_path(string $path): string
|
||||
{
|
||||
_doing_it_wrong('asset_path', 'This method has been deprecated in favor of asset()->path', '0.2.0');
|
||||
return asset($path)->path();
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('asset_uri')) {
|
||||
/**
|
||||
* Get the asset file uri.
|
||||
*
|
||||
* @param string $path
|
||||
* @example '/images/..img'
|
||||
* @return string
|
||||
*/
|
||||
|
||||
function asset_uri(string $path): string
|
||||
{
|
||||
_doing_it_wrong('asset_uri', 'This method has been deprecated in favor of asset()->uri', '0.2.0');
|
||||
return asset($path)->uri();
|
||||
}
|
||||
}
|
||||
310
src/helpers.php
Normal file
310
src/helpers.php
Normal file
@@ -0,0 +1,310 @@
|
||||
<?php
|
||||
|
||||
namespace Webshr\Core;
|
||||
|
||||
use Webshr\Core\Application;
|
||||
use Webshr\Core\Config\Environment;
|
||||
use Webshr\Core\Assets\Bundle;
|
||||
use Webshr\Core\Assets\Contracts\Asset;
|
||||
use Webshr\Core\Assets\Contracts\Asset_Meta;
|
||||
use Webshr\Core\Filesystem\Loader;
|
||||
|
||||
/**
|
||||
* Instantiate the bootloader.
|
||||
*
|
||||
* @param Application|null $app
|
||||
*
|
||||
* @return Bootloader
|
||||
*/
|
||||
function bootloader(?Application $app = null): Bootloader
|
||||
{
|
||||
$bootloader = Bootloader::get_instance($app);
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
\Webshr\Core\add_actions(['after_setup_theme', 'rest_api_init'], function () use ($bootloader) {
|
||||
$app = $bootloader->get_application();
|
||||
|
||||
if ($app->has_been_bootstrapped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
\Webshr\Core\wp_die(
|
||||
'Webshr Core failed to boot. Run <code>\\Webshr\\Core\\bootloader()->boot()</code>.<br><br>If you\'re using a Webshore Theme, you need to <a href="https://git.webshore.io/Webshr/Core/functions.php#L32">update <strong>webshr/functions.php:32</strong></a>',
|
||||
'<code>\\Webshr\\Core\\bootloader()</code> was called incorrectly.',
|
||||
'Webshr Core › Boot Error',
|
||||
'Check out the <a href="https://git.webshore.io/Webshr/Core">release notes</a> for more information.',
|
||||
);
|
||||
}, 6);
|
||||
|
||||
return $bootloader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the available theme instance.
|
||||
*
|
||||
* @param string|null $abstract
|
||||
* @param array $parameters
|
||||
* @return \Webshr\Core\Application|mixed
|
||||
*/
|
||||
function app($abstract = null, array $parameters = [])
|
||||
{
|
||||
if (is_null($abstract)) {
|
||||
return Application::get_instance();
|
||||
}
|
||||
return Application::get_instance()->make($abstract, $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Require files from a directory
|
||||
*
|
||||
* @param string|null $path
|
||||
* @return void
|
||||
*/
|
||||
function require_files(string $dir = null): void
|
||||
{
|
||||
$loader = new Loader();
|
||||
$loader->load($dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get asset from manifest
|
||||
*/
|
||||
function asset(string $asset, ?string $manifest = null): Asset
|
||||
{
|
||||
if (! $manifest) {
|
||||
return \app('assets.manifest')->asset($asset);
|
||||
}
|
||||
|
||||
return \app('assets')->manifest($manifest)->asset($asset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bundle from manifest
|
||||
*/
|
||||
function bundle(string $bundle, ?string $manifest = null): Bundle
|
||||
{
|
||||
if (! $manifest) {
|
||||
return \app('assets.manifest')->bundle($bundle);
|
||||
}
|
||||
|
||||
return \app('assets')->manifest($manifest)->bundle($bundle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get asset meta from manifest
|
||||
*/
|
||||
function meta(string $asset, ?string $manifest = null): Asset_Meta
|
||||
{
|
||||
if (! $manifest) {
|
||||
return \app('assets.manifest')->meta($asset);
|
||||
} else {
|
||||
return \app('assets')->manifest($manifest)->meta($asset);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get module from manager
|
||||
*/
|
||||
function module(string $module): Module
|
||||
{
|
||||
return \app()->module_manager->module($module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts the given data.
|
||||
*/
|
||||
function encrypt(string $data): string
|
||||
{
|
||||
return \app('encryption')->encrypt($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts the given string without serialization.
|
||||
*/
|
||||
function encrypt_string(string $data): string
|
||||
{
|
||||
return \app('encryption')->encrypt_string($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts the given data.
|
||||
*/
|
||||
function decrypt(string $payload): string
|
||||
{
|
||||
return \app('encryption')->decrypt($payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts the given string without unserialization.
|
||||
*/
|
||||
function decrypt_string(string $payload): string
|
||||
{
|
||||
return \app('encryption')->decrypt_string($payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hashes the given data.
|
||||
*/
|
||||
function hash(string $data): string
|
||||
{
|
||||
return \app('hash')->make($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the given data against the hash.
|
||||
*/
|
||||
function check(string $data, string $hash): bool
|
||||
{
|
||||
return \app('hash')->check($data, $hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of an environment variable.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $default
|
||||
* @return mixed
|
||||
*
|
||||
* @copyright Taylor Otwell
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://github.com/laravel/framework/blob/v5.6.25/src/Illuminate/Support/helpers.php#L597-L632 Original
|
||||
*
|
||||
* @deprecated use Webshr\Core\Config\Environment::get() instead
|
||||
*/
|
||||
function env($key, $default = null)
|
||||
{
|
||||
if (class_exists(Environment::class)) {
|
||||
return Environment::get($key, $default);
|
||||
}
|
||||
|
||||
$value = getenv($key);
|
||||
if ($value === false) {
|
||||
return value($default);
|
||||
}
|
||||
|
||||
switch (strtolower($value)) {
|
||||
case 'true':
|
||||
case '(true)':
|
||||
return true;
|
||||
|
||||
case 'false':
|
||||
case '(false)':
|
||||
return false;
|
||||
|
||||
case 'empty':
|
||||
case '(empty)':
|
||||
return '';
|
||||
|
||||
case 'null':
|
||||
case '(null)':
|
||||
return;
|
||||
}
|
||||
|
||||
if (($valueLength = strlen($value)) > 1 && $value[0] === '"' && $value[($valueLength - 1)] === '"') {
|
||||
return substr($value, 1, -1);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default value of the given value.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*
|
||||
* @copyright Taylor Otwell
|
||||
* @license https://opensource.org/licenses/GPL-3.0-or-later GPL-3.0-or-later
|
||||
* @link https://github.com/laravel/framework/blob/v5.6.25/src/Illuminate/Support/helpers.php#L1143-L1152 Original
|
||||
*/
|
||||
function value($value)
|
||||
{
|
||||
return $value instanceof \Closure ? $value() : $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind single callback to multiple filters
|
||||
*
|
||||
* @param iterable $filters List of filters
|
||||
* @param callable $callback
|
||||
* @param integer $priority
|
||||
* @param integer $args
|
||||
* @return void
|
||||
*/
|
||||
function add_filters(iterable $filters, $callback, $priority = 10, $args = 2)
|
||||
{
|
||||
$count = count($filters);
|
||||
array_map(
|
||||
'\add_filter',
|
||||
(array) $filters,
|
||||
array_fill(0, $count, $callback),
|
||||
array_fill(0, $count, $priority),
|
||||
array_fill(0, $count, $args),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove single callback from multiple filters
|
||||
*
|
||||
* @param iterable $filters List of filters
|
||||
* @param callable $callback
|
||||
* @param integer $priority
|
||||
* @return void
|
||||
*/
|
||||
function remove_filters(iterable $filters, $callback, $priority = 10)
|
||||
{
|
||||
$count = count($filters);
|
||||
array_map(
|
||||
'\remove_filter',
|
||||
(array) $filters,
|
||||
array_fill(0, $count, $callback),
|
||||
array_fill(0, $count, $priority),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of add_filters
|
||||
*
|
||||
* @see add_filters
|
||||
* @param iterable $actions List of actions
|
||||
* @param callable $callback
|
||||
* @param integer $priority
|
||||
* @param integer $args
|
||||
* @return void
|
||||
*/
|
||||
function add_actions(iterable $actions, $callback, $priority = 10, $args = 2)
|
||||
{
|
||||
add_filters($actions, $callback, $priority, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of remove_filters
|
||||
*
|
||||
* @see remove_filters
|
||||
* @param iterable $actions List of actions
|
||||
* @param callable $callback
|
||||
* @param integer $priority
|
||||
* @return void
|
||||
*/
|
||||
function remove_actions(iterable $actions, $callback, $priority = 10)
|
||||
{
|
||||
remove_filters($actions, $callback, $priority);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for prettying up errors
|
||||
*
|
||||
* @param string $message
|
||||
* @param string $subtitle
|
||||
* @param string $title
|
||||
* @param string $footer
|
||||
*/
|
||||
function wp_die($message, $subtitle = '', $title = '', $footer = '')
|
||||
{
|
||||
$title = $title ?: __('WordPress › Error', 'webshr');
|
||||
$footer = $footer ?: '<a href="https://webshore.eu/">Webshore</a>';
|
||||
$message = "<h1>{$title}<br><small>{$subtitle}</small></h1><p>{$message}</p><p>{$footer}</p>";
|
||||
\wp_die($message, $title);
|
||||
}
|
||||
Reference in New Issue
Block a user