diff --git a/web/Dockerfile b/web/Dockerfile index 87bd85b..3d916a3 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -4,12 +4,9 @@ ARG CLONE_ADDFLAG ENV DEBIAN_FRONTEND=noninteractive RUN dpkg -s gnupg 2>/dev/null || (apt-get update && apt-get install -y gnupg) &&\ -echo "deb http://ppa.launchpad.net/stesie/libv8/ubuntu bionic main" | tee /etc/apt/sources.list.d/stesie-libv8.list && apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 1A10946ED858A0DF &&\ echo "deb http://ppa.launchpad.net/ondrej/php/ubuntu jammy main" | tee /etc/apt/sources.list.d/ondrej-php.list && apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4F4EA0AAE5267A6C &&\ apt-get update --allow-unauthenticated &&\ -apt-get install -y --allow-unauthenticated -o Dpkg::Options::="--force-overwrite" libv8 php7.4 php7.4-yaml php7.4-xml php7.4-dev php7.4-zip php7.4-mysql php7.4-mbstring php7.4-gd libseccomp-dev git vim ntp zip unzip curl wget libapache2-mod-xsendfile mysql-server php-pear cmake fp-compiler re2c libv8-7.5-dev libyaml-dev python2.7 python3.10 python3-requests openjdk-8-jdk openjdk-11-jdk openjdk-17-jdk &&\ -git clone https://github.com/phpv8/v8js.git --depth=1 -b 2.1.2 /tmp/pear/download/v8js-master && cd /tmp/pear/download/v8js-master &&\ -phpize && ./configure --with-php-config=/usr/bin/php-config --with-v8js=/opt/libv8-7.5 && make install && cd - +apt-get install -y --allow-unauthenticated -o Dpkg::Options::="--force-overwrite" php7.4 php7.4-yaml php7.4-xml php7.4-dev php7.4-zip php7.4-mysql php7.4-mbstring php7.4-gd libseccomp-dev git vim ntp zip unzip curl wget libapache2-mod-xsendfile mysql-server php-pear cmake fp-compiler re2c libyaml-dev python2.7 python3.10 python3-requests openjdk-8-jdk openjdk-11-jdk openjdk-17-jdk ADD . /opt/uoj WORKDIR /opt/uoj diff --git a/web/app/composer.json b/web/app/composer.json index 386b74e..dfc4587 100644 --- a/web/app/composer.json +++ b/web/app/composer.json @@ -2,6 +2,15 @@ "require": { "gregwar/captcha": "^1.1", "phpmailer/phpmailer": "^6.6", - "ezyang/htmlpurifier": "^4.16" - } + "ezyang/htmlpurifier": "^4.16", + "erusev/parsedown": "^1.7", + "benjaminhoegh/parsedown-math": "dev-master" + }, + "autoload": { + "classmap": [ + "vendor/benjaminhoegh/parsedown-math/ParsedownMath.php" + ] + }, + "minimum-stability": "dev", + "prefer-stable": true } diff --git a/web/app/controllers/problem.php b/web/app/controllers/problem.php index 0da9869..be8c8f5 100644 --- a/web/app/controllers/problem.php +++ b/web/app/controllers/problem.php @@ -222,7 +222,12 @@ EOD @@ -522,15 +527,6 @@ $('#contest-countdown').countdown(getTimestamp() - UOJ - - - diff --git a/web/app/controllers/problem_managers_manage.php b/web/app/controllers/problem_managers_manage.php index 1134fba..99bf811 100644 --- a/web/app/controllers/problem_managers_manage.php +++ b/web/app/controllers/problem_managers_manage.php @@ -192,14 +192,6 @@ table-hover - - diff --git a/web/app/controllers/problem_solutions.php b/web/app/controllers/problem_solutions.php index 929d32e..1eeaebc 100644 --- a/web/app/controllers/problem_solutions.php +++ b/web/app/controllers/problem_solutions.php @@ -298,12 +298,4 @@ EOD; - - diff --git a/web/app/controllers/problem_statement_manage.php b/web/app/controllers/problem_statement_manage.php index bd90e34..54d825a 100644 --- a/web/app/controllers/problem_statement_manage.php +++ b/web/app/controllers/problem_statement_manage.php @@ -168,14 +168,6 @@ - - diff --git a/web/app/controllers/problem_statistics.php b/web/app/controllers/problem_statistics.php index bd1819b..f1b5331 100644 --- a/web/app/controllers/problem_statistics.php +++ b/web/app/controllers/problem_statistics.php @@ -369,15 +369,6 @@ $('#contest-countdown').countdown(getTimestamp() - UOJ - - - diff --git a/web/app/controllers/subdomain/blog/archive.php b/web/app/controllers/subdomain/blog/archive.php index 8c081a2..36d3cc2 100644 --- a/web/app/controllers/subdomain/blog/archive.php +++ b/web/app/controllers/subdomain/blog/archive.php @@ -36,7 +36,7 @@ $all_tags = DB::selectAll("select distinct tag from blogs_tags where blog_id in (select id from blogs where $blogs_cond)"); requireLib('mathjax'); - requireLib('shjs'); + requireLib('hljs'); ?> diff --git a/web/app/controllers/subdomain/blog/index.php b/web/app/controllers/subdomain/blog/index.php index 2b1263a..86d69dd 100644 --- a/web/app/controllers/subdomain/blog/index.php +++ b/web/app/controllers/subdomain/blog/index.php @@ -23,7 +23,7 @@ $REQUIRE_LIB['mathjax'] = ''; if (isset($REQUIRE_LIB['bootstrap5'])) { - $REQUIRE_LIB['hljs'] = ''; + requireLib('hljs'); } else { $REQUIRE_LIB['shjs'] = ''; } diff --git a/web/app/models/HTML.php b/web/app/models/HTML.php index b9b127f..e361358 100644 --- a/web/app/models/HTML.php +++ b/web/app/models/HTML.php @@ -143,7 +143,6 @@ class HTML { } public static function purifier() { - include_once $_SERVER['DOCUMENT_ROOT'] . '/app/vendor/htmlpurifier/HTMLPurifier.auto.php'; $config = HTMLPurifier_Config::createDefault(); //$config->set('HTML.Doctype', 'HTML 4.01 Transitional'); $config->set('Cache.DefinitionImpl', null); @@ -160,8 +159,6 @@ class HTML { } public static function purifier_inline() { - include_once $_SERVER['DOCUMENT_ROOT'] . '/app/vendor/htmlpurifier/HTMLPurifier.auto.php'; - $allowed_html = [ 'a' => ['href' => 'URI'], 'b' => [], diff --git a/web/app/models/UOJBlogEditor.php b/web/app/models/UOJBlogEditor.php index a406225..c807a67 100644 --- a/web/app/models/UOJBlogEditor.php +++ b/web/app/models/UOJBlogEditor.php @@ -102,20 +102,26 @@ class UOJBlogEditor { $this->post_data['is_hidden'] = isset($_POST["{$this->name}_is_hidden"]) ? 1 : 0; $purifier = HTML::purifier(); + $parsedown = new ParsedownMath([ + 'math' => [ + 'enabled' => true, + 'matchSingleDollar' => true + ] + ]); $this->post_data['title'] = HTML::escape($this->post_data['title']); if ($this->show_editor) { if ($this->type == 'blog') { $content_md = $_POST[$this->name . '_content_md']; - try { - $v8 = new V8Js('POST'); - $v8->content_md = $this->post_data['content_md']; - $v8->executeString(file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/js/marked.js'), 'marked.js'); - $this->post_data['content'] = $v8->executeString('marked(POST.content_md)'); - } catch (V8JsException $e) { - die(json_encode(array('content_md' => '未知错误'))); + $dom = new DOMDocument; + $dom->loadHTML(mb_convert_encoding($parsedown->text($this->post_data['content_md']), 'HTML-ENTITIES', 'UTF-8')); + $elements = $dom->getElementsByTagName('table'); + foreach ($elements as $element) { + $element->setAttribute('class', + $element->getAttribute('class') . ' table table-bordered'); } + $this->post_data['content'] = $purifier->purify($dom->saveHTML()); if (preg_match('/^.*.*$/m', $this->post_data['content'], $matches, PREG_OFFSET_CAPTURE)) { $content_less = substr($this->post_data['content'], 0, $matches[0][1]); @@ -130,44 +136,17 @@ class UOJBlogEditor { die(json_encode(array('content_md' => '不合法的 YAML 格式'))); } - try { - $v8 = new V8Js('PHP'); - $v8->executeString(file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/js/marked.js'), 'marked.js'); - $v8->executeString(<< '未知错误'))); - } - - $marked = function($md) use ($v8, $purifier) { - try { - $v8->md = $md; - return $purifier->purify($v8->executeString('marked(PHP.md)')); - } catch (V8JsException $e) { - die(json_encode(array('content_md' => '未知错误'))); + $marked = function($md) use ($parsedown, $purifier) { + $dom = new DOMDocument; + $dom->loadHTML(mb_convert_encoding($parsedown->text($md), 'HTML-ENTITIES', 'UTF-8')); + $elements = $dom->getElementsByTagName('li'); + + foreach ($elements as $element) { + $element->setAttribute('class', + $element->getAttribute('class') . ' fragment'); } + + return $purifier->purify($dom->saveHTML()); }; $config = array(); @@ -231,18 +210,6 @@ EOD echo '
'; echo $this->post_data['content']; echo '
'; - - if (isset($REQUIRE_LIB['bootstrap5'])) { - echo << -$(document).ready(function() { - $('.markdown-body table').each(function() { - $(this).addClass('table table-bordered'); - }); -}); - -EOD; - } echoUOJPageFooter(array('ShowPageFooter' => false)); } elseif ($this->type == 'slide') { uojIncludeView('slide', array_merge( diff --git a/web/app/models/UOJMail.php b/web/app/models/UOJMail.php index 4bc0dac..d9978a6 100644 --- a/web/app/models/UOJMail.php +++ b/web/app/models/UOJMail.php @@ -1,6 +1,6 @@ $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', - 'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php', - 'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', - 'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php', - 'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php', + 'DynamicParent' => $vendorDir . '/benjaminhoegh/parsedown-math/ParsedownMath.php', + 'ParsedownMath' => $vendorDir . '/benjaminhoegh/parsedown-math/ParsedownMath.php', ); diff --git a/web/app/vendor/composer/autoload_files.php b/web/app/vendor/composer/autoload_files.php index 0b49593..c25686b 100644 --- a/web/app/vendor/composer/autoload_files.php +++ b/web/app/vendor/composer/autoload_files.php @@ -6,7 +6,5 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( - '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php', - 'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php', '2cffec82183ee1cea088009cef9a6fc3' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php', ); diff --git a/web/app/vendor/composer/autoload_namespaces.php b/web/app/vendor/composer/autoload_namespaces.php index 1ee7a39..42fad91 100644 --- a/web/app/vendor/composer/autoload_namespaces.php +++ b/web/app/vendor/composer/autoload_namespaces.php @@ -6,5 +6,7 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( + 'parsedownMath' => array($vendorDir . '/benjaminhoegh/parsedown-math'), + 'Parsedown' => array($vendorDir . '/erusev/parsedown'), 'HTMLPurifier' => array($vendorDir . '/ezyang/htmlpurifier/library'), ); diff --git a/web/app/vendor/composer/autoload_psr4.php b/web/app/vendor/composer/autoload_psr4.php index d7892aa..bd5bb53 100644 --- a/web/app/vendor/composer/autoload_psr4.php +++ b/web/app/vendor/composer/autoload_psr4.php @@ -6,7 +6,6 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( - 'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'), 'Symfony\\Component\\Finder\\' => array($vendorDir . '/symfony/finder'), 'PHPMailer\\PHPMailer\\' => array($vendorDir . '/phpmailer/phpmailer/src'), 'Gregwar\\' => array($vendorDir . '/gregwar/captcha/src/Gregwar'), diff --git a/web/app/vendor/composer/autoload_static.php b/web/app/vendor/composer/autoload_static.php index 1b7c7cd..30df0e0 100644 --- a/web/app/vendor/composer/autoload_static.php +++ b/web/app/vendor/composer/autoload_static.php @@ -7,15 +7,12 @@ namespace Composer\Autoload; class ComposerStaticInit0d7c2cd5c2dbf2120e4372996869e900 { public static $files = array ( - '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php', - 'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php', '2cffec82183ee1cea088009cef9a6fc3' => __DIR__ . '/..' . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php', ); public static $prefixLengthsPsr4 = array ( 'S' => array ( - 'Symfony\\Polyfill\\Php80\\' => 23, 'Symfony\\Component\\Finder\\' => 25, ), 'P' => @@ -29,10 +26,6 @@ class ComposerStaticInit0d7c2cd5c2dbf2120e4372996869e900 ); public static $prefixDirsPsr4 = array ( - 'Symfony\\Polyfill\\Php80\\' => - array ( - 0 => __DIR__ . '/..' . '/symfony/polyfill-php80', - ), 'Symfony\\Component\\Finder\\' => array ( 0 => __DIR__ . '/..' . '/symfony/finder', @@ -48,6 +41,20 @@ class ComposerStaticInit0d7c2cd5c2dbf2120e4372996869e900 ); public static $prefixesPsr0 = array ( + 'p' => + array ( + 'parsedownMath' => + array ( + 0 => __DIR__ . '/..' . '/benjaminhoegh/parsedown-math', + ), + ), + 'P' => + array ( + 'Parsedown' => + array ( + 0 => __DIR__ . '/..' . '/erusev/parsedown', + ), + ), 'H' => array ( 'HTMLPurifier' => @@ -58,11 +65,8 @@ class ComposerStaticInit0d7c2cd5c2dbf2120e4372996869e900 ); public static $classMap = array ( - 'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', - 'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php', - 'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', - 'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php', - 'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php', + 'DynamicParent' => __DIR__ . '/..' . '/benjaminhoegh/parsedown-math/ParsedownMath.php', + 'ParsedownMath' => __DIR__ . '/..' . '/benjaminhoegh/parsedown-math/ParsedownMath.php', ); public static function getInitializer(ClassLoader $loader) diff --git a/web/app/vendor/composer/installed.json b/web/app/vendor/composer/installed.json index 3a63e6b..2903c4d 100644 --- a/web/app/vendor/composer/installed.json +++ b/web/app/vendor/composer/installed.json @@ -1,4 +1,101 @@ [ + { + "name": "benjaminhoegh/parsedown-math", + "version": "dev-master", + "version_normalized": "9999999-dev", + "source": { + "type": "git", + "url": "https://github.com/BenjaminHoegh/ParsedownMath.git", + "reference": "668b11b80c7d1dc9812274405426cb62bbb4d78b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/BenjaminHoegh/ParsedownMath/zipball/668b11b80c7d1dc9812274405426cb62bbb4d78b", + "reference": "668b11b80c7d1dc9812274405426cb62bbb4d78b", + "shasum": "" + }, + "require": { + "php": "^5.6||^8.0" + }, + "time": "2022-05-17T18:37:50+00:00", + "type": "library", + "installation-source": "source", + "autoload": { + "psr-0": { + "parsedownMath": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "An extension of Parsedown that adds support for LaTeX.", + "homepage": "https://github.com/BenjaminHoegh/ParsedownMath", + "keywords": [ + "latex", + "markdown", + "parsedown", + "parser" + ], + "funding": [ + { + "url": "https://paypal.me/BenjaminHoegh", + "type": "custom" + }, + { + "url": "https://www.buymeacoffee.com/BenjaminHoegh", + "type": "custom" + } + ] + }, + { + "name": "erusev/parsedown", + "version": "1.7.4", + "version_normalized": "1.7.4.0", + "source": { + "type": "git", + "url": "https://github.com/erusev/parsedown.git", + "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/erusev/parsedown/zipball/cb17b6477dfff935958ba01325f2e8a2bfa6dab3", + "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35" + }, + "time": "2019-12-30T22:54:17+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "Parsedown": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Emanuil Rusev", + "email": "hello@erusev.com", + "homepage": "http://erusev.com" + } + ], + "description": "Parser for Markdown.", + "homepage": "http://parsedown.org", + "keywords": [ + "markdown", + "parser" + ] + }, { "name": "ezyang/htmlpurifier", "version": "v4.16.0", @@ -189,93 +286,28 @@ } ] }, - { - "name": "symfony/deprecation-contracts", - "version": "v2.5.2", - "version_normalized": "2.5.2.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66", - "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "time": "2022-01-02T09:53:40+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "installation-source": "dist", - "autoload": { - "files": [ - "function.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ] - }, { "name": "symfony/finder", - "version": "v5.4.11", - "version_normalized": "5.4.11.0", + "version": "v6.1.3", + "version_normalized": "6.1.3.0", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "7872a66f57caffa2916a584db1aa7f12adc76f8c" + "reference": "39696bff2c2970b3779a5cac7bf9f0b88fc2b709" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/7872a66f57caffa2916a584db1aa7f12adc76f8c", - "reference": "7872a66f57caffa2916a584db1aa7f12adc76f8c", + "url": "https://api.github.com/repos/symfony/finder/zipball/39696bff2c2970b3779a5cac7bf9f0b88fc2b709", + "reference": "39696bff2c2970b3779a5cac7bf9f0b88fc2b709", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1" }, - "time": "2022-07-29T07:37:50+00:00", + "require-dev": { + "symfony/filesystem": "^6.0" + }, + "time": "2022-07-29T07:42:06+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -316,87 +348,5 @@ "type": "tidelift" } ] - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.26.0", - "version_normalized": "1.26.0.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace", - "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "time": "2022-05-10T07:21:04+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "installation-source": "dist", - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ] } ] diff --git a/web/app/vendor/erusev/parsedown/LICENSE.txt b/web/app/vendor/erusev/parsedown/LICENSE.txt new file mode 100644 index 0000000..8e7c764 --- /dev/null +++ b/web/app/vendor/erusev/parsedown/LICENSE.txt @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013-2018 Emanuil Rusev, erusev.com + +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. diff --git a/web/app/vendor/erusev/parsedown/Parsedown.php b/web/app/vendor/erusev/parsedown/Parsedown.php new file mode 100644 index 0000000..1b9d6d5 --- /dev/null +++ b/web/app/vendor/erusev/parsedown/Parsedown.php @@ -0,0 +1,1712 @@ +DefinitionData = array(); + + # standardize line breaks + $text = str_replace(array("\r\n", "\r"), "\n", $text); + + # remove surrounding line breaks + $text = trim($text, "\n"); + + # split text into lines + $lines = explode("\n", $text); + + # iterate through lines to identify blocks + $markup = $this->lines($lines); + + # trim line breaks + $markup = trim($markup, "\n"); + + return $markup; + } + + # + # Setters + # + + function setBreaksEnabled($breaksEnabled) + { + $this->breaksEnabled = $breaksEnabled; + + return $this; + } + + protected $breaksEnabled; + + function setMarkupEscaped($markupEscaped) + { + $this->markupEscaped = $markupEscaped; + + return $this; + } + + protected $markupEscaped; + + function setUrlsLinked($urlsLinked) + { + $this->urlsLinked = $urlsLinked; + + return $this; + } + + protected $urlsLinked = true; + + function setSafeMode($safeMode) + { + $this->safeMode = (bool) $safeMode; + + return $this; + } + + protected $safeMode; + + protected $safeLinksWhitelist = array( + 'http://', + 'https://', + 'ftp://', + 'ftps://', + 'mailto:', + 'data:image/png;base64,', + 'data:image/gif;base64,', + 'data:image/jpeg;base64,', + 'irc:', + 'ircs:', + 'git:', + 'ssh:', + 'news:', + 'steam:', + ); + + # + # Lines + # + + protected $BlockTypes = array( + '#' => array('Header'), + '*' => array('Rule', 'List'), + '+' => array('List'), + '-' => array('SetextHeader', 'Table', 'Rule', 'List'), + '0' => array('List'), + '1' => array('List'), + '2' => array('List'), + '3' => array('List'), + '4' => array('List'), + '5' => array('List'), + '6' => array('List'), + '7' => array('List'), + '8' => array('List'), + '9' => array('List'), + ':' => array('Table'), + '<' => array('Comment', 'Markup'), + '=' => array('SetextHeader'), + '>' => array('Quote'), + '[' => array('Reference'), + '_' => array('Rule'), + '`' => array('FencedCode'), + '|' => array('Table'), + '~' => array('FencedCode'), + ); + + # ~ + + protected $unmarkedBlockTypes = array( + 'Code', + ); + + # + # Blocks + # + + protected function lines(array $lines) + { + $CurrentBlock = null; + + foreach ($lines as $line) + { + if (chop($line) === '') + { + if (isset($CurrentBlock)) + { + $CurrentBlock['interrupted'] = true; + } + + continue; + } + + if (strpos($line, "\t") !== false) + { + $parts = explode("\t", $line); + + $line = $parts[0]; + + unset($parts[0]); + + foreach ($parts as $part) + { + $shortage = 4 - mb_strlen($line, 'utf-8') % 4; + + $line .= str_repeat(' ', $shortage); + $line .= $part; + } + } + + $indent = 0; + + while (isset($line[$indent]) and $line[$indent] === ' ') + { + $indent ++; + } + + $text = $indent > 0 ? substr($line, $indent) : $line; + + # ~ + + $Line = array('body' => $line, 'indent' => $indent, 'text' => $text); + + # ~ + + if (isset($CurrentBlock['continuable'])) + { + $Block = $this->{'block'.$CurrentBlock['type'].'Continue'}($Line, $CurrentBlock); + + if (isset($Block)) + { + $CurrentBlock = $Block; + + continue; + } + else + { + if ($this->isBlockCompletable($CurrentBlock['type'])) + { + $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock); + } + } + } + + # ~ + + $marker = $text[0]; + + # ~ + + $blockTypes = $this->unmarkedBlockTypes; + + if (isset($this->BlockTypes[$marker])) + { + foreach ($this->BlockTypes[$marker] as $blockType) + { + $blockTypes []= $blockType; + } + } + + # + # ~ + + foreach ($blockTypes as $blockType) + { + $Block = $this->{'block'.$blockType}($Line, $CurrentBlock); + + if (isset($Block)) + { + $Block['type'] = $blockType; + + if ( ! isset($Block['identified'])) + { + $Blocks []= $CurrentBlock; + + $Block['identified'] = true; + } + + if ($this->isBlockContinuable($blockType)) + { + $Block['continuable'] = true; + } + + $CurrentBlock = $Block; + + continue 2; + } + } + + # ~ + + if (isset($CurrentBlock) and ! isset($CurrentBlock['type']) and ! isset($CurrentBlock['interrupted'])) + { + $CurrentBlock['element']['text'] .= "\n".$text; + } + else + { + $Blocks []= $CurrentBlock; + + $CurrentBlock = $this->paragraph($Line); + + $CurrentBlock['identified'] = true; + } + } + + # ~ + + if (isset($CurrentBlock['continuable']) and $this->isBlockCompletable($CurrentBlock['type'])) + { + $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock); + } + + # ~ + + $Blocks []= $CurrentBlock; + + unset($Blocks[0]); + + # ~ + + $markup = ''; + + foreach ($Blocks as $Block) + { + if (isset($Block['hidden'])) + { + continue; + } + + $markup .= "\n"; + $markup .= isset($Block['markup']) ? $Block['markup'] : $this->element($Block['element']); + } + + $markup .= "\n"; + + # ~ + + return $markup; + } + + protected function isBlockContinuable($Type) + { + return method_exists($this, 'block'.$Type.'Continue'); + } + + protected function isBlockCompletable($Type) + { + return method_exists($this, 'block'.$Type.'Complete'); + } + + # + # Code + + protected function blockCode($Line, $Block = null) + { + if (isset($Block) and ! isset($Block['type']) and ! isset($Block['interrupted'])) + { + return; + } + + if ($Line['indent'] >= 4) + { + $text = substr($Line['body'], 4); + + $Block = array( + 'element' => array( + 'name' => 'pre', + 'handler' => 'element', + 'text' => array( + 'name' => 'code', + 'text' => $text, + ), + ), + ); + + return $Block; + } + } + + protected function blockCodeContinue($Line, $Block) + { + if ($Line['indent'] >= 4) + { + if (isset($Block['interrupted'])) + { + $Block['element']['text']['text'] .= "\n"; + + unset($Block['interrupted']); + } + + $Block['element']['text']['text'] .= "\n"; + + $text = substr($Line['body'], 4); + + $Block['element']['text']['text'] .= $text; + + return $Block; + } + } + + protected function blockCodeComplete($Block) + { + $text = $Block['element']['text']['text']; + + $Block['element']['text']['text'] = $text; + + return $Block; + } + + # + # Comment + + protected function blockComment($Line) + { + if ($this->markupEscaped or $this->safeMode) + { + return; + } + + if (isset($Line['text'][3]) and $Line['text'][3] === '-' and $Line['text'][2] === '-' and $Line['text'][1] === '!') + { + $Block = array( + 'markup' => $Line['body'], + ); + + if (preg_match('/-->$/', $Line['text'])) + { + $Block['closed'] = true; + } + + return $Block; + } + } + + protected function blockCommentContinue($Line, array $Block) + { + if (isset($Block['closed'])) + { + return; + } + + $Block['markup'] .= "\n" . $Line['body']; + + if (preg_match('/-->$/', $Line['text'])) + { + $Block['closed'] = true; + } + + return $Block; + } + + # + # Fenced Code + + protected function blockFencedCode($Line) + { + if (preg_match('/^['.$Line['text'][0].']{3,}[ ]*([^`]+)?[ ]*$/', $Line['text'], $matches)) + { + $Element = array( + 'name' => 'code', + 'text' => '', + ); + + if (isset($matches[1])) + { + /** + * https://www.w3.org/TR/2011/WD-html5-20110525/elements.html#classes + * Every HTML element may have a class attribute specified. + * The attribute, if specified, must have a value that is a set + * of space-separated tokens representing the various classes + * that the element belongs to. + * [...] + * The space characters, for the purposes of this specification, + * are U+0020 SPACE, U+0009 CHARACTER TABULATION (tab), + * U+000A LINE FEED (LF), U+000C FORM FEED (FF), and + * U+000D CARRIAGE RETURN (CR). + */ + $language = substr($matches[1], 0, strcspn($matches[1], " \t\n\f\r")); + + $class = 'language-'.$language; + + $Element['attributes'] = array( + 'class' => $class, + ); + } + + $Block = array( + 'char' => $Line['text'][0], + 'element' => array( + 'name' => 'pre', + 'handler' => 'element', + 'text' => $Element, + ), + ); + + return $Block; + } + } + + protected function blockFencedCodeContinue($Line, $Block) + { + if (isset($Block['complete'])) + { + return; + } + + if (isset($Block['interrupted'])) + { + $Block['element']['text']['text'] .= "\n"; + + unset($Block['interrupted']); + } + + if (preg_match('/^'.$Block['char'].'{3,}[ ]*$/', $Line['text'])) + { + $Block['element']['text']['text'] = substr($Block['element']['text']['text'], 1); + + $Block['complete'] = true; + + return $Block; + } + + $Block['element']['text']['text'] .= "\n".$Line['body']; + + return $Block; + } + + protected function blockFencedCodeComplete($Block) + { + $text = $Block['element']['text']['text']; + + $Block['element']['text']['text'] = $text; + + return $Block; + } + + # + # Header + + protected function blockHeader($Line) + { + if (isset($Line['text'][1])) + { + $level = 1; + + while (isset($Line['text'][$level]) and $Line['text'][$level] === '#') + { + $level ++; + } + + if ($level > 6) + { + return; + } + + $text = trim($Line['text'], '# '); + + $Block = array( + 'element' => array( + 'name' => 'h' . min(6, $level), + 'text' => $text, + 'handler' => 'line', + ), + ); + + return $Block; + } + } + + # + # List + + protected function blockList($Line) + { + list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]+[.]'); + + if (preg_match('/^('.$pattern.'[ ]+)(.*)/', $Line['text'], $matches)) + { + $Block = array( + 'indent' => $Line['indent'], + 'pattern' => $pattern, + 'element' => array( + 'name' => $name, + 'handler' => 'elements', + ), + ); + + if($name === 'ol') + { + $listStart = stristr($matches[0], '.', true); + + if($listStart !== '1') + { + $Block['element']['attributes'] = array('start' => $listStart); + } + } + + $Block['li'] = array( + 'name' => 'li', + 'handler' => 'li', + 'text' => array( + $matches[2], + ), + ); + + $Block['element']['text'] []= & $Block['li']; + + return $Block; + } + } + + protected function blockListContinue($Line, array $Block) + { + if ($Block['indent'] === $Line['indent'] and preg_match('/^'.$Block['pattern'].'(?:[ ]+(.*)|$)/', $Line['text'], $matches)) + { + if (isset($Block['interrupted'])) + { + $Block['li']['text'] []= ''; + + $Block['loose'] = true; + + unset($Block['interrupted']); + } + + unset($Block['li']); + + $text = isset($matches[1]) ? $matches[1] : ''; + + $Block['li'] = array( + 'name' => 'li', + 'handler' => 'li', + 'text' => array( + $text, + ), + ); + + $Block['element']['text'] []= & $Block['li']; + + return $Block; + } + + if ($Line['text'][0] === '[' and $this->blockReference($Line)) + { + return $Block; + } + + if ( ! isset($Block['interrupted'])) + { + $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']); + + $Block['li']['text'] []= $text; + + return $Block; + } + + if ($Line['indent'] > 0) + { + $Block['li']['text'] []= ''; + + $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']); + + $Block['li']['text'] []= $text; + + unset($Block['interrupted']); + + return $Block; + } + } + + protected function blockListComplete(array $Block) + { + if (isset($Block['loose'])) + { + foreach ($Block['element']['text'] as &$li) + { + if (end($li['text']) !== '') + { + $li['text'] []= ''; + } + } + } + + return $Block; + } + + # + # Quote + + protected function blockQuote($Line) + { + if (preg_match('/^>[ ]?(.*)/', $Line['text'], $matches)) + { + $Block = array( + 'element' => array( + 'name' => 'blockquote', + 'handler' => 'lines', + 'text' => (array) $matches[1], + ), + ); + + return $Block; + } + } + + protected function blockQuoteContinue($Line, array $Block) + { + if ($Line['text'][0] === '>' and preg_match('/^>[ ]?(.*)/', $Line['text'], $matches)) + { + if (isset($Block['interrupted'])) + { + $Block['element']['text'] []= ''; + + unset($Block['interrupted']); + } + + $Block['element']['text'] []= $matches[1]; + + return $Block; + } + + if ( ! isset($Block['interrupted'])) + { + $Block['element']['text'] []= $Line['text']; + + return $Block; + } + } + + # + # Rule + + protected function blockRule($Line) + { + if (preg_match('/^(['.$Line['text'][0].'])([ ]*\1){2,}[ ]*$/', $Line['text'])) + { + $Block = array( + 'element' => array( + 'name' => 'hr' + ), + ); + + return $Block; + } + } + + # + # Setext + + protected function blockSetextHeader($Line, array $Block = null) + { + if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted'])) + { + return; + } + + if (chop($Line['text'], $Line['text'][0]) === '') + { + $Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2'; + + return $Block; + } + } + + # + # Markup + + protected function blockMarkup($Line) + { + if ($this->markupEscaped or $this->safeMode) + { + return; + } + + if (preg_match('/^<(\w[\w-]*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches)) + { + $element = strtolower($matches[1]); + + if (in_array($element, $this->textLevelElements)) + { + return; + } + + $Block = array( + 'name' => $matches[1], + 'depth' => 0, + 'markup' => $Line['text'], + ); + + $length = strlen($matches[0]); + + $remainder = substr($Line['text'], $length); + + if (trim($remainder) === '') + { + if (isset($matches[2]) or in_array($matches[1], $this->voidElements)) + { + $Block['closed'] = true; + + $Block['void'] = true; + } + } + else + { + if (isset($matches[2]) or in_array($matches[1], $this->voidElements)) + { + return; + } + + if (preg_match('/<\/'.$matches[1].'>[ ]*$/i', $remainder)) + { + $Block['closed'] = true; + } + } + + return $Block; + } + } + + protected function blockMarkupContinue($Line, array $Block) + { + if (isset($Block['closed'])) + { + return; + } + + if (preg_match('/^<'.$Block['name'].'(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*>/i', $Line['text'])) # open + { + $Block['depth'] ++; + } + + if (preg_match('/(.*?)<\/'.$Block['name'].'>[ ]*$/i', $Line['text'], $matches)) # close + { + if ($Block['depth'] > 0) + { + $Block['depth'] --; + } + else + { + $Block['closed'] = true; + } + } + + if (isset($Block['interrupted'])) + { + $Block['markup'] .= "\n"; + + unset($Block['interrupted']); + } + + $Block['markup'] .= "\n".$Line['body']; + + return $Block; + } + + # + # Reference + + protected function blockReference($Line) + { + if (preg_match('/^\[(.+?)\]:[ ]*?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches)) + { + $id = strtolower($matches[1]); + + $Data = array( + 'url' => $matches[2], + 'title' => null, + ); + + if (isset($matches[3])) + { + $Data['title'] = $matches[3]; + } + + $this->DefinitionData['Reference'][$id] = $Data; + + $Block = array( + 'hidden' => true, + ); + + return $Block; + } + } + + # + # Table + + protected function blockTable($Line, array $Block = null) + { + if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted'])) + { + return; + } + + if (strpos($Block['element']['text'], '|') !== false and chop($Line['text'], ' -:|') === '') + { + $alignments = array(); + + $divider = $Line['text']; + + $divider = trim($divider); + $divider = trim($divider, '|'); + + $dividerCells = explode('|', $divider); + + foreach ($dividerCells as $dividerCell) + { + $dividerCell = trim($dividerCell); + + if ($dividerCell === '') + { + continue; + } + + $alignment = null; + + if ($dividerCell[0] === ':') + { + $alignment = 'left'; + } + + if (substr($dividerCell, - 1) === ':') + { + $alignment = $alignment === 'left' ? 'center' : 'right'; + } + + $alignments []= $alignment; + } + + # ~ + + $HeaderElements = array(); + + $header = $Block['element']['text']; + + $header = trim($header); + $header = trim($header, '|'); + + $headerCells = explode('|', $header); + + foreach ($headerCells as $index => $headerCell) + { + $headerCell = trim($headerCell); + + $HeaderElement = array( + 'name' => 'th', + 'text' => $headerCell, + 'handler' => 'line', + ); + + if (isset($alignments[$index])) + { + $alignment = $alignments[$index]; + + $HeaderElement['attributes'] = array( + 'style' => 'text-align: '.$alignment.';', + ); + } + + $HeaderElements []= $HeaderElement; + } + + # ~ + + $Block = array( + 'alignments' => $alignments, + 'identified' => true, + 'element' => array( + 'name' => 'table', + 'handler' => 'elements', + ), + ); + + $Block['element']['text'] []= array( + 'name' => 'thead', + 'handler' => 'elements', + ); + + $Block['element']['text'] []= array( + 'name' => 'tbody', + 'handler' => 'elements', + 'text' => array(), + ); + + $Block['element']['text'][0]['text'] []= array( + 'name' => 'tr', + 'handler' => 'elements', + 'text' => $HeaderElements, + ); + + return $Block; + } + } + + protected function blockTableContinue($Line, array $Block) + { + if (isset($Block['interrupted'])) + { + return; + } + + if ($Line['text'][0] === '|' or strpos($Line['text'], '|')) + { + $Elements = array(); + + $row = $Line['text']; + + $row = trim($row); + $row = trim($row, '|'); + + preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]+`|`)+/', $row, $matches); + + foreach ($matches[0] as $index => $cell) + { + $cell = trim($cell); + + $Element = array( + 'name' => 'td', + 'handler' => 'line', + 'text' => $cell, + ); + + if (isset($Block['alignments'][$index])) + { + $Element['attributes'] = array( + 'style' => 'text-align: '.$Block['alignments'][$index].';', + ); + } + + $Elements []= $Element; + } + + $Element = array( + 'name' => 'tr', + 'handler' => 'elements', + 'text' => $Elements, + ); + + $Block['element']['text'][1]['text'] []= $Element; + + return $Block; + } + } + + # + # ~ + # + + protected function paragraph($Line) + { + $Block = array( + 'element' => array( + 'name' => 'p', + 'text' => $Line['text'], + 'handler' => 'line', + ), + ); + + return $Block; + } + + # + # Inline Elements + # + + protected $InlineTypes = array( + '"' => array('SpecialCharacter'), + '!' => array('Image'), + '&' => array('SpecialCharacter'), + '*' => array('Emphasis'), + ':' => array('Url'), + '<' => array('UrlTag', 'EmailTag', 'Markup', 'SpecialCharacter'), + '>' => array('SpecialCharacter'), + '[' => array('Link'), + '_' => array('Emphasis'), + '`' => array('Code'), + '~' => array('Strikethrough'), + '\\' => array('EscapeSequence'), + ); + + # ~ + + protected $inlineMarkerList = '!"*_&[:<>`~\\'; + + # + # ~ + # + + public function line($text, $nonNestables=array()) + { + $markup = ''; + + # $excerpt is based on the first occurrence of a marker + + while ($excerpt = strpbrk($text, $this->inlineMarkerList)) + { + $marker = $excerpt[0]; + + $markerPosition = strpos($text, $marker); + + $Excerpt = array('text' => $excerpt, 'context' => $text); + + foreach ($this->InlineTypes[$marker] as $inlineType) + { + # check to see if the current inline type is nestable in the current context + + if ( ! empty($nonNestables) and in_array($inlineType, $nonNestables)) + { + continue; + } + + $Inline = $this->{'inline'.$inlineType}($Excerpt); + + if ( ! isset($Inline)) + { + continue; + } + + # makes sure that the inline belongs to "our" marker + + if (isset($Inline['position']) and $Inline['position'] > $markerPosition) + { + continue; + } + + # sets a default inline position + + if ( ! isset($Inline['position'])) + { + $Inline['position'] = $markerPosition; + } + + # cause the new element to 'inherit' our non nestables + + foreach ($nonNestables as $non_nestable) + { + $Inline['element']['nonNestables'][] = $non_nestable; + } + + # the text that comes before the inline + $unmarkedText = substr($text, 0, $Inline['position']); + + # compile the unmarked text + $markup .= $this->unmarkedText($unmarkedText); + + # compile the inline + $markup .= isset($Inline['markup']) ? $Inline['markup'] : $this->element($Inline['element']); + + # remove the examined text + $text = substr($text, $Inline['position'] + $Inline['extent']); + + continue 2; + } + + # the marker does not belong to an inline + + $unmarkedText = substr($text, 0, $markerPosition + 1); + + $markup .= $this->unmarkedText($unmarkedText); + + $text = substr($text, $markerPosition + 1); + } + + $markup .= $this->unmarkedText($text); + + return $markup; + } + + # + # ~ + # + + protected function inlineCode($Excerpt) + { + $marker = $Excerpt['text'][0]; + + if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(? strlen($matches[0]), + 'element' => array( + 'name' => 'code', + 'text' => $text, + ), + ); + } + } + + protected function inlineEmailTag($Excerpt) + { + if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $Excerpt['text'], $matches)) + { + $url = $matches[1]; + + if ( ! isset($matches[2])) + { + $url = 'mailto:' . $url; + } + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'a', + 'text' => $matches[1], + 'attributes' => array( + 'href' => $url, + ), + ), + ); + } + } + + protected function inlineEmphasis($Excerpt) + { + if ( ! isset($Excerpt['text'][1])) + { + return; + } + + $marker = $Excerpt['text'][0]; + + if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches)) + { + $emphasis = 'strong'; + } + elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches)) + { + $emphasis = 'em'; + } + else + { + return; + } + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => $emphasis, + 'handler' => 'line', + 'text' => $matches[1], + ), + ); + } + + protected function inlineEscapeSequence($Excerpt) + { + if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters)) + { + return array( + 'markup' => $Excerpt['text'][1], + 'extent' => 2, + ); + } + } + + protected function inlineImage($Excerpt) + { + if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[') + { + return; + } + + $Excerpt['text']= substr($Excerpt['text'], 1); + + $Link = $this->inlineLink($Excerpt); + + if ($Link === null) + { + return; + } + + $Inline = array( + 'extent' => $Link['extent'] + 1, + 'element' => array( + 'name' => 'img', + 'attributes' => array( + 'src' => $Link['element']['attributes']['href'], + 'alt' => $Link['element']['text'], + ), + ), + ); + + $Inline['element']['attributes'] += $Link['element']['attributes']; + + unset($Inline['element']['attributes']['href']); + + return $Inline; + } + + protected function inlineLink($Excerpt) + { + $Element = array( + 'name' => 'a', + 'handler' => 'line', + 'nonNestables' => array('Url', 'Link'), + 'text' => null, + 'attributes' => array( + 'href' => null, + 'title' => null, + ), + ); + + $extent = 0; + + $remainder = $Excerpt['text']; + + if (preg_match('/\[((?:[^][]++|(?R))*+)\]/', $remainder, $matches)) + { + $Element['text'] = $matches[1]; + + $extent += strlen($matches[0]); + + $remainder = substr($remainder, $extent); + } + else + { + return; + } + + if (preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*"|\'[^\']*\'))?\s*[)]/', $remainder, $matches)) + { + $Element['attributes']['href'] = $matches[1]; + + if (isset($matches[2])) + { + $Element['attributes']['title'] = substr($matches[2], 1, - 1); + } + + $extent += strlen($matches[0]); + } + else + { + if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches)) + { + $definition = strlen($matches[1]) ? $matches[1] : $Element['text']; + $definition = strtolower($definition); + + $extent += strlen($matches[0]); + } + else + { + $definition = strtolower($Element['text']); + } + + if ( ! isset($this->DefinitionData['Reference'][$definition])) + { + return; + } + + $Definition = $this->DefinitionData['Reference'][$definition]; + + $Element['attributes']['href'] = $Definition['url']; + $Element['attributes']['title'] = $Definition['title']; + } + + return array( + 'extent' => $extent, + 'element' => $Element, + ); + } + + protected function inlineMarkup($Excerpt) + { + if ($this->markupEscaped or $this->safeMode or strpos($Excerpt['text'], '>') === false) + { + return; + } + + if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w[\w-]*[ ]*>/s', $Excerpt['text'], $matches)) + { + return array( + 'markup' => $matches[0], + 'extent' => strlen($matches[0]), + ); + } + + if ($Excerpt['text'][1] === '!' and preg_match('/^/s', $Excerpt['text'], $matches)) + { + return array( + 'markup' => $matches[0], + 'extent' => strlen($matches[0]), + ); + } + + if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w[\w-]*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches)) + { + return array( + 'markup' => $matches[0], + 'extent' => strlen($matches[0]), + ); + } + } + + protected function inlineSpecialCharacter($Excerpt) + { + if ($Excerpt['text'][0] === '&' and ! preg_match('/^&#?\w+;/', $Excerpt['text'])) + { + return array( + 'markup' => '&', + 'extent' => 1, + ); + } + + $SpecialCharacter = array('>' => 'gt', '<' => 'lt', '"' => 'quot'); + + if (isset($SpecialCharacter[$Excerpt['text'][0]])) + { + return array( + 'markup' => '&'.$SpecialCharacter[$Excerpt['text'][0]].';', + 'extent' => 1, + ); + } + } + + protected function inlineStrikethrough($Excerpt) + { + if ( ! isset($Excerpt['text'][1])) + { + return; + } + + if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches)) + { + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'del', + 'text' => $matches[1], + 'handler' => 'line', + ), + ); + } + } + + protected function inlineUrl($Excerpt) + { + if ($this->urlsLinked !== true or ! isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/') + { + return; + } + + if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE)) + { + $url = $matches[0][0]; + + $Inline = array( + 'extent' => strlen($matches[0][0]), + 'position' => $matches[0][1], + 'element' => array( + 'name' => 'a', + 'text' => $url, + 'attributes' => array( + 'href' => $url, + ), + ), + ); + + return $Inline; + } + } + + protected function inlineUrlTag($Excerpt) + { + if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches)) + { + $url = $matches[1]; + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'a', + 'text' => $url, + 'attributes' => array( + 'href' => $url, + ), + ), + ); + } + } + + # ~ + + protected function unmarkedText($text) + { + if ($this->breaksEnabled) + { + $text = preg_replace('/[ ]*\n/', "
\n", $text); + } + else + { + $text = preg_replace('/(?:[ ][ ]+|[ ]*\\\\)\n/', "
\n", $text); + $text = str_replace(" \n", "\n", $text); + } + + return $text; + } + + # + # Handlers + # + + protected function element(array $Element) + { + if ($this->safeMode) + { + $Element = $this->sanitiseElement($Element); + } + + $markup = '<'.$Element['name']; + + if (isset($Element['attributes'])) + { + foreach ($Element['attributes'] as $name => $value) + { + if ($value === null) + { + continue; + } + + $markup .= ' '.$name.'="'.self::escape($value).'"'; + } + } + + $permitRawHtml = false; + + if (isset($Element['text'])) + { + $text = $Element['text']; + } + // very strongly consider an alternative if you're writing an + // extension + elseif (isset($Element['rawHtml'])) + { + $text = $Element['rawHtml']; + $allowRawHtmlInSafeMode = isset($Element['allowRawHtmlInSafeMode']) && $Element['allowRawHtmlInSafeMode']; + $permitRawHtml = !$this->safeMode || $allowRawHtmlInSafeMode; + } + + if (isset($text)) + { + $markup .= '>'; + + if (!isset($Element['nonNestables'])) + { + $Element['nonNestables'] = array(); + } + + if (isset($Element['handler'])) + { + $markup .= $this->{$Element['handler']}($text, $Element['nonNestables']); + } + elseif (!$permitRawHtml) + { + $markup .= self::escape($text, true); + } + else + { + $markup .= $text; + } + + $markup .= ''; + } + else + { + $markup .= ' />'; + } + + return $markup; + } + + protected function elements(array $Elements) + { + $markup = ''; + + foreach ($Elements as $Element) + { + $markup .= "\n" . $this->element($Element); + } + + $markup .= "\n"; + + return $markup; + } + + # ~ + + protected function li($lines) + { + $markup = $this->lines($lines); + + $trimmedMarkup = trim($markup); + + if ( ! in_array('', $lines) and substr($trimmedMarkup, 0, 3) === '

') + { + $markup = $trimmedMarkup; + $markup = substr($markup, 3); + + $position = strpos($markup, "

"); + + $markup = substr_replace($markup, '', $position, 4); + } + + return $markup; + } + + # + # Deprecated Methods + # + + function parse($text) + { + $markup = $this->text($text); + + return $markup; + } + + protected function sanitiseElement(array $Element) + { + static $goodAttribute = '/^[a-zA-Z0-9][a-zA-Z0-9-_]*+$/'; + static $safeUrlNameToAtt = array( + 'a' => 'href', + 'img' => 'src', + ); + + if (isset($safeUrlNameToAtt[$Element['name']])) + { + $Element = $this->filterUnsafeUrlInAttribute($Element, $safeUrlNameToAtt[$Element['name']]); + } + + if ( ! empty($Element['attributes'])) + { + foreach ($Element['attributes'] as $att => $val) + { + # filter out badly parsed attribute + if ( ! preg_match($goodAttribute, $att)) + { + unset($Element['attributes'][$att]); + } + # dump onevent attribute + elseif (self::striAtStart($att, 'on')) + { + unset($Element['attributes'][$att]); + } + } + } + + return $Element; + } + + protected function filterUnsafeUrlInAttribute(array $Element, $attribute) + { + foreach ($this->safeLinksWhitelist as $scheme) + { + if (self::striAtStart($Element['attributes'][$attribute], $scheme)) + { + return $Element; + } + } + + $Element['attributes'][$attribute] = str_replace(':', '%3A', $Element['attributes'][$attribute]); + + return $Element; + } + + # + # Static Methods + # + + protected static function escape($text, $allowQuotes = false) + { + return htmlspecialchars($text, $allowQuotes ? ENT_NOQUOTES : ENT_QUOTES, 'UTF-8'); + } + + protected static function striAtStart($string, $needle) + { + $len = strlen($needle); + + if ($len > strlen($string)) + { + return false; + } + else + { + return strtolower(substr($string, 0, $len)) === strtolower($needle); + } + } + + static function instance($name = 'default') + { + if (isset(self::$instances[$name])) + { + return self::$instances[$name]; + } + + $instance = new static(); + + self::$instances[$name] = $instance; + + return $instance; + } + + private static $instances = array(); + + # + # Fields + # + + protected $DefinitionData; + + # + # Read-Only + + protected $specialCharacters = array( + '\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|', + ); + + protected $StrongRegex = array( + '*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s', + '_' => '/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us', + ); + + protected $EmRegex = array( + '*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s', + '_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us', + ); + + protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"\'=<>`\s]+|"[^"]*"|\'[^\']*\'))?'; + + protected $voidElements = array( + 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', + ); + + protected $textLevelElements = array( + 'a', 'br', 'bdo', 'abbr', 'blink', 'nextid', 'acronym', 'basefont', + 'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing', + 'i', 'rp', 'del', 'code', 'strike', 'marquee', + 'q', 'rt', 'ins', 'font', 'strong', + 's', 'tt', 'kbd', 'mark', + 'u', 'xm', 'sub', 'nobr', + 'sup', 'ruby', + 'var', 'span', + 'wbr', 'time', + ); +} diff --git a/web/app/vendor/erusev/parsedown/README.md b/web/app/vendor/erusev/parsedown/README.md new file mode 100644 index 0000000..b5d9ed2 --- /dev/null +++ b/web/app/vendor/erusev/parsedown/README.md @@ -0,0 +1,86 @@ +> I also make [Caret](https://caret.io?ref=parsedown) - a Markdown editor for Mac and PC. + +## Parsedown + +[![Build Status](https://img.shields.io/travis/erusev/parsedown/master.svg?style=flat-square)](https://travis-ci.org/erusev/parsedown) + + +Better Markdown Parser in PHP + +[Demo](http://parsedown.org/demo) | +[Benchmarks](http://parsedown.org/speed) | +[Tests](http://parsedown.org/tests/) | +[Documentation](https://github.com/erusev/parsedown/wiki/) + +### Features + +* One File +* No Dependencies +* Super Fast +* Extensible +* [GitHub flavored](https://help.github.com/articles/github-flavored-markdown) +* Tested in 5.3 to 7.1 and in HHVM +* [Markdown Extra extension](https://github.com/erusev/parsedown-extra) + +### Installation + +Include `Parsedown.php` or install [the composer package](https://packagist.org/packages/erusev/parsedown). + +### Example + +``` php +$Parsedown = new Parsedown(); + +echo $Parsedown->text('Hello _Parsedown_!'); # prints:

Hello Parsedown!

+``` + +More examples in [the wiki](https://github.com/erusev/parsedown/wiki/) and in [this video tutorial](http://youtu.be/wYZBY8DEikI). + +### Security + +Parsedown is capable of escaping user-input within the HTML that it generates. Additionally Parsedown will apply sanitisation to additional scripting vectors (such as scripting link destinations) that are introduced by the markdown syntax itself. + +To tell Parsedown that it is processing untrusted user-input, use the following: +```php +$parsedown = new Parsedown; +$parsedown->setSafeMode(true); +``` + +If instead, you wish to allow HTML within untrusted user-input, but still want output to be free from XSS it is recommended that you make use of a HTML sanitiser that allows HTML tags to be whitelisted, like [HTML Purifier](http://htmlpurifier.org/). + +In both cases you should strongly consider employing defence-in-depth measures, like [deploying a Content-Security-Policy](https://scotthelme.co.uk/content-security-policy-an-introduction/) (a browser security feature) so that your page is likely to be safe even if an attacker finds a vulnerability in one of the first lines of defence above. + +#### Security of Parsedown Extensions + +Safe mode does not necessarily yield safe results when using extensions to Parsedown. Extensions should be evaluated on their own to determine their specific safety against XSS. + +### Escaping HTML +> ⚠️  **WARNING:** This method isn't safe from XSS! + +If you wish to escape HTML **in trusted input**, you can use the following: +```php +$parsedown = new Parsedown; +$parsedown->setMarkupEscaped(true); +``` + +Beware that this still allows users to insert unsafe scripting vectors, such as links like `[xss](javascript:alert%281%29)`. + +### Questions + +**How does Parsedown work?** + +It tries to read Markdown like a human. First, it looks at the lines. It’s interested in how the lines start. This helps it recognise blocks. It knows, for example, that if a line starts with a `-` then perhaps it belongs to a list. Once it recognises the blocks, it continues to the content. As it reads, it watches out for special characters. This helps it recognise inline elements (or inlines). + +We call this approach "line based". We believe that Parsedown is the first Markdown parser to use it. Since the release of Parsedown, other developers have used the same approach to develop other Markdown parsers in PHP and in other languages. + +**Is it compliant with CommonMark?** + +It passes most of the CommonMark tests. Most of the tests that don't pass deal with cases that are quite uncommon. Still, as CommonMark matures, compliance should improve. + +**Who uses it?** + +[Laravel Framework](https://laravel.com/), [Bolt CMS](http://bolt.cm/), [Grav CMS](http://getgrav.org/), [Herbie CMS](http://www.getherbie.org/), [Kirby CMS](http://getkirby.com/), [October CMS](http://octobercms.com/), [Pico CMS](http://picocms.org), [Statamic CMS](http://www.statamic.com/), [phpDocumentor](http://www.phpdoc.org/), [RaspberryPi.org](http://www.raspberrypi.org/), [Symfony demo](https://github.com/symfony/symfony-demo) and [more](https://packagist.org/packages/erusev/parsedown/dependents). + +**How can I help?** + +Use it, star it, share it and if you feel generous, [donate](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=528P3NZQMP8N2). diff --git a/web/app/vendor/erusev/parsedown/composer.json b/web/app/vendor/erusev/parsedown/composer.json new file mode 100644 index 0000000..f8b40f8 --- /dev/null +++ b/web/app/vendor/erusev/parsedown/composer.json @@ -0,0 +1,33 @@ +{ + "name": "erusev/parsedown", + "description": "Parser for Markdown.", + "keywords": ["markdown", "parser"], + "homepage": "http://parsedown.org", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Emanuil Rusev", + "email": "hello@erusev.com", + "homepage": "http://erusev.com" + } + ], + "require": { + "php": ">=5.3.0", + "ext-mbstring": "*" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35" + }, + "autoload": { + "psr-0": {"Parsedown": ""} + }, + "autoload-dev": { + "psr-0": { + "TestParsedown": "test/", + "ParsedownTest": "test/", + "CommonMarkTest": "test/", + "CommonMarkTestWeak": "test/" + } + } +} diff --git a/web/app/vendor/symfony/deprecation-contracts/.gitignore b/web/app/vendor/symfony/deprecation-contracts/.gitignore deleted file mode 100644 index c49a5d8..0000000 --- a/web/app/vendor/symfony/deprecation-contracts/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -vendor/ -composer.lock -phpunit.xml diff --git a/web/app/vendor/symfony/deprecation-contracts/CHANGELOG.md b/web/app/vendor/symfony/deprecation-contracts/CHANGELOG.md deleted file mode 100644 index 7932e26..0000000 --- a/web/app/vendor/symfony/deprecation-contracts/CHANGELOG.md +++ /dev/null @@ -1,5 +0,0 @@ -CHANGELOG -========= - -The changelog is maintained for all Symfony contracts at the following URL: -https://github.com/symfony/contracts/blob/main/CHANGELOG.md diff --git a/web/app/vendor/symfony/deprecation-contracts/LICENSE b/web/app/vendor/symfony/deprecation-contracts/LICENSE deleted file mode 100644 index 406242f..0000000 --- a/web/app/vendor/symfony/deprecation-contracts/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2020-2022 Fabien Potencier - -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. diff --git a/web/app/vendor/symfony/deprecation-contracts/README.md b/web/app/vendor/symfony/deprecation-contracts/README.md deleted file mode 100644 index 4957933..0000000 --- a/web/app/vendor/symfony/deprecation-contracts/README.md +++ /dev/null @@ -1,26 +0,0 @@ -Symfony Deprecation Contracts -============================= - -A generic function and convention to trigger deprecation notices. - -This package provides a single global function named `trigger_deprecation()` that triggers silenced deprecation notices. - -By using a custom PHP error handler such as the one provided by the Symfony ErrorHandler component, -the triggered deprecations can be caught and logged for later discovery, both on dev and prod environments. - -The function requires at least 3 arguments: - - the name of the Composer package that is triggering the deprecation - - the version of the package that introduced the deprecation - - the message of the deprecation - - more arguments can be provided: they will be inserted in the message using `printf()` formatting - -Example: -```php -trigger_deprecation('symfony/blockchain', '8.9', 'Using "%s" is deprecated, use "%s" instead.', 'bitcoin', 'fabcoin'); -``` - -This will generate the following message: -`Since symfony/blockchain 8.9: Using "bitcoin" is deprecated, use "fabcoin" instead.` - -While not necessarily recommended, the deprecation notices can be completely ignored by declaring an empty -`function trigger_deprecation() {}` in your application. diff --git a/web/app/vendor/symfony/deprecation-contracts/composer.json b/web/app/vendor/symfony/deprecation-contracts/composer.json deleted file mode 100644 index cc7cc12..0000000 --- a/web/app/vendor/symfony/deprecation-contracts/composer.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "symfony/deprecation-contracts", - "type": "library", - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", - "license": "MIT", - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "require": { - "php": ">=7.1" - }, - "autoload": { - "files": [ - "function.php" - ] - }, - "minimum-stability": "dev", - "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - } -} diff --git a/web/app/vendor/symfony/deprecation-contracts/function.php b/web/app/vendor/symfony/deprecation-contracts/function.php deleted file mode 100644 index d437150..0000000 --- a/web/app/vendor/symfony/deprecation-contracts/function.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -if (!function_exists('trigger_deprecation')) { - /** - * Triggers a silenced deprecation notice. - * - * @param string $package The name of the Composer package that is triggering the deprecation - * @param string $version The version of the package that introduced the deprecation - * @param string $message The message of the deprecation - * @param mixed ...$args Values to insert in the message using printf() formatting - * - * @author Nicolas Grekas - */ - function trigger_deprecation(string $package, string $version, string $message, ...$args): void - { - @trigger_error(($package || $version ? "Since $package $version: " : '').($args ? vsprintf($message, $args) : $message), \E_USER_DEPRECATED); - } -} diff --git a/web/app/vendor/symfony/finder/CHANGELOG.md b/web/app/vendor/symfony/finder/CHANGELOG.md index 6a44e87..9e2fc5a 100644 --- a/web/app/vendor/symfony/finder/CHANGELOG.md +++ b/web/app/vendor/symfony/finder/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +6.0 +--- + + * Remove `Comparator::setTarget()` and `Comparator::setOperator()` + 5.4.0 ----- diff --git a/web/app/vendor/symfony/finder/Comparator/Comparator.php b/web/app/vendor/symfony/finder/Comparator/Comparator.php index 3af551f..bd68583 100644 --- a/web/app/vendor/symfony/finder/Comparator/Comparator.php +++ b/web/app/vendor/symfony/finder/Comparator/Comparator.php @@ -16,102 +16,47 @@ namespace Symfony\Component\Finder\Comparator; */ class Comparator { - private $target; - private $operator = '=='; + private string $target; + private string $operator; - public function __construct(string $target = null, string $operator = '==') - { - if (null === $target) { - trigger_deprecation('symfony/finder', '5.4', 'Constructing a "%s" without setting "$target" is deprecated.', __CLASS__); - } - - $this->target = $target; - $this->doSetOperator($operator); - } - - /** - * Gets the target value. - * - * @return string - */ - public function getTarget() - { - if (null === $this->target) { - trigger_deprecation('symfony/finder', '5.4', 'Calling "%s" without initializing the target is deprecated.', __METHOD__); - } - - return $this->target; - } - - /** - * @deprecated set the target via the constructor instead - */ - public function setTarget(string $target) - { - trigger_deprecation('symfony/finder', '5.4', '"%s" is deprecated. Set the target via the constructor instead.', __METHOD__); - - $this->target = $target; - } - - /** - * Gets the comparison operator. - * - * @return string - */ - public function getOperator() - { - return $this->operator; - } - - /** - * Sets the comparison operator. - * - * @throws \InvalidArgumentException - * - * @deprecated set the operator via the constructor instead - */ - public function setOperator(string $operator) - { - trigger_deprecation('symfony/finder', '5.4', '"%s" is deprecated. Set the operator via the constructor instead.', __METHOD__); - - $this->doSetOperator('' === $operator ? '==' : $operator); - } - - /** - * Tests against the target. - * - * @param mixed $test A test value - * - * @return bool - */ - public function test($test) - { - if (null === $this->target) { - trigger_deprecation('symfony/finder', '5.4', 'Calling "%s" without initializing the target is deprecated.', __METHOD__); - } - - switch ($this->operator) { - case '>': - return $test > $this->target; - case '>=': - return $test >= $this->target; - case '<': - return $test < $this->target; - case '<=': - return $test <= $this->target; - case '!=': - return $test != $this->target; - } - - return $test == $this->target; - } - - private function doSetOperator(string $operator): void + public function __construct(string $target, string $operator = '==') { if (!\in_array($operator, ['>', '<', '>=', '<=', '==', '!='])) { throw new \InvalidArgumentException(sprintf('Invalid operator "%s".', $operator)); } + $this->target = $target; $this->operator = $operator; } + + /** + * Gets the target value. + */ + public function getTarget(): string + { + return $this->target; + } + + /** + * Gets the comparison operator. + */ + public function getOperator(): string + { + return $this->operator; + } + + /** + * Tests against the target. + */ + public function test(mixed $test): bool + { + return match ($this->operator) { + '>' => $test > $this->target, + '>=' => $test >= $this->target, + '<' => $test < $this->target, + '<=' => $test <= $this->target, + '!=' => $test != $this->target, + default => $test == $this->target, + }; + } } diff --git a/web/app/vendor/symfony/finder/Comparator/DateComparator.php b/web/app/vendor/symfony/finder/Comparator/DateComparator.php index 8f651e1..159964d 100644 --- a/web/app/vendor/symfony/finder/Comparator/DateComparator.php +++ b/web/app/vendor/symfony/finder/Comparator/DateComparator.php @@ -32,7 +32,7 @@ class DateComparator extends Comparator try { $date = new \DateTime($matches[2]); $target = $date->format('U'); - } catch (\Exception $e) { + } catch (\Exception) { throw new \InvalidArgumentException(sprintf('"%s" is not a valid date.', $matches[2])); } diff --git a/web/app/vendor/symfony/finder/Finder.php b/web/app/vendor/symfony/finder/Finder.php index 8cc564c..4636f05 100644 --- a/web/app/vendor/symfony/finder/Finder.php +++ b/web/app/vendor/symfony/finder/Finder.php @@ -45,27 +45,27 @@ class Finder implements \IteratorAggregate, \Countable public const IGNORE_DOT_FILES = 2; public const IGNORE_VCS_IGNORED_FILES = 4; - private $mode = 0; - private $names = []; - private $notNames = []; - private $exclude = []; - private $filters = []; - private $depths = []; - private $sizes = []; - private $followLinks = false; - private $reverseSorting = false; - private $sort = false; - private $ignore = 0; - private $dirs = []; - private $dates = []; - private $iterators = []; - private $contains = []; - private $notContains = []; - private $paths = []; - private $notPaths = []; - private $ignoreUnreadableDirs = false; + private int $mode = 0; + private array $names = []; + private array $notNames = []; + private array $exclude = []; + private array $filters = []; + private array $depths = []; + private array $sizes = []; + private bool $followLinks = false; + private bool $reverseSorting = false; + private \Closure|int|false $sort = false; + private int $ignore = 0; + private array $dirs = []; + private array $dates = []; + private array $iterators = []; + private array $contains = []; + private array $notContains = []; + private array $paths = []; + private array $notPaths = []; + private bool $ignoreUnreadableDirs = false; - private static $vcsPatterns = ['.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg']; + private static array $vcsPatterns = ['.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg']; public function __construct() { @@ -74,10 +74,8 @@ class Finder implements \IteratorAggregate, \Countable /** * Creates a new Finder. - * - * @return static */ - public static function create() + public static function create(): static { return new static(); } @@ -87,7 +85,7 @@ class Finder implements \IteratorAggregate, \Countable * * @return $this */ - public function directories() + public function directories(): static { $this->mode = Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES; @@ -99,7 +97,7 @@ class Finder implements \IteratorAggregate, \Countable * * @return $this */ - public function files() + public function files(): static { $this->mode = Iterator\FileTypeFilterIterator::ONLY_FILES; @@ -122,7 +120,7 @@ class Finder implements \IteratorAggregate, \Countable * @see DepthRangeFilterIterator * @see NumberComparator */ - public function depth($levels) + public function depth(string|int|array $levels): static { foreach ((array) $levels as $level) { $this->depths[] = new Comparator\NumberComparator($level); @@ -150,7 +148,7 @@ class Finder implements \IteratorAggregate, \Countable * @see DateRangeFilterIterator * @see DateComparator */ - public function date($dates) + public function date(string|array $dates): static { foreach ((array) $dates as $date) { $this->dates[] = new Comparator\DateComparator($date); @@ -175,7 +173,7 @@ class Finder implements \IteratorAggregate, \Countable * * @see FilenameFilterIterator */ - public function name($patterns) + public function name(string|array $patterns): static { $this->names = array_merge($this->names, (array) $patterns); @@ -191,7 +189,7 @@ class Finder implements \IteratorAggregate, \Countable * * @see FilenameFilterIterator */ - public function notName($patterns) + public function notName(string|array $patterns): static { $this->notNames = array_merge($this->notNames, (array) $patterns); @@ -213,7 +211,7 @@ class Finder implements \IteratorAggregate, \Countable * * @see FilecontentFilterIterator */ - public function contains($patterns) + public function contains(string|array $patterns): static { $this->contains = array_merge($this->contains, (array) $patterns); @@ -235,7 +233,7 @@ class Finder implements \IteratorAggregate, \Countable * * @see FilecontentFilterIterator */ - public function notContains($patterns) + public function notContains(string|array $patterns): static { $this->notContains = array_merge($this->notContains, (array) $patterns); @@ -259,7 +257,7 @@ class Finder implements \IteratorAggregate, \Countable * * @see FilenameFilterIterator */ - public function path($patterns) + public function path(string|array $patterns): static { $this->paths = array_merge($this->paths, (array) $patterns); @@ -283,7 +281,7 @@ class Finder implements \IteratorAggregate, \Countable * * @see FilenameFilterIterator */ - public function notPath($patterns) + public function notPath(string|array $patterns): static { $this->notPaths = array_merge($this->notPaths, (array) $patterns); @@ -305,7 +303,7 @@ class Finder implements \IteratorAggregate, \Countable * @see SizeRangeFilterIterator * @see NumberComparator */ - public function size($sizes) + public function size(string|int|array $sizes): static { foreach ((array) $sizes as $size) { $this->sizes[] = new Comparator\NumberComparator($size); @@ -327,7 +325,7 @@ class Finder implements \IteratorAggregate, \Countable * * @see ExcludeDirectoryFilterIterator */ - public function exclude($dirs) + public function exclude(string|array $dirs): static { $this->exclude = array_merge($this->exclude, (array) $dirs); @@ -343,7 +341,7 @@ class Finder implements \IteratorAggregate, \Countable * * @see ExcludeDirectoryFilterIterator */ - public function ignoreDotFiles(bool $ignoreDotFiles) + public function ignoreDotFiles(bool $ignoreDotFiles): static { if ($ignoreDotFiles) { $this->ignore |= static::IGNORE_DOT_FILES; @@ -363,7 +361,7 @@ class Finder implements \IteratorAggregate, \Countable * * @see ExcludeDirectoryFilterIterator */ - public function ignoreVCS(bool $ignoreVCS) + public function ignoreVCS(bool $ignoreVCS): static { if ($ignoreVCS) { $this->ignore |= static::IGNORE_VCS_FILES; @@ -381,7 +379,7 @@ class Finder implements \IteratorAggregate, \Countable * * @return $this */ - public function ignoreVCSIgnored(bool $ignoreVCSIgnored) + public function ignoreVCSIgnored(bool $ignoreVCSIgnored): static { if ($ignoreVCSIgnored) { $this->ignore |= static::IGNORE_VCS_IGNORED_FILES; @@ -399,7 +397,7 @@ class Finder implements \IteratorAggregate, \Countable * * @param string|string[] $pattern VCS patterns to ignore */ - public static function addVCSPattern($pattern) + public static function addVCSPattern(string|array $pattern) { foreach ((array) $pattern as $p) { self::$vcsPatterns[] = $p; @@ -419,7 +417,7 @@ class Finder implements \IteratorAggregate, \Countable * * @see SortableIterator */ - public function sort(\Closure $closure) + public function sort(\Closure $closure): static { $this->sort = $closure; @@ -435,7 +433,7 @@ class Finder implements \IteratorAggregate, \Countable * * @see SortableIterator */ - public function sortByName(bool $useNaturalSort = false) + public function sortByName(bool $useNaturalSort = false): static { $this->sort = $useNaturalSort ? Iterator\SortableIterator::SORT_BY_NAME_NATURAL : Iterator\SortableIterator::SORT_BY_NAME; @@ -451,7 +449,7 @@ class Finder implements \IteratorAggregate, \Countable * * @see SortableIterator */ - public function sortByType() + public function sortByType(): static { $this->sort = Iterator\SortableIterator::SORT_BY_TYPE; @@ -469,7 +467,7 @@ class Finder implements \IteratorAggregate, \Countable * * @see SortableIterator */ - public function sortByAccessedTime() + public function sortByAccessedTime(): static { $this->sort = Iterator\SortableIterator::SORT_BY_ACCESSED_TIME; @@ -481,7 +479,7 @@ class Finder implements \IteratorAggregate, \Countable * * @return $this */ - public function reverseSorting() + public function reverseSorting(): static { $this->reverseSorting = true; @@ -501,7 +499,7 @@ class Finder implements \IteratorAggregate, \Countable * * @see SortableIterator */ - public function sortByChangedTime() + public function sortByChangedTime(): static { $this->sort = Iterator\SortableIterator::SORT_BY_CHANGED_TIME; @@ -519,7 +517,7 @@ class Finder implements \IteratorAggregate, \Countable * * @see SortableIterator */ - public function sortByModifiedTime() + public function sortByModifiedTime(): static { $this->sort = Iterator\SortableIterator::SORT_BY_MODIFIED_TIME; @@ -536,7 +534,7 @@ class Finder implements \IteratorAggregate, \Countable * * @see CustomFilterIterator */ - public function filter(\Closure $closure) + public function filter(\Closure $closure): static { $this->filters[] = $closure; @@ -548,7 +546,7 @@ class Finder implements \IteratorAggregate, \Countable * * @return $this */ - public function followLinks() + public function followLinks(): static { $this->followLinks = true; @@ -562,7 +560,7 @@ class Finder implements \IteratorAggregate, \Countable * * @return $this */ - public function ignoreUnreadableDirs(bool $ignore = true) + public function ignoreUnreadableDirs(bool $ignore = true): static { $this->ignoreUnreadableDirs = $ignore; @@ -578,7 +576,7 @@ class Finder implements \IteratorAggregate, \Countable * * @throws DirectoryNotFoundException if one of the directories does not exist */ - public function in($dirs) + public function in(string|array $dirs): static { $resolvedDirs = []; @@ -587,7 +585,7 @@ class Finder implements \IteratorAggregate, \Countable $resolvedDirs[] = [$this->normalizeDir($dir)]; } elseif ($glob = glob($dir, (\defined('GLOB_BRACE') ? \GLOB_BRACE : 0) | \GLOB_ONLYDIR | \GLOB_NOSORT)) { sort($glob); - $resolvedDirs[] = array_map([$this, 'normalizeDir'], $glob); + $resolvedDirs[] = array_map($this->normalizeDir(...), $glob); } else { throw new DirectoryNotFoundException(sprintf('The "%s" directory does not exist.', $dir)); } @@ -607,8 +605,7 @@ class Finder implements \IteratorAggregate, \Countable * * @throws \LogicException if the in() method has not been called */ - #[\ReturnTypeWillChange] - public function getIterator() + public function getIterator(): \Iterator { if (0 === \count($this->dirs) && 0 === \count($this->iterators)) { throw new \LogicException('You must call one of in() or append() methods before iterating over a Finder.'); @@ -651,7 +648,7 @@ class Finder implements \IteratorAggregate, \Countable * * @throws \InvalidArgumentException when the given argument is not iterable */ - public function append(iterable $iterator) + public function append(iterable $iterator): static { if ($iterator instanceof \IteratorAggregate) { $this->iterators[] = $iterator->getIterator(); @@ -673,10 +670,8 @@ class Finder implements \IteratorAggregate, \Countable /** * Check if any results were found. - * - * @return bool */ - public function hasResults() + public function hasResults(): bool { foreach ($this->getIterator() as $_) { return true; @@ -687,11 +682,8 @@ class Finder implements \IteratorAggregate, \Countable /** * Counts all the results collected by the iterators. - * - * @return int */ - #[\ReturnTypeWillChange] - public function count() + public function count(): int { return iterator_count($this->getIterator()); } diff --git a/web/app/vendor/symfony/finder/Gitignore.php b/web/app/vendor/symfony/finder/Gitignore.php index d42cca1..070074b 100644 --- a/web/app/vendor/symfony/finder/Gitignore.php +++ b/web/app/vendor/symfony/finder/Gitignore.php @@ -43,7 +43,7 @@ class Gitignore foreach ($gitignoreLines as $line) { $line = preg_replace('~(? $iterator The Iterator to filter @@ -45,11 +45,8 @@ class CustomFilterIterator extends \FilterIterator /** * Filters the iterator values. - * - * @return bool */ - #[\ReturnTypeWillChange] - public function accept() + public function accept(): bool { $fileinfo = $this->current(); diff --git a/web/app/vendor/symfony/finder/Iterator/DateRangeFilterIterator.php b/web/app/vendor/symfony/finder/Iterator/DateRangeFilterIterator.php index f592e19..718d42b 100644 --- a/web/app/vendor/symfony/finder/Iterator/DateRangeFilterIterator.php +++ b/web/app/vendor/symfony/finder/Iterator/DateRangeFilterIterator.php @@ -22,7 +22,7 @@ use Symfony\Component\Finder\Comparator\DateComparator; */ class DateRangeFilterIterator extends \FilterIterator { - private $comparators = []; + private array $comparators = []; /** * @param \Iterator $iterator @@ -37,11 +37,8 @@ class DateRangeFilterIterator extends \FilterIterator /** * Filters the iterator values. - * - * @return bool */ - #[\ReturnTypeWillChange] - public function accept() + public function accept(): bool { $fileinfo = $this->current(); diff --git a/web/app/vendor/symfony/finder/Iterator/DepthRangeFilterIterator.php b/web/app/vendor/symfony/finder/Iterator/DepthRangeFilterIterator.php index f593a3f..1cddb5f 100644 --- a/web/app/vendor/symfony/finder/Iterator/DepthRangeFilterIterator.php +++ b/web/app/vendor/symfony/finder/Iterator/DepthRangeFilterIterator.php @@ -23,7 +23,7 @@ namespace Symfony\Component\Finder\Iterator; */ class DepthRangeFilterIterator extends \FilterIterator { - private $minDepth = 0; + private int $minDepth = 0; /** * @param \RecursiveIteratorIterator<\RecursiveIterator> $iterator The Iterator to filter @@ -40,11 +40,8 @@ class DepthRangeFilterIterator extends \FilterIterator /** * Filters the iterator values. - * - * @return bool */ - #[\ReturnTypeWillChange] - public function accept() + public function accept(): bool { return $this->getInnerIterator()->getDepth() >= $this->minDepth; } diff --git a/web/app/vendor/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php b/web/app/vendor/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php index d9e182c..efe9364 100644 --- a/web/app/vendor/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php +++ b/web/app/vendor/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php @@ -11,24 +11,27 @@ namespace Symfony\Component\Finder\Iterator; +use Symfony\Component\Finder\SplFileInfo; + /** * ExcludeDirectoryFilterIterator filters out directories. * * @author Fabien Potencier * - * @extends \FilterIterator - * @implements \RecursiveIterator + * @extends \FilterIterator + * @implements \RecursiveIterator */ class ExcludeDirectoryFilterIterator extends \FilterIterator implements \RecursiveIterator { - private $iterator; - private $isRecursive; - private $excludedDirs = []; - private $excludedPattern; + /** @var \Iterator */ + private \Iterator $iterator; + private bool $isRecursive; + private array $excludedDirs = []; + private ?string $excludedPattern = null; /** - * @param \Iterator $iterator The Iterator to filter - * @param string[] $directories An array of directories to exclude + * @param \Iterator $iterator The Iterator to filter + * @param string[] $directories An array of directories to exclude */ public function __construct(\Iterator $iterator, array $directories) { @@ -52,11 +55,8 @@ class ExcludeDirectoryFilterIterator extends \FilterIterator implements \Recursi /** * Filters the iterator values. - * - * @return bool */ - #[\ReturnTypeWillChange] - public function accept() + public function accept(): bool { if ($this->isRecursive && isset($this->excludedDirs[$this->getFilename()]) && $this->isDir()) { return false; @@ -72,20 +72,12 @@ class ExcludeDirectoryFilterIterator extends \FilterIterator implements \Recursi return true; } - /** - * @return bool - */ - #[\ReturnTypeWillChange] - public function hasChildren() + public function hasChildren(): bool { return $this->isRecursive && $this->iterator->hasChildren(); } - /** - * @return self - */ - #[\ReturnTypeWillChange] - public function getChildren() + public function getChildren(): self { $children = new self($this->iterator->getChildren(), []); $children->excludedDirs = $this->excludedDirs; diff --git a/web/app/vendor/symfony/finder/Iterator/FileTypeFilterIterator.php b/web/app/vendor/symfony/finder/Iterator/FileTypeFilterIterator.php index 793ae35..2130378 100644 --- a/web/app/vendor/symfony/finder/Iterator/FileTypeFilterIterator.php +++ b/web/app/vendor/symfony/finder/Iterator/FileTypeFilterIterator.php @@ -23,11 +23,11 @@ class FileTypeFilterIterator extends \FilterIterator public const ONLY_FILES = 1; public const ONLY_DIRECTORIES = 2; - private $mode; + private int $mode; /** - * @param \Iterator $iterator The Iterator to filter - * @param int $mode The mode (self::ONLY_FILES or self::ONLY_DIRECTORIES) + * @param \Iterator $iterator The Iterator to filter + * @param int $mode The mode (self::ONLY_FILES or self::ONLY_DIRECTORIES) */ public function __construct(\Iterator $iterator, int $mode) { @@ -38,11 +38,8 @@ class FileTypeFilterIterator extends \FilterIterator /** * Filters the iterator values. - * - * @return bool */ - #[\ReturnTypeWillChange] - public function accept() + public function accept(): bool { $fileinfo = $this->current(); if (self::ONLY_DIRECTORIES === (self::ONLY_DIRECTORIES & $this->mode) && $fileinfo->isFile()) { diff --git a/web/app/vendor/symfony/finder/Iterator/FilecontentFilterIterator.php b/web/app/vendor/symfony/finder/Iterator/FilecontentFilterIterator.php index 79f8c29..bdc71ff 100644 --- a/web/app/vendor/symfony/finder/Iterator/FilecontentFilterIterator.php +++ b/web/app/vendor/symfony/finder/Iterator/FilecontentFilterIterator.php @@ -11,23 +11,22 @@ namespace Symfony\Component\Finder\Iterator; +use Symfony\Component\Finder\SplFileInfo; + /** * FilecontentFilterIterator filters files by their contents using patterns (regexps or strings). * * @author Fabien Potencier * @author Włodzimierz Gajda * - * @extends MultiplePcreFilterIterator + * @extends MultiplePcreFilterIterator */ class FilecontentFilterIterator extends MultiplePcreFilterIterator { /** * Filters the iterator values. - * - * @return bool */ - #[\ReturnTypeWillChange] - public function accept() + public function accept(): bool { if (!$this->matchRegexps && !$this->noMatchRegexps) { return true; @@ -51,10 +50,8 @@ class FilecontentFilterIterator extends MultiplePcreFilterIterator * Converts string to regexp if necessary. * * @param string $str Pattern: string or regexp - * - * @return string */ - protected function toRegex(string $str) + protected function toRegex(string $str): string { return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/'; } diff --git a/web/app/vendor/symfony/finder/Iterator/FilenameFilterIterator.php b/web/app/vendor/symfony/finder/Iterator/FilenameFilterIterator.php index 77b3b24..05d9535 100644 --- a/web/app/vendor/symfony/finder/Iterator/FilenameFilterIterator.php +++ b/web/app/vendor/symfony/finder/Iterator/FilenameFilterIterator.php @@ -24,11 +24,8 @@ class FilenameFilterIterator extends MultiplePcreFilterIterator { /** * Filters the iterator values. - * - * @return bool */ - #[\ReturnTypeWillChange] - public function accept() + public function accept(): bool { return $this->isAccepted($this->current()->getFilename()); } @@ -40,10 +37,8 @@ class FilenameFilterIterator extends MultiplePcreFilterIterator * Glob strings are transformed with Glob::toRegex(). * * @param string $str Pattern: glob or regexp - * - * @return string */ - protected function toRegex(string $str) + protected function toRegex(string $str): string { return $this->isRegex($str) ? $str : Glob::toRegex($str); } diff --git a/web/app/vendor/symfony/finder/Iterator/LazyIterator.php b/web/app/vendor/symfony/finder/Iterator/LazyIterator.php index 32cc37f..5b5806b 100644 --- a/web/app/vendor/symfony/finder/Iterator/LazyIterator.php +++ b/web/app/vendor/symfony/finder/Iterator/LazyIterator.php @@ -18,11 +18,11 @@ namespace Symfony\Component\Finder\Iterator; */ class LazyIterator implements \IteratorAggregate { - private $iteratorFactory; + private \Closure $iteratorFactory; public function __construct(callable $iteratorFactory) { - $this->iteratorFactory = $iteratorFactory; + $this->iteratorFactory = $iteratorFactory(...); } public function getIterator(): \Traversable diff --git a/web/app/vendor/symfony/finder/Iterator/MultiplePcreFilterIterator.php b/web/app/vendor/symfony/finder/Iterator/MultiplePcreFilterIterator.php index 564765d..82a9df3 100644 --- a/web/app/vendor/symfony/finder/Iterator/MultiplePcreFilterIterator.php +++ b/web/app/vendor/symfony/finder/Iterator/MultiplePcreFilterIterator.php @@ -27,9 +27,9 @@ abstract class MultiplePcreFilterIterator extends \FilterIterator protected $noMatchRegexps = []; /** - * @param \Iterator $iterator The Iterator to filter - * @param string[] $matchPatterns An array of patterns that need to match - * @param string[] $noMatchPatterns An array of patterns that need to not match + * @param \Iterator $iterator The Iterator to filter + * @param string[] $matchPatterns An array of patterns that need to match + * @param string[] $noMatchPatterns An array of patterns that need to not match */ public function __construct(\Iterator $iterator, array $matchPatterns, array $noMatchPatterns) { @@ -50,10 +50,8 @@ abstract class MultiplePcreFilterIterator extends \FilterIterator * If there is no regexps defined in the class, this method will accept the string. * Such case can be handled by child classes before calling the method if they want to * apply a different behavior. - * - * @return bool */ - protected function isAccepted(string $string) + protected function isAccepted(string $string): bool { // should at least not match one rule to exclude foreach ($this->noMatchRegexps as $regex) { @@ -79,10 +77,8 @@ abstract class MultiplePcreFilterIterator extends \FilterIterator /** * Checks whether the string is a regex. - * - * @return bool */ - protected function isRegex(string $str) + protected function isRegex(string $str): bool { $availableModifiers = 'imsxuADU'; @@ -110,8 +106,6 @@ abstract class MultiplePcreFilterIterator extends \FilterIterator /** * Converts string into regexp. - * - * @return string */ - abstract protected function toRegex(string $str); + abstract protected function toRegex(string $str): string; } diff --git a/web/app/vendor/symfony/finder/Iterator/PathFilterIterator.php b/web/app/vendor/symfony/finder/Iterator/PathFilterIterator.php index 7974c4e..c6d5813 100644 --- a/web/app/vendor/symfony/finder/Iterator/PathFilterIterator.php +++ b/web/app/vendor/symfony/finder/Iterator/PathFilterIterator.php @@ -11,23 +11,22 @@ namespace Symfony\Component\Finder\Iterator; +use Symfony\Component\Finder\SplFileInfo; + /** * PathFilterIterator filters files by path patterns (e.g. some/special/dir). * * @author Fabien Potencier * @author Włodzimierz Gajda * - * @extends MultiplePcreFilterIterator + * @extends MultiplePcreFilterIterator */ class PathFilterIterator extends MultiplePcreFilterIterator { /** * Filters the iterator values. - * - * @return bool */ - #[\ReturnTypeWillChange] - public function accept() + public function accept(): bool { $filename = $this->current()->getRelativePathname(); @@ -49,10 +48,8 @@ class PathFilterIterator extends MultiplePcreFilterIterator * Use only / as directory separator (on Windows also). * * @param string $str Pattern: regexp or dirname - * - * @return string */ - protected function toRegex(string $str) + protected function toRegex(string $str): string { return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/'; } diff --git a/web/app/vendor/symfony/finder/Iterator/RecursiveDirectoryIterator.php b/web/app/vendor/symfony/finder/Iterator/RecursiveDirectoryIterator.php index 27589cd..c321aee 100644 --- a/web/app/vendor/symfony/finder/Iterator/RecursiveDirectoryIterator.php +++ b/web/app/vendor/symfony/finder/Iterator/RecursiveDirectoryIterator.php @@ -18,23 +18,17 @@ use Symfony\Component\Finder\SplFileInfo; * Extends the \RecursiveDirectoryIterator to support relative paths. * * @author Victor Berchet + * @extends \RecursiveDirectoryIterator */ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator { - /** - * @var bool - */ - private $ignoreUnreadableDirs; - - /** - * @var bool - */ - private $rewindable; + private bool $ignoreUnreadableDirs; + private ?bool $rewindable = null; // these 3 properties take part of the performance optimization to avoid redoing the same work in all iterations - private $rootPath; - private $subPath; - private $directorySeparator = '/'; + private string $rootPath; + private string $subPath; + private string $directorySeparator = '/'; /** * @throws \RuntimeException @@ -55,17 +49,15 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator /** * Return an instance of SplFileInfo with support for relative paths. - * - * @return SplFileInfo */ - #[\ReturnTypeWillChange] - public function current() + public function current(): SplFileInfo { // the logic here avoids redoing the same work in all iterations - if (null === $subPathname = $this->subPath) { - $subPathname = $this->subPath = $this->getSubPath(); + if (!isset($this->subPath)) { + $this->subPath = $this->getSubPath(); } + $subPathname = $this->subPath; if ('' !== $subPathname) { $subPathname .= $this->directorySeparator; } @@ -78,13 +70,7 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator return new SplFileInfo($basePath.$subPathname, $this->subPath, $subPathname); } - /** - * @param bool $allowLinks - * - * @return bool - */ - #[\ReturnTypeWillChange] - public function hasChildren($allowLinks = false) + public function hasChildren(bool $allowLinks = false): bool { $hasChildren = parent::hasChildren($allowLinks); @@ -96,19 +82,16 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator parent::getChildren(); return true; - } catch (\UnexpectedValueException $e) { + } catch (\UnexpectedValueException) { // If directory is unreadable and finder is set to ignore it, skip children return false; } } /** - * @return \RecursiveDirectoryIterator - * * @throws AccessDeniedException */ - #[\ReturnTypeWillChange] - public function getChildren() + public function getChildren(): \RecursiveDirectoryIterator { try { $children = parent::getChildren(); @@ -130,11 +113,8 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator /** * Do nothing for non rewindable stream. - * - * @return void */ - #[\ReturnTypeWillChange] - public function rewind() + public function rewind(): void { if (false === $this->isRewindable()) { return; @@ -145,10 +125,8 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator /** * Checks if the stream is rewindable. - * - * @return bool */ - public function isRewindable() + public function isRewindable(): bool { if (null !== $this->rewindable) { return $this->rewindable; diff --git a/web/app/vendor/symfony/finder/Iterator/SizeRangeFilterIterator.php b/web/app/vendor/symfony/finder/Iterator/SizeRangeFilterIterator.php index 575bf29..925830a 100644 --- a/web/app/vendor/symfony/finder/Iterator/SizeRangeFilterIterator.php +++ b/web/app/vendor/symfony/finder/Iterator/SizeRangeFilterIterator.php @@ -22,7 +22,7 @@ use Symfony\Component\Finder\Comparator\NumberComparator; */ class SizeRangeFilterIterator extends \FilterIterator { - private $comparators = []; + private array $comparators = []; /** * @param \Iterator $iterator @@ -37,11 +37,8 @@ class SizeRangeFilterIterator extends \FilterIterator /** * Filters the iterator values. - * - * @return bool */ - #[\ReturnTypeWillChange] - public function accept() + public function accept(): bool { $fileinfo = $this->current(); if (!$fileinfo->isFile()) { diff --git a/web/app/vendor/symfony/finder/Iterator/SortableIterator.php b/web/app/vendor/symfony/finder/Iterator/SortableIterator.php index 9afde5c..cc4dbd6 100644 --- a/web/app/vendor/symfony/finder/Iterator/SortableIterator.php +++ b/web/app/vendor/symfony/finder/Iterator/SortableIterator.php @@ -28,8 +28,9 @@ class SortableIterator implements \IteratorAggregate public const SORT_BY_MODIFIED_TIME = 5; public const SORT_BY_NAME_NATURAL = 6; - private $iterator; - private $sort; + /** @var \Traversable */ + private \Traversable $iterator; + private \Closure|int $sort; /** * @param \Traversable $iterator @@ -37,7 +38,7 @@ class SortableIterator implements \IteratorAggregate * * @throws \InvalidArgumentException */ - public function __construct(\Traversable $iterator, $sort, bool $reverseOrder = false) + public function __construct(\Traversable $iterator, int|callable $sort, bool $reverseOrder = false) { $this->iterator = $iterator; $order = $reverseOrder ? -1 : 1; @@ -75,17 +76,13 @@ class SortableIterator implements \IteratorAggregate } elseif (self::SORT_BY_NONE === $sort) { $this->sort = $order; } elseif (\is_callable($sort)) { - $this->sort = $reverseOrder ? static function (\SplFileInfo $a, \SplFileInfo $b) use ($sort) { return -$sort($a, $b); } : $sort; + $this->sort = $reverseOrder ? static function (\SplFileInfo $a, \SplFileInfo $b) use ($sort) { return -$sort($a, $b); } : $sort(...); } else { throw new \InvalidArgumentException('The SortableIterator takes a PHP callable or a valid built-in sort algorithm as an argument.'); } } - /** - * @return \Traversable - */ - #[\ReturnTypeWillChange] - public function getIterator() + public function getIterator(): \Traversable { if (1 === $this->sort) { return $this->iterator; diff --git a/web/app/vendor/symfony/finder/Iterator/VcsIgnoredFilterIterator.php b/web/app/vendor/symfony/finder/Iterator/VcsIgnoredFilterIterator.php index e27158c..29fc2d9 100644 --- a/web/app/vendor/symfony/finder/Iterator/VcsIgnoredFilterIterator.php +++ b/web/app/vendor/symfony/finder/Iterator/VcsIgnoredFilterIterator.php @@ -13,6 +13,9 @@ namespace Symfony\Component\Finder\Iterator; use Symfony\Component\Finder\Gitignore; +/** + * @extends \FilterIterator + */ final class VcsIgnoredFilterIterator extends \FilterIterator { /** @@ -30,10 +33,20 @@ final class VcsIgnoredFilterIterator extends \FilterIterator */ private $ignoredPathsCache = []; + /** + * @param \Iterator $iterator + */ public function __construct(\Iterator $iterator, string $baseDir) { $this->baseDir = $this->normalizePath($baseDir); + foreach ($this->parentDirectoriesUpwards($this->baseDir) as $parentDirectory) { + if (@is_dir("{$parentDirectory}/.git")) { + $this->baseDir = $parentDirectory; + break; + } + } + parent::__construct($iterator); } @@ -58,7 +71,7 @@ final class VcsIgnoredFilterIterator extends \FilterIterator $ignored = false; - foreach ($this->parentsDirectoryDownward($fileRealPath) as $parentDirectory) { + foreach ($this->parentDirectoriesDownwards($fileRealPath) as $parentDirectory) { if ($this->isIgnored($parentDirectory)) { // rules in ignored directories are ignored, no need to check further. break; @@ -89,11 +102,11 @@ final class VcsIgnoredFilterIterator extends \FilterIterator /** * @return list */ - private function parentsDirectoryDownward(string $fileRealPath): array + private function parentDirectoriesUpwards(string $from): array { $parentDirectories = []; - $parentDirectory = $fileRealPath; + $parentDirectory = $from; while (true) { $newParentDirectory = \dirname($parentDirectory); @@ -103,16 +116,30 @@ final class VcsIgnoredFilterIterator extends \FilterIterator break; } - $parentDirectory = $newParentDirectory; - - if (0 !== strpos($parentDirectory, $this->baseDir)) { - break; - } - - $parentDirectories[] = $parentDirectory; + $parentDirectories[] = $parentDirectory = $newParentDirectory; } - return array_reverse($parentDirectories); + return $parentDirectories; + } + + private function parentDirectoriesUpTo(string $from, string $upTo): array + { + return array_filter( + $this->parentDirectoriesUpwards($from), + static function (string $directory) use ($upTo): bool { + return str_starts_with($directory, $upTo); + } + ); + } + + /** + * @return list + */ + private function parentDirectoriesDownwards(string $fileRealPath): array + { + return array_reverse( + $this->parentDirectoriesUpTo($fileRealPath, $this->baseDir) + ); } /** diff --git a/web/app/vendor/symfony/finder/SplFileInfo.php b/web/app/vendor/symfony/finder/SplFileInfo.php index 11604a2..867e8e8 100644 --- a/web/app/vendor/symfony/finder/SplFileInfo.php +++ b/web/app/vendor/symfony/finder/SplFileInfo.php @@ -18,8 +18,8 @@ namespace Symfony\Component\Finder; */ class SplFileInfo extends \SplFileInfo { - private $relativePath; - private $relativePathname; + private string $relativePath; + private string $relativePathname; /** * @param string $file The file name @@ -37,10 +37,8 @@ class SplFileInfo extends \SplFileInfo * Returns the relative path. * * This path does not contain the file name. - * - * @return string */ - public function getRelativePath() + public function getRelativePath(): string { return $this->relativePath; } @@ -49,10 +47,8 @@ class SplFileInfo extends \SplFileInfo * Returns the relative path name. * * This path contains the file name. - * - * @return string */ - public function getRelativePathname() + public function getRelativePathname(): string { return $this->relativePathname; } @@ -67,11 +63,9 @@ class SplFileInfo extends \SplFileInfo /** * Returns the contents of the file. * - * @return string - * * @throws \RuntimeException */ - public function getContents() + public function getContents(): string { set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; }); try { diff --git a/web/app/vendor/symfony/finder/composer.json b/web/app/vendor/symfony/finder/composer.json index ef19911..06d129c 100644 --- a/web/app/vendor/symfony/finder/composer.json +++ b/web/app/vendor/symfony/finder/composer.json @@ -16,9 +16,10 @@ } ], "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1" + }, + "require-dev": { + "symfony/filesystem": "^6.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Finder\\": "" }, diff --git a/web/app/vendor/symfony/polyfill-php80/LICENSE b/web/app/vendor/symfony/polyfill-php80/LICENSE deleted file mode 100644 index 5593b1d..0000000 --- a/web/app/vendor/symfony/polyfill-php80/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2020 Fabien Potencier - -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. diff --git a/web/app/vendor/symfony/polyfill-php80/Php80.php b/web/app/vendor/symfony/polyfill-php80/Php80.php deleted file mode 100644 index 362dd1a..0000000 --- a/web/app/vendor/symfony/polyfill-php80/Php80.php +++ /dev/null @@ -1,115 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Polyfill\Php80; - -/** - * @author Ion Bazan - * @author Nico Oelgart - * @author Nicolas Grekas - * - * @internal - */ -final class Php80 -{ - public static function fdiv(float $dividend, float $divisor): float - { - return @($dividend / $divisor); - } - - public static function get_debug_type($value): string - { - switch (true) { - case null === $value: return 'null'; - case \is_bool($value): return 'bool'; - case \is_string($value): return 'string'; - case \is_array($value): return 'array'; - case \is_int($value): return 'int'; - case \is_float($value): return 'float'; - case \is_object($value): break; - case $value instanceof \__PHP_Incomplete_Class: return '__PHP_Incomplete_Class'; - default: - if (null === $type = @get_resource_type($value)) { - return 'unknown'; - } - - if ('Unknown' === $type) { - $type = 'closed'; - } - - return "resource ($type)"; - } - - $class = \get_class($value); - - if (false === strpos($class, '@')) { - return $class; - } - - return (get_parent_class($class) ?: key(class_implements($class)) ?: 'class').'@anonymous'; - } - - public static function get_resource_id($res): int - { - if (!\is_resource($res) && null === @get_resource_type($res)) { - throw new \TypeError(sprintf('Argument 1 passed to get_resource_id() must be of the type resource, %s given', get_debug_type($res))); - } - - return (int) $res; - } - - public static function preg_last_error_msg(): string - { - switch (preg_last_error()) { - case \PREG_INTERNAL_ERROR: - return 'Internal error'; - case \PREG_BAD_UTF8_ERROR: - return 'Malformed UTF-8 characters, possibly incorrectly encoded'; - case \PREG_BAD_UTF8_OFFSET_ERROR: - return 'The offset did not correspond to the beginning of a valid UTF-8 code point'; - case \PREG_BACKTRACK_LIMIT_ERROR: - return 'Backtrack limit exhausted'; - case \PREG_RECURSION_LIMIT_ERROR: - return 'Recursion limit exhausted'; - case \PREG_JIT_STACKLIMIT_ERROR: - return 'JIT stack limit exhausted'; - case \PREG_NO_ERROR: - return 'No error'; - default: - return 'Unknown error'; - } - } - - public static function str_contains(string $haystack, string $needle): bool - { - return '' === $needle || false !== strpos($haystack, $needle); - } - - public static function str_starts_with(string $haystack, string $needle): bool - { - return 0 === strncmp($haystack, $needle, \strlen($needle)); - } - - public static function str_ends_with(string $haystack, string $needle): bool - { - if ('' === $needle || $needle === $haystack) { - return true; - } - - if ('' === $haystack) { - return false; - } - - $needleLength = \strlen($needle); - - return $needleLength <= \strlen($haystack) && 0 === substr_compare($haystack, $needle, -$needleLength); - } -} diff --git a/web/app/vendor/symfony/polyfill-php80/PhpToken.php b/web/app/vendor/symfony/polyfill-php80/PhpToken.php deleted file mode 100644 index fe6e691..0000000 --- a/web/app/vendor/symfony/polyfill-php80/PhpToken.php +++ /dev/null @@ -1,103 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Polyfill\Php80; - -/** - * @author Fedonyuk Anton - * - * @internal - */ -class PhpToken implements \Stringable -{ - /** - * @var int - */ - public $id; - - /** - * @var string - */ - public $text; - - /** - * @var int - */ - public $line; - - /** - * @var int - */ - public $pos; - - public function __construct(int $id, string $text, int $line = -1, int $position = -1) - { - $this->id = $id; - $this->text = $text; - $this->line = $line; - $this->pos = $position; - } - - public function getTokenName(): ?string - { - if ('UNKNOWN' === $name = token_name($this->id)) { - $name = \strlen($this->text) > 1 || \ord($this->text) < 32 ? null : $this->text; - } - - return $name; - } - - /** - * @param int|string|array $kind - */ - public function is($kind): bool - { - foreach ((array) $kind as $value) { - if (\in_array($value, [$this->id, $this->text], true)) { - return true; - } - } - - return false; - } - - public function isIgnorable(): bool - { - return \in_array($this->id, [\T_WHITESPACE, \T_COMMENT, \T_DOC_COMMENT, \T_OPEN_TAG], true); - } - - public function __toString(): string - { - return (string) $this->text; - } - - /** - * @return static[] - */ - public static function tokenize(string $code, int $flags = 0): array - { - $line = 1; - $position = 0; - $tokens = token_get_all($code, $flags); - foreach ($tokens as $index => $token) { - if (\is_string($token)) { - $id = \ord($token); - $text = $token; - } else { - [$id, $text, $line] = $token; - } - $tokens[$index] = new static($id, $text, $line, $position); - $position += \strlen($text); - } - - return $tokens; - } -} diff --git a/web/app/vendor/symfony/polyfill-php80/README.md b/web/app/vendor/symfony/polyfill-php80/README.md deleted file mode 100644 index 3816c55..0000000 --- a/web/app/vendor/symfony/polyfill-php80/README.md +++ /dev/null @@ -1,25 +0,0 @@ -Symfony Polyfill / Php80 -======================== - -This component provides features added to PHP 8.0 core: - -- [`Stringable`](https://php.net/stringable) interface -- [`fdiv`](https://php.net/fdiv) -- [`ValueError`](https://php.net/valueerror) class -- [`UnhandledMatchError`](https://php.net/unhandledmatcherror) class -- `FILTER_VALIDATE_BOOL` constant -- [`get_debug_type`](https://php.net/get_debug_type) -- [`PhpToken`](https://php.net/phptoken) class -- [`preg_last_error_msg`](https://php.net/preg_last_error_msg) -- [`str_contains`](https://php.net/str_contains) -- [`str_starts_with`](https://php.net/str_starts_with) -- [`str_ends_with`](https://php.net/str_ends_with) -- [`get_resource_id`](https://php.net/get_resource_id) - -More information can be found in the -[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md). - -License -======= - -This library is released under the [MIT license](LICENSE). diff --git a/web/app/vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php b/web/app/vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php deleted file mode 100644 index 7ea6d27..0000000 --- a/web/app/vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php +++ /dev/null @@ -1,22 +0,0 @@ -flags = $flags; - } -} diff --git a/web/app/vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php b/web/app/vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php deleted file mode 100644 index 72f1081..0000000 --- a/web/app/vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php +++ /dev/null @@ -1,7 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -use Symfony\Polyfill\Php80 as p; - -if (\PHP_VERSION_ID >= 80000) { - return; -} - -if (!defined('FILTER_VALIDATE_BOOL') && defined('FILTER_VALIDATE_BOOLEAN')) { - define('FILTER_VALIDATE_BOOL', \FILTER_VALIDATE_BOOLEAN); -} - -if (!function_exists('fdiv')) { - function fdiv(float $num1, float $num2): float { return p\Php80::fdiv($num1, $num2); } -} -if (!function_exists('preg_last_error_msg')) { - function preg_last_error_msg(): string { return p\Php80::preg_last_error_msg(); } -} -if (!function_exists('str_contains')) { - function str_contains(?string $haystack, ?string $needle): bool { return p\Php80::str_contains($haystack ?? '', $needle ?? ''); } -} -if (!function_exists('str_starts_with')) { - function str_starts_with(?string $haystack, ?string $needle): bool { return p\Php80::str_starts_with($haystack ?? '', $needle ?? ''); } -} -if (!function_exists('str_ends_with')) { - function str_ends_with(?string $haystack, ?string $needle): bool { return p\Php80::str_ends_with($haystack ?? '', $needle ?? ''); } -} -if (!function_exists('get_debug_type')) { - function get_debug_type($value): string { return p\Php80::get_debug_type($value); } -} -if (!function_exists('get_resource_id')) { - function get_resource_id($resource): int { return p\Php80::get_resource_id($resource); } -} diff --git a/web/app/vendor/symfony/polyfill-php80/composer.json b/web/app/vendor/symfony/polyfill-php80/composer.json deleted file mode 100644 index cd3e9b6..0000000 --- a/web/app/vendor/symfony/polyfill-php80/composer.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "symfony/polyfill-php80", - "type": "library", - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "keywords": ["polyfill", "shim", "compatibility", "portable"], - "homepage": "https://symfony.com", - "license": "MIT", - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "require": { - "php": ">=7.1" - }, - "autoload": { - "psr-4": { "Symfony\\Polyfill\\Php80\\": "" }, - "files": [ "bootstrap.php" ], - "classmap": [ "Resources/stubs" ] - }, - "minimum-stability": "dev", - "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - } -} diff --git a/web/app/views/blog-preview.php b/web/app/views/blog-preview.php index ec85ae8..ee050dd 100644 --- a/web/app/views/blog-preview.php +++ b/web/app/views/blog-preview.php @@ -39,16 +39,6 @@ text-decoration-none text-body
- - - - diff --git a/web/install.sh b/web/install.sh index 0f54fdb..177dee6 100644 --- a/web/install.sh +++ b/web/install.sh @@ -18,13 +18,9 @@ getAptPackage(){ # Update apt sources and install export DEBIAN_FRONTEND=noninteractive dpkg -s gnupg 2>/dev/null || (apt-get update && apt-get install -y gnupg) - echo "deb http://ppa.launchpad.net/stesie/libv8/ubuntu bionic main" | tee /etc/apt/sources.list.d/stesie-libv8.list && apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 1A10946ED858A0DF echo "deb http://ppa.launchpad.net/ondrej/php/ubuntu jammy main" | tee /etc/apt/sources.list.d/ondrej-php.list && apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4F4EA0AAE5267A6C apt-get update --allow-unauthenticated - apt-get install -y --allow-unauthenticated -o Dpkg::Options::="--force-overwrite" libv8 php7.4 php7.4-yaml php7.4-xml php7.4-dev php7.4-zip php7.4-mysql php7.4-mbstring php7.4-gd libseccomp-dev git vim ntp zip unzip curl wget libapache2-mod-xsendfile mysql-server php-pear cmake fp-compiler re2c libv8-7.5-dev libyaml-dev python2.7 python3.10 python3-requests openjdk-8-jdk openjdk-11-jdk openjdk-17-jdk - # Install PHP extensions - git clone https://github.com/phpv8/v8js.git --depth=1 -b 2.1.2 /tmp/pear/download/v8js-master && cd /tmp/pear/download/v8js-master - phpize && ./configure --with-php-config=/usr/bin/php-config --with-v8js=/opt/libv8-7.5 && make install && cd - + apt-get install -y --allow-unauthenticated -o Dpkg::Options::="--force-overwrite" php7.4 php7.4-yaml php7.4-xml php7.4-dev php7.4-zip php7.4-mysql php7.4-mbstring php7.4-gd libseccomp-dev git vim ntp zip unzip curl wget libapache2-mod-xsendfile mysql-server php-pear cmake fp-compiler re2c libyaml-dev python2.7 python3.10 python3-requests openjdk-8-jdk openjdk-11-jdk openjdk-17-jdk } setLAMPConf(){ @@ -53,7 +49,6 @@ UOJEOF a2enmod rewrite headers && sed -i -e '172s/AllowOverride None/AllowOverride All/' /etc/apache2/apache2.conf #Create UOJ session save dir and make PHP extensions available mkdir --mode=733 /var/lib/php/uoj_sessions && chmod +t /var/lib/php/uoj_sessions - sed -i -e '912a\extension=v8js.so\nextension=yaml.so' /etc/php/7.4/apache2/php.ini sed -i 's|;sys_temp_dir = "/tmp"|sys_temp_dir = "/tmp"|g' /etc/php/7.4/apache2/php.ini }