Simple Route
This commit is contained in:
1 parent
156d277e77
commit
ab1df63788
156 files changed
+13136
No files matched your search
@@ -1,37 +0,0 @@
|
||||
RewriteEngine On
|
||||
|
||||
##
|
||||
## You may need to uncomment the following line for some hosting environments,
|
||||
## if you have installed to a subdirectory, enter the name here also.
|
||||
##
|
||||
RewriteBase /phpCourse/exam
|
||||
|
||||
##
|
||||
## Black listed folders
|
||||
##
|
||||
RewriteRule ^phpCourse/exam/app/.* index.php [L,NC]
|
||||
RewriteRule ^phpCourse/exam/config/.* index.php [L,NC]
|
||||
RewriteRule ^phpCourse/exam/vendor/.* index.php [L,NC]
|
||||
RewriteRule ^phpCourse/exam/routes/.* index.php [L,NC]
|
||||
|
||||
##
|
||||
## White listed folders
|
||||
##
|
||||
RewriteCond %{REQUEST_FILENAME} -f
|
||||
RewriteCond %{REQUEST_FILENAME} !/api/docs/.*
|
||||
RewriteCond %{REQUEST_FILENAME} !/pages/.*
|
||||
RewriteCond %{REQUEST_FILENAME} !/pages/assets/.*
|
||||
RewriteRule !^index.php index.php [L,NC]
|
||||
|
||||
##
|
||||
## Block all PHP files, except index
|
||||
##
|
||||
RewriteCond %{REQUEST_FILENAME} -f
|
||||
RewriteCond %{REQUEST_FILENAME} \.php$
|
||||
RewriteRule !^index.php index.php [L,NC]
|
||||
|
||||
##
|
||||
## Standard routes
|
||||
##
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^ index.php [L]
|
||||
@@ -1,2 +0,0 @@
|
||||
Order deny,allow
|
||||
Allow from All
|
||||
@@ -1,114 +0,0 @@
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: PHP Course Exam
|
||||
version: 1.0.0
|
||||
contact:
|
||||
name: Kilian Kurt Hofmann
|
||||
email: khofmann@zedat.fu-berlin.de
|
||||
description: PHP Course (ABV FU Berlin) 2024 Exam
|
||||
paths:
|
||||
/users:
|
||||
get:
|
||||
summary: Get Users
|
||||
description: Returns all users
|
||||
parameters: []
|
||||
operationId: ""
|
||||
responses:
|
||||
"200":
|
||||
description: Default response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/UsersListResponse"
|
||||
"401":
|
||||
description: Not allowed
|
||||
tags:
|
||||
- Users
|
||||
security:
|
||||
- BasicAuth: []
|
||||
put:
|
||||
summary: Add User
|
||||
description: Add a new user
|
||||
parameters: []
|
||||
operationId: ""
|
||||
responses:
|
||||
"200":
|
||||
description: Default response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/TrueResponse"
|
||||
"401":
|
||||
description: Not allowed
|
||||
"500":
|
||||
description: Error
|
||||
tags:
|
||||
- Users
|
||||
security:
|
||||
- BasicAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/UserAddBody"
|
||||
externalDocs:
|
||||
url: "https://khofmann.userpage.fu-berlin.de/phpCourse/exam/api/docs/"
|
||||
security: []
|
||||
servers:
|
||||
- url: "https://khofmann.userpage.fu-berlin.de/phpCourse/exam/api/"
|
||||
description: ""
|
||||
variables: {}
|
||||
components:
|
||||
links: {}
|
||||
callbacks: {}
|
||||
schemas:
|
||||
UsersListResponse:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/UserType"
|
||||
UserType:
|
||||
type: object
|
||||
properties:
|
||||
ID:
|
||||
type: number
|
||||
description: User ID
|
||||
example: 1
|
||||
FirstName:
|
||||
type: string
|
||||
description: Given name of user
|
||||
example: Max
|
||||
LastName:
|
||||
type: string
|
||||
description: Family name of User
|
||||
example: Mustermann
|
||||
token:
|
||||
type: string
|
||||
description: Access Token
|
||||
format: uuid
|
||||
example: 3be6453c-03eb-4357-ae5a-984a0e574a54
|
||||
UserAddBody:
|
||||
type: object
|
||||
required:
|
||||
- FirstName
|
||||
- LastName
|
||||
properties:
|
||||
FirstName:
|
||||
type: string
|
||||
description: Given name of user
|
||||
example: Max
|
||||
LastName:
|
||||
type: string
|
||||
description: Family name of User
|
||||
example: Mustermann
|
||||
TrueResponse:
|
||||
type: boolean
|
||||
example: true
|
||||
securitySchemes:
|
||||
BasicAuth:
|
||||
type: apiKey
|
||||
name: token
|
||||
in: header
|
||||
tags:
|
||||
- name: Users
|
||||
- name: Posts
|
||||
Whitespace-only changes.
@@ -1,10 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<link href="./index.css" rel="stylesheet" />
|
||||
|
||||
<title>API Docs</title>
|
||||
</head>
|
||||
|
||||
<body></body>
|
||||
</html>
|
||||
@@ -1,3 +0,0 @@
|
||||
<?php
|
||||
|
||||
Headers::redirect("docs/index.html");
|
||||
@@ -1,26 +0,0 @@
|
||||
<?php
|
||||
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
|
||||
switch ($method) {
|
||||
case "GET":
|
||||
return get();
|
||||
default:
|
||||
return Response::api("$method not supported", 500);
|
||||
}
|
||||
|
||||
function get()
|
||||
{
|
||||
$db = Database::getInstance();
|
||||
|
||||
$query =
|
||||
"SELECT
|
||||
*
|
||||
FROM
|
||||
egb_gaestebuch";
|
||||
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->execute();
|
||||
|
||||
Response::api($stmt->fetchAll());
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
<?php
|
||||
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
$params = PathParams::get();
|
||||
|
||||
if (empty($params[2])) {
|
||||
return Response::api("Missing param", 500);
|
||||
}
|
||||
|
||||
switch ($method) {
|
||||
case "GET":
|
||||
if (Auth::hasPermission("read")) return get($params[2]);
|
||||
break;
|
||||
case "POST":
|
||||
if (Auth::hasPermission("write")) return post($params[2]);
|
||||
break;
|
||||
case "DELETE":
|
||||
if (Auth::hasPermission("write")) return delete($params[2]);
|
||||
break;
|
||||
default:
|
||||
return Response::api("$method not supported", 500);
|
||||
}
|
||||
|
||||
return Response::api("Not allowed", 401);
|
||||
|
||||
function get($id)
|
||||
{
|
||||
$db = Database::getInstance();
|
||||
|
||||
$query =
|
||||
"SELECT
|
||||
*
|
||||
FROM
|
||||
Users
|
||||
WHERE
|
||||
ID = :ID";
|
||||
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->bindValue(":ID", $id);
|
||||
$stmt->execute();
|
||||
|
||||
Response::api($stmt->fetchAll());
|
||||
}
|
||||
|
||||
function post($id)
|
||||
{
|
||||
|
||||
$db = Database::getInstance();
|
||||
|
||||
$query =
|
||||
"UPDATE
|
||||
Users
|
||||
SET
|
||||
FirstName = :FIRST, LastName = :LAST
|
||||
WHERE
|
||||
ID = :ID";
|
||||
|
||||
$_POST = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
try {
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->bindValue(":FIRST", $_POST["firstName"]);
|
||||
$stmt->bindValue(":LAST", $_POST["lastName"]);
|
||||
$stmt->bindValue(":ID", $id);
|
||||
|
||||
Response::api($stmt->execute());
|
||||
} catch (Exception $e) {
|
||||
Response::api($e->getMessage(), 500);
|
||||
}
|
||||
}
|
||||
|
||||
function delete($id)
|
||||
{
|
||||
$db = Database::getInstance();
|
||||
|
||||
$query =
|
||||
"DELETE FROM
|
||||
Users
|
||||
WHERE
|
||||
ID = :ID";
|
||||
|
||||
try {
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->bindValue(":ID", $id);
|
||||
|
||||
Response::api($stmt->execute());
|
||||
} catch (Exception $e) {
|
||||
Response::api($e->getMessage(), 500);
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
<?php
|
||||
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
|
||||
switch ($method) {
|
||||
case "GET":
|
||||
if (Auth::hasPermission("read")) return get();
|
||||
break;
|
||||
case "PUT":
|
||||
if (Auth::hasPermission("write")) return put();
|
||||
break;
|
||||
default:
|
||||
return Response::api("$method not supported", 500);
|
||||
}
|
||||
|
||||
return Response::api("Not allowed", 401);
|
||||
|
||||
function get()
|
||||
{
|
||||
$db = Database::getInstance();
|
||||
|
||||
$query =
|
||||
"SELECT
|
||||
*
|
||||
FROM
|
||||
Users";
|
||||
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->execute();
|
||||
|
||||
Response::api($stmt->fetchAll());
|
||||
}
|
||||
|
||||
function put()
|
||||
{
|
||||
$db = Database::getInstance();
|
||||
|
||||
$_PUT = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
$query =
|
||||
"INSERT INTO
|
||||
Users(FirstName, LastName, Token)
|
||||
VALUES(:FIRST, :LAST, UUID())";
|
||||
|
||||
try {
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->bindValue(":FIRST", $_PUT["firstName"]);
|
||||
$stmt->bindValue(":LAST", $_PUT["lastName"]);
|
||||
|
||||
Response::api($stmt->execute());
|
||||
} catch (Exception $e) {
|
||||
Response::api($e->getMessage(), 500);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
Order deny,allow
|
||||
Deny from all
|
||||
Allow from 127.0.0.1
|
||||
@@ -1,15 +0,0 @@
|
||||
<?php
|
||||
|
||||
if (strpos($_SERVER["REQUEST_URI"], "api") === false) {
|
||||
ini_set("display_errors", 1);
|
||||
}
|
||||
ini_set("default_charset", "utf-8");
|
||||
ini_set('session.cookie_httponly', 1);
|
||||
ini_set('session.cookie_secure', 1);
|
||||
ini_set('session.use_only_cookies', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . "/../vendor/autoloader.php";
|
||||
Headers::html();
|
||||
|
||||
require_once __DIR__ . "/../routes/routes.php";
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"require": {
|
||||
"pecee/simple-router": "^5.4"
|
||||
}
|
||||
}
|
||||
Generated
+82
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "d067465d9a99de373ba3093d61eda1d8",
|
||||
"packages": [
|
||||
{
|
||||
"name": "pecee/simple-router",
|
||||
"version": "5.4.1.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/skipperbent/simple-php-router.git",
|
||||
"reference": "a2843d5b1e037f8b61cc99f27eab52a28bf41dfd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/skipperbent/simple-php-router/zipball/a2843d5b1e037f8b61cc99f27eab52a28bf41dfd",
|
||||
"reference": "a2843d5b1e037f8b61cc99f27eab52a28bf41dfd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"php": ">=7.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1",
|
||||
"phpstan/phpstan": "^1",
|
||||
"phpstan/phpstan-deprecation-rules": "^1",
|
||||
"phpstan/phpstan-phpunit": "^1",
|
||||
"phpstan/phpstan-strict-rules": "^1",
|
||||
"phpunit/phpunit": "^8"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Pecee\\": "src/Pecee/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Simon Sessingø",
|
||||
"email": "simon.sessingoe@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "Simple, fast PHP router that is easy to get integrated and in almost any project. Heavily inspired by the Laravel router.",
|
||||
"keywords": [
|
||||
"framework",
|
||||
"input-handler",
|
||||
"laravel",
|
||||
"pecee",
|
||||
"php",
|
||||
"request-handler",
|
||||
"route",
|
||||
"router",
|
||||
"routing",
|
||||
"routing-engine",
|
||||
"simple-php-router",
|
||||
"url-handling"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/skipperbent/simple-php-router/issues",
|
||||
"source": "https://github.com/skipperbent/simple-php-router/issues"
|
||||
},
|
||||
"time": "2023-12-11T21:48:25+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.6.0"
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
Order deny,allow
|
||||
Deny from all
|
||||
Allow from 127.0.0.1
|
||||
@@ -1,5 +0,0 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
"basePath" => "phpCourse/exam/",
|
||||
];
|
||||
@@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
"host" => "usersql.zedat.fu-berlin.de",
|
||||
"user" => "khofmann-sql",
|
||||
"passwd" => "xz8c7m7p",
|
||||
"database" => "khofmann-db1",
|
||||
"charset" => "utf8",
|
||||
];
|
||||
@@ -1,3 +0,0 @@
|
||||
<?php
|
||||
|
||||
require_once "./app/app.php";
|
||||
@@ -1,23 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
|
||||
<head>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
|
||||
<title>
|
||||
Page not Found
|
||||
</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="site-content" class="site-content">
|
||||
<div class="p-5 mb-4 bg-light rounded-3">
|
||||
<div class="container-fluid py-5">
|
||||
<h1 class="display-5 fw-bold">404 Page not Found</h1>
|
||||
<p class="col-md-8 fs-4">Page does not exist.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,23 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
|
||||
<head>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
|
||||
<title>
|
||||
Server Error
|
||||
</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="site-content" class="site-content">
|
||||
<div class="p-5 mb-4 bg-light rounded-3">
|
||||
<div class="container-fluid py-5">
|
||||
<h1 class="display-5 fw-bold">500 Internal Server Error</h1>
|
||||
<pre class="col-md-8 fs-4"><?= $e->getMessage() ?></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,40 +0,0 @@
|
||||
(function(){const n=document.createElement("link").relList;if(n&&n.supports&&n.supports("modulepreload"))return;for(const l of document.querySelectorAll('link[rel="modulepreload"]'))r(l);new MutationObserver(l=>{for(const o of l)if(o.type==="childList")for(const u of o.addedNodes)u.tagName==="LINK"&&u.rel==="modulepreload"&&r(u)}).observe(document,{childList:!0,subtree:!0});function t(l){const o={};return l.integrity&&(o.integrity=l.integrity),l.referrerPolicy&&(o.referrerPolicy=l.referrerPolicy),l.crossOrigin==="use-credentials"?o.credentials="include":l.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function r(l){if(l.ep)return;l.ep=!0;const o=t(l);fetch(l.href,o)}})();function tc(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var Hi={exports:{}},br={},Wi={exports:{}},L={};/**
|
||||
* @license React
|
||||
* react.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/var Yt=Symbol.for("react.element"),rc=Symbol.for("react.portal"),lc=Symbol.for("react.fragment"),oc=Symbol.for("react.strict_mode"),uc=Symbol.for("react.profiler"),ic=Symbol.for("react.provider"),sc=Symbol.for("react.context"),ac=Symbol.for("react.forward_ref"),cc=Symbol.for("react.suspense"),fc=Symbol.for("react.memo"),dc=Symbol.for("react.lazy"),Du=Symbol.iterator;function pc(e){return e===null||typeof e!="object"?null:(e=Du&&e[Du]||e["@@iterator"],typeof e=="function"?e:null)}var Qi={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},Ki=Object.assign,Yi={};function lt(e,n,t){this.props=e,this.context=n,this.refs=Yi,this.updater=t||Qi}lt.prototype.isReactComponent={};lt.prototype.setState=function(e,n){if(typeof e!="object"&&typeof e!="function"&&e!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,n,"setState")};lt.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function Xi(){}Xi.prototype=lt.prototype;function $o(e,n,t){this.props=e,this.context=n,this.refs=Yi,this.updater=t||Qi}var Vo=$o.prototype=new Xi;Vo.constructor=$o;Ki(Vo,lt.prototype);Vo.isPureReactComponent=!0;var ju=Array.isArray,Gi=Object.prototype.hasOwnProperty,Ao={current:null},Zi={key:!0,ref:!0,__self:!0,__source:!0};function Ji(e,n,t){var r,l={},o=null,u=null;if(n!=null)for(r in n.ref!==void 0&&(u=n.ref),n.key!==void 0&&(o=""+n.key),n)Gi.call(n,r)&&!Zi.hasOwnProperty(r)&&(l[r]=n[r]);var i=arguments.length-2;if(i===1)l.children=t;else if(1<i){for(var s=Array(i),c=0;c<i;c++)s[c]=arguments[c+2];l.children=s}if(e&&e.defaultProps)for(r in i=e.defaultProps,i)l[r]===void 0&&(l[r]=i[r]);return{$$typeof:Yt,type:e,key:o,ref:u,props:l,_owner:Ao.current}}function mc(e,n){return{$$typeof:Yt,type:e.type,key:n,ref:e.ref,props:e.props,_owner:e._owner}}function Bo(e){return typeof e=="object"&&e!==null&&e.$$typeof===Yt}function vc(e){var n={"=":"=0",":":"=2"};return"$"+e.replace(/[=:]/g,function(t){return n[t]})}var Iu=/\/+/g;function wl(e,n){return typeof e=="object"&&e!==null&&e.key!=null?vc(""+e.key):n.toString(36)}function yr(e,n,t,r,l){var o=typeof e;(o==="undefined"||o==="boolean")&&(e=null);var u=!1;if(e===null)u=!0;else switch(o){case"string":case"number":u=!0;break;case"object":switch(e.$$typeof){case Yt:case rc:u=!0}}if(u)return u=e,l=l(u),e=r===""?"."+wl(u,0):r,ju(l)?(t="",e!=null&&(t=e.replace(Iu,"$&/")+"/"),yr(l,n,t,"",function(c){return c})):l!=null&&(Bo(l)&&(l=mc(l,t+(!l.key||u&&u.key===l.key?"":(""+l.key).replace(Iu,"$&/")+"/")+e)),n.push(l)),1;if(u=0,r=r===""?".":r+":",ju(e))for(var i=0;i<e.length;i++){o=e[i];var s=r+wl(o,i);u+=yr(o,n,t,s,l)}else if(s=pc(e),typeof s=="function")for(e=s.call(e),i=0;!(o=e.next()).done;)o=o.value,s=r+wl(o,i++),u+=yr(o,n,t,s,l);else if(o==="object")throw n=String(e),Error("Objects are not valid as a React child (found: "+(n==="[object Object]"?"object with keys {"+Object.keys(e).join(", ")+"}":n)+"). If you meant to render a collection of children, use an array instead.");return u}function er(e,n,t){if(e==null)return e;var r=[],l=0;return yr(e,r,"","",function(o){return n.call(t,o,l++)}),r}function hc(e){if(e._status===-1){var n=e._result;n=n(),n.then(function(t){(e._status===0||e._status===-1)&&(e._status=1,e._result=t)},function(t){(e._status===0||e._status===-1)&&(e._status=2,e._result=t)}),e._status===-1&&(e._status=0,e._result=n)}if(e._status===1)return e._result.default;throw e._result}var ie={current:null},gr={transition:null},yc={ReactCurrentDispatcher:ie,ReactCurrentBatchConfig:gr,ReactCurrentOwner:Ao};function qi(){throw Error("act(...) is not supported in production builds of React.")}L.Children={map:er,forEach:function(e,n,t){er(e,function(){n.apply(this,arguments)},t)},count:function(e){var n=0;return er(e,function(){n++}),n},toArray:function(e){return er(e,function(n){return n})||[]},only:function(e){if(!Bo(e))throw Error("React.Children.only expected to receive a single React element child.");return e}};L.Component=lt;L.Fragment=lc;L.Profiler=uc;L.PureComponent=$o;L.StrictMode=oc;L.Suspense=cc;L.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=yc;L.act=qi;L.cloneElement=function(e,n,t){if(e==null)throw Error("React.cloneElement(...): The argument must be a React element, but you passed "+e+".");var r=Ki({},e.props),l=e.key,o=e.ref,u=e._owner;if(n!=null){if(n.ref!==void 0&&(o=n.ref,u=Ao.current),n.key!==void 0&&(l=""+n.key),e.type&&e.type.defaultProps)var i=e.type.defaultProps;for(s in n)Gi.call(n,s)&&!Zi.hasOwnProperty(s)&&(r[s]=n[s]===void 0&&i!==void 0?i[s]:n[s])}var s=arguments.length-2;if(s===1)r.children=t;else if(1<s){i=Array(s);for(var c=0;c<s;c++)i[c]=arguments[c+2];r.children=i}return{$$typeof:Yt,type:e.type,key:l,ref:o,props:r,_owner:u}};L.createContext=function(e){return e={$$typeof:sc,_currentValue:e,_currentValue2:e,_threadCount:0,ProviderLine truncated
|
||||
* @license React
|
||||
* react-jsx-runtime.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/var wc=el,Sc=Symbol.for("react.element"),kc=Symbol.for("react.fragment"),Ec=Object.prototype.hasOwnProperty,Cc=wc.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,_c={key:!0,ref:!0,__self:!0,__source:!0};function bi(e,n,t){var r,l={},o=null,u=null;t!==void 0&&(o=""+t),n.key!==void 0&&(o=""+n.key),n.ref!==void 0&&(u=n.ref);for(r in n)Ec.call(n,r)&&!_c.hasOwnProperty(r)&&(l[r]=n[r]);if(e&&e.defaultProps)for(r in n=e.defaultProps,n)l[r]===void 0&&(l[r]=n[r]);return{$$typeof:Sc,type:e,key:o,ref:u,props:l,_owner:Cc.current}}br.Fragment=kc;br.jsx=bi;br.jsxs=bi;Hi.exports=br;var le=Hi.exports,Ql={},es={exports:{}},ge={},ns={exports:{}},ts={};/**
|
||||
* @license React
|
||||
* scheduler.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/(function(e){function n(C,N){var z=C.length;C.push(N);e:for(;0<z;){var H=z-1>>>1,X=C[H];if(0<l(X,N))C[H]=N,C[z]=X,z=H;else break e}}function t(C){return C.length===0?null:C[0]}function r(C){if(C.length===0)return null;var N=C[0],z=C.pop();if(z!==N){C[0]=z;e:for(var H=0,X=C.length,qt=X>>>1;H<qt;){var hn=2*(H+1)-1,gl=C[hn],yn=hn+1,bt=C[yn];if(0>l(gl,z))yn<X&&0>l(bt,gl)?(C[H]=bt,C[yn]=z,H=yn):(C[H]=gl,C[hn]=z,H=hn);else if(yn<X&&0>l(bt,z))C[H]=bt,C[yn]=z,H=yn;else break e}}return N}function l(C,N){var z=C.sortIndex-N.sortIndex;return z!==0?z:C.id-N.id}if(typeof performance=="object"&&typeof performance.now=="function"){var o=performance;e.unstable_now=function(){return o.now()}}else{var u=Date,i=u.now();e.unstable_now=function(){return u.now()-i}}var s=[],c=[],v=1,m=null,p=3,g=!1,w=!1,S=!1,I=typeof setTimeout=="function"?setTimeout:null,f=typeof clearTimeout=="function"?clearTimeout:null,a=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function d(C){for(var N=t(c);N!==null;){if(N.callback===null)r(c);else if(N.startTime<=C)r(c),N.sortIndex=N.expirationTime,n(s,N);else break;N=t(c)}}function h(C){if(S=!1,d(C),!w)if(t(s)!==null)w=!0,hl(E);else{var N=t(c);N!==null&&yl(h,N.startTime-C)}}function E(C,N){w=!1,S&&(S=!1,f(P),P=-1),g=!0;var z=p;try{for(d(N),m=t(s);m!==null&&(!(m.expirationTime>N)||C&&!Pe());){var H=m.callback;if(typeof H=="function"){m.callback=null,p=m.priorityLevel;var X=H(m.expirationTime<=N);N=e.unstable_now(),typeof X=="function"?m.callback=X:m===t(s)&&r(s),d(N)}else r(s);m=t(s)}if(m!==null)var qt=!0;else{var hn=t(c);hn!==null&&yl(h,hn.startTime-N),qt=!1}return qt}finally{m=null,p=z,g=!1}}var _=!1,x=null,P=-1,B=5,T=-1;function Pe(){return!(e.unstable_now()-T<B)}function it(){if(x!==null){var C=e.unstable_now();T=C;var N=!0;try{N=x(!0,C)}finally{N?st():(_=!1,x=null)}}else _=!1}var st;if(typeof a=="function")st=function(){a(it)};else if(typeof MessageChannel<"u"){var Mu=new MessageChannel,nc=Mu.port2;Mu.port1.onmessage=it,st=function(){nc.postMessage(null)}}else st=function(){I(it,0)};function hl(C){x=C,_||(_=!0,st())}function yl(C,N){P=I(function(){C(e.unstable_now())},N)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function(C){C.callback=null},e.unstable_continueExecution=function(){w||g||(w=!0,hl(E))},e.unstable_forceFrameRate=function(C){0>C||125<C?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):B=0<C?Math.floor(1e3/C):5},e.unstable_getCurrentPriorityLevel=function(){return p},e.unstable_getFirstCallbackNode=function(){return t(s)},e.unstable_next=function(C){switch(p){case 1:case 2:case 3:var N=3;break;default:N=p}var z=p;p=N;try{return C()}finally{p=z}},e.unstable_pauseExecution=function(){},e.unstable_requestPaint=function(){},e.unstable_runWithPriority=function(C,N){switch(C){case 1:case 2:case 3:case 4:case 5:break;default:C=3}var z=p;p=C;try{return N()}finally{p=z}},e.unstable_scheduleCallback=function(C,N,z){var H=e.unstable_now();switch(typeof z=="object"&&z!==null?(z=z.delay,z=typeof z=="number"&&0<z?H+z:H):z=H,C){case 1:var X=-1;break;case 2:X=250;break;case 5:X=1073741823;break;case 4:X=1e4;break;default:X=5e3}return X=z+X,C={id:v++,callback:N,priorityLevel:C,startTime:z,expirationTime:X,sortIndex:-1},z>H?(C.sortIndex=z,n(c,C),t(s)===null&&C===t(c)&&(S?(f(P),P=-1):S=!0,yl(h,z-H))):(C.sortIndex=X,n(s,C),w||g||(w=!0,hl(E))),C},e.unstable_shouldYield=Pe,e.unstable_wrapCallback=function(C){var N=p;return function(){var z=p;p=N;try{return C.apply(this,arguments)}finally{p=z}}}})(ts);ns.exports=ts;var xc=ns.exports;/**
|
||||
* @license React
|
||||
* react-dom.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/var Pc=el,ye=xc;function y(e){for(var n="https://reactjs.org/docs/error-decoder.html?invariant="+e,t=1;t<arguments.length;t++)n+="&args[]="+encodeURIComponent(arguments[t]);return"Minified React error #"+e+"; visit "+n+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}var rs=new Set,Tt={};function Tn(e,n){Jn(e,n),Jn(e+"Capture",n)}function Jn(e,n){for(Tt[e]=n,e=0;e<n.length;e++)rs.add(n[e])}var We=!(typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Kl=Object.prototype.hasOwnProperty,Nc=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,Fu={},Uu={};function zc(e){return Kl.call(Uu,e)?!0:Kl.call(Fu,e)?!1:Nc.test(e)?Uu[e]=!0:(Fu[e]=!0,!1)}function Lc(e,n,t,r){if(t!==null&&t.type===0)return!1;switch(typeof n){case"function":case"symbol":return!0;case"boolean":return r?!1:t!==null?!t.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function Tc(e,n,t,r){if(n===null||typeof n>"u"||Lc(e,n,t,r))return!0;if(r)return!1;if(t!==null)switch(t.type){case 3:return!n;case 4:return n===!1;case 5:return isNaN(n);case 6:return isNaN(n)||1>n}return!1}function se(e,n,t,r,l,o,u){this.acceptsBooleans=n===2||n===3||n===4,this.attributeName=r,this.attributeNamespace=l,this.mustUseProperty=t,this.propertyName=e,this.type=n,this.sanitizeURL=o,this.removeEmptyString=u}var b={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){b[e]=new se(e,0,!1,e,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var n=e[0];b[n]=new se(n,1,!1,e[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(e){b[e]=new se(e,2,!1,e.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){b[e]=new se(e,2,!1,e,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){b[e]=new se(e,3,!1,e.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(e){b[e]=new se(e,3,!0,e,null,!1,!1)});["capture","download"].forEach(function(e){b[e]=new se(e,4,!1,e,null,!1,!1)});["cols","rows","size","span"].forEach(function(e){b[e]=new se(e,6,!1,e,null,!1,!1)});["rowSpan","start"].forEach(function(e){b[e]=new se(e,5,!1,e.toLowerCase(),null,!1,!1)});var Ho=/[\-:]([a-z])/g;function Wo(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var n=e.replace(Ho,Wo);b[n]=new se(n,1,!1,e,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var n=e.replace(Ho,Wo);b[n]=new se(n,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(e){var n=e.replace(Ho,Wo);b[n]=new se(n,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(e){b[e]=new se(e,1,!1,e.toLowerCase(),null,!1,!1)});b.xlinkHref=new se("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(e){b[e]=new se(e,1,!1,e.toLowerCase(),null,!0,!0)});function Qo(e,n,t,r){var l=b.hasOwnProperty(n)?b[n]:null;(l!==null?l.type!==0:r||!(2<n.length)||n[0]!=="o"&&n[0]!=="O"||n[1]!=="n"&&n
Line truncated
|
||||
`+Sl+e}var kl=!1;function El(e,n){if(!e||kl)return"";kl=!0;var t=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{if(n)if(n=function(){throw Error()},Object.defineProperty(n.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(n,[])}catch(c){var r=c}Reflect.construct(e,[],n)}else{try{n.call()}catch(c){r=c}e.call(n.prototype)}else{try{throw Error()}catch(c){r=c}e()}}catch(c){if(c&&r&&typeof c.stack=="string"){for(var l=c.stack.split(`
|
||||
`),o=r.stack.split(`
|
||||
`),u=l.length-1,i=o.length-1;1<=u&&0<=i&&l[u]!==o[i];)i--;for(;1<=u&&0<=i;u--,i--)if(l[u]!==o[i]){if(u!==1||i!==1)do if(u--,i--,0>i||l[u]!==o[i]){var s=`
|
||||
`+l[u].replace(" at new "," at ");return e.displayName&&s.includes("<anonymous>")&&(s=s.replace("<anonymous>",e.displayName)),s}while(1<=u&&0<=i);break}}}finally{kl=!1,Error.prepareStackTrace=t}return(e=e?e.displayName||e.name:"")?yt(e):""}function Rc(e){switch(e.tag){case 5:return yt(e.type);case 16:return yt("Lazy");case 13:return yt("Suspense");case 19:return yt("SuspenseList");case 0:case 2:case 15:return e=El(e.type,!1),e;case 11:return e=El(e.type.render,!1),e;case 1:return e=El(e.type,!0),e;default:return""}}function Zl(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case Dn:return"Fragment";case Mn:return"Portal";case Yl:return"Profiler";case Ko:return"StrictMode";case Xl:return"Suspense";case Gl:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case os:return(e.displayName||"Context")+".Consumer";case ls:return(e._context.displayName||"Context")+".Provider";case Yo:var n=e.render;return e=e.displayName,e||(e=n.displayName||n.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case Xo:return n=e.displayName||null,n!==null?n:Zl(e.type)||"Memo";case Ze:n=e._payload,e=e._init;try{return Zl(e(n))}catch{}}return null}function Oc(e){var n=e.type;switch(e.tag){case 24:return"Cache";case 9:return(n.displayName||"Context")+".Consumer";case 10:return(n._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=n.render,e=e.displayName||e.name||"",n.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return n;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return Zl(n);case 8:return n===Ko?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof n=="function")return n.displayName||n.name||null;if(typeof n=="string")return n}return null}function fn(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function is(e){var n=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(n==="checkbox"||n==="radio")}function Mc(e){var n=is(e)?"checked":"value",t=Object.getOwnPropertyDescriptor(e.constructor.prototype,n),r=""+e[n];if(!e.hasOwnProperty(n)&&typeof t<"u"&&typeof t.get=="function"&&typeof t.set=="function"){var l=t.get,o=t.set;return Object.defineProperty(e,n,{configurable:!0,get:function(){return l.call(this)},set:function(u){r=""+u,o.call(this,u)}}),Object.defineProperty(e,n,{enumerable:t.enumerable}),{getValue:function(){return r},setValue:function(u){r=""+u},stopTracking:function(){e._valueTracker=null,delete e[n]}}}}function tr(e){e._valueTracker||(e._valueTracker=Mc(e))}function ss(e){if(!e)return!1;var n=e._valueTracker;if(!n)return!0;var t=n.getValue(),r="";return e&&(r=is(e)?e.checked?"true":"false":e.value),e=r,e!==t?(n.setValue(e),!0):!1}function Lr(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function Jl(e,n){var t=n.checked;return V({},n,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:t??e._wrapperState.initialChecked})}function Vu(e,n){var t=n.defaultValue==null?"":n.defaultValue,r=n.checked!=null?n.checked:n.defaultChecked;t=fn(n.value!=null?n.value:t),e._wrapperState={initialChecked:r,initialValue:t,controlled:n.type==="checkbox"||n.type==="radio"?n.checked!=null:n.value!=null}}function as(e,n){n=n.checked,n!=null&&Qo(e,"checked",n,!1)}function ql(e,n){as(e,n);var t=fn(n.value),r=n.type;if(t!=null)r==="number"?(t===0&&e.value===""||e.value!=t)&&(e.value=""+t):e.value!==""+t&&(e.value=""+t);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}n.hasOwnProperty("value")?bl(e,n.type,t):n.hasOwnProperty("defaultValue")&&bl(e,n.type,fn(n.defaultValue)),n.checked==null&&n.defaultChecked!=null&&(e.defaultChecked=!!n.defaultChecked)}function Au(e,n,t){if(n.hasOwnProperty("value")||n.hasOwnProperty("defaultValue")){var r=n.type;if(!(r!=="submit"&&r!=="reset"||n.value!==void 0&&n.value!==null))return;n=""+e._wrapperState.initialValue,t||n===e.value||(e.value=n),e.defaultValue=n}t=e.name,t!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,t!==""&&(e.name=t)}function bl(e,n,t){(n!=="number"||Lr(e.ownerDocument)!==e)&&(t==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+t&&(e.defaultValue=""+t))}var gt=Array.isArray;function Qn(e,n,t,r){if(e=e.options,n){n={};for(var l=0;l<t.length;l++)n["$"+t[l]]=!0;for(t=0;t<e.length;t++)l=n.hasOwnProperty("$"+e[t].value),e[t].selected!==l&&(e[t].selected=l),l&&r&&(e[t].defaultSelected=!0)}else{for(t=""+fn(t),n=null,l=0;l<e.length;l++){if(e[l].value===t){e[l].selected=!0,r&&(e[l].defaultSelected=!0);return}n!==null||e[l].disabled||(n=e[l])}n!==null&&(n.selected=!0)}}function eo(e,n){if(n.dang
Line truncated
|
||||
`).replace(Wf,"")}function cr(e,n,t){if(n=ci(n),ci(e)!==n&&t)throw Error(y(425))}function Fr(){}var mo=null,vo=null;function ho(e,n){return e==="textarea"||e==="noscript"||typeof n.children=="string"||typeof n.children=="number"||typeof n.dangerouslySetInnerHTML=="object"&&n.dangerouslySetInnerHTML!==null&&n.dangerouslySetInnerHTML.__html!=null}var yo=typeof setTimeout=="function"?setTimeout:void 0,Qf=typeof clearTimeout=="function"?clearTimeout:void 0,fi=typeof Promise=="function"?Promise:void 0,Kf=typeof queueMicrotask=="function"?queueMicrotask:typeof fi<"u"?function(e){return fi.resolve(null).then(e).catch(Yf)}:yo;function Yf(e){setTimeout(function(){throw e})}function jl(e,n){var t=n,r=0;do{var l=t.nextSibling;if(e.removeChild(t),l&&l.nodeType===8)if(t=l.data,t==="/$"){if(r===0){e.removeChild(l),jt(n);return}r--}else t!=="$"&&t!=="$?"&&t!=="$!"||r++;t=l}while(t);jt(n)}function on(e){for(;e!=null;e=e.nextSibling){var n=e.nodeType;if(n===1||n===3)break;if(n===8){if(n=e.data,n==="$"||n==="$!"||n==="$?")break;if(n==="/$")return null}}return e}function di(e){e=e.previousSibling;for(var n=0;e;){if(e.nodeType===8){var t=e.data;if(t==="$"||t==="$!"||t==="$?"){if(n===0)return e;n--}else t==="/$"&&n++}e=e.previousSibling}return null}var ut=Math.random().toString(36).slice(2),Ie="__reactFiber$"+ut,Vt="__reactProps$"+ut,Qe="__reactContainer$"+ut,go="__reactEvents$"+ut,Xf="__reactListeners$"+ut,Gf="__reactHandles$"+ut;function Sn(e){var n=e[Ie];if(n)return n;for(var t=e.parentNode;t;){if(n=t[Qe]||t[Ie]){if(t=n.alternate,n.child!==null||t!==null&&t.child!==null)for(e=di(e);e!==null;){if(t=e[Ie])return t;e=di(e)}return n}e=t,t=e.parentNode}return null}function Zt(e){return e=e[Ie]||e[Qe],!e||e.tag!==5&&e.tag!==6&&e.tag!==13&&e.tag!==3?null:e}function Un(e){if(e.tag===5||e.tag===6)return e.stateNode;throw Error(y(33))}function ol(e){return e[Vt]||null}var wo=[],$n=-1;function mn(e){return{current:e}}function j(e){0>$n||(e.current=wo[$n],wo[$n]=null,$n--)}function M(e,n){$n++,wo[$n]=e.current,e.current=n}var dn={},re=mn(dn),fe=mn(!1),xn=dn;function qn(e,n){var t=e.type.contextTypes;if(!t)return dn;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===n)return r.__reactInternalMemoizedMaskedChildContext;var l={},o;for(o in t)l[o]=n[o];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=n,e.__reactInternalMemoizedMaskedChildContext=l),l}function de(e){return e=e.childContextTypes,e!=null}function Ur(){j(fe),j(re)}function pi(e,n,t){if(re.current!==dn)throw Error(y(168));M(re,n),M(fe,t)}function Js(e,n,t){var r=e.stateNode;if(n=n.childContextTypes,typeof r.getChildContext!="function")return t;r=r.getChildContext();for(var l in r)if(!(l in n))throw Error(y(108,Oc(e)||"Unknown",l));return V({},t,r)}function $r(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||dn,xn=re.current,M(re,e),M(fe,fe.current),!0}function mi(e,n,t){var r=e.stateNode;if(!r)throw Error(y(169));t?(e=Js(e,n,xn),r.__reactInternalMemoizedMergedChildContext=e,j(fe),j(re),M(re,e)):j(fe),M(fe,t)}var Ve=null,ul=!1,Il=!1;function qs(e){Ve===null?Ve=[e]:Ve.push(e)}function Zf(e){ul=!0,qs(e)}function vn(){if(!Il&&Ve!==null){Il=!0;var e=0,n=O;try{var t=Ve;for(O=1;e<t.length;e++){var r=t[e];do r=r(!0);while(r!==null)}Ve=null,ul=!1}catch(l){throw Ve!==null&&(Ve=Ve.slice(e+1)),Cs(Zo,vn),l}finally{O=n,Il=!1}}return null}var Vn=[],An=0,Vr=null,Ar=0,Se=[],ke=0,Pn=null,Ae=1,Be="";function gn(e,n){Vn[An++]=Ar,Vn[An++]=Vr,Vr=e,Ar=n}function bs(e,n,t){Se[ke++]=Ae,Se[ke++]=Be,Se[ke++]=Pn,Pn=e;var r=Ae;e=Be;var l=32-Re(r)-1;r&=~(1<<l),t+=1;var o=32-Re(n)+l;if(30<o){var u=l-l%5;o=(r&(1<<u)-1).toString(32),r>>=u,l-=u,Ae=1<<32-Re(n)+l|t<<l|r,Be=o+e}else Ae=1<<o|t<<l|r,Be=e}function ou(e){e.return!==null&&(gn(e,1),bs(e,1,0))}function uu(e){for(;e===Vr;)Vr=Vn[--An],Vn[An]=null,Ar=Vn[--An],Vn[An]=null;for(;e===Pn;)Pn=Se[--ke],Se[ke]=null,Be=Se[--ke],Se[ke]=null,Ae=Se[--ke],Se[ke]=null}var he=null,ve=null,F=!1,Te=null;function ea(e,n){var t=Ee(5,null,null,0);t.elementType="DELETED",t.stateNode=n,t.return=e,n=e.deletions,n===null?(e.deletions=[t],e.flags|=16):n.push(t)}function vi(e,n){switch(e.tag){case 5:var t=e.type;return n=n.nodeType!==1||t.toLowerCase()!==n.nodeName.toLowerCase()?null:n,n!==null?(e.stateNode=n,he=e,ve=on(n.firstChild),!0):!1;case 6:return n=e.pendingProps===""||n.nodeType!==3?null:n,n!==null?(e.stateNode=n,he=e,ve=null,!0):!1;case 13:return n=n.nodeType!==8?null:n,n!==null?(t=Pn!==null?{id:Ae,overflow:Be}:null,e.memoizedState={dehydrated:n,treeContext:t,retryLane:1073741824},t=Ee(18,null,null,0),t.stateNode=n,t.return=e,e.child=t,he=e,ve=null,!0):!1;default:return!1}}function So(e){return(e.mode&1)!==0&&(e.flags&128)===0}function ko(e){if(F){var n=ve;if(n){var t=n;if(!vi(e,n)){if(So(e))throw Error(y(418));n=on(t.nextSibling);var r=he;n&&vi(e,n)?ea(r,t):(e.flags=e.flags&-4097|2,F=!1,he=e)}}else{if(So(e))throw Error(y(418));e.flags=e.flags&-4097|2,F=!1,he=e}}}function hi(e){for(e=e.return;e!==null&&e.t
Line truncated
|
||||
Error generating stack: `+o.message+`
|
||||
`+o.stack}return{value:e,source:n,stack:l,digest:null}}function Al(e,n,t){return{value:e,source:null,stack:t??null,digest:n??null}}function xo(e,n){try{console.error(n.value)}catch(t){setTimeout(function(){throw t})}}var od=typeof WeakMap=="function"?WeakMap:Map;function Na(e,n,t){t=He(-1,t),t.tag=3,t.payload={element:null};var r=n.value;return t.callback=function(){Gr||(Gr=!0,jo=r),xo(e,n)},t}function za(e,n,t){t=He(-1,t),t.tag=3;var r=e.type.getDerivedStateFromError;if(typeof r=="function"){var l=n.value;t.payload=function(){return r(l)},t.callback=function(){xo(e,n)}}var o=e.stateNode;return o!==null&&typeof o.componentDidCatch=="function"&&(t.callback=function(){xo(e,n),typeof r!="function"&&(sn===null?sn=new Set([this]):sn.add(this));var u=n.stack;this.componentDidCatch(n.value,{componentStack:u!==null?u:""})}),t}function _i(e,n,t){var r=e.pingCache;if(r===null){r=e.pingCache=new od;var l=new Set;r.set(n,l)}else l=r.get(n),l===void 0&&(l=new Set,r.set(n,l));l.has(t)||(l.add(t),e=wd.bind(null,e,n,t),n.then(e,e))}function xi(e){do{var n;if((n=e.tag===13)&&(n=e.memoizedState,n=n!==null?n.dehydrated!==null:!0),n)return e;e=e.return}while(e!==null);return null}function Pi(e,n,t,r,l){return e.mode&1?(e.flags|=65536,e.lanes=l,e):(e===n?e.flags|=65536:(e.flags|=128,t.flags|=131072,t.flags&=-52805,t.tag===1&&(t.alternate===null?t.tag=17:(n=He(-1,1),n.tag=2,un(t,n,1))),t.lanes|=1),e)}var ud=Xe.ReactCurrentOwner,ce=!1;function oe(e,n,t,r){n.child=e===null?ra(n,null,t,r):et(n,e.child,t,r)}function Ni(e,n,t,r,l){t=t.render;var o=n.ref;return Gn(n,l),r=yu(e,n,t,r,o,l),t=gu(),e!==null&&!ce?(n.updateQueue=e.updateQueue,n.flags&=-2053,e.lanes&=~l,Ye(e,n,l)):(F&&t&&ou(n),n.flags|=1,oe(e,n,r,l),n.child)}function zi(e,n,t,r,l){if(e===null){var o=t.type;return typeof o=="function"&&!zu(o)&&o.defaultProps===void 0&&t.compare===null&&t.defaultProps===void 0?(n.tag=15,n.type=o,La(e,n,o,r,l)):(e=zr(t.type,null,r,n,n.mode,l),e.ref=n.ref,e.return=n,n.child=e)}if(o=e.child,!(e.lanes&l)){var u=o.memoizedProps;if(t=t.compare,t=t!==null?t:Ft,t(u,r)&&e.ref===n.ref)return Ye(e,n,l)}return n.flags|=1,e=cn(o,r),e.ref=n.ref,e.return=n,n.child=e}function La(e,n,t,r,l){if(e!==null){var o=e.memoizedProps;if(Ft(o,r)&&e.ref===n.ref)if(ce=!1,n.pendingProps=r=o,(e.lanes&l)!==0)e.flags&131072&&(ce=!0);else return n.lanes=e.lanes,Ye(e,n,l)}return Po(e,n,t,r,l)}function Ta(e,n,t){var r=n.pendingProps,l=r.children,o=e!==null?e.memoizedState:null;if(r.mode==="hidden")if(!(n.mode&1))n.memoizedState={baseLanes:0,cachePool:null,transitions:null},M(Wn,me),me|=t;else{if(!(t&1073741824))return e=o!==null?o.baseLanes|t:t,n.lanes=n.childLanes=1073741824,n.memoizedState={baseLanes:e,cachePool:null,transitions:null},n.updateQueue=null,M(Wn,me),me|=e,null;n.memoizedState={baseLanes:0,cachePool:null,transitions:null},r=o!==null?o.baseLanes:t,M(Wn,me),me|=r}else o!==null?(r=o.baseLanes|t,n.memoizedState=null):r=t,M(Wn,me),me|=r;return oe(e,n,l,t),n.child}function Ra(e,n){var t=n.ref;(e===null&&t!==null||e!==null&&e.ref!==t)&&(n.flags|=512,n.flags|=2097152)}function Po(e,n,t,r,l){var o=de(t)?xn:re.current;return o=qn(n,o),Gn(n,l),t=yu(e,n,t,r,o,l),r=gu(),e!==null&&!ce?(n.updateQueue=e.updateQueue,n.flags&=-2053,e.lanes&=~l,Ye(e,n,l)):(F&&r&&ou(n),n.flags|=1,oe(e,n,t,l),n.child)}function Li(e,n,t,r,l){if(de(t)){var o=!0;$r(n)}else o=!1;if(Gn(n,l),n.stateNode===null)xr(e,n),Pa(n,t,r),_o(n,t,r,l),r=!0;else if(e===null){var u=n.stateNode,i=n.memoizedProps;u.props=i;var s=u.context,c=t.contextType;typeof c=="object"&&c!==null?c=_e(c):(c=de(t)?xn:re.current,c=qn(n,c));var v=t.getDerivedStateFromProps,m=typeof v=="function"||typeof u.getSnapshotBeforeUpdate=="function";m||typeof u.UNSAFE_componentWillReceiveProps!="function"&&typeof u.componentWillReceiveProps!="function"||(i!==r||s!==c)&&Ci(n,u,r,c),Je=!1;var p=n.memoizedState;u.state=p,Wr(n,r,u,l),s=n.memoizedState,i!==r||p!==s||fe.current||Je?(typeof v=="function"&&(Co(n,t,v,r),s=n.memoizedState),(i=Je||Ei(n,t,i,r,p,s,c))?(m||typeof u.UNSAFE_componentWillMount!="function"&&typeof u.componentWillMount!="function"||(typeof u.componentWillMount=="function"&&u.componentWillMount(),typeof u.UNSAFE_componentWillMount=="function"&&u.UNSAFE_componentWillMount()),typeof u.componentDidMount=="function"&&(n.flags|=4194308)):(typeof u.componentDidMount=="function"&&(n.flags|=4194308),n.memoizedProps=r,n.memoizedState=s),u.props=r,u.state=s,u.context=c,r=i):(typeof u.componentDidMount=="function"&&(n.flags|=4194308),r=!1)}else{u=n.stateNode,oa(e,n),i=n.memoizedProps,c=n.type===n.elementType?i:ze(n.type,i),u.props=c,m=n.pendingProps,p=u.context,s=t.contextType,typeof s=="object"&&s!==null?s=_e(s):(s=de(t)?xn:re.current,s=qn(n,s));var g=t.getDerivedStateFromProps;(v=typeof g=="function"||typeof u.getSnapshotBeforeUpdate=="function")||typeof u.UNSAFE_componentWillReceiveProps!="function"&&typeof u.componentWillReceiveProps!="function"||(i!==m||p!==s)&&Ci(n,u,r,s),Je=!1,p=n.memoizedState,u.state=p,Wr(n,r,u,l);var w=n.memoizedState;i
Line truncated
|
||||
@@ -1 +0,0 @@
|
||||
#root{max-width:1280px;margin:0 auto;padding:2rem;text-align:center}.logo{height:6em;padding:1.5em;will-change:filter;transition:filter .3s}.logo:hover{filter:drop-shadow(0 0 2em #646cffaa)}.logo.react:hover{filter:drop-shadow(0 0 2em #61dafbaa)}@keyframes logo-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@media (prefers-reduced-motion: no-preference){a:nth-of-type(2) .logo{animation:logo-spin infinite 20s linear}}.card{padding:2em}.read-the-docs{color:#888}:root{font-family:Inter,system-ui,Avenir,Helvetica,Arial,sans-serif;line-height:1.5;font-weight:400;color-scheme:light dark;color:#ffffffde;background-color:#242424;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}a{font-weight:500;color:#646cff;text-decoration:inherit}a:hover{color:#535bf2}body{margin:0;display:flex;place-items:center;min-width:320px;min-height:100vh}h1{font-size:3.2em;line-height:1.1}button{border-radius:8px;border:1px solid transparent;padding:.6em 1.2em;font-size:1em;font-weight:500;font-family:inherit;background-color:#1a1a1a;cursor:pointer;transition:border-color .25s}button:hover{border-color:#646cff}button:focus,button:focus-visible{outline:4px auto -webkit-focus-ring-color}@media (prefers-color-scheme: light){:root{color:#213547;background-color:#fff}a:hover{color:#747bff}button{background-color:#f9f9f9}}
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 4.0 KiB |
@@ -1,14 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/phpCourse/exam/pages/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + React + TS</title>
|
||||
<script type="module" crossorigin src="/phpCourse/exam/pages/assets/index-D81sf-ye.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/phpCourse/exam/pages/assets/index-DiwrgTda.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1,18 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: { browser: true, es2020: true },
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:react-hooks/recommended',
|
||||
],
|
||||
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['react-refresh'],
|
||||
rules: {
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"printWidth": 120,
|
||||
"tabWidth": 2,
|
||||
"semi": true,
|
||||
"trailingComma": "es5",
|
||||
"singleQuote": true,
|
||||
"arrowParens": "always",
|
||||
"plugins": ["prettier-plugin-organize-imports"]
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
# React + TypeScript + Vite
|
||||
|
||||
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||
|
||||
Currently, two official plugins are available:
|
||||
|
||||
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
||||
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
||||
|
||||
## Expanding the ESLint configuration
|
||||
|
||||
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
|
||||
|
||||
- Configure the top-level `parserOptions` property like this:
|
||||
|
||||
```js
|
||||
export default {
|
||||
// other rules...
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
project: ['./tsconfig.json', './tsconfig.node.json', './tsconfig.app.json'],
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
|
||||
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
|
||||
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
|
||||
@@ -1,13 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + React + TS</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,30 +0,0 @@
|
||||
{
|
||||
"name": "react",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build --emptyOutDir",
|
||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.15.0",
|
||||
"@typescript-eslint/parser": "^7.15.0",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"eslint-plugin-react-refresh": "^0.4.7",
|
||||
"prettier": "^3.2.5",
|
||||
"prettier-plugin-organize-imports": "^3.2.4",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^5.3.4"
|
||||
}
|
||||
}
|
||||
Generated
-2173
File diff suppressed because it is too large.
Load diff
@@ -1,23 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
|
||||
<head>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
|
||||
<title>
|
||||
Page not Found
|
||||
</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="site-content" class="site-content">
|
||||
<div class="p-5 mb-4 bg-light rounded-3">
|
||||
<div class="container-fluid py-5">
|
||||
<h1 class="display-5 fw-bold">404 Page not Found</h1>
|
||||
<p class="col-md-8 fs-4">Page does not exist.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,23 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
|
||||
<head>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
|
||||
<title>
|
||||
Server Error
|
||||
</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="site-content" class="site-content">
|
||||
<div class="p-5 mb-4 bg-light rounded-3">
|
||||
<div class="container-fluid py-5">
|
||||
<h1 class="display-5 fw-bold">500 Internal Server Error</h1>
|
||||
<pre class="col-md-8 fs-4"><?= $e->getMessage() ?></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1,42 +0,0 @@
|
||||
#root {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 6em;
|
||||
padding: 1.5em;
|
||||
will-change: filter;
|
||||
transition: filter 300ms;
|
||||
}
|
||||
.logo:hover {
|
||||
filter: drop-shadow(0 0 2em #646cffaa);
|
||||
}
|
||||
.logo.react:hover {
|
||||
filter: drop-shadow(0 0 2em #61dafbaa);
|
||||
}
|
||||
|
||||
@keyframes logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
a:nth-of-type(2) .logo {
|
||||
animation: logo-spin infinite 20s linear;
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
.read-the-docs {
|
||||
color: #888;
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import { useState } from 'react'
|
||||
import reactLogo from './assets/react.svg'
|
||||
import viteLogo from '/vite.svg'
|
||||
import './App.css'
|
||||
|
||||
function App() {
|
||||
const [count, setCount] = useState(0)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<a href="https://vitejs.dev" target="_blank">
|
||||
<img src={viteLogo} className="logo" alt="Vite logo" />
|
||||
</a>
|
||||
<a href="https://react.dev" target="_blank">
|
||||
<img src={reactLogo} className="logo react" alt="React logo" />
|
||||
</a>
|
||||
</div>
|
||||
<h1>Vite + React</h1>
|
||||
<div className="card">
|
||||
<button onClick={() => setCount((count) => count + 1)}>
|
||||
count is {count}
|
||||
</button>
|
||||
<p>
|
||||
Edit <code>src/App.tsx</code> and save to test HMR
|
||||
</p>
|
||||
</div>
|
||||
<p className="read-the-docs">
|
||||
Click on the Vite and React logos to learn more
|
||||
</p>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 4.0 KiB |
@@ -1,68 +0,0 @@
|
||||
:root {
|
||||
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
|
||||
color-scheme: light dark;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
background-color: #242424;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
color: #646cff;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
a:hover {
|
||||
color: #535bf2;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
place-items: center;
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3.2em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
padding: 0.6em 1.2em;
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
background-color: #1a1a1a;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.25s;
|
||||
}
|
||||
button:hover {
|
||||
border-color: #646cff;
|
||||
}
|
||||
button:focus,
|
||||
button:focus-visible {
|
||||
outline: 4px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
color: #213547;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
a:hover {
|
||||
color: #747bff;
|
||||
}
|
||||
button {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App.tsx'
|
||||
import './index.css'
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
)
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
/// <reference types="vite/client" />
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.node.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"noEmit": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
build: {
|
||||
outDir: '../pages',
|
||||
},
|
||||
base: 'phpCourse/exam/pages',
|
||||
});
|
||||
@@ -1,3 +0,0 @@
|
||||
Order deny,allow
|
||||
Deny from all
|
||||
Allow from 127.0.0.1
|
||||
@@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
$path = ltrim(str_replace(Config::getBasePath(), "", $_SERVER['REQUEST_URI']), "/");
|
||||
|
||||
try {
|
||||
$segments = PathParams::get();
|
||||
|
||||
if ($segments[0] === "api") {
|
||||
if ($segments[1] !== "docs") {
|
||||
require_once __DIR__ . "/../api/{$segments[1]}/index.php";
|
||||
} else {
|
||||
Headers::redirect("index.html");
|
||||
}
|
||||
} else {
|
||||
require_once __DIR__ . "/../pages/index.html";
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
require_once __DIR__ . "/../pages/500.php";
|
||||
}
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
Order deny,allow
|
||||
Deny from all
|
||||
Allow from 127.0.0.1
|
||||
Vendored
-28
@@ -1,28 +0,0 @@
|
||||
<?php
|
||||
|
||||
class Auth
|
||||
{
|
||||
public static function hasPermission(string $required)
|
||||
{
|
||||
$db = Database::getInstance();
|
||||
|
||||
if (!isset($_SERVER["HTTP_TOKEN"])) return false;
|
||||
$token = $_SERVER["HTTP_TOKEN"];
|
||||
|
||||
$query =
|
||||
"SELECT
|
||||
UserPermissions.Permission
|
||||
FROM
|
||||
UserPermissions, Users
|
||||
WHERE
|
||||
Users.ID = UserPermissions.fkUserID AND
|
||||
Users.Token = :TOKEN";
|
||||
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->bindValue(":TOKEN", $token);
|
||||
$stmt->execute();
|
||||
$perms = $stmt->fetchAll(PDO::FETCH_COLUMN, 0);
|
||||
|
||||
return in_array($required, $perms);
|
||||
}
|
||||
}
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
if (PHP_VERSION_ID < 50600) {
|
||||
if (!headers_sent()) {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
}
|
||||
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
|
||||
if (!ini_get('display_errors')) {
|
||||
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||
fwrite(STDERR, $err);
|
||||
} elseif (!headers_sent()) {
|
||||
echo $err;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
$err,
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInitd067465d9a99de373ba3093d61eda1d8::getLoader();
|
||||
Vendored
-7
@@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
spl_autoload_register(function ($name) {
|
||||
$_name = lcfirst($name);
|
||||
|
||||
require_once __DIR__ . "/$_name/$_name.php";
|
||||
});
|
||||
Vendored
+579
@@ -0,0 +1,579 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
/**
|
||||
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||
*
|
||||
* $loader = new \Composer\Autoload\ClassLoader();
|
||||
*
|
||||
* // register classes with namespaces
|
||||
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||
* $loader->add('Symfony', __DIR__.'/framework');
|
||||
*
|
||||
* // activate the autoloader
|
||||
* $loader->register();
|
||||
*
|
||||
* // to enable searching the include path (eg. for PEAR packages)
|
||||
* $loader->setUseIncludePath(true);
|
||||
*
|
||||
* In this example, if you try to use a class in the Symfony\Component
|
||||
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||
* the autoloader will first look for the class under the component/
|
||||
* directory, and it will then fallback to the framework/ directory if not
|
||||
* found before giving up.
|
||||
*
|
||||
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @see https://www.php-fig.org/psr/psr-0/
|
||||
* @see https://www.php-fig.org/psr/psr-4/
|
||||
*/
|
||||
class ClassLoader
|
||||
{
|
||||
/** @var \Closure(string):void */
|
||||
private static $includeFile;
|
||||
|
||||
/** @var string|null */
|
||||
private $vendorDir;
|
||||
|
||||
// PSR-4
|
||||
/**
|
||||
* @var array<string, array<string, int>>
|
||||
*/
|
||||
private $prefixLengthsPsr4 = array();
|
||||
/**
|
||||
* @var array<string, list<string>>
|
||||
*/
|
||||
private $prefixDirsPsr4 = array();
|
||||
/**
|
||||
* @var list<string>
|
||||
*/
|
||||
private $fallbackDirsPsr4 = array();
|
||||
|
||||
// PSR-0
|
||||
/**
|
||||
* List of PSR-0 prefixes
|
||||
*
|
||||
* Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
|
||||
*
|
||||
* @var array<string, array<string, list<string>>>
|
||||
*/
|
||||
private $prefixesPsr0 = array();
|
||||
/**
|
||||
* @var list<string>
|
||||
*/
|
||||
private $fallbackDirsPsr0 = array();
|
||||
|
||||
/** @var bool */
|
||||
private $useIncludePath = false;
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private $classMap = array();
|
||||
|
||||
/** @var bool */
|
||||
private $classMapAuthoritative = false;
|
||||
|
||||
/**
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
private $missingClasses = array();
|
||||
|
||||
/** @var string|null */
|
||||
private $apcuPrefix;
|
||||
|
||||
/**
|
||||
* @var array<string, self>
|
||||
*/
|
||||
private static $registeredLoaders = array();
|
||||
|
||||
/**
|
||||
* @param string|null $vendorDir
|
||||
*/
|
||||
public function __construct($vendorDir = null)
|
||||
{
|
||||
$this->vendorDir = $vendorDir;
|
||||
self::initializeIncludeClosure();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, list<string>>
|
||||
*/
|
||||
public function getPrefixes()
|
||||
{
|
||||
if (!empty($this->prefixesPsr0)) {
|
||||
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, list<string>>
|
||||
*/
|
||||
public function getPrefixesPsr4()
|
||||
{
|
||||
return $this->prefixDirsPsr4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public function getFallbackDirs()
|
||||
{
|
||||
return $this->fallbackDirsPsr0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public function getFallbackDirsPsr4()
|
||||
{
|
||||
return $this->fallbackDirsPsr4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string> Array of classname => path
|
||||
*/
|
||||
public function getClassMap()
|
||||
{
|
||||
return $this->classMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $classMap Class to filename map
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addClassMap(array $classMap)
|
||||
{
|
||||
if ($this->classMap) {
|
||||
$this->classMap = array_merge($this->classMap, $classMap);
|
||||
} else {
|
||||
$this->classMap = $classMap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix, either
|
||||
* appending or prepending to the ones previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param list<string>|string $paths The PSR-0 root directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add($prefix, $paths, $prepend = false)
|
||||
{
|
||||
$paths = (array) $paths;
|
||||
if (!$prefix) {
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$paths,
|
||||
$this->fallbackDirsPsr0
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$this->fallbackDirsPsr0,
|
||||
$paths
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$first = $prefix[0];
|
||||
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||
$this->prefixesPsr0[$first][$prefix] = $paths;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($prepend) {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$paths,
|
||||
$this->prefixesPsr0[$first][$prefix]
|
||||
);
|
||||
} else {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$this->prefixesPsr0[$first][$prefix],
|
||||
$paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace, either
|
||||
* appending or prepending to the ones previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param list<string>|string $paths The PSR-4 base directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addPsr4($prefix, $paths, $prepend = false)
|
||||
{
|
||||
$paths = (array) $paths;
|
||||
if (!$prefix) {
|
||||
// Register directories for the root namespace.
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$paths,
|
||||
$this->fallbackDirsPsr4
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$this->fallbackDirsPsr4,
|
||||
$paths
|
||||
);
|
||||
}
|
||||
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = $paths;
|
||||
} elseif ($prepend) {
|
||||
// Prepend directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$paths,
|
||||
$this->prefixDirsPsr4[$prefix]
|
||||
);
|
||||
} else {
|
||||
// Append directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$this->prefixDirsPsr4[$prefix],
|
||||
$paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix,
|
||||
* replacing any others previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param list<string>|string $paths The PSR-0 base directories
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr0 = (array) $paths;
|
||||
} else {
|
||||
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace,
|
||||
* replacing any others previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param list<string>|string $paths The PSR-4 base directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setPsr4($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr4 = (array) $paths;
|
||||
} else {
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns on searching the include path for class files.
|
||||
*
|
||||
* @param bool $useIncludePath
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setUseIncludePath($useIncludePath)
|
||||
{
|
||||
$this->useIncludePath = $useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to check if the autoloader uses the include path to check
|
||||
* for classes.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getUseIncludePath()
|
||||
{
|
||||
return $this->useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns off searching the prefix and fallback directories for classes
|
||||
* that have not been registered with the class map.
|
||||
*
|
||||
* @param bool $classMapAuthoritative
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||
{
|
||||
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should class lookup fail if not found in the current class map?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isClassMapAuthoritative()
|
||||
{
|
||||
return $this->classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||
*
|
||||
* @param string|null $apcuPrefix
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setApcuPrefix($apcuPrefix)
|
||||
{
|
||||
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getApcuPrefix()
|
||||
{
|
||||
return $this->apcuPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this instance as an autoloader.
|
||||
*
|
||||
* @param bool $prepend Whether to prepend the autoloader or not
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register($prepend = false)
|
||||
{
|
||||
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||
|
||||
if (null === $this->vendorDir) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($prepend) {
|
||||
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
||||
} else {
|
||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||
self::$registeredLoaders[$this->vendorDir] = $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters this instance as an autoloader.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function unregister()
|
||||
{
|
||||
spl_autoload_unregister(array($this, 'loadClass'));
|
||||
|
||||
if (null !== $this->vendorDir) {
|
||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given class or interface.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
* @return true|null True if loaded, null otherwise
|
||||
*/
|
||||
public function loadClass($class)
|
||||
{
|
||||
if ($file = $this->findFile($class)) {
|
||||
$includeFile = self::$includeFile;
|
||||
$includeFile($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the path to the file where the class is defined.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
*
|
||||
* @return string|false The path if found, false otherwise
|
||||
*/
|
||||
public function findFile($class)
|
||||
{
|
||||
// class map lookup
|
||||
if (isset($this->classMap[$class])) {
|
||||
return $this->classMap[$class];
|
||||
}
|
||||
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||
return false;
|
||||
}
|
||||
if (null !== $this->apcuPrefix) {
|
||||
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||
if ($hit) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
$file = $this->findFileWithExtension($class, '.php');
|
||||
|
||||
// Search for Hack files if we are running on HHVM
|
||||
if (false === $file && defined('HHVM_VERSION')) {
|
||||
$file = $this->findFileWithExtension($class, '.hh');
|
||||
}
|
||||
|
||||
if (null !== $this->apcuPrefix) {
|
||||
apcu_add($this->apcuPrefix.$class, $file);
|
||||
}
|
||||
|
||||
if (false === $file) {
|
||||
// Remember that this class does not exist.
|
||||
$this->missingClasses[$class] = true;
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently registered loaders keyed by their corresponding vendor directories.
|
||||
*
|
||||
* @return array<string, self>
|
||||
*/
|
||||
public static function getRegisteredLoaders()
|
||||
{
|
||||
return self::$registeredLoaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $class
|
||||
* @param string $ext
|
||||
* @return string|false
|
||||
*/
|
||||
private function findFileWithExtension($class, $ext)
|
||||
{
|
||||
// PSR-4 lookup
|
||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||
|
||||
$first = $class[0];
|
||||
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||
$subPath = $class;
|
||||
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||
$subPath = substr($subPath, 0, $lastPos);
|
||||
$search = $subPath . '\\';
|
||||
if (isset($this->prefixDirsPsr4[$search])) {
|
||||
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||
if (file_exists($file = $dir . $pathEnd)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-4 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 lookup
|
||||
if (false !== $pos = strrpos($class, '\\')) {
|
||||
// namespaced class name
|
||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||
} else {
|
||||
// PEAR-like class name
|
||||
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||
}
|
||||
|
||||
if (isset($this->prefixesPsr0[$first])) {
|
||||
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 include paths.
|
||||
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
private static function initializeIncludeClosure()
|
||||
{
|
||||
if (self::$includeFile !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope isolated include.
|
||||
*
|
||||
* Prevents access to $this/self from included files.
|
||||
*
|
||||
* @param string $file
|
||||
* @return void
|
||||
*/
|
||||
self::$includeFile = \Closure::bind(static function($file) {
|
||||
include $file;
|
||||
}, null, null);
|
||||
}
|
||||
}
|
||||
+359
@@ -0,0 +1,359 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer;
|
||||
|
||||
use Composer\Autoload\ClassLoader;
|
||||
use Composer\Semver\VersionParser;
|
||||
|
||||
/**
|
||||
* This class is copied in every Composer installed project and available to all
|
||||
*
|
||||
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
|
||||
*
|
||||
* To require its presence, you can require `composer-runtime-api ^2.0`
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class InstalledVersions
|
||||
{
|
||||
/**
|
||||
* @var mixed[]|null
|
||||
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
|
||||
*/
|
||||
private static $installed;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
private static $canGetVendors;
|
||||
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
private static $installedByVendor = array();
|
||||
|
||||
/**
|
||||
* Returns a list of all package names which are present, either by being installed, replaced or provided
|
||||
*
|
||||
* @return string[]
|
||||
* @psalm-return list<string>
|
||||
*/
|
||||
public static function getInstalledPackages()
|
||||
{
|
||||
$packages = array();
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
$packages[] = array_keys($installed['versions']);
|
||||
}
|
||||
|
||||
if (1 === \count($packages)) {
|
||||
return $packages[0];
|
||||
}
|
||||
|
||||
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all package names with a specific type e.g. 'library'
|
||||
*
|
||||
* @param string $type
|
||||
* @return string[]
|
||||
* @psalm-return list<string>
|
||||
*/
|
||||
public static function getInstalledPackagesByType($type)
|
||||
{
|
||||
$packagesByType = array();
|
||||
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
foreach ($installed['versions'] as $name => $package) {
|
||||
if (isset($package['type']) && $package['type'] === $type) {
|
||||
$packagesByType[] = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $packagesByType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given package is installed
|
||||
*
|
||||
* This also returns true if the package name is provided or replaced by another package
|
||||
*
|
||||
* @param string $packageName
|
||||
* @param bool $includeDevRequirements
|
||||
* @return bool
|
||||
*/
|
||||
public static function isInstalled($packageName, $includeDevRequirements = true)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (isset($installed['versions'][$packageName])) {
|
||||
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given package satisfies a version constraint
|
||||
*
|
||||
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
|
||||
*
|
||||
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
|
||||
*
|
||||
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
|
||||
* @param string $packageName
|
||||
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
|
||||
* @return bool
|
||||
*/
|
||||
public static function satisfies(VersionParser $parser, $packageName, $constraint)
|
||||
{
|
||||
$constraint = $parser->parseConstraints((string) $constraint);
|
||||
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
|
||||
|
||||
return $provided->matches($constraint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a version constraint representing all the range(s) which are installed for a given package
|
||||
*
|
||||
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
|
||||
* whether a given version of a package is installed, and not just whether it exists
|
||||
*
|
||||
* @param string $packageName
|
||||
* @return string Version constraint usable with composer/semver
|
||||
*/
|
||||
public static function getVersionRanges($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ranges = array();
|
||||
if (isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
|
||||
}
|
||||
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
|
||||
}
|
||||
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
|
||||
}
|
||||
if (array_key_exists('provided', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
|
||||
}
|
||||
|
||||
return implode(' || ', $ranges);
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||
*/
|
||||
public static function getVersion($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['version'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['version'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||
*/
|
||||
public static function getPrettyVersion($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['pretty_version'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
|
||||
*/
|
||||
public static function getReference($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['reference'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['reference'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
|
||||
*/
|
||||
public static function getInstallPath($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
|
||||
*/
|
||||
public static function getRootPackage()
|
||||
{
|
||||
$installed = self::getInstalled();
|
||||
|
||||
return $installed[0]['root'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw installed.php data for custom implementations
|
||||
*
|
||||
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
|
||||
* @return array[]
|
||||
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
|
||||
*/
|
||||
public static function getRawData()
|
||||
{
|
||||
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
|
||||
|
||||
if (null === self::$installed) {
|
||||
// only require the installed.php file if this file is loaded from its dumped location,
|
||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||
self::$installed = include __DIR__ . '/installed.php';
|
||||
} else {
|
||||
self::$installed = array();
|
||||
}
|
||||
}
|
||||
|
||||
return self::$installed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw data of all installed.php which are currently loaded for custom implementations
|
||||
*
|
||||
* @return array[]
|
||||
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
public static function getAllRawData()
|
||||
{
|
||||
return self::getInstalled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lets you reload the static array from another file
|
||||
*
|
||||
* This is only useful for complex integrations in which a project needs to use
|
||||
* this class but then also needs to execute another project's autoloader in process,
|
||||
* and wants to ensure both projects have access to their version of installed.php.
|
||||
*
|
||||
* A typical case would be PHPUnit, where it would need to make sure it reads all
|
||||
* the data it needs from this class, then call reload() with
|
||||
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
|
||||
* the project in which it runs can then also use this class safely, without
|
||||
* interference between PHPUnit's dependencies and the project's dependencies.
|
||||
*
|
||||
* @param array[] $data A vendor/composer/installed.php data set
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
|
||||
*/
|
||||
public static function reload($data)
|
||||
{
|
||||
self::$installed = $data;
|
||||
self::$installedByVendor = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
private static function getInstalled()
|
||||
{
|
||||
if (null === self::$canGetVendors) {
|
||||
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
|
||||
}
|
||||
|
||||
$installed = array();
|
||||
|
||||
if (self::$canGetVendors) {
|
||||
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
||||
if (isset(self::$installedByVendor[$vendorDir])) {
|
||||
$installed[] = self::$installedByVendor[$vendorDir];
|
||||
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
||||
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||
$required = require $vendorDir.'/composer/installed.php';
|
||||
$installed[] = self::$installedByVendor[$vendorDir] = $required;
|
||||
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
|
||||
self::$installed = $installed[count($installed) - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (null === self::$installed) {
|
||||
// only require the installed.php file if this file is loaded from its dumped location,
|
||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||
$required = require __DIR__ . '/installed.php';
|
||||
self::$installed = $required;
|
||||
} else {
|
||||
self::$installed = array();
|
||||
}
|
||||
}
|
||||
|
||||
if (self::$installed !== array()) {
|
||||
$installed[] = self::$installed;
|
||||
}
|
||||
|
||||
return $installed;
|
||||
}
|
||||
}
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
|
||||
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
// autoload_classmap.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||
);
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
);
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
// autoload_psr4.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Pecee\\' => array($vendorDir . '/pecee/simple-router/src/Pecee'),
|
||||
);
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInitd067465d9a99de373ba3093d61eda1d8
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
public static function loadClassLoader($class)
|
||||
{
|
||||
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||
require __DIR__ . '/ClassLoader.php';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Composer\Autoload\ClassLoader
|
||||
*/
|
||||
public static function getLoader()
|
||||
{
|
||||
if (null !== self::$loader) {
|
||||
return self::$loader;
|
||||
}
|
||||
|
||||
require __DIR__ . '/platform_check.php';
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInitd067465d9a99de373ba3093d61eda1d8', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInitd067465d9a99de373ba3093d61eda1d8', 'loadClassLoader'));
|
||||
|
||||
require __DIR__ . '/autoload_static.php';
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInitd067465d9a99de373ba3093d61eda1d8::getInitializer($loader));
|
||||
|
||||
$loader->register(true);
|
||||
|
||||
return $loader;
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
// autoload_static.php @generated by Composer
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
class ComposerStaticInitd067465d9a99de373ba3093d61eda1d8
|
||||
{
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
'P' =>
|
||||
array (
|
||||
'Pecee\\' => 6,
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixDirsPsr4 = array (
|
||||
'Pecee\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/pecee/simple-router/src/Pecee',
|
||||
),
|
||||
);
|
||||
|
||||
public static $classMap = array (
|
||||
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
||||
);
|
||||
|
||||
public static function getInitializer(ClassLoader $loader)
|
||||
{
|
||||
return \Closure::bind(function () use ($loader) {
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInitd067465d9a99de373ba3093d61eda1d8::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInitd067465d9a99de373ba3093d61eda1d8::$prefixDirsPsr4;
|
||||
$loader->classMap = ComposerStaticInitd067465d9a99de373ba3093d61eda1d8::$classMap;
|
||||
|
||||
}, null, ClassLoader::class);
|
||||
}
|
||||
}
|
||||
Vendored
+72
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"packages": [
|
||||
{
|
||||
"name": "pecee/simple-router",
|
||||
"version": "5.4.1.7",
|
||||
"version_normalized": "5.4.1.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/skipperbent/simple-php-router.git",
|
||||
"reference": "a2843d5b1e037f8b61cc99f27eab52a28bf41dfd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/skipperbent/simple-php-router/zipball/a2843d5b1e037f8b61cc99f27eab52a28bf41dfd",
|
||||
"reference": "a2843d5b1e037f8b61cc99f27eab52a28bf41dfd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"php": ">=7.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1",
|
||||
"phpstan/phpstan": "^1",
|
||||
"phpstan/phpstan-deprecation-rules": "^1",
|
||||
"phpstan/phpstan-phpunit": "^1",
|
||||
"phpstan/phpstan-strict-rules": "^1",
|
||||
"phpunit/phpunit": "^8"
|
||||
},
|
||||
"time": "2023-12-11T21:48:25+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Pecee\\": "src/Pecee/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Simon Sessingø",
|
||||
"email": "simon.sessingoe@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "Simple, fast PHP router that is easy to get integrated and in almost any project. Heavily inspired by the Laravel router.",
|
||||
"keywords": [
|
||||
"framework",
|
||||
"input-handler",
|
||||
"laravel",
|
||||
"pecee",
|
||||
"php",
|
||||
"request-handler",
|
||||
"route",
|
||||
"router",
|
||||
"routing",
|
||||
"routing-engine",
|
||||
"simple-php-router",
|
||||
"url-handling"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/skipperbent/simple-php-router/issues",
|
||||
"source": "https://github.com/skipperbent/simple-php-router/issues"
|
||||
},
|
||||
"install-path": "../pecee/simple-router"
|
||||
}
|
||||
],
|
||||
"dev": true,
|
||||
"dev-package-names": []
|
||||
}
|
||||
Vendored
+32
@@ -0,0 +1,32 @@
|
||||
<?php return array(
|
||||
'root' => array(
|
||||
'name' => '__root__',
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'reference' => '156d277e775d31b796bdb4cdd123800f41b0a1cb',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'dev' => true,
|
||||
),
|
||||
'versions' => array(
|
||||
'__root__' => array(
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'reference' => '156d277e775d31b796bdb4cdd123800f41b0a1cb',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'pecee/simple-router' => array(
|
||||
'pretty_version' => '5.4.1.7',
|
||||
'version' => '5.4.1.7',
|
||||
'reference' => 'a2843d5b1e037f8b61cc99f27eab52a28bf41dfd',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../pecee/simple-router',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
),
|
||||
);
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
// platform_check.php @generated by Composer
|
||||
|
||||
$issues = array();
|
||||
|
||||
if (!(PHP_VERSION_ID >= 70400)) {
|
||||
$issues[] = 'Your Composer dependencies require a PHP version ">= 7.4.0". You are running ' . PHP_VERSION . '.';
|
||||
}
|
||||
|
||||
if ($issues) {
|
||||
if (!headers_sent()) {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
}
|
||||
if (!ini_get('display_errors')) {
|
||||
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
|
||||
} elseif (!headers_sent()) {
|
||||
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
'Composer detected issues in your platform: ' . implode(' ', $issues),
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
Vendored
-44
@@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
class Config
|
||||
{
|
||||
private static array $instances = [];
|
||||
|
||||
private array $app;
|
||||
private array $database;
|
||||
|
||||
protected function __construct()
|
||||
{
|
||||
$this->app = require_once __DIR__ . "/../../config/app.php";
|
||||
$this->database = require_once __DIR__ . "/../../config/database.php";
|
||||
}
|
||||
|
||||
protected function __clone()
|
||||
{
|
||||
}
|
||||
|
||||
public function __wakeup()
|
||||
{
|
||||
throw new \Exception("Cannot unserialize a singleton.");
|
||||
}
|
||||
|
||||
private static function getInstance(): Config
|
||||
{
|
||||
$cls = static::class;
|
||||
if (!isset(self::$instances[$cls])) {
|
||||
self::$instances[$cls] = new static();
|
||||
}
|
||||
|
||||
return self::$instances[$cls];
|
||||
}
|
||||
|
||||
public static function getBasePath()
|
||||
{
|
||||
return Config::getInstance()->app["basePath"];
|
||||
}
|
||||
|
||||
public static function getDatabase()
|
||||
{
|
||||
return Config::getInstance()->database;
|
||||
}
|
||||
}
|
||||
Vendored
-32
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
class Database extends PDO
|
||||
{
|
||||
private static array $instances = [];
|
||||
|
||||
protected function __construct($dsn, $username = null, $password = null, array $options = null)
|
||||
{
|
||||
parent::__construct($dsn, $username, $password, $options);
|
||||
}
|
||||
|
||||
public static function getInstance(): Database
|
||||
{
|
||||
$cls = static::class;
|
||||
if (!isset(self::$instances[$cls])) {
|
||||
$dataAccess = Config::getDatabase();
|
||||
self::$instances[$cls] = new static(
|
||||
"mysql:host={$dataAccess["host"]};dbname={$dataAccess["database"]};charset={$dataAccess["charset"]}",
|
||||
$dataAccess["user"],
|
||||
$dataAccess["passwd"],
|
||||
[
|
||||
PDO::ATTR_PERSISTENT => false,
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
PDO::ATTR_EMULATE_PREPARES => false,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return self::$instances[$cls];
|
||||
}
|
||||
}
|
||||
Vendored
-22
@@ -1,22 +0,0 @@
|
||||
<?php
|
||||
|
||||
class Headers
|
||||
{
|
||||
public static function json()
|
||||
{
|
||||
header('Content-Type: text/html; charset=utf-8');
|
||||
header("Content-Type: text/json");
|
||||
}
|
||||
|
||||
public static function html()
|
||||
{
|
||||
header('Content-Type: text/html; charset=utf-8');
|
||||
}
|
||||
|
||||
public static function redirect(string $newUrl, bool $permanent = FALSE)
|
||||
{
|
||||
header('Location: ' . $newUrl, true, $permanent ? 301 : 303);
|
||||
|
||||
exit();
|
||||
}
|
||||
}
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
<?php
|
||||
|
||||
class PathParams
|
||||
{
|
||||
public static function get()
|
||||
{
|
||||
$path = ltrim(str_replace(Config::getBasePath(), "", $_SERVER['REQUEST_URI']), "/");
|
||||
$segs = explode("/", $path);
|
||||
return $segs ? $segs : [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
name: CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build-test:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
env:
|
||||
PHP_EXTENSIONS: json
|
||||
PHP_INI_VALUES: assert.exception=1, zend.assertions=1
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-latest
|
||||
- windows-latest
|
||||
php-version:
|
||||
- 7.4
|
||||
- 8.0
|
||||
phpunit-version:
|
||||
- 8.5.32
|
||||
dependencies:
|
||||
- lowest
|
||||
- highest
|
||||
name: PHPUnit Tests
|
||||
steps:
|
||||
- name: Configure git to avoid issues with line endings
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: git config --global core.autocrlf false
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
tools: composer:v5, phpunit:${{ matrix.phpunit-versions }}
|
||||
coverage: xdebug
|
||||
extensions: ${{ env.PHP_EXTENSIONS }}
|
||||
ini-values: ${{ env.PHP_INI_VALUES }}
|
||||
- name: Get composer cache directory
|
||||
id: composer-cache
|
||||
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: php${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.json') }}
|
||||
restore-keys: |
|
||||
php${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-
|
||||
- name: Install lowest dependencies with composer
|
||||
if: matrix.dependencies == 'lowest'
|
||||
run: composer update --no-ansi --no-interaction --no-progress --prefer-lowest
|
||||
- name: Install highest dependencies with composer
|
||||
if: matrix.dependencies == 'highest'
|
||||
run: composer update --no-ansi --no-interaction --no-progress
|
||||
- name: Run tests with phpunit
|
||||
run: composer test
|
||||
@@ -0,0 +1,5 @@
|
||||
composer.lock
|
||||
vendor/
|
||||
.idea/
|
||||
.phpunit.result.cache
|
||||
tests/tmp
|
||||
+2105
File diff suppressed because it is too large.
Load diff
+56
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"name": "pecee/simple-router",
|
||||
"description": "Simple, fast PHP router that is easy to get integrated and in almost any project. Heavily inspired by the Laravel router.",
|
||||
"keywords": [
|
||||
"router",
|
||||
"router",
|
||||
"routing",
|
||||
"route",
|
||||
"simple-php-router",
|
||||
"laravel",
|
||||
"pecee",
|
||||
"php",
|
||||
"framework",
|
||||
"url-handling",
|
||||
"input-handler",
|
||||
"routing-engine",
|
||||
"request-handler"
|
||||
],
|
||||
"license": "MIT",
|
||||
"support": {
|
||||
"source": "https://github.com/skipperbent/simple-php-router/issues"
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "Simon Sessingø",
|
||||
"email": "simon.sessingoe@gmail.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.4",
|
||||
"ext-json": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^8",
|
||||
"mockery/mockery": "^1",
|
||||
"phpstan/phpstan": "^1",
|
||||
"phpstan/phpstan-phpunit": "^1",
|
||||
"phpstan/phpstan-deprecation-rules": "^1",
|
||||
"phpstan/phpstan-strict-rules": "^1"
|
||||
},
|
||||
"scripts": {
|
||||
"test": [
|
||||
"phpunit tests"
|
||||
]
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Pecee\\": "src/Pecee/"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"ocramius/package-versions": true
|
||||
}
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
use Pecee\SimpleRouter\SimpleRouter as Router;
|
||||
use Pecee\Http\Url;
|
||||
use Pecee\Http\Response;
|
||||
use Pecee\Http\Request;
|
||||
|
||||
/**
|
||||
* Get url for a route by using either name/alias, class or method name.
|
||||
*
|
||||
* The name parameter supports the following values:
|
||||
* - Route name
|
||||
* - Controller/resource name (with or without method)
|
||||
* - Controller class name
|
||||
*
|
||||
* When searching for controller/resource by name, you can use this syntax "route.name@method".
|
||||
* You can also use the same syntax when searching for a specific controller-class "MyController@home".
|
||||
* If no arguments is specified, it will return the url for the current loaded route.
|
||||
*
|
||||
* @param string|null $name
|
||||
* @param string|array|null $parameters
|
||||
* @param array|null $getParams
|
||||
* @return \Pecee\Http\Url
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
function url(?string $name = null, $parameters = null, ?array $getParams = null): Url
|
||||
{
|
||||
return Router::getUrl($name, $parameters, $getParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Pecee\Http\Response
|
||||
*/
|
||||
function response(): Response
|
||||
{
|
||||
return Router::response();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Pecee\Http\Request
|
||||
*/
|
||||
function request(): Request
|
||||
{
|
||||
return Router::request();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get input class
|
||||
* @param string|null $index Parameter index name
|
||||
* @param string|mixed|null $defaultValue Default return value
|
||||
* @param array ...$methods Default methods
|
||||
* @return \Pecee\Http\Input\InputHandler|array|string|null
|
||||
*/
|
||||
function input($index = null, $defaultValue = null, ...$methods)
|
||||
{
|
||||
if ($index !== null) {
|
||||
return request()->getInputHandler()->value($index, $defaultValue, ...$methods);
|
||||
}
|
||||
|
||||
return request()->getInputHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param int|null $code
|
||||
*/
|
||||
function redirect(string $url, ?int $code = null): void
|
||||
{
|
||||
if ($code !== null) {
|
||||
response()->httpCode($code);
|
||||
}
|
||||
|
||||
response()->redirect($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current csrf-token
|
||||
* @return string|null
|
||||
*/
|
||||
function csrf_token(): ?string
|
||||
{
|
||||
$baseVerifier = Router::router()->getCsrfVerifier();
|
||||
if ($baseVerifier !== null) {
|
||||
return $baseVerifier->getTokenProvider()->getToken();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
parameters:
|
||||
level: 6
|
||||
paths:
|
||||
- src
|
||||
fileExtensions:
|
||||
- php
|
||||
bootstrapFiles:
|
||||
- ./vendor/autoload.php
|
||||
ignoreErrors:
|
||||
reportUnmatchedIgnoredErrors: true
|
||||
checkMissingIterableValueType: false
|
||||
checkGenericClassInNonGenericObjectType: false
|
||||
parallel:
|
||||
processTimeout: 300.0
|
||||
jobSize: 10
|
||||
maximumNumberOfProcesses: 4
|
||||
minimumNumberOfJobsPerProcess: 4
|
||||
includes:
|
||||
- vendor/phpstan/phpstan-strict-rules/rules.neon
|
||||
- vendor/phpstan/phpstan-phpunit/extension.neon
|
||||
- vendor/phpstan/phpstan-phpunit/rules.neon
|
||||
- vendor/phpstan/phpstan-deprecation-rules/rules.neon
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit
|
||||
backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
colors="true"
|
||||
bootstrap="tests/bootstrap.php"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false">
|
||||
<testsuites>
|
||||
<testsuite name="SimpleRouter Test Suite">
|
||||
<directory>tests/Pecee/SimpleRouter/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist addUncoveredFilesFromWhitelist="true"
|
||||
processUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">src</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Controllers;
|
||||
|
||||
interface IResourceController
|
||||
{
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function index();
|
||||
|
||||
/**
|
||||
* @param mixed $id
|
||||
* @return mixed
|
||||
*/
|
||||
public function show($id);
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function store();
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function create();
|
||||
|
||||
/**
|
||||
* View
|
||||
* @param mixed $id
|
||||
* @return mixed
|
||||
*/
|
||||
public function edit($id);
|
||||
|
||||
/**
|
||||
* @param mixed $id
|
||||
* @return mixed
|
||||
*/
|
||||
public function update($id);
|
||||
|
||||
/**
|
||||
* @param mixed $id
|
||||
* @return mixed
|
||||
*/
|
||||
public function destroy($id);
|
||||
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Exceptions;
|
||||
|
||||
class InvalidArgumentException extends \InvalidArgumentException
|
||||
{
|
||||
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class MalformedUrlException extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http\Input;
|
||||
|
||||
interface IInputItem
|
||||
{
|
||||
|
||||
public function getIndex(): string;
|
||||
|
||||
public function setIndex(string $index): self;
|
||||
|
||||
public function getName(): ?string;
|
||||
|
||||
public function setName(string $name): self;
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getValue();
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function setValue($value): self;
|
||||
|
||||
public function __toString(): string;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http\Input;
|
||||
|
||||
use Pecee\Exceptions\InvalidArgumentException;
|
||||
|
||||
class InputFile implements IInputItem
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public string $index;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public string $name;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
public ?string $filename = null;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
public ?int $size = null;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
public ?string $type = null;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public int $errors = 0;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
public ?string $tmpName = null;
|
||||
|
||||
public function __construct(string $index)
|
||||
{
|
||||
$this->index = $index;
|
||||
|
||||
$this->errors = 0;
|
||||
|
||||
// Make the name human friendly, by replace _ with space
|
||||
$this->name = ucfirst(str_replace('_', ' ', strtolower($this->index)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create from array
|
||||
*
|
||||
* @param array $values
|
||||
* @throws InvalidArgumentException
|
||||
* @return static
|
||||
*/
|
||||
public static function createFromArray(array $values): self
|
||||
{
|
||||
if (isset($values['index']) === false) {
|
||||
throw new InvalidArgumentException('Index key is required');
|
||||
}
|
||||
|
||||
/* Easy way of ensuring that all indexes-are set and not filling the screen with isset() */
|
||||
|
||||
$values += [
|
||||
'tmp_name' => null,
|
||||
'type' => null,
|
||||
'size' => null,
|
||||
'name' => null,
|
||||
'error' => null,
|
||||
];
|
||||
|
||||
return (new self($values['index']))
|
||||
->setSize((int)$values['size'])
|
||||
->setError((int)$values['error'])
|
||||
->setType($values['type'])
|
||||
->setTmpName($values['tmp_name'])
|
||||
->setFilename($values['name']);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getIndex(): string
|
||||
{
|
||||
return $this->index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set input index
|
||||
* @param string $index
|
||||
* @return static
|
||||
*/
|
||||
public function setIndex(string $index): IInputItem
|
||||
{
|
||||
$this->index = $index;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getSize(): ?int
|
||||
{
|
||||
return $this->size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set file size
|
||||
* @param int $size
|
||||
* @return static
|
||||
*/
|
||||
public function setSize(int $size): IInputItem
|
||||
{
|
||||
$this->size = $size;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mime-type of file
|
||||
* @return string
|
||||
*/
|
||||
public function getMime(): string
|
||||
{
|
||||
return $this->getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set type
|
||||
* @param string $type
|
||||
* @return static
|
||||
*/
|
||||
public function setType(string $type): IInputItem
|
||||
{
|
||||
$this->type = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns extension without "."
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getExtension(): string
|
||||
{
|
||||
return pathinfo($this->getFilename(), PATHINFO_EXTENSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get human friendly name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): ?string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set human friendly name.
|
||||
* Useful for adding validation etc.
|
||||
*
|
||||
* @param string $name
|
||||
* @return static
|
||||
*/
|
||||
public function setName(string $name): IInputItem
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set filename
|
||||
*
|
||||
* @param string $name
|
||||
* @return static
|
||||
*/
|
||||
public function setFilename(string $name): IInputItem
|
||||
{
|
||||
$this->filename = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get filename
|
||||
*
|
||||
* @return string mixed
|
||||
*/
|
||||
public function getFilename(): ?string
|
||||
{
|
||||
return $this->filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the uploaded temporary file to it's new home
|
||||
*
|
||||
* @param string $destination
|
||||
* @return bool
|
||||
*/
|
||||
public function move(string $destination): bool
|
||||
{
|
||||
return move_uploaded_file($this->tmpName, $destination);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file contents
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContents(): string
|
||||
{
|
||||
return file_get_contents($this->tmpName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if an upload error occurred.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasError(): bool
|
||||
{
|
||||
return ($this->getError() !== 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get upload-error code.
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getError(): ?int
|
||||
{
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set error
|
||||
*
|
||||
* @param int|null $error
|
||||
* @return static
|
||||
*/
|
||||
public function setError(?int $error): IInputItem
|
||||
{
|
||||
$this->errors = (int)$error;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTmpName(): string
|
||||
{
|
||||
return $this->tmpName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set file temp. name
|
||||
* @param string $name
|
||||
* @return static
|
||||
*/
|
||||
public function setTmpName(string $name): IInputItem
|
||||
{
|
||||
$this->tmpName = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->getTmpName();
|
||||
}
|
||||
|
||||
public function getValue(): string
|
||||
{
|
||||
return $this->getFilename();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @return static
|
||||
*/
|
||||
public function setValue($value): IInputItem
|
||||
{
|
||||
$this->filename = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'tmp_name' => $this->tmpName,
|
||||
'type' => $this->type,
|
||||
'size' => $this->size,
|
||||
'name' => $this->name,
|
||||
'error' => $this->errors,
|
||||
'filename' => $this->filename,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,474 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http\Input;
|
||||
|
||||
use Pecee\Exceptions\InvalidArgumentException;
|
||||
use Pecee\Http\Request;
|
||||
|
||||
class InputHandler
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $get = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $post = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $file = [];
|
||||
|
||||
/**
|
||||
* @var Request
|
||||
*/
|
||||
protected Request $request;
|
||||
|
||||
/**
|
||||
* Original post variables
|
||||
* @var array
|
||||
*/
|
||||
protected array $originalPost = [];
|
||||
|
||||
/**
|
||||
* Original get/params variables
|
||||
* @var array
|
||||
*/
|
||||
protected array $originalParams = [];
|
||||
|
||||
/**
|
||||
* Get original file variables
|
||||
* @var array
|
||||
*/
|
||||
protected array $originalFile = [];
|
||||
|
||||
/**
|
||||
* Input constructor.
|
||||
* @param Request $request
|
||||
*/
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
|
||||
$this->parseInputs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse input values
|
||||
*
|
||||
*/
|
||||
public function parseInputs(): void
|
||||
{
|
||||
/* Parse get requests */
|
||||
if (count($_GET) !== 0) {
|
||||
$this->originalParams = $_GET;
|
||||
$this->get = $this->parseInputItem($this->originalParams);
|
||||
}
|
||||
|
||||
/* Parse post requests */
|
||||
$this->originalPost = $_POST;
|
||||
|
||||
if ($this->request->isPostBack() === true) {
|
||||
|
||||
$contents = file_get_contents('php://input');
|
||||
|
||||
// Append any PHP-input json
|
||||
if (strpos(trim($contents), '{') === 0) {
|
||||
$post = json_decode($contents, true);
|
||||
|
||||
if ($post !== false) {
|
||||
$this->originalPost += $post;
|
||||
}
|
||||
} else {
|
||||
$post = [];
|
||||
parse_str($contents, $post);
|
||||
$this->originalPost += $post;
|
||||
}
|
||||
}
|
||||
|
||||
if (count($this->originalPost) !== 0) {
|
||||
$this->post = $this->parseInputItem($this->originalPost);
|
||||
}
|
||||
|
||||
/* Parse get requests */
|
||||
if (count($_FILES) !== 0) {
|
||||
$this->originalFile = $_FILES;
|
||||
$this->file = $this->parseFiles($this->originalFile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $files Array with files to parse
|
||||
* @param string|null $parentKey Key from parent (used when parsing nested array).
|
||||
* @return array
|
||||
*/
|
||||
public function parseFiles(array $files, ?string $parentKey = null): array
|
||||
{
|
||||
$list = [];
|
||||
|
||||
foreach ($files as $key => $value) {
|
||||
|
||||
// Parse multi dept file array
|
||||
if (isset($value['name']) === false && is_array($value) === true) {
|
||||
$list[$key] = $this->parseFiles($value, $key);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle array input
|
||||
if (is_array($value['name']) === false) {
|
||||
$values = ['index' => $parentKey ?? $key];
|
||||
|
||||
try {
|
||||
$list[$key] = InputFile::createFromArray($values + $value);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$keys = [$key];
|
||||
$files = $this->rearrangeFile($value['name'], $keys, $value);
|
||||
|
||||
if (isset($list[$key]) === true) {
|
||||
$list[$key][] = $files;
|
||||
} else {
|
||||
$list[$key] = $files;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rearrange multi-dimensional file object created by PHP.
|
||||
*
|
||||
* @param array $values
|
||||
* @param array $index
|
||||
* @param array|null $original
|
||||
* @return array
|
||||
*/
|
||||
protected function rearrangeFile(array $values, array &$index, ?array $original): array
|
||||
{
|
||||
$originalIndex = $index[0];
|
||||
array_shift($index);
|
||||
|
||||
$output = [];
|
||||
|
||||
foreach ($values as $key => $value) {
|
||||
|
||||
if (is_array($original['name'][$key]) === false) {
|
||||
|
||||
try {
|
||||
|
||||
$file = InputFile::createFromArray([
|
||||
'index' => ($key === '' && $originalIndex !== '') ? $originalIndex : $key,
|
||||
'name' => $original['name'][$key],
|
||||
'error' => $original['error'][$key],
|
||||
'tmp_name' => $original['tmp_name'][$key],
|
||||
'type' => $original['type'][$key],
|
||||
'size' => $original['size'][$key],
|
||||
]);
|
||||
|
||||
if (isset($output[$key]) === true) {
|
||||
$output[$key][] = $file;
|
||||
continue;
|
||||
}
|
||||
|
||||
$output[$key] = $file;
|
||||
continue;
|
||||
|
||||
} catch (InvalidArgumentException $e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$index[] = $key;
|
||||
|
||||
$files = $this->rearrangeFile($value, $index, $original);
|
||||
|
||||
if (isset($output[$key]) === true) {
|
||||
$output[$key][] = $files;
|
||||
} else {
|
||||
$output[$key] = $files;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse input item from array
|
||||
*
|
||||
* @param array $array
|
||||
* @return array
|
||||
*/
|
||||
protected function parseInputItem(array $array): array
|
||||
{
|
||||
$list = [];
|
||||
|
||||
foreach ($array as $key => $value) {
|
||||
|
||||
// Handle array input
|
||||
if (is_array($value) === true) {
|
||||
$value = $this->parseInputItem($value);
|
||||
}
|
||||
|
||||
$list[$key] = new InputItem($key, $value);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find input object
|
||||
*
|
||||
* @param string $index
|
||||
* @param array ...$methods
|
||||
* @return IInputItem|array|null
|
||||
*/
|
||||
public function find(string $index, ...$methods)
|
||||
{
|
||||
$element = null;
|
||||
|
||||
if (count($methods) > 0) {
|
||||
$methods = is_array(...$methods) ? array_values(...$methods) : $methods;
|
||||
}
|
||||
|
||||
if (count($methods) === 0 || in_array(Request::REQUEST_TYPE_GET, $methods, true) === true) {
|
||||
$element = $this->get($index);
|
||||
}
|
||||
|
||||
if (($element === null && count($methods) === 0) || (count($methods) !== 0 && in_array(Request::REQUEST_TYPE_POST, $methods, true) === true)) {
|
||||
$element = $this->post($index);
|
||||
}
|
||||
|
||||
if (($element === null && count($methods) === 0) || (count($methods) !== 0 && in_array('file', $methods, true) === true)) {
|
||||
$element = $this->file($index);
|
||||
}
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
protected function getValueFromArray(array $array): array
|
||||
{
|
||||
$output = [];
|
||||
/* @var $item InputItem */
|
||||
foreach ($array as $key => $item) {
|
||||
|
||||
if ($item instanceof IInputItem) {
|
||||
$item = $item->getValue();
|
||||
}
|
||||
|
||||
$output[$key] = is_array($item) ? $this->getValueFromArray($item) : $item;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get input element value matching index
|
||||
*
|
||||
* @param string $index
|
||||
* @param string|mixed|null $defaultValue
|
||||
* @param array ...$methods
|
||||
* @return string|array
|
||||
*/
|
||||
public function value(string $index, $defaultValue = null, ...$methods)
|
||||
{
|
||||
$input = $this->find($index, ...$methods);
|
||||
|
||||
if ($input instanceof IInputItem) {
|
||||
$input = $input->getValue();
|
||||
}
|
||||
|
||||
/* Handle collection */
|
||||
if (is_array($input) === true) {
|
||||
$output = $this->getValueFromArray($input);
|
||||
|
||||
return (count($output) === 0) ? $defaultValue : $output;
|
||||
}
|
||||
|
||||
return ($input === null || (is_string($input) && trim($input) === '')) ? $defaultValue : $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a input-item exist.
|
||||
* If an array is as $index parameter the method returns true if all elements exist.
|
||||
*
|
||||
* @param string|array $index
|
||||
* @param array ...$methods
|
||||
* @return bool
|
||||
*/
|
||||
public function exists($index, ...$methods): bool
|
||||
{
|
||||
// Check array
|
||||
if (is_array($index) === true) {
|
||||
foreach ($index as $key) {
|
||||
if ($this->value($key, null, ...$methods) === null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->value($index, null, ...$methods) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find post-value by index or return default value.
|
||||
*
|
||||
* @param string $index
|
||||
* @param mixed|null $defaultValue
|
||||
* @return InputItem|array|string|null
|
||||
*/
|
||||
public function post(string $index, $defaultValue = null)
|
||||
{
|
||||
return $this->post[$index] ?? $defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find file by index or return default value.
|
||||
*
|
||||
* @param string $index
|
||||
* @param mixed|null $defaultValue
|
||||
* @return InputFile|array|string|null
|
||||
*/
|
||||
public function file(string $index, $defaultValue = null)
|
||||
{
|
||||
return $this->file[$index] ?? $defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find parameter/query-string by index or return default value.
|
||||
*
|
||||
* @param string $index
|
||||
* @param mixed|null $defaultValue
|
||||
* @return InputItem|array|string|null
|
||||
*/
|
||||
public function get(string $index, $defaultValue = null)
|
||||
{
|
||||
return $this->get[$index] ?? $defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all get/post items
|
||||
* @param array $filter Only take items in filter
|
||||
* @return array
|
||||
*/
|
||||
public function all(array $filter = []): array
|
||||
{
|
||||
$output = $this->originalParams + $this->originalPost + $this->originalFile;
|
||||
$output = (count($filter) > 0) ? array_intersect_key($output, array_flip($filter)) : $output;
|
||||
|
||||
foreach ($filter as $filterKey) {
|
||||
if (array_key_exists($filterKey, $output) === false) {
|
||||
$output[$filterKey] = null;
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add GET parameter
|
||||
*
|
||||
* @param string $key
|
||||
* @param InputItem $item
|
||||
*/
|
||||
public function addGet(string $key, InputItem $item): void
|
||||
{
|
||||
$this->get[$key] = $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add POST parameter
|
||||
*
|
||||
* @param string $key
|
||||
* @param InputItem $item
|
||||
*/
|
||||
public function addPost(string $key, InputItem $item): void
|
||||
{
|
||||
$this->post[$key] = $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add FILE parameter
|
||||
*
|
||||
* @param string $key
|
||||
* @param InputFile $item
|
||||
*/
|
||||
public function addFile(string $key, InputFile $item): void
|
||||
{
|
||||
$this->file[$key] = $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get original post variables
|
||||
* @return array
|
||||
*/
|
||||
public function getOriginalPost(): array
|
||||
{
|
||||
return $this->originalPost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set original post variables
|
||||
* @param array $post
|
||||
* @return static $this
|
||||
*/
|
||||
public function setOriginalPost(array $post): self
|
||||
{
|
||||
$this->originalPost = $post;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get original get variables
|
||||
* @return array
|
||||
*/
|
||||
public function getOriginalParams(): array
|
||||
{
|
||||
return $this->originalParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set original get-variables
|
||||
* @param array $params
|
||||
* @return static $this
|
||||
*/
|
||||
public function setOriginalParams(array $params): self
|
||||
{
|
||||
$this->originalParams = $params;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get original file variables
|
||||
* @return array
|
||||
*/
|
||||
public function getOriginalFile(): array
|
||||
{
|
||||
return $this->originalFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set original file posts variables
|
||||
* @param array $file
|
||||
* @return static $this
|
||||
*/
|
||||
public function setOriginalFile(array $file): self
|
||||
{
|
||||
$this->originalFile = $file;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http\Input;
|
||||
|
||||
use ArrayAccess;
|
||||
use ArrayIterator;
|
||||
use IteratorAggregate;
|
||||
|
||||
class InputItem implements ArrayAccess, IInputItem, IteratorAggregate
|
||||
{
|
||||
public string $index;
|
||||
public string $name;
|
||||
|
||||
/**
|
||||
* @var mixed|null
|
||||
*/
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* @param string $index
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function __construct(string $index, $value = null)
|
||||
{
|
||||
$this->index = $index;
|
||||
$this->value = $value;
|
||||
|
||||
// Make the name human friendly, by replace _ with space
|
||||
$this->name = ucfirst(str_replace('_', ' ', strtolower($this->index)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getIndex(): string
|
||||
{
|
||||
return $this->index;
|
||||
}
|
||||
|
||||
public function setIndex(string $index): IInputItem
|
||||
{
|
||||
$this->index = $index;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): ?string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set input name
|
||||
* @param string $name
|
||||
* @return static
|
||||
*/
|
||||
public function setName(string $name): IInputItem
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set input value
|
||||
* @param mixed $value
|
||||
* @return static
|
||||
*/
|
||||
public function setValue($value): IInputItem
|
||||
{
|
||||
$this->value = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function offsetExists($offset): bool
|
||||
{
|
||||
return isset($this->value[$offset]);
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($offset): ?self
|
||||
{
|
||||
if ($this->offsetExists($offset) === true) {
|
||||
return $this->value[$offset];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function offsetSet($offset, $value): void
|
||||
{
|
||||
$this->value[$offset] = $value;
|
||||
}
|
||||
|
||||
public function offsetUnset($offset): void
|
||||
{
|
||||
unset($this->value[$offset]);
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
$value = $this->getValue();
|
||||
|
||||
return (is_array($value) === true) ? json_encode($value) : $value;
|
||||
}
|
||||
|
||||
public function getIterator(): ArrayIterator
|
||||
{
|
||||
return new ArrayIterator($this->getValue());
|
||||
}
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http\Middleware;
|
||||
|
||||
use Pecee\Http\Middleware\Exceptions\TokenMismatchException;
|
||||
use Pecee\Http\Request;
|
||||
use Pecee\Http\Security\CookieTokenProvider;
|
||||
use Pecee\Http\Security\ITokenProvider;
|
||||
|
||||
class BaseCsrfVerifier implements IMiddleware
|
||||
{
|
||||
public const POST_KEY = 'csrf_token';
|
||||
public const HEADER_KEY = 'X-CSRF-TOKEN';
|
||||
|
||||
/**
|
||||
* Urls to ignore. You can use * to exclude all sub-urls on a given path.
|
||||
* For example: /admin/*
|
||||
* @var array|null
|
||||
*/
|
||||
protected array $except = [];
|
||||
|
||||
/**
|
||||
* Urls to include. Can be used to include urls from a certain path.
|
||||
* @var array|null
|
||||
*/
|
||||
protected array $include = [];
|
||||
|
||||
/**
|
||||
* @var ITokenProvider
|
||||
*/
|
||||
protected ITokenProvider $tokenProvider;
|
||||
|
||||
/**
|
||||
* BaseCsrfVerifier constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->tokenProvider = new CookieTokenProvider();
|
||||
}
|
||||
|
||||
protected function isIncluded(Request $request): bool
|
||||
{
|
||||
if (count($this->include) > 0) {
|
||||
foreach ($this->include as $includeUrl) {
|
||||
$includeUrl = rtrim($includeUrl, '/');
|
||||
if ($includeUrl[strlen($includeUrl) - 1] === '*') {
|
||||
$includeUrl = rtrim($includeUrl, '*');
|
||||
return $request->getUrl()->contains($includeUrl);
|
||||
}
|
||||
|
||||
return ($includeUrl === rtrim($request->getUrl()->getRelativeUrl(false), '/'));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the url matches the urls in the except property
|
||||
* @param Request $request
|
||||
* @return bool
|
||||
*/
|
||||
protected function skip(Request $request): bool
|
||||
{
|
||||
if (count($this->except) === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($this->except as $url) {
|
||||
$url = rtrim($url, '/');
|
||||
if ($url[strlen($url) - 1] === '*') {
|
||||
$url = rtrim($url, '*');
|
||||
$skip = $request->getUrl()->contains($url);
|
||||
} else {
|
||||
$skip = ($url === rtrim($request->getUrl()->getRelativeUrl(false), '/'));
|
||||
}
|
||||
|
||||
if ($skip === true) {
|
||||
|
||||
$skip = !$this->isIncluded($request);
|
||||
|
||||
if ($skip === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle request
|
||||
*
|
||||
* @param Request $request
|
||||
* @throws TokenMismatchException
|
||||
*/
|
||||
public function handle(Request $request): void
|
||||
{
|
||||
if ($this->skip($request) === false && ($request->isPostBack() === true || $request->isPostBack() === true && $this->isIncluded($request) === true)) {
|
||||
|
||||
$token = $request->getInputHandler()->value(
|
||||
static::POST_KEY,
|
||||
$request->getHeader(static::HEADER_KEY),
|
||||
);
|
||||
|
||||
if ($this->tokenProvider->validate((string)$token) === false) {
|
||||
throw new TokenMismatchException('Invalid CSRF-token.');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Refresh existing token
|
||||
$this->tokenProvider->refresh();
|
||||
}
|
||||
|
||||
public function getTokenProvider(): ITokenProvider
|
||||
{
|
||||
return $this->tokenProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set token provider
|
||||
* @param ITokenProvider $provider
|
||||
*/
|
||||
public function setTokenProvider(ITokenProvider $provider): void
|
||||
{
|
||||
$this->tokenProvider = $provider;
|
||||
}
|
||||
|
||||
}
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http\Middleware\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class TokenMismatchException extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http\Middleware;
|
||||
|
||||
use Pecee\Http\Request;
|
||||
|
||||
interface IMiddleware
|
||||
{
|
||||
/**
|
||||
* @param Request $request
|
||||
*/
|
||||
public function handle(Request $request): void;
|
||||
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http\Middleware;
|
||||
|
||||
use Pecee\Http\Request;
|
||||
use Pecee\SimpleRouter\Exceptions\HttpException;
|
||||
|
||||
abstract class IpRestrictAccess implements IMiddleware
|
||||
{
|
||||
protected array $ipBlacklist = [];
|
||||
protected array $ipWhitelist = [];
|
||||
|
||||
protected function validate(string $ip): bool
|
||||
{
|
||||
// Accept ip that is in white-list
|
||||
if(in_array($ip, $this->ipWhitelist, true) === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($this->ipBlacklist as $blackIp) {
|
||||
|
||||
// Blocks range (8.8.*)
|
||||
if ($blackIp[strlen($blackIp) - 1] === '*' && strpos($ip, trim($blackIp, '*')) === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Blocks exact match
|
||||
if ($blackIp === $ip) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @throws HttpException
|
||||
*/
|
||||
public function handle(Request $request): void
|
||||
{
|
||||
if($this->validate((string)$request->getIp()) === false) {
|
||||
throw new HttpException(sprintf('Restricted ip. Access to %s has been blocked', $request->getIp()), 403);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,564 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http;
|
||||
|
||||
use Pecee\Http\Exceptions\MalformedUrlException;
|
||||
use Pecee\Http\Input\InputHandler;
|
||||
use Pecee\Http\Middleware\BaseCsrfVerifier;
|
||||
use Pecee\SimpleRouter\Route\ILoadableRoute;
|
||||
use Pecee\SimpleRouter\Route\RouteUrl;
|
||||
use Pecee\SimpleRouter\SimpleRouter;
|
||||
|
||||
class Request
|
||||
{
|
||||
public const REQUEST_TYPE_GET = 'get';
|
||||
public const REQUEST_TYPE_POST = 'post';
|
||||
public const REQUEST_TYPE_PUT = 'put';
|
||||
public const REQUEST_TYPE_PATCH = 'patch';
|
||||
public const REQUEST_TYPE_OPTIONS = 'options';
|
||||
public const REQUEST_TYPE_DELETE = 'delete';
|
||||
public const REQUEST_TYPE_HEAD = 'head';
|
||||
|
||||
public const CONTENT_TYPE_JSON = 'application/json';
|
||||
public const CONTENT_TYPE_FORM_DATA = 'multipart/form-data';
|
||||
public const CONTENT_TYPE_X_FORM_ENCODED = 'application/x-www-form-urlencoded';
|
||||
|
||||
public const FORCE_METHOD_KEY = '_method';
|
||||
|
||||
/**
|
||||
* All request-types
|
||||
* @var string[]
|
||||
*/
|
||||
public static array $requestTypes = [
|
||||
self::REQUEST_TYPE_GET,
|
||||
self::REQUEST_TYPE_POST,
|
||||
self::REQUEST_TYPE_PUT,
|
||||
self::REQUEST_TYPE_PATCH,
|
||||
self::REQUEST_TYPE_OPTIONS,
|
||||
self::REQUEST_TYPE_DELETE,
|
||||
self::REQUEST_TYPE_HEAD,
|
||||
];
|
||||
|
||||
/**
|
||||
* Post request-types.
|
||||
* @var string[]
|
||||
*/
|
||||
public static array $requestTypesPost = [
|
||||
self::REQUEST_TYPE_POST,
|
||||
self::REQUEST_TYPE_PUT,
|
||||
self::REQUEST_TYPE_PATCH,
|
||||
self::REQUEST_TYPE_DELETE,
|
||||
];
|
||||
|
||||
/**
|
||||
* Additional data
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private array $data = [];
|
||||
|
||||
/**
|
||||
* Server headers
|
||||
* @var array
|
||||
*/
|
||||
protected array $headers = [];
|
||||
|
||||
/**
|
||||
* Request ContentType
|
||||
* @var string
|
||||
*/
|
||||
protected string $contentType;
|
||||
|
||||
/**
|
||||
* Request host
|
||||
* @var string|null
|
||||
*/
|
||||
protected ?string $host;
|
||||
|
||||
/**
|
||||
* Current request url
|
||||
* @var Url
|
||||
*/
|
||||
protected Url $url;
|
||||
|
||||
/**
|
||||
* Request method
|
||||
* @var string
|
||||
*/
|
||||
protected string $method;
|
||||
|
||||
/**
|
||||
* Input handler
|
||||
* @var InputHandler
|
||||
*/
|
||||
protected InputHandler $inputHandler;
|
||||
|
||||
/**
|
||||
* Defines if request has pending rewrite
|
||||
* @var bool
|
||||
*/
|
||||
protected bool $hasPendingRewrite = false;
|
||||
|
||||
/**
|
||||
* @var ILoadableRoute|null
|
||||
*/
|
||||
protected ?ILoadableRoute $rewriteRoute = null;
|
||||
|
||||
/**
|
||||
* Rewrite url
|
||||
* @var string|null
|
||||
*/
|
||||
protected ?string $rewriteUrl = null;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $loadedRoutes = [];
|
||||
|
||||
/**
|
||||
* Request constructor.
|
||||
* @throws MalformedUrlException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
foreach ($_SERVER as $key => $value) {
|
||||
$this->headers[strtolower($key)] = $value;
|
||||
$this->headers[str_replace('_', '-', strtolower($key))] = $value;
|
||||
}
|
||||
|
||||
$this->setHost($this->getHeader('http-host'));
|
||||
|
||||
// Check if special IIS header exist, otherwise use default.
|
||||
$url = $this->getHeader('unencoded-url');
|
||||
if ($url !== null) {
|
||||
$this->setUrl(new Url($url));
|
||||
} else {
|
||||
$this->setUrl(new Url(urldecode((string)$this->getHeader('request-uri'))));
|
||||
}
|
||||
$this->setContentType((string)$this->getHeader('content-type'));
|
||||
$this->setMethod((string)($_POST[static::FORCE_METHOD_KEY] ?? $this->getHeader('request-method')));
|
||||
$this->inputHandler = new InputHandler($this);
|
||||
}
|
||||
|
||||
public function isSecure(): bool
|
||||
{
|
||||
return $this->getHeader('http-x-forwarded-proto') === 'https' || $this->getHeader('https') !== null || (int)$this->getHeader('server-port') === 443;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Url
|
||||
*/
|
||||
public function getUrl(): Url
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy url object
|
||||
*
|
||||
* @return Url
|
||||
*/
|
||||
public function getUrlCopy(): Url
|
||||
{
|
||||
return clone $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getHost(): ?string
|
||||
{
|
||||
return $this->host;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getMethod(): ?string
|
||||
{
|
||||
return $this->method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get http basic auth user
|
||||
* @return string|null
|
||||
*/
|
||||
public function getUser(): ?string
|
||||
{
|
||||
return $this->getHeader('php-auth-user');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get http basic auth password
|
||||
* @return string|null
|
||||
*/
|
||||
public function getPassword(): ?string
|
||||
{
|
||||
return $this->getHeader('php-auth-pw');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the csrf token
|
||||
* @return string|null
|
||||
*/
|
||||
public function getCsrfToken(): ?string
|
||||
{
|
||||
return $this->getHeader(BaseCsrfVerifier::HEADER_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all headers
|
||||
* @return array
|
||||
*/
|
||||
public function getHeaders(): array
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get id address
|
||||
* If $safe is false, this function will detect Proxys. But the user can edit this header to whatever he wants!
|
||||
* https://stackoverflow.com/questions/3003145/how-to-get-the-client-ip-address-in-php#comment-25086804
|
||||
* @param bool $safeMode When enabled, only safe non-spoofable headers will be returned. Note this can cause issues when using proxy.
|
||||
* @return string|null
|
||||
*/
|
||||
public function getIp(bool $safeMode = false): ?string
|
||||
{
|
||||
$headers = [];
|
||||
if ($safeMode === false) {
|
||||
$headers = [
|
||||
'http-cf-connecting-ip',
|
||||
'http-client-ip',
|
||||
'http-x-forwarded-for',
|
||||
];
|
||||
}
|
||||
|
||||
$headers[] = 'remote-addr';
|
||||
|
||||
return $this->getFirstHeader($headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get remote address/ip
|
||||
*
|
||||
* @alias static::getIp
|
||||
* @return string|null
|
||||
*/
|
||||
public function getRemoteAddr(): ?string
|
||||
{
|
||||
return $this->getIp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get referer
|
||||
* @return string|null
|
||||
*/
|
||||
public function getReferer(): ?string
|
||||
{
|
||||
return $this->getHeader('http-referer');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user agent
|
||||
* @return string|null
|
||||
*/
|
||||
public function getUserAgent(): ?string
|
||||
{
|
||||
return $this->getHeader('http-user-agent');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get header value by name
|
||||
*
|
||||
* @param string $name Name of the header.
|
||||
* @param string|mixed|null $defaultValue Value to be returned if header is not found.
|
||||
* @param bool $tryParse When enabled the method will try to find the header from both from client (http) and server-side variants, if the header is not found.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getHeader(string $name, $defaultValue = null, bool $tryParse = true): ?string
|
||||
{
|
||||
$name = strtolower($name);
|
||||
$header = $this->headers[$name] ?? null;
|
||||
|
||||
if ($tryParse === true && $header === null) {
|
||||
if (strpos($name, 'http-') === 0) {
|
||||
// Trying to find client header variant which was not found, searching for header variant without http- prefix.
|
||||
$header = $this->headers[str_replace('http-', '', $name)] ?? null;
|
||||
} else {
|
||||
// Trying to find server variant which was not found, searching for client variant with http- prefix.
|
||||
$header = $this->headers['http-' . $name] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
return $header ?? $defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will try to find first header from list of headers.
|
||||
*
|
||||
* @param array $headers
|
||||
* @param mixed|null $defaultValue
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getFirstHeader(array $headers, $defaultValue = null)
|
||||
{
|
||||
foreach ($headers as $header) {
|
||||
$header = $this->getHeader($header);
|
||||
if ($header !== null) {
|
||||
return $header;
|
||||
}
|
||||
}
|
||||
|
||||
return $defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get request content-type
|
||||
* @return string|null
|
||||
*/
|
||||
public function getContentType(): ?string
|
||||
{
|
||||
return $this->contentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set request content-type
|
||||
* @param string $contentType
|
||||
* @return $this
|
||||
*/
|
||||
protected function setContentType(string $contentType): self
|
||||
{
|
||||
if (strpos($contentType, ';') > 0) {
|
||||
$this->contentType = strtolower(substr($contentType, 0, strpos($contentType, ';')));
|
||||
} else {
|
||||
$this->contentType = strtolower($contentType);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get input class
|
||||
* @return InputHandler
|
||||
*/
|
||||
public function getInputHandler(): InputHandler
|
||||
{
|
||||
return $this->inputHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is format accepted
|
||||
*
|
||||
* @param string $format
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isFormatAccepted(string $format): bool
|
||||
{
|
||||
return ($this->getHeader('http-accept') !== null && stripos($this->getHeader('http-accept'), $format) !== false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the request is made through Ajax
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isAjax(): bool
|
||||
{
|
||||
return (strtolower((string)$this->getHeader('http-x-requested-with')) === 'xmlhttprequest');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when request-method is type that could contain data in the page body.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isPostBack(): bool
|
||||
{
|
||||
return in_array($this->getMethod(), static::$requestTypesPost, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get accept formats
|
||||
* @return array
|
||||
*/
|
||||
public function getAcceptFormats(): array
|
||||
{
|
||||
return explode(',', $this->getHeader('http-accept'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Url $url
|
||||
*/
|
||||
public function setUrl(Url $url): void
|
||||
{
|
||||
$this->url = $url;
|
||||
|
||||
if ($this->isSecure() === true) {
|
||||
$this->url->setScheme('https');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $host
|
||||
*/
|
||||
public function setHost(?string $host): void
|
||||
{
|
||||
// Strip any potential ports from hostname
|
||||
if (strpos((string)$host, ':') !== false) {
|
||||
$host = strstr($host, strrchr($host, ':'), true);
|
||||
}
|
||||
|
||||
$this->host = $host;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method
|
||||
*/
|
||||
public function setMethod(string $method): void
|
||||
{
|
||||
$this->method = strtolower($method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set rewrite route
|
||||
*
|
||||
* @param ILoadableRoute $route
|
||||
* @return static
|
||||
*/
|
||||
public function setRewriteRoute(ILoadableRoute $route): self
|
||||
{
|
||||
$this->hasPendingRewrite = true;
|
||||
$this->rewriteRoute = SimpleRouter::addDefaultNamespace($route);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get rewrite route
|
||||
*
|
||||
* @return ILoadableRoute|null
|
||||
*/
|
||||
public function getRewriteRoute(): ?ILoadableRoute
|
||||
{
|
||||
return $this->rewriteRoute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get rewrite url
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getRewriteUrl(): ?string
|
||||
{
|
||||
return $this->rewriteUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set rewrite url
|
||||
*
|
||||
* @param string $rewriteUrl
|
||||
* @return static
|
||||
*/
|
||||
public function setRewriteUrl(string $rewriteUrl): self
|
||||
{
|
||||
$this->hasPendingRewrite = true;
|
||||
$this->rewriteUrl = rtrim($rewriteUrl, '/') . '/';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set rewrite callback
|
||||
* @param string|\Closure $callback
|
||||
* @return static
|
||||
*/
|
||||
public function setRewriteCallback($callback): self
|
||||
{
|
||||
$this->hasPendingRewrite = true;
|
||||
|
||||
return $this->setRewriteRoute(new RouteUrl($this->getUrl()->getPath(), $callback));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get loaded route
|
||||
* @return ILoadableRoute|null
|
||||
*/
|
||||
public function getLoadedRoute(): ?ILoadableRoute
|
||||
{
|
||||
return (count($this->loadedRoutes) > 0) ? end($this->loadedRoutes) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all loaded routes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getLoadedRoutes(): array
|
||||
{
|
||||
return $this->loadedRoutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set loaded routes
|
||||
*
|
||||
* @param array $routes
|
||||
* @return static
|
||||
*/
|
||||
public function setLoadedRoutes(array $routes): self
|
||||
{
|
||||
$this->loadedRoutes = $routes;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Added loaded route
|
||||
*
|
||||
* @param ILoadableRoute $route
|
||||
* @return static
|
||||
*/
|
||||
public function addLoadedRoute(ILoadableRoute $route): self
|
||||
{
|
||||
$this->loadedRoutes[] = $route;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the request contains a rewrite
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPendingRewrite(): bool
|
||||
{
|
||||
return $this->hasPendingRewrite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines if the current request contains a rewrite.
|
||||
*
|
||||
* @param bool $boolean
|
||||
* @return Request
|
||||
*/
|
||||
public function setHasPendingRewrite(bool $boolean): self
|
||||
{
|
||||
$this->hasPendingRewrite = $boolean;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function __isset($name): bool
|
||||
{
|
||||
return array_key_exists($name, $this->data) === true;
|
||||
}
|
||||
|
||||
public function __set($name, $value = null)
|
||||
{
|
||||
$this->data[$name] = $value;
|
||||
}
|
||||
|
||||
public function __get($name)
|
||||
{
|
||||
return $this->data[$name] ?? null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http;
|
||||
|
||||
use JsonSerializable;
|
||||
use Pecee\Exceptions\InvalidArgumentException;
|
||||
|
||||
class Response
|
||||
{
|
||||
protected Request $request;
|
||||
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the http status code
|
||||
*
|
||||
* @param int $code
|
||||
* @return static
|
||||
*/
|
||||
public function httpCode(int $code): self
|
||||
{
|
||||
http_response_code($code);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect the response
|
||||
*
|
||||
* @param string $url
|
||||
* @param ?int $httpCode
|
||||
*
|
||||
* @return never
|
||||
*/
|
||||
public function redirect(string $url, ?int $httpCode = null): void
|
||||
{
|
||||
if ($httpCode !== null) {
|
||||
$this->httpCode($httpCode);
|
||||
}
|
||||
|
||||
$this->header('location: ' . $url);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
public function refresh(): void
|
||||
{
|
||||
$this->redirect($this->request->getUrl()->getOriginalUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add http authorisation
|
||||
* @param string $name
|
||||
* @return static
|
||||
*/
|
||||
public function auth(string $name = ''): self
|
||||
{
|
||||
$this->headers([
|
||||
'WWW-Authenticate: Basic realm="' . $name . '"',
|
||||
'HTTP/1.0 401 Unauthorized',
|
||||
]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function cache(string $eTag, int $lastModifiedTime = 2592000): self
|
||||
{
|
||||
$this->headers([
|
||||
'Cache-Control: public',
|
||||
sprintf('Last-Modified: %s GMT', gmdate('D, d M Y H:i:s', $lastModifiedTime)),
|
||||
sprintf('Etag: %s', $eTag),
|
||||
]);
|
||||
|
||||
$httpModified = $this->request->getHeader('http-if-modified-since');
|
||||
$httpIfNoneMatch = $this->request->getHeader('http-if-none-match');
|
||||
|
||||
if (($httpIfNoneMatch !== null && $httpIfNoneMatch === $eTag) || ($httpModified !== null && strtotime($httpModified) === $lastModifiedTime)) {
|
||||
|
||||
$this->header('HTTP/1.1 304 Not Modified');
|
||||
exit(0);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Json encode
|
||||
* @param array|JsonSerializable $value
|
||||
* @param int $options JSON options Bitmask consisting of JSON_HEX_QUOT, JSON_HEX_TAG, JSON_HEX_AMP, JSON_HEX_APOS, JSON_NUMERIC_CHECK, JSON_PRETTY_PRINT, JSON_UNESCAPED_SLASHES, JSON_FORCE_OBJECT, JSON_PRESERVE_ZERO_FRACTION, JSON_UNESCAPED_UNICODE, JSON_PARTIAL_OUTPUT_ON_ERROR.
|
||||
* @param int $dept JSON debt.
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function json($value, int $options = 0, int $dept = 512): void
|
||||
{
|
||||
if (($value instanceof JsonSerializable) === false && is_array($value) === false) {
|
||||
throw new InvalidArgumentException('Invalid type for parameter "value". Must be of type array or object implementing the \JsonSerializable interface.');
|
||||
}
|
||||
|
||||
$this->header('Content-Type: application/json; charset=utf-8');
|
||||
echo json_encode($value, $options, $dept);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add header to response
|
||||
* @param string $value
|
||||
* @return static
|
||||
*/
|
||||
public function header(string $value): self
|
||||
{
|
||||
header($value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add multiple headers to response
|
||||
* @param array $headers
|
||||
* @return static
|
||||
*/
|
||||
public function headers(array $headers): self
|
||||
{
|
||||
foreach ($headers as $header) {
|
||||
$this->header($header);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http\Security;
|
||||
|
||||
use Exception;
|
||||
use Pecee\Http\Security\Exceptions\SecurityException;
|
||||
|
||||
class CookieTokenProvider implements ITokenProvider
|
||||
{
|
||||
public const CSRF_KEY = 'CSRF-TOKEN';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected ?string $token = null;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected int $cookieTimeoutMinutes = 120;
|
||||
|
||||
/**
|
||||
* CookieTokenProvider constructor.
|
||||
* @throws SecurityException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->token = ($this->hasToken() === true) ? $_COOKIE[static::CSRF_KEY] : null;
|
||||
|
||||
if ($this->token === null) {
|
||||
$this->token = $this->generateToken();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate random identifier for CSRF token
|
||||
*
|
||||
* @return string
|
||||
* @throws SecurityException
|
||||
*/
|
||||
public function generateToken(): string
|
||||
{
|
||||
try {
|
||||
return bin2hex(random_bytes(32));
|
||||
} catch (Exception $e) {
|
||||
throw new SecurityException($e->getMessage(), (int)$e->getCode(), $e->getPrevious());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate valid CSRF token
|
||||
*
|
||||
* @param string $token
|
||||
* @return bool
|
||||
*/
|
||||
public function validate(string $token): bool
|
||||
{
|
||||
if ($this->getToken() !== null) {
|
||||
return hash_equals($token, $this->getToken());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set csrf token cookie
|
||||
* Overwrite this method to save the token to another storage like session etc.
|
||||
*
|
||||
* @param string $token
|
||||
*/
|
||||
public function setToken(string $token): void
|
||||
{
|
||||
$this->token = $token;
|
||||
setcookie(static::CSRF_KEY, $token, time() + (60 * $this->cookieTimeoutMinutes), '/', ini_get('session.cookie_domain'), ini_get('session.cookie_secure'), ini_get('session.cookie_httponly'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get csrf token
|
||||
* @param string|null $defaultValue
|
||||
* @return string|null
|
||||
*/
|
||||
public function getToken(?string $defaultValue = null): ?string
|
||||
{
|
||||
return $this->token ?? $defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh existing token
|
||||
*/
|
||||
public function refresh(): void
|
||||
{
|
||||
if ($this->token !== null) {
|
||||
$this->setToken($this->token);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the csrf token has been defined
|
||||
* @return bool
|
||||
*/
|
||||
public function hasToken(): bool
|
||||
{
|
||||
return isset($_COOKIE[static::CSRF_KEY]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get timeout for cookie in minutes
|
||||
* @return int
|
||||
*/
|
||||
public function getCookieTimeoutMinutes(): int
|
||||
{
|
||||
return $this->cookieTimeoutMinutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set cookie timeout in minutes
|
||||
* @param int $minutes
|
||||
*/
|
||||
public function setCookieTimeoutMinutes(int $minutes): void
|
||||
{
|
||||
$this->cookieTimeoutMinutes = $minutes;
|
||||
}
|
||||
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http\Security\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class SecurityException extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http\Security;
|
||||
|
||||
interface ITokenProvider
|
||||
{
|
||||
|
||||
/**
|
||||
* Refresh existing token
|
||||
*/
|
||||
public function refresh(): void;
|
||||
|
||||
/**
|
||||
* Validate valid CSRF token
|
||||
*
|
||||
* @param string $token
|
||||
* @return bool
|
||||
*/
|
||||
public function validate(string $token): bool;
|
||||
|
||||
/**
|
||||
* Get token token
|
||||
*
|
||||
* @param string|null $defaultValue
|
||||
* @return string|null
|
||||
*/
|
||||
public function getToken(?string $defaultValue = null): ?string;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,547 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http;
|
||||
|
||||
use JsonSerializable;
|
||||
use Pecee\Http\Exceptions\MalformedUrlException;
|
||||
|
||||
class Url implements JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private ?string $originalUrl = null;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private ?string $scheme = null;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private ?string $host = null;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private ?int $port = null;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private ?string $username = null;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private ?string $password = null;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private ?string $path = null;
|
||||
|
||||
/**
|
||||
* Original path with no sanitization to ending slash
|
||||
* @var string|null
|
||||
*/
|
||||
private ?string $originalPath = null;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private array $params = [];
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private ?string $fragment = null;
|
||||
|
||||
/**
|
||||
* Url constructor.
|
||||
*
|
||||
* @param ?string $url
|
||||
* @throws MalformedUrlException
|
||||
*/
|
||||
public function __construct(?string $url)
|
||||
{
|
||||
$this->originalUrl = $url;
|
||||
$this->parse($url, true);
|
||||
}
|
||||
|
||||
public function parse(?string $url, bool $setOriginalPath = false): self
|
||||
{
|
||||
if ($url !== null) {
|
||||
$data = $this->parseUrl($url);
|
||||
|
||||
$this->scheme = $data['scheme'] ?? null;
|
||||
$this->host = $data['host'] ?? null;
|
||||
$this->port = $data['port'] ?? null;
|
||||
$this->username = $data['user'] ?? null;
|
||||
$this->password = $data['pass'] ?? null;
|
||||
|
||||
if (isset($data['path']) === true) {
|
||||
$this->setPath($data['path']);
|
||||
|
||||
if ($setOriginalPath === true) {
|
||||
$this->originalPath = $data['path'];
|
||||
}
|
||||
}
|
||||
|
||||
$this->fragment = $data['fragment'] ?? null;
|
||||
|
||||
if (isset($data['query']) === true) {
|
||||
$this->setQueryString($data['query']);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if url is using a secure protocol like https
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isSecure(): bool
|
||||
{
|
||||
return (strtolower($this->getScheme()) === 'https');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if url is relative
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isRelative(): bool
|
||||
{
|
||||
return ($this->getHost() === null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get url scheme
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getScheme(): ?string
|
||||
{
|
||||
return $this->scheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the scheme of the url
|
||||
*
|
||||
* @param string $scheme
|
||||
* @return static
|
||||
*/
|
||||
public function setScheme(string $scheme): self
|
||||
{
|
||||
$this->scheme = $scheme;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get url host
|
||||
*
|
||||
* @param bool $includeTrails Prepend // in front of hostname
|
||||
* @return string|null
|
||||
*/
|
||||
public function getHost(bool $includeTrails = false): ?string
|
||||
{
|
||||
if ((string)$this->host !== '' && $includeTrails === true) {
|
||||
return '//' . $this->host;
|
||||
}
|
||||
|
||||
return $this->host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the host of the url
|
||||
*
|
||||
* @param string $host
|
||||
* @return static
|
||||
*/
|
||||
public function setHost(string $host): self
|
||||
{
|
||||
$this->host = $host;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get url port
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getPort(): ?int
|
||||
{
|
||||
return ($this->port !== null) ? (int)$this->port : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the port of the url
|
||||
*
|
||||
* @param int $port
|
||||
* @return static
|
||||
*/
|
||||
public function setPort(int $port): self
|
||||
{
|
||||
$this->port = $port;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse username from url
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getUsername(): ?string
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the username of the url
|
||||
*
|
||||
* @param string $username
|
||||
* @return static
|
||||
*/
|
||||
public function setUsername(string $username): self
|
||||
{
|
||||
$this->username = $username;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse password from url
|
||||
* @return string|null
|
||||
*/
|
||||
public function getPassword(): ?string
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the url password
|
||||
*
|
||||
* @param string $password
|
||||
* @return static
|
||||
*/
|
||||
public function setPassword(string $password): self
|
||||
{
|
||||
$this->password = $password;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get path from url
|
||||
* @return string
|
||||
*/
|
||||
public function getPath(): ?string
|
||||
{
|
||||
return $this->path ?? '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get original path with no sanitization of ending trail/slash.
|
||||
* @return string|null
|
||||
*/
|
||||
public function getOriginalPath(): ?string
|
||||
{
|
||||
return $this->originalPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the url path
|
||||
*
|
||||
* @param string $path
|
||||
* @return static
|
||||
*/
|
||||
public function setPath(string $path): self
|
||||
{
|
||||
$this->path = rtrim($path, '/') . '/';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get query-string from url
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getParams(): array
|
||||
{
|
||||
return $this->params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge parameters array
|
||||
*
|
||||
* @param array $params
|
||||
* @return static
|
||||
*/
|
||||
public function mergeParams(array $params): self
|
||||
{
|
||||
return $this->setParams(array_merge($this->getParams(), $params));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the url params
|
||||
*
|
||||
* @param array $params
|
||||
* @return static
|
||||
*/
|
||||
public function setParams(array $params): self
|
||||
{
|
||||
$this->params = $params;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set raw query-string parameters as string
|
||||
*
|
||||
* @param string $queryString
|
||||
* @return static
|
||||
*/
|
||||
public function setQueryString(string $queryString): self
|
||||
{
|
||||
$params = [];
|
||||
parse_str($queryString, $params);
|
||||
|
||||
if (count($params) > 0) {
|
||||
return $this->setParams($params);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get query-string params as string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getQueryString(): string
|
||||
{
|
||||
return static::arrayToParams($this->getParams());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get fragment from url (everything after #)
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getFragment(): ?string
|
||||
{
|
||||
return $this->fragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set url fragment
|
||||
*
|
||||
* @param string $fragment
|
||||
* @return static
|
||||
*/
|
||||
public function setFragment(string $fragment): self
|
||||
{
|
||||
$this->fragment = $fragment;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getOriginalUrl(): string
|
||||
{
|
||||
return $this->originalUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get position of value.
|
||||
* Returns -1 on failure.
|
||||
*
|
||||
* @param string $value
|
||||
* @return int
|
||||
*/
|
||||
public function indexOf(string $value): int
|
||||
{
|
||||
$index = stripos($this->getOriginalUrl(), $value);
|
||||
|
||||
return ($index === false) ? -1 : $index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if url contains value.
|
||||
*
|
||||
* @param string $value
|
||||
* @return bool
|
||||
*/
|
||||
public function contains(string $value): bool
|
||||
{
|
||||
return (stripos($this->getOriginalUrl(), $value) !== false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if url contains parameter/query string.
|
||||
*
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function hasParam(string $name): bool
|
||||
{
|
||||
return array_key_exists($name, $this->getParams());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes multiple parameters from the query-string
|
||||
*
|
||||
* @param array ...$names
|
||||
* @return static
|
||||
*/
|
||||
public function removeParams(...$names): self
|
||||
{
|
||||
$params = array_diff_key($this->getParams(), array_flip(...$names));
|
||||
$this->setParams($params);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes parameter from the query-string
|
||||
*
|
||||
* @param string $name
|
||||
* @return static
|
||||
*/
|
||||
public function removeParam(string $name): self
|
||||
{
|
||||
$params = $this->getParams();
|
||||
unset($params[$name]);
|
||||
$this->setParams($params);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parameter by name.
|
||||
* Returns parameter value or default value.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string|null $defaultValue
|
||||
* @return string|null
|
||||
*/
|
||||
public function getParam(string $name, ?string $defaultValue = null): ?string
|
||||
{
|
||||
return (isset($this->getParams()[$name]) === true) ? $this->getParams()[$name] : $defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* UTF-8 aware parse_url() replacement.
|
||||
* @param string $url
|
||||
* @param int $component
|
||||
* @return array
|
||||
* @throws MalformedUrlException
|
||||
*/
|
||||
public function parseUrl(string $url, int $component = -1): array
|
||||
{
|
||||
$encodedUrl = preg_replace_callback(
|
||||
'/[^:\/@?&=#]+/u',
|
||||
static function ($matches): string {
|
||||
return urlencode($matches[0]);
|
||||
},
|
||||
$url
|
||||
);
|
||||
|
||||
$parts = parse_url($encodedUrl, $component);
|
||||
|
||||
if ($parts === false) {
|
||||
throw new MalformedUrlException(sprintf('Failed to parse url: "%s"', $url));
|
||||
}
|
||||
|
||||
return array_map('urldecode', $parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert array to query-string params
|
||||
*
|
||||
* @param array $getParams
|
||||
* @param bool $includeEmpty
|
||||
* @return string
|
||||
*/
|
||||
public static function arrayToParams(array $getParams = [], bool $includeEmpty = true): string
|
||||
{
|
||||
if (count($getParams) !== 0) {
|
||||
|
||||
if ($includeEmpty === false) {
|
||||
$getParams = array_filter($getParams, static function ($item): bool {
|
||||
return (trim($item) !== '');
|
||||
});
|
||||
}
|
||||
|
||||
return http_build_query($getParams);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the relative url
|
||||
*
|
||||
* @param bool $includeParams
|
||||
* @return string
|
||||
*/
|
||||
public function getRelativeUrl(bool $includeParams = true): string
|
||||
{
|
||||
$path = $this->path ?? '/';
|
||||
|
||||
if ($includeParams === false) {
|
||||
return $path;
|
||||
}
|
||||
|
||||
$query = $this->getQueryString() !== '' ? '?' . $this->getQueryString() : '';
|
||||
$fragment = $this->fragment !== null ? '#' . $this->fragment : '';
|
||||
|
||||
return $path . $query . $fragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the absolute url
|
||||
*
|
||||
* @param bool $includeParams
|
||||
* @return string
|
||||
*/
|
||||
public function getAbsoluteUrl(bool $includeParams = true): string
|
||||
{
|
||||
$scheme = $this->scheme !== null ? $this->scheme . '://' : '';
|
||||
$host = $this->host ?? '';
|
||||
$port = $this->port !== null ? ':' . $this->port : '';
|
||||
$user = $this->username ?? '';
|
||||
$pass = $this->password !== null ? ':' . $this->password : '';
|
||||
$pass = ($user !== '' || $pass !== '') ? $pass . '@' : '';
|
||||
|
||||
return $scheme . $user . $pass . $host . $port . $this->getRelativeUrl($includeParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify data which should be serialized to JSON
|
||||
* @link http://php.net/manual/en/jsonserializable.jsonserialize.php
|
||||
* @return string data which can be serialized by <b>json_encode</b>,
|
||||
* which is a value of any type other than a resource.
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function jsonSerialize(): string
|
||||
{
|
||||
return $this->getHost(true) . $this->getRelativeUrl();
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->getHost(true) . $this->getRelativeUrl();
|
||||
}
|
||||
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\SimpleRouter\ClassLoader;
|
||||
|
||||
use Pecee\SimpleRouter\Exceptions\ClassNotFoundHttpException;
|
||||
|
||||
class ClassLoader implements IClassLoader
|
||||
{
|
||||
/**
|
||||
* Load class
|
||||
*
|
||||
* @param string $class
|
||||
* @return object
|
||||
* @throws ClassNotFoundHttpException
|
||||
*/
|
||||
public function loadClass(string $class)
|
||||
{
|
||||
if (class_exists($class) === false) {
|
||||
throw new ClassNotFoundHttpException($class, null, sprintf('Class "%s" does not exist', $class), 404, null);
|
||||
}
|
||||
|
||||
return new $class();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when loading class method
|
||||
* @param object $class
|
||||
* @param string $method
|
||||
* @param array $parameters
|
||||
* @return string
|
||||
*/
|
||||
public function loadClassMethod($class, string $method, array $parameters): string
|
||||
{
|
||||
return (string)call_user_func_array([$class, $method], array_values($parameters));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load closure
|
||||
*
|
||||
* @param Callable $closure
|
||||
* @param array $parameters
|
||||
* @return string
|
||||
*/
|
||||
public function loadClosure(callable $closure, array $parameters): string
|
||||
{
|
||||
return (string)call_user_func_array($closure, array_values($parameters));
|
||||
}
|
||||
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\SimpleRouter\ClassLoader;
|
||||
|
||||
interface IClassLoader
|
||||
{
|
||||
|
||||
/**
|
||||
* Called when loading class
|
||||
* @param string $class
|
||||
* @return object
|
||||
*/
|
||||
public function loadClass(string $class);
|
||||
|
||||
/**
|
||||
* Called when loading class method
|
||||
* @param object $class
|
||||
* @param string $method
|
||||
* @param array $parameters
|
||||
* @return mixed
|
||||
*/
|
||||
public function loadClassMethod($class, string $method, array $parameters);
|
||||
|
||||
/**
|
||||
* Called when loading method
|
||||
*
|
||||
* @param callable $closure
|
||||
* @param array $parameters
|
||||
* @return mixed
|
||||
*/
|
||||
public function loadClosure(Callable $closure, array $parameters);
|
||||
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\SimpleRouter\Event;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Pecee\Http\Request;
|
||||
use Pecee\SimpleRouter\Router;
|
||||
|
||||
class EventArgument implements IEventArgument
|
||||
{
|
||||
/**
|
||||
* Event name
|
||||
* @var string
|
||||
*/
|
||||
protected string $eventName;
|
||||
|
||||
/**
|
||||
* @var Router
|
||||
*/
|
||||
protected Router $router;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $arguments = [];
|
||||
|
||||
public function __construct(string $eventName, Router $router, array $arguments = [])
|
||||
{
|
||||
$this->eventName = $eventName;
|
||||
$this->router = $router;
|
||||
$this->arguments = $arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get event name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getEventName(): string
|
||||
{
|
||||
return $this->eventName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the event name
|
||||
*
|
||||
* @param string $name
|
||||
*/
|
||||
public function setEventName(string $name): void
|
||||
{
|
||||
$this->eventName = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the router instance
|
||||
*
|
||||
* @return Router
|
||||
*/
|
||||
public function getRouter(): Router
|
||||
{
|
||||
return $this->router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the request instance
|
||||
*
|
||||
* @return Request
|
||||
*/
|
||||
public function getRequest(): Request
|
||||
{
|
||||
return $this->getRouter()->getRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get(string $name)
|
||||
{
|
||||
return $this->arguments[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset(string $name): bool
|
||||
{
|
||||
return array_key_exists($name, $this->arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function __set(string $name, $value): void
|
||||
{
|
||||
throw new InvalidArgumentException('Not supported');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get arguments
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getArguments(): array
|
||||
{
|
||||
return $this->arguments;
|
||||
}
|
||||
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\SimpleRouter\Event;
|
||||
|
||||
use Pecee\Http\Request;
|
||||
use Pecee\SimpleRouter\Router;
|
||||
|
||||
interface IEventArgument
|
||||
{
|
||||
|
||||
/**
|
||||
* Get event name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getEventName(): string;
|
||||
|
||||
/**
|
||||
* Set event name
|
||||
*
|
||||
* @param string $name
|
||||
*/
|
||||
public function setEventName(string $name): void;
|
||||
|
||||
/**
|
||||
* Get router instance
|
||||
*
|
||||
* @return Router
|
||||
*/
|
||||
public function getRouter(): Router;
|
||||
|
||||
/**
|
||||
* Get request instance
|
||||
*
|
||||
* @return Request
|
||||
*/
|
||||
public function getRequest(): Request;
|
||||
|
||||
/**
|
||||
* Get all event arguments
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getArguments(): array;
|
||||
|
||||
}
|
||||
Vendored
+45
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\SimpleRouter\Exceptions;
|
||||
|
||||
use Throwable;
|
||||
|
||||
class ClassNotFoundHttpException extends NotFoundHttpException
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected string $class;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected ?string $method = null;
|
||||
|
||||
public function __construct(string $class, ?string $method = null, string $message = "", int $code = 0, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
|
||||
$this->class = $class;
|
||||
$this->method = $method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get class name
|
||||
* @return string
|
||||
*/
|
||||
public function getClass(): string
|
||||
{
|
||||
return $this->class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get method
|
||||
* @return string|null
|
||||
*/
|
||||
public function getMethod(): ?string
|
||||
{
|
||||
return $this->method;
|
||||
}
|
||||
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\SimpleRouter\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class HttpException extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\SimpleRouter\Exceptions;
|
||||
|
||||
class NotFoundHttpException extends HttpException
|
||||
{
|
||||
|
||||
}
|
||||
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\SimpleRouter\Handlers;
|
||||
|
||||
use Closure;
|
||||
use Exception;
|
||||
use Pecee\Http\Request;
|
||||
|
||||
/**
|
||||
* Class CallbackExceptionHandler
|
||||
*
|
||||
* Class is used to create callbacks which are fired when an exception is reached.
|
||||
* This allows for easy handling 404-exception etc. without creating an custom ExceptionHandler.
|
||||
*
|
||||
* @package \Pecee\SimpleRouter\Handlers
|
||||
*/
|
||||
class CallbackExceptionHandler implements IExceptionHandler
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Closure
|
||||
*/
|
||||
protected Closure $callback;
|
||||
|
||||
public function __construct(Closure $callback)
|
||||
{
|
||||
$this->callback = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Exception $error
|
||||
*/
|
||||
public function handleError(Request $request, Exception $error): void
|
||||
{
|
||||
/* Fire exceptions */
|
||||
call_user_func($this->callback,
|
||||
$request,
|
||||
$error
|
||||
);
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\SimpleRouter\Handlers;
|
||||
|
||||
use Closure;
|
||||
use Pecee\SimpleRouter\Event\EventArgument;
|
||||
use Pecee\SimpleRouter\Router;
|
||||
|
||||
class DebugEventHandler implements IEventHandler
|
||||
{
|
||||
|
||||
/**
|
||||
* Debug callback
|
||||
* @var Closure
|
||||
*/
|
||||
protected Closure $callback;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->callback = static function (EventArgument $argument): void {
|
||||
// todo: log in database
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get events.
|
||||
*
|
||||
* @param string|null $name Filter events by name.
|
||||
* @return array
|
||||
*/
|
||||
public function getEvents(?string $name): array
|
||||
{
|
||||
return [
|
||||
$name => [
|
||||
$this->callback,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires any events registered with given event-name
|
||||
*
|
||||
* @param Router $router Router instance
|
||||
* @param string $name Event name
|
||||
* @param array $eventArgs Event arguments
|
||||
*/
|
||||
public function fireEvents(Router $router, string $name, array $eventArgs = []): void
|
||||
{
|
||||
$callback = $this->callback;
|
||||
$callback(new EventArgument($name, $router, $eventArgs));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set debug callback
|
||||
*
|
||||
* @param Closure $event
|
||||
*/
|
||||
public function setCallback(Closure $event): void
|
||||
{
|
||||
$this->callback = $event;
|
||||
}
|
||||
|
||||
}
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\SimpleRouter\Handlers;
|
||||
|
||||
use Closure;
|
||||
use Pecee\SimpleRouter\Event\EventArgument;
|
||||
use Pecee\SimpleRouter\Router;
|
||||
|
||||
class EventHandler implements IEventHandler
|
||||
{
|
||||
/**
|
||||
* Fires when a event is triggered.
|
||||
*/
|
||||
public const EVENT_ALL = '*';
|
||||
|
||||
/**
|
||||
* Fires when router is initializing and before routes are loaded.
|
||||
*/
|
||||
public const EVENT_INIT = 'onInit';
|
||||
|
||||
/**
|
||||
* Fires when all routes has been loaded and rendered, just before the output is returned.
|
||||
*/
|
||||
public const EVENT_LOAD = 'onLoad';
|
||||
|
||||
/**
|
||||
* Fires when route is added to the router
|
||||
*/
|
||||
public const EVENT_ADD_ROUTE = 'onAddRoute';
|
||||
|
||||
/**
|
||||
* Fires when a url-rewrite is and just before the routes are re-initialized.
|
||||
*/
|
||||
public const EVENT_REWRITE = 'onRewrite';
|
||||
|
||||
/**
|
||||
* Fires when the router is booting.
|
||||
* This happens just before boot-managers are rendered and before any routes has been loaded.
|
||||
*/
|
||||
public const EVENT_BOOT = 'onBoot';
|
||||
|
||||
/**
|
||||
* Fires before a boot-manager is rendered.
|
||||
*/
|
||||
public const EVENT_RENDER_BOOTMANAGER = 'onRenderBootManager';
|
||||
|
||||
/**
|
||||
* Fires when the router is about to load all routes.
|
||||
*/
|
||||
public const EVENT_LOAD_ROUTES = 'onLoadRoutes';
|
||||
|
||||
/**
|
||||
* Fires whenever the `findRoute` method is called within the `Router`.
|
||||
* This usually happens when the router tries to find routes that
|
||||
* contains a certain url, usually after the EventHandler::EVENT_GET_URL event.
|
||||
*/
|
||||
public const EVENT_FIND_ROUTE = 'onFindRoute';
|
||||
|
||||
/**
|
||||
* Fires whenever the `Router::getUrl` method or `url`-helper function
|
||||
* is called and the router tries to find the route.
|
||||
*/
|
||||
public const EVENT_GET_URL = 'onGetUrl';
|
||||
|
||||
/**
|
||||
* Fires when a route is matched and valid (correct request-type etc).
|
||||
* and before the route is rendered.
|
||||
*/
|
||||
public const EVENT_MATCH_ROUTE = 'onMatchRoute';
|
||||
|
||||
/**
|
||||
* Fires before a route is rendered.
|
||||
*/
|
||||
public const EVENT_RENDER_ROUTE = 'onRenderRoute';
|
||||
|
||||
/**
|
||||
* Fires when the router is loading exception-handlers.
|
||||
*/
|
||||
public const EVENT_LOAD_EXCEPTIONS = 'onLoadExceptions';
|
||||
|
||||
/**
|
||||
* Fires before the router is rendering a exception-handler.
|
||||
*/
|
||||
public const EVENT_RENDER_EXCEPTION = 'onRenderException';
|
||||
|
||||
/**
|
||||
* Fires before a middleware is rendered.
|
||||
*/
|
||||
public const EVENT_RENDER_MIDDLEWARES = 'onRenderMiddlewares';
|
||||
|
||||
/**
|
||||
* Fires before the CSRF-verifier is rendered.
|
||||
*/
|
||||
public const EVENT_RENDER_CSRF = 'onRenderCsrfVerifier';
|
||||
|
||||
/**
|
||||
* All available events
|
||||
* @var array
|
||||
*/
|
||||
public static array $events = [
|
||||
self::EVENT_ALL,
|
||||
self::EVENT_INIT,
|
||||
self::EVENT_LOAD,
|
||||
self::EVENT_ADD_ROUTE,
|
||||
self::EVENT_REWRITE,
|
||||
self::EVENT_BOOT,
|
||||
self::EVENT_RENDER_BOOTMANAGER,
|
||||
self::EVENT_LOAD_ROUTES,
|
||||
self::EVENT_FIND_ROUTE,
|
||||
self::EVENT_GET_URL,
|
||||
self::EVENT_MATCH_ROUTE,
|
||||
self::EVENT_RENDER_ROUTE,
|
||||
self::EVENT_LOAD_EXCEPTIONS,
|
||||
self::EVENT_RENDER_EXCEPTION,
|
||||
self::EVENT_RENDER_MIDDLEWARES,
|
||||
self::EVENT_RENDER_CSRF,
|
||||
];
|
||||
|
||||
/**
|
||||
* List of all registered events
|
||||
* @var array
|
||||
*/
|
||||
private array $registeredEvents = [];
|
||||
|
||||
/**
|
||||
* Register new event
|
||||
*
|
||||
* @param string $name
|
||||
* @param Closure $callback
|
||||
* @return static
|
||||
*/
|
||||
public function register(string $name, Closure $callback): IEventHandler
|
||||
{
|
||||
if (isset($this->registeredEvents[$name]) === true) {
|
||||
$this->registeredEvents[$name][] = $callback;
|
||||
} else {
|
||||
$this->registeredEvents[$name] = [$callback];
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get events.
|
||||
*
|
||||
* @param string|null $name Filter events by name.
|
||||
* @param array|string ...$names Add multiple names...
|
||||
* @return array
|
||||
*/
|
||||
public function getEvents(?string $name, ...$names): array
|
||||
{
|
||||
if ($name === null) {
|
||||
return $this->registeredEvents;
|
||||
}
|
||||
|
||||
$names[] = $name;
|
||||
$events = [];
|
||||
|
||||
foreach ($names as $eventName) {
|
||||
if (isset($this->registeredEvents[$eventName]) === true) {
|
||||
$events += $this->registeredEvents[$eventName];
|
||||
}
|
||||
}
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires any events registered with given event-name
|
||||
*
|
||||
* @param Router $router Router instance
|
||||
* @param string $name Event name
|
||||
* @param array $eventArgs Event arguments
|
||||
*/
|
||||
public function fireEvents(Router $router, string $name, array $eventArgs = []): void
|
||||
{
|
||||
$events = $this->getEvents(static::EVENT_ALL, $name);
|
||||
|
||||
/* @var $event Closure */
|
||||
foreach ($events as $event) {
|
||||
$event(new EventArgument($name, $router, $eventArgs));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\SimpleRouter\Handlers;
|
||||
|
||||
use Pecee\SimpleRouter\Router;
|
||||
|
||||
interface IEventHandler
|
||||
{
|
||||
|
||||
/**
|
||||
* Get events.
|
||||
*
|
||||
* @param string|null $name Filter events by name.
|
||||
* @return array
|
||||
*/
|
||||
public function getEvents(?string $name): array;
|
||||
|
||||
/**
|
||||
* Fires any events registered with given event-name
|
||||
*
|
||||
* @param Router $router Router instance
|
||||
* @param string $name Event name
|
||||
* @param array $eventArgs Event arguments
|
||||
*/
|
||||
public function fireEvents(Router $router, string $name, array $eventArgs = []): void;
|
||||
|
||||
}
|
||||
Loaded 100 of 156 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user