summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Config.php19
-rw-r--r--src/ERPHP.php43
-rw-r--r--src/Hook.php70
-rw-r--r--src/Router/Route.php39
-rw-r--r--src/Router/Router.php68
-rw-r--r--src/Router/RoutesDefinitionInterface.php11
-rw-r--r--src/routes.php20
7 files changed, 270 insertions, 0 deletions
diff --git a/src/Config.php b/src/Config.php
new file mode 100644
index 0000000..06b32d0
--- /dev/null
+++ b/src/Config.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace ERPHP;
+
+class Config
+{
+ public static function load(): void
+ {
+ $_ENV = array_replace_recursive(
+ $_ENV,
+ json_decode(file_get_contents(dirname(__DIR__) . "/config.json"), true) ?: []
+ );
+ }
+
+ public static function get(string $key, mixed $default = null): mixed
+ {
+ return $_ENV[$key] ?? getenv($key) === false ? $default : getenv($key);
+ }
+}
diff --git a/src/ERPHP.php b/src/ERPHP.php
new file mode 100644
index 0000000..b688361
--- /dev/null
+++ b/src/ERPHP.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace ERPHP;
+
+use ERPHP\Router\Router;
+
+class ERPHP
+{
+ public function run(): void
+ {
+ // start session
+ session_start();
+
+ // load config
+ Config::load();
+
+ // load plugins
+ $this->loadPlugins();
+
+ // load routes
+ Hook::addFilter("Router::getRouteFiles", function (array $routeFiles) {
+ $routeFiles[] = __DIR__ . "/routes.php";
+ return $routeFiles;
+ });
+
+ Router::getInstance()->run();
+ }
+
+ private function loadPlugins(): void
+ {
+ $pluginsDirectory = Config::get("plugins_directory", dirname(__DIR__) . "/plugins");
+ $pluginDirectories = scandir($pluginsDirectory);
+
+ foreach ($pluginDirectories as $directory) {
+ if ($directory === "." || $directory === "..") {
+ continue;
+ }
+
+ $composerConfig = file_get_contents("$pluginsDirectory/$directory/composer.json");
+ include_once "$pluginsDirectory/$directory/index.php";
+ }
+ }
+}
diff --git a/src/Hook.php b/src/Hook.php
new file mode 100644
index 0000000..0b292ce
--- /dev/null
+++ b/src/Hook.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace ERPHP;
+
+class Hook
+{
+ /**
+ * @var array<string, array<int, callable[]>> $actions
+ */
+ private static array $actions;
+
+ /**
+ * @var array<string, array<int, callable[]>> $filters
+ */
+ private static array $filters;
+
+ /**
+ * @param string $name
+ * @param callable $function
+ */
+ public static function addAction(string $name, callable $function, int $priority = 10): void
+ {
+ self::$actions[$name][$priority][] = $function;
+ }
+
+ /**
+ * @param string $name
+ * @param array $parameters
+ */
+ public static function runAction(string $name, ...$parameters): void
+ {
+ $actions = self::$actions[$name] ?? [];
+
+ foreach ($actions as $priority => $functions) {
+ foreach ($functions as $function) {
+ call_user_func_array($function, $parameters);
+ }
+ }
+ }
+
+ /**
+ * @param string $name
+ * @param callable $function
+ */
+ public static function addFilter(string $name, callable $function, int $priority = 10): void
+ {
+ self::$filters[$name][$priority][] = $function;
+ }
+
+ /**
+ * @param string $name
+ * @param mixed $value
+ * @param array $parameters
+ *
+ * @return mixed
+ */
+ public static function applyFilter(string $name, mixed $value, ...$parameters): mixed
+ {
+ $filteredValue = $value;
+ $filters = self::$filters[$name] ?? [];
+
+ foreach ($filters as $priority => $functions) {
+ foreach ($functions as $function) {
+ $filteredValue = call_user_func_array($function, [$value, ...$parameters]);
+ }
+ }
+
+ return $filteredValue;
+ }
+}
diff --git a/src/Router/Route.php b/src/Router/Route.php
new file mode 100644
index 0000000..ee9637b
--- /dev/null
+++ b/src/Router/Route.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace ERPHP\Router;
+
+use Symfony\Component\Routing\Route as SymfonyRoute;
+
+class Route extends SymfonyRoute
+{
+ public function __construct(
+ string $path,
+ callable $action,
+ private string $name = "",
+ array $defaults = [],
+ array $requirements = [],
+ array $options = [],
+ ?string $host = '',
+ string|array $schemes = [],
+ string|array $methods = [],
+ ?string $condition = '',
+ )
+ {
+ if (empty($name)) {
+ $this->name = $path;
+ }
+
+ $defaults = array_merge([
+ "_" => [
+ "action" => $action,
+ ],
+ ], $defaults);
+
+ parent::__construct($path, $defaults, $requirements, $options, $host, $schemes, $methods, $condition);
+ }
+
+ public function getName(): string
+ {
+ return $this->name;
+ }
+}
diff --git a/src/Router/Router.php b/src/Router/Router.php
new file mode 100644
index 0000000..d770f15
--- /dev/null
+++ b/src/Router/Router.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace ERPHP\Router;
+
+use ERPHP\Hook;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Routing\Matcher\UrlMatcher;
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\Routing\RouteCollection;
+
+class Router
+{
+ private RouteCollection $routes;
+
+ private static self $instance;
+
+ public static function getInstance(): self
+ {
+ if (! isset(self::$instance)) {
+ self::$instance = new self();
+ }
+
+ return self::$instance;
+ }
+
+ public function __construct()
+ {
+ $this->routes = new RouteCollection();
+ }
+
+ public function run(): void
+ {
+ // get defined routes
+ $routeFiles = [];
+ $routeFiles = Hook::applyFilter("Router::getRouteFiles", $routeFiles);
+
+ foreach ($routeFiles as $routesFile) {
+ /** @var RoutesDefinitionInterface $routesDefinition */
+ $routesDefinition = include $routesFile;
+ $routes = $routesDefinition->get();
+
+ foreach ($routes as $route) {
+ /** @var Route[] $route */
+ if (! is_null($this->routes->get($route->getName()))) {
+ throw new \Exception("Route \"{$route->getName()}\" already defined.");
+ }
+
+ $this->routes->add($route->getName(), $route);
+ }
+ }
+
+ $request = Request::createFromGlobals();
+ $response = new Response();
+
+ // set semantically correct request method
+ if ($request->get("_method", false)) {
+ $request->setMethod($request->get("_method"));
+ }
+
+ $matcher = new UrlMatcher($this->routes, new RequestContext());
+ $match = $matcher->matchRequest($request);
+
+ /** @var Response $response */
+ $response = call_user_func_array($match["_"]["action"], [$request]);
+ $response->send();
+ }
+}
diff --git a/src/Router/RoutesDefinitionInterface.php b/src/Router/RoutesDefinitionInterface.php
new file mode 100644
index 0000000..614c7cd
--- /dev/null
+++ b/src/Router/RoutesDefinitionInterface.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace ERPHP\Router;
+
+interface RoutesDefinitionInterface
+{
+ /**
+ * @return Route[]
+ */
+ public function get(): array;
+}
diff --git a/src/routes.php b/src/routes.php
new file mode 100644
index 0000000..19a81b2
--- /dev/null
+++ b/src/routes.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace ERPHP;
+
+use ERPHP\Router\Route;
+use ERPHP\Router\RoutesDefinitionInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+return new class implements RoutesDefinitionInterface
+{
+ public function get(): array
+ {
+ return [
+ new Route("/", function (Request $request) {
+ return new Response("/ route response");
+ }),
+ ];
+ }
+};