mirror of
https://github.com/standardebooks/web.git
synced 2025-07-12 09:32:24 -04:00
Add Safe PHP functions
This commit is contained in:
parent
04a956886a
commit
58cc098058
260 changed files with 49458 additions and 45 deletions
35
vendor/thecodingmachine/safe/generator/composer.json
vendored
Normal file
35
vendor/thecodingmachine/safe/generator/composer.json
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"name": "thecodingmachine/safe-generator",
|
||||
"description": "Generated the files for thecodingmachine/safe",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Safe\\": "src/"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1",
|
||||
"ext-simplexml": "*",
|
||||
"phpoffice/phpspreadsheet": "^1.4",
|
||||
"ext-json": "^1.5",
|
||||
"symfony/console": "^4.1.4",
|
||||
"symfony/process": "^4.1",
|
||||
"symfony/finder": "^4.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7",
|
||||
"phpstan/phpstan": "^0.10.3",
|
||||
"thecodingmachine/phpstan-strict-rules": "^0.10.3",
|
||||
"squizlabs/php_codesniffer": "^3.2",
|
||||
"php-coveralls/php-coveralls": "^2.1"
|
||||
},
|
||||
"scripts": {
|
||||
"phpstan": "phpstan analyse src -c phpstan.neon --level=7 --no-progress -vvv",
|
||||
"cs-fix": "phpcbf",
|
||||
"cs-check": "phpcs"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "0.1-dev"
|
||||
}
|
||||
}
|
||||
}
|
3607
vendor/thecodingmachine/safe/generator/composer.lock
generated
vendored
Normal file
3607
vendor/thecodingmachine/safe/generator/composer.lock
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
15
vendor/thecodingmachine/safe/generator/config/ignoredFunctions.php
vendored
Normal file
15
vendor/thecodingmachine/safe/generator/config/ignoredFunctions.php
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
return [
|
||||
'array_key_exists',
|
||||
'is_executable',
|
||||
'is_uploaded_file',
|
||||
'stream_is_local',
|
||||
'is_soap_fault',
|
||||
// Type hints to object OCI-Lob (weird class that has a dash in its name!)
|
||||
'oci_lob_copy',
|
||||
'func_get_arg',
|
||||
//'mktime', // 7th parameter has been removed in PHP 7
|
||||
'call_user_func_array',
|
||||
'mb_check_encoding',
|
||||
'array_search',
|
||||
];
|
52
vendor/thecodingmachine/safe/generator/config/ignoredModules.php
vendored
Normal file
52
vendor/thecodingmachine/safe/generator/config/ignoredModules.php
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
return [
|
||||
'apd',
|
||||
'bbcode',
|
||||
'bcompiler',
|
||||
'cairo',
|
||||
'chdb',
|
||||
'classkit',
|
||||
'crack',
|
||||
'cyrus',
|
||||
'dba',
|
||||
'dbase',
|
||||
'dbplus',
|
||||
'dbx',
|
||||
'dio',
|
||||
'enchant',
|
||||
'fam',
|
||||
'fann',
|
||||
'fbsql',
|
||||
'fdf',
|
||||
'filepro',
|
||||
'fribidi',
|
||||
'gupnp',
|
||||
'id3',
|
||||
'ifx',
|
||||
'kadm5',
|
||||
'maxdb',
|
||||
'mcrypt',
|
||||
'mhash',
|
||||
'mnogosearch',
|
||||
'ncurses',
|
||||
'newt',
|
||||
'nis',
|
||||
'nsapi',
|
||||
'openal',
|
||||
'paradox',
|
||||
'proctitle',
|
||||
'radius',
|
||||
'rpmreader',
|
||||
'runkit',
|
||||
'session_pgsql',
|
||||
'snmp',
|
||||
'sqlite',
|
||||
'suhosin',
|
||||
'svn',
|
||||
'sybase',
|
||||
'tidy',
|
||||
'wddx',
|
||||
'win32ps',
|
||||
'wincache',
|
||||
'xattr',
|
||||
];
|
12
vendor/thecodingmachine/safe/generator/config/specialCasesFunctions.php
vendored
Normal file
12
vendor/thecodingmachine/safe/generator/config/specialCasesFunctions.php
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
/**
|
||||
* A list of functions that must be dealt with manually.
|
||||
* They are declared in lib/special_cases.php
|
||||
*/
|
||||
return [
|
||||
'json_decode',
|
||||
'apc_fetch',
|
||||
'apcu_fetch',
|
||||
'preg_replace',
|
||||
'openssl_encrypt',
|
||||
];
|
0
vendor/thecodingmachine/safe/generator/doc/entities/.gitkeep
vendored
Normal file
0
vendor/thecodingmachine/safe/generator/doc/entities/.gitkeep
vendored
Normal file
21
vendor/thecodingmachine/safe/generator/phpcs.xml.dist
vendored
Normal file
21
vendor/thecodingmachine/safe/generator/phpcs.xml.dist
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0"?>
|
||||
<ruleset name="Expressive Skeleton coding standard">
|
||||
<description>Expressive Skeleton coding standard</description>
|
||||
|
||||
<!-- display progress -->
|
||||
<arg value="p"/>
|
||||
<arg name="colors"/>
|
||||
|
||||
<!-- inherit rules from: -->
|
||||
<rule ref="PSR2"/>
|
||||
|
||||
<!-- Paths to check -->
|
||||
<file>src</file>
|
||||
|
||||
<rule ref="Generic.Files.LineLength">
|
||||
<properties>
|
||||
<property name="lineLimit" value="300"/>
|
||||
<property name="absoluteLineLimit" value="500"/>
|
||||
</properties>
|
||||
</rule>
|
||||
</ruleset>
|
6
vendor/thecodingmachine/safe/generator/phpstan.neon
vendored
Normal file
6
vendor/thecodingmachine/safe/generator/phpstan.neon
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
parameters:
|
||||
ignoreErrors:
|
||||
- "#subject of function str_replace expects array|string, string|true given#"
|
||||
- "#createExceptionFile\\(\\) expects string, int|string given#"
|
||||
includes:
|
||||
- vendor/thecodingmachine/phpstan-strict-rules/phpstan-strict-rules.neon
|
27
vendor/thecodingmachine/safe/generator/phpunit.xml.dist
vendored
Normal file
27
vendor/thecodingmachine/safe/generator/phpunit.xml.dist
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
bootstrap="vendor/autoload.php"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Main test Suite">
|
||||
<directory>./tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">src</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
<logging>
|
||||
<log type="coverage-html" target="build/coverage"/>
|
||||
<log type="coverage-clover" target="build/logs/clover.xml"/>
|
||||
</logging>
|
||||
</phpunit>
|
15
vendor/thecodingmachine/safe/generator/safe.php
vendored
Executable file
15
vendor/thecodingmachine/safe/generator/safe.php
vendored
Executable file
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
// application.php
|
||||
|
||||
require __DIR__.'/vendor/autoload.php';
|
||||
|
||||
use Safe\GenerateCommand;
|
||||
use Safe\ScanObjectsCommand;
|
||||
use Symfony\Component\Console\Application;
|
||||
|
||||
$application = new Application();
|
||||
$application->addCommands([new GenerateCommand()]);
|
||||
$application->addCommands([new ScanObjectsCommand()]);
|
||||
|
||||
$application->run();
|
30
vendor/thecodingmachine/safe/generator/src/ComposerJsonEditor.php
vendored
Normal file
30
vendor/thecodingmachine/safe/generator/src/ComposerJsonEditor.php
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Safe;
|
||||
|
||||
/**
|
||||
* This class will edit the main composer.json file to add the list of files generated from modules.
|
||||
*/
|
||||
class ComposerJsonEditor
|
||||
{
|
||||
/**
|
||||
* @param string[] $modules A list of modules
|
||||
*/
|
||||
public static function editFiles(array $modules): void
|
||||
{
|
||||
$files = \array_map(function (string $module) {
|
||||
return 'generated/'.lcfirst($module).'.php';
|
||||
}, $modules);
|
||||
$files[] = 'lib/special_cases.php';
|
||||
$composerContent = file_get_contents(__DIR__.'/../../composer.json');
|
||||
if ($composerContent === false) {
|
||||
throw new \RuntimeException('Error while loading composer.json file for edition.');
|
||||
}
|
||||
$composerJson = \json_decode($composerContent, true);
|
||||
$composerJson['autoload']['files'] = $files;
|
||||
|
||||
$newContent = json_encode($composerJson, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES);
|
||||
\file_put_contents(__DIR__.'/../../composer.json', $newContent);
|
||||
}
|
||||
}
|
211
vendor/thecodingmachine/safe/generator/src/DocPage.php
vendored
Normal file
211
vendor/thecodingmachine/safe/generator/src/DocPage.php
vendored
Normal file
|
@ -0,0 +1,211 @@
|
|||
<?php
|
||||
|
||||
namespace Safe;
|
||||
|
||||
use function explode;
|
||||
use function strpos;
|
||||
|
||||
class DocPage
|
||||
{
|
||||
/*
|
||||
* @var string
|
||||
*/
|
||||
private $path;
|
||||
|
||||
/*
|
||||
* @return string
|
||||
* @parameter string
|
||||
*/
|
||||
public function __construct(string $_path)
|
||||
{
|
||||
$this->path = $_path;
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect function which didn't return FALSE on error.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function detectFalsyFunction(): bool
|
||||
{
|
||||
$file = file_get_contents($this->path);
|
||||
|
||||
if (preg_match('/&warn\.deprecated\.function-(\d+-\d+-\d+)\.removed-(\d+-\d+-\d+)/', $file, $matches)) {
|
||||
$removedVersion = $matches[2];
|
||||
[$major, $minor] = explode('-', $removedVersion);
|
||||
if ($major < 7 || ($major == 7 && $minor == 0)) {
|
||||
// Ignore function if it was removed before PHP 7.1
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (preg_match('/&warn\.removed\.function-(\d+-\d+-\d+)/', $file, $matches) && isset($matches[2])) {
|
||||
$removedVersion = $matches[2];
|
||||
[$major, $minor] = explode('-', $removedVersion);
|
||||
if ($major < 7 || ($major == 7 && $minor == 0)) {
|
||||
// Ignore function if it was removed before PHP 7.1
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (preg_match('/&false;\s+on\s+error/m', $file)) {
|
||||
return true;
|
||||
}
|
||||
if (preg_match('/&false;\s+on\s+failure/m', $file)) {
|
||||
return true;
|
||||
}
|
||||
if (preg_match('/&false;\s+otherwise/m', $file) && !preg_match('/(returns\s+&true;|&true;\s+on\s+success|&true;\s+if)/im', $file)) {
|
||||
return true;
|
||||
}
|
||||
if (preg_match('/may\s+return\s+&false;/m', $file) && !preg_match('/(returns\s+&true;|&true;\s+on\s+success|&true;\s+if)/im', $file)) {
|
||||
return true;
|
||||
}
|
||||
if (preg_match('/&false;\s+if\s+an\s+error\s+occurred/m', $file)) {
|
||||
return true;
|
||||
}
|
||||
if (preg_match('/&return.success;/m', $file)) {
|
||||
return true;
|
||||
}
|
||||
if (preg_match('/&return.nullorfalse;/m', $file)) {
|
||||
return true;
|
||||
}
|
||||
if (preg_match('/&return.falseforfailure;/m', $file)) {
|
||||
return true;
|
||||
}
|
||||
if (preg_match('/&date.datetime.return.modifiedobjectorfalseforfailure;/m', $file)) {
|
||||
return true;
|
||||
}
|
||||
if (preg_match('/ or &false; \\(and generates an error/m', $file)) {
|
||||
return true;
|
||||
}
|
||||
if (preg_match('/&false;\s+if\s+the\s+number\s+of\s+elements\s+for\s+each\s+array\s+isn\'t\s+equal/m', $file)) {
|
||||
return true;
|
||||
}
|
||||
if (preg_match('/If\s+the\s+call\s+fails,\s+it\s+will\s+return\s+&false;/m', $file)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return \SimpleXMLElement[]
|
||||
*/
|
||||
public function getMethodSynopsis(): array
|
||||
{
|
||||
/** @var string[] $cleanedFunctions */
|
||||
$cleanedFunctions = [];
|
||||
|
||||
$file = \file_get_contents($this->path);
|
||||
if (!preg_match_all('/<\/?methodsynopsis[\s\S]*?>[\s\S]*?<\/methodsynopsis>/m', $file, $functions, PREG_SET_ORDER, 0)) {
|
||||
return [];
|
||||
}
|
||||
$functions = $this->arrayFlatten($functions);
|
||||
foreach ($functions as $function) {
|
||||
$cleaningFunction = \str_replace(['&false;', '&true;', '&null;'], ['false', 'true', 'null'], $function);
|
||||
$cleaningFunction = preg_replace('/&(.*);/m', '', $cleaningFunction);
|
||||
if (!\is_string($cleaningFunction)) {
|
||||
throw new \RuntimeException('Error occured in preg_replace');
|
||||
}
|
||||
$cleanedFunctions[] = $cleaningFunction;
|
||||
}
|
||||
$functionObjects = [];
|
||||
foreach ($cleanedFunctions as $cleanedFunction) {
|
||||
$functionObject = \simplexml_load_string($cleanedFunction);
|
||||
if ($functionObject) {
|
||||
$functionObjects[] = $functionObject;
|
||||
}
|
||||
}
|
||||
return $functionObjects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the XML file, resolving all DTD declared entities.
|
||||
*
|
||||
* @return \SimpleXMLElement
|
||||
*/
|
||||
public function loadAndResolveFile(): \SimpleXMLElement
|
||||
{
|
||||
$content = \file_get_contents($this->path);
|
||||
$strpos = \strpos($content, '?>')+2;
|
||||
if (!\file_exists(__DIR__.'/../doc/entities/generated.ent')) {
|
||||
self::buildEntities();
|
||||
}
|
||||
$path = \realpath(__DIR__.'/../doc/entities/generated.ent');
|
||||
|
||||
|
||||
$content = \substr($content, 0, $strpos)
|
||||
.'<!DOCTYPE refentry SYSTEM "'.$path.'">'
|
||||
.\substr($content, $strpos+1);
|
||||
|
||||
echo 'Loading '.$this->path."\n";
|
||||
$elem = \simplexml_load_string($content, \SimpleXMLElement::class, LIBXML_DTDLOAD | LIBXML_NOENT);
|
||||
if ($elem === false) {
|
||||
throw new \RuntimeException('Invalid XML file for '.$this->path);
|
||||
}
|
||||
$elem->registerXPathNamespace('docbook', 'http://docbook.org/ns/docbook');
|
||||
|
||||
return $elem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the module name in Camelcase.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getModule(): string
|
||||
{
|
||||
return $this->toCamelCase(\basename(\dirname($this->path, 2)));
|
||||
}
|
||||
|
||||
private function toCamelCase(string $str): string
|
||||
{
|
||||
$tokens = preg_split("/[_ ]+/", $str);
|
||||
if ($tokens === false) {
|
||||
throw new \RuntimeException('Unexpected preg_split error'); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$str = '';
|
||||
foreach ($tokens as $token) {
|
||||
$str .= ucfirst($token);
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $array multidimensional string array
|
||||
* @return string[]
|
||||
*/
|
||||
private function arrayFlatten(array $array): array
|
||||
{
|
||||
$result = array();
|
||||
|
||||
foreach ($array as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$result = array_merge($result, $this->arrayFlatten($value));
|
||||
} else {
|
||||
$result[$key] = $value;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function buildEntities(): void
|
||||
{
|
||||
$file1 = \file_get_contents(__DIR__.'/../doc/doc-en/en/language-defs.ent');
|
||||
$file2 = \file_get_contents(__DIR__.'/../doc/doc-en/en/language-snippets.ent');
|
||||
$file3 = \file_get_contents(__DIR__.'/../doc/doc-en/en/extensions.ent');
|
||||
$file4 = \file_get_contents(__DIR__.'/../doc/doc-en/doc-base/entities/global.ent');
|
||||
|
||||
$completeFile = $file1 . self::extractXmlHeader($file2) . self::extractXmlHeader($file3) . $file4;
|
||||
|
||||
\file_put_contents(__DIR__.'/../doc/entities/generated.ent', $completeFile);
|
||||
}
|
||||
|
||||
private static function extractXmlHeader(string $content): string
|
||||
{
|
||||
$strpos = strpos($content, '?>')+2;
|
||||
return substr($content, $strpos);
|
||||
}
|
||||
}
|
11
vendor/thecodingmachine/safe/generator/src/EmptyTypeException.php
vendored
Normal file
11
vendor/thecodingmachine/safe/generator/src/EmptyTypeException.php
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Safe;
|
||||
|
||||
use Exception;
|
||||
|
||||
class EmptyTypeException extends Exception
|
||||
{
|
||||
|
||||
}
|
169
vendor/thecodingmachine/safe/generator/src/FileCreator.php
vendored
Normal file
169
vendor/thecodingmachine/safe/generator/src/FileCreator.php
vendored
Normal file
|
@ -0,0 +1,169 @@
|
|||
<?php
|
||||
|
||||
namespace Safe;
|
||||
|
||||
use function array_merge;
|
||||
use Complex\Exception;
|
||||
use function file_exists;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
||||
|
||||
class FileCreator
|
||||
{
|
||||
/**
|
||||
* This function generate an xls file
|
||||
*
|
||||
* @param string[] $protoFunctions
|
||||
* @param string $path
|
||||
*/
|
||||
public function generateXlsFile(array $protoFunctions, string $path): void
|
||||
{
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$numb = 1;
|
||||
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$sheet->setCellValue('A1', 'Function name');
|
||||
$sheet->setCellValue('B1', 'Status');
|
||||
|
||||
foreach ($protoFunctions as $protoFunction) {
|
||||
if ($protoFunction) {
|
||||
if (strpos($protoFunction, '=') === false && strpos($protoFunction, 'json') === false) {
|
||||
$status = 'classic';
|
||||
} elseif (strpos($protoFunction, 'json')) {
|
||||
$status = 'json';
|
||||
} else {
|
||||
$status = 'opt';
|
||||
}
|
||||
$sheet->setCellValue('A'.$numb, $protoFunction);
|
||||
$sheet->setCellValue('B'.$numb++, $status);
|
||||
}
|
||||
}
|
||||
$writer = new Xlsx($spreadsheet);
|
||||
$writer->save($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function generate an improved php lib function in a php file
|
||||
*
|
||||
* @param Method[] $functions
|
||||
* @param string $path
|
||||
*/
|
||||
public function generatePhpFile(array $functions, string $path): void
|
||||
{
|
||||
$path = rtrim($path, '/').'/';
|
||||
$phpFunctionsByModule = [];
|
||||
foreach ($functions as $function) {
|
||||
$writePhpFunction = new WritePhpFunction($function);
|
||||
$phpFunctionsByModule[$function->getModuleName()][] = $writePhpFunction->getPhpFunctionalFunction();
|
||||
}
|
||||
|
||||
foreach ($phpFunctionsByModule as $module => $phpFunctions) {
|
||||
$lcModule = \lcfirst($module);
|
||||
$stream = \fopen($path.$lcModule.'.php', 'w');
|
||||
if ($stream === false) {
|
||||
throw new \RuntimeException('Unable to write to '.$path);
|
||||
}
|
||||
\fwrite($stream, "<?php\n
|
||||
namespace Safe;
|
||||
|
||||
use Safe\\Exceptions\\".self::toExceptionName($module). ';
|
||||
|
||||
');
|
||||
foreach ($phpFunctions as $phpFunction) {
|
||||
\fwrite($stream, $phpFunction."\n");
|
||||
}
|
||||
\fclose($stream);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Method[] $functions
|
||||
* @return string[]
|
||||
*/
|
||||
private function getFunctionsNameList(array $functions): array
|
||||
{
|
||||
$functionNames = array_map(function (Method $function) {
|
||||
return $function->getFunctionName();
|
||||
}, $functions);
|
||||
$specialCases = require __DIR__.'/../config/specialCasesFunctions.php';
|
||||
return array_merge($functionNames, $specialCases);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This function generate a PHP file containing the list of functions we can handle.
|
||||
*
|
||||
* @param Method[] $functions
|
||||
* @param string $path
|
||||
*/
|
||||
public function generateFunctionsList(array $functions, string $path): void
|
||||
{
|
||||
$functionNames = $this->getFunctionsNameList($functions);
|
||||
$stream = fopen($path, 'w');
|
||||
if ($stream === false) {
|
||||
throw new \RuntimeException('Unable to write to '.$path);
|
||||
}
|
||||
fwrite($stream, "<?php\n
|
||||
return [\n");
|
||||
foreach ($functionNames as $functionName) {
|
||||
fwrite($stream, ' '.\var_export($functionName, true).",\n");
|
||||
}
|
||||
fwrite($stream, "];\n");
|
||||
fclose($stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function generate a rector yml file containing a replacer for all functions
|
||||
*
|
||||
* @param Method[] $functions
|
||||
* @param string $path
|
||||
*/
|
||||
public function generateRectorFile(array $functions, string $path): void
|
||||
{
|
||||
$functionNames = $this->getFunctionsNameList($functions);
|
||||
$stream = fopen($path, 'w');
|
||||
if ($stream === false) {
|
||||
throw new \RuntimeException('Unable to write to '.$path);
|
||||
}
|
||||
fwrite($stream, "# This rector file is replacing all core PHP functions with the equivalent \"safe\" functions
|
||||
services:
|
||||
Rector\Rector\Function_\FunctionReplaceRector:
|
||||
\$oldFunctionToNewFunction:
|
||||
");
|
||||
foreach ($functionNames as $functionName) {
|
||||
fwrite($stream, ' '.$functionName.": 'Safe\\".$functionName."'\n");
|
||||
}
|
||||
fclose($stream);
|
||||
}
|
||||
|
||||
|
||||
public function createExceptionFile(string $moduleName): void
|
||||
{
|
||||
$exceptionName = self::toExceptionName($moduleName);
|
||||
if (!file_exists(__DIR__.'/../../lib/Exceptions/'.$exceptionName.'.php')) {
|
||||
\file_put_contents(
|
||||
__DIR__.'/../../generated/Exceptions/'.$exceptionName.'.php',
|
||||
<<<EOF
|
||||
<?php
|
||||
namespace Safe\Exceptions;
|
||||
|
||||
class {$exceptionName} extends AbstractSafeException
|
||||
{
|
||||
}
|
||||
|
||||
EOF
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the name of the exception class
|
||||
*
|
||||
* @param string $moduleName
|
||||
* @return string
|
||||
*/
|
||||
public static function toExceptionName(string $moduleName): string
|
||||
{
|
||||
return str_replace('-', '', \ucfirst($moduleName)).'Exception';
|
||||
}
|
||||
}
|
107
vendor/thecodingmachine/safe/generator/src/GenerateCommand.php
vendored
Normal file
107
vendor/thecodingmachine/safe/generator/src/GenerateCommand.php
vendored
Normal file
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Safe;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class GenerateCommand extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('generate')
|
||||
->setDescription('Generates the PHP file with all functions.')
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
|
||||
$this->rmGenerated();
|
||||
// Let's build the DTD necessary to load the XML files.
|
||||
DocPage::buildEntities();
|
||||
$scanner = new Scanner(__DIR__ . '/../doc/doc-en/en/reference/');
|
||||
|
||||
$paths = $scanner->getFunctionsPaths();
|
||||
|
||||
[
|
||||
'functions' => $functions,
|
||||
'overloadedFunctions' => $overloadedFunctions
|
||||
] = $scanner->getMethods($paths);
|
||||
|
||||
$output->writeln('These functions have been ignored and must be dealt with manually: '.\implode(', ', $overloadedFunctions));
|
||||
|
||||
$fileCreator = new FileCreator();
|
||||
//$fileCreator->generateXlsFile($protoFunctions, __DIR__ . '/../generated/lib.xls');
|
||||
$fileCreator->generatePhpFile($functions, __DIR__ . '/../../generated/');
|
||||
$fileCreator->generateFunctionsList($functions, __DIR__ . '/../../generated/functionsList.php');
|
||||
$fileCreator->generateRectorFile($functions, __DIR__ . '/../../rector-migrate.yml');
|
||||
|
||||
|
||||
$modules = [];
|
||||
foreach ($functions as $function) {
|
||||
$modules[$function->getModuleName()] = $function->getModuleName();
|
||||
}
|
||||
|
||||
foreach ($modules as $moduleName => $foo) {
|
||||
$fileCreator->createExceptionFile($moduleName);
|
||||
}
|
||||
|
||||
$this->runCsFix($output);
|
||||
|
||||
// Let's require the generated file to check there is no error.
|
||||
$files = \glob(__DIR__.'/../../generated/*.php');
|
||||
|
||||
foreach ($files as $file) {
|
||||
require($file);
|
||||
}
|
||||
|
||||
$files = \glob(__DIR__.'/../../generated/Exceptions/*.php');
|
||||
|
||||
require_once __DIR__.'/../../lib/Exceptions/SafeExceptionInterface.php';
|
||||
require_once __DIR__.'/../../lib/Exceptions/AbstractSafeException.php';
|
||||
foreach ($files as $file) {
|
||||
require($file);
|
||||
}
|
||||
|
||||
// Finally, let's edit the composer.json file
|
||||
$output->writeln('Editing composer.json');
|
||||
ComposerJsonEditor::editFiles(\array_values($modules));
|
||||
}
|
||||
|
||||
private function rmGenerated(): void
|
||||
{
|
||||
$exceptions = \glob(__DIR__.'/../../generated/Exceptions/*.php');
|
||||
|
||||
foreach ($exceptions as $exception) {
|
||||
\unlink($exception);
|
||||
}
|
||||
|
||||
$files = \glob(__DIR__.'/../../generated/*.php');
|
||||
|
||||
foreach ($files as $file) {
|
||||
\unlink($file);
|
||||
}
|
||||
|
||||
if (\file_exists(__DIR__.'/../doc/entities/generated.ent')) {
|
||||
\unlink(__DIR__.'/../doc/entities/generated.ent');
|
||||
}
|
||||
}
|
||||
|
||||
private function runCsFix(OutputInterface $output): void
|
||||
{
|
||||
$process = new Process('vendor/bin/phpcbf', __DIR__.'/../..');
|
||||
$process->setTimeout(600);
|
||||
$process->run(function ($type, $buffer) use ($output) {
|
||||
if (Process::ERR === $type) {
|
||||
echo $output->write('<error>'.$buffer.'</error>');
|
||||
} else {
|
||||
echo $output->write($buffer);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
233
vendor/thecodingmachine/safe/generator/src/Method.php
vendored
Normal file
233
vendor/thecodingmachine/safe/generator/src/Method.php
vendored
Normal file
|
@ -0,0 +1,233 @@
|
|||
<?php
|
||||
|
||||
namespace Safe;
|
||||
|
||||
use Safe\PhpStanFunctions\PhpStanFunction;
|
||||
use Safe\PhpStanFunctions\PhpStanFunctionMapReader;
|
||||
|
||||
class Method
|
||||
{
|
||||
/**
|
||||
* @var \SimpleXMLElement
|
||||
*/
|
||||
private $functionObject;
|
||||
/**
|
||||
* @var \SimpleXMLElement
|
||||
*/
|
||||
private $rootEntity;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $moduleName;
|
||||
/**
|
||||
* @var Parameter[]|null
|
||||
*/
|
||||
private $params = null;
|
||||
/**
|
||||
* @var PhpStanFunctionMapReader
|
||||
*/
|
||||
private $phpStanFunctionMapReader;
|
||||
|
||||
public function __construct(\SimpleXMLElement $_functionObject, \SimpleXMLElement $rootEntity, string $moduleName, PhpStanFunctionMapReader $phpStanFunctionMapReader)
|
||||
{
|
||||
$this->functionObject = $_functionObject;
|
||||
$this->rootEntity = $rootEntity;
|
||||
$this->moduleName = $moduleName;
|
||||
$this->phpStanFunctionMapReader = $phpStanFunctionMapReader;
|
||||
}
|
||||
|
||||
public function getFunctionName(): string
|
||||
{
|
||||
return $this->functionObject->methodname->__toString();
|
||||
}
|
||||
|
||||
public function getReturnType(): string
|
||||
{
|
||||
// If the function returns a boolean, since false is for error, true is for success.
|
||||
// Let's replace this with a "void".
|
||||
$type = $this->functionObject->type->__toString();
|
||||
if ($type === 'bool') {
|
||||
return 'void';
|
||||
}
|
||||
// Some types are completely weird. For instance, oci_new_collection returns a "OCI-Collection" (with a dash, yup)
|
||||
if (\strpos($type, '-') !== false) {
|
||||
return 'mixed';
|
||||
}
|
||||
|
||||
return Type::toRootNamespace($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Parameter[]
|
||||
*/
|
||||
public function getParams(): array
|
||||
{
|
||||
if ($this->params === null) {
|
||||
if (!isset($this->functionObject->methodparam)) {
|
||||
return [];
|
||||
}
|
||||
$phpStanFunction = $this->getPhpStanData();
|
||||
$params = [];
|
||||
$i=1;
|
||||
foreach ($this->functionObject->methodparam as $param) {
|
||||
$notes = $this->stripReturnFalseText($this->getStringForXPath("(//docbook:refsect1[@role='parameters']//docbook:varlistentry)[$i]//docbook:note//docbook:para"));
|
||||
$i++;
|
||||
|
||||
if (preg_match('/This parameter has been removed in PHP (\d+\.\d+\.\d+)/', $notes, $matches)) {
|
||||
$removedVersion = $matches[1];
|
||||
[$major, $minor] = explode('.', $removedVersion);
|
||||
if ($major < 7 || ($major == 7 && $minor == 0)) {
|
||||
// Ignore parameter if it was removed before PHP 7.1
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$params[] = new Parameter($param, $phpStanFunction);
|
||||
}
|
||||
$this->params = $params;
|
||||
}
|
||||
return $this->params;
|
||||
}
|
||||
|
||||
public function getPhpDoc(): string
|
||||
{
|
||||
$str = "/**\n".
|
||||
implode("\n", array_map(function (string $line) {
|
||||
return ' * '.ltrim($line);
|
||||
}, \explode("\n", \strip_tags($this->getDocBlock()))))
|
||||
."\n */\n";
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
private function getDocBlock(): string
|
||||
{
|
||||
$str = $this->stripReturnFalseText($this->getStringForXPath("//docbook:refsect1[@role='description']/docbook:para"));
|
||||
$str .= "\n\n";
|
||||
|
||||
$i=1;
|
||||
foreach ($this->getParams() as $parameter) {
|
||||
$str .= '@param '.$parameter->getBestType().' $'.$parameter->getParameter().' ';
|
||||
$str .= $this->getStringForXPath("(//docbook:refsect1[@role='parameters']//docbook:varlistentry)[$i]//docbook:para")."\n";
|
||||
$i++;
|
||||
}
|
||||
|
||||
$bestReturnType = $this->getBestReturnType();
|
||||
if ($bestReturnType !== 'void') {
|
||||
$str .= '@return '.$bestReturnType. ' ' .$this->getReturnDoc()."\n";
|
||||
}
|
||||
|
||||
$str .= '@throws '.FileCreator::toExceptionName($this->getModuleName()). "\n";
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
private function getReturnDoc(): string
|
||||
{
|
||||
$returnDoc = $this->getStringForXPath("//docbook:refsect1[@role='returnvalues']/docbook:para");
|
||||
return $this->stripReturnFalseText($returnDoc);
|
||||
}
|
||||
|
||||
private function stripReturnFalseText(string $string): string
|
||||
{
|
||||
$string = \strip_tags($string);
|
||||
$string = $this->removeString($string, 'or FALSE on failure');
|
||||
$string = $this->removeString($string, 'may return FALSE');
|
||||
$string = $this->removeString($string, 'and FALSE on failure');
|
||||
$string = $this->removeString($string, 'on success, or FALSE otherwise');
|
||||
$string = $this->removeString($string, 'or FALSE on error');
|
||||
$string = $this->removeString($string, 'or FALSE if an error occurred');
|
||||
$string = $this->removeString($string, 'the function will return TRUE, or FALSE otherwise');
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a string, even if the string is split on multiple lines.
|
||||
* @param string $string
|
||||
* @param string $search
|
||||
* @return string
|
||||
*/
|
||||
private function removeString(string $string, string $search): string
|
||||
{
|
||||
$search = str_replace(' ', '\s+', $search);
|
||||
$result = preg_replace('/[\s\,]*'.$search.'/m', '', $string);
|
||||
if ($result === null) {
|
||||
throw new \RuntimeException('An error occurred while calling preg_replace');
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function getStringForXPath(string $xpath): string
|
||||
{
|
||||
$paragraphs = $this->rootEntity->xpath($xpath);
|
||||
if ($paragraphs === false) {
|
||||
throw new \RuntimeException('Error while performing Xpath request.');
|
||||
}
|
||||
$str = '';
|
||||
foreach ($paragraphs as $paragraph) {
|
||||
$str .= $this->getInnerXml($paragraph)."\n\n";
|
||||
}
|
||||
return trim($str);
|
||||
}
|
||||
|
||||
private function getBestReturnType(): ?string
|
||||
{
|
||||
$phpStanFunction = $this->getPhpStanData();
|
||||
// Get the type from PhpStan database first, then from the php doc.
|
||||
if ($phpStanFunction !== null) {
|
||||
return Type::toRootNamespace($phpStanFunction->getReturnType());
|
||||
} else {
|
||||
return Type::toRootNamespace($this->getReturnType());
|
||||
}
|
||||
}
|
||||
|
||||
private function getPhpStanData(): ?PhpStanFunction
|
||||
{
|
||||
$functionName = $this->getFunctionName();
|
||||
if (!$this->phpStanFunctionMapReader->hasFunction($functionName)) {
|
||||
return null;
|
||||
}
|
||||
return $this->phpStanFunctionMapReader->getFunction($functionName);
|
||||
}
|
||||
|
||||
private function getInnerXml(\SimpleXMLElement $SimpleXMLElement): string
|
||||
{
|
||||
$element_name = $SimpleXMLElement->getName();
|
||||
$inner_xml = $SimpleXMLElement->asXML();
|
||||
if ($inner_xml === false) {
|
||||
throw new \RuntimeException('Unable to serialize to XML');
|
||||
}
|
||||
$inner_xml = str_replace(['<'.$element_name.'>', '</'.$element_name.'>'], '', $inner_xml);
|
||||
$inner_xml = trim($inner_xml);
|
||||
return $inner_xml;
|
||||
}
|
||||
|
||||
public function getModuleName(): string
|
||||
{
|
||||
return $this->moduleName;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function is overloaded if at least one parameter is optional with no default value and this parameter is not by reference.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isOverloaded(): bool
|
||||
{
|
||||
foreach ($this->getParams() as $parameter) {
|
||||
if ($parameter->isOptionalWithNoDefault() && !$parameter->isByReference()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function cloneAndRemoveAParameter(): Method
|
||||
{
|
||||
$new = clone $this;
|
||||
$params = $this->getParams();
|
||||
\array_pop($params);
|
||||
$new->params = $params;
|
||||
return $new;
|
||||
}
|
||||
}
|
160
vendor/thecodingmachine/safe/generator/src/Parameter.php
vendored
Normal file
160
vendor/thecodingmachine/safe/generator/src/Parameter.php
vendored
Normal file
|
@ -0,0 +1,160 @@
|
|||
<?php
|
||||
namespace Safe;
|
||||
|
||||
use Safe\PhpStanFunctions\PhpStanFunction;
|
||||
|
||||
class Parameter
|
||||
{
|
||||
/**
|
||||
* @var \SimpleXMLElement
|
||||
*/
|
||||
private $parameter;
|
||||
/**
|
||||
* @var PhpStanFunction|null
|
||||
*/
|
||||
private $phpStanFunction;
|
||||
|
||||
public function __construct(\SimpleXMLElement $parameter, ?PhpStanFunction $phpStanFunction)
|
||||
{
|
||||
$this->parameter = $parameter;
|
||||
$this->phpStanFunction = $phpStanFunction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type as declared in the doc.
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
$type = $this->parameter->type->__toString();
|
||||
$strType = Type::toRootNamespace($type);
|
||||
if ($strType !== 'mixed' && $strType !== 'resource' && $this->phpStanFunction !== null) {
|
||||
$phpStanParameter = $this->phpStanFunction->getParameter($this->getParameter());
|
||||
if ($phpStanParameter) {
|
||||
// Let's make the parameter nullable if it is by reference and is used only for writing.
|
||||
if ($phpStanParameter->isWriteOnly()) {
|
||||
$strType = '?'.$strType;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $strType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type as declared in the doc.
|
||||
* @return string
|
||||
*/
|
||||
public function getBestType(): string
|
||||
{
|
||||
// Get the type from PhpStan database first, then from the php doc.
|
||||
if ($this->phpStanFunction !== null) {
|
||||
$phpStanParameter = $this->phpStanFunction->getParameter($this->getParameter());
|
||||
if ($phpStanParameter) {
|
||||
try {
|
||||
return $phpStanParameter->getType();
|
||||
} catch (EmptyTypeException $e) {
|
||||
// If the type is empty in PHPStan, let's fallback to documentation.
|
||||
return $this->getType();
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->getType();
|
||||
}
|
||||
|
||||
/*
|
||||
* @return string
|
||||
*/
|
||||
public function getParameter(): string
|
||||
{
|
||||
if ($this->isVariadic()) {
|
||||
return 'params';
|
||||
}
|
||||
// The db2_bind_param method has parameters with a dash in it... yep... (patch submitted)
|
||||
return \str_replace('-', '_', $this->parameter->parameter->__toString());
|
||||
}
|
||||
|
||||
public function isByReference(): bool
|
||||
{
|
||||
return ((string)$this->parameter->parameter['role']) === 'reference';
|
||||
}
|
||||
|
||||
/**
|
||||
* Some parameters can be optional with no default value. In this case, the function is "overloaded" (which is not
|
||||
* possible in user-land but possible in core...)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isOptionalWithNoDefault(): bool
|
||||
{
|
||||
if (((string)$this->parameter['choice']) !== 'opt') {
|
||||
return false;
|
||||
}
|
||||
if (!$this->hasDefaultValue()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$initializer = $this->getInitializer();
|
||||
// Some default value have weird values. For instance, first parameter of "mb_internal_encoding" has default value "mb_internal_encoding()"
|
||||
if ($initializer !== 'array()' && strpos($initializer, '(') !== false) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isVariadic(): bool
|
||||
{
|
||||
return $this->parameter->parameter->__toString() === '...';
|
||||
}
|
||||
|
||||
public function isNullable(): bool
|
||||
{
|
||||
if ($this->phpStanFunction !== null) {
|
||||
$phpStanParameter = $this->phpStanFunction->getParameter($this->getParameter());
|
||||
if ($phpStanParameter) {
|
||||
return $phpStanParameter->isNullable();
|
||||
}
|
||||
}
|
||||
return $this->hasDefaultValue() && $this->getDefaultValue() === 'null';
|
||||
}
|
||||
|
||||
/*
|
||||
* @return string
|
||||
*/
|
||||
public function getInitializer(): string
|
||||
{
|
||||
return \str_replace(['<constant>', '</constant>'], '', $this->getInnerXml($this->parameter->initializer));
|
||||
}
|
||||
|
||||
public function hasDefaultValue(): bool
|
||||
{
|
||||
return isset($this->parameter->initializer);
|
||||
}
|
||||
|
||||
public function getDefaultValue(): ?string
|
||||
{
|
||||
if (!$this->hasDefaultValue()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$initializer = $this->getInitializer();
|
||||
|
||||
// Some default value have weird values. For instance, first parameter of "mb_internal_encoding" has default value "mb_internal_encoding()"
|
||||
if (strpos($initializer, '(') !== false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $initializer;
|
||||
}
|
||||
|
||||
private function getInnerXml(\SimpleXMLElement $SimpleXMLElement): string
|
||||
{
|
||||
$element_name = $SimpleXMLElement->getName();
|
||||
$inner_xml = $SimpleXMLElement->asXML();
|
||||
if ($inner_xml === false) {
|
||||
throw new \RuntimeException('Unable to serialize to XML');
|
||||
}
|
||||
$inner_xml = str_replace(['<'.$element_name.'>', '</'.$element_name.'>', '<'.$element_name.'/>'], '', $inner_xml);
|
||||
$inner_xml = trim($inner_xml);
|
||||
return $inner_xml;
|
||||
}
|
||||
}
|
53
vendor/thecodingmachine/safe/generator/src/PhpStanFunctions/PhpStanFunction.php
vendored
Normal file
53
vendor/thecodingmachine/safe/generator/src/PhpStanFunctions/PhpStanFunction.php
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Safe\PhpStanFunctions;
|
||||
|
||||
class PhpStanFunction
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $returnType;
|
||||
|
||||
/**
|
||||
* @var PhpStanParameter[]
|
||||
*/
|
||||
private $parameters = [];
|
||||
|
||||
/**
|
||||
* @param mixed[] $signature
|
||||
*/
|
||||
public function __construct(array $signature)
|
||||
{
|
||||
$this->returnType = \array_shift($signature);
|
||||
foreach ($signature as $name => $type) {
|
||||
$param = new PhpStanParameter($name, $type);
|
||||
$this->parameters[$param->getName()] = $param;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getReturnType(): string
|
||||
{
|
||||
if ($this->returnType === 'bool') {
|
||||
$this->returnType = 'void';
|
||||
}
|
||||
return \str_replace(['|bool', '|false'], '', $this->returnType);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string,PhpStanParameter>
|
||||
*/
|
||||
public function getParameters(): array
|
||||
{
|
||||
return $this->parameters;
|
||||
}
|
||||
|
||||
public function getParameter(string $name): ?PhpStanParameter
|
||||
{
|
||||
return $this->parameters[$name] ?? null;
|
||||
}
|
||||
}
|
27
vendor/thecodingmachine/safe/generator/src/PhpStanFunctions/PhpStanFunctionMapReader.php
vendored
Normal file
27
vendor/thecodingmachine/safe/generator/src/PhpStanFunctions/PhpStanFunctionMapReader.php
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Safe\PhpStanFunctions;
|
||||
|
||||
class PhpStanFunctionMapReader
|
||||
{
|
||||
/**
|
||||
* @var array<string, array>
|
||||
*/
|
||||
private $functionMap;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->functionMap = require __DIR__.'/../../vendor/phpstan/phpstan/src/Reflection/SignatureMap/functionMap.php';
|
||||
}
|
||||
|
||||
public function hasFunction(string $functionName): bool
|
||||
{
|
||||
return isset($this->functionMap[$functionName]);
|
||||
}
|
||||
|
||||
public function getFunction(string $functionName): PhpStanFunction
|
||||
{
|
||||
return new PhpStanFunction($this->functionMap[$functionName]);
|
||||
}
|
||||
}
|
124
vendor/thecodingmachine/safe/generator/src/PhpStanFunctions/PhpStanParameter.php
vendored
Normal file
124
vendor/thecodingmachine/safe/generator/src/PhpStanFunctions/PhpStanParameter.php
vendored
Normal file
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Safe\PhpStanFunctions;
|
||||
|
||||
use Safe\Type;
|
||||
|
||||
class PhpStanParameter
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $type;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $optional = false;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $variadic = false;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $byReference = false;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $nullable = false;
|
||||
/**
|
||||
* Whether the parameter is "write only" (applies only to "by reference" parameters)
|
||||
* @var bool
|
||||
*/
|
||||
private $writeOnly = false;
|
||||
|
||||
public function __construct(string $name, string $type)
|
||||
{
|
||||
if (\strpos($name, '=') !== false) {
|
||||
$this->optional = true;
|
||||
}
|
||||
if (\strpos($name, '...') !== false) {
|
||||
$this->variadic = true;
|
||||
}
|
||||
if (\strpos($name, '&') !== false) {
|
||||
$this->byReference = true;
|
||||
}
|
||||
if (\strpos($name, '&w_') !== false) {
|
||||
$this->writeOnly = true;
|
||||
}
|
||||
$name = \str_replace(['&rw_', '&w_'], '', $name);
|
||||
$name = trim($name, '=.&');
|
||||
|
||||
$this->name = $name;
|
||||
|
||||
if (\strpos($type, '?') !== false) {
|
||||
$type = \str_replace('?', '', $type).'|null';
|
||||
$this->nullable = true;
|
||||
}
|
||||
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return Type::toRootNamespace($this->type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isOptional(): bool
|
||||
{
|
||||
return $this->optional;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isVariadic(): bool
|
||||
{
|
||||
return $this->variadic;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isByReference(): bool
|
||||
{
|
||||
return $this->byReference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the parameter is "write only" (applies only to "by reference" parameters)
|
||||
* @return bool
|
||||
*/
|
||||
public function isWriteOnly(): bool
|
||||
{
|
||||
return $this->writeOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isNullable(): bool
|
||||
{
|
||||
return $this->nullable;
|
||||
}
|
||||
}
|
38
vendor/thecodingmachine/safe/generator/src/ScanObjectsCommand.php
vendored
Normal file
38
vendor/thecodingmachine/safe/generator/src/ScanObjectsCommand.php
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Safe;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class ScanObjectsCommand extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('scan-objects')
|
||||
->setDescription('Displays all methods of all objects not handled yet by Safe.')
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$scanner = new Scanner(__DIR__ . '/../doc/doc-en/en/reference/');
|
||||
|
||||
$paths = $scanner->getMethodsPaths();
|
||||
|
||||
[
|
||||
'functions' => $functions,
|
||||
'overloadedFunctions' => $overloadedFunctions
|
||||
] = $scanner->getMethods($paths);
|
||||
|
||||
foreach ($functions as $function) {
|
||||
$name = $function->getFunctionName();
|
||||
$output->writeln('Found method '.$name);
|
||||
}
|
||||
|
||||
$output->writeln('These methods are overloaded: '.\implode(', ', $overloadedFunctions));
|
||||
}
|
||||
}
|
124
vendor/thecodingmachine/safe/generator/src/Scanner.php
vendored
Normal file
124
vendor/thecodingmachine/safe/generator/src/Scanner.php
vendored
Normal file
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
|
||||
namespace Safe;
|
||||
|
||||
use function array_merge;
|
||||
use function iterator_to_array;
|
||||
use Safe\PhpStanFunctions\PhpStanFunctionMapReader;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use SplFileInfo;
|
||||
|
||||
class Scanner
|
||||
{
|
||||
/*
|
||||
* @var string
|
||||
*/
|
||||
private $path;
|
||||
|
||||
public function __construct(string $path)
|
||||
{
|
||||
$this->path = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, SplFileInfo>
|
||||
*/
|
||||
public function getFunctionsPaths(): array
|
||||
{
|
||||
$finder = new Finder();
|
||||
$finder->in($this->path.'*/functions/')->name('*.xml')->sortByName();
|
||||
|
||||
return iterator_to_array($finder);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, SplFileInfo>
|
||||
*/
|
||||
public function getMethodsPaths(): array
|
||||
{
|
||||
$finder = new Finder();
|
||||
$finder->in($this->path)->notPath('functions')->name('*.xml')->sortByName();
|
||||
|
||||
return iterator_to_array($finder);
|
||||
}
|
||||
|
||||
private $ignoredFunctions;
|
||||
|
||||
/**
|
||||
* Returns the list of functions that must be ignored.
|
||||
* @return string[]
|
||||
*/
|
||||
private function getIgnoredFunctions(): array
|
||||
{
|
||||
if ($this->ignoredFunctions === null) {
|
||||
$ignoredFunctions = require __DIR__.'/../config/ignoredFunctions.php';
|
||||
$specialCaseFunctions = require __DIR__.'/../config/specialCasesFunctions.php';
|
||||
|
||||
$this->ignoredFunctions = array_merge($ignoredFunctions, $specialCaseFunctions);
|
||||
}
|
||||
return $this->ignoredFunctions;
|
||||
}
|
||||
|
||||
private $ignoredModules;
|
||||
|
||||
/**
|
||||
* Returns the list of modules that must be ignored.
|
||||
* @return string[]
|
||||
*/
|
||||
private function getIgnoredModules(): array
|
||||
{
|
||||
if ($this->ignoredModules === null) {
|
||||
$this->ignoredModules = require __DIR__.'/../config/ignoredModules.php';
|
||||
}
|
||||
return $this->ignoredModules;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SplFileInfo[] $paths
|
||||
* @return mixed[] Structure: ['functions'=>Method[], 'overloadedFunctions'=>string[]]
|
||||
*/
|
||||
public function getMethods(array $paths): array
|
||||
{
|
||||
$functions = [];
|
||||
$overloadedFunctions = [];
|
||||
|
||||
$phpStanFunctionMapReader = new PhpStanFunctionMapReader();
|
||||
$ignoredFunctions = $this->getIgnoredFunctions();
|
||||
$ignoredFunctions = \array_combine($ignoredFunctions, $ignoredFunctions);
|
||||
$ignoredModules = $this->getIgnoredModules();
|
||||
$ignoredModules = \array_combine($ignoredModules, $ignoredModules);
|
||||
foreach ($paths as $path) {
|
||||
$module = \basename(\dirname($path, 2));
|
||||
if (isset($ignoredModules[$module])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$docPage = new DocPage($path);
|
||||
if ($docPage->detectFalsyFunction()) {
|
||||
$functionObjects = $docPage->getMethodSynopsis();
|
||||
if (count($functionObjects) > 1) {
|
||||
$overloadedFunctions = array_merge($overloadedFunctions, \array_map(function ($functionObject) {
|
||||
return $functionObject->methodname->__toString();
|
||||
}, $functionObjects));
|
||||
$overloadedFunctions = \array_filter($overloadedFunctions, function (string $functionName) use ($ignoredFunctions) {
|
||||
return !isset($ignoredFunctions[$functionName]);
|
||||
});
|
||||
continue;
|
||||
}
|
||||
$rootEntity = $docPage->loadAndResolveFile();
|
||||
foreach ($functionObjects as $functionObject) {
|
||||
$function = new Method($functionObject, $rootEntity, $docPage->getModule(), $phpStanFunctionMapReader);
|
||||
if (isset($ignoredFunctions[$function->getFunctionName()])) {
|
||||
continue;
|
||||
}
|
||||
$functions[] = $function;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'functions' => $functions,
|
||||
'overloadedFunctions' => \array_unique($overloadedFunctions)
|
||||
];
|
||||
}
|
||||
}
|
42
vendor/thecodingmachine/safe/generator/src/Type.php
vendored
Normal file
42
vendor/thecodingmachine/safe/generator/src/Type.php
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Safe;
|
||||
|
||||
class Type
|
||||
{
|
||||
/**
|
||||
* Returns true if the type passed in parameter is a class, false if it is scalar or resource
|
||||
*
|
||||
* @param string $type
|
||||
* @return bool
|
||||
*/
|
||||
private static function isClass(string $type): bool
|
||||
{
|
||||
if ($type === '') {
|
||||
throw new EmptyTypeException('Empty type passed');
|
||||
}
|
||||
if ($type === 'stdClass') {
|
||||
return true;
|
||||
}
|
||||
// Classes start with uppercase letters. Otherwise, it's most likely a scalar.
|
||||
if ($type[0] === strtoupper($type[0])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put classes in the root namespace
|
||||
*
|
||||
* @param string $type
|
||||
* @return string
|
||||
*/
|
||||
public static function toRootNamespace(string $type): string
|
||||
{
|
||||
if (self::isClass($type)) {
|
||||
return '\\'.$type;
|
||||
}
|
||||
return $type;
|
||||
}
|
||||
}
|
190
vendor/thecodingmachine/safe/generator/src/WritePhpFunction.php
vendored
Normal file
190
vendor/thecodingmachine/safe/generator/src/WritePhpFunction.php
vendored
Normal file
|
@ -0,0 +1,190 @@
|
|||
<?php
|
||||
|
||||
namespace Safe;
|
||||
|
||||
class WritePhpFunction
|
||||
{
|
||||
/**
|
||||
* @var Method
|
||||
*/
|
||||
private $method;
|
||||
|
||||
public function __construct(Method $method)
|
||||
{
|
||||
$this->method = $method;
|
||||
}
|
||||
|
||||
/*
|
||||
* @return string
|
||||
*/
|
||||
public function getPhpPrototypeFunction(): string
|
||||
{
|
||||
if ($this->method->getFunctionName()) {
|
||||
return 'function '.$this->method->getFunctionName().'('.$this->displayParamsWithType($this->method->getParams()).')'.': '.$this->method->getReturnType().'{}';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/*
|
||||
* return string
|
||||
*/
|
||||
public function getPhpFunctionalFunction(): string
|
||||
{
|
||||
if ($this->getPhpPrototypeFunction()) {
|
||||
return $this->writePhpFunction();
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/*
|
||||
* return string
|
||||
*/
|
||||
private function writePhpFunction(): string
|
||||
{
|
||||
$phpFunction = $this->method->getPhpDoc();
|
||||
if ($this->method->getReturnType() !== 'mixed' && $this->method->getReturnType() !== 'resource') {
|
||||
$returnType = ': ' . $this->method->getReturnType();
|
||||
} else {
|
||||
$returnType = '';
|
||||
}
|
||||
$returnStatement = '';
|
||||
if ($this->method->getReturnType() !== 'void') {
|
||||
$returnStatement = " return \$result;\n";
|
||||
}
|
||||
$moduleName = $this->method->getModuleName();
|
||||
|
||||
$phpFunction .= "function {$this->method->getFunctionName()}({$this->displayParamsWithType($this->method->getParams())}){$returnType}
|
||||
{
|
||||
error_clear_last();
|
||||
";
|
||||
|
||||
if (!$this->method->isOverloaded()) {
|
||||
$phpFunction .= ' $result = '.$this->printFunctionCall($this->method);
|
||||
} else {
|
||||
$method = $this->method;
|
||||
$inElse = false;
|
||||
do {
|
||||
$lastParameter = $method->getParams()[count($method->getParams())-1];
|
||||
if ($inElse) {
|
||||
$phpFunction .= ' else';
|
||||
} else {
|
||||
$phpFunction .= ' ';
|
||||
}
|
||||
if ($lastParameter->isVariadic()) {
|
||||
$defaultValueToString = '[]';
|
||||
} else {
|
||||
$defaultValue = $lastParameter->getDefaultValue();
|
||||
$defaultValueToString = $this->defaultValueToString($defaultValue);
|
||||
}
|
||||
$phpFunction .= 'if ($'.$lastParameter->getParameter().' !== '.$defaultValueToString.') {'."\n";
|
||||
$phpFunction .= ' $result = '.$this->printFunctionCall($method)."\n";
|
||||
$phpFunction .= ' }';
|
||||
$inElse = true;
|
||||
$method = $method->cloneAndRemoveAParameter();
|
||||
if (!$method->isOverloaded()) {
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
$phpFunction .= 'else {'."\n";
|
||||
$phpFunction .= ' $result = '.$this->printFunctionCall($method)."\n";
|
||||
$phpFunction .= ' }';
|
||||
}
|
||||
|
||||
$phpFunction .= $this->generateExceptionCode($moduleName, $this->method).$returnStatement. '}
|
||||
|
||||
';
|
||||
|
||||
return $phpFunction;
|
||||
}
|
||||
|
||||
private function generateExceptionCode(string $moduleName, Method $method) : string
|
||||
{
|
||||
// Special case for CURL: we need the first argument of the method if this is a resource.
|
||||
if ($moduleName === 'Curl') {
|
||||
$params = $method->getParams();
|
||||
if (\count($params) > 0 && $params[0]->getParameter() === 'ch') {
|
||||
return "
|
||||
if (\$result === false) {
|
||||
throw CurlException::createFromCurlResource(\$ch);
|
||||
}
|
||||
";
|
||||
}
|
||||
}
|
||||
|
||||
$exceptionName = FileCreator::toExceptionName($moduleName);
|
||||
return "
|
||||
if (\$result === false) {
|
||||
throw {$exceptionName}::createFromPhpError();
|
||||
}
|
||||
";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Parameter[] $params
|
||||
* @return string
|
||||
*/
|
||||
private function displayParamsWithType(array $params): string
|
||||
{
|
||||
$paramsAsString = [];
|
||||
$optDetected = false;
|
||||
|
||||
foreach ($params as $param) {
|
||||
$paramAsString = '';
|
||||
if ($param->getType() !== 'mixed' && $param->getType() !== 'resource') {
|
||||
if ($param->isNullable()) {
|
||||
$paramAsString .= '?';
|
||||
}
|
||||
$paramAsString .= $param->getType().' ';
|
||||
}
|
||||
|
||||
$paramName = $param->getParameter();
|
||||
if ($param->isVariadic()) {
|
||||
$paramAsString .= ' ...$'.$paramName;
|
||||
} else {
|
||||
if ($param->isByReference()) {
|
||||
$paramAsString .= '&';
|
||||
}
|
||||
$paramAsString .= '$'.$paramName;
|
||||
}
|
||||
|
||||
|
||||
if ($param->hasDefaultValue() || $param->isOptionalWithNoDefault()) {
|
||||
$optDetected = true;
|
||||
}
|
||||
$defaultValue = $param->getDefaultValue();
|
||||
if ($defaultValue !== null) {
|
||||
$paramAsString .= ' = '.$this->defaultValueToString($defaultValue);
|
||||
} elseif ($optDetected && !$param->isVariadic()) {
|
||||
$paramAsString .= ' = null';
|
||||
}
|
||||
$paramsAsString[] = $paramAsString;
|
||||
}
|
||||
|
||||
return implode(', ', $paramsAsString);
|
||||
}
|
||||
|
||||
private function printFunctionCall(Method $function): string
|
||||
{
|
||||
$functionCall = '\\'.$function->getFunctionName().'(';
|
||||
$functionCall .= implode(', ', \array_map(function (Parameter $parameter) {
|
||||
$str = '';
|
||||
if ($parameter->isVariadic()) {
|
||||
$str = '...';
|
||||
}
|
||||
return $str.'$'.$parameter->getParameter();
|
||||
}, $function->getParams()));
|
||||
$functionCall .= ');';
|
||||
return $functionCall;
|
||||
}
|
||||
|
||||
private function defaultValueToString(?string $defaultValue): string
|
||||
{
|
||||
if ($defaultValue === null) {
|
||||
return 'null';
|
||||
}
|
||||
if ($defaultValue === '') {
|
||||
return "''";
|
||||
}
|
||||
return $defaultValue;
|
||||
}
|
||||
}
|
28
vendor/thecodingmachine/safe/generator/tests/DocPageTest.php
vendored
Normal file
28
vendor/thecodingmachine/safe/generator/tests/DocPageTest.php
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace Safe;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class DocPageTest extends TestCase
|
||||
{
|
||||
public function testDetectFalsyFunction() {
|
||||
$pregMatch = new DocPage(__DIR__ . '/../doc/doc-en/en/reference/pcre/functions/preg-match.xml');
|
||||
$implode = new DocPage(__DIR__ . '/../doc/doc-en/en/reference/strings/functions/implode.xml');
|
||||
$getCwd = new DocPage(__DIR__ . '/../doc/doc-en/en/reference/dir/functions/getcwd.xml');
|
||||
$setTime = new DocPage(__DIR__ . '/../doc/doc-en/en/reference/datetime/datetime/settime.xml');
|
||||
$filesize = new DocPage(__DIR__ . '/../doc/doc-en/en/reference/filesystem/functions/filesize.xml');
|
||||
$sessionRegister = new DocPage(__DIR__ . '/../doc/doc-en/en/reference/session/functions/session-register.xml');
|
||||
$mcryptDecrypt = new DocPage(__DIR__ . '/../doc/doc-en/en/reference/mcrypt/functions/mcrypt-decrypt.xml');
|
||||
$fsockopen = new DocPage(__DIR__ . '/../doc/doc-en/en/reference/network/functions/fsockopen.xml');
|
||||
|
||||
$this->assertTrue($pregMatch->detectFalsyFunction());
|
||||
$this->assertFalse($implode->detectFalsyFunction());
|
||||
$this->assertTrue($getCwd->detectFalsyFunction());
|
||||
$this->assertTrue($setTime->detectFalsyFunction());
|
||||
$this->assertTrue($filesize->detectFalsyFunction());
|
||||
$this->assertFalse($sessionRegister->detectFalsyFunction());
|
||||
$this->assertTrue($mcryptDecrypt->detectFalsyFunction());
|
||||
$this->assertTrue($fsockopen->detectFalsyFunction());
|
||||
}
|
||||
}
|
142
vendor/thecodingmachine/safe/generator/tests/GeneratedFilesTest.php
vendored
Normal file
142
vendor/thecodingmachine/safe/generator/tests/GeneratedFilesTest.php
vendored
Normal file
|
@ -0,0 +1,142 @@
|
|||
<?php
|
||||
|
||||
namespace Safe;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use function restore_error_handler;
|
||||
use Safe\Exceptions\StringsException;
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* This test must be called AFTER generation of files has occurred
|
||||
*/
|
||||
class GeneratedFilesTest extends TestCase
|
||||
{
|
||||
public function testSprintf()
|
||||
{
|
||||
require_once __DIR__ . '/../../generated/strings.php';
|
||||
require_once __DIR__ . '/../../lib/Exceptions/SafeExceptionInterface.php';
|
||||
require_once __DIR__ . '/../../lib/Exceptions/AbstractSafeException.php';
|
||||
require_once __DIR__ . '/../../generated/Exceptions/StringsException.php';
|
||||
|
||||
$this->assertSame('foo', sprintf('foo'));
|
||||
$this->assertSame('foobar', sprintf('foo%s', 'bar'));
|
||||
$this->assertSame('foobarbaz', sprintf('foo%s%s', 'bar', 'baz'));
|
||||
|
||||
set_error_handler(function () {
|
||||
});
|
||||
try {
|
||||
$this->expectException(StringsException::class);
|
||||
sprintf('foo%s%s', 'bar');
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
}
|
||||
|
||||
public function testPregMatch()
|
||||
{
|
||||
require_once __DIR__ . '/../../generated/pcre.php';
|
||||
require_once __DIR__ . '/../../lib/Exceptions/SafeExceptionInterface.php';
|
||||
require_once __DIR__ . '/../../lib/Exceptions/AbstractSafeException.php';
|
||||
require_once __DIR__ . '/../../lib/Exceptions/PcreException.php';
|
||||
|
||||
|
||||
$url = 'https://open.spotify.com/track/0nCqpKBrvDchO1BIvt7DTR?si=iLUKDfkLSy-IpnLA7qImnw';
|
||||
$spotifyRegex = "/https?:\/\/(?:embed\.|open\.)(?:spotify\.com\/)(?:track\/|\?uri=spotify:track:)((\w|-){22})/";
|
||||
preg_match($spotifyRegex, $url, $matches);
|
||||
\preg_match($spotifyRegex, $url, $originalMatches);
|
||||
$this->assertSame($originalMatches, $matches);
|
||||
}
|
||||
|
||||
public function testObjects()
|
||||
{
|
||||
require_once __DIR__ . '/../../generated/simplexml.php';
|
||||
require_once __DIR__ . '/../../lib/Exceptions/SafeExceptionInterface.php';
|
||||
require_once __DIR__ . '/../../lib/Exceptions/AbstractSafeException.php';
|
||||
require_once __DIR__ . '/../../generated/Exceptions/SimplexmlException.php';
|
||||
|
||||
$xmlStr = <<<XML
|
||||
<?xml version='1.0' standalone='yes'?>
|
||||
<movies>
|
||||
<movie>
|
||||
<title>PHP: Behind the Parser</title>
|
||||
</movie>
|
||||
</movies>
|
||||
XML;
|
||||
|
||||
$movies = simplexml_load_string($xmlStr);
|
||||
|
||||
$this->assertInstanceOf(SimpleXMLElement::class, $movies);
|
||||
|
||||
$domImplementation = new \DOMImplementation();
|
||||
$doc = $domImplementation->createDocument(null, 'foo');
|
||||
|
||||
$xmlElem = simplexml_import_dom($doc);
|
||||
$this->assertInstanceOf(SimpleXMLElement::class, $xmlElem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the limit parameter is nullable.
|
||||
* See https://github.com/thecodingmachine/safe/issues/56
|
||||
*/
|
||||
public function testPregSplit()
|
||||
{
|
||||
require_once __DIR__ . '/../../generated/pcre.php';
|
||||
require_once __DIR__ . '/../../lib/Exceptions/SafeExceptionInterface.php';
|
||||
require_once __DIR__ . '/../../lib/Exceptions/AbstractSafeException.php';
|
||||
require_once __DIR__ . '/../../lib/Exceptions/PcreException.php';
|
||||
|
||||
$keywords = preg_split("/[\s,]+/", "hypertext language, programming", null);
|
||||
$this->assertSame(['hypertext', 'language', 'programming'], $keywords);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests that parameters with "time()" default value are correctly handled.
|
||||
*/
|
||||
public function testStrtotime()
|
||||
{
|
||||
require_once __DIR__ . '/../../generated/datetime.php';
|
||||
require_once __DIR__ . '/../../lib/Exceptions/SafeExceptionInterface.php';
|
||||
require_once __DIR__ . '/../../lib/Exceptions/AbstractSafeException.php';
|
||||
require_once __DIR__ . '/../../generated/Exceptions/DatetimeException.php';
|
||||
|
||||
$this->assertSame(\strtotime('+1 day'), strtotime('+1 day'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that parameters signature can be not passed. See https://github.com/thecodingmachine/safe/issues/86
|
||||
*/
|
||||
public function testOpenSslSign()
|
||||
{
|
||||
require_once __DIR__ . '/../../generated/openssl.php';
|
||||
require_once __DIR__ . '/../../lib/Exceptions/SafeExceptionInterface.php';
|
||||
require_once __DIR__ . '/../../lib/Exceptions/AbstractSafeException.php';
|
||||
require_once __DIR__ . '/../../generated/Exceptions/OpensslException.php';
|
||||
|
||||
\openssl_sign('foo', $signature, file_get_contents(__DIR__ . '/fixtures/id_rsa'));
|
||||
openssl_sign('foo', $signatureSafe, file_get_contents(__DIR__ . '/fixtures/id_rsa'));
|
||||
|
||||
$this->assertSame($signature, $signatureSafe);
|
||||
|
||||
}
|
||||
|
||||
public function testOpenSslEncrypt()
|
||||
{
|
||||
$result = \openssl_encrypt(
|
||||
'test',
|
||||
'aes-256-cbc',
|
||||
pack('H*', 'a2e8ccd0e7985cc0b6213a55815a1034afc252980e970ca90e5202689f9473b0'),
|
||||
\OPENSSL_RAW_DATA,
|
||||
pack('H*', '123ce954203b7caaaa9da67f59839456'));
|
||||
|
||||
$resultSafe = openssl_encrypt(
|
||||
'test',
|
||||
'aes-256-cbc',
|
||||
pack('H*', 'a2e8ccd0e7985cc0b6213a55815a1034afc252980e970ca90e5202689f9473b0'),
|
||||
\OPENSSL_RAW_DATA,
|
||||
pack('H*', '123ce954203b7caaaa9da67f59839456'));
|
||||
|
||||
$this->assertSame($result, $resultSafe);
|
||||
}
|
||||
}
|
44
vendor/thecodingmachine/safe/generator/tests/MethodTest.php
vendored
Normal file
44
vendor/thecodingmachine/safe/generator/tests/MethodTest.php
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace Safe;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Safe\PhpStanFunctions\PhpStanFunctionMapReader;
|
||||
|
||||
class MethodTest extends TestCase
|
||||
{
|
||||
public function testGetFunctionName() {
|
||||
$docPage = new DocPage(__DIR__ . '/../doc/doc-en/en/reference/pcre/functions/preg-match.xml');
|
||||
$xmlObject = $docPage->getMethodSynopsis();
|
||||
$method = new Method($xmlObject[0], $docPage->loadAndResolveFile(), $docPage->getModule(), new PhpStanFunctionMapReader());
|
||||
$name = $method->getFunctionName();
|
||||
$this->assertEquals('preg_match', $name);
|
||||
}
|
||||
|
||||
public function testGetFunctionType() {
|
||||
$docPage = new DocPage(__DIR__ . '/../doc/doc-en/en/reference/pcre/functions/preg-match.xml');
|
||||
$xmlObject = $docPage->getMethodSynopsis();
|
||||
$method = new Method($xmlObject[0], $docPage->loadAndResolveFile(), $docPage->getModule(), new PhpStanFunctionMapReader());
|
||||
$type = $method->getReturnType();
|
||||
$this->assertEquals('int', $type);
|
||||
}
|
||||
|
||||
public function testGetFunctionParam() {
|
||||
$docPage = new DocPage(__DIR__ . '/../doc/doc-en/en/reference/pcre/functions/preg-match.xml');
|
||||
$xmlObject = $docPage->getMethodSynopsis();
|
||||
$method = new Method($xmlObject[0], $docPage->loadAndResolveFile(), $docPage->getModule(), new PhpStanFunctionMapReader());
|
||||
$params = $method->getParams();
|
||||
$this->assertEquals('string', $params[0]->getType());
|
||||
$this->assertEquals('pattern', $params[0]->getParameter());
|
||||
}
|
||||
|
||||
public function testGetInitializer() {
|
||||
$docPage = new DocPage(__DIR__ . '/../doc/doc-en/en/reference/apc/functions/apc-cache-info.xml');
|
||||
$xmlObject = $docPage->getMethodSynopsis();
|
||||
$method = new Method($xmlObject[0], $docPage->loadAndResolveFile(), $docPage->getModule(), new PhpStanFunctionMapReader());
|
||||
|
||||
$params = $method->getParams();
|
||||
$this->assertEquals('', $params[0]->getDefaultValue());
|
||||
}
|
||||
|
||||
}
|
32
vendor/thecodingmachine/safe/generator/tests/PhpStanFunctions/PhpStanFunctionMapReaderTest.php
vendored
Normal file
32
vendor/thecodingmachine/safe/generator/tests/PhpStanFunctions/PhpStanFunctionMapReaderTest.php
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace Safe\PhpStanFunctions;
|
||||
|
||||
use PHPStan\Testing\TestCase;
|
||||
|
||||
class PhpStanFunctionMapReaderTest extends TestCase
|
||||
{
|
||||
public function testHas(): void
|
||||
{
|
||||
$mapReader = new PhpStanFunctionMapReader();
|
||||
$this->assertTrue($mapReader->hasFunction('strpos'));
|
||||
$this->assertFalse($mapReader->hasFunction('foobar'));
|
||||
}
|
||||
|
||||
public function testGet(): void
|
||||
{
|
||||
$mapReader = new PhpStanFunctionMapReader();
|
||||
$function = $mapReader->getFunction('apcu_fetch');
|
||||
|
||||
|
||||
// 'apcu_fetch' => ['mixed', 'key'=>'string|string[]', '&w_success='=>'bool'],
|
||||
$this->assertSame('mixed', $function->getReturnType());
|
||||
$parameters = $function->getParameters();
|
||||
$this->assertCount(2, $parameters);
|
||||
$this->assertSame('success', $parameters['success']->getName());
|
||||
$this->assertSame('bool', $parameters['success']->getType());
|
||||
$this->assertFalse($parameters['success']->isVariadic());
|
||||
$this->assertTrue($parameters['success']->isByReference());
|
||||
$this->assertTrue($parameters['success']->isOptional());
|
||||
}
|
||||
}
|
27
vendor/thecodingmachine/safe/generator/tests/ScannerTest.php
vendored
Normal file
27
vendor/thecodingmachine/safe/generator/tests/ScannerTest.php
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Safe;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ScannerTest extends TestCase
|
||||
{
|
||||
|
||||
public function testGetMethodsPaths()
|
||||
{
|
||||
$scanner = new Scanner(__DIR__ . '/../doc/doc-en/en/reference/');
|
||||
$paths = $scanner->getFunctionsPaths();
|
||||
|
||||
$this->assertArrayHasKey(__DIR__.'/../doc/doc-en/en/reference/filesystem/functions/chmod.xml', $paths);
|
||||
$this->assertArrayNotHasKey(__DIR__.'/../doc/doc-en/en/reference/spl/appenditerator/getarrayiterator.xml', $paths);
|
||||
}
|
||||
|
||||
public function testGetFunctionsPaths()
|
||||
{
|
||||
$scanner = new Scanner(__DIR__ . '/../doc/doc-en/en/reference/');
|
||||
$paths = $scanner->getMethodsPaths();
|
||||
|
||||
$this->assertArrayNotHasKey(__DIR__.'/../doc/doc-en/en/reference/filesystem/functions/chmod.xml', $paths);
|
||||
$this->assertArrayHasKey(__DIR__.'/../doc/doc-en/en/reference/spl/appenditerator/getarrayiterator.xml', $paths);
|
||||
}
|
||||
}
|
21
vendor/thecodingmachine/safe/generator/tests/SpecialCasesTest.php
vendored
Normal file
21
vendor/thecodingmachine/safe/generator/tests/SpecialCasesTest.php
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace Safe;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Safe\Exceptions\PcreException;
|
||||
|
||||
class SpecialCasesTest extends TestCase
|
||||
{
|
||||
public function testPregReplace()
|
||||
{
|
||||
require_once __DIR__.'/../../lib/special_cases.php';
|
||||
require_once __DIR__.'/../../lib/Exceptions/SafeExceptionInterface.php';
|
||||
require_once __DIR__.'/../../lib/Exceptions/AbstractSafeException.php';
|
||||
require_once __DIR__.'/../../lib/Exceptions/PcreException.php';
|
||||
|
||||
$this->expectException(PcreException::class);
|
||||
$this->expectExceptionMessage('PREG_BAD_UTF8_ERROR: Invalid UTF8 character');
|
||||
preg_replace("/([\s,]+)/u", "foo", "\xc3\x28");
|
||||
}
|
||||
}
|
16
vendor/thecodingmachine/safe/generator/tests/TypeTest.php
vendored
Normal file
16
vendor/thecodingmachine/safe/generator/tests/TypeTest.php
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Safe;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class TypeTest extends TestCase
|
||||
{
|
||||
public function testIsClass()
|
||||
{
|
||||
$this->assertSame('\\stdClass', Type::toRootNamespace('stdClass'));
|
||||
$this->assertSame('\\SimpleXMLElement', Type::toRootNamespace('SimpleXMLElement'));
|
||||
$this->assertSame('bool', Type::toRootNamespace('bool'));
|
||||
$this->assertSame('int', Type::toRootNamespace('int'));
|
||||
}
|
||||
}
|
27
vendor/thecodingmachine/safe/generator/tests/fixtures/id_rsa
vendored
Normal file
27
vendor/thecodingmachine/safe/generator/tests/fixtures/id_rsa
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAoc4Ay97wfke5L1RLeiUtstzzdiSrzhwdgirjLSUeniEtXI6g
|
||||
Yj7PxX2rBV6f6a4o9CpkkeoSbj3lA82hkDzc411XVSws+OJ4cz4cc4K5fZ5HLJFA
|
||||
GpCqgO2ywCXH2pPmse+ACrdX1fPdZPqSyW1M+ZKYUDRAaHjlSsCkR8KSTN8Max2A
|
||||
4EMsntrWVWiMdOGg5jB7CAAwIz3a6R3gKEmFml2gs77gpYme/bTVERuNW+TCG/x3
|
||||
3xUhkdOrvRKPE81K4D6rtCXuBrqfmE8r1vp29yPbjIM6KStKLTCIJHLx1/h34CNj
|
||||
MXW6d8abQ7KKxAycw4ADwCO4VNhAMxhhyylNGQIDAQABAoIBABBoiaAjDZRgxaDN
|
||||
FZJfbtDyoyXZ9ipCo6NtofgUCZTTnHwEYrNT11IZulwjCmhyJukL24MIj7HqSO0n
|
||||
zziHU4cjEhYrEqxymvBk3hHAsfrRMDqOyEdpYEEO2c+uhSPfV8e3l+5+RUQHJO52
|
||||
kqgmk2XDvfp0MgvmJqp7+qzkSF3PH2R00c2AdjbPQoFxXv5Tkk7EBWa92pw+81Om
|
||||
YEK7ch/a5tqqz2AHrcU6P8I7MsqIDLc0AuUg/Xx5Nb9K2phBpQ9Extu1cg3t769C
|
||||
RR/5gqHRh3QgdaG6PGaGDSpb4ZIxNYdH7DxSVZGtZ/rBCCV8UNFBoiuXemm+QzQ2
|
||||
ob8e+/kCgYEA15FgAOTsSINZIFsWnuUTK8vQHTCkf0IL/89vvN8CIvN86Mri2yw8
|
||||
n4cQrVKjxBhhCdhu7coLzrMlo9saL9oS14Z43mPtAx6C4ACr5lcFcxcKd4zqoBP7
|
||||
YM7TJ6DN9oYd7yWQTPjUJHZXYuuVisqXDMics+rccQrsSdrN7hSFwB8CgYEAwCcn
|
||||
9SU9BFZ2LnCAmMDve4+xotaxiYJMwkVLr4f/7/fOWxgsmjQUThXNfLMHgFGA0kE2
|
||||
tn1Z9axsdlZGkhr7CRQ2gUHhpEldNEKWswoj6I1uKU7fVTPuRQP7FmMYAN0cGtpC
|
||||
NvQLcQbKI51VDKp6BN1JpDhw4RaWfWAntYOca8cCgYAM65GtfAsNbZHx9oOskl5L
|
||||
ybN3jGrPlc3ST0SLNXKTVbSu3zopmMUpapb9TG60WiG2zVoYtntusB4ZbBBtvcxT
|
||||
TqP/8E1MWo/NjTzAWYbiFwdxkuidH5V5f+v3/BNcKz2wD7hOZLuSU+V48FXOM8ZJ
|
||||
5dw+jF0aCWWQJ6UgF+rMUQKBgQCdnICN+CmAcVsm5GjW4wqMk2TlaKzfWd2UTDq0
|
||||
lB0+vEirN7z+D1HIauHOMxsoCbJcw9kY/uDXPsMInePs2+ylmcrLYzquAu9MeoIa
|
||||
AWtKyRdfEq7luF7shUNjLYz2yGbRL824PZ1U7HjJlbe5V+wWKVzKMvGD738o/8IS
|
||||
hQ286QKBgQCeIP6DWRAvIYNWs+R1oe5jln8N7SrQUCA2F5AmffBaRU+gLcHHyTBM
|
||||
VPtwF5uOAU7C7/ZhLe0MuMdVBvHSrfGh4y1qUXj1qu3KKrgF5kohyxlYvmEylDHY
|
||||
mFgG7Mb6/WmP+RORcAaaIrBg9rAj6v4wdLP5FRcvQY+csP/GmksZXw==
|
||||
-----END RSA PRIVATE KEY-----
|
Loading…
Add table
Add a link
Reference in a new issue