mirror of
https://github.com/renbaoshuo/S2OJ.git
synced 2024-11-24 00:48:41 +00:00
feat: Remote Judge (Prepare) (#27)
This commit is contained in:
commit
1689c34aa3
@ -622,8 +622,10 @@ CREATE TABLE `problems` (
|
||||
`ac_num` int NOT NULL DEFAULT '0',
|
||||
`submit_num` int NOT NULL DEFAULT '0',
|
||||
`difficulty` int NOT NULL DEFAULT '-1',
|
||||
`type` varchar(20) NOT NULL DEFAULT 'local',
|
||||
`assigned_to_judger` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'any',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `type` (`type`),
|
||||
KEY `assigned_to_judger` (`assigned_to_judger`),
|
||||
KEY `uploader` (`uploader`),
|
||||
KEY `difficulty` (`difficulty`),
|
||||
@ -648,6 +650,7 @@ UNLOCK TABLES;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `problems_contents` (
|
||||
`id` int NOT NULL,
|
||||
`remote_content` longtext COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
|
||||
`statement` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`statement_md` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
|
@ -6,7 +6,7 @@ ENV USE_MIRROR $USE_MIRROR
|
||||
SHELL ["/bin/bash", "-c"]
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
ENV PKGS="php7.4 php7.4-yaml php7.4-xml php7.4-dev php7.4-zip php7.4-mysql php7.4-mbstring php7.4-gd php7.4-imagick libseccomp-dev git vim ntp zip unzip curl wget apache2 libapache2-mod-xsendfile php-pear mysql-client build-essential fp-compiler re2c libseccomp-dev libyaml-dev python2.7 python3.10 python3-requests openjdk-8-jdk openjdk-11-jdk openjdk-17-jdk"
|
||||
ENV PKGS="php7.4 php7.4-yaml php7.4-xml php7.4-dev php7.4-zip php7.4-mysql php7.4-mbstring php7.4-gd php7.4-curl php7.4-imagick libseccomp-dev git vim ntp zip unzip curl wget apache2 libapache2-mod-xsendfile php-pear mysql-client build-essential fp-compiler re2c libseccomp-dev libyaml-dev python2.7 python3.10 python3-requests openjdk-8-jdk openjdk-11-jdk openjdk-17-jdk"
|
||||
RUN if [[ "$USE_MIRROR" == "1" ]]; then\
|
||||
sed -i "s@http://.*archive.ubuntu.com@https://mirrors.aliyun.com@g" /etc/apt/sources.list &&\
|
||||
sed -i "s@http://.*security.ubuntu.com@https://mirrors.aliyun.com@g" /etc/apt/sources.list ;\
|
||||
|
@ -3,7 +3,10 @@
|
||||
"gregwar/captcha": "^1.1",
|
||||
"phpmailer/phpmailer": "^6.6",
|
||||
"ezyang/htmlpurifier": "^4.16",
|
||||
"erusev/parsedown": "^1.7"
|
||||
"erusev/parsedown": "^1.7",
|
||||
"php-curl-class/php-curl-class": "^2.0",
|
||||
"ext-dom": "20031129",
|
||||
"ivopetkov/html5-dom-document-php": "2.*"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
|
@ -54,10 +54,10 @@ if ($is_manager) {
|
||||
isset($tabs_info[$cur_tab]) || UOJResponse::page404();
|
||||
|
||||
if (UOJContest::cur()->userCanStartFinalTest(Auth::user())) {
|
||||
if (CONTEST_PENDING_FINAL_TEST <= $contest['cur_progress']) {
|
||||
if (CONTEST_PENDING_FINAL_TEST == $contest['cur_progress']) {
|
||||
$start_test_form = new UOJBs4Form('start_test');
|
||||
$start_test_form->handle = function () {
|
||||
UOJContest::finalTest();
|
||||
UOJContest::cur()->finalTest();
|
||||
};
|
||||
$start_test_form->submit_button_config['class_str'] = 'btn btn-danger d-block w-100';
|
||||
$start_test_form->submit_button_config['smart_confirm'] = '';
|
||||
|
@ -596,11 +596,11 @@ EOD);
|
||||
</tr>
|
||||
EOD,
|
||||
function ($row) {
|
||||
$problem = UOJProblem::query($row['problem_id']);
|
||||
$problem = UOJContestProblem::query($row['problem_id'], UOJContest::cur());
|
||||
echo '<tr>';
|
||||
echo '<td>', $row['problem_id'], '</td>';
|
||||
echo '<td>', $problem->getLink(['with' => 'none']), '</td>';
|
||||
echo '<td>', isset($contest['extra_config']["problem_{$problem->info['id']}"]) ? $contest['extra_config']["problem_{$problem->info['id']}"] : 'default', '</td>';
|
||||
echo '<td>', $problem->getJudgeTypeInContest(), '</td>';
|
||||
echo '<td>';
|
||||
echo '<form class="d-inline-block" method="POST" target="_self" onsubmit=\'return confirm("你确定要将题目 #', $problem->info['id'], ' 从比赛中移除吗?")\'>';
|
||||
echo '<input type="hidden" name="_token" value="', crsf_token(), '">';
|
||||
|
@ -130,17 +130,39 @@ if (isset($_POST['update-status'])) {
|
||||
die();
|
||||
}
|
||||
|
||||
$problem_ban_list = DB::selectAll([
|
||||
$assignCond = [];
|
||||
|
||||
$problem_ban_list = array_map(fn ($x) => $x['id'], DB::selectAll([
|
||||
"select id from problems",
|
||||
"where", [
|
||||
["assigned_to_judger", "!=", "any"],
|
||||
["assigned_to_judger", "!=", $_POST['judger_name']]
|
||||
]
|
||||
]);
|
||||
foreach ($problem_ban_list as &$val) {
|
||||
$val = $val['id'];
|
||||
]));
|
||||
|
||||
if ($problem_ban_list) {
|
||||
$assignCond[] = ["problem_id", "not in", DB::rawtuple($problem_ban_list)];
|
||||
}
|
||||
|
||||
if ($_POST['judger_name'] == "remote_judger") {
|
||||
$problem_ban_list = array_map(fn ($x) => $x['id'], DB::selectAll([
|
||||
"select id from problems",
|
||||
"where", [
|
||||
["type", "!=", "remote"],
|
||||
],
|
||||
]));
|
||||
} else {
|
||||
$problem_ban_list = array_map(fn ($x) => $x['id'], DB::selectAll([
|
||||
"select id from problems",
|
||||
"where", [
|
||||
["type", "!=", "local"],
|
||||
],
|
||||
]));
|
||||
}
|
||||
|
||||
if ($problem_ban_list) {
|
||||
$assignCond[] = ["problem_id", "not in", DB::rawtuple($problem_ban_list)];
|
||||
}
|
||||
$assignCond = $problem_ban_list ? [["problem_id", "not in", DB::rawtuple($problem_ban_list)]] : [];
|
||||
|
||||
$submission = null;
|
||||
$hack = null;
|
||||
|
@ -20,6 +20,9 @@ function getProblemTR($info) {
|
||||
if ($problem->isUserOwnProblem(Auth::user())) {
|
||||
$html .= ' <span class="badge text-white bg-info">' . UOJLocale::get('problems::my problem') . '</span> ';
|
||||
}
|
||||
if ($info['type'] == 'remote') {
|
||||
$html .= ' ' . HTML::tag('span', ['class' => 'badge text-bg-success'], '远端评测题');
|
||||
}
|
||||
if ($info['is_hidden']) {
|
||||
$html .= ' <span class="badge text-bg-danger"><i class="bi bi-eye-slash-fill"></i> ' . UOJLocale::get('hidden') . '</span> ';
|
||||
}
|
||||
|
124
web/app/controllers/new_remote_problem.php
Normal file
124
web/app/controllers/new_remote_problem.php
Normal file
@ -0,0 +1,124 @@
|
||||
<?php
|
||||
requireLib('bootstrap5');
|
||||
requirePHPLib('form');
|
||||
requirePHPLib('data');
|
||||
|
||||
Auth::check() || redirectToLogin();
|
||||
UOJProblem::userCanCreateProblem(Auth::user()) || UOJResponse::page403();
|
||||
|
||||
$new_remote_problem_form = new UOJForm('new_remote_problem');
|
||||
$new_remote_problem_form->addSelect('remote_online_judge', [
|
||||
'label' => '远程 OJ',
|
||||
'options' => [
|
||||
'codeforces' => 'Codeforces',
|
||||
],
|
||||
]);
|
||||
$new_remote_problem_form->addInput('remote_problem_id', [
|
||||
'div_class' => 'mt-3',
|
||||
'label' => '远程 OJ 上的题目 ID',
|
||||
'validator_php' => function ($id, &$vdata) {
|
||||
if ($_POST['remote_online_judge'] === 'codeforces') {
|
||||
$id = trim(strtoupper($id));
|
||||
|
||||
if (!validateCodeforcesProblemId($id)) {
|
||||
return '不合法的题目 ID';
|
||||
}
|
||||
|
||||
$vdata['remote_problem_id'] = $id;
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
return '不合法的远程 OJ 类型';
|
||||
},
|
||||
]);
|
||||
$new_remote_problem_form->handle = function (&$vdata) {
|
||||
$remote_online_judge = $_POST['remote_online_judge'];
|
||||
$remote_problem_id = $vdata['remote_problem_id'];
|
||||
$remote_provider = UOJRemoteProblem::$providers[$remote_online_judge];
|
||||
|
||||
try {
|
||||
$data = UOJRemoteProblem::getProblemBasicInfo($remote_online_judge, $remote_problem_id);
|
||||
} catch (Exception $e) {
|
||||
$data = null;
|
||||
UOJLog::error($e->getMessage());
|
||||
}
|
||||
|
||||
if ($data === null) {
|
||||
UOJResponse::page500('题目抓取失败,可能是题目不存在或者没有题面!如果题目没有问题,请稍后再试。<a href="">返回</a>');
|
||||
}
|
||||
|
||||
$submission_requirement = [
|
||||
[
|
||||
"name" => "answer",
|
||||
"type" => "source code",
|
||||
"file_name" => "answer.code",
|
||||
"languages" => $remote_provider['languages'],
|
||||
]
|
||||
];
|
||||
$enc_submission_requirement = json_encode($submission_requirement);
|
||||
|
||||
$extra_config = [
|
||||
'remote_online_judge' => $remote_online_judge,
|
||||
'remote_problem_id' => $remote_problem_id,
|
||||
'time_limit' => $data['time_limit'],
|
||||
'memory_limit' => $data['memory_limit'],
|
||||
];
|
||||
$enc_extra_config = json_encode($extra_config);
|
||||
|
||||
DB::insert([
|
||||
"insert into problems",
|
||||
"(title, uploader, is_hidden, submission_requirement, extra_config, difficulty, type)",
|
||||
"values", DB::tuple([$data['title'], Auth::id(), 1, $enc_submission_requirement, $enc_extra_config, $data['difficulty'] ?: -1, "remote"])
|
||||
]);
|
||||
|
||||
$id = DB::insert_id();
|
||||
|
||||
DB::insert([
|
||||
"insert into problems_contents",
|
||||
"(id, remote_content, statement, statement_md)",
|
||||
"values",
|
||||
DB::tuple([$id, HTML::purifier()->purify($data['statement']), '', ''])
|
||||
]);
|
||||
dataNewProblem($id);
|
||||
|
||||
redirectTo("/problem/{$id}");
|
||||
die();
|
||||
};
|
||||
$new_remote_problem_form->runAtServer();
|
||||
?>
|
||||
|
||||
<?php echoUOJPageHeader('导入远程题库') ?>
|
||||
|
||||
<h1>导入远程题库</h1>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<?php $new_remote_problem_form->printHTML() ?>
|
||||
</div>
|
||||
<div class="col-md-6 mt-3 mt-md-0">
|
||||
<h4>使用帮助</h4>
|
||||
<ul>
|
||||
<li>
|
||||
<p>目前支持导入以下题库的题目作为远端评测题:</p>
|
||||
<ul class="mb-3">
|
||||
<li>Codeforces</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>在导入题目前请先搜索题库中是否已经存在相应题目,避免重复添加。</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<?php uojIncludeView('sidebar') ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php echoUOJPageFooter() ?>
|
@ -223,7 +223,6 @@ if (UOJContest::cur()) {
|
||||
<div class="row">
|
||||
<!-- Left col -->
|
||||
<div class="col-lg-9">
|
||||
|
||||
<?php if (isset($tabs_info)) : ?>
|
||||
<!-- 比赛导航 -->
|
||||
<div class="mb-2">
|
||||
@ -233,7 +232,6 @@ if (UOJContest::cur()) {
|
||||
|
||||
<div class="card card-default mb-2">
|
||||
<div class="card-body">
|
||||
|
||||
<h1 class="card-title text-center">
|
||||
<?php if (UOJContest::cur()) : ?>
|
||||
<?= UOJProblem::cur()->getTitle(['with' => 'letter', 'simplify' => true]) ?>
|
||||
@ -243,8 +241,13 @@ if (UOJContest::cur()) {
|
||||
</h1>
|
||||
|
||||
<?php
|
||||
if (UOJProblem::info('type') == 'local') {
|
||||
$time_limit = $conf instanceof UOJProblemConf ? $conf->getVal('time_limit', 1) : null;
|
||||
$memory_limit = $conf instanceof UOJProblemConf ? $conf->getVal('memory_limit', 256) : null;
|
||||
} else if (UOJProblem::info('type') == 'remote') {
|
||||
$time_limit = UOJProblem::cur()->getExtraConfig('time_limit');
|
||||
$memory_limit = UOJProblem::cur()->getExtraConfig('memory_limit');
|
||||
}
|
||||
?>
|
||||
<div class="text-center small">
|
||||
时间限制: <?= $time_limit ? "$time_limit s" : "N/A" ?>
|
||||
@ -259,6 +262,12 @@ if (UOJContest::cur()) {
|
||||
<article class="mt-3 markdown-body">
|
||||
<?= $problem_content['statement'] ?>
|
||||
</article>
|
||||
|
||||
<?php if (UOJProblem::info('type') == 'remote') : ?>
|
||||
<article class="mt-3 markdown-body remote-content">
|
||||
<?= UOJProblem::cur()->queryContent()['remote_content'] ?>
|
||||
</article>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<div class="tab-pane" id="submit">
|
||||
<?php if ($pre_submit_check_ret !== true) : ?>
|
||||
@ -392,6 +401,14 @@ if (UOJContest::cur()) {
|
||||
<?= UOJProblem::cur()->getUploaderLink() ?>
|
||||
</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<span class="flex-shrink-0">
|
||||
题目来源
|
||||
</span>
|
||||
<span>
|
||||
<?= UOJProblem::cur()->getProviderLink() ?>
|
||||
</span>
|
||||
</li>
|
||||
<?php if (!UOJContest::cur() || UOJContest::cur()->progress() >= CONTEST_FINISHED) : ?>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<span class="flex-shrink-0">
|
||||
|
@ -10,6 +10,7 @@ requirePHPLib('data');
|
||||
|
||||
UOJProblem::init(UOJRequest::get('id')) || UOJResponse::page404();
|
||||
UOJProblem::cur()->userCanManage(Auth::user()) || UOJResponse::page403();
|
||||
UOJProblem::info('type') === 'local' || UOJResponse::page404();
|
||||
$problem = UOJProblem::info();
|
||||
$problem_extra_config = UOJProblem::cur()->getExtraConfig();
|
||||
|
||||
@ -466,93 +467,6 @@ $rejudgege97_form->submit_button_config['class_str'] = 'btn btn-danger d-block w
|
||||
$rejudgege97_form->submit_button_config['text'] = '重测 >=97 的程序';
|
||||
$rejudgege97_form->submit_button_config['smart_confirm'] = '';
|
||||
|
||||
$view_type_form = new UOJBs4Form('view_type');
|
||||
$view_type_form->addVSelect(
|
||||
'view_content_type',
|
||||
array(
|
||||
'NONE' => '禁止',
|
||||
'ALL_AFTER_AC' => 'AC后',
|
||||
'ALL' => '所有人'
|
||||
),
|
||||
'查看提交文件:',
|
||||
$problem_extra_config['view_content_type']
|
||||
);
|
||||
$view_type_form->addVSelect(
|
||||
'view_all_details_type',
|
||||
array(
|
||||
'NONE' => '禁止',
|
||||
'SELF' => '仅自己',
|
||||
'ALL_AFTER_AC' => 'AC后',
|
||||
'ALL' => '所有人'
|
||||
),
|
||||
'查看全部详细信息:',
|
||||
$problem_extra_config['view_all_details_type']
|
||||
);
|
||||
$view_type_form->addVSelect(
|
||||
'view_details_type',
|
||||
array(
|
||||
'NONE' => '禁止',
|
||||
'SELF' => '仅自己',
|
||||
'ALL_AFTER_AC' => 'AC后',
|
||||
'ALL' => '所有人'
|
||||
),
|
||||
'查看测试点详细信息:',
|
||||
$problem_extra_config['view_details_type']
|
||||
);
|
||||
$view_type_form->handle = function () {
|
||||
global $problem, $problem_extra_config;
|
||||
|
||||
$config = $problem_extra_config;
|
||||
$config['view_content_type'] = $_POST['view_content_type'];
|
||||
$config['view_all_details_type'] = $_POST['view_all_details_type'];
|
||||
$config['view_details_type'] = $_POST['view_details_type'];
|
||||
$esc_config = json_encode($config);
|
||||
|
||||
DB::update([
|
||||
"update problems",
|
||||
"set", ["extra_config" => $esc_config],
|
||||
"where", ["id" => $problem['id']]
|
||||
]);
|
||||
};
|
||||
$view_type_form->submit_button_config['class_str'] = 'btn btn-warning d-block w-100 mt-2';
|
||||
|
||||
$solution_view_type_form = new UOJBs4Form('solution_view_type');
|
||||
$solution_view_type_form->addVSelect(
|
||||
'view_solution_type',
|
||||
array(
|
||||
'NONE' => '禁止',
|
||||
'ALL_AFTER_AC' => 'AC后',
|
||||
'ALL' => '所有人'
|
||||
),
|
||||
'查看题解:',
|
||||
$problem_extra_config['view_solution_type']
|
||||
);
|
||||
$solution_view_type_form->addVSelect(
|
||||
'submit_solution_type',
|
||||
array(
|
||||
'NONE' => '禁止',
|
||||
'ALL_AFTER_AC' => 'AC后',
|
||||
'ALL' => '所有人'
|
||||
),
|
||||
'提交题解:',
|
||||
$problem_extra_config['submit_solution_type']
|
||||
);
|
||||
$solution_view_type_form->handle = function () {
|
||||
global $problem, $problem_extra_config;
|
||||
|
||||
$config = $problem_extra_config;
|
||||
$config['view_solution_type'] = $_POST['view_solution_type'];
|
||||
$config['submit_solution_type'] = $_POST['submit_solution_type'];
|
||||
$esc_config = json_encode($config);
|
||||
|
||||
DB::update([
|
||||
"update problems",
|
||||
"set", ["extra_config" => $esc_config],
|
||||
"where", ["id" => $problem['id']]
|
||||
]);
|
||||
};
|
||||
$solution_view_type_form->submit_button_config['class_str'] = 'btn btn-warning d-block w-100 mt-2';
|
||||
|
||||
if ($problem['hackable']) {
|
||||
$test_std_form = new UOJBs4Form('test_std');
|
||||
$test_std_form->handle = function () use ($problem, $data_disp) {
|
||||
@ -618,8 +532,6 @@ if ($problem['hackable']) {
|
||||
}
|
||||
|
||||
$hackable_form->runAtServer();
|
||||
$view_type_form->runAtServer();
|
||||
$solution_view_type_form->runAtServer();
|
||||
$data_form->runAtServer();
|
||||
$clear_data_form->runAtServer();
|
||||
$rejudge_form->runAtServer();
|
||||
@ -753,18 +665,6 @@ $info_form->runAtServer();
|
||||
<?php $test_std_form->printHTML() ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<div class="mt-2">
|
||||
<button id="button-display_view_type" type="button" class="btn btn-primary d-block w-100" onclick="$('#div-view_type').toggle('fast');">提交记录可视权限</button>
|
||||
<div class="mt-2" id="div-view_type" style="display:none; padding-left:5px; padding-right:5px;">
|
||||
<?php $view_type_form->printHTML(); ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<button id="button-solution_view_type" type="button" class="btn btn-primary d-block w-100" onclick="$('#div-solution_view_type').toggle('fast');">题解可视权限</button>
|
||||
<div class="mt-2" id="div-solution_view_type" style="display:none; padding-left:5px; padding-right:5px;">
|
||||
<?php $solution_view_type_form->printHTML(); ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<?php $data_form->printHTML(); ?>
|
||||
</div>
|
||||
|
@ -82,11 +82,13 @@ if (isSuperUser(Auth::user())) {
|
||||
管理者
|
||||
</a>
|
||||
</li>
|
||||
<?php if (UOJProblem::info('type') === 'local') : ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/problem/<?= UOJProblem::info('id') ?>/manage/data" role="tab">
|
||||
数据
|
||||
</a>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
</ul>
|
||||
|
||||
<div class="card card-default">
|
||||
|
@ -55,7 +55,7 @@ if (UOJProblem::userCanCreateProblem(Auth::user())) {
|
||||
如有,在此处填写其他于题意或数据相关的说明。
|
||||
|
||||
EOD;
|
||||
$new_problem_form = new UOJBs4Form('new_problem');
|
||||
$new_problem_form = new UOJForm('new_problem');
|
||||
$new_problem_form->handle = function () use ($default_statement) {
|
||||
DB::insert([
|
||||
"insert into problems",
|
||||
@ -77,11 +77,10 @@ EOD;
|
||||
redirectTo("/problem/{$id}/manage/statement");
|
||||
die();
|
||||
};
|
||||
$new_problem_form->submit_button_config['align'] = 'right';
|
||||
$new_problem_form->submit_button_config['class_str'] = 'btn btn-primary';
|
||||
$new_problem_form->submit_button_config['text'] = UOJLocale::get('problems::add new');
|
||||
$new_problem_form->submit_button_config['smart_confirm'] = '';
|
||||
|
||||
$new_problem_form->config['submit_container']['class'] = '';
|
||||
$new_problem_form->config['submit_button']['class'] = 'bg-transparent border-0 d-block w-100 px-3 py-2 text-start';
|
||||
$new_problem_form->config['submit_button']['text'] = '<i class="bi bi-plus-lg"></i> 新建本地题目';
|
||||
$new_problem_form->config['confirm']['text'] = '添加新题';
|
||||
$new_problem_form->runAtServer();
|
||||
}
|
||||
|
||||
@ -95,6 +94,9 @@ function getProblemTR($info) {
|
||||
if ($problem->isUserOwnProblem(Auth::user())) {
|
||||
$html .= ' <a href="/problems?my=on"><span class="badge text-white bg-info">' . UOJLocale::get('problems::my problem') . '</span></a> ';
|
||||
}
|
||||
if ($info['type'] == 'remote') {
|
||||
$html .= ' ' . HTML::tag('span', ['class' => 'badge text-bg-success'], '远端评测题');
|
||||
}
|
||||
if ($info['is_hidden']) {
|
||||
$html .= ' <a href="/problems?is_hidden=on"><span class="badge text-bg-danger"><i class="bi bi-eye-slash-fill"></i> ' . UOJLocale::get('hidden') . '</span></a> ';
|
||||
}
|
||||
@ -254,25 +256,20 @@ $pag = new Paginator([
|
||||
<!-- left col -->
|
||||
<div class="col-md-9">
|
||||
<!-- title -->
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="d-flex justify-content-between flex-wrap">
|
||||
<h1>
|
||||
<?= UOJLocale::get('problems') ?>
|
||||
</h1>
|
||||
|
||||
<?php if (isset($new_problem_form)) : ?>
|
||||
<div class="text-end">
|
||||
<?php $new_problem_form->printHTML(); ?>
|
||||
<div>
|
||||
<?= HTML::tablist($tabs_info, $cur_tab, 'nav-pills') ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
</div>
|
||||
<!-- end title -->
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-4 col-12">
|
||||
<?= HTML::tablist($tabs_info, $cur_tab, 'nav-pills') ?>
|
||||
</div>
|
||||
<div class="text-end p-2 col-12 col-sm-8">
|
||||
<?= $pag->pagination() ?>
|
||||
|
||||
<div class="text-end">
|
||||
<div class="form-check d-inline-block me-2">
|
||||
<input type="checkbox" id="input-show_tags_mode" class="form-check-input" <?= isset($_COOKIE['show_tags_mode']) ? 'checked="checked" ' : '' ?> />
|
||||
<label class="form-check-label" for="input-show_tags_mode">
|
||||
@ -287,9 +284,6 @@ $pag = new Paginator([
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?= $pag->pagination() ?>
|
||||
|
||||
<script type="text/javascript">
|
||||
$('#input-show_tags_mode').click(function() {
|
||||
@ -390,6 +384,23 @@ $pag = new Paginator([
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php if (UOJProblem::userCanCreateProblem(Auth::user())) : ?>
|
||||
<div class="card mb-3">
|
||||
<div class="card-header fw-bold">
|
||||
新建题目
|
||||
</div>
|
||||
<div class="list-group list-group-flush">
|
||||
<div class="list-group-item list-group-item-action p-0">
|
||||
<?php $new_problem_form->printHTML() ?>
|
||||
</div>
|
||||
<a class="list-group-item list-group-item-action" href="/problems/new/remote">
|
||||
<i class="bi bi-cloud-plus"></i>
|
||||
新建远端评测题目
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
<!-- sidebar -->
|
||||
<?php uojIncludeView('sidebar') ?>
|
||||
</aside>
|
||||
|
@ -65,9 +65,12 @@ $problem_editor->runAtServer();
|
||||
|
||||
$difficulty_form = new UOJForm('difficulty');
|
||||
$difficulty_form->addSelect('difficulty', [
|
||||
'div_class' => 'flex-grow-1',
|
||||
'options' => [-1 => '暂无评定'] + array_combine(UOJProblem::$difficulty, UOJProblem::$difficulty),
|
||||
'default_value' => UOJProblem::info('difficulty'),
|
||||
]);
|
||||
$difficulty_form->config['form']['class'] = 'd-flex';
|
||||
$difficulty_form->config['submit_container']['class'] = 'ms-2';
|
||||
$difficulty_form->handle = function () {
|
||||
DB::update([
|
||||
"update problems",
|
||||
@ -80,6 +83,170 @@ $difficulty_form->handle = function () {
|
||||
]);
|
||||
};
|
||||
$difficulty_form->runAtServer();
|
||||
|
||||
if (UOJProblem::info('type') == 'remote') {
|
||||
$remote_online_judge = UOJProblem::cur()->getExtraConfig('remote_online_judge');
|
||||
$remote_problem_id = UOJProblem::cur()->getExtraConfig('remote_problem_id');
|
||||
$remote_provider = UOJRemoteProblem::$providers[$remote_online_judge];
|
||||
|
||||
$re_crawl_form = new UOJForm('re_crawl');
|
||||
$re_crawl_form->appendHTML(<<<EOD
|
||||
<ul>
|
||||
<li>远程题库:{$remote_provider['name']}</li>
|
||||
<li>远程题号:{$remote_problem_id}</li>
|
||||
</ul>
|
||||
EOD);
|
||||
$re_crawl_form->config['submit_button']['text'] = '重新爬取';
|
||||
$re_crawl_form->handle = function () use ($remote_online_judge, $remote_problem_id, $remote_provider) {
|
||||
try {
|
||||
$data = UOJRemoteProblem::getProblemBasicInfo($remote_online_judge, $remote_problem_id);
|
||||
} catch (Exception $e) {
|
||||
$data = null;
|
||||
UOJLog::error($e->getMessage());
|
||||
}
|
||||
|
||||
if ($data === null) {
|
||||
UOJResponse::page500('题目抓取失败,可能是题目不存在或者没有题面!如果题目没有问题,请稍后再试。<a href="">返回</a>');
|
||||
}
|
||||
|
||||
$submission_requirement = [
|
||||
[
|
||||
"name" => "answer",
|
||||
"type" => "source code",
|
||||
"file_name" => "answer.code",
|
||||
"languages" => $remote_provider['languages'],
|
||||
]
|
||||
];
|
||||
$enc_submission_requirement = json_encode($submission_requirement);
|
||||
|
||||
$extra_config = [
|
||||
'remote_online_judge' => $remote_online_judge,
|
||||
'remote_problem_id' => $remote_problem_id,
|
||||
'time_limit' => $data['time_limit'],
|
||||
'memory_limit' => $data['memory_limit'],
|
||||
];
|
||||
$enc_extra_config = json_encode($extra_config);
|
||||
|
||||
DB::update([
|
||||
"update problems",
|
||||
"set", [
|
||||
"title" => $data['title'],
|
||||
"submission_requirement" => $enc_submission_requirement,
|
||||
"extra_config" => $enc_extra_config,
|
||||
"difficulty" => $data['difficulty'] ?: -1,
|
||||
],
|
||||
"where", [
|
||||
"id" => UOJProblem::info('id'),
|
||||
],
|
||||
]);
|
||||
|
||||
DB::update([
|
||||
"update problems_contents",
|
||||
"set", [
|
||||
"remote_content" => HTML::purifier()->purify($data['statement']),
|
||||
],
|
||||
"where", [
|
||||
"id" => UOJProblem::info('id'),
|
||||
],
|
||||
]);
|
||||
|
||||
redirectTo(UOJProblem::cur()->getUri());
|
||||
};
|
||||
$re_crawl_form->runAtServer();
|
||||
}
|
||||
|
||||
$view_type_form = new UOJForm('view_type');
|
||||
$view_type_form->addSelect('view_content_type', [
|
||||
'div_class' => 'row align-items-center g-0',
|
||||
'label_class' => 'form-label col-auto m-0 flex-grow-1',
|
||||
'select_class' => 'col-auto form-select w-auto',
|
||||
'label' => '查看提交文件',
|
||||
'options' => [
|
||||
'NONE' => '禁止',
|
||||
'ALL_AFTER_AC' => 'AC 后',
|
||||
'ALL' => '所有人',
|
||||
],
|
||||
'default_value' => $problem_extra_config['view_content_type'],
|
||||
]);
|
||||
$view_type_form->addSelect('view_all_details_type', [
|
||||
'div_class' => 'row align-items-center g-0 mt-3',
|
||||
'label_class' => 'form-label col-auto m-0 flex-grow-1',
|
||||
'select_class' => 'col-auto form-select w-auto',
|
||||
'label' => '查看全部详细信息',
|
||||
'options' => [
|
||||
'NONE' => '禁止',
|
||||
'SELF' => '仅自己',
|
||||
'ALL_AFTER_AC' => 'AC 后',
|
||||
'ALL' => '所有人'
|
||||
],
|
||||
'default_value' => $problem_extra_config['view_all_details_type'],
|
||||
]);
|
||||
$view_type_form->addSelect('view_details_type', [
|
||||
'div_class' => 'row align-items-center g-0 mt-3',
|
||||
'label_class' => 'form-label col-auto m-0 flex-grow-1',
|
||||
'select_class' => 'col-auto form-select w-auto',
|
||||
'label' => '查看测试点详细信息',
|
||||
'options' => [
|
||||
'NONE' => '禁止',
|
||||
'SELF' => '仅自己',
|
||||
'ALL_AFTER_AC' => 'AC 后',
|
||||
'ALL' => '所有人',
|
||||
],
|
||||
'default_value' => $problem_extra_config['view_details_type'],
|
||||
]);
|
||||
$view_type_form->handle = function () {
|
||||
$config = UOJProblem::cur()->getExtraConfig();
|
||||
$config['view_content_type'] = $_POST['view_content_type'];
|
||||
$config['view_all_details_type'] = $_POST['view_all_details_type'];
|
||||
$config['view_details_type'] = $_POST['view_details_type'];
|
||||
$esc_config = json_encode($config);
|
||||
|
||||
DB::update([
|
||||
"update problems",
|
||||
"set", ["extra_config" => $esc_config],
|
||||
"where", ["id" => UOJProblem::info('id')]
|
||||
]);
|
||||
};
|
||||
$view_type_form->runAtServer();
|
||||
|
||||
$solution_view_type_form = new UOJForm('solution_view_type');
|
||||
$solution_view_type_form->addSelect('view_solution_type', [
|
||||
'div_class' => 'row align-items-center g-0',
|
||||
'label_class' => 'form-label col-auto m-0 flex-grow-1',
|
||||
'select_class' => 'col-auto form-select w-auto',
|
||||
'label' => '查看题解',
|
||||
'options' => [
|
||||
'NONE' => '禁止',
|
||||
'ALL_AFTER_AC' => 'AC 后',
|
||||
'ALL' => '所有人',
|
||||
],
|
||||
'default_value' => $problem_extra_config['view_solution_type'],
|
||||
]);
|
||||
$solution_view_type_form->addSelect('submit_solution_type', [
|
||||
'div_class' => 'row align-items-center g-0 mt-3',
|
||||
'label_class' => 'form-label col-auto m-0 flex-grow-1',
|
||||
'select_class' => 'col-auto form-select w-auto',
|
||||
'label' => '提交题解',
|
||||
'options' => [
|
||||
'NONE' => '禁止',
|
||||
'ALL_AFTER_AC' => 'AC 后',
|
||||
'ALL' => '所有人',
|
||||
],
|
||||
'default_value' => $problem_extra_config['submit_solution_type'],
|
||||
]);
|
||||
$solution_view_type_form->handle = function () {
|
||||
$config = UOJProblem::cur()->getExtraConfig();
|
||||
$config['view_solution_type'] = $_POST['view_solution_type'];
|
||||
$config['submit_solution_type'] = $_POST['submit_solution_type'];
|
||||
$esc_config = json_encode($config);
|
||||
|
||||
DB::update([
|
||||
"update problems",
|
||||
"set", ["extra_config" => $esc_config],
|
||||
"where", ["id" => UOJProblem::info('id')]
|
||||
]);
|
||||
};
|
||||
$solution_view_type_form->runAtServer();
|
||||
?>
|
||||
|
||||
<?php echoUOJPageHeader('题面编辑 - ' . HTML::stripTags(UOJProblem::info('title'))) ?>
|
||||
@ -102,11 +269,13 @@ $difficulty_form->runAtServer();
|
||||
管理者
|
||||
</a>
|
||||
</li>
|
||||
<?php if (UOJProblem::info('type') == 'local') : ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/problem/<?= UOJProblem::info('id') ?>/manage/data" role="tab">
|
||||
数据
|
||||
</a>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
</ul>
|
||||
|
||||
<div class="card card-default">
|
||||
@ -261,6 +430,35 @@ $difficulty_form->runAtServer();
|
||||
<?php $difficulty_form->printHTML() ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (UOJProblem::info('type') == 'remote') : ?>
|
||||
<div class="card mt-3">
|
||||
<div class="card-header fw-bold">
|
||||
重新爬取题目信息
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php $re_crawl_form->printHTML() ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header fw-bold">
|
||||
提交记录可视权限
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php $view_type_form->printHTML() ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header fw-bold">
|
||||
题解可视权限
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php $solution_view_type_form->printHTML() ?>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
|
@ -75,3 +75,7 @@ function validateUserAndStoreByUsername($username, &$vdata) {
|
||||
function is_short_string($str) {
|
||||
return is_string($str) && strlen($str) <= 256;
|
||||
}
|
||||
|
||||
function validateCodeforcesProblemId($str) {
|
||||
return preg_match('/[1-9][0-9]{0,5}[A-Z][1-9]?/', $str) !== true;
|
||||
}
|
||||
|
@ -423,7 +423,7 @@ class HTML {
|
||||
return implode("&", $r);
|
||||
}
|
||||
|
||||
public static function purifier() {
|
||||
public static function purifier($extra_allowed_html = []) {
|
||||
$config = HTMLPurifier_Config::createDefault();
|
||||
$config->set('Output.Newline', true);
|
||||
$def = $config->getHTMLDefinition(true);
|
||||
@ -435,14 +435,14 @@ class HTML {
|
||||
$def->addElement('header', 'Block', 'Flow', 'Common');
|
||||
$def->addElement('footer', 'Block', 'Flow', 'Common');
|
||||
|
||||
$extra_allowed_html = [
|
||||
mergeConfig($extra_allowed_html, [
|
||||
'span' => [
|
||||
'class' => 'Enum#uoj-username',
|
||||
'data-realname' => 'Text',
|
||||
'data-color' => 'Color',
|
||||
],
|
||||
'img' => ['width' => 'Text'],
|
||||
];
|
||||
]);
|
||||
|
||||
foreach ($extra_allowed_html as $element => $attributes) {
|
||||
foreach ($attributes as $attribute => $type) {
|
||||
|
@ -53,65 +53,6 @@ class UOJContest {
|
||||
return isSuperUser($user) || UOJUser::checkPermission($user, 'contests.create');
|
||||
}
|
||||
|
||||
public static function finalTest() {
|
||||
$contest = self::info();
|
||||
|
||||
$res = DB::selectAll([
|
||||
"select id, problem_id, content, submitter, hide_score_to_others from submissions",
|
||||
"where", ["contest_id" => $contest['id']]
|
||||
]);
|
||||
foreach ($res as $submission) {
|
||||
$content = json_decode($submission['content'], true);
|
||||
if (isset($content['final_test_config'])) {
|
||||
$content['config'] = $content['final_test_config'];
|
||||
unset($content['final_test_config']);
|
||||
}
|
||||
if (isset($content['first_test_config'])) {
|
||||
unset($content['first_test_config']);
|
||||
}
|
||||
UOJSubmission::rejudgeById($submission['id'], [
|
||||
'reason_text' => HTML::stripTags($contest['name']) . ' 最终测试',
|
||||
'reason_url' => HTML::url(UOJContest::cur()->getUri()),
|
||||
'set_q' => [
|
||||
"content" => json_encode($content)
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
// warning: check if this command works well when the database is not MySQL
|
||||
DB::update([
|
||||
"update submissions",
|
||||
"set", [
|
||||
"score = hidden_score",
|
||||
"hidden_score = NULL",
|
||||
"hide_score_to_others = 0"
|
||||
], "where", [
|
||||
"contest_id" => $contest['id'],
|
||||
"hide_score_to_others" => 1
|
||||
]
|
||||
]);
|
||||
|
||||
$updated = [];
|
||||
foreach ($res as $submission) {
|
||||
$submitter = $submission['submitter'];
|
||||
$pid = $submission['problem_id'];
|
||||
if (isset($updated[$submitter]) && isset($updated[$submitter][$pid])) {
|
||||
continue;
|
||||
}
|
||||
updateBestACSubmissions($submitter, $pid);
|
||||
if (!isset($updated[$submitter])) {
|
||||
$updated[$submitter] = [];
|
||||
}
|
||||
$updated[$submitter][$pid] = true;
|
||||
}
|
||||
|
||||
DB::update([
|
||||
"update contests",
|
||||
"set", ["status" => 'testing'],
|
||||
"where", ["id" => $contest['id']]
|
||||
]);
|
||||
}
|
||||
|
||||
public static function announceOfficialResults() {
|
||||
// time config
|
||||
set_time_limit(0);
|
||||
@ -246,11 +187,121 @@ class UOJContest {
|
||||
$label = '开始最终测试';
|
||||
}
|
||||
|
||||
if ($this->progress() >= CONTEST_TESTING) {
|
||||
$label = '重新' . $label;
|
||||
return $label;
|
||||
}
|
||||
|
||||
return $label;
|
||||
public function finalTest() {
|
||||
ignore_user_abort(true);
|
||||
set_time_limit(0);
|
||||
|
||||
DB::update([
|
||||
"update contests",
|
||||
"set", ["status" => 'testing'],
|
||||
"where", ["id" => $this->info['id']]
|
||||
]);
|
||||
|
||||
if (DB::affected_rows() !== 1) {
|
||||
// 已经有其他人开始评测了,不进行任何操作
|
||||
return;
|
||||
}
|
||||
|
||||
$res = DB::selectAll([
|
||||
"select id, problem_id, content, result, submitter, hide_score_to_others from submissions",
|
||||
"where", ["contest_id" => $this->info['id']]
|
||||
]);
|
||||
foreach ($res as $submission) {
|
||||
$content = json_decode($submission['content'], true);
|
||||
|
||||
if (isset($content['final_test_config'])) {
|
||||
$content['config'] = $content['final_test_config'];
|
||||
unset($content['final_test_config']);
|
||||
}
|
||||
|
||||
if (isset($content['first_test_config'])) {
|
||||
unset($content['first_test_config']);
|
||||
}
|
||||
|
||||
$q = [
|
||||
'content' => json_encode($content),
|
||||
];
|
||||
|
||||
$problem_judge_type = $this->info['extra_config']["problem_{$submission['problem_id']}"] ?: $this->defaultProblemJudgeType();
|
||||
$result = json_decode($submission['result'], true);
|
||||
|
||||
switch ($problem_judge_type) {
|
||||
case 'sample':
|
||||
if (isset($result['final_result']) && $result['final_result']['status'] == 'Judged') {
|
||||
$q += [
|
||||
'result' => json_encode($result['final_result']),
|
||||
'score' => $result['final_result']['score'],
|
||||
'used_time' => $result['final_result']['time'],
|
||||
'used_memory' => $result['final_result']['memory'],
|
||||
'judge_time' => $this->info['end_time_str'],
|
||||
'status' => 'Judged',
|
||||
];
|
||||
|
||||
if ($submission['hide_score_to_others']) {
|
||||
$q['hidden_score'] = $q['score'];
|
||||
$q['score'] = null;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'no-details':
|
||||
case 'full':
|
||||
if ($result['status'] == 'Judged') {
|
||||
$q += [
|
||||
'result' => $submission['result'],
|
||||
'score' => $result['score'],
|
||||
'used_time' => $result['time'],
|
||||
'used_memory' => $result['memory'],
|
||||
'judge_time' => $this->info['end_time_str'],
|
||||
'status' => 'Judged',
|
||||
];
|
||||
|
||||
if ($submission['hide_score_to_others']) {
|
||||
$q['hidden_score'] = $q['score'];
|
||||
$q['score'] = null;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
UOJSubmission::rejudgeById($submission['id'], [
|
||||
'reason_text' => HTML::stripTags($this->info['name']) . ' 最终测试',
|
||||
'reason_url' => HTML::url(UOJContest::cur()->getUri()),
|
||||
'set_q' => $q,
|
||||
]);
|
||||
}
|
||||
|
||||
// warning: check if this command works well when the database is not MySQL
|
||||
DB::update([
|
||||
"update submissions",
|
||||
"set", [
|
||||
"score = hidden_score",
|
||||
"hidden_score = NULL",
|
||||
"hide_score_to_others = 0"
|
||||
], "where", [
|
||||
"contest_id" => $this->info['id'],
|
||||
"hide_score_to_others" => 1
|
||||
]
|
||||
]);
|
||||
|
||||
$updated = [];
|
||||
foreach ($res as $submission) {
|
||||
$submitter = $submission['submitter'];
|
||||
$pid = $submission['problem_id'];
|
||||
if (isset($updated[$submitter]) && isset($updated[$submitter][$pid])) {
|
||||
continue;
|
||||
}
|
||||
updateBestACSubmissions($submitter, $pid);
|
||||
if (!isset($updated[$submitter])) {
|
||||
$updated[$submitter] = [];
|
||||
}
|
||||
$updated[$submitter][$pid] = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function queryJudgeProgress() {
|
||||
|
@ -120,6 +120,53 @@ class UOJForm {
|
||||
$this->add($name, $html, $validator_php, $validator_js);
|
||||
}
|
||||
|
||||
public function addInput($name, $config) {
|
||||
$config += [
|
||||
'type' => 'text',
|
||||
'div_class' => '',
|
||||
'input_class' => 'form-control',
|
||||
'default_value' => '',
|
||||
'label' => '',
|
||||
'label_class' => 'form-label',
|
||||
'placeholder' => '',
|
||||
'help' => '',
|
||||
'help_class' => 'form-text',
|
||||
'validator_php' => function ($x) {
|
||||
return '';
|
||||
},
|
||||
'validator_js' => null,
|
||||
];
|
||||
|
||||
$html = '';
|
||||
$html .= HTML::tag_begin('div', ['class' => $config['div_class'], 'id' => "div-$name"]);
|
||||
|
||||
if ($config['label']) {
|
||||
$html .= HTML::tag('label', [
|
||||
'class' => $config['label_class'],
|
||||
'for' => "input-$name",
|
||||
'id' => "label-$name"
|
||||
], $config['label']);
|
||||
}
|
||||
|
||||
$html .= HTML::empty_tag('input', [
|
||||
'class' => $config['input_class'],
|
||||
'type' => $config['type'],
|
||||
'name' => $name,
|
||||
'id' => "input-$name",
|
||||
'value' => $config['default_value'],
|
||||
'placeholder' => $config['placeholder'],
|
||||
]);
|
||||
$html .= HTML::tag('div', ['class' => 'invalid-feedback', 'id' => "help-$name"], '');
|
||||
|
||||
if ($config['help']) {
|
||||
$html .= HTML::tag('div', ['class' => $config['help_class']], $config['help']);
|
||||
}
|
||||
|
||||
$html .= HTML::tag_end('div');
|
||||
|
||||
$this->add($name, $html, $config['validator_php'], $config['validator_js']);
|
||||
}
|
||||
|
||||
public function addCheckbox($name, $config) {
|
||||
$config += [
|
||||
'checked' => false,
|
||||
|
@ -65,7 +65,7 @@ class UOJLang {
|
||||
}
|
||||
$is_avail = [];
|
||||
$dep_list = [
|
||||
['C++', 'C++11', 'C++14', 'C++17', 'C++20'],
|
||||
['C++98', 'C++03', 'C++11', 'C++', 'C++17', 'C++20'],
|
||||
['Java8', 'Java11', 'Java17']
|
||||
];
|
||||
foreach ($list as $lang) {
|
||||
|
@ -382,6 +382,26 @@ class UOJProblem {
|
||||
return UOJUser::getLink($this->info['uploader'] ?: "root");
|
||||
}
|
||||
|
||||
public function getProviderLink() {
|
||||
if ($this->info['type'] == 'local') {
|
||||
return HTML::tag('a', ['href' => HTML::url('/')], UOJConfig::$data['profile']['oj-name-short']);
|
||||
}
|
||||
|
||||
$remote_oj = $this->getExtraConfig('remote_online_judge');
|
||||
$remote_id = $this->getExtraConfig('remote_problem_id');
|
||||
|
||||
if (!$remote_oj || !array_key_exists($remote_oj, UOJRemoteProblem::$providers)) {
|
||||
return 'Error';
|
||||
}
|
||||
|
||||
$provider = UOJRemoteProblem::$providers[$remote_oj];
|
||||
|
||||
return HTML::tag('a', [
|
||||
'href' => UOJRemoteProblem::getProblemRemoteUrl($remote_oj, $remote_id),
|
||||
'target' => '_blank'
|
||||
], $provider['name']);
|
||||
}
|
||||
|
||||
public function getDifficultyHTML() {
|
||||
$difficulty = (int)$this->info['difficulty'];
|
||||
$difficulty_text = in_array($difficulty, static::$difficulty) ? $difficulty : '?';
|
||||
@ -440,10 +460,16 @@ class UOJProblem {
|
||||
return $key === null ? $extra_config : $extra_config[$key];
|
||||
}
|
||||
public function getCustomTestRequirement() {
|
||||
if ($this->info['type'] == 'remote') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$extra_config = json_decode($this->info['extra_config'], true);
|
||||
|
||||
if (isset($extra_config['custom_test_requirement'])) {
|
||||
return $extra_config['custom_test_requirement'];
|
||||
} else {
|
||||
}
|
||||
|
||||
$answer = [
|
||||
'name' => 'answer',
|
||||
'type' => 'source code',
|
||||
@ -462,7 +488,6 @@ class UOJProblem {
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
public function userCanView(array $user = null, array $cfg = []) {
|
||||
$cfg += ['ensure' => false];
|
||||
|
146
web/app/models/UOJRemoteProblem.php
Normal file
146
web/app/models/UOJRemoteProblem.php
Normal file
@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
class UOJRemoteProblem {
|
||||
static $providers = [
|
||||
'codeforces' => [
|
||||
'name' => 'Codeforces',
|
||||
'short_name' => 'CF',
|
||||
'url' => 'https://codeforces.com',
|
||||
'not_exists_texts' => [
|
||||
'<th>Actions</th>',
|
||||
'Statement is not available on English language',
|
||||
'ограничение по времени на тест',
|
||||
],
|
||||
'languages' => ['C', 'C++', 'C++17', 'C++20', 'Java17', 'Pascal', 'Python2', 'Python3'],
|
||||
],
|
||||
];
|
||||
|
||||
static function getCodeforcesProblemUrl($id) {
|
||||
return static::$providers['codeforces']['url'] . '/problemset/problem/' . preg_replace_callback('/([1-9][0-9]{0,5})([A-Z][1-9]?)/', fn ($matches) => $matches[1] . '/' . $matches[2], $id);
|
||||
}
|
||||
|
||||
// 传入 ID 需确保有效
|
||||
static function getCodeforcesProblemBasicInfo($id) {
|
||||
$curl = new Curl();
|
||||
$curl->setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36 S2OJ/3.1.0');
|
||||
|
||||
$remote_provider = static::$providers['codeforces'];
|
||||
|
||||
$html = retry_loop(function () use ($curl, $id) {
|
||||
$curl->get(static::getCodeforcesProblemUrl($id));
|
||||
|
||||
if ($curl->error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $curl->response;
|
||||
});
|
||||
|
||||
if (!$html) return null;
|
||||
|
||||
$html = preg_replace('/\$\$\$/', '$', $html);
|
||||
$dom = new \IvoPetkov\HTML5DOMDocument();
|
||||
$dom->loadHTML($html);
|
||||
|
||||
$judgestatement = $dom->querySelector('html')->innerHTML;
|
||||
|
||||
foreach ($remote_provider['not_exists_texts'] as $text) {
|
||||
if (str_contains($judgestatement, $text)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
$statement_dom = $dom->querySelector('.problem-statement');
|
||||
$title = explode('. ', trim($statement_dom->querySelector('.title')->innerHTML))[1];
|
||||
$title = "【{$remote_provider['short_name']}{$id}】{$title}";
|
||||
$time_limit = intval(substr($statement_dom->querySelector('.time-limit')->innerHTML, 53));
|
||||
$memory_limit = intval(substr($statement_dom->querySelector('.memory-limit')->innerHTML, 55));
|
||||
$difficulty = -1;
|
||||
|
||||
foreach ($dom->querySelectorAll('.tag-box') as &$elem) {
|
||||
$matches = [];
|
||||
|
||||
if (preg_match('/\*([0-9]{3,4})/', trim($elem->innerHTML), $matches)) {
|
||||
$difficulty = intval($matches[1]);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($difficulty != -1) {
|
||||
$closest = null;
|
||||
|
||||
foreach (UOJProblem::$difficulty as $val) {
|
||||
if ($closest === null || abs($val - $difficulty) < abs($closest - $difficulty)) {
|
||||
$closest = $val;
|
||||
}
|
||||
}
|
||||
|
||||
$difficulty = $closest;
|
||||
}
|
||||
|
||||
$statement_dom->removeChild($statement_dom->querySelector('.header'));
|
||||
$statement_dom->childNodes->item(0)->insertBefore($dom->createElement('h3', 'Description'), $statement_dom->childNodes->item(0)->childNodes->item(0));
|
||||
|
||||
foreach ($statement_dom->querySelectorAll('.section-title') as &$elem) {
|
||||
$elem->outerHTML = '<h3>' . $elem->innerHTML . '</h3>';
|
||||
}
|
||||
|
||||
$sample_input_cnt = 0;
|
||||
$sample_output_cnt = 0;
|
||||
|
||||
foreach ($statement_dom->querySelectorAll('.input') as &$input_dom) {
|
||||
$sample_input_cnt++;
|
||||
$input_text = '';
|
||||
|
||||
if ($input_dom->querySelector('.test-example-line')) {
|
||||
foreach ($input_dom->querySelectorAll('.test-example-line') as &$line) {
|
||||
$input_text .= HTML::stripTags($line->innerHTML) . "\n";
|
||||
}
|
||||
} else {
|
||||
$input_text = HTML::stripTags($input_dom->querySelector('pre')->innerHTML);
|
||||
}
|
||||
|
||||
$input_dom->outerHTML = HTML::tag('h4', [], "Input #{$sample_input_cnt}") . HTML::tag('pre', [], HTML::tag('code', [], $input_text));
|
||||
}
|
||||
|
||||
foreach ($statement_dom->querySelectorAll('.output') as &$output_dom) {
|
||||
$sample_output_cnt++;
|
||||
$output_text = '';
|
||||
|
||||
if ($output_dom->querySelector('.test-example-line')) {
|
||||
foreach ($output_dom->querySelectorAll('.test-example-line') as &$line) {
|
||||
$output_text .= HTML::stripTags($line->innerHTML) . "\n";
|
||||
}
|
||||
} else {
|
||||
$output_text = HTML::stripTags($output_dom->querySelector('pre')->innerHTML);
|
||||
}
|
||||
|
||||
$output_dom->outerHTML = HTML::tag('h4', [], "Output #{$sample_output_cnt}") . HTML::tag('pre', [], HTML::tag('code', [], $output_text));
|
||||
}
|
||||
|
||||
return [
|
||||
'title' => $title,
|
||||
'time_limit' => $time_limit,
|
||||
'memory_limit' => $memory_limit,
|
||||
'difficulty' => $difficulty,
|
||||
'statement' => $statement_dom->innerHTML,
|
||||
];
|
||||
}
|
||||
|
||||
public static function getProblemRemoteUrl($oj, $id) {
|
||||
if ($oj === 'codeforces') {
|
||||
return static::getCodeforcesProblemUrl($id);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function getProblemBasicInfo($oj, $id) {
|
||||
if ($oj === 'codeforces') {
|
||||
return static::getCodeforcesProblemBasicInfo($id);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -87,11 +87,18 @@ class UOJSubmission {
|
||||
$judge_reason = '';
|
||||
|
||||
$content['config'][] = ['problem_id', UOJProblem::info('id')];
|
||||
|
||||
if (UOJProblem::info('type') == 'remote') {
|
||||
$content['config'][] = ['remote_online_judge', UOJProblem::cur()->getExtraConfig('remote_online_judge')];
|
||||
$content['config'][] = ['remote_problem_id', UOJProblem::cur()->getExtraConfig('remote_problem_id')];
|
||||
}
|
||||
|
||||
if ($is_contest_submission && UOJContestProblem::cur()->getJudgeTypeInContest() == 'sample') {
|
||||
$content['final_test_config'] = $content['config'];
|
||||
$content['config'][] = ['test_sample_only', 'on'];
|
||||
$judge_reason = json_encode(['text' => '样例测评']);
|
||||
}
|
||||
|
||||
$content_json = json_encode($content);
|
||||
|
||||
$language = static::getAndRememberSubmissionLanguage($content);
|
||||
@ -407,9 +414,14 @@ class UOJSubmission {
|
||||
}
|
||||
|
||||
public function userCanRejudge(array $user = null) {
|
||||
if ($this->problem->info['type'] == 'remote') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isSuperUser($user)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->userCanManageProblemOrContest($user) && $this->hasFullyJudged();
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ Route::group(
|
||||
Route::any('/', '/index.php');
|
||||
Route::any('/problems', '/problem_set.php');
|
||||
Route::any('/problems/template', '/problem_set.php?tab=template');
|
||||
Route::any('/problems/new/remote', '/new_remote_problem.php');
|
||||
Route::any('/problem/{id}', '/problem.php');
|
||||
Route::any('/problem/{id}/solutions', '/problem_solutions.php');
|
||||
Route::any('/problem/{id}/statistics', '/problem_statistics.php');
|
||||
|
145
web/app/vendor/composer/ClassLoader.php
vendored
145
web/app/vendor/composer/ClassLoader.php
vendored
@ -37,57 +37,130 @@ namespace Composer\Autoload;
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @see http://www.php-fig.org/psr/psr-0/
|
||||
* @see http://www.php-fig.org/psr/psr-4/
|
||||
* @see https://www.php-fig.org/psr/psr-0/
|
||||
* @see https://www.php-fig.org/psr/psr-4/
|
||||
*/
|
||||
class ClassLoader
|
||||
{
|
||||
/** @var ?string */
|
||||
private $vendorDir;
|
||||
|
||||
// PSR-4
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, array<string, int>>
|
||||
*/
|
||||
private $prefixLengthsPsr4 = array();
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, array<int, string>>
|
||||
*/
|
||||
private $prefixDirsPsr4 = array();
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, string>
|
||||
*/
|
||||
private $fallbackDirsPsr4 = array();
|
||||
|
||||
// PSR-0
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, array<string, string[]>>
|
||||
*/
|
||||
private $prefixesPsr0 = array();
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, string>
|
||||
*/
|
||||
private $fallbackDirsPsr0 = array();
|
||||
|
||||
/** @var bool */
|
||||
private $useIncludePath = false;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
* @psalm-var array<string, string>
|
||||
*/
|
||||
private $classMap = array();
|
||||
|
||||
/** @var bool */
|
||||
private $classMapAuthoritative = false;
|
||||
|
||||
/**
|
||||
* @var bool[]
|
||||
* @psalm-var array<string, bool>
|
||||
*/
|
||||
private $missingClasses = array();
|
||||
|
||||
/** @var ?string */
|
||||
private $apcuPrefix;
|
||||
|
||||
/**
|
||||
* @var self[]
|
||||
*/
|
||||
private static $registeredLoaders = array();
|
||||
|
||||
/**
|
||||
* @param ?string $vendorDir
|
||||
*/
|
||||
public function __construct($vendorDir = null)
|
||||
{
|
||||
$this->vendorDir = $vendorDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getPrefixes()
|
||||
{
|
||||
if (!empty($this->prefixesPsr0)) {
|
||||
return call_user_func_array('array_merge', $this->prefixesPsr0);
|
||||
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
* @psalm-return array<string, array<int, string>>
|
||||
*/
|
||||
public function getPrefixesPsr4()
|
||||
{
|
||||
return $this->prefixDirsPsr4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
* @psalm-return array<string, string>
|
||||
*/
|
||||
public function getFallbackDirs()
|
||||
{
|
||||
return $this->fallbackDirsPsr0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
* @psalm-return array<string, string>
|
||||
*/
|
||||
public function getFallbackDirsPsr4()
|
||||
{
|
||||
return $this->fallbackDirsPsr4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[] Array of classname => path
|
||||
* @psalm-return array<string, string>
|
||||
*/
|
||||
public function getClassMap()
|
||||
{
|
||||
return $this->classMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $classMap Class to filename map
|
||||
* @param string[] $classMap Class to filename map
|
||||
* @psalm-param array<string, string> $classMap
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addClassMap(array $classMap)
|
||||
{
|
||||
@ -103,8 +176,10 @@ class ClassLoader
|
||||
* appending or prepending to the ones previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param array|string $paths The PSR-0 root directories
|
||||
* @param string[]|string $paths The PSR-0 root directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add($prefix, $paths, $prepend = false)
|
||||
{
|
||||
@ -148,10 +223,12 @@ class ClassLoader
|
||||
* appending or prepending to the ones previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-4 base directories
|
||||
* @param string[]|string $paths The PSR-4 base directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addPsr4($prefix, $paths, $prepend = false)
|
||||
{
|
||||
@ -196,7 +273,9 @@ class ClassLoader
|
||||
* replacing any others previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param array|string $paths The PSR-0 base directories
|
||||
* @param string[]|string $paths The PSR-0 base directories
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set($prefix, $paths)
|
||||
{
|
||||
@ -212,9 +291,11 @@ class ClassLoader
|
||||
* replacing any others previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-4 base directories
|
||||
* @param string[]|string $paths The PSR-4 base directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setPsr4($prefix, $paths)
|
||||
{
|
||||
@ -234,6 +315,8 @@ class ClassLoader
|
||||
* Turns on searching the include path for class files.
|
||||
*
|
||||
* @param bool $useIncludePath
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setUseIncludePath($useIncludePath)
|
||||
{
|
||||
@ -256,6 +339,8 @@ class ClassLoader
|
||||
* that have not been registered with the class map.
|
||||
*
|
||||
* @param bool $classMapAuthoritative
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||
{
|
||||
@ -276,6 +361,8 @@ class ClassLoader
|
||||
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||
*
|
||||
* @param string|null $apcuPrefix
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setApcuPrefix($apcuPrefix)
|
||||
{
|
||||
@ -296,25 +383,44 @@ class ClassLoader
|
||||
* Registers this instance as an autoloader.
|
||||
*
|
||||
* @param bool $prepend Whether to prepend the autoloader or not
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register($prepend = false)
|
||||
{
|
||||
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||
|
||||
if (null === $this->vendorDir) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($prepend) {
|
||||
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
||||
} else {
|
||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||
self::$registeredLoaders[$this->vendorDir] = $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters this instance as an autoloader.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function unregister()
|
||||
{
|
||||
spl_autoload_unregister(array($this, 'loadClass'));
|
||||
|
||||
if (null !== $this->vendorDir) {
|
||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given class or interface.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
* @return bool|null True if loaded, null otherwise
|
||||
* @return true|null True if loaded, null otherwise
|
||||
*/
|
||||
public function loadClass($class)
|
||||
{
|
||||
@ -323,6 +429,8 @@ class ClassLoader
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -367,6 +475,21 @@ class ClassLoader
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently registered loaders indexed by their corresponding vendor directories.
|
||||
*
|
||||
* @return self[]
|
||||
*/
|
||||
public static function getRegisteredLoaders()
|
||||
{
|
||||
return self::$registeredLoaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $class
|
||||
* @param string $ext
|
||||
* @return string|false
|
||||
*/
|
||||
private function findFileWithExtension($class, $ext)
|
||||
{
|
||||
// PSR-4 lookup
|
||||
@ -438,6 +561,10 @@ class ClassLoader
|
||||
* Scope isolated include.
|
||||
*
|
||||
* Prevents access to $this/self from included files.
|
||||
*
|
||||
* @param string $file
|
||||
* @return void
|
||||
* @private
|
||||
*/
|
||||
function includeFile($file)
|
||||
{
|
||||
|
350
web/app/vendor/composer/InstalledVersions.php
vendored
Normal file
350
web/app/vendor/composer/InstalledVersions.php
vendored
Normal file
@ -0,0 +1,350 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer;
|
||||
|
||||
use Composer\Autoload\ClassLoader;
|
||||
use Composer\Semver\VersionParser;
|
||||
|
||||
/**
|
||||
* This class is copied in every Composer installed project and available to all
|
||||
*
|
||||
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
|
||||
*
|
||||
* To require its presence, you can require `composer-runtime-api ^2.0`
|
||||
*/
|
||||
class InstalledVersions
|
||||
{
|
||||
/**
|
||||
* @var mixed[]|null
|
||||
* @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null
|
||||
*/
|
||||
private static $installed;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
private static $canGetVendors;
|
||||
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
|
||||
*/
|
||||
private static $installedByVendor = array();
|
||||
|
||||
/**
|
||||
* Returns a list of all package names which are present, either by being installed, replaced or provided
|
||||
*
|
||||
* @return string[]
|
||||
* @psalm-return list<string>
|
||||
*/
|
||||
public static function getInstalledPackages()
|
||||
{
|
||||
$packages = array();
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
$packages[] = array_keys($installed['versions']);
|
||||
}
|
||||
|
||||
if (1 === \count($packages)) {
|
||||
return $packages[0];
|
||||
}
|
||||
|
||||
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all package names with a specific type e.g. 'library'
|
||||
*
|
||||
* @param string $type
|
||||
* @return string[]
|
||||
* @psalm-return list<string>
|
||||
*/
|
||||
public static function getInstalledPackagesByType($type)
|
||||
{
|
||||
$packagesByType = array();
|
||||
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
foreach ($installed['versions'] as $name => $package) {
|
||||
if (isset($package['type']) && $package['type'] === $type) {
|
||||
$packagesByType[] = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $packagesByType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given package is installed
|
||||
*
|
||||
* This also returns true if the package name is provided or replaced by another package
|
||||
*
|
||||
* @param string $packageName
|
||||
* @param bool $includeDevRequirements
|
||||
* @return bool
|
||||
*/
|
||||
public static function isInstalled($packageName, $includeDevRequirements = true)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (isset($installed['versions'][$packageName])) {
|
||||
return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given package satisfies a version constraint
|
||||
*
|
||||
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
|
||||
*
|
||||
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
|
||||
*
|
||||
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
|
||||
* @param string $packageName
|
||||
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
|
||||
* @return bool
|
||||
*/
|
||||
public static function satisfies(VersionParser $parser, $packageName, $constraint)
|
||||
{
|
||||
$constraint = $parser->parseConstraints($constraint);
|
||||
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
|
||||
|
||||
return $provided->matches($constraint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a version constraint representing all the range(s) which are installed for a given package
|
||||
*
|
||||
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
|
||||
* whether a given version of a package is installed, and not just whether it exists
|
||||
*
|
||||
* @param string $packageName
|
||||
* @return string Version constraint usable with composer/semver
|
||||
*/
|
||||
public static function getVersionRanges($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ranges = array();
|
||||
if (isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
|
||||
}
|
||||
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
|
||||
}
|
||||
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
|
||||
}
|
||||
if (array_key_exists('provided', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
|
||||
}
|
||||
|
||||
return implode(' || ', $ranges);
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||
*/
|
||||
public static function getVersion($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['version'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['version'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||
*/
|
||||
public static function getPrettyVersion($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['pretty_version'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
|
||||
*/
|
||||
public static function getReference($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['reference'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['reference'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
|
||||
*/
|
||||
public static function getInstallPath($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
|
||||
*/
|
||||
public static function getRootPackage()
|
||||
{
|
||||
$installed = self::getInstalled();
|
||||
|
||||
return $installed[0]['root'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw installed.php data for custom implementations
|
||||
*
|
||||
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
|
||||
* @return array[]
|
||||
* @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}
|
||||
*/
|
||||
public static function getRawData()
|
||||
{
|
||||
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
|
||||
|
||||
if (null === self::$installed) {
|
||||
// only require the installed.php file if this file is loaded from its dumped location,
|
||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||
self::$installed = include __DIR__ . '/installed.php';
|
||||
} else {
|
||||
self::$installed = array();
|
||||
}
|
||||
}
|
||||
|
||||
return self::$installed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw data of all installed.php which are currently loaded for custom implementations
|
||||
*
|
||||
* @return array[]
|
||||
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
|
||||
*/
|
||||
public static function getAllRawData()
|
||||
{
|
||||
return self::getInstalled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lets you reload the static array from another file
|
||||
*
|
||||
* This is only useful for complex integrations in which a project needs to use
|
||||
* this class but then also needs to execute another project's autoloader in process,
|
||||
* and wants to ensure both projects have access to their version of installed.php.
|
||||
*
|
||||
* A typical case would be PHPUnit, where it would need to make sure it reads all
|
||||
* the data it needs from this class, then call reload() with
|
||||
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
|
||||
* the project in which it runs can then also use this class safely, without
|
||||
* interference between PHPUnit's dependencies and the project's dependencies.
|
||||
*
|
||||
* @param array[] $data A vendor/composer/installed.php data set
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data
|
||||
*/
|
||||
public static function reload($data)
|
||||
{
|
||||
self::$installed = $data;
|
||||
self::$installedByVendor = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
|
||||
*/
|
||||
private static function getInstalled()
|
||||
{
|
||||
if (null === self::$canGetVendors) {
|
||||
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
|
||||
}
|
||||
|
||||
$installed = array();
|
||||
|
||||
if (self::$canGetVendors) {
|
||||
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
||||
if (isset(self::$installedByVendor[$vendorDir])) {
|
||||
$installed[] = self::$installedByVendor[$vendorDir];
|
||||
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
||||
$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
|
||||
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
|
||||
self::$installed = $installed[count($installed) - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (null === self::$installed) {
|
||||
// only require the installed.php file if this file is loaded from its dumped location,
|
||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||
self::$installed = require __DIR__ . '/installed.php';
|
||||
} else {
|
||||
self::$installed = array();
|
||||
}
|
||||
}
|
||||
$installed[] = self::$installed;
|
||||
|
||||
return $installed;
|
||||
}
|
||||
}
|
@ -6,5 +6,13 @@ $vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
|
||||
'CaseInsensitiveArray' => $vendorDir . '/php-curl-class/php-curl-class/src/Curl.class.php',
|
||||
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||
'Curl' => $vendorDir . '/php-curl-class/php-curl-class/src/Curl.class.php',
|
||||
'ParsedownMath' => $vendorDir . '/parsedown-math/ParsedownMath.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',
|
||||
);
|
||||
|
3
web/app/vendor/composer/autoload_files.php
vendored
3
web/app/vendor/composer/autoload_files.php
vendored
@ -6,5 +6,8 @@ $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',
|
||||
'16eed290c5592c18dc3f16802ad3d0e4' => $vendorDir . '/ivopetkov/html5-dom-document-php/autoload.php',
|
||||
);
|
||||
|
1
web/app/vendor/composer/autoload_psr4.php
vendored
1
web/app/vendor/composer/autoload_psr4.php
vendored
@ -6,6 +6,7 @@ $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'),
|
||||
|
15
web/app/vendor/composer/autoload_real.php
vendored
15
web/app/vendor/composer/autoload_real.php
vendored
@ -22,13 +22,15 @@ class ComposerAutoloaderInit0d7c2cd5c2dbf2120e4372996869e900
|
||||
return self::$loader;
|
||||
}
|
||||
|
||||
require __DIR__ . '/platform_check.php';
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInit0d7c2cd5c2dbf2120e4372996869e900', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInit0d7c2cd5c2dbf2120e4372996869e900', 'loadClassLoader'));
|
||||
|
||||
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
||||
if ($useStaticLoader) {
|
||||
require_once __DIR__ . '/autoload_static.php';
|
||||
require __DIR__ . '/autoload_static.php';
|
||||
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInit0d7c2cd5c2dbf2120e4372996869e900::getInitializer($loader));
|
||||
} else {
|
||||
@ -63,11 +65,16 @@ class ComposerAutoloaderInit0d7c2cd5c2dbf2120e4372996869e900
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fileIdentifier
|
||||
* @param string $file
|
||||
* @return void
|
||||
*/
|
||||
function composerRequire0d7c2cd5c2dbf2120e4372996869e900($fileIdentifier, $file)
|
||||
{
|
||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||
require $file;
|
||||
|
||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||
|
||||
require $file;
|
||||
}
|
||||
}
|
||||
|
16
web/app/vendor/composer/autoload_static.php
vendored
16
web/app/vendor/composer/autoload_static.php
vendored
@ -7,12 +7,16 @@ 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',
|
||||
'16eed290c5592c18dc3f16802ad3d0e4' => __DIR__ . '/..' . '/ivopetkov/html5-dom-document-php/autoload.php',
|
||||
);
|
||||
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
'S' =>
|
||||
array (
|
||||
'Symfony\\Polyfill\\Php80\\' => 23,
|
||||
'Symfony\\Component\\Finder\\' => 25,
|
||||
),
|
||||
'P' =>
|
||||
@ -26,6 +30,10 @@ class ComposerStaticInit0d7c2cd5c2dbf2120e4372996869e900
|
||||
);
|
||||
|
||||
public static $prefixDirsPsr4 = array (
|
||||
'Symfony\\Polyfill\\Php80\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-php80',
|
||||
),
|
||||
'Symfony\\Component\\Finder\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/finder',
|
||||
@ -58,7 +66,15 @@ class ComposerStaticInit0d7c2cd5c2dbf2120e4372996869e900
|
||||
);
|
||||
|
||||
public static $classMap = array (
|
||||
'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
|
||||
'CaseInsensitiveArray' => __DIR__ . '/..' . '/php-curl-class/php-curl-class/src/Curl.class.php',
|
||||
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
||||
'Curl' => __DIR__ . '/..' . '/php-curl-class/php-curl-class/src/Curl.class.php',
|
||||
'ParsedownMath' => __DIR__ . '/..' . '/parsedown-math/ParsedownMath.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',
|
||||
);
|
||||
|
||||
public static function getInitializer(ClassLoader $loader)
|
||||
|
271
web/app/vendor/composer/installed.json
vendored
271
web/app/vendor/composer/installed.json
vendored
@ -1,4 +1,5 @@
|
||||
[
|
||||
{
|
||||
"packages": [
|
||||
{
|
||||
"name": "erusev/parsedown",
|
||||
"version": "1.7.4",
|
||||
@ -45,7 +46,8 @@
|
||||
"keywords": [
|
||||
"markdown",
|
||||
"parser"
|
||||
]
|
||||
],
|
||||
"install-path": "../erusev/parsedown"
|
||||
},
|
||||
{
|
||||
"name": "ezyang/htmlpurifier",
|
||||
@ -104,7 +106,8 @@
|
||||
"homepage": "http://htmlpurifier.org/",
|
||||
"keywords": [
|
||||
"html"
|
||||
]
|
||||
],
|
||||
"install-path": "../ezyang/htmlpurifier"
|
||||
},
|
||||
{
|
||||
"name": "gregwar/captcha",
|
||||
@ -159,8 +162,88 @@
|
||||
"bot",
|
||||
"captcha",
|
||||
"spam"
|
||||
],
|
||||
"install-path": "../gregwar/captcha"
|
||||
},
|
||||
{
|
||||
"name": "ivopetkov/html5-dom-document-php",
|
||||
"version": "v2.4.0",
|
||||
"version_normalized": "2.4.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ivopetkov/html5-dom-document-php.git",
|
||||
"reference": "32c5ba748d661a9654c190bf70ce2854eaf5ad22"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ivopetkov/html5-dom-document-php/zipball/32c5ba748d661a9654c190bf70ce2854eaf5ad22",
|
||||
"reference": "32c5ba748d661a9654c190bf70ce2854eaf5ad22",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"php": "7.0.*|7.1.*|7.2.*|7.3.*|7.4.*|8.0.*|8.1.*|8.2.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"ivopetkov/docs-generator": "1.*"
|
||||
},
|
||||
"time": "2022-12-17T00:20:55+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"autoload.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ivo Petkov",
|
||||
"email": "ivo@ivopetkov.com",
|
||||
"homepage": "http://ivopetkov.com"
|
||||
}
|
||||
],
|
||||
"description": "HTML5 DOMDocument PHP library (extends DOMDocument)",
|
||||
"support": {
|
||||
"issues": "https://github.com/ivopetkov/html5-dom-document-php/issues",
|
||||
"source": "https://github.com/ivopetkov/html5-dom-document-php/tree/v2.4.0"
|
||||
},
|
||||
"install-path": "../ivopetkov/html5-dom-document-php"
|
||||
},
|
||||
{
|
||||
"name": "php-curl-class/php-curl-class",
|
||||
"version": "2.0.0",
|
||||
"version_normalized": "2.0.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-curl-class/php-curl-class.git",
|
||||
"reference": "24a93bdc51058ad50d219842b63f7f2e0cb350ac"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-curl-class/php-curl-class/zipball/24a93bdc51058ad50d219842b63f7f2e0cb350ac",
|
||||
"reference": "24a93bdc51058ad50d219842b63f7f2e0cb350ac",
|
||||
"shasum": ""
|
||||
},
|
||||
"time": "2014-04-12T09:46:33+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"src/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"description": "PHP Curl Class is an object-oriented wrapper of the PHP cURL extension.",
|
||||
"support": {
|
||||
"issues": "https://github.com/php-curl-class/php-curl-class/issues",
|
||||
"source": "https://github.com/php-curl-class/php-curl-class/tree/2.0.0"
|
||||
},
|
||||
"install-path": "../php-curl-class/php-curl-class"
|
||||
},
|
||||
{
|
||||
"name": "phpmailer/phpmailer",
|
||||
"version": "v6.6.5",
|
||||
@ -235,30 +318,97 @@
|
||||
"url": "https://github.com/Synchro",
|
||||
"type": "github"
|
||||
}
|
||||
]
|
||||
],
|
||||
"install-path": "../phpmailer/phpmailer"
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
"version": "v6.1.3",
|
||||
"version_normalized": "6.1.3.0",
|
||||
"name": "symfony/deprecation-contracts",
|
||||
"version": "v2.5.2",
|
||||
"version_normalized": "2.5.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/finder.git",
|
||||
"reference": "39696bff2c2970b3779a5cac7bf9f0b88fc2b709"
|
||||
"url": "https://github.com/symfony/deprecation-contracts.git",
|
||||
"reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/39696bff2c2970b3779a5cac7bf9f0b88fc2b709",
|
||||
"reference": "39696bff2c2970b3779a5cac7bf9f0b88fc2b709",
|
||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
|
||||
"reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.1"
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/filesystem": "^6.0"
|
||||
"time": "2022-01-02T09:53:40+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "2.5-dev"
|
||||
},
|
||||
"time": "2022-07-29T07:42:06+00:00",
|
||||
"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"
|
||||
}
|
||||
],
|
||||
"install-path": "../symfony/deprecation-contracts"
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
"version": "v5.4.11",
|
||||
"version_normalized": "5.4.11.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/finder.git",
|
||||
"reference": "7872a66f57caffa2916a584db1aa7f12adc76f8c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/7872a66f57caffa2916a584db1aa7f12adc76f8c",
|
||||
"reference": "7872a66f57caffa2916a584db1aa7f12adc76f8c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"symfony/deprecation-contracts": "^2.1|^3",
|
||||
"symfony/polyfill-php80": "^1.16"
|
||||
},
|
||||
"time": "2022-07-29T07:37:50+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
@ -298,6 +448,93 @@
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
]
|
||||
],
|
||||
"install-path": "../symfony/finder"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
}
|
||||
],
|
||||
"install-path": "../symfony/polyfill-php80"
|
||||
}
|
||||
],
|
||||
"dev": true,
|
||||
"dev-package-names": []
|
||||
}
|
||||
|
104
web/app/vendor/composer/installed.php
vendored
Normal file
104
web/app/vendor/composer/installed.php
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
<?php return array(
|
||||
'root' => array(
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'reference' => 'd6997b84758fbe52d9d90a2d5fe2f2e06806b176',
|
||||
'name' => '__root__',
|
||||
'dev' => true,
|
||||
),
|
||||
'versions' => array(
|
||||
'__root__' => array(
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'reference' => 'd6997b84758fbe52d9d90a2d5fe2f2e06806b176',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'erusev/parsedown' => array(
|
||||
'pretty_version' => '1.7.4',
|
||||
'version' => '1.7.4.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../erusev/parsedown',
|
||||
'aliases' => array(),
|
||||
'reference' => 'cb17b6477dfff935958ba01325f2e8a2bfa6dab3',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'ezyang/htmlpurifier' => array(
|
||||
'pretty_version' => 'v4.16.0',
|
||||
'version' => '4.16.0.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../ezyang/htmlpurifier',
|
||||
'aliases' => array(),
|
||||
'reference' => '523407fb06eb9e5f3d59889b3978d5bfe94299c8',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'gregwar/captcha' => array(
|
||||
'pretty_version' => 'v1.1.9',
|
||||
'version' => '1.1.9.0',
|
||||
'type' => 'captcha',
|
||||
'install_path' => __DIR__ . '/../gregwar/captcha',
|
||||
'aliases' => array(),
|
||||
'reference' => '4bb668e6b40e3205a020ca5ee4ca8cff8b8780c5',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'ivopetkov/html5-dom-document-php' => array(
|
||||
'pretty_version' => 'v2.4.0',
|
||||
'version' => '2.4.0.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../ivopetkov/html5-dom-document-php',
|
||||
'aliases' => array(),
|
||||
'reference' => '32c5ba748d661a9654c190bf70ce2854eaf5ad22',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'php-curl-class/php-curl-class' => array(
|
||||
'pretty_version' => '2.0.0',
|
||||
'version' => '2.0.0.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../php-curl-class/php-curl-class',
|
||||
'aliases' => array(),
|
||||
'reference' => '24a93bdc51058ad50d219842b63f7f2e0cb350ac',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'phpmailer/phpmailer' => array(
|
||||
'pretty_version' => 'v6.6.5',
|
||||
'version' => '6.6.5.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phpmailer/phpmailer',
|
||||
'aliases' => array(),
|
||||
'reference' => '8b6386d7417526d1ea4da9edb70b8352f7543627',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/deprecation-contracts' => array(
|
||||
'pretty_version' => 'v2.5.2',
|
||||
'version' => '2.5.2.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
|
||||
'aliases' => array(),
|
||||
'reference' => 'e8b495ea28c1d97b5e0c121748d6f9b53d075c66',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/finder' => array(
|
||||
'pretty_version' => 'v5.4.11',
|
||||
'version' => '5.4.11.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/finder',
|
||||
'aliases' => array(),
|
||||
'reference' => '7872a66f57caffa2916a584db1aa7f12adc76f8c',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-php80' => array(
|
||||
'pretty_version' => 'v1.26.0',
|
||||
'version' => '1.26.0.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
|
||||
'aliases' => array(),
|
||||
'reference' => 'cfa0ae98841b9e461207c13ab093d76b0fa7bace',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
),
|
||||
);
|
26
web/app/vendor/composer/platform_check.php
vendored
Normal file
26
web/app/vendor/composer/platform_check.php
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
// platform_check.php @generated by Composer
|
||||
|
||||
$issues = array();
|
||||
|
||||
if (!(PHP_VERSION_ID >= 70205)) {
|
||||
$issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.5". You are running ' . PHP_VERSION . '.';
|
||||
}
|
||||
|
||||
if ($issues) {
|
||||
if (!headers_sent()) {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
}
|
||||
if (!ini_get('display_errors')) {
|
||||
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
|
||||
} elseif (!headers_sent()) {
|
||||
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
'Composer detected issues in your platform: ' . implode(' ', $issues),
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
21
web/app/vendor/ivopetkov/html5-dom-document-php/LICENSE
vendored
Normal file
21
web/app/vendor/ivopetkov/html5-dom-document-php/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) Ivo Petkov
|
||||
|
||||
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.
|
22
web/app/vendor/ivopetkov/html5-dom-document-php/autoload.php
vendored
Normal file
22
web/app/vendor/ivopetkov/html5-dom-document-php/autoload.php
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* HTML5 DOMDocument PHP library (extends DOMDocument)
|
||||
* https://github.com/ivopetkov/html5-dom-document-php
|
||||
* Copyright (c) Ivo Petkov
|
||||
* Free to use under the MIT license.
|
||||
*/
|
||||
|
||||
$classes = array(
|
||||
'IvoPetkov\HTML5DOMDocument' => __DIR__ . '/src/HTML5DOMDocument.php',
|
||||
'IvoPetkov\HTML5DOMDocument\Internal\QuerySelectors' => __DIR__ . '/src/HTML5DOMDocument/Internal/QuerySelectors.php',
|
||||
'IvoPetkov\HTML5DOMElement' => __DIR__ . '/src/HTML5DOMElement.php',
|
||||
'IvoPetkov\HTML5DOMNodeList' => __DIR__ . '/src/HTML5DOMNodeList.php',
|
||||
'IvoPetkov\HTML5DOMTokenList' => __DIR__ . '/src/HTML5DOMTokenList.php'
|
||||
);
|
||||
|
||||
spl_autoload_register(function ($class) use ($classes) {
|
||||
if (isset($classes[$class])) {
|
||||
require $classes[$class];
|
||||
}
|
||||
});
|
24
web/app/vendor/ivopetkov/html5-dom-document-php/composer.json
vendored
Normal file
24
web/app/vendor/ivopetkov/html5-dom-document-php/composer.json
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "ivopetkov/html5-dom-document-php",
|
||||
"description": "HTML5 DOMDocument PHP library (extends DOMDocument)",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ivo Petkov",
|
||||
"email": "ivo@ivopetkov.com",
|
||||
"homepage": "http://ivopetkov.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "7.0.*|7.1.*|7.2.*|7.3.*|7.4.*|8.0.*|8.1.*|8.2.*",
|
||||
"ext-dom": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"ivopetkov/docs-generator": "1.*"
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"autoload.php"
|
||||
]
|
||||
}
|
||||
}
|
747
web/app/vendor/ivopetkov/html5-dom-document-php/src/HTML5DOMDocument.php
vendored
Normal file
747
web/app/vendor/ivopetkov/html5-dom-document-php/src/HTML5DOMDocument.php
vendored
Normal file
@ -0,0 +1,747 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* HTML5 DOMDocument PHP library (extends DOMDocument)
|
||||
* https://github.com/ivopetkov/html5-dom-document-php
|
||||
* Copyright (c) Ivo Petkov
|
||||
* Free to use under the MIT license.
|
||||
*/
|
||||
|
||||
namespace IvoPetkov;
|
||||
|
||||
use IvoPetkov\HTML5DOMDocument\Internal\QuerySelectors;
|
||||
|
||||
/**
|
||||
* Represents a live (can be manipulated) representation of a HTML5 document.
|
||||
*
|
||||
* @method \IvoPetkov\HTML5DOMElement|false createElement(string $localName, string $value = '') Create new element node.
|
||||
* @method \IvoPetkov\HTML5DOMElement|false createElementNS(?string $namespace, string $qualifiedName, string $value = '') Create new element node with an associated namespace.
|
||||
* @method ?\IvoPetkov\HTML5DOMElement getElementById(string $elementId) Searches for an element with a certain id.
|
||||
*/
|
||||
class HTML5DOMDocument extends \DOMDocument
|
||||
{
|
||||
|
||||
use QuerySelectors;
|
||||
|
||||
/**
|
||||
* An option passed to loadHTML() and loadHTMLFile() to disable duplicate element IDs exception.
|
||||
*/
|
||||
const ALLOW_DUPLICATE_IDS = 67108864;
|
||||
|
||||
/**
|
||||
* A modification (passed to modify()) that removes all but the last title elements.
|
||||
*/
|
||||
const FIX_MULTIPLE_TITLES = 2;
|
||||
|
||||
/**
|
||||
* A modification (passed to modify()) that removes all but the last metatags with matching name or property attributes.
|
||||
*/
|
||||
const FIX_DUPLICATE_METATAGS = 4;
|
||||
|
||||
/**
|
||||
* A modification (passed to modify()) that merges multiple head elements.
|
||||
*/
|
||||
const FIX_MULTIPLE_HEADS = 8;
|
||||
|
||||
/**
|
||||
* A modification (passed to modify()) that merges multiple body elements.
|
||||
*/
|
||||
const FIX_MULTIPLE_BODIES = 16;
|
||||
|
||||
/**
|
||||
* A modification (passed to modify()) that moves charset metatag and title elements first.
|
||||
*/
|
||||
const OPTIMIZE_HEAD = 32;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static private $newObjectsCache = [];
|
||||
|
||||
/**
|
||||
* Indicates whether an HTML code is loaded.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $loaded = false;
|
||||
|
||||
/**
|
||||
* Creates a new HTML5DOMDocument object.
|
||||
*
|
||||
* @param string $version The version number of the document as part of the XML declaration.
|
||||
* @param string $encoding The encoding of the document as part of the XML declaration.
|
||||
*/
|
||||
public function __construct(string $version = '1.0', string $encoding = '')
|
||||
{
|
||||
parent::__construct($version, $encoding);
|
||||
$this->registerNodeClass('DOMElement', '\IvoPetkov\HTML5DOMElement');
|
||||
}
|
||||
|
||||
/**
|
||||
* Load HTML from a string.
|
||||
*
|
||||
* @param string $source The HTML code.
|
||||
* @param int $options Additional Libxml parameters.
|
||||
* @return boolean TRUE on success or FALSE on failure.
|
||||
*/
|
||||
public function loadHTML($source, $options = 0)
|
||||
{
|
||||
// Enables libxml errors handling
|
||||
$internalErrorsOptionValue = libxml_use_internal_errors();
|
||||
if ($internalErrorsOptionValue === false) {
|
||||
libxml_use_internal_errors(true);
|
||||
}
|
||||
|
||||
$source = trim($source);
|
||||
|
||||
// Add CDATA around script tags content
|
||||
$matches = null;
|
||||
preg_match_all('/<script(.*?)>/', $source, $matches);
|
||||
if (isset($matches[0])) {
|
||||
$matches[0] = array_unique($matches[0]);
|
||||
foreach ($matches[0] as $match) {
|
||||
if (substr($match, -2, 1) !== '/') { // check if ends with />
|
||||
$source = str_replace($match, $match . '<![CDATA[-html5-dom-document-internal-cdata', $source); // Add CDATA after the open tag
|
||||
}
|
||||
}
|
||||
}
|
||||
$source = str_replace('</script>', '-html5-dom-document-internal-cdata]]></script>', $source); // Add CDATA before the end tag
|
||||
$source = str_replace('<![CDATA[-html5-dom-document-internal-cdata-html5-dom-document-internal-cdata]]>', '', $source); // Clean empty script tags
|
||||
$matches = null;
|
||||
preg_match_all('/\<!\[CDATA\[-html5-dom-document-internal-cdata.*?-html5-dom-document-internal-cdata\]\]>/s', $source, $matches);
|
||||
if (isset($matches[0])) {
|
||||
$matches[0] = array_unique($matches[0]);
|
||||
foreach ($matches[0] as $match) {
|
||||
if (strpos($match, '</') !== false) { // check if contains </
|
||||
$source = str_replace($match, str_replace('</', '<-html5-dom-document-internal-cdata-endtagfix/', $match), $source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$autoAddHtmlAndBodyTags = !defined('LIBXML_HTML_NOIMPLIED') || ($options & LIBXML_HTML_NOIMPLIED) === 0;
|
||||
$autoAddDoctype = !defined('LIBXML_HTML_NODEFDTD') || ($options & LIBXML_HTML_NODEFDTD) === 0;
|
||||
|
||||
$allowDuplicateIDs = ($options & self::ALLOW_DUPLICATE_IDS) !== 0;
|
||||
|
||||
// Add body tag if missing
|
||||
if ($autoAddHtmlAndBodyTags && $source !== '' && preg_match('/\<!DOCTYPE.*?\>/', $source) === 0 && preg_match('/\<html.*?\>/', $source) === 0 && preg_match('/\<body.*?\>/', $source) === 0 && preg_match('/\<head.*?\>/', $source) === 0) {
|
||||
$source = '<body>' . $source . '</body>';
|
||||
}
|
||||
|
||||
// Add DOCTYPE if missing
|
||||
if ($autoAddDoctype && strtoupper(substr($source, 0, 9)) !== '<!DOCTYPE') {
|
||||
$source = "<!DOCTYPE html>\n" . $source;
|
||||
}
|
||||
|
||||
// Adds temporary head tag
|
||||
$charsetTag = '<meta data-html5-dom-document-internal-attribute="charset-meta" http-equiv="content-type" content="text/html; charset=utf-8" />';
|
||||
$matches = [];
|
||||
preg_match('/\<head.*?\>/', $source, $matches);
|
||||
$removeHeadTag = false;
|
||||
$removeHtmlTag = false;
|
||||
if (isset($matches[0])) { // has head tag
|
||||
$insertPosition = strpos($source, $matches[0]) + strlen($matches[0]);
|
||||
$source = substr($source, 0, $insertPosition) . $charsetTag . substr($source, $insertPosition);
|
||||
} else {
|
||||
$matches = [];
|
||||
preg_match('/\<html.*?\>/', $source, $matches);
|
||||
if (isset($matches[0])) { // has html tag
|
||||
$source = str_replace($matches[0], $matches[0] . '<head>' . $charsetTag . '</head>', $source);
|
||||
} else {
|
||||
$source = '<head>' . $charsetTag . '</head>' . $source;
|
||||
$removeHtmlTag = true;
|
||||
}
|
||||
$removeHeadTag = true;
|
||||
}
|
||||
|
||||
// Preserve html entities
|
||||
$source = preg_replace('/&([a-zA-Z]*);/', 'html5-dom-document-internal-entity1-$1-end', $source);
|
||||
$source = preg_replace('/&#([0-9]*);/', 'html5-dom-document-internal-entity2-$1-end', $source);
|
||||
|
||||
$result = parent::loadHTML('<?xml encoding="utf-8" ?>' . $source, $options);
|
||||
if ($internalErrorsOptionValue === false) {
|
||||
libxml_use_internal_errors(false);
|
||||
}
|
||||
if ($result === false) {
|
||||
return false;
|
||||
}
|
||||
$this->encoding = 'utf-8';
|
||||
foreach ($this->childNodes as $item) {
|
||||
if ($item->nodeType === XML_PI_NODE) {
|
||||
$this->removeChild($item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/** @var HTML5DOMElement|null */
|
||||
$metaTagElement = $this->getElementsByTagName('meta')->item(0);
|
||||
if ($metaTagElement !== null) {
|
||||
if ($metaTagElement->getAttribute('data-html5-dom-document-internal-attribute') === 'charset-meta') {
|
||||
$headElement = $metaTagElement->parentNode;
|
||||
$htmlElement = $headElement->parentNode;
|
||||
$metaTagElement->parentNode->removeChild($metaTagElement);
|
||||
if ($removeHeadTag && $headElement !== null && $headElement->parentNode !== null && ($headElement->firstChild === null || ($headElement->childNodes->length === 1 && $headElement->firstChild instanceof \DOMText))) {
|
||||
$headElement->parentNode->removeChild($headElement);
|
||||
}
|
||||
if ($removeHtmlTag && $htmlElement !== null && $htmlElement->parentNode !== null && $htmlElement->firstChild === null) {
|
||||
$htmlElement->parentNode->removeChild($htmlElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$allowDuplicateIDs) {
|
||||
$matches = [];
|
||||
preg_match_all('/\sid[\s]*=[\s]*(["\'])(.*?)\1/', $source, $matches);
|
||||
if (!empty($matches[2]) && max(array_count_values($matches[2])) > 1) {
|
||||
$elementIDs = [];
|
||||
$walkChildren = function ($element) use (&$walkChildren, &$elementIDs) {
|
||||
foreach ($element->childNodes as $child) {
|
||||
if ($child instanceof \DOMElement) {
|
||||
if ($child->attributes->length > 0) { // Performance optimization
|
||||
$id = $child->getAttribute('id');
|
||||
if ($id !== '') {
|
||||
if (isset($elementIDs[$id])) {
|
||||
throw new \Exception('A DOM node with an ID value "' . $id . '" already exists! Pass the HTML5DOMDocument::ALLOW_DUPLICATE_IDS option to disable this check.');
|
||||
} else {
|
||||
$elementIDs[$id] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
$walkChildren($child);
|
||||
}
|
||||
}
|
||||
};
|
||||
$walkChildren($this);
|
||||
}
|
||||
}
|
||||
|
||||
$this->loaded = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load HTML from a file.
|
||||
*
|
||||
* @param string $filename The path to the HTML file.
|
||||
* @param int $options Additional Libxml parameters.
|
||||
*/
|
||||
public function loadHTMLFile($filename, $options = 0)
|
||||
{
|
||||
return $this->loadHTML(file_get_contents($filename), $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the HTML tag to the document if missing.
|
||||
*
|
||||
* @return boolean TRUE on success, FALSE otherwise.
|
||||
*/
|
||||
private function addHtmlElementIfMissing(): bool
|
||||
{
|
||||
if ($this->getElementsByTagName('html')->length === 0) {
|
||||
if (!isset(self::$newObjectsCache['htmlelement'])) {
|
||||
self::$newObjectsCache['htmlelement'] = new \DOMElement('html');
|
||||
}
|
||||
$this->appendChild(clone (self::$newObjectsCache['htmlelement']));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the HEAD tag to the document if missing.
|
||||
*
|
||||
* @return boolean TRUE on success, FALSE otherwise.
|
||||
*/
|
||||
private function addHeadElementIfMissing(): bool
|
||||
{
|
||||
if ($this->getElementsByTagName('head')->length === 0) {
|
||||
$htmlElement = $this->getElementsByTagName('html')->item(0);
|
||||
if (!isset(self::$newObjectsCache['headelement'])) {
|
||||
self::$newObjectsCache['headelement'] = new \DOMElement('head');
|
||||
}
|
||||
$headElement = clone (self::$newObjectsCache['headelement']);
|
||||
if ($htmlElement->firstChild === null) {
|
||||
$htmlElement->appendChild($headElement);
|
||||
} else {
|
||||
$htmlElement->insertBefore($headElement, $htmlElement->firstChild);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the BODY tag to the document if missing.
|
||||
*
|
||||
* @return boolean TRUE on success, FALSE otherwise.
|
||||
*/
|
||||
private function addBodyElementIfMissing(): bool
|
||||
{
|
||||
if ($this->getElementsByTagName('body')->length === 0) {
|
||||
if (!isset(self::$newObjectsCache['bodyelement'])) {
|
||||
self::$newObjectsCache['bodyelement'] = new \DOMElement('body');
|
||||
}
|
||||
$this->getElementsByTagName('html')->item(0)->appendChild(clone (self::$newObjectsCache['bodyelement']));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps the internal document into a string using HTML formatting.
|
||||
*
|
||||
* @param \DOMNode $node Optional parameter to output a subset of the document.
|
||||
* @return string The document (or node) HTML code as string.
|
||||
*/
|
||||
public function saveHTML(\DOMNode $node = null): string
|
||||
{
|
||||
$nodeMode = $node !== null;
|
||||
if ($nodeMode && $node instanceof \DOMDocument) {
|
||||
$nodeMode = false;
|
||||
}
|
||||
|
||||
if ($nodeMode) {
|
||||
if (!isset(self::$newObjectsCache['html5domdocument'])) {
|
||||
self::$newObjectsCache['html5domdocument'] = new HTML5DOMDocument();
|
||||
}
|
||||
$tempDomDocument = clone (self::$newObjectsCache['html5domdocument']);
|
||||
if ($node->nodeName === 'html') {
|
||||
$tempDomDocument->loadHTML('<!DOCTYPE html>');
|
||||
$tempDomDocument->appendChild($tempDomDocument->importNode(clone ($node), true));
|
||||
$html = $tempDomDocument->saveHTML();
|
||||
$html = substr($html, 16); // remove the DOCTYPE + the new line after
|
||||
} elseif ($node->nodeName === 'head' || $node->nodeName === 'body') {
|
||||
$tempDomDocument->loadHTML("<!DOCTYPE html>\n<html></html>");
|
||||
$tempDomDocument->childNodes[1]->appendChild($tempDomDocument->importNode(clone ($node), true));
|
||||
$html = $tempDomDocument->saveHTML();
|
||||
$html = substr($html, 22, -7); // remove the DOCTYPE + the new line after + html tag
|
||||
} else {
|
||||
$isInHead = false;
|
||||
$parentNode = $node;
|
||||
for ($i = 0; $i < 1000; $i++) {
|
||||
$parentNode = $parentNode->parentNode;
|
||||
if ($parentNode === null) {
|
||||
break;
|
||||
}
|
||||
if ($parentNode->nodeName === 'body') {
|
||||
break;
|
||||
} elseif ($parentNode->nodeName === 'head') {
|
||||
$isInHead = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$tempDomDocument->loadHTML("<!DOCTYPE html>\n<html>" . ($isInHead ? '<head></head>' : '<body></body>') . '</html>');
|
||||
$tempDomDocument->childNodes[1]->childNodes[0]->appendChild($tempDomDocument->importNode(clone ($node), true));
|
||||
$html = $tempDomDocument->saveHTML();
|
||||
$html = substr($html, 28, -14); // remove the DOCTYPE + the new line + html + body or head tags
|
||||
}
|
||||
$html = trim($html);
|
||||
} else {
|
||||
$removeHtmlElement = false;
|
||||
$removeHeadElement = false;
|
||||
$headElement = $this->getElementsByTagName('head')->item(0);
|
||||
if ($headElement === null) {
|
||||
if ($this->addHtmlElementIfMissing()) {
|
||||
$removeHtmlElement = true;
|
||||
}
|
||||
if ($this->addHeadElementIfMissing()) {
|
||||
$removeHeadElement = true;
|
||||
}
|
||||
$headElement = $this->getElementsByTagName('head')->item(0);
|
||||
}
|
||||
$meta = $this->createElement('meta');
|
||||
$meta->setAttribute('data-html5-dom-document-internal-attribute', 'charset-meta');
|
||||
$meta->setAttribute('http-equiv', 'content-type');
|
||||
$meta->setAttribute('content', 'text/html; charset=utf-8');
|
||||
if ($headElement->firstChild !== null) {
|
||||
$headElement->insertBefore($meta, $headElement->firstChild);
|
||||
} else {
|
||||
$headElement->appendChild($meta);
|
||||
}
|
||||
$html = parent::saveHTML();
|
||||
$html = rtrim($html, "\n");
|
||||
|
||||
if ($removeHeadElement) {
|
||||
$headElement->parentNode->removeChild($headElement);
|
||||
} else {
|
||||
$meta->parentNode->removeChild($meta);
|
||||
}
|
||||
|
||||
if (strpos($html, 'html5-dom-document-internal-entity') !== false) {
|
||||
$html = preg_replace('/html5-dom-document-internal-entity1-(.*?)-end/', '&$1;', $html);
|
||||
$html = preg_replace('/html5-dom-document-internal-entity2-(.*?)-end/', '&#$1;', $html);
|
||||
}
|
||||
|
||||
$codeToRemove = [
|
||||
'html5-dom-document-internal-content',
|
||||
'<meta data-html5-dom-document-internal-attribute="charset-meta" http-equiv="content-type" content="text/html; charset=utf-8">',
|
||||
'</area>', '</base>', '</br>', '</col>', '</command>', '</embed>', '</hr>', '</img>', '</input>', '</keygen>', '</link>', '</meta>', '</param>', '</source>', '</track>', '</wbr>',
|
||||
'<![CDATA[-html5-dom-document-internal-cdata', '-html5-dom-document-internal-cdata]]>', '-html5-dom-document-internal-cdata-endtagfix'
|
||||
];
|
||||
if ($removeHeadElement) {
|
||||
$codeToRemove[] = '<head></head>';
|
||||
}
|
||||
if ($removeHtmlElement) {
|
||||
$codeToRemove[] = '<html></html>';
|
||||
}
|
||||
|
||||
$html = str_replace($codeToRemove, '', $html);
|
||||
}
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps the internal document into a file using HTML formatting.
|
||||
*
|
||||
* @param string $filename The path to the saved HTML document.
|
||||
* @return int|false the number of bytes written or FALSE if an error occurred.
|
||||
*/
|
||||
#[\ReturnTypeWillChange] // Return type "int|false" is invalid in older supported versions.
|
||||
public function saveHTMLFile($filename)
|
||||
{
|
||||
if (!is_writable($filename)) {
|
||||
return false;
|
||||
}
|
||||
$result = $this->saveHTML();
|
||||
file_put_contents($filename, $result);
|
||||
$bytesWritten = filesize($filename);
|
||||
if ($bytesWritten === strlen($result)) {
|
||||
return $bytesWritten;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first document element matching the selector.
|
||||
*
|
||||
* @param string $selector A CSS query selector. Available values: *, tagname, tagname#id, #id, tagname.classname, .classname, tagname.classname.classname2, .classname.classname2, tagname[attribute-selector], [attribute-selector], "div, p", div p, div > p, div + p and p ~ ul.
|
||||
* @return HTML5DOMElement|null The result DOMElement or null if not found.
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function querySelector(string $selector)
|
||||
{
|
||||
return $this->internalQuerySelector($selector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of document elements matching the selector.
|
||||
*
|
||||
* @param string $selector A CSS query selector. Available values: *, tagname, tagname#id, #id, tagname.classname, .classname, tagname.classname.classname2, .classname.classname2, tagname[attribute-selector], [attribute-selector], "div, p", div p, div > p, div + p and p ~ ul.
|
||||
* @return HTML5DOMNodeList Returns a list of DOMElements matching the criteria.
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function querySelectorAll(string $selector)
|
||||
{
|
||||
return $this->internalQuerySelectorAll($selector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an element that will be replaced by the new body in insertHTML.
|
||||
*
|
||||
* @param string $name The name of the insert target.
|
||||
* @return HTML5DOMElement A new DOMElement that must be set in the place where the new body will be inserted.
|
||||
*/
|
||||
public function createInsertTarget(string $name)
|
||||
{
|
||||
if (!$this->loaded) {
|
||||
$this->loadHTML('');
|
||||
}
|
||||
$element = $this->createElement('html5-dom-document-insert-target');
|
||||
$element->setAttribute('name', $name);
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a HTML document into the current document. The elements from the head and the body will be moved to their proper locations.
|
||||
*
|
||||
* @param string $source The HTML code to be inserted.
|
||||
* @param string $target Body target position. Available values: afterBodyBegin, beforeBodyEnd or insertTarget name.
|
||||
*/
|
||||
public function insertHTML(string $source, string $target = 'beforeBodyEnd')
|
||||
{
|
||||
$this->insertHTMLMulti([['source' => $source, 'target' => $target]]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts multiple HTML documents into the current document. The elements from the head and the body will be moved to their proper locations.
|
||||
*
|
||||
* @param array $sources An array containing the source of the document to be inserted in the following format: [ ['source'=>'', 'target'=>''], ['source'=>'', 'target'=>''], ... ]
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function insertHTMLMulti(array $sources)
|
||||
{
|
||||
if (!$this->loaded) {
|
||||
$this->loadHTML('');
|
||||
}
|
||||
|
||||
if (!isset(self::$newObjectsCache['html5domdocument'])) {
|
||||
self::$newObjectsCache['html5domdocument'] = new HTML5DOMDocument();
|
||||
}
|
||||
|
||||
$currentDomDocument = &$this;
|
||||
|
||||
$copyAttributes = function ($sourceNode, $targetNode) {
|
||||
foreach ($sourceNode->attributes as $attributeName => $attribute) {
|
||||
$targetNode->setAttribute($attributeName, $attribute->value);
|
||||
}
|
||||
};
|
||||
|
||||
$currentDomHTMLElement = null;
|
||||
$currentDomHeadElement = null;
|
||||
$currentDomBodyElement = null;
|
||||
|
||||
$insertTargetsList = null;
|
||||
$prepareInsertTargetsList = function () use (&$insertTargetsList) {
|
||||
if ($insertTargetsList === null) {
|
||||
$insertTargetsList = [];
|
||||
$targetElements = $this->getElementsByTagName('html5-dom-document-insert-target');
|
||||
foreach ($targetElements as $targetElement) {
|
||||
$insertTargetsList[$targetElement->getAttribute('name')] = $targetElement;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
foreach ($sources as $sourceData) {
|
||||
if (!isset($sourceData['source'])) {
|
||||
throw new \Exception('Missing source key');
|
||||
}
|
||||
$source = $sourceData['source'];
|
||||
$target = isset($sourceData['target']) ? $sourceData['target'] : 'beforeBodyEnd';
|
||||
|
||||
$domDocument = clone (self::$newObjectsCache['html5domdocument']);
|
||||
$domDocument->loadHTML($source, self::ALLOW_DUPLICATE_IDS);
|
||||
|
||||
$htmlElement = $domDocument->getElementsByTagName('html')->item(0);
|
||||
if ($htmlElement !== null) {
|
||||
if ($htmlElement->attributes->length > 0) {
|
||||
if ($currentDomHTMLElement === null) {
|
||||
$currentDomHTMLElement = $this->getElementsByTagName('html')->item(0);
|
||||
if ($currentDomHTMLElement === null) {
|
||||
$this->addHtmlElementIfMissing();
|
||||
$currentDomHTMLElement = $this->getElementsByTagName('html')->item(0);
|
||||
}
|
||||
}
|
||||
$copyAttributes($htmlElement, $currentDomHTMLElement);
|
||||
}
|
||||
}
|
||||
|
||||
$headElement = $domDocument->getElementsByTagName('head')->item(0);
|
||||
if ($headElement !== null) {
|
||||
if ($currentDomHeadElement === null) {
|
||||
$currentDomHeadElement = $this->getElementsByTagName('head')->item(0);
|
||||
if ($currentDomHeadElement === null) {
|
||||
$this->addHtmlElementIfMissing();
|
||||
$this->addHeadElementIfMissing();
|
||||
$currentDomHeadElement = $this->getElementsByTagName('head')->item(0);
|
||||
}
|
||||
}
|
||||
foreach ($headElement->childNodes as $headElementChild) {
|
||||
$newNode = $currentDomDocument->importNode($headElementChild, true);
|
||||
if ($newNode !== null) {
|
||||
$currentDomHeadElement->appendChild($newNode);
|
||||
}
|
||||
}
|
||||
if ($headElement->attributes->length > 0) {
|
||||
$copyAttributes($headElement, $currentDomHeadElement);
|
||||
}
|
||||
}
|
||||
|
||||
$bodyElement = $domDocument->getElementsByTagName('body')->item(0);
|
||||
if ($bodyElement !== null) {
|
||||
if ($currentDomBodyElement === null) {
|
||||
$currentDomBodyElement = $this->getElementsByTagName('body')->item(0);
|
||||
if ($currentDomBodyElement === null) {
|
||||
$this->addHtmlElementIfMissing();
|
||||
$this->addBodyElementIfMissing();
|
||||
$currentDomBodyElement = $this->getElementsByTagName('body')->item(0);
|
||||
}
|
||||
}
|
||||
$bodyElementChildren = $bodyElement->childNodes;
|
||||
if ($target === 'afterBodyBegin') {
|
||||
$bodyElementChildrenCount = $bodyElementChildren->length;
|
||||
for ($i = $bodyElementChildrenCount - 1; $i >= 0; $i--) {
|
||||
$newNode = $currentDomDocument->importNode($bodyElementChildren->item($i), true);
|
||||
if ($newNode !== null) {
|
||||
if ($currentDomBodyElement->firstChild === null) {
|
||||
$currentDomBodyElement->appendChild($newNode);
|
||||
} else {
|
||||
$currentDomBodyElement->insertBefore($newNode, $currentDomBodyElement->firstChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ($target === 'beforeBodyEnd') {
|
||||
foreach ($bodyElementChildren as $bodyElementChild) {
|
||||
$newNode = $currentDomDocument->importNode($bodyElementChild, true);
|
||||
if ($newNode !== null) {
|
||||
$currentDomBodyElement->appendChild($newNode);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$prepareInsertTargetsList();
|
||||
if (isset($insertTargetsList[$target])) {
|
||||
$targetElement = $insertTargetsList[$target];
|
||||
$targetElementParent = $targetElement->parentNode;
|
||||
foreach ($bodyElementChildren as $bodyElementChild) {
|
||||
$newNode = $currentDomDocument->importNode($bodyElementChild, true);
|
||||
if ($newNode !== null) {
|
||||
$targetElementParent->insertBefore($newNode, $targetElement);
|
||||
}
|
||||
}
|
||||
$targetElementParent->removeChild($targetElement);
|
||||
}
|
||||
}
|
||||
if ($bodyElement->attributes->length > 0) {
|
||||
$copyAttributes($bodyElement, $currentDomBodyElement);
|
||||
}
|
||||
} else { // clear the insert target when there is no body element
|
||||
$prepareInsertTargetsList();
|
||||
if (isset($insertTargetsList[$target])) {
|
||||
$targetElement = $insertTargetsList[$target];
|
||||
$targetElement->parentNode->removeChild($targetElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the modifications specified to the DOM document.
|
||||
*
|
||||
* @param int $modifications The modifications to apply. Available values:
|
||||
* - HTML5DOMDocument::FIX_MULTIPLE_TITLES - removes all but the last title elements.
|
||||
* - HTML5DOMDocument::FIX_DUPLICATE_METATAGS - removes all but the last metatags with matching name or property attributes.
|
||||
* - HTML5DOMDocument::FIX_MULTIPLE_HEADS - merges multiple head elements.
|
||||
* - HTML5DOMDocument::FIX_MULTIPLE_BODIES - merges multiple body elements.
|
||||
* - HTML5DOMDocument::OPTIMIZE_HEAD - moves charset metatag and title elements first.
|
||||
*/
|
||||
public function modify($modifications = 0)
|
||||
{
|
||||
|
||||
$fixMultipleTitles = ($modifications & self::FIX_MULTIPLE_TITLES) !== 0;
|
||||
$fixDuplicateMetatags = ($modifications & self::FIX_DUPLICATE_METATAGS) !== 0;
|
||||
$fixMultipleHeads = ($modifications & self::FIX_MULTIPLE_HEADS) !== 0;
|
||||
$fixMultipleBodies = ($modifications & self::FIX_MULTIPLE_BODIES) !== 0;
|
||||
$optimizeHead = ($modifications & self::OPTIMIZE_HEAD) !== 0;
|
||||
|
||||
/** @var \DOMNodeList<HTML5DOMElement> */
|
||||
$headElements = $this->getElementsByTagName('head');
|
||||
|
||||
if ($fixMultipleHeads) { // Merges multiple head elements.
|
||||
if ($headElements->length > 1) {
|
||||
$firstHeadElement = $headElements->item(0);
|
||||
while ($headElements->length > 1) {
|
||||
$nextHeadElement = $headElements->item(1);
|
||||
$nextHeadElementChildren = $nextHeadElement->childNodes;
|
||||
$nextHeadElementChildrenCount = $nextHeadElementChildren->length;
|
||||
for ($i = 0; $i < $nextHeadElementChildrenCount; $i++) {
|
||||
$firstHeadElement->appendChild($nextHeadElementChildren->item(0));
|
||||
}
|
||||
$nextHeadElement->parentNode->removeChild($nextHeadElement);
|
||||
}
|
||||
$headElements = [$firstHeadElement];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($headElements as $headElement) {
|
||||
|
||||
if ($fixMultipleTitles) { // Remove all title elements except the last one.
|
||||
$titleTags = $headElement->getElementsByTagName('title');
|
||||
$titleTagsCount = $titleTags->length;
|
||||
for ($i = 0; $i < $titleTagsCount - 1; $i++) {
|
||||
$node = $titleTags->item($i);
|
||||
$node->parentNode->removeChild($node);
|
||||
}
|
||||
}
|
||||
|
||||
if ($fixDuplicateMetatags) { // Remove all meta tags that has matching name or property attributes.
|
||||
$metaTags = $headElement->getElementsByTagName('meta');
|
||||
if ($metaTags->length > 0) {
|
||||
$list = [];
|
||||
$idsList = [];
|
||||
foreach ($metaTags as $metaTag) {
|
||||
$id = $metaTag->getAttribute('name');
|
||||
if ($id !== '') {
|
||||
$id = 'name:' . $id;
|
||||
} else {
|
||||
$id = $metaTag->getAttribute('property');
|
||||
if ($id !== '') {
|
||||
$id = 'property:' . $id;
|
||||
} else {
|
||||
$id = $metaTag->getAttribute('charset');
|
||||
if ($id !== '') {
|
||||
$id = 'charset';
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isset($idsList[$id])) {
|
||||
$idsList[$id] = 0;
|
||||
}
|
||||
$idsList[$id]++;
|
||||
$list[] = [$metaTag, $id];
|
||||
}
|
||||
foreach ($idsList as $id => $count) {
|
||||
if ($count > 1 && $id !== '') {
|
||||
foreach ($list as $i => $item) {
|
||||
if ($item[1] === $id) {
|
||||
$node = $item[0];
|
||||
$node->parentNode->removeChild($node);
|
||||
unset($list[$i]);
|
||||
$count--;
|
||||
}
|
||||
if ($count === 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($optimizeHead) { // Moves charset metatag and title elements first.
|
||||
$titleElement = $headElement->getElementsByTagName('title')->item(0);
|
||||
$hasTitleElement = false;
|
||||
if ($titleElement !== null && $titleElement->previousSibling !== null) {
|
||||
$headElement->insertBefore($titleElement, $headElement->firstChild);
|
||||
$hasTitleElement = true;
|
||||
}
|
||||
$metaTags = $headElement->getElementsByTagName('meta');
|
||||
$metaTagsLength = $metaTags->length;
|
||||
if ($metaTagsLength > 0) {
|
||||
$charsetMetaTag = null;
|
||||
$nodesToMove = [];
|
||||
for ($i = $metaTagsLength - 1; $i >= 0; $i--) {
|
||||
$nodesToMove[$i] = $metaTags->item($i);
|
||||
}
|
||||
for ($i = $metaTagsLength - 1; $i >= 0; $i--) {
|
||||
$nodeToMove = $nodesToMove[$i];
|
||||
if ($charsetMetaTag === null && $nodeToMove->getAttribute('charset') !== '') {
|
||||
$charsetMetaTag = $nodeToMove;
|
||||
}
|
||||
$referenceNode = $headElement->childNodes->item($hasTitleElement ? 1 : 0);
|
||||
if ($nodeToMove !== $referenceNode) {
|
||||
$headElement->insertBefore($nodeToMove, $referenceNode);
|
||||
}
|
||||
}
|
||||
if ($charsetMetaTag !== null && $charsetMetaTag->previousSibling !== null) {
|
||||
$headElement->insertBefore($charsetMetaTag, $headElement->firstChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($fixMultipleBodies) { // Merges multiple body elements.
|
||||
$bodyElements = $this->getElementsByTagName('body');
|
||||
if ($bodyElements->length > 1) {
|
||||
$firstBodyElement = $bodyElements->item(0);
|
||||
while ($bodyElements->length > 1) {
|
||||
$nextBodyElement = $bodyElements->item(1);
|
||||
$nextBodyElementChildren = $nextBodyElement->childNodes;
|
||||
$nextBodyElementChildrenCount = $nextBodyElementChildren->length;
|
||||
for ($i = 0; $i < $nextBodyElementChildrenCount; $i++) {
|
||||
$firstBodyElement->appendChild($nextBodyElementChildren->item(0));
|
||||
}
|
||||
$nextBodyElement->parentNode->removeChild($nextBodyElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
514
web/app/vendor/ivopetkov/html5-dom-document-php/src/HTML5DOMDocument/Internal/QuerySelectors.php
vendored
Normal file
514
web/app/vendor/ivopetkov/html5-dom-document-php/src/HTML5DOMDocument/Internal/QuerySelectors.php
vendored
Normal file
@ -0,0 +1,514 @@
|
||||
<?php
|
||||
|
||||
namespace IvoPetkov\HTML5DOMDocument\Internal;
|
||||
|
||||
use IvoPetkov\HTML5DOMElement;
|
||||
|
||||
trait QuerySelectors
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns the first element matching the selector.
|
||||
*
|
||||
* @param string $selector A CSS query selector. Available values: *, tagname, tagname#id, #id, tagname.classname, .classname, tagname[attribute-selector] and [attribute-selector].
|
||||
* @return HTML5DOMElement|null The result DOMElement or null if not found
|
||||
*/
|
||||
private function internalQuerySelector(string $selector)
|
||||
{
|
||||
$result = $this->internalQuerySelectorAll($selector, 1);
|
||||
return $result->item(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of document elements matching the selector.
|
||||
*
|
||||
* @param string $selector A CSS query selector. Available values: *, tagname, tagname#id, #id, tagname.classname, .classname, tagname[attribute-selector] and [attribute-selector].
|
||||
* @param int|null $preferredLimit Preferred maximum number of elements to return.
|
||||
* @return DOMNodeList Returns a list of DOMElements matching the criteria.
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
private function internalQuerySelectorAll(string $selector, $preferredLimit = null)
|
||||
{
|
||||
$selector = trim($selector);
|
||||
|
||||
$cache = [];
|
||||
$walkChildren = function (\DOMNode $context, $tagNames, callable $callback) use (&$cache) {
|
||||
if (!empty($tagNames)) {
|
||||
$children = [];
|
||||
foreach ($tagNames as $tagName) {
|
||||
$elements = $context->getElementsByTagName($tagName);
|
||||
foreach ($elements as $element) {
|
||||
$children[] = $element;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$getChildren = function () use ($context) {
|
||||
$result = [];
|
||||
$process = function (\DOMNode $node) use (&$process, &$result) {
|
||||
foreach ($node->childNodes as $child) {
|
||||
if ($child instanceof \DOMElement) {
|
||||
$result[] = $child;
|
||||
$process($child);
|
||||
}
|
||||
}
|
||||
};
|
||||
$process($context);
|
||||
return $result;
|
||||
};
|
||||
if ($this === $context) {
|
||||
$cacheKey = 'walk_children';
|
||||
if (!isset($cache[$cacheKey])) {
|
||||
$cache[$cacheKey] = $getChildren();
|
||||
}
|
||||
$children = $cache[$cacheKey];
|
||||
} else {
|
||||
$children = $getChildren();
|
||||
}
|
||||
}
|
||||
foreach ($children as $child) {
|
||||
if ($callback($child) === true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$getElementById = function (\DOMNode $context, $id, $tagName) use (&$walkChildren) {
|
||||
if ($context instanceof \DOMDocument) {
|
||||
$element = $context->getElementById($id);
|
||||
if ($element && ($tagName === null || $element->tagName === $tagName)) {
|
||||
return $element;
|
||||
}
|
||||
} else {
|
||||
$foundElement = null;
|
||||
$walkChildren($context, $tagName !== null ? [$tagName] : null, function ($element) use ($id, &$foundElement) {
|
||||
if ($element->attributes->length > 0 && $element->getAttribute('id') === $id) {
|
||||
$foundElement = $element;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return $foundElement;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
$simpleSelectors = [];
|
||||
|
||||
// all
|
||||
$simpleSelectors['\*'] = function (string $mode, array $matches, \DOMNode $context, callable $add = null) use ($walkChildren) {
|
||||
if ($mode === 'validate') {
|
||||
return true;
|
||||
} else {
|
||||
$walkChildren($context, [], function ($element) use ($add) {
|
||||
if ($add($element)) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// tagname
|
||||
$simpleSelectors['[a-zA-Z0-9\-]+'] = function (string $mode, array $matches, \DOMNode $context, callable $add = null) use ($walkChildren) {
|
||||
$tagNames = [];
|
||||
foreach ($matches as $match) {
|
||||
$tagNames[] = strtolower($match[0]);
|
||||
}
|
||||
if ($mode === 'validate') {
|
||||
return array_search($context->tagName, $tagNames) !== false;
|
||||
}
|
||||
$walkChildren($context, $tagNames, function ($element) use ($add) {
|
||||
if ($add($element)) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// tagname[target] or [target] // Available values for targets: attr, attr="value", attr~="value", attr|="value", attr^="value", attr$="value", attr*="value"
|
||||
$simpleSelectors['(?:[a-zA-Z0-9\-]*)(?:\[.+?\])'] = function (string $mode, array $matches, \DOMNode $context, callable $add = null) use ($walkChildren) {
|
||||
$run = function ($match) use ($mode, $context, $add, $walkChildren) {
|
||||
$attributeSelectors = explode('][', substr($match[2], 1, -1));
|
||||
foreach ($attributeSelectors as $i => $attributeSelector) {
|
||||
$attributeSelectorMatches = null;
|
||||
if (preg_match('/^(.+?)(=|~=|\|=|\^=|\$=|\*=)\"(.+?)\"$/', $attributeSelector, $attributeSelectorMatches) === 1) {
|
||||
$attributeSelectors[$i] = [
|
||||
'name' => strtolower($attributeSelectorMatches[1]),
|
||||
'value' => $attributeSelectorMatches[3],
|
||||
'operator' => $attributeSelectorMatches[2]
|
||||
];
|
||||
} else {
|
||||
$attributeSelectors[$i] = [
|
||||
'name' => $attributeSelector
|
||||
];
|
||||
}
|
||||
}
|
||||
$tagName = strlen($match[1]) > 0 ? strtolower($match[1]) : null;
|
||||
$check = function ($element) use ($attributeSelectors) {
|
||||
if ($element->attributes->length > 0) {
|
||||
foreach ($attributeSelectors as $attributeSelector) {
|
||||
$isMatch = false;
|
||||
$attributeValue = $element->getAttribute($attributeSelector['name']);
|
||||
if (isset($attributeSelector['value'])) {
|
||||
$valueToMatch = $attributeSelector['value'];
|
||||
switch ($attributeSelector['operator']) {
|
||||
case '=':
|
||||
if ($attributeValue === $valueToMatch) {
|
||||
$isMatch = true;
|
||||
}
|
||||
break;
|
||||
case '~=':
|
||||
$words = preg_split("/[\s]+/", $attributeValue);
|
||||
if (array_search($valueToMatch, $words) !== false) {
|
||||
$isMatch = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case '|=':
|
||||
if ($attributeValue === $valueToMatch || strpos($attributeValue, $valueToMatch . '-') === 0) {
|
||||
$isMatch = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case '^=':
|
||||
if (strpos($attributeValue, $valueToMatch) === 0) {
|
||||
$isMatch = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case '$=':
|
||||
if (substr($attributeValue, -strlen($valueToMatch)) === $valueToMatch) {
|
||||
$isMatch = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case '*=':
|
||||
if (strpos($attributeValue, $valueToMatch) !== false) {
|
||||
$isMatch = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if ($attributeValue !== '') {
|
||||
$isMatch = true;
|
||||
}
|
||||
}
|
||||
if (!$isMatch) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if ($mode === 'validate') {
|
||||
return ($tagName === null ? true : $context->tagName === $tagName) && $check($context);
|
||||
} else {
|
||||
$walkChildren($context, $tagName !== null ? [$tagName] : null, function ($element) use ($check, $add) {
|
||||
if ($check($element)) {
|
||||
if ($add($element)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
// todo optimize
|
||||
foreach ($matches as $match) {
|
||||
if ($mode === 'validate') {
|
||||
if ($run($match)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
$run($match);
|
||||
}
|
||||
}
|
||||
if ($mode === 'validate') {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// tagname#id or #id
|
||||
$simpleSelectors['(?:[a-zA-Z0-9\-]*)#(?:[a-zA-Z0-9\-\_]+?)'] = function (string $mode, array $matches, \DOMNode $context, callable $add = null) use ($getElementById) {
|
||||
$run = function ($match) use ($mode, $context, $add, $getElementById) {
|
||||
$tagName = strlen($match[1]) > 0 ? strtolower($match[1]) : null;
|
||||
$id = $match[2];
|
||||
if ($mode === 'validate') {
|
||||
return ($tagName === null ? true : $context->tagName === $tagName) && $context->getAttribute('id') === $id;
|
||||
} else {
|
||||
$element = $getElementById($context, $id, $tagName);
|
||||
if ($element) {
|
||||
$add($element);
|
||||
}
|
||||
}
|
||||
};
|
||||
// todo optimize
|
||||
foreach ($matches as $match) {
|
||||
if ($mode === 'validate') {
|
||||
if ($run($match)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
$run($match);
|
||||
}
|
||||
}
|
||||
if ($mode === 'validate') {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// tagname.classname, .classname, tagname.classname.classname2, .classname.classname2
|
||||
$simpleSelectors['(?:[a-zA-Z0-9\-]*)\.(?:[a-zA-Z0-9\-\_\.]+?)'] = function (string $mode, array $matches, \DOMNode $context, callable $add = null) use ($walkChildren) {
|
||||
$rawData = []; // Array containing [tag, classnames]
|
||||
$tagNames = [];
|
||||
foreach ($matches as $match) {
|
||||
$tagName = strlen($match[1]) > 0 ? $match[1] : null;
|
||||
$classes = explode('.', $match[2]);
|
||||
if (empty($classes)) {
|
||||
continue;
|
||||
}
|
||||
$rawData[] = [$tagName, $classes];
|
||||
if ($tagName !== null) {
|
||||
$tagNames[] = $tagName;
|
||||
}
|
||||
}
|
||||
$check = function ($element) use ($rawData) {
|
||||
if ($element->attributes->length > 0) {
|
||||
$classAttribute = ' ' . $element->getAttribute('class') . ' ';
|
||||
$tagName = $element->tagName;
|
||||
foreach ($rawData as $rawMatch) {
|
||||
if ($rawMatch[0] !== null && $tagName !== $rawMatch[0]) {
|
||||
continue;
|
||||
}
|
||||
$allClassesFound = true;
|
||||
foreach ($rawMatch[1] as $class) {
|
||||
if (strpos($classAttribute, ' ' . $class . ' ') === false) {
|
||||
$allClassesFound = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($allClassesFound) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if ($mode === 'validate') {
|
||||
return $check($context);
|
||||
}
|
||||
$walkChildren($context, $tagNames, function ($element) use ($check, $add) {
|
||||
if ($check($element)) {
|
||||
if ($add($element)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$isMatchingElement = function (\DOMNode $context, string $selector) use ($simpleSelectors) {
|
||||
foreach ($simpleSelectors as $simpleSelector => $callback) {
|
||||
$match = null;
|
||||
if (preg_match('/^' . (str_replace('?:', '', $simpleSelector)) . '$/', $selector, $match) === 1) {
|
||||
return call_user_func($callback, 'validate', [$match], $context);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$complexSelectors = [];
|
||||
|
||||
$getMatchingElements = function (\DOMNode $context, string $selector, $preferredLimit = null) use (&$simpleSelectors, &$complexSelectors) {
|
||||
|
||||
$processSelector = function (string $mode, string $selector, $operator = null) use (&$processSelector, $simpleSelectors, $complexSelectors, $context, $preferredLimit) {
|
||||
$supportedSimpleSelectors = array_keys($simpleSelectors);
|
||||
$supportedSimpleSelectorsExpression = '(?:(?:' . implode(')|(?:', $supportedSimpleSelectors) . '))';
|
||||
$supportedSelectors = $supportedSimpleSelectors;
|
||||
$supportedComplexOperators = array_keys($complexSelectors);
|
||||
if ($operator === null) {
|
||||
$operator = ',';
|
||||
foreach ($supportedComplexOperators as $complexOperator) {
|
||||
array_unshift($supportedSelectors, '(?:(?:(?:' . $supportedSimpleSelectorsExpression . '\s*\\' . $complexOperator . '\s*))+' . $supportedSimpleSelectorsExpression . ')');
|
||||
}
|
||||
}
|
||||
$supportedSelectorsExpression = '(?:(?:' . implode(')|(?:', $supportedSelectors) . '))';
|
||||
|
||||
$vallidationExpression = '/^(?:(?:' . $supportedSelectorsExpression . '\s*\\' . $operator . '\s*))*' . $supportedSelectorsExpression . '$/';
|
||||
if (preg_match($vallidationExpression, $selector) !== 1) {
|
||||
return false;
|
||||
}
|
||||
$selector .= $operator; // append the seprator at the back for easier matching below
|
||||
|
||||
$result = [];
|
||||
if ($mode === 'execute') {
|
||||
$add = function ($element) use ($preferredLimit, &$result) {
|
||||
$found = false;
|
||||
foreach ($result as $addedElement) {
|
||||
if ($addedElement === $element) {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$found) {
|
||||
$result[] = $element;
|
||||
if ($preferredLimit !== null && sizeof($result) >= $preferredLimit) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
$selectorsToCall = [];
|
||||
$addSelectorToCall = function ($type, $selector, $argument) use (&$selectorsToCall) {
|
||||
$previousIndex = sizeof($selectorsToCall) - 1;
|
||||
// todo optimize complex too
|
||||
if ($type === 1 && isset($selectorsToCall[$previousIndex]) && $selectorsToCall[$previousIndex][0] === $type && $selectorsToCall[$previousIndex][1] === $selector) {
|
||||
$selectorsToCall[$previousIndex][2][] = $argument;
|
||||
} else {
|
||||
$selectorsToCall[] = [$type, $selector, [$argument]];
|
||||
}
|
||||
};
|
||||
for ($i = 0; $i < 100000; $i++) {
|
||||
$matches = null;
|
||||
preg_match('/^(?<subselector>' . $supportedSelectorsExpression . ')\s*\\' . $operator . '\s*/', $selector, $matches); // getting the next subselector
|
||||
if (isset($matches['subselector'])) {
|
||||
$subSelector = $matches['subselector'];
|
||||
$selectorFound = false;
|
||||
foreach ($simpleSelectors as $simpleSelector => $callback) {
|
||||
$match = null;
|
||||
if (preg_match('/^' . (str_replace('?:', '', $simpleSelector)) . '$/', $subSelector, $match) === 1) { // if simple selector
|
||||
if ($mode === 'parse') {
|
||||
$result[] = $match[0];
|
||||
} else {
|
||||
$addSelectorToCall(1, $simpleSelector, $match);
|
||||
//call_user_func($callback, 'execute', $match, $context, $add);
|
||||
}
|
||||
$selectorFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$selectorFound) {
|
||||
foreach ($complexSelectors as $complexOperator => $callback) {
|
||||
$subSelectorParts = $processSelector('parse', $subSelector, $complexOperator);
|
||||
if ($subSelectorParts !== false) {
|
||||
$addSelectorToCall(2, $complexOperator, $subSelectorParts);
|
||||
//call_user_func($callback, $subSelectorParts, $context, $add);
|
||||
$selectorFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$selectorFound) {
|
||||
throw new \Exception('Internal error for selector "' . $selector . '"!');
|
||||
}
|
||||
$selector = substr($selector, strlen($matches[0])); // remove the matched subselector and continue parsing
|
||||
if (strlen($selector) === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($selectorsToCall as $selectorToCall) {
|
||||
if ($selectorToCall[0] === 1) { // is simple selector
|
||||
call_user_func($simpleSelectors[$selectorToCall[1]], 'execute', $selectorToCall[2], $context, $add);
|
||||
} else { // is complex selector
|
||||
call_user_func($complexSelectors[$selectorToCall[1]], $selectorToCall[2][0], $context, $add); // todo optimize and send all arguments
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
};
|
||||
|
||||
return $processSelector('execute', $selector);
|
||||
};
|
||||
|
||||
// div p (space between) - all <p> elements inside <div> elements
|
||||
$complexSelectors[' '] = function (array $parts, \DOMNode $context, callable $add = null) use (&$getMatchingElements) {
|
||||
$elements = null;
|
||||
foreach ($parts as $part) {
|
||||
if ($elements === null) {
|
||||
$elements = $getMatchingElements($context, $part);
|
||||
} else {
|
||||
$temp = [];
|
||||
foreach ($elements as $element) {
|
||||
$temp = array_merge($temp, $getMatchingElements($element, $part));
|
||||
}
|
||||
$elements = $temp;
|
||||
}
|
||||
}
|
||||
foreach ($elements as $element) {
|
||||
$add($element);
|
||||
}
|
||||
};
|
||||
|
||||
// div > p - all <p> elements where the parent is a <div> element
|
||||
$complexSelectors['>'] = function (array $parts, \DOMNode $context, callable $add = null) use (&$getMatchingElements, &$isMatchingElement) {
|
||||
$elements = null;
|
||||
foreach ($parts as $part) {
|
||||
if ($elements === null) {
|
||||
$elements = $getMatchingElements($context, $part);
|
||||
} else {
|
||||
$temp = [];
|
||||
foreach ($elements as $element) {
|
||||
foreach ($element->childNodes as $child) {
|
||||
if ($child instanceof \DOMElement && $isMatchingElement($child, $part)) {
|
||||
$temp[] = $child;
|
||||
}
|
||||
}
|
||||
}
|
||||
$elements = $temp;
|
||||
}
|
||||
}
|
||||
foreach ($elements as $element) {
|
||||
$add($element);
|
||||
}
|
||||
};
|
||||
|
||||
// div + p - all <p> elements that are placed immediately after <div> elements
|
||||
$complexSelectors['+'] = function (array $parts, \DOMNode $context, callable $add = null) use (&$getMatchingElements, &$isMatchingElement) {
|
||||
$elements = null;
|
||||
foreach ($parts as $part) {
|
||||
if ($elements === null) {
|
||||
$elements = $getMatchingElements($context, $part);
|
||||
} else {
|
||||
$temp = [];
|
||||
foreach ($elements as $element) {
|
||||
if ($element->nextSibling !== null && $isMatchingElement($element->nextSibling, $part)) {
|
||||
$temp[] = $element->nextSibling;
|
||||
}
|
||||
}
|
||||
$elements = $temp;
|
||||
}
|
||||
}
|
||||
foreach ($elements as $element) {
|
||||
$add($element);
|
||||
}
|
||||
};
|
||||
|
||||
// p ~ ul - all <ul> elements that are preceded by a <p> element
|
||||
$complexSelectors['~'] = function (array $parts, \DOMNode $context, callable $add = null) use (&$getMatchingElements, &$isMatchingElement) {
|
||||
$elements = null;
|
||||
foreach ($parts as $part) {
|
||||
if ($elements === null) {
|
||||
$elements = $getMatchingElements($context, $part);
|
||||
} else {
|
||||
$temp = [];
|
||||
foreach ($elements as $element) {
|
||||
$nextSibling = $element->nextSibling;
|
||||
while ($nextSibling !== null) {
|
||||
if ($isMatchingElement($nextSibling, $part)) {
|
||||
$temp[] = $nextSibling;
|
||||
}
|
||||
$nextSibling = $nextSibling->nextSibling;
|
||||
}
|
||||
}
|
||||
$elements = $temp;
|
||||
}
|
||||
}
|
||||
foreach ($elements as $element) {
|
||||
$add($element);
|
||||
}
|
||||
};
|
||||
|
||||
$result = $getMatchingElements($this, $selector, $preferredLimit);
|
||||
if ($result === false) {
|
||||
throw new \InvalidArgumentException('Unsupported selector (' . $selector . ')');
|
||||
}
|
||||
return new \IvoPetkov\HTML5DOMNodeList($result);
|
||||
}
|
||||
}
|
240
web/app/vendor/ivopetkov/html5-dom-document-php/src/HTML5DOMElement.php
vendored
Normal file
240
web/app/vendor/ivopetkov/html5-dom-document-php/src/HTML5DOMElement.php
vendored
Normal file
@ -0,0 +1,240 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* HTML5 DOMDocument PHP library (extends DOMDocument)
|
||||
* https://github.com/ivopetkov/html5-dom-document-php
|
||||
* Copyright (c) Ivo Petkov
|
||||
* Free to use under the MIT license.
|
||||
*/
|
||||
|
||||
namespace IvoPetkov;
|
||||
|
||||
use IvoPetkov\HTML5DOMDocument\Internal\QuerySelectors;
|
||||
|
||||
/**
|
||||
* Represents a live (can be manipulated) representation of an element in a HTML5 document.
|
||||
*
|
||||
* @property string $innerHTML The HTML code inside the element.
|
||||
* @property string $outerHTML The HTML code for the element including the code inside.
|
||||
* @property \IvoPetkov\HTML5DOMTokenList $classList A collection of the class attributes of the element.
|
||||
*/
|
||||
class HTML5DOMElement extends \DOMElement
|
||||
{
|
||||
|
||||
use QuerySelectors;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static private $foundEntitiesCache = [[], []];
|
||||
|
||||
/**
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static private $newObjectsCache = [];
|
||||
|
||||
/*
|
||||
*
|
||||
* @var HTML5DOMTokenList
|
||||
*/
|
||||
private $classList = null;
|
||||
|
||||
/**
|
||||
* Returns the value for the property specified.
|
||||
*
|
||||
* @param string $name
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __get(string $name)
|
||||
{
|
||||
if ($name === 'innerHTML') {
|
||||
if ($this->firstChild === null) {
|
||||
return '';
|
||||
}
|
||||
$html = $this->ownerDocument->saveHTML($this);
|
||||
$nodeName = $this->nodeName;
|
||||
return preg_replace('@^<' . $nodeName . '[^>]*>|</' . $nodeName . '>$@', '', $html);
|
||||
} elseif ($name === 'outerHTML') {
|
||||
if ($this->firstChild === null) {
|
||||
$nodeName = $this->nodeName;
|
||||
$attributes = $this->getAttributes();
|
||||
$result = '<' . $nodeName . '';
|
||||
foreach ($attributes as $name => $value) {
|
||||
$result .= ' ' . $name . '="' . htmlentities($value) . '"';
|
||||
}
|
||||
if (array_search($nodeName, ['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr']) === false) {
|
||||
$result .= '></' . $nodeName . '>';
|
||||
} else {
|
||||
$result .= '/>';
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
return $this->ownerDocument->saveHTML($this);
|
||||
} elseif ($name === 'classList') {
|
||||
if ($this->classList === null) {
|
||||
$this->classList = new HTML5DOMTokenList($this, 'class');
|
||||
}
|
||||
return $this->classList;
|
||||
}
|
||||
throw new \Exception('Undefined property: HTML5DOMElement::$' . $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value for the property specified.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $value
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __set(string $name, $value)
|
||||
{
|
||||
if ($name === 'innerHTML') {
|
||||
while ($this->hasChildNodes()) {
|
||||
$this->removeChild($this->firstChild);
|
||||
}
|
||||
if (!isset(self::$newObjectsCache['html5domdocument'])) {
|
||||
self::$newObjectsCache['html5domdocument'] = new \IvoPetkov\HTML5DOMDocument();
|
||||
}
|
||||
$tmpDoc = clone (self::$newObjectsCache['html5domdocument']);
|
||||
$tmpDoc->loadHTML('<body>' . $value . '</body>', HTML5DOMDocument::ALLOW_DUPLICATE_IDS);
|
||||
foreach ($tmpDoc->getElementsByTagName('body')->item(0)->childNodes as $node) {
|
||||
$node = $this->ownerDocument->importNode($node, true);
|
||||
$this->appendChild($node);
|
||||
}
|
||||
return;
|
||||
} elseif ($name === 'outerHTML') {
|
||||
if (!isset(self::$newObjectsCache['html5domdocument'])) {
|
||||
self::$newObjectsCache['html5domdocument'] = new \IvoPetkov\HTML5DOMDocument();
|
||||
}
|
||||
$tmpDoc = clone (self::$newObjectsCache['html5domdocument']);
|
||||
$tmpDoc->loadHTML('<body>' . $value . '</body>', HTML5DOMDocument::ALLOW_DUPLICATE_IDS);
|
||||
foreach ($tmpDoc->getElementsByTagName('body')->item(0)->childNodes as $node) {
|
||||
$node = $this->ownerDocument->importNode($node, true);
|
||||
$this->parentNode->insertBefore($node, $this);
|
||||
}
|
||||
$this->parentNode->removeChild($this);
|
||||
return;
|
||||
} elseif ($name === 'classList') {
|
||||
$this->setAttribute('class', $value);
|
||||
return;
|
||||
}
|
||||
throw new \Exception('Undefined property: HTML5DOMElement::$' . $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the result value before returning it.
|
||||
*
|
||||
* @param string $value
|
||||
* @return string The updated value
|
||||
*/
|
||||
private function updateResult(string $value): string
|
||||
{
|
||||
$value = str_replace(self::$foundEntitiesCache[0], self::$foundEntitiesCache[1], $value);
|
||||
if (strstr($value, 'html5-dom-document-internal-entity') !== false) {
|
||||
$search = [];
|
||||
$replace = [];
|
||||
$matches = [];
|
||||
preg_match_all('/html5-dom-document-internal-entity([12])-(.*?)-end/', $value, $matches);
|
||||
$matches[0] = array_unique($matches[0]);
|
||||
foreach ($matches[0] as $i => $match) {
|
||||
$search[] = $match;
|
||||
$replace[] = html_entity_decode(($matches[1][$i] === '1' ? '&' : '&#') . $matches[2][$i] . ';');
|
||||
}
|
||||
$value = str_replace($search, $replace, $value);
|
||||
self::$foundEntitiesCache[0] = array_merge(self::$foundEntitiesCache[0], $search);
|
||||
self::$foundEntitiesCache[1] = array_merge(self::$foundEntitiesCache[1], $replace);
|
||||
unset($search);
|
||||
unset($replace);
|
||||
unset($matches);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the updated nodeValue Property
|
||||
*
|
||||
* @return string The updated $nodeValue
|
||||
*/
|
||||
public function getNodeValue(): string
|
||||
{
|
||||
return $this->updateResult($this->nodeValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the updated $textContent Property
|
||||
*
|
||||
* @return string The updated $textContent
|
||||
*/
|
||||
public function getTextContent(): string
|
||||
{
|
||||
return $this->updateResult($this->textContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value for the attribute name specified.
|
||||
*
|
||||
* @param string $name The attribute name.
|
||||
* @return string The attribute value.
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function getAttribute($name): string
|
||||
{
|
||||
if ($this->attributes->length === 0) { // Performance optimization
|
||||
return '';
|
||||
}
|
||||
$value = parent::getAttribute($name);
|
||||
return $value !== '' ? (strstr($value, 'html5-dom-document-internal-entity') !== false ? $this->updateResult($value) : $value) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array containing all attributes.
|
||||
*
|
||||
* @return array An associative array containing all attributes.
|
||||
*/
|
||||
public function getAttributes(): array
|
||||
{
|
||||
$attributes = [];
|
||||
foreach ($this->attributes as $attributeName => $attribute) {
|
||||
$value = $attribute->value;
|
||||
$attributes[$attributeName] = $value !== '' ? (strstr($value, 'html5-dom-document-internal-entity') !== false ? $this->updateResult($value) : $value) : '';
|
||||
}
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the element outerHTML.
|
||||
*
|
||||
* @return string The element outerHTML.
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->outerHTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first child element matching the selector.
|
||||
*
|
||||
* @param string $selector A CSS query selector. Available values: *, tagname, tagname#id, #id, tagname.classname, .classname, tagname.classname.classname2, .classname.classname2, tagname[attribute-selector], [attribute-selector], "div, p", div p, div > p, div + p and p ~ ul.
|
||||
* @return HTML5DOMElement|null The result DOMElement or null if not found.
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function querySelector(string $selector)
|
||||
{
|
||||
return $this->internalQuerySelector($selector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of children elements matching the selector.
|
||||
*
|
||||
* @param string $selector A CSS query selector. Available values: *, tagname, tagname#id, #id, tagname.classname, .classname, tagname.classname.classname2, .classname.classname2, tagname[attribute-selector], [attribute-selector], "div, p", div p, div > p, div + p and p ~ ul.
|
||||
* @return HTML5DOMNodeList Returns a list of DOMElements matching the criteria.
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function querySelectorAll(string $selector)
|
||||
{
|
||||
return $this->internalQuerySelectorAll($selector);
|
||||
}
|
||||
}
|
45
web/app/vendor/ivopetkov/html5-dom-document-php/src/HTML5DOMNodeList.php
vendored
Normal file
45
web/app/vendor/ivopetkov/html5-dom-document-php/src/HTML5DOMNodeList.php
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* HTML5 DOMDocument PHP library (extends DOMDocument)
|
||||
* https://github.com/ivopetkov/html5-dom-document-php
|
||||
* Copyright (c) Ivo Petkov
|
||||
* Free to use under the MIT license.
|
||||
*/
|
||||
|
||||
namespace IvoPetkov;
|
||||
|
||||
/**
|
||||
* Represents a list of DOM nodes.
|
||||
*
|
||||
* @property-read int $length The list items count
|
||||
*/
|
||||
class HTML5DOMNodeList extends \ArrayObject
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns the item at the specified index.
|
||||
*
|
||||
* @param int $index The item index.
|
||||
* @return \IvoPetkov\HTML5DOMElement|null The item at the specified index or null if not existent.
|
||||
*/
|
||||
public function item(int $index)
|
||||
{
|
||||
return $this->offsetExists($index) ? $this->offsetGet($index) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value for the property specified.
|
||||
*
|
||||
* @param string $name The name of the property.
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __get(string $name)
|
||||
{
|
||||
if ($name === 'length') {
|
||||
return sizeof($this);
|
||||
}
|
||||
throw new \Exception('Undefined property: \IvoPetkov\HTML5DOMNodeList::$' . $name);
|
||||
}
|
||||
}
|
266
web/app/vendor/ivopetkov/html5-dom-document-php/src/HTML5DOMTokenList.php
vendored
Normal file
266
web/app/vendor/ivopetkov/html5-dom-document-php/src/HTML5DOMTokenList.php
vendored
Normal file
@ -0,0 +1,266 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* HTML5 DOMDocument PHP library (extends DOMDocument)
|
||||
* https://github.com/ivopetkov/html5-dom-document-php
|
||||
* Copyright (c) Ivo Petkov
|
||||
* Free to use under the MIT license.
|
||||
*/
|
||||
|
||||
namespace IvoPetkov;
|
||||
|
||||
use ArrayIterator;
|
||||
use DOMElement;
|
||||
|
||||
/**
|
||||
* Represents a set of space-separated tokens of an element attribute.
|
||||
*
|
||||
* @property-read int $length The number of tokens.
|
||||
* @property-read string $value A space-separated list of the tokens.
|
||||
*/
|
||||
class HTML5DOMTokenList
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $attributeName;
|
||||
|
||||
/**
|
||||
* @var DOMElement
|
||||
*/
|
||||
private $element;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $tokens;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $previousValue;
|
||||
|
||||
/**
|
||||
* Creates a list of space-separated tokens based on the attribute value of an element.
|
||||
*
|
||||
* @param DOMElement $element The DOM element.
|
||||
* @param string $attributeName The name of the attribute.
|
||||
*/
|
||||
public function __construct(DOMElement $element, string $attributeName)
|
||||
{
|
||||
$this->element = $element;
|
||||
$this->attributeName = $attributeName;
|
||||
$this->previousValue = null;
|
||||
$this->tokenize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given tokens to the list.
|
||||
*
|
||||
* @param string[] $tokens The tokens you want to add to the list.
|
||||
* @return void
|
||||
*/
|
||||
public function add(string ...$tokens)
|
||||
{
|
||||
if (count($tokens) === 0) {
|
||||
return;
|
||||
}
|
||||
foreach ($tokens as $t) {
|
||||
if (in_array($t, $this->tokens)) {
|
||||
continue;
|
||||
}
|
||||
$this->tokens[] = $t;
|
||||
}
|
||||
$this->setAttributeValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified tokens from the list. If the string does not exist in the list, no error is thrown.
|
||||
*
|
||||
* @param string[] $tokens The token you want to remove from the list.
|
||||
* @return void
|
||||
*/
|
||||
public function remove(string ...$tokens)
|
||||
{
|
||||
if (count($tokens) === 0) {
|
||||
return;
|
||||
}
|
||||
if (count($this->tokens) === 0) {
|
||||
return;
|
||||
}
|
||||
foreach ($tokens as $t) {
|
||||
$i = array_search($t, $this->tokens);
|
||||
if ($i === false) {
|
||||
continue;
|
||||
}
|
||||
array_splice($this->tokens, $i, 1);
|
||||
}
|
||||
$this->setAttributeValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an item in the list by its index (returns null if the number is greater than or equal to the length of the list).
|
||||
*
|
||||
* @param int $index The zero-based index of the item you want to return.
|
||||
* @return null|string
|
||||
*/
|
||||
public function item(int $index)
|
||||
{
|
||||
$this->tokenize();
|
||||
if ($index >= count($this->tokens)) {
|
||||
return null;
|
||||
}
|
||||
return $this->tokens[$index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a given token from the list and returns false. If token doesn't exist it's added and the function returns true.
|
||||
*
|
||||
* @param string $token The token you want to toggle.
|
||||
* @param bool $force A Boolean that, if included, turns the toggle into a one way-only operation. If set to false, the token will only be removed but not added again. If set to true, the token will only be added but not removed again.
|
||||
* @return bool false if the token is not in the list after the call, or true if the token is in the list after the call.
|
||||
*/
|
||||
public function toggle(string $token, bool $force = null): bool
|
||||
{
|
||||
$this->tokenize();
|
||||
$isThereAfter = false;
|
||||
$i = array_search($token, $this->tokens);
|
||||
if (is_null($force)) {
|
||||
if ($i === false) {
|
||||
$this->tokens[] = $token;
|
||||
$isThereAfter = true;
|
||||
} else {
|
||||
array_splice($this->tokens, $i, 1);
|
||||
}
|
||||
} else {
|
||||
if ($force) {
|
||||
if ($i === false) {
|
||||
$this->tokens[] = $token;
|
||||
}
|
||||
$isThereAfter = true;
|
||||
} else {
|
||||
if ($i !== false) {
|
||||
array_splice($this->tokens, $i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->setAttributeValue();
|
||||
return $isThereAfter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the list contains the given token, otherwise false.
|
||||
*
|
||||
* @param string $token The token you want to check for the existence of in the list.
|
||||
* @return bool true if the list contains the given token, otherwise false.
|
||||
*/
|
||||
public function contains(string $token): bool
|
||||
{
|
||||
$this->tokenize();
|
||||
return in_array($token, $this->tokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces an existing token with a new token.
|
||||
*
|
||||
* @param string $old The token you want to replace.
|
||||
* @param string $new The token you want to replace $old with.
|
||||
* @return void
|
||||
*/
|
||||
public function replace(string $old, string $new)
|
||||
{
|
||||
if ($old === $new) {
|
||||
return;
|
||||
}
|
||||
$this->tokenize();
|
||||
$i = array_search($old, $this->tokens);
|
||||
if ($i !== false) {
|
||||
$j = array_search($new, $this->tokens);
|
||||
if ($j === false) {
|
||||
$this->tokens[$i] = $new;
|
||||
} else {
|
||||
array_splice($this->tokens, $i, 1);
|
||||
}
|
||||
$this->setAttributeValue();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
$this->tokenize();
|
||||
return implode(' ', $this->tokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator allowing you to go through all tokens contained in the list.
|
||||
*
|
||||
* @return ArrayIterator
|
||||
*/
|
||||
public function entries(): ArrayIterator
|
||||
{
|
||||
$this->tokenize();
|
||||
return new ArrayIterator($this->tokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value for the property specified
|
||||
*
|
||||
* @param string $name The name of the property
|
||||
* @return string The value of the property specified
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __get(string $name)
|
||||
{
|
||||
if ($name === 'length') {
|
||||
$this->tokenize();
|
||||
return count($this->tokens);
|
||||
} elseif ($name === 'value') {
|
||||
return $this->__toString();
|
||||
}
|
||||
throw new \Exception('Undefined property: HTML5DOMTokenList::$' . $name);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function tokenize()
|
||||
{
|
||||
$current = $this->element->getAttribute($this->attributeName);
|
||||
if ($this->previousValue === $current) {
|
||||
return;
|
||||
}
|
||||
$this->previousValue = $current;
|
||||
$tokens = explode(' ', $current);
|
||||
$finals = [];
|
||||
foreach ($tokens as $token) {
|
||||
if ($token === '') {
|
||||
continue;
|
||||
}
|
||||
if (in_array($token, $finals)) {
|
||||
continue;
|
||||
}
|
||||
$finals[] = $token;
|
||||
}
|
||||
$this->tokens = $finals;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function setAttributeValue()
|
||||
{
|
||||
$value = implode(' ', $this->tokens);
|
||||
if ($this->previousValue === $value) {
|
||||
return;
|
||||
}
|
||||
$this->previousValue = $value;
|
||||
$this->element->setAttribute($this->attributeName, $value);
|
||||
}
|
||||
}
|
2
web/app/vendor/php-curl-class/php-curl-class/.gitignore
vendored
Normal file
2
web/app/vendor/php-curl-class/php-curl-class/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
vendor/
|
||||
composer.lock
|
19
web/app/vendor/php-curl-class/php-curl-class/.travis.yml
vendored
Normal file
19
web/app/vendor/php-curl-class/php-curl-class/.travis.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
language: php
|
||||
php:
|
||||
- 5.3
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.6
|
||||
- hhvm
|
||||
matrix:
|
||||
allow_failures:
|
||||
- php: 5.6
|
||||
- php: hhvm
|
||||
before_script:
|
||||
- if [[ "$TRAVIS_PHP_VERSION" == "5.4" ]]; then sh -c "php -S 127.0.0.1:8000 -t tests/PHPCurlClass/ &"; fi
|
||||
- if [[ "$TRAVIS_PHP_VERSION" == "5.5" ]]; then sh -c "php -S 127.0.0.1:8000 -t tests/PHPCurlClass/ &"; fi
|
||||
- if [[ "$TRAVIS_PHP_VERSION" == "5.6" ]]; then sh -c "php -S 127.0.0.1:8000 -t tests/PHPCurlClass/ &"; fi
|
||||
- if [[ "$TRAVIS_PHP_VERSION" == "hhvm" ]]; then sh -c "cd tests && hhvm --mode server --port 8000 --config PHPCurlClass/server.hdf &"; fi
|
||||
script:
|
||||
- php -l src/*
|
||||
- if [[ "$TRAVIS_PHP_VERSION" != "5.3" ]]; then sh -c "cd tests && phpunit --configuration phpunit.xml"; fi
|
24
web/app/vendor/php-curl-class/php-curl-class/LICENSE
vendored
Normal file
24
web/app/vendor/php-curl-class/php-curl-class/LICENSE
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
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 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.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
136
web/app/vendor/php-curl-class/php-curl-class/README.md
vendored
Normal file
136
web/app/vendor/php-curl-class/php-curl-class/README.md
vendored
Normal file
@ -0,0 +1,136 @@
|
||||
# php-curl-class
|
||||
|
||||
[![Build Status](https://travis-ci.org/php-curl-class/php-curl-class.png?branch=master)](https://travis-ci.org/php-curl-class/php-curl-class)
|
||||
|
||||
PHP Curl Class is an object-oriented wrapper of the PHP cURL extension.
|
||||
|
||||
### Composer
|
||||
|
||||
$ composer require php-curl-class/php-curl-class
|
||||
|
||||
### Quick Start and Examples
|
||||
|
||||
```php
|
||||
require 'Curl.class.php';
|
||||
|
||||
$curl = new Curl();
|
||||
$curl->get('http://www.example.com/');
|
||||
```
|
||||
|
||||
```php
|
||||
$curl = new Curl();
|
||||
$curl->get('http://www.example.com/search', array(
|
||||
'q' => 'keyword',
|
||||
));
|
||||
```
|
||||
|
||||
```php
|
||||
$curl = new Curl();
|
||||
$curl->post('http://www.example.com/login/', array(
|
||||
'username' => 'myusername',
|
||||
'password' => 'mypassword',
|
||||
));
|
||||
```
|
||||
|
||||
```php
|
||||
$curl = new Curl();
|
||||
$curl->setBasicAuthentication('username', 'password');
|
||||
$curl->setUserAgent('');
|
||||
$curl->setReferrer('');
|
||||
$curl->setHeader('X-Requested-With', 'XMLHttpRequest');
|
||||
$curl->setCookie('key', 'value');
|
||||
$curl->get('http://www.example.com/');
|
||||
|
||||
if ($curl->error) {
|
||||
echo $curl->error_code;
|
||||
}
|
||||
else {
|
||||
echo $curl->response;
|
||||
}
|
||||
|
||||
var_dump($curl->request_headers);
|
||||
var_dump($curl->response_headers);
|
||||
```
|
||||
|
||||
```php
|
||||
$curl = new Curl();
|
||||
$curl->setOpt(CURLOPT_SSL_VERIFYPEER, false);
|
||||
$curl->get('https://encrypted.example.com/');
|
||||
```
|
||||
|
||||
```php
|
||||
$curl = new Curl();
|
||||
$curl->put('http://api.example.com/user/', array(
|
||||
'first_name' => 'Zach',
|
||||
'last_name' => 'Borboa',
|
||||
));
|
||||
```
|
||||
|
||||
```php
|
||||
$curl = new Curl();
|
||||
$curl->patch('http://api.example.com/profile/', array(
|
||||
'image' => '@path/to/file.jpg',
|
||||
));
|
||||
```
|
||||
|
||||
```php
|
||||
$curl = new Curl();
|
||||
$curl->delete('http://api.example.com/user/', array(
|
||||
'id' => '1234',
|
||||
));
|
||||
```
|
||||
|
||||
```php
|
||||
// Enable gzip compression.
|
||||
$curl = new Curl();
|
||||
$curl->setOpt(CURLOPT_ENCODING , 'gzip');
|
||||
$curl->get('https://www.example.com/image.png');
|
||||
```
|
||||
|
||||
```php
|
||||
// Case-insensitive access to headers.
|
||||
$curl = new Curl();
|
||||
$curl->get('https://www.example.com/image.png');
|
||||
echo $curl->response_headers['Content-Type'] . "\n"; // image/png
|
||||
echo $curl->response_headers['CoNTeNT-TyPE'] . "\n"; // image/png
|
||||
```
|
||||
|
||||
```php
|
||||
$curl->close();
|
||||
```
|
||||
|
||||
```php
|
||||
// Example access to curl object.
|
||||
curl_set_opt($curl->curl, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1');
|
||||
curl_close($curl->curl);
|
||||
```
|
||||
|
||||
```php
|
||||
// Requests in parallel with callback functions.
|
||||
$curl = new Curl();
|
||||
$curl->setOpt(CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1');
|
||||
|
||||
$curl->success(function($instance) {
|
||||
echo 'call was successful. response was' . "\n";
|
||||
echo $instance->response . "\n";
|
||||
});
|
||||
$curl->error(function($instance) {
|
||||
echo 'call was unsuccessful.' . "\n";
|
||||
echo 'error code:' . $instance->error_code . "\n";
|
||||
echo 'error message:' . $instance->error_message . "\n";
|
||||
});
|
||||
$curl->complete(function($instance) {
|
||||
echo 'call completed' . "\n";
|
||||
});
|
||||
|
||||
$curl->get(array(
|
||||
'https://duckduckgo.com/',
|
||||
'https://search.yahoo.com/search',
|
||||
'https://www.bing.com/search',
|
||||
'http://www.dogpile.com/search/web',
|
||||
'https://www.google.com/search',
|
||||
'https://www.wolframalpha.com/input/',
|
||||
), array(
|
||||
'q' => 'hello world',
|
||||
));
|
||||
```
|
7
web/app/vendor/php-curl-class/php-curl-class/composer.json
vendored
Normal file
7
web/app/vendor/php-curl-class/php-curl-class/composer.json
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "php-curl-class/php-curl-class",
|
||||
"description": "PHP Curl Class is an object-oriented wrapper of the PHP cURL extension.",
|
||||
"autoload": {
|
||||
"classmap": ["src/"]
|
||||
}
|
||||
}
|
22
web/app/vendor/php-curl-class/php-curl-class/examples/coinbase_account_balance.php
vendored
Normal file
22
web/app/vendor/php-curl-class/php-curl-class/examples/coinbase_account_balance.php
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
require '../src/Curl.class.php';
|
||||
|
||||
|
||||
define('API_KEY', '');
|
||||
define('API_SECRET', '');
|
||||
|
||||
$url = 'https://coinbase.com/api/v1/account/balance';
|
||||
|
||||
$nonce = (int)(microtime(true) * 1e6);
|
||||
$message = $nonce . $url;
|
||||
$signature = hash_hmac('sha256', $message, API_SECRET);
|
||||
|
||||
$curl = new Curl();
|
||||
$curl->setHeader('ACCESS_KEY', API_KEY);
|
||||
$curl->setHeader('ACCESS_SIGNATURE', $signature);
|
||||
$curl->setHeader('ACCESS_NONCE', $nonce);
|
||||
$curl->get($url);
|
||||
|
||||
echo
|
||||
'My current account balance at Coinbase is ' .
|
||||
$curl->response->amount . ' ' . $curl->response->currency . '.' . "\n";
|
10
web/app/vendor/php-curl-class/php-curl-class/examples/coinbase_spot_rate.php
vendored
Normal file
10
web/app/vendor/php-curl-class/php-curl-class/examples/coinbase_spot_rate.php
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
require '../src/Curl.class.php';
|
||||
|
||||
|
||||
$curl = new Curl();
|
||||
$curl->get('https://coinbase.com/api/v1/prices/spot_rate');
|
||||
|
||||
echo
|
||||
'The current price of bitcoin at Coinbase is ' .
|
||||
'$' . $curl->response->amount . ' ' . $curl->response->currency . '.' . "\n";
|
17
web/app/vendor/php-curl-class/php-curl-class/examples/instagram_search_photos.php
vendored
Normal file
17
web/app/vendor/php-curl-class/php-curl-class/examples/instagram_search_photos.php
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
require '../src/Curl.class.php';
|
||||
|
||||
|
||||
define('INSTAGRAM_CLIENT_ID', 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');
|
||||
|
||||
$curl = new Curl();
|
||||
$curl->get('https://api.instagram.com/v1/media/search', array(
|
||||
'client_id' => INSTAGRAM_CLIENT_ID,
|
||||
'lat' => '37.8296',
|
||||
'lng' => '-122.4832',
|
||||
));
|
||||
|
||||
foreach ($curl->response->data as $media) {
|
||||
$image = $media->images->low_resolution;
|
||||
echo '<img alt="" src="' . $image->url . '" width="' . $image->width . '" height="' . $image->height . '" />';
|
||||
}
|
14
web/app/vendor/php-curl-class/php-curl-class/examples/put.php
vendored
Normal file
14
web/app/vendor/php-curl-class/php-curl-class/examples/put.php
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
require '../src/Curl.class.php';
|
||||
|
||||
|
||||
// curl -X PUT -d "id=1&first_name=Zach&last_name=Borboa" "http://httpbin.org/put"
|
||||
$curl = new Curl();
|
||||
$curl->put('http://httpbin.org/put', array(
|
||||
'id' => 1,
|
||||
'first_name' => 'Zach',
|
||||
'last_name' => 'Borboa',
|
||||
));
|
||||
|
||||
echo 'Data server received via PUT:' . "\n";
|
||||
var_dump($curl->response->form);
|
35
web/app/vendor/php-curl-class/php-curl-class/examples/twitter_post_tweet.php
vendored
Normal file
35
web/app/vendor/php-curl-class/php-curl-class/examples/twitter_post_tweet.php
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
require '../src/Curl.class.php';
|
||||
|
||||
|
||||
define('API_KEY', 'XXXXXXXXXXXXXXXXXXXXXXXXX');
|
||||
define('API_SECRET', 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');
|
||||
define('OAUTH_ACCESS_TOKEN', 'XXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');
|
||||
define('OAUTH_TOKEN_SECRET', 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');
|
||||
|
||||
$status = 'I love php curl class. https://github.com/php-curl-class/php-curl-class';
|
||||
|
||||
$oauth_data = array(
|
||||
'oauth_consumer_key' => API_KEY,
|
||||
'oauth_nonce' => md5(microtime() . mt_rand()),
|
||||
'oauth_signature_method' => 'HMAC-SHA1',
|
||||
'oauth_timestamp' => time(),
|
||||
'oauth_token' => OAUTH_ACCESS_TOKEN,
|
||||
'oauth_version' => '1.0',
|
||||
'status' => $status,
|
||||
);
|
||||
|
||||
$url = 'https://api.twitter.com/1.1/statuses/update.json';
|
||||
$request = implode('&', array(
|
||||
'POST',
|
||||
rawurlencode($url),
|
||||
rawurlencode(http_build_query($oauth_data, '', '&', PHP_QUERY_RFC3986)),
|
||||
));
|
||||
$key = implode('&', array(API_SECRET, OAUTH_TOKEN_SECRET));
|
||||
$oauth_data['oauth_signature'] = base64_encode(hash_hmac('sha1', $request, $key, true));
|
||||
$data = http_build_query($oauth_data, '', '&');
|
||||
|
||||
$curl = new Curl();
|
||||
$curl->post($url, $data);
|
||||
|
||||
echo 'Posted "' . $curl->response->text . '" at ' . $curl->response->created_at . '.' . "\n";
|
496
web/app/vendor/php-curl-class/php-curl-class/src/Curl.class.php
vendored
Normal file
496
web/app/vendor/php-curl-class/php-curl-class/src/Curl.class.php
vendored
Normal file
@ -0,0 +1,496 @@
|
||||
<?php
|
||||
|
||||
class Curl
|
||||
{
|
||||
const USER_AGENT = 'PHP-Curl-Class/2.0 (+https://github.com/php-curl-class/php-curl-class)';
|
||||
|
||||
private $cookies = array();
|
||||
private $headers = array();
|
||||
private $options = array();
|
||||
|
||||
private $multi_parent = false;
|
||||
private $multi_child = false;
|
||||
private $before_send_function = null;
|
||||
private $success_function = null;
|
||||
private $error_function = null;
|
||||
private $complete_function = null;
|
||||
|
||||
public $curl;
|
||||
public $curls;
|
||||
|
||||
public $error = false;
|
||||
public $error_code = 0;
|
||||
public $error_message = null;
|
||||
|
||||
public $curl_error = false;
|
||||
public $curl_error_code = 0;
|
||||
public $curl_error_message = null;
|
||||
|
||||
public $http_error = false;
|
||||
public $http_status_code = 0;
|
||||
public $http_error_message = null;
|
||||
|
||||
public $request_headers = null;
|
||||
public $response_headers = null;
|
||||
public $response = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
if (!extension_loaded('curl')) {
|
||||
throw new \ErrorException('cURL library is not loaded');
|
||||
}
|
||||
|
||||
$this->curl = curl_init();
|
||||
$this->setUserAgent(self::USER_AGENT);
|
||||
$this->setOpt(CURLINFO_HEADER_OUT, true);
|
||||
$this->setOpt(CURLOPT_HEADER, true);
|
||||
$this->setOpt(CURLOPT_RETURNTRANSFER, true);
|
||||
}
|
||||
|
||||
public function get($url_mixed, $data = array())
|
||||
{
|
||||
if (is_array($url_mixed)) {
|
||||
$curl_multi = curl_multi_init();
|
||||
$this->multi_parent = true;
|
||||
|
||||
$this->curls = array();
|
||||
|
||||
foreach ($url_mixed as $url) {
|
||||
$curl = new Curl();
|
||||
$curl->multi_child = true;
|
||||
$curl->setOpt(CURLOPT_URL, $this->buildURL($url, $data), $curl->curl);
|
||||
$curl->setOpt(CURLOPT_CUSTOMREQUEST, 'GET');
|
||||
$curl->setOpt(CURLOPT_HTTPGET, true);
|
||||
$this->call($this->before_send_function, $curl);
|
||||
$this->curls[] = $curl;
|
||||
|
||||
$curlm_error_code = curl_multi_add_handle($curl_multi, $curl->curl);
|
||||
if (!($curlm_error_code === CURLM_OK)) {
|
||||
throw new \ErrorException('cURL multi add handle error: ' . curl_multi_strerror($curlm_error_code));
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->curls as $ch) {
|
||||
foreach ($this->options as $key => $value) {
|
||||
$ch->setOpt($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
$status = curl_multi_exec($curl_multi, $active);
|
||||
} while ($status === CURLM_CALL_MULTI_PERFORM || $active);
|
||||
|
||||
foreach ($this->curls as $ch) {
|
||||
$this->exec($ch);
|
||||
}
|
||||
} else {
|
||||
$this->setopt(CURLOPT_URL, $this->buildURL($url_mixed, $data));
|
||||
$this->setOpt(CURLOPT_CUSTOMREQUEST, 'GET');
|
||||
$this->setopt(CURLOPT_HTTPGET, true);
|
||||
return $this->exec();
|
||||
}
|
||||
}
|
||||
|
||||
public function post($url, $data = array())
|
||||
{
|
||||
if (is_array($data) && empty($data)) {
|
||||
$this->setHeader('Content-Length');
|
||||
}
|
||||
|
||||
$this->setOpt(CURLOPT_URL, $this->buildURL($url));
|
||||
$this->setOpt(CURLOPT_CUSTOMREQUEST, 'POST');
|
||||
$this->setOpt(CURLOPT_POST, true);
|
||||
$this->setOpt(CURLOPT_POSTFIELDS, $this->postfields($data));
|
||||
return $this->exec();
|
||||
}
|
||||
|
||||
public function put($url, $data = array())
|
||||
{
|
||||
$this->setOpt(CURLOPT_URL, $url);
|
||||
$this->setOpt(CURLOPT_CUSTOMREQUEST, 'PUT');
|
||||
$put_data = http_build_query($data);
|
||||
if (empty($this->options[CURLOPT_INFILE]) && empty($this->options[CURLOPT_INFILESIZE])) {
|
||||
$this->setHeader('Content-Length', strlen($put_data));
|
||||
}
|
||||
$this->setOpt(CURLOPT_POSTFIELDS, $put_data);
|
||||
return $this->exec();
|
||||
}
|
||||
|
||||
public function patch($url, $data = array())
|
||||
{
|
||||
$this->setHeader('Content-Length');
|
||||
$this->setOpt(CURLOPT_URL, $this->buildURL($url));
|
||||
$this->setOpt(CURLOPT_CUSTOMREQUEST, 'PATCH');
|
||||
$this->setOpt(CURLOPT_POSTFIELDS, $data);
|
||||
return $this->exec();
|
||||
}
|
||||
|
||||
public function delete($url, $data = array())
|
||||
{
|
||||
$this->setHeader('Content-Length');
|
||||
$this->setOpt(CURLOPT_URL, $this->buildURL($url, $data));
|
||||
$this->setOpt(CURLOPT_CUSTOMREQUEST, 'DELETE');
|
||||
return $this->exec();
|
||||
}
|
||||
|
||||
public function head($url, $data = array())
|
||||
{
|
||||
$this->setOpt(CURLOPT_URL, $this->buildURL($url, $data));
|
||||
$this->setOpt(CURLOPT_CUSTOMREQUEST, 'HEAD');
|
||||
$this->setOpt(CURLOPT_NOBODY, true);
|
||||
return $this->exec();
|
||||
}
|
||||
|
||||
public function options($url, $data = array())
|
||||
{
|
||||
$this->setHeader('Content-Length');
|
||||
$this->setOpt(CURLOPT_URL, $this->buildURL($url, $data));
|
||||
$this->setOpt(CURLOPT_CUSTOMREQUEST, 'OPTIONS');
|
||||
return $this->exec();
|
||||
}
|
||||
|
||||
public function setBasicAuthentication($username, $password)
|
||||
{
|
||||
$this->setOpt(CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
|
||||
$this->setOpt(CURLOPT_USERPWD, $username . ':' . $password);
|
||||
}
|
||||
|
||||
public function setHeader($key, $value = '')
|
||||
{
|
||||
$this->headers[$key] = $key . ': ' . $value;
|
||||
$this->setOpt(CURLOPT_HTTPHEADER, array_values($this->headers));
|
||||
}
|
||||
|
||||
public function setUserAgent($user_agent)
|
||||
{
|
||||
$this->setOpt(CURLOPT_USERAGENT, $user_agent);
|
||||
}
|
||||
|
||||
public function setReferrer($referrer)
|
||||
{
|
||||
$this->setOpt(CURLOPT_REFERER, $referrer);
|
||||
}
|
||||
|
||||
public function setCookie($key, $value)
|
||||
{
|
||||
$this->cookies[$key] = $value;
|
||||
$this->setOpt(CURLOPT_COOKIE, http_build_query($this->cookies, '', '; '));
|
||||
}
|
||||
|
||||
public function setCookieFile($cookie_file)
|
||||
{
|
||||
$this->setOpt(CURLOPT_COOKIEFILE, $cookie_file);
|
||||
}
|
||||
|
||||
public function setCookieJar($cookie_jar)
|
||||
{
|
||||
$this->setOpt(CURLOPT_COOKIEJAR, $cookie_jar);
|
||||
}
|
||||
|
||||
public function setOpt($option, $value, $_ch = null)
|
||||
{
|
||||
$ch = is_null($_ch) ? $this->curl : $_ch;
|
||||
|
||||
$required_options = array(
|
||||
CURLINFO_HEADER_OUT => 'CURLINFO_HEADER_OUT',
|
||||
CURLOPT_HEADER => 'CURLOPT_HEADER',
|
||||
CURLOPT_RETURNTRANSFER => 'CURLOPT_RETURNTRANSFER',
|
||||
);
|
||||
|
||||
if (in_array($option, array_keys($required_options), true) && !($value === true)) {
|
||||
trigger_error($required_options[$option] . ' is a required option', E_USER_WARNING);
|
||||
}
|
||||
|
||||
$this->options[$option] = $value;
|
||||
return curl_setopt($ch, $option, $value);
|
||||
}
|
||||
|
||||
public function verbose($on = true)
|
||||
{
|
||||
$this->setOpt(CURLOPT_VERBOSE, $on);
|
||||
}
|
||||
|
||||
public function close()
|
||||
{
|
||||
if ($this->multi_parent) {
|
||||
foreach ($this->curls as $curl) {
|
||||
$curl->close();
|
||||
}
|
||||
}
|
||||
|
||||
if (is_resource($this->curl)) {
|
||||
curl_close($this->curl);
|
||||
}
|
||||
}
|
||||
|
||||
public function beforeSend($function)
|
||||
{
|
||||
$this->before_send_function = $function;
|
||||
}
|
||||
|
||||
public function success($callback)
|
||||
{
|
||||
$this->success_function = $callback;
|
||||
}
|
||||
|
||||
public function error($callback)
|
||||
{
|
||||
$this->error_function = $callback;
|
||||
}
|
||||
|
||||
public function complete($callback)
|
||||
{
|
||||
$this->complete_function = $callback;
|
||||
}
|
||||
|
||||
private function buildURL($url, $data = array())
|
||||
{
|
||||
return $url . (empty($data) ? '' : '?' . http_build_query($data));
|
||||
}
|
||||
|
||||
private function parseHeaders($raw_headers)
|
||||
{
|
||||
$raw_headers = preg_split('/\r\n/', $raw_headers, null, PREG_SPLIT_NO_EMPTY);
|
||||
$http_headers = new CaseInsensitiveArray();
|
||||
|
||||
for ($i = 1; $i < count($raw_headers); $i++) {
|
||||
list($key, $value) = explode(':', $raw_headers[$i], 2);
|
||||
$key = trim($key);
|
||||
$value = trim($value);
|
||||
// Use isset() as array_key_exists() and ArrayAccess are not compatible.
|
||||
if (isset($http_headers[$key])) {
|
||||
$http_headers[$key] .= ',' . $value;
|
||||
} else {
|
||||
$http_headers[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return array(isset($raw_headers['0']) ? $raw_headers['0'] : '', $http_headers);
|
||||
}
|
||||
|
||||
private function parseRequestHeaders($raw_headers)
|
||||
{
|
||||
$request_headers = new CaseInsensitiveArray();
|
||||
list($first_line, $headers) = $this->parseHeaders($raw_headers);
|
||||
$request_headers['Request-Line'] = $first_line;
|
||||
foreach ($headers as $key => $value) {
|
||||
$request_headers[$key] = $value;
|
||||
}
|
||||
return $request_headers;
|
||||
}
|
||||
|
||||
private function parseResponseHeaders($raw_headers)
|
||||
{
|
||||
$response_headers = new CaseInsensitiveArray();
|
||||
list($first_line, $headers) = $this->parseHeaders($raw_headers);
|
||||
$response_headers['Status-Line'] = $first_line;
|
||||
foreach ($headers as $key => $value) {
|
||||
$response_headers[$key] = $value;
|
||||
}
|
||||
return $response_headers;
|
||||
}
|
||||
|
||||
private function postfields($data)
|
||||
{
|
||||
if (is_array($data)) {
|
||||
if (is_array_multidim($data)) {
|
||||
$data = http_build_multi_query($data);
|
||||
} else {
|
||||
foreach ($data as $key => $value) {
|
||||
// Fix "Notice: Array to string conversion" when $value in
|
||||
// curl_setopt($ch, CURLOPT_POSTFIELDS, $value) is an array
|
||||
// that contains an empty array.
|
||||
if (is_array($value) && empty($value)) {
|
||||
$data[$key] = '';
|
||||
// Fix "curl_setopt(): The usage of the @filename API for
|
||||
// file uploading is deprecated. Please use the CURLFile
|
||||
// class instead".
|
||||
} elseif (is_string($value) && strpos($value, '@') === 0) {
|
||||
if (class_exists('CURLFile')) {
|
||||
$data[$key] = new CURLFile(substr($value, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function exec($_ch = null)
|
||||
{
|
||||
$ch = is_null($_ch) ? $this : $_ch;
|
||||
|
||||
if ($ch->multi_child) {
|
||||
$ch->response = curl_multi_getcontent($ch->curl);
|
||||
} else {
|
||||
$ch->response = curl_exec($ch->curl);
|
||||
}
|
||||
|
||||
$ch->curl_error_code = curl_errno($ch->curl);
|
||||
$ch->curl_error_message = curl_error($ch->curl);
|
||||
$ch->curl_error = !($ch->curl_error_code === 0);
|
||||
$ch->http_status_code = curl_getinfo($ch->curl, CURLINFO_HTTP_CODE);
|
||||
$ch->http_error = in_array(floor($ch->http_status_code / 100), array(4, 5));
|
||||
$ch->error = $ch->curl_error || $ch->http_error;
|
||||
$ch->error_code = $ch->error ? ($ch->curl_error ? $ch->curl_error_code : $ch->http_status_code) : 0;
|
||||
|
||||
$ch->request_headers = $this->parseRequestHeaders(curl_getinfo($ch->curl, CURLINFO_HEADER_OUT));
|
||||
$ch->response_headers = '';
|
||||
if (!(strpos($ch->response, "\r\n\r\n") === false)) {
|
||||
list($response_header, $ch->response) = explode("\r\n\r\n", $ch->response, 2);
|
||||
if ($response_header === 'HTTP/1.1 100 Continue') {
|
||||
list($response_header, $ch->response) = explode("\r\n\r\n", $ch->response, 2);
|
||||
}
|
||||
$ch->response_headers = $this->parseResponseHeaders($response_header);
|
||||
|
||||
if (isset($ch->response_headers['Content-Type'])) {
|
||||
if (preg_match('/^application\/json/i', $ch->response_headers['Content-Type'])) {
|
||||
$json_obj = json_decode($ch->response, false);
|
||||
if (!is_null($json_obj)) {
|
||||
$ch->response = $json_obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$ch->http_error_message = '';
|
||||
if ($ch->error) {
|
||||
if (isset($ch->response_headers['Status-Line'])) {
|
||||
$ch->http_error_message = $ch->response_headers['Status-Line'];
|
||||
}
|
||||
}
|
||||
$ch->error_message = $ch->curl_error ? $ch->curl_error_message : $ch->http_error_message;
|
||||
|
||||
if (!$ch->error) {
|
||||
$ch->call($this->success_function, $ch);
|
||||
} else {
|
||||
$ch->call($this->error_function, $ch);
|
||||
}
|
||||
|
||||
$ch->call($this->complete_function, $ch);
|
||||
|
||||
return $ch->error_code;
|
||||
}
|
||||
|
||||
private function call($function)
|
||||
{
|
||||
if (is_callable($function)) {
|
||||
$args = func_get_args();
|
||||
array_shift($args);
|
||||
call_user_func_array($function, $args);
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
$this->close();
|
||||
}
|
||||
}
|
||||
|
||||
class CaseInsensitiveArray implements ArrayAccess, Countable, Iterator
|
||||
{
|
||||
private $container = array();
|
||||
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
if (is_null($offset)) {
|
||||
$this->container[] = $value;
|
||||
} else {
|
||||
$index = array_search(strtolower($offset), array_keys(array_change_key_case($this->container, CASE_LOWER)));
|
||||
if (!($index === false)) {
|
||||
$keys = array_keys($this->container);
|
||||
unset($this->container[$keys[$index]]);
|
||||
}
|
||||
$this->container[$offset] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return array_key_exists(strtolower($offset), array_change_key_case($this->container, CASE_LOWER));
|
||||
}
|
||||
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
unset($this->container[$offset]);
|
||||
}
|
||||
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
$index = array_search(strtolower($offset), array_keys(array_change_key_case($this->container, CASE_LOWER)));
|
||||
if ($index === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$values = array_values($this->container);
|
||||
return $values[$index];
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return count($this->container);
|
||||
}
|
||||
|
||||
public function current()
|
||||
{
|
||||
return current($this->container);
|
||||
}
|
||||
|
||||
public function next()
|
||||
{
|
||||
return next($this->container);
|
||||
}
|
||||
|
||||
public function key()
|
||||
{
|
||||
return key($this->container);
|
||||
}
|
||||
|
||||
public function valid()
|
||||
{
|
||||
return !($this->current() === false);
|
||||
}
|
||||
|
||||
public function rewind()
|
||||
{
|
||||
reset($this->container);
|
||||
}
|
||||
}
|
||||
|
||||
function is_array_assoc($array)
|
||||
{
|
||||
return (bool)count(array_filter(array_keys($array), 'is_string'));
|
||||
}
|
||||
|
||||
function is_array_multidim($array)
|
||||
{
|
||||
if (!is_array($array)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !(count($array) === count($array, COUNT_RECURSIVE));
|
||||
}
|
||||
|
||||
function http_build_multi_query($data, $key = null)
|
||||
{
|
||||
$query = array();
|
||||
|
||||
if (empty($data)) {
|
||||
return $key . '=';
|
||||
}
|
||||
|
||||
$is_array_assoc = is_array_assoc($data);
|
||||
|
||||
foreach ($data as $k => $value) {
|
||||
if (is_string($value) || is_numeric($value)) {
|
||||
$brackets = $is_array_assoc ? '[' . $k . ']' : '[]';
|
||||
$query[] = urlencode(is_null($key) ? $k : $key . $brackets) . '=' . rawurlencode($value);
|
||||
} elseif (is_array($value)) {
|
||||
$nested = is_null($key) ? $k : $key . '[' . $k . ']';
|
||||
$query[] = http_build_multi_query($value, $nested);
|
||||
}
|
||||
}
|
||||
|
||||
return implode('&', $query);
|
||||
}
|
740
web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/PHPCurlClassTest.php
vendored
Normal file
740
web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/PHPCurlClassTest.php
vendored
Normal file
@ -0,0 +1,740 @@
|
||||
<?php
|
||||
// Usage: phpunit --verbose run.php
|
||||
|
||||
require '../src/Curl.class.php';
|
||||
require 'helper.inc.php';
|
||||
|
||||
|
||||
class CurlTest extends PHPUnit_Framework_TestCase {
|
||||
public function testExtensionLoaded() {
|
||||
$this->assertTrue(extension_loaded('curl'));
|
||||
}
|
||||
|
||||
public function testArrayAssociative() {
|
||||
$this->assertTrue(is_array_assoc(array(
|
||||
'foo' => 'wibble',
|
||||
'bar' => 'wubble',
|
||||
'baz' => 'wobble',
|
||||
)));
|
||||
}
|
||||
|
||||
public function testArrayIndexed() {
|
||||
$this->assertFalse(is_array_assoc(array(
|
||||
'wibble',
|
||||
'wubble',
|
||||
'wobble',
|
||||
)));
|
||||
}
|
||||
|
||||
public function testCaseInsensitiveArrayGet() {
|
||||
$array = new CaseInsensitiveArray();
|
||||
$this->assertTrue(is_object($array));
|
||||
$this->assertCount(0, $array);
|
||||
$this->assertNull($array[(string)rand()]);
|
||||
|
||||
$array['foo'] = 'bar';
|
||||
$this->assertNotEmpty($array);
|
||||
$this->assertCount(1, $array);
|
||||
}
|
||||
|
||||
public function testCaseInsensitiveArraySet() {
|
||||
function assertions($array, $count=1) {
|
||||
PHPUnit_Framework_Assert::assertCount($count, $array);
|
||||
PHPUnit_Framework_Assert::assertTrue($array['foo'] === 'bar');
|
||||
PHPUnit_Framework_Assert::assertTrue($array['Foo'] === 'bar');
|
||||
PHPUnit_Framework_Assert::assertTrue($array['FOo'] === 'bar');
|
||||
PHPUnit_Framework_Assert::assertTrue($array['FOO'] === 'bar');
|
||||
}
|
||||
|
||||
$array = new CaseInsensitiveArray();
|
||||
$array['foo'] = 'bar';
|
||||
assertions($array);
|
||||
|
||||
$array['Foo'] = 'bar';
|
||||
assertions($array);
|
||||
|
||||
$array['FOo'] = 'bar';
|
||||
assertions($array);
|
||||
|
||||
$array['FOO'] = 'bar';
|
||||
assertions($array);
|
||||
|
||||
$array['baz'] = 'qux';
|
||||
assertions($array, 2);
|
||||
}
|
||||
|
||||
public function testUserAgent() {
|
||||
$test = new Test();
|
||||
$test->curl->setUserAgent(Curl::USER_AGENT);
|
||||
$this->assertTrue($test->server('server', 'GET', array(
|
||||
'key' => 'HTTP_USER_AGENT',
|
||||
)) === Curl::USER_AGENT);
|
||||
}
|
||||
|
||||
public function testGet() {
|
||||
$test = new Test();
|
||||
$this->assertTrue($test->server('server', 'GET', array(
|
||||
'key' => 'REQUEST_METHOD',
|
||||
)) === 'GET');
|
||||
}
|
||||
|
||||
public function testPostRequestMethod() {
|
||||
$test = new Test();
|
||||
$this->assertTrue($test->server('server', 'POST', array(
|
||||
'key' => 'REQUEST_METHOD',
|
||||
)) === 'POST');
|
||||
}
|
||||
|
||||
public function testPostData() {
|
||||
$test = new Test();
|
||||
$this->assertTrue($test->server('post', 'POST', array(
|
||||
'key' => 'value',
|
||||
)) === 'key=value');
|
||||
}
|
||||
|
||||
public function testPostAssociativeArrayData() {
|
||||
$test = new Test();
|
||||
$this->assertTrue($test->server('post_multidimensional', 'POST', array(
|
||||
'username' => 'myusername',
|
||||
'password' => 'mypassword',
|
||||
'more_data' => array(
|
||||
'param1' => 'something',
|
||||
'param2' => 'other thing',
|
||||
'param3' => 123,
|
||||
'param4' => 3.14,
|
||||
),
|
||||
)) === 'username=myusername&password=mypassword&more_data%5Bparam1%5D=something&more_data%5Bparam2%5D=other%20thing&more_data%5Bparam3%5D=123&more_data%5Bparam4%5D=3.14');
|
||||
}
|
||||
|
||||
public function testPostMultidimensionalData() {
|
||||
$test = new Test();
|
||||
$this->assertTrue($test->server('post_multidimensional', 'POST', array(
|
||||
'key' => 'file',
|
||||
'file' => array(
|
||||
'wibble',
|
||||
'wubble',
|
||||
'wobble',
|
||||
),
|
||||
)) === 'key=file&file%5B%5D=wibble&file%5B%5D=wubble&file%5B%5D=wobble');
|
||||
}
|
||||
|
||||
public function testPostFilePathUpload() {
|
||||
$file_path = get_png();
|
||||
|
||||
$test = new Test();
|
||||
$this->assertTrue($test->server('post_file_path_upload', 'POST', array(
|
||||
'key' => 'image',
|
||||
'image' => '@' . $file_path,
|
||||
)) === 'image/png');
|
||||
|
||||
unlink($file_path);
|
||||
$this->assertFalse(file_exists($file_path));
|
||||
}
|
||||
|
||||
public function testPostCurlFileUpload() {
|
||||
if (class_exists('CURLFile')) {
|
||||
$file_path = get_png();
|
||||
|
||||
$test = new Test();
|
||||
$this->assertTrue($test->server('post_file_path_upload', 'POST', array(
|
||||
'key' => 'image',
|
||||
'image' => new CURLFile($file_path),
|
||||
)) === 'image/png');
|
||||
|
||||
unlink($file_path);
|
||||
$this->assertFalse(file_exists($file_path));
|
||||
}
|
||||
}
|
||||
|
||||
public function testPutRequestMethod() {
|
||||
$test = new Test();
|
||||
$this->assertTrue($test->server('request_method', 'PUT') === 'PUT');
|
||||
}
|
||||
|
||||
public function testPutData() {
|
||||
$test = new Test();
|
||||
$this->assertTrue($test->server('put', 'PUT', array(
|
||||
'key' => 'value',
|
||||
)) === 'key=value');
|
||||
}
|
||||
|
||||
public function testPutFileHandle() {
|
||||
$png = create_png();
|
||||
$tmp_file = create_tmp_file($png);
|
||||
|
||||
$test = new Test();
|
||||
$test->curl->setHeader('X-DEBUG-TEST', 'put_file_handle');
|
||||
$test->curl->setOpt(CURLOPT_PUT, true);
|
||||
$test->curl->setOpt(CURLOPT_INFILE, $tmp_file);
|
||||
$test->curl->setOpt(CURLOPT_INFILESIZE, strlen($png));
|
||||
$test->curl->put(Test::TEST_URL);
|
||||
|
||||
fclose($tmp_file);
|
||||
|
||||
$this->assertTrue($test->curl->response === 'image/png');
|
||||
}
|
||||
|
||||
public function testPatchRequestMethod() {
|
||||
$test = new Test();
|
||||
$this->assertTrue($test->server('request_method', 'PATCH') === 'PATCH');
|
||||
}
|
||||
|
||||
public function testDelete() {
|
||||
$test = new Test();
|
||||
$this->assertTrue($test->server('server', 'DELETE', array(
|
||||
'key' => 'REQUEST_METHOD',
|
||||
)) === 'DELETE');
|
||||
|
||||
$test = new Test();
|
||||
$this->assertTrue($test->server('delete', 'DELETE', array(
|
||||
'test' => 'delete',
|
||||
'key' => 'test',
|
||||
)) === 'delete');
|
||||
}
|
||||
|
||||
public function testHeadRequestMethod() {
|
||||
$test = new Test();
|
||||
$test->server('request_method', 'HEAD', array(
|
||||
'key' => 'REQUEST_METHOD',
|
||||
));
|
||||
$this->assertEquals($test->curl->response_headers['X-REQUEST-METHOD'], 'HEAD');
|
||||
$this->assertEmpty($test->curl->response);
|
||||
}
|
||||
|
||||
public function testOptionsRequestMethod() {
|
||||
$test = new Test();
|
||||
$test->server('request_method', 'OPTIONS', array(
|
||||
'key' => 'REQUEST_METHOD',
|
||||
));
|
||||
$this->assertEquals($test->curl->response_headers['X-REQUEST-METHOD'], 'OPTIONS');
|
||||
}
|
||||
|
||||
public function testBasicHttpAuth401Unauthorized() {
|
||||
$test = new Test();
|
||||
$this->assertTrue($test->server('http_basic_auth', 'GET') === 'canceled');
|
||||
}
|
||||
|
||||
public function testBasicHttpAuthSuccess() {
|
||||
$username = 'myusername';
|
||||
$password = 'mypassword';
|
||||
$test = new Test();
|
||||
$test->curl->setBasicAuthentication($username, $password);
|
||||
$test->server('http_basic_auth', 'GET');
|
||||
$json = $test->curl->response;
|
||||
$this->assertTrue($json->username === $username);
|
||||
$this->assertTrue($json->password === $password);
|
||||
}
|
||||
|
||||
public function testReferrer() {
|
||||
$test = new Test();
|
||||
$test->curl->setReferrer('myreferrer');
|
||||
$this->assertTrue($test->server('server', 'GET', array(
|
||||
'key' => 'HTTP_REFERER',
|
||||
)) === 'myreferrer');
|
||||
}
|
||||
|
||||
public function testCookies() {
|
||||
$test = new Test();
|
||||
$test->curl->setCookie('mycookie', 'yum');
|
||||
$this->assertTrue($test->server('cookie', 'GET', array(
|
||||
'key' => 'mycookie',
|
||||
)) === 'yum');
|
||||
}
|
||||
|
||||
public function testCookieFile() {
|
||||
$cookie_file = dirname(__FILE__) . '/cookies.txt';
|
||||
$cookie_data = implode("\t", array(
|
||||
'127.0.0.1', // domain
|
||||
'FALSE', // tailmatch
|
||||
'/', // path
|
||||
'FALSE', // secure
|
||||
'0', // expires
|
||||
'mycookie', // name
|
||||
'yum', // value
|
||||
));
|
||||
file_put_contents($cookie_file, $cookie_data);
|
||||
|
||||
$test = new Test();
|
||||
$test->curl->setCookieFile($cookie_file);
|
||||
$this->assertTrue($test->server('cookie', 'GET', array(
|
||||
'key' => 'mycookie',
|
||||
)) === 'yum');
|
||||
|
||||
unlink($cookie_file);
|
||||
$this->assertFalse(file_exists($cookie_file));
|
||||
}
|
||||
|
||||
public function testCookieJar() {
|
||||
$cookie_file = dirname(__FILE__) . '/cookies.txt';
|
||||
|
||||
$test = new Test();
|
||||
$test->curl->setCookieJar($cookie_file);
|
||||
$test->server('cookiejar', 'GET');
|
||||
$test->curl->close();
|
||||
|
||||
$this->assertTrue(!(strpos(file_get_contents($cookie_file), "\t" . 'mycookie' . "\t" . 'yum') === false));
|
||||
unlink($cookie_file);
|
||||
$this->assertFalse(file_exists($cookie_file));
|
||||
}
|
||||
|
||||
public function testMultipleCookieResponse() {
|
||||
$expected_response = 'cookie1=scrumptious,cookie2=mouthwatering';
|
||||
|
||||
// github.com/facebook/hhvm/issues/2345
|
||||
if (defined('HHVM_VERSION')) {
|
||||
$expected_response = 'cookie2=mouthwatering,cookie1=scrumptious';
|
||||
}
|
||||
|
||||
$test = new Test();
|
||||
$test->server('multiple_cookie', 'GET');
|
||||
$this->assertEquals($test->curl->response_headers['Set-Cookie'], $expected_response);
|
||||
}
|
||||
|
||||
public function testError() {
|
||||
$test = new Test();
|
||||
$test->curl->setOpt(CURLOPT_CONNECTTIMEOUT_MS, 4000);
|
||||
$test->curl->get(Test::ERROR_URL);
|
||||
$this->assertTrue($test->curl->error);
|
||||
$this->assertTrue($test->curl->curl_error);
|
||||
$this->assertTrue($test->curl->curl_error_code === CURLE_OPERATION_TIMEOUTED);
|
||||
}
|
||||
|
||||
public function testErrorMessage() {
|
||||
$test = new Test();
|
||||
$test->server('error_message', 'GET');
|
||||
|
||||
$expected_response = 'HTTP/1.1 401 Unauthorized';
|
||||
if (defined('HHVM_VERSION')) {
|
||||
$expected_response = 'HTTP/1.1 401';
|
||||
}
|
||||
|
||||
$this->assertEquals($test->curl->error_message, $expected_response);
|
||||
}
|
||||
|
||||
public function testHeaders() {
|
||||
$test = new Test();
|
||||
$test->curl->setHeader('Content-Type', 'application/json');
|
||||
$test->curl->setHeader('X-Requested-With', 'XMLHttpRequest');
|
||||
$test->curl->setHeader('Accept', 'application/json');
|
||||
$this->assertTrue($test->server('server', 'GET', array(
|
||||
'key' => 'HTTP_CONTENT_TYPE', // OR "CONTENT_TYPE".
|
||||
)) === 'application/json');
|
||||
$this->assertTrue($test->server('server', 'GET', array(
|
||||
'key' => 'HTTP_X_REQUESTED_WITH',
|
||||
)) === 'XMLHttpRequest');
|
||||
$this->assertTrue($test->server('server', 'GET', array(
|
||||
'key' => 'HTTP_ACCEPT',
|
||||
)) === 'application/json');
|
||||
}
|
||||
|
||||
public function testHeaderCaseSensitivity() {
|
||||
$content_type = 'application/json';
|
||||
$test = new Test();
|
||||
$test->curl->setHeader('Content-Type', $content_type);
|
||||
$test->server('response_header', 'GET');
|
||||
|
||||
$request_headers = $test->curl->request_headers;
|
||||
$response_headers = $test->curl->response_headers;
|
||||
|
||||
$this->assertEquals($request_headers['Content-Type'], $content_type);
|
||||
$this->assertEquals($request_headers['content-type'], $content_type);
|
||||
$this->assertEquals($request_headers['CONTENT-TYPE'], $content_type);
|
||||
$this->assertEquals($request_headers['cOnTeNt-TyPe'], $content_type);
|
||||
|
||||
$etag = $response_headers['ETag'];
|
||||
$this->assertEquals($response_headers['ETAG'], $etag);
|
||||
$this->assertEquals($response_headers['etag'], $etag);
|
||||
$this->assertEquals($response_headers['eTAG'], $etag);
|
||||
$this->assertEquals($response_headers['eTaG'], $etag);
|
||||
}
|
||||
|
||||
public function testRequestURL() {
|
||||
$test = new Test();
|
||||
$this->assertFalse(substr($test->server('request_uri', 'GET'), -1) === '?');
|
||||
$test = new Test();
|
||||
$this->assertFalse(substr($test->server('request_uri', 'POST'), -1) === '?');
|
||||
$test = new Test();
|
||||
$this->assertFalse(substr($test->server('request_uri', 'PUT'), -1) === '?');
|
||||
$test = new Test();
|
||||
$this->assertFalse(substr($test->server('request_uri', 'PATCH'), -1) === '?');
|
||||
$test = new Test();
|
||||
$this->assertFalse(substr($test->server('request_uri', 'DELETE'), -1) === '?');
|
||||
}
|
||||
|
||||
public function testNestedData() {
|
||||
$test = new Test();
|
||||
$data = array(
|
||||
'username' => 'myusername',
|
||||
'password' => 'mypassword',
|
||||
'more_data' => array(
|
||||
'param1' => 'something',
|
||||
'param2' => 'other thing',
|
||||
'another' => array(
|
||||
'extra' => 'level',
|
||||
'because' => 'I need it',
|
||||
),
|
||||
),
|
||||
);
|
||||
$this->assertTrue(
|
||||
$test->server('post', 'POST', $data) === http_build_query($data)
|
||||
);
|
||||
}
|
||||
|
||||
public function testPostContentTypes() {
|
||||
$test = new Test();
|
||||
$test->server('server', 'POST', 'foo=bar');
|
||||
$this->assertEquals($test->curl->request_headers['Content-Type'], 'application/x-www-form-urlencoded');
|
||||
|
||||
$test = new Test();
|
||||
$test->server('server', 'POST', array(
|
||||
'foo' => 'bar',
|
||||
));
|
||||
$this->assertEquals($test->curl->request_headers['Expect'], '100-continue');
|
||||
preg_match('/^multipart\/form-data; boundary=/', $test->curl->request_headers['Content-Type'], $content_type);
|
||||
$this->assertTrue(!empty($content_type));
|
||||
}
|
||||
|
||||
public function testJSONResponse() {
|
||||
function assertion($key, $value) {
|
||||
$test = new Test();
|
||||
$test->server('json_response', 'POST', array(
|
||||
'key' => $key,
|
||||
'value' => $value,
|
||||
));
|
||||
|
||||
$response = $test->curl->response;
|
||||
PHPUnit_Framework_Assert::assertNotNull($response);
|
||||
PHPUnit_Framework_Assert::assertNull($response->null);
|
||||
PHPUnit_Framework_Assert::assertTrue($response->true);
|
||||
PHPUnit_Framework_Assert::assertFalse($response->false);
|
||||
PHPUnit_Framework_Assert::assertTrue(is_int($response->integer));
|
||||
PHPUnit_Framework_Assert::assertTrue(is_float($response->float));
|
||||
PHPUnit_Framework_Assert::assertEmpty($response->empty);
|
||||
PHPUnit_Framework_Assert::assertTrue(is_string($response->string));
|
||||
}
|
||||
|
||||
assertion('Content-Type', 'application/json; charset=utf-8');
|
||||
assertion('content-type', 'application/json; charset=utf-8');
|
||||
assertion('Content-Type', 'application/json');
|
||||
assertion('content-type', 'application/json');
|
||||
assertion('CONTENT-TYPE', 'application/json');
|
||||
assertion('CONTENT-TYPE', 'APPLICATION/JSON');
|
||||
}
|
||||
|
||||
public function testArrayToStringConversion() {
|
||||
$test = new Test();
|
||||
$test->server('post', 'POST', array(
|
||||
'foo' => 'bar',
|
||||
'baz' => array(
|
||||
),
|
||||
));
|
||||
$this->assertTrue($test->curl->response === 'foo=bar&baz=');
|
||||
|
||||
$test = new Test();
|
||||
$test->server('post', 'POST', array(
|
||||
'foo' => 'bar',
|
||||
'baz' => array(
|
||||
'qux' => array(
|
||||
),
|
||||
),
|
||||
));
|
||||
$this->assertTrue(urldecode($test->curl->response) ===
|
||||
'foo=bar&baz[qux]='
|
||||
);
|
||||
|
||||
$test = new Test();
|
||||
$test->server('post', 'POST', array(
|
||||
'foo' => 'bar',
|
||||
'baz' => array(
|
||||
'qux' => array(
|
||||
),
|
||||
'wibble' => 'wobble',
|
||||
),
|
||||
));
|
||||
$this->assertTrue(urldecode($test->curl->response) ===
|
||||
'foo=bar&baz[qux]=&baz[wibble]=wobble'
|
||||
);
|
||||
}
|
||||
|
||||
public function testParallelRequests() {
|
||||
$test = new Test();
|
||||
$curl = $test->curl;
|
||||
$curl->beforeSend(function($instance) {
|
||||
$instance->setHeader('X-DEBUG-TEST', 'request_uri');
|
||||
});
|
||||
$curl->get(array(
|
||||
Test::TEST_URL . 'a/',
|
||||
Test::TEST_URL . 'b/',
|
||||
Test::TEST_URL . 'c/',
|
||||
), array(
|
||||
'foo' => 'bar',
|
||||
));
|
||||
|
||||
$len = strlen('/a/?foo=bar');
|
||||
$this->assertTrue(substr($curl->curls['0']->response, - $len) === '/a/?foo=bar');
|
||||
$this->assertTrue(substr($curl->curls['1']->response, - $len) === '/b/?foo=bar');
|
||||
$this->assertTrue(substr($curl->curls['2']->response, - $len) === '/c/?foo=bar');
|
||||
}
|
||||
|
||||
public function testParallelSetOptions() {
|
||||
$test = new Test();
|
||||
$curl = $test->curl;
|
||||
$curl->setHeader('X-DEBUG-TEST', 'server');
|
||||
$curl->setOpt(CURLOPT_USERAGENT, 'useragent');
|
||||
$curl->complete(function($instance) {
|
||||
PHPUnit_Framework_Assert::assertTrue($instance->response === 'useragent');
|
||||
});
|
||||
$curl->get(array(
|
||||
Test::TEST_URL,
|
||||
), array(
|
||||
'key' => 'HTTP_USER_AGENT',
|
||||
));
|
||||
}
|
||||
|
||||
public function testSuccessCallback() {
|
||||
$success_called = false;
|
||||
$error_called = false;
|
||||
$complete_called = false;
|
||||
|
||||
$test = new Test();
|
||||
$curl = $test->curl;
|
||||
$curl->setHeader('X-DEBUG-TEST', 'get');
|
||||
|
||||
$curl->success(function($instance) use (&$success_called, &$error_called, &$complete_called) {
|
||||
PHPUnit_Framework_Assert::assertInstanceOf('Curl', $instance);
|
||||
PHPUnit_Framework_Assert::assertFalse($success_called);
|
||||
PHPUnit_Framework_Assert::assertFalse($error_called);
|
||||
PHPUnit_Framework_Assert::assertFalse($complete_called);
|
||||
$success_called = true;
|
||||
});
|
||||
$curl->error(function($instance) use (&$success_called, &$error_called, &$complete_called, &$curl) {
|
||||
PHPUnit_Framework_Assert::assertInstanceOf('Curl', $instance);
|
||||
PHPUnit_Framework_Assert::assertFalse($success_called);
|
||||
PHPUnit_Framework_Assert::assertFalse($error_called);
|
||||
PHPUnit_Framework_Assert::assertFalse($complete_called);
|
||||
$error_called = true;
|
||||
});
|
||||
$curl->complete(function($instance) use (&$success_called, &$error_called, &$complete_called) {
|
||||
PHPUnit_Framework_Assert::assertInstanceOf('Curl', $instance);
|
||||
PHPUnit_Framework_Assert::assertTrue($success_called);
|
||||
PHPUnit_Framework_Assert::assertFalse($error_called);
|
||||
PHPUnit_Framework_Assert::assertFalse($complete_called);
|
||||
$complete_called = true;
|
||||
});
|
||||
|
||||
$curl->get(Test::TEST_URL);
|
||||
|
||||
$this->assertTrue($success_called);
|
||||
$this->assertFalse($error_called);
|
||||
$this->assertTrue($complete_called);
|
||||
}
|
||||
|
||||
public function testParallelSuccessCallback() {
|
||||
$success_called = false;
|
||||
$error_called = false;
|
||||
$complete_called = false;
|
||||
|
||||
$success_called_once = false;
|
||||
$error_called_once = false;
|
||||
$complete_called_once = false;
|
||||
|
||||
$test = new Test();
|
||||
$curl = $test->curl;
|
||||
$curl->setHeader('X-DEBUG-TEST', 'get');
|
||||
|
||||
$curl->success(function($instance) use (&$success_called,
|
||||
&$error_called,
|
||||
&$complete_called,
|
||||
&$success_called_once) {
|
||||
PHPUnit_Framework_Assert::assertInstanceOf('Curl', $instance);
|
||||
PHPUnit_Framework_Assert::assertFalse($success_called);
|
||||
PHPUnit_Framework_Assert::assertFalse($error_called);
|
||||
PHPUnit_Framework_Assert::assertFalse($complete_called);
|
||||
$success_called = true;
|
||||
$success_called_once = true;
|
||||
});
|
||||
$curl->error(function($instance) use (&$success_called,
|
||||
&$error_called,
|
||||
&$complete_called,
|
||||
&$curl,
|
||||
&$error_called_once) {
|
||||
PHPUnit_Framework_Assert::assertInstanceOf('Curl', $instance);
|
||||
PHPUnit_Framework_Assert::assertFalse($success_called);
|
||||
PHPUnit_Framework_Assert::assertFalse($error_called);
|
||||
PHPUnit_Framework_Assert::assertFalse($complete_called);
|
||||
$error_called = true;
|
||||
$error_called_once = true;
|
||||
});
|
||||
$curl->complete(function($instance) use (&$success_called,
|
||||
&$error_called,
|
||||
&$complete_called,
|
||||
&$complete_called_once) {
|
||||
PHPUnit_Framework_Assert::assertInstanceOf('Curl', $instance);
|
||||
PHPUnit_Framework_Assert::assertTrue($success_called);
|
||||
PHPUnit_Framework_Assert::assertFalse($error_called);
|
||||
PHPUnit_Framework_Assert::assertFalse($complete_called);
|
||||
$complete_called = true;
|
||||
$complete_called_once = true;
|
||||
|
||||
PHPUnit_Framework_Assert::assertTrue($success_called);
|
||||
PHPUnit_Framework_Assert::assertFalse($error_called);
|
||||
PHPUnit_Framework_Assert::assertTrue($complete_called);
|
||||
|
||||
$success_called = false;
|
||||
$error_called = false;
|
||||
$complete_called = false;
|
||||
});
|
||||
|
||||
$curl->get(array(
|
||||
Test::TEST_URL . 'a/',
|
||||
Test::TEST_URL . 'b/',
|
||||
Test::TEST_URL . 'c/',
|
||||
));
|
||||
|
||||
PHPUnit_Framework_Assert::assertTrue($success_called_once || $error_called_once);
|
||||
PHPUnit_Framework_Assert::assertTrue($complete_called_once);
|
||||
}
|
||||
|
||||
public function testErrorCallback() {
|
||||
$success_called = false;
|
||||
$error_called = false;
|
||||
$complete_called = false;
|
||||
|
||||
$test = new Test();
|
||||
$curl = $test->curl;
|
||||
$curl->setHeader('X-DEBUG-TEST', 'get');
|
||||
$curl->setOpt(CURLOPT_CONNECTTIMEOUT_MS, 2000);
|
||||
|
||||
$curl->success(function($instance) use (&$success_called, &$error_called, &$complete_called) {
|
||||
PHPUnit_Framework_Assert::assertInstanceOf('Curl', $instance);
|
||||
PHPUnit_Framework_Assert::assertFalse($success_called);
|
||||
PHPUnit_Framework_Assert::assertFalse($error_called);
|
||||
PHPUnit_Framework_Assert::assertFalse($complete_called);
|
||||
$success_called = true;
|
||||
});
|
||||
$curl->error(function($instance) use (&$success_called, &$error_called, &$complete_called, &$curl) {
|
||||
PHPUnit_Framework_Assert::assertInstanceOf('Curl', $instance);
|
||||
PHPUnit_Framework_Assert::assertFalse($success_called);
|
||||
PHPUnit_Framework_Assert::assertFalse($error_called);
|
||||
PHPUnit_Framework_Assert::assertFalse($complete_called);
|
||||
$error_called = true;
|
||||
});
|
||||
$curl->complete(function($instance) use (&$success_called, &$error_called, &$complete_called) {
|
||||
PHPUnit_Framework_Assert::assertInstanceOf('Curl', $instance);
|
||||
PHPUnit_Framework_Assert::assertFalse($success_called);
|
||||
PHPUnit_Framework_Assert::assertTrue($error_called);
|
||||
PHPUnit_Framework_Assert::assertFalse($complete_called);
|
||||
$complete_called = true;
|
||||
});
|
||||
|
||||
$curl->get(Test::ERROR_URL);
|
||||
|
||||
$this->assertFalse($success_called);
|
||||
$this->assertTrue($error_called);
|
||||
$this->assertTrue($complete_called);
|
||||
}
|
||||
|
||||
public function testClose() {
|
||||
$test = new Test();
|
||||
$curl = $test->curl;
|
||||
$curl->setHeader('X-DEBUG-TEST', 'post');
|
||||
$curl->post(Test::TEST_URL);
|
||||
$this->assertTrue(is_resource($curl->curl));
|
||||
$curl->close();
|
||||
$this->assertFalse(is_resource($curl->curl));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException PHPUnit_Framework_Error_Warning
|
||||
*/
|
||||
public function testRequiredOptionCurlInfoHeaderOutEmitsWarning() {
|
||||
$curl = new Curl();
|
||||
$curl->setOpt(CURLINFO_HEADER_OUT, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException PHPUnit_Framework_Error_Warning
|
||||
*/
|
||||
public function testRequiredOptionCurlOptHeaderEmitsWarning() {
|
||||
$curl = new Curl();
|
||||
$curl->setOpt(CURLOPT_HEADER, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException PHPUnit_Framework_Error_Warning
|
||||
*/
|
||||
public function testRequiredOptionCurlOptReturnTransferEmitsWarning() {
|
||||
$curl = new Curl();
|
||||
$curl->setOpt(CURLOPT_RETURNTRANSFER, false);
|
||||
}
|
||||
|
||||
public function testRequestMethodSuccessiveGetRequests() {
|
||||
$test = new Test();
|
||||
test($test, 'GET', 'POST');
|
||||
test($test, 'GET', 'PUT');
|
||||
test($test, 'GET', 'PATCH');
|
||||
test($test, 'GET', 'DELETE');
|
||||
test($test, 'GET', 'HEAD');
|
||||
test($test, 'GET', 'OPTIONS');
|
||||
}
|
||||
|
||||
public function testRequestMethodSuccessivePostRequests() {
|
||||
$test = new Test();
|
||||
test($test, 'POST', 'GET');
|
||||
test($test, 'POST', 'PUT');
|
||||
test($test, 'POST', 'PATCH');
|
||||
test($test, 'POST', 'DELETE');
|
||||
test($test, 'POST', 'HEAD');
|
||||
test($test, 'POST', 'OPTIONS');
|
||||
}
|
||||
|
||||
public function testRequestMethodSuccessivePutRequests() {
|
||||
$test = new Test();
|
||||
test($test, 'PUT', 'GET');
|
||||
test($test, 'PUT', 'POST');
|
||||
test($test, 'PUT', 'PATCH');
|
||||
test($test, 'PUT', 'DELETE');
|
||||
test($test, 'PUT', 'HEAD');
|
||||
test($test, 'PUT', 'OPTIONS');
|
||||
}
|
||||
|
||||
public function testRequestMethodSuccessivePatchRequests() {
|
||||
$test = new Test();
|
||||
test($test, 'PATCH', 'GET');
|
||||
test($test, 'PATCH', 'POST');
|
||||
test($test, 'PATCH', 'PUT');
|
||||
test($test, 'PATCH', 'DELETE');
|
||||
test($test, 'PATCH', 'HEAD');
|
||||
test($test, 'PATCH', 'OPTIONS');
|
||||
}
|
||||
|
||||
public function testRequestMethodSuccessiveDeleteRequests() {
|
||||
$test = new Test();
|
||||
test($test, 'DELETE', 'GET');
|
||||
test($test, 'DELETE', 'POST');
|
||||
test($test, 'DELETE', 'PUT');
|
||||
test($test, 'DELETE', 'PATCH');
|
||||
test($test, 'DELETE', 'HEAD');
|
||||
test($test, 'DELETE', 'OPTIONS');
|
||||
}
|
||||
|
||||
public function testRequestMethodSuccessiveHeadRequests() {
|
||||
$test = new Test();
|
||||
test($test, 'HEAD', 'GET');
|
||||
test($test, 'HEAD', 'POST');
|
||||
test($test, 'HEAD', 'PUT');
|
||||
test($test, 'HEAD', 'PATCH');
|
||||
test($test, 'HEAD', 'DELETE');
|
||||
test($test, 'HEAD', 'OPTIONS');
|
||||
}
|
||||
|
||||
public function testRequestMethodSuccessiveOptionsRequests() {
|
||||
$test = new Test();
|
||||
test($test, 'OPTIONS', 'GET');
|
||||
test($test, 'OPTIONS', 'POST');
|
||||
test($test, 'OPTIONS', 'PUT');
|
||||
test($test, 'OPTIONS', 'PATCH');
|
||||
test($test, 'OPTIONS', 'DELETE');
|
||||
test($test, 'OPTIONS', 'HEAD');
|
||||
}
|
||||
}
|
47
web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/helper.inc.php
vendored
Normal file
47
web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/helper.inc.php
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
class Test {
|
||||
const TEST_URL = 'http://127.0.0.1:8000/';
|
||||
const ERROR_URL = 'https://1.2.3.4/';
|
||||
|
||||
function __construct() {
|
||||
$this->curl = new Curl();
|
||||
$this->curl->setOpt(CURLOPT_SSL_VERIFYPEER, false);
|
||||
$this->curl->setOpt(CURLOPT_SSL_VERIFYHOST, false);
|
||||
}
|
||||
|
||||
function server($test, $request_method, $data=array()) {
|
||||
$this->curl->setHeader('X-DEBUG-TEST', $test);
|
||||
$request_method = strtolower($request_method);
|
||||
$this->curl->$request_method(self::TEST_URL, $data);
|
||||
return $this->curl->response;
|
||||
}
|
||||
}
|
||||
|
||||
function test($instance, $before, $after) {
|
||||
$instance->server('request_method', $before);
|
||||
PHPUnit_Framework_Assert::assertEquals($instance->curl->response_headers['X-REQUEST-METHOD'], $before);
|
||||
$instance->server('request_method', $after);
|
||||
PHPUnit_Framework_Assert::assertEquals($instance->curl->response_headers['X-REQUEST-METHOD'], $after);
|
||||
}
|
||||
|
||||
function create_png() {
|
||||
// PNG image data, 1 x 1, 1-bit colormap, non-interlaced
|
||||
ob_start();
|
||||
imagepng(imagecreatefromstring(base64_decode('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7')));
|
||||
$raw_image = ob_get_contents();
|
||||
ob_end_clean();
|
||||
return $raw_image;
|
||||
}
|
||||
|
||||
function create_tmp_file($data) {
|
||||
$tmp_file = tmpfile();
|
||||
fwrite($tmp_file, $data);
|
||||
rewind($tmp_file);
|
||||
return $tmp_file;
|
||||
}
|
||||
|
||||
function get_png() {
|
||||
$tmp_filename = tempnam('/tmp', 'php-curl-class.');
|
||||
file_put_contents($tmp_filename, create_png());
|
||||
return $tmp_filename;
|
||||
}
|
1
web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/index.php
vendored
Symbolic link
1
web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/index.php
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
server.php
|
7
web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/server.hdf
vendored
Normal file
7
web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/server.hdf
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
Log {
|
||||
Level = Verbose
|
||||
}
|
||||
|
||||
Server {
|
||||
DefaultDocument = index.php
|
||||
}
|
132
web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/server.php
vendored
Normal file
132
web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/server.php
vendored
Normal file
@ -0,0 +1,132 @@
|
||||
<?php
|
||||
$http_raw_post_data = file_get_contents('php://input');
|
||||
$_PUT = array();
|
||||
$_PATCH = array();
|
||||
|
||||
$request_method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : '';
|
||||
$content_type = isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : '';
|
||||
$data_values = $_GET;
|
||||
if ($request_method === 'POST') {
|
||||
$data_values = $_POST;
|
||||
}
|
||||
else if ($request_method === 'PUT') {
|
||||
if (strpos($content_type, 'application/x-www-form-urlencoded') === 0) {
|
||||
parse_str($http_raw_post_data, $_PUT);
|
||||
$data_values = $_PUT;
|
||||
}
|
||||
}
|
||||
else if ($request_method === 'PATCH') {
|
||||
if (strpos($content_type, 'application/x-www-form-urlencoded') === 0) {
|
||||
parse_str($http_raw_post_data, $_PATCH);
|
||||
$data_values = $_PATCH;
|
||||
}
|
||||
}
|
||||
|
||||
$test = isset($_SERVER['HTTP_X_DEBUG_TEST']) ? $_SERVER['HTTP_X_DEBUG_TEST'] : '';
|
||||
$key = isset($data_values['key']) ? $data_values['key'] : '';
|
||||
|
||||
if ($test == 'http_basic_auth') {
|
||||
if (!isset($_SERVER['PHP_AUTH_USER'])) {
|
||||
header('WWW-Authenticate: Basic realm="My Realm"');
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
echo 'canceled';
|
||||
exit;
|
||||
}
|
||||
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(array(
|
||||
'username' => $_SERVER['PHP_AUTH_USER'],
|
||||
'password' => $_SERVER['PHP_AUTH_PW'],
|
||||
));
|
||||
exit;
|
||||
}
|
||||
else if ($test === 'get') {
|
||||
echo http_build_query($_GET);
|
||||
exit;
|
||||
}
|
||||
else if ($test === 'post') {
|
||||
echo http_build_query($_POST);
|
||||
exit;
|
||||
}
|
||||
else if ($test === 'put') {
|
||||
echo $http_raw_post_data;
|
||||
exit;
|
||||
}
|
||||
else if ($test === 'post_multidimensional') {
|
||||
echo $http_raw_post_data;
|
||||
exit;
|
||||
}
|
||||
else if ($test === 'post_file_path_upload') {
|
||||
echo mime_content_type($_FILES[$key]['tmp_name']);
|
||||
exit;
|
||||
}
|
||||
else if ($test === 'put_file_handle') {
|
||||
$tmp_filename = tempnam('/tmp', 'php-curl-class.');
|
||||
file_put_contents($tmp_filename, $http_raw_post_data);
|
||||
echo mime_content_type($tmp_filename);
|
||||
unlink($tmp_filename);
|
||||
exit;
|
||||
}
|
||||
else if ($test === 'request_method') {
|
||||
header('X-REQUEST-METHOD: ' . $request_method);
|
||||
echo $request_method;
|
||||
exit;
|
||||
}
|
||||
else if ($test === 'request_uri') {
|
||||
echo $_SERVER['REQUEST_URI'];
|
||||
exit;
|
||||
}
|
||||
else if ($test === 'cookiejar') {
|
||||
setcookie('mycookie', 'yum');
|
||||
exit;
|
||||
}
|
||||
else if ($test === 'multiple_cookie') {
|
||||
setcookie('cookie1', 'scrumptious');
|
||||
setcookie('cookie2', 'mouthwatering');
|
||||
exit;
|
||||
}
|
||||
else if ($test === 'response_header') {
|
||||
header('Content-Type: application/json');
|
||||
header('ETag: ' . md5('worldpeace'));
|
||||
exit;
|
||||
}
|
||||
else if ($test === 'json_response') {
|
||||
$key = $_POST['key'];
|
||||
$value = $_POST['value'];
|
||||
header($key . ': ' . $value);
|
||||
echo json_encode(array(
|
||||
'null' => null,
|
||||
'true' => true,
|
||||
'false' => false,
|
||||
'integer' => 1,
|
||||
'float' => 3.14,
|
||||
'empty' => '',
|
||||
'string' => 'string',
|
||||
));
|
||||
exit;
|
||||
}
|
||||
else if ($test === 'error_message') {
|
||||
if (function_exists('http_response_code')) {
|
||||
http_response_code(401);
|
||||
}
|
||||
else {
|
||||
header('HTTP/1.1 401 Unauthorized');
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
header('Content-Type: text/plain');
|
||||
|
||||
$data_mapping = array(
|
||||
'cookie' => '_COOKIE',
|
||||
'delete' => '_GET',
|
||||
'get' => '_GET',
|
||||
'patch' => '_PATCH',
|
||||
'post' => '_POST',
|
||||
'put' => '_PUT',
|
||||
'server' => '_SERVER',
|
||||
);
|
||||
|
||||
$data = $$data_mapping[$test];
|
||||
$value = isset($data[$key]) ? $data[$key] : '';
|
||||
echo $value;
|
8
web/app/vendor/php-curl-class/php-curl-class/tests/phpunit.xml
vendored
Normal file
8
web/app/vendor/php-curl-class/php-curl-class/tests/phpunit.xml
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
<phpunit>
|
||||
<testsuite name="PHPCurlClass">
|
||||
<directory>.</directory>
|
||||
</testsuite>
|
||||
<logging>
|
||||
<log type="coverage-text" target="php://stdout" showUncoveredFiles="false" />
|
||||
</logging>
|
||||
</phpunit>
|
4
web/app/vendor/php-curl-class/php-curl-class/tests/run.sh
vendored
Normal file
4
web/app/vendor/php-curl-class/php-curl-class/tests/run.sh
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
php -S 127.0.0.1:8000 -t PHPCurlClass/ &
|
||||
pid=$!
|
||||
phpunit --configuration phpunit.xml
|
||||
kill $pid
|
1
web/app/vendor/php-curl-class/php-curl-class/tests/syntax.sh
vendored
Normal file
1
web/app/vendor/php-curl-class/php-curl-class/tests/syntax.sh
vendored
Normal file
@ -0,0 +1 @@
|
||||
phpcs --standard=PSR2 ../src/Curl.class.php
|
3
web/app/vendor/symfony/deprecation-contracts/.gitignore
vendored
Normal file
3
web/app/vendor/symfony/deprecation-contracts/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
vendor/
|
||||
composer.lock
|
||||
phpunit.xml
|
5
web/app/vendor/symfony/deprecation-contracts/CHANGELOG.md
vendored
Normal file
5
web/app/vendor/symfony/deprecation-contracts/CHANGELOG.md
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
The changelog is maintained for all Symfony contracts at the following URL:
|
||||
https://github.com/symfony/contracts/blob/main/CHANGELOG.md
|
19
web/app/vendor/symfony/deprecation-contracts/LICENSE
vendored
Normal file
19
web/app/vendor/symfony/deprecation-contracts/LICENSE
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
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.
|
26
web/app/vendor/symfony/deprecation-contracts/README.md
vendored
Normal file
26
web/app/vendor/symfony/deprecation-contracts/README.md
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
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.
|
35
web/app/vendor/symfony/deprecation-contracts/composer.json
vendored
Normal file
35
web/app/vendor/symfony/deprecation-contracts/composer.json
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
27
web/app/vendor/symfony/deprecation-contracts/function.php
vendored
Normal file
27
web/app/vendor/symfony/deprecation-contracts/function.php
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* 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 <p@tchwork.com>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
5
web/app/vendor/symfony/finder/CHANGELOG.md
vendored
5
web/app/vendor/symfony/finder/CHANGELOG.md
vendored
@ -1,11 +1,6 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
6.0
|
||||
---
|
||||
|
||||
* Remove `Comparator::setTarget()` and `Comparator::setOperator()`
|
||||
|
||||
5.4.0
|
||||
-----
|
||||
|
||||
|
@ -16,47 +16,102 @@ namespace Symfony\Component\Finder\Comparator;
|
||||
*/
|
||||
class Comparator
|
||||
{
|
||||
private string $target;
|
||||
private string $operator;
|
||||
private $target;
|
||||
private $operator = '==';
|
||||
|
||||
public function __construct(string $target, string $operator = '==')
|
||||
public function __construct(string $target = null, string $operator = '==')
|
||||
{
|
||||
if (!\in_array($operator, ['>', '<', '>=', '<=', '==', '!='])) {
|
||||
throw new \InvalidArgumentException(sprintf('Invalid operator "%s".', $operator));
|
||||
if (null === $target) {
|
||||
trigger_deprecation('symfony/finder', '5.4', 'Constructing a "%s" without setting "$target" is deprecated.', __CLASS__);
|
||||
}
|
||||
|
||||
$this->target = $target;
|
||||
$this->operator = $operator;
|
||||
$this->doSetOperator($operator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the target value.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTarget(): 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the comparison operator.
|
||||
* @deprecated set the target via the constructor instead
|
||||
*/
|
||||
public function getOperator(): string
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests against the target.
|
||||
* Sets the comparison operator.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @deprecated set the operator via the constructor instead
|
||||
*/
|
||||
public function test(mixed $test): bool
|
||||
public function setOperator(string $operator)
|
||||
{
|
||||
return match ($this->operator) {
|
||||
'>' => $test > $this->target,
|
||||
'>=' => $test >= $this->target,
|
||||
'<' => $test < $this->target,
|
||||
'<=' => $test <= $this->target,
|
||||
'!=' => $test != $this->target,
|
||||
default => $test == $this->target,
|
||||
};
|
||||
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
|
||||
{
|
||||
if (!\in_array($operator, ['>', '<', '>=', '<=', '==', '!='])) {
|
||||
throw new \InvalidArgumentException(sprintf('Invalid operator "%s".', $operator));
|
||||
}
|
||||
|
||||
$this->operator = $operator;
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ class DateComparator extends Comparator
|
||||
try {
|
||||
$date = new \DateTime($matches[2]);
|
||||
$target = $date->format('U');
|
||||
} catch (\Exception) {
|
||||
} catch (\Exception $e) {
|
||||
throw new \InvalidArgumentException(sprintf('"%s" is not a valid date.', $matches[2]));
|
||||
}
|
||||
|
||||
|
114
web/app/vendor/symfony/finder/Finder.php
vendored
114
web/app/vendor/symfony/finder/Finder.php
vendored
@ -45,27 +45,27 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
public const IGNORE_DOT_FILES = 2;
|
||||
public const IGNORE_VCS_IGNORED_FILES = 4;
|
||||
|
||||
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 $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 static array $vcsPatterns = ['.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg'];
|
||||
private static $vcsPatterns = ['.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg'];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
@ -74,8 +74,10 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
|
||||
/**
|
||||
* Creates a new Finder.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function create(): static
|
||||
public static function create()
|
||||
{
|
||||
return new static();
|
||||
}
|
||||
@ -85,7 +87,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function directories(): static
|
||||
public function directories()
|
||||
{
|
||||
$this->mode = Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES;
|
||||
|
||||
@ -97,7 +99,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function files(): static
|
||||
public function files()
|
||||
{
|
||||
$this->mode = Iterator\FileTypeFilterIterator::ONLY_FILES;
|
||||
|
||||
@ -120,7 +122,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
* @see DepthRangeFilterIterator
|
||||
* @see NumberComparator
|
||||
*/
|
||||
public function depth(string|int|array $levels): static
|
||||
public function depth($levels)
|
||||
{
|
||||
foreach ((array) $levels as $level) {
|
||||
$this->depths[] = new Comparator\NumberComparator($level);
|
||||
@ -148,7 +150,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
* @see DateRangeFilterIterator
|
||||
* @see DateComparator
|
||||
*/
|
||||
public function date(string|array $dates): static
|
||||
public function date($dates)
|
||||
{
|
||||
foreach ((array) $dates as $date) {
|
||||
$this->dates[] = new Comparator\DateComparator($date);
|
||||
@ -173,7 +175,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @see FilenameFilterIterator
|
||||
*/
|
||||
public function name(string|array $patterns): static
|
||||
public function name($patterns)
|
||||
{
|
||||
$this->names = array_merge($this->names, (array) $patterns);
|
||||
|
||||
@ -189,7 +191,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @see FilenameFilterIterator
|
||||
*/
|
||||
public function notName(string|array $patterns): static
|
||||
public function notName($patterns)
|
||||
{
|
||||
$this->notNames = array_merge($this->notNames, (array) $patterns);
|
||||
|
||||
@ -211,7 +213,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @see FilecontentFilterIterator
|
||||
*/
|
||||
public function contains(string|array $patterns): static
|
||||
public function contains($patterns)
|
||||
{
|
||||
$this->contains = array_merge($this->contains, (array) $patterns);
|
||||
|
||||
@ -233,7 +235,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @see FilecontentFilterIterator
|
||||
*/
|
||||
public function notContains(string|array $patterns): static
|
||||
public function notContains($patterns)
|
||||
{
|
||||
$this->notContains = array_merge($this->notContains, (array) $patterns);
|
||||
|
||||
@ -257,7 +259,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @see FilenameFilterIterator
|
||||
*/
|
||||
public function path(string|array $patterns): static
|
||||
public function path($patterns)
|
||||
{
|
||||
$this->paths = array_merge($this->paths, (array) $patterns);
|
||||
|
||||
@ -281,7 +283,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @see FilenameFilterIterator
|
||||
*/
|
||||
public function notPath(string|array $patterns): static
|
||||
public function notPath($patterns)
|
||||
{
|
||||
$this->notPaths = array_merge($this->notPaths, (array) $patterns);
|
||||
|
||||
@ -303,7 +305,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
* @see SizeRangeFilterIterator
|
||||
* @see NumberComparator
|
||||
*/
|
||||
public function size(string|int|array $sizes): static
|
||||
public function size($sizes)
|
||||
{
|
||||
foreach ((array) $sizes as $size) {
|
||||
$this->sizes[] = new Comparator\NumberComparator($size);
|
||||
@ -325,7 +327,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @see ExcludeDirectoryFilterIterator
|
||||
*/
|
||||
public function exclude(string|array $dirs): static
|
||||
public function exclude($dirs)
|
||||
{
|
||||
$this->exclude = array_merge($this->exclude, (array) $dirs);
|
||||
|
||||
@ -341,7 +343,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @see ExcludeDirectoryFilterIterator
|
||||
*/
|
||||
public function ignoreDotFiles(bool $ignoreDotFiles): static
|
||||
public function ignoreDotFiles(bool $ignoreDotFiles)
|
||||
{
|
||||
if ($ignoreDotFiles) {
|
||||
$this->ignore |= static::IGNORE_DOT_FILES;
|
||||
@ -361,7 +363,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @see ExcludeDirectoryFilterIterator
|
||||
*/
|
||||
public function ignoreVCS(bool $ignoreVCS): static
|
||||
public function ignoreVCS(bool $ignoreVCS)
|
||||
{
|
||||
if ($ignoreVCS) {
|
||||
$this->ignore |= static::IGNORE_VCS_FILES;
|
||||
@ -379,7 +381,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function ignoreVCSIgnored(bool $ignoreVCSIgnored): static
|
||||
public function ignoreVCSIgnored(bool $ignoreVCSIgnored)
|
||||
{
|
||||
if ($ignoreVCSIgnored) {
|
||||
$this->ignore |= static::IGNORE_VCS_IGNORED_FILES;
|
||||
@ -397,7 +399,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @param string|string[] $pattern VCS patterns to ignore
|
||||
*/
|
||||
public static function addVCSPattern(string|array $pattern)
|
||||
public static function addVCSPattern($pattern)
|
||||
{
|
||||
foreach ((array) $pattern as $p) {
|
||||
self::$vcsPatterns[] = $p;
|
||||
@ -417,7 +419,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @see SortableIterator
|
||||
*/
|
||||
public function sort(\Closure $closure): static
|
||||
public function sort(\Closure $closure)
|
||||
{
|
||||
$this->sort = $closure;
|
||||
|
||||
@ -433,7 +435,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @see SortableIterator
|
||||
*/
|
||||
public function sortByName(bool $useNaturalSort = false): static
|
||||
public function sortByName(bool $useNaturalSort = false)
|
||||
{
|
||||
$this->sort = $useNaturalSort ? Iterator\SortableIterator::SORT_BY_NAME_NATURAL : Iterator\SortableIterator::SORT_BY_NAME;
|
||||
|
||||
@ -449,7 +451,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @see SortableIterator
|
||||
*/
|
||||
public function sortByType(): static
|
||||
public function sortByType()
|
||||
{
|
||||
$this->sort = Iterator\SortableIterator::SORT_BY_TYPE;
|
||||
|
||||
@ -467,7 +469,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @see SortableIterator
|
||||
*/
|
||||
public function sortByAccessedTime(): static
|
||||
public function sortByAccessedTime()
|
||||
{
|
||||
$this->sort = Iterator\SortableIterator::SORT_BY_ACCESSED_TIME;
|
||||
|
||||
@ -479,7 +481,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function reverseSorting(): static
|
||||
public function reverseSorting()
|
||||
{
|
||||
$this->reverseSorting = true;
|
||||
|
||||
@ -499,7 +501,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @see SortableIterator
|
||||
*/
|
||||
public function sortByChangedTime(): static
|
||||
public function sortByChangedTime()
|
||||
{
|
||||
$this->sort = Iterator\SortableIterator::SORT_BY_CHANGED_TIME;
|
||||
|
||||
@ -517,7 +519,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @see SortableIterator
|
||||
*/
|
||||
public function sortByModifiedTime(): static
|
||||
public function sortByModifiedTime()
|
||||
{
|
||||
$this->sort = Iterator\SortableIterator::SORT_BY_MODIFIED_TIME;
|
||||
|
||||
@ -534,7 +536,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @see CustomFilterIterator
|
||||
*/
|
||||
public function filter(\Closure $closure): static
|
||||
public function filter(\Closure $closure)
|
||||
{
|
||||
$this->filters[] = $closure;
|
||||
|
||||
@ -546,7 +548,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function followLinks(): static
|
||||
public function followLinks()
|
||||
{
|
||||
$this->followLinks = true;
|
||||
|
||||
@ -560,7 +562,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function ignoreUnreadableDirs(bool $ignore = true): static
|
||||
public function ignoreUnreadableDirs(bool $ignore = true)
|
||||
{
|
||||
$this->ignoreUnreadableDirs = $ignore;
|
||||
|
||||
@ -576,7 +578,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @throws DirectoryNotFoundException if one of the directories does not exist
|
||||
*/
|
||||
public function in(string|array $dirs): static
|
||||
public function in($dirs)
|
||||
{
|
||||
$resolvedDirs = [];
|
||||
|
||||
@ -585,7 +587,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));
|
||||
}
|
||||
@ -605,7 +607,8 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @throws \LogicException if the in() method has not been called
|
||||
*/
|
||||
public function getIterator(): \Iterator
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
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.');
|
||||
@ -648,7 +651,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @throws \InvalidArgumentException when the given argument is not iterable
|
||||
*/
|
||||
public function append(iterable $iterator): static
|
||||
public function append(iterable $iterator)
|
||||
{
|
||||
if ($iterator instanceof \IteratorAggregate) {
|
||||
$this->iterators[] = $iterator->getIterator();
|
||||
@ -670,8 +673,10 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
|
||||
/**
|
||||
* Check if any results were found.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasResults(): bool
|
||||
public function hasResults()
|
||||
{
|
||||
foreach ($this->getIterator() as $_) {
|
||||
return true;
|
||||
@ -682,8 +687,11 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
|
||||
/**
|
||||
* Counts all the results collected by the iterators.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int
|
||||
#[\ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
return iterator_count($this->getIterator());
|
||||
}
|
||||
|
2
web/app/vendor/symfony/finder/Gitignore.php
vendored
2
web/app/vendor/symfony/finder/Gitignore.php
vendored
@ -43,7 +43,7 @@ class Gitignore
|
||||
foreach ($gitignoreLines as $line) {
|
||||
$line = preg_replace('~(?<!\\\\)[ \t]+$~', '', $line);
|
||||
|
||||
if (str_starts_with($line, '!')) {
|
||||
if ('!' === substr($line, 0, 1)) {
|
||||
$line = substr($line, 1);
|
||||
$isNegative = true;
|
||||
} else {
|
||||
|
4
web/app/vendor/symfony/finder/Glob.php
vendored
4
web/app/vendor/symfony/finder/Glob.php
vendored
@ -37,8 +37,10 @@ class Glob
|
||||
{
|
||||
/**
|
||||
* Returns a regexp which is the equivalent of the glob pattern.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function toRegex(string $glob, bool $strictLeadingDot = true, bool $strictWildcardSlash = true, string $delimiter = '#'): string
|
||||
public static function toRegex(string $glob, bool $strictLeadingDot = true, bool $strictWildcardSlash = true, string $delimiter = '#')
|
||||
{
|
||||
$firstByte = true;
|
||||
$escaping = false;
|
||||
|
@ -23,7 +23,7 @@ namespace Symfony\Component\Finder\Iterator;
|
||||
*/
|
||||
class CustomFilterIterator extends \FilterIterator
|
||||
{
|
||||
private array $filters = [];
|
||||
private $filters = [];
|
||||
|
||||
/**
|
||||
* @param \Iterator<string, \SplFileInfo> $iterator The Iterator to filter
|
||||
@ -45,8 +45,11 @@ class CustomFilterIterator extends \FilterIterator
|
||||
|
||||
/**
|
||||
* Filters the iterator values.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function accept(): bool
|
||||
#[\ReturnTypeWillChange]
|
||||
public function accept()
|
||||
{
|
||||
$fileinfo = $this->current();
|
||||
|
||||
|
@ -22,7 +22,7 @@ use Symfony\Component\Finder\Comparator\DateComparator;
|
||||
*/
|
||||
class DateRangeFilterIterator extends \FilterIterator
|
||||
{
|
||||
private array $comparators = [];
|
||||
private $comparators = [];
|
||||
|
||||
/**
|
||||
* @param \Iterator<string, \SplFileInfo> $iterator
|
||||
@ -37,8 +37,11 @@ class DateRangeFilterIterator extends \FilterIterator
|
||||
|
||||
/**
|
||||
* Filters the iterator values.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function accept(): bool
|
||||
#[\ReturnTypeWillChange]
|
||||
public function accept()
|
||||
{
|
||||
$fileinfo = $this->current();
|
||||
|
||||
|
@ -23,7 +23,7 @@ namespace Symfony\Component\Finder\Iterator;
|
||||
*/
|
||||
class DepthRangeFilterIterator extends \FilterIterator
|
||||
{
|
||||
private int $minDepth = 0;
|
||||
private $minDepth = 0;
|
||||
|
||||
/**
|
||||
* @param \RecursiveIteratorIterator<\RecursiveIterator<TKey, TValue>> $iterator The Iterator to filter
|
||||
@ -40,8 +40,11 @@ class DepthRangeFilterIterator extends \FilterIterator
|
||||
|
||||
/**
|
||||
* Filters the iterator values.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function accept(): bool
|
||||
#[\ReturnTypeWillChange]
|
||||
public function accept()
|
||||
{
|
||||
return $this->getInnerIterator()->getDepth() >= $this->minDepth;
|
||||
}
|
||||
|
@ -11,26 +11,23 @@
|
||||
|
||||
namespace Symfony\Component\Finder\Iterator;
|
||||
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
|
||||
/**
|
||||
* ExcludeDirectoryFilterIterator filters out directories.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @extends \FilterIterator<string, SplFileInfo>
|
||||
* @implements \RecursiveIterator<string, SplFileInfo>
|
||||
* @extends \FilterIterator<string, \SplFileInfo>
|
||||
* @implements \RecursiveIterator<string, \SplFileInfo>
|
||||
*/
|
||||
class ExcludeDirectoryFilterIterator extends \FilterIterator implements \RecursiveIterator
|
||||
{
|
||||
/** @var \Iterator<string, SplFileInfo> */
|
||||
private \Iterator $iterator;
|
||||
private bool $isRecursive;
|
||||
private array $excludedDirs = [];
|
||||
private ?string $excludedPattern = null;
|
||||
private $iterator;
|
||||
private $isRecursive;
|
||||
private $excludedDirs = [];
|
||||
private $excludedPattern;
|
||||
|
||||
/**
|
||||
* @param \Iterator<string, SplFileInfo> $iterator The Iterator to filter
|
||||
* @param \Iterator $iterator The Iterator to filter
|
||||
* @param string[] $directories An array of directories to exclude
|
||||
*/
|
||||
public function __construct(\Iterator $iterator, array $directories)
|
||||
@ -55,8 +52,11 @@ class ExcludeDirectoryFilterIterator extends \FilterIterator implements \Recursi
|
||||
|
||||
/**
|
||||
* Filters the iterator values.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function accept(): bool
|
||||
#[\ReturnTypeWillChange]
|
||||
public function accept()
|
||||
{
|
||||
if ($this->isRecursive && isset($this->excludedDirs[$this->getFilename()]) && $this->isDir()) {
|
||||
return false;
|
||||
@ -72,12 +72,20 @@ class ExcludeDirectoryFilterIterator extends \FilterIterator implements \Recursi
|
||||
return true;
|
||||
}
|
||||
|
||||
public function hasChildren(): bool
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function hasChildren()
|
||||
{
|
||||
return $this->isRecursive && $this->iterator->hasChildren();
|
||||
}
|
||||
|
||||
public function getChildren(): self
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getChildren()
|
||||
{
|
||||
$children = new self($this->iterator->getChildren(), []);
|
||||
$children->excludedDirs = $this->excludedDirs;
|
||||
|
@ -23,10 +23,10 @@ class FileTypeFilterIterator extends \FilterIterator
|
||||
public const ONLY_FILES = 1;
|
||||
public const ONLY_DIRECTORIES = 2;
|
||||
|
||||
private int $mode;
|
||||
private $mode;
|
||||
|
||||
/**
|
||||
* @param \Iterator<string, \SplFileInfo> $iterator The Iterator to filter
|
||||
* @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,8 +38,11 @@ class FileTypeFilterIterator extends \FilterIterator
|
||||
|
||||
/**
|
||||
* Filters the iterator values.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function accept(): bool
|
||||
#[\ReturnTypeWillChange]
|
||||
public function accept()
|
||||
{
|
||||
$fileinfo = $this->current();
|
||||
if (self::ONLY_DIRECTORIES === (self::ONLY_DIRECTORIES & $this->mode) && $fileinfo->isFile()) {
|
||||
|
@ -11,22 +11,23 @@
|
||||
|
||||
namespace Symfony\Component\Finder\Iterator;
|
||||
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
|
||||
/**
|
||||
* FilecontentFilterIterator filters files by their contents using patterns (regexps or strings).
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Włodzimierz Gajda <gajdaw@gajdaw.pl>
|
||||
*
|
||||
* @extends MultiplePcreFilterIterator<string, SplFileInfo>
|
||||
* @extends MultiplePcreFilterIterator<string, \SplFileInfo>
|
||||
*/
|
||||
class FilecontentFilterIterator extends MultiplePcreFilterIterator
|
||||
{
|
||||
/**
|
||||
* Filters the iterator values.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function accept(): bool
|
||||
#[\ReturnTypeWillChange]
|
||||
public function accept()
|
||||
{
|
||||
if (!$this->matchRegexps && !$this->noMatchRegexps) {
|
||||
return true;
|
||||
@ -50,8 +51,10 @@ class FilecontentFilterIterator extends MultiplePcreFilterIterator
|
||||
* Converts string to regexp if necessary.
|
||||
*
|
||||
* @param string $str Pattern: string or regexp
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function toRegex(string $str): string
|
||||
protected function toRegex(string $str)
|
||||
{
|
||||
return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/';
|
||||
}
|
||||
|
@ -24,8 +24,11 @@ class FilenameFilterIterator extends MultiplePcreFilterIterator
|
||||
{
|
||||
/**
|
||||
* Filters the iterator values.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function accept(): bool
|
||||
#[\ReturnTypeWillChange]
|
||||
public function accept()
|
||||
{
|
||||
return $this->isAccepted($this->current()->getFilename());
|
||||
}
|
||||
@ -37,8 +40,10 @@ 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): string
|
||||
protected function toRegex(string $str)
|
||||
{
|
||||
return $this->isRegex($str) ? $str : Glob::toRegex($str);
|
||||
}
|
||||
|
@ -18,11 +18,11 @@ namespace Symfony\Component\Finder\Iterator;
|
||||
*/
|
||||
class LazyIterator implements \IteratorAggregate
|
||||
{
|
||||
private \Closure $iteratorFactory;
|
||||
private $iteratorFactory;
|
||||
|
||||
public function __construct(callable $iteratorFactory)
|
||||
{
|
||||
$this->iteratorFactory = $iteratorFactory(...);
|
||||
$this->iteratorFactory = $iteratorFactory;
|
||||
}
|
||||
|
||||
public function getIterator(): \Traversable
|
||||
|
@ -27,7 +27,7 @@ abstract class MultiplePcreFilterIterator extends \FilterIterator
|
||||
protected $noMatchRegexps = [];
|
||||
|
||||
/**
|
||||
* @param \Iterator<TKey, TValue> $iterator The Iterator to filter
|
||||
* @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
|
||||
*/
|
||||
@ -50,8 +50,10 @@ 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): bool
|
||||
protected function isAccepted(string $string)
|
||||
{
|
||||
// should at least not match one rule to exclude
|
||||
foreach ($this->noMatchRegexps as $regex) {
|
||||
@ -77,8 +79,10 @@ abstract class MultiplePcreFilterIterator extends \FilterIterator
|
||||
|
||||
/**
|
||||
* Checks whether the string is a regex.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isRegex(string $str): bool
|
||||
protected function isRegex(string $str)
|
||||
{
|
||||
$availableModifiers = 'imsxuADU';
|
||||
|
||||
@ -106,6 +110,8 @@ abstract class MultiplePcreFilterIterator extends \FilterIterator
|
||||
|
||||
/**
|
||||
* Converts string into regexp.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function toRegex(string $str): string;
|
||||
abstract protected function toRegex(string $str);
|
||||
}
|
||||
|
@ -11,22 +11,23 @@
|
||||
|
||||
namespace Symfony\Component\Finder\Iterator;
|
||||
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
|
||||
/**
|
||||
* PathFilterIterator filters files by path patterns (e.g. some/special/dir).
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Włodzimierz Gajda <gajdaw@gajdaw.pl>
|
||||
*
|
||||
* @extends MultiplePcreFilterIterator<string, SplFileInfo>
|
||||
* @extends MultiplePcreFilterIterator<string, \SplFileInfo>
|
||||
*/
|
||||
class PathFilterIterator extends MultiplePcreFilterIterator
|
||||
{
|
||||
/**
|
||||
* Filters the iterator values.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function accept(): bool
|
||||
#[\ReturnTypeWillChange]
|
||||
public function accept()
|
||||
{
|
||||
$filename = $this->current()->getRelativePathname();
|
||||
|
||||
@ -48,8 +49,10 @@ 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): string
|
||||
protected function toRegex(string $str)
|
||||
{
|
||||
return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/';
|
||||
}
|
||||
|
@ -18,17 +18,23 @@ use Symfony\Component\Finder\SplFileInfo;
|
||||
* Extends the \RecursiveDirectoryIterator to support relative paths.
|
||||
*
|
||||
* @author Victor Berchet <victor@suumit.com>
|
||||
* @extends \RecursiveDirectoryIterator<string, SplFileInfo>
|
||||
*/
|
||||
class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
|
||||
{
|
||||
private bool $ignoreUnreadableDirs;
|
||||
private ?bool $rewindable = null;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $ignoreUnreadableDirs;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $rewindable;
|
||||
|
||||
// these 3 properties take part of the performance optimization to avoid redoing the same work in all iterations
|
||||
private string $rootPath;
|
||||
private string $subPath;
|
||||
private string $directorySeparator = '/';
|
||||
private $rootPath;
|
||||
private $subPath;
|
||||
private $directorySeparator = '/';
|
||||
|
||||
/**
|
||||
* @throws \RuntimeException
|
||||
@ -49,15 +55,17 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
|
||||
|
||||
/**
|
||||
* Return an instance of SplFileInfo with support for relative paths.
|
||||
*
|
||||
* @return SplFileInfo
|
||||
*/
|
||||
public function current(): SplFileInfo
|
||||
#[\ReturnTypeWillChange]
|
||||
public function current()
|
||||
{
|
||||
// the logic here avoids redoing the same work in all iterations
|
||||
|
||||
if (!isset($this->subPath)) {
|
||||
$this->subPath = $this->getSubPath();
|
||||
if (null === $subPathname = $this->subPath) {
|
||||
$subPathname = $this->subPath = $this->getSubPath();
|
||||
}
|
||||
$subPathname = $this->subPath;
|
||||
if ('' !== $subPathname) {
|
||||
$subPathname .= $this->directorySeparator;
|
||||
}
|
||||
@ -70,7 +78,13 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
|
||||
return new SplFileInfo($basePath.$subPathname, $this->subPath, $subPathname);
|
||||
}
|
||||
|
||||
public function hasChildren(bool $allowLinks = false): bool
|
||||
/**
|
||||
* @param bool $allowLinks
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function hasChildren($allowLinks = false)
|
||||
{
|
||||
$hasChildren = parent::hasChildren($allowLinks);
|
||||
|
||||
@ -82,16 +96,19 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
|
||||
parent::getChildren();
|
||||
|
||||
return true;
|
||||
} catch (\UnexpectedValueException) {
|
||||
} catch (\UnexpectedValueException $e) {
|
||||
// If directory is unreadable and finder is set to ignore it, skip children
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \RecursiveDirectoryIterator
|
||||
*
|
||||
* @throws AccessDeniedException
|
||||
*/
|
||||
public function getChildren(): \RecursiveDirectoryIterator
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getChildren()
|
||||
{
|
||||
try {
|
||||
$children = parent::getChildren();
|
||||
@ -113,8 +130,11 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
|
||||
|
||||
/**
|
||||
* Do nothing for non rewindable stream.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rewind(): void
|
||||
#[\ReturnTypeWillChange]
|
||||
public function rewind()
|
||||
{
|
||||
if (false === $this->isRewindable()) {
|
||||
return;
|
||||
@ -125,8 +145,10 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
|
||||
|
||||
/**
|
||||
* Checks if the stream is rewindable.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isRewindable(): bool
|
||||
public function isRewindable()
|
||||
{
|
||||
if (null !== $this->rewindable) {
|
||||
return $this->rewindable;
|
||||
|
@ -22,7 +22,7 @@ use Symfony\Component\Finder\Comparator\NumberComparator;
|
||||
*/
|
||||
class SizeRangeFilterIterator extends \FilterIterator
|
||||
{
|
||||
private array $comparators = [];
|
||||
private $comparators = [];
|
||||
|
||||
/**
|
||||
* @param \Iterator<string, \SplFileInfo> $iterator
|
||||
@ -37,8 +37,11 @@ class SizeRangeFilterIterator extends \FilterIterator
|
||||
|
||||
/**
|
||||
* Filters the iterator values.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function accept(): bool
|
||||
#[\ReturnTypeWillChange]
|
||||
public function accept()
|
||||
{
|
||||
$fileinfo = $this->current();
|
||||
if (!$fileinfo->isFile()) {
|
||||
|
@ -28,9 +28,8 @@ class SortableIterator implements \IteratorAggregate
|
||||
public const SORT_BY_MODIFIED_TIME = 5;
|
||||
public const SORT_BY_NAME_NATURAL = 6;
|
||||
|
||||
/** @var \Traversable<string, \SplFileInfo> */
|
||||
private \Traversable $iterator;
|
||||
private \Closure|int $sort;
|
||||
private $iterator;
|
||||
private $sort;
|
||||
|
||||
/**
|
||||
* @param \Traversable<string, \SplFileInfo> $iterator
|
||||
@ -38,7 +37,7 @@ class SortableIterator implements \IteratorAggregate
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function __construct(\Traversable $iterator, int|callable $sort, bool $reverseOrder = false)
|
||||
public function __construct(\Traversable $iterator, $sort, bool $reverseOrder = false)
|
||||
{
|
||||
$this->iterator = $iterator;
|
||||
$order = $reverseOrder ? -1 : 1;
|
||||
@ -76,13 +75,17 @@ 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.');
|
||||
}
|
||||
}
|
||||
|
||||
public function getIterator(): \Traversable
|
||||
/**
|
||||
* @return \Traversable<string, \SplFileInfo>
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
if (1 === $this->sort) {
|
||||
return $this->iterator;
|
||||
|
@ -13,9 +13,6 @@ namespace Symfony\Component\Finder\Iterator;
|
||||
|
||||
use Symfony\Component\Finder\Gitignore;
|
||||
|
||||
/**
|
||||
* @extends \FilterIterator<string, \SplFileInfo>
|
||||
*/
|
||||
final class VcsIgnoredFilterIterator extends \FilterIterator
|
||||
{
|
||||
/**
|
||||
@ -33,20 +30,10 @@ final class VcsIgnoredFilterIterator extends \FilterIterator
|
||||
*/
|
||||
private $ignoredPathsCache = [];
|
||||
|
||||
/**
|
||||
* @param \Iterator<string, \SplFileInfo> $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);
|
||||
}
|
||||
|
||||
@ -71,7 +58,7 @@ final class VcsIgnoredFilterIterator extends \FilterIterator
|
||||
|
||||
$ignored = false;
|
||||
|
||||
foreach ($this->parentDirectoriesDownwards($fileRealPath) as $parentDirectory) {
|
||||
foreach ($this->parentsDirectoryDownward($fileRealPath) as $parentDirectory) {
|
||||
if ($this->isIgnored($parentDirectory)) {
|
||||
// rules in ignored directories are ignored, no need to check further.
|
||||
break;
|
||||
@ -102,11 +89,11 @@ final class VcsIgnoredFilterIterator extends \FilterIterator
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
private function parentDirectoriesUpwards(string $from): array
|
||||
private function parentsDirectoryDownward(string $fileRealPath): array
|
||||
{
|
||||
$parentDirectories = [];
|
||||
|
||||
$parentDirectory = $from;
|
||||
$parentDirectory = $fileRealPath;
|
||||
|
||||
while (true) {
|
||||
$newParentDirectory = \dirname($parentDirectory);
|
||||
@ -116,30 +103,16 @@ final class VcsIgnoredFilterIterator extends \FilterIterator
|
||||
break;
|
||||
}
|
||||
|
||||
$parentDirectories[] = $parentDirectory = $newParentDirectory;
|
||||
$parentDirectory = $newParentDirectory;
|
||||
|
||||
if (0 !== strpos($parentDirectory, $this->baseDir)) {
|
||||
break;
|
||||
}
|
||||
|
||||
return $parentDirectories;
|
||||
$parentDirectories[] = $parentDirectory;
|
||||
}
|
||||
|
||||
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<string>
|
||||
*/
|
||||
private function parentDirectoriesDownwards(string $fileRealPath): array
|
||||
{
|
||||
return array_reverse(
|
||||
$this->parentDirectoriesUpTo($fileRealPath, $this->baseDir)
|
||||
);
|
||||
return array_reverse($parentDirectories);
|
||||
}
|
||||
|
||||
/**
|
||||
|
16
web/app/vendor/symfony/finder/SplFileInfo.php
vendored
16
web/app/vendor/symfony/finder/SplFileInfo.php
vendored
@ -18,8 +18,8 @@ namespace Symfony\Component\Finder;
|
||||
*/
|
||||
class SplFileInfo extends \SplFileInfo
|
||||
{
|
||||
private string $relativePath;
|
||||
private string $relativePathname;
|
||||
private $relativePath;
|
||||
private $relativePathname;
|
||||
|
||||
/**
|
||||
* @param string $file The file name
|
||||
@ -37,8 +37,10 @@ class SplFileInfo extends \SplFileInfo
|
||||
* Returns the relative path.
|
||||
*
|
||||
* This path does not contain the file name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRelativePath(): string
|
||||
public function getRelativePath()
|
||||
{
|
||||
return $this->relativePath;
|
||||
}
|
||||
@ -47,8 +49,10 @@ class SplFileInfo extends \SplFileInfo
|
||||
* Returns the relative path name.
|
||||
*
|
||||
* This path contains the file name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRelativePathname(): string
|
||||
public function getRelativePathname()
|
||||
{
|
||||
return $this->relativePathname;
|
||||
}
|
||||
@ -63,9 +67,11 @@ class SplFileInfo extends \SplFileInfo
|
||||
/**
|
||||
* Returns the contents of the file.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function getContents(): string
|
||||
public function getContents()
|
||||
{
|
||||
set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; });
|
||||
try {
|
||||
|
7
web/app/vendor/symfony/finder/composer.json
vendored
7
web/app/vendor/symfony/finder/composer.json
vendored
@ -16,10 +16,9 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/filesystem": "^6.0"
|
||||
"php": ">=7.2.5",
|
||||
"symfony/deprecation-contracts": "^2.1|^3",
|
||||
"symfony/polyfill-php80": "^1.16"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Component\\Finder\\": "" },
|
||||
|
19
web/app/vendor/symfony/polyfill-php80/LICENSE
vendored
Normal file
19
web/app/vendor/symfony/polyfill-php80/LICENSE
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
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.
|
115
web/app/vendor/symfony/polyfill-php80/Php80.php
vendored
Normal file
115
web/app/vendor/symfony/polyfill-php80/Php80.php
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* 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 <ion.bazan@gmail.com>
|
||||
* @author Nico Oelgart <nicoswd@gmail.com>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
103
web/app/vendor/symfony/polyfill-php80/PhpToken.php
vendored
Normal file
103
web/app/vendor/symfony/polyfill-php80/PhpToken.php
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* 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 <info@ensostudio.ru>
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
25
web/app/vendor/symfony/polyfill-php80/README.md
vendored
Normal file
25
web/app/vendor/symfony/polyfill-php80/README.md
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
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).
|
22
web/app/vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php
vendored
Normal file
22
web/app/vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
#[Attribute(Attribute::TARGET_CLASS)]
|
||||
final class Attribute
|
||||
{
|
||||
public const TARGET_CLASS = 1;
|
||||
public const TARGET_FUNCTION = 2;
|
||||
public const TARGET_METHOD = 4;
|
||||
public const TARGET_PROPERTY = 8;
|
||||
public const TARGET_CLASS_CONSTANT = 16;
|
||||
public const TARGET_PARAMETER = 32;
|
||||
public const TARGET_ALL = 63;
|
||||
public const IS_REPEATABLE = 64;
|
||||
|
||||
/** @var int */
|
||||
public $flags;
|
||||
|
||||
public function __construct(int $flags = self::TARGET_ALL)
|
||||
{
|
||||
$this->flags = $flags;
|
||||
}
|
||||
}
|
7
web/app/vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php
vendored
Normal file
7
web/app/vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
if (\PHP_VERSION_ID < 80000 && \extension_loaded('tokenizer')) {
|
||||
class PhpToken extends Symfony\Polyfill\Php80\PhpToken
|
||||
{
|
||||
}
|
||||
}
|
11
web/app/vendor/symfony/polyfill-php80/Resources/stubs/Stringable.php
vendored
Normal file
11
web/app/vendor/symfony/polyfill-php80/Resources/stubs/Stringable.php
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
if (\PHP_VERSION_ID < 80000) {
|
||||
interface Stringable
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString();
|
||||
}
|
||||
}
|
7
web/app/vendor/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php
vendored
Normal file
7
web/app/vendor/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
if (\PHP_VERSION_ID < 80000) {
|
||||
class UnhandledMatchError extends Error
|
||||
{
|
||||
}
|
||||
}
|
7
web/app/vendor/symfony/polyfill-php80/Resources/stubs/ValueError.php
vendored
Normal file
7
web/app/vendor/symfony/polyfill-php80/Resources/stubs/ValueError.php
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
if (\PHP_VERSION_ID < 80000) {
|
||||
class ValueError extends Error
|
||||
{
|
||||
}
|
||||
}
|
42
web/app/vendor/symfony/polyfill-php80/bootstrap.php
vendored
Normal file
42
web/app/vendor/symfony/polyfill-php80/bootstrap.php
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* 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); }
|
||||
}
|
40
web/app/vendor/symfony/polyfill-php80/composer.json
vendored
Normal file
40
web/app/vendor/symfony/polyfill-php80/composer.json
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
@ -13,25 +13,29 @@ if (!isset($ShowPageFooter)) {
|
||||
<?php if (isset($REQUIRE_LIB['bootstrap5'])) : ?>
|
||||
<footer class="bg-white text-muted pt-3 pb-4 mt-4" style="font-size: 0.9em">
|
||||
<div class="container d-lg-flex justify-content-lg-between">
|
||||
<p>
|
||||
<div>
|
||||
<div>
|
||||
© <?= date('Y') ?>
|
||||
<a class="text-decoration-none" href="<?= HTML::url('/') ?>">S2OJ</a>
|
||||
(build: <a class="text-decoration-none" href="https://github.com/renbaoshuo/S2OJ<?= UOJConfig::$data['profile']['s2oj-version'] == "dev" ? '' : '/tree/' . UOJConfig::$data['profile']['s2oj-version'] ?>"><?= UOJConfig::$data['profile']['s2oj-version'] ?></a>)
|
||||
<?= UOJLocale::get('server time') ?>: <?= UOJTime::$time_now_str ?>
|
||||
<?php if (UOJConfig::$data['profile']['ICP-license'] != '') : ?>
|
||||
| <a class="text-muted text-decoration-none" target="_blank" href="https://beian.miit.gov.cn">
|
||||
<?= UOJConfig::$data['profile']['ICP-license'] ?>
|
||||
</a>
|
||||
<?php endif ?>
|
||||
</p>
|
||||
<p>
|
||||
</div>
|
||||
<div class="small mt-1">
|
||||
<?= UOJLocale::get('server time') ?>: <?= UOJTime::$time_now_str ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2 mt-lg-0">
|
||||
Based on
|
||||
<a class="text-decoration-none" href="https://uoj.ac" target="_blank">UOJ</a>,
|
||||
modified by
|
||||
<a class="text-decoration-none" href="https://baoshuo.ren" target="_blank">Baoshuo</a>
|
||||
for
|
||||
<a class="text-decoration-none" href="http://www.sjzez.com">SJZEZ</a>.
|
||||
</p>
|
||||
<a class="text-decoration-none" href="http://www.sjzez.com">SJZEZ</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<?php else : ?>
|
||||
|
@ -463,3 +463,8 @@ form.uoj-bs4-form-compressed button {
|
||||
--bs-btn-hover-bg: #d3d4d570;
|
||||
--bs-btn-hover-border-color: transparent;
|
||||
}
|
||||
|
||||
.remote-content center > img + span {
|
||||
display: block;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user