modifs.mineures sans commentaires

This commit is contained in:
Jephté Clain 2023-11-09 10:03:35 +04:00
parent 02e49afb65
commit 6037b3e7ca
27 changed files with 690 additions and 0 deletions

8
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@ -0,0 +1,14 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PhpAbstractStaticMethodInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpDocMissingReturnTagInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="PhpDocSignatureInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="PhpSignatureMismatchDuringInheritanceInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
</profile>
</component>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/nur-sery.iml" filepath="$PROJECT_DIR$/.idea/nur-sery.iml" />
</modules>
</component>
</project>

12
.idea/nur-sery.iml Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" packagePrefix="nur\sery\" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/vendor" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PhpDockerContainerSettings">
<list>
<map>
<entry key="125ffb9d-fd5f-4e71-8182-94191665795a">
<value>
<DockerContainerSettings>
<option name="version" value="1" />
<option name="volumeBindings">
<list>
<DockerVolumeBindingImpl>
<option name="containerPath" value="/opt/project" />
</DockerVolumeBindingImpl>
</list>
</option>
</DockerContainerSettings>
</value>
</entry>
</map>
</list>
</component>
</project>

60
.idea/php.xml Normal file
View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MessDetectorOptionsConfiguration">
<option name="transferred" value="true" />
</component>
<component name="PHPCSFixerOptionsConfiguration">
<option name="transferred" value="true" />
</component>
<component name="PHPCodeSnifferOptionsConfiguration">
<option name="highlightLevel" value="WARNING" />
<option name="transferred" value="true" />
</component>
<component name="PhpIncludePathManager">
<include_path>
<path value="$PROJECT_DIR$/vendor/phar-io/manifest" />
<path value="$PROJECT_DIR$/vendor/phar-io/version" />
<path value="$PROJECT_DIR$/vendor/nulib/tests" />
<path value="$PROJECT_DIR$/vendor/nulib/php" />
<path value="$PROJECT_DIR$/vendor/composer" />
<path value="$PROJECT_DIR$/vendor/sebastian/exporter" />
<path value="$PROJECT_DIR$/vendor/sebastian/code-unit" />
<path value="$PROJECT_DIR$/vendor/sebastian/resource-operations" />
<path value="$PROJECT_DIR$/vendor/sebastian/code-unit-reverse-lookup" />
<path value="$PROJECT_DIR$/vendor/theseer/tokenizer" />
<path value="$PROJECT_DIR$/vendor/sebastian/global-state" />
<path value="$PROJECT_DIR$/vendor/sebastian/complexity" />
<path value="$PROJECT_DIR$/vendor/sebastian/recursion-context" />
<path value="$PROJECT_DIR$/vendor/sebastian/object-enumerator" />
<path value="$PROJECT_DIR$/vendor/sebastian/object-reflector" />
<path value="$PROJECT_DIR$/vendor/sebastian/cli-parser" />
<path value="$PROJECT_DIR$/vendor/sebastian/diff" />
<path value="$PROJECT_DIR$/vendor/sebastian/lines-of-code" />
<path value="$PROJECT_DIR$/vendor/nikic/php-parser" />
<path value="$PROJECT_DIR$/vendor/sebastian/type" />
<path value="$PROJECT_DIR$/vendor/sebastian/version" />
<path value="$PROJECT_DIR$/vendor/sebastian/comparator" />
<path value="$PROJECT_DIR$/vendor/sebastian/environment" />
<path value="$PROJECT_DIR$/vendor/doctrine/instantiator" />
<path value="$PROJECT_DIR$/vendor/myclabs/deep-copy" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-timer" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-file-iterator" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-text-template" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-invoker" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-code-coverage" />
<path value="$PROJECT_DIR$/vendor/phpunit/phpunit" />
</include_path>
</component>
<component name="PhpProjectSharedConfiguration" php_language_level="7.4" />
<component name="PhpStanOptionsConfiguration">
<option name="transferred" value="true" />
</component>
<component name="PhpUnit">
<phpunit_settings>
<PhpUnitSettings custom_loader_path="$PROJECT_DIR$/vendor/autoload.php" />
</phpunit_settings>
</component>
<component name="PsalmOptionsConfiguration">
<option name="transferred" value="true" />
</component>
</project>

10
.idea/phpunit.xml Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PHPUnit">
<option name="directories">
<list>
<option value="$PROJECT_DIR$/tests" />
</list>
</option>
</component>
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

22
src/output/Output.php Normal file
View File

@ -0,0 +1,22 @@
<?php
namespace nur\sery\output;
/**
* Class Output: une destination pour écrire du contenu
*/
abstract class Output {
abstract function print($value): void;
/** afficher un tag, dont la fonction n'est pas d'être affiché à l'utilisateur, mais de structuer l'information affichée à l'utilisateur */
function printTag(?array $tag): void {
}
/** afficher un contenu destiné à être affiché à l'utilisateur */
function printContent(?array $content): void {
}
/** afficher une ligne de donnée au format CSV */
abstract function printCsv(?array $row): void;
abstract function printObject($object): void;
}

View File

@ -0,0 +1,11 @@
<?php
namespace nur\sery\output;
trait TOutputObjectAsCsv {
function printObject($object): void {
if ($object === null) return;
if (is_object($object)) $object = (array)$object;
if (is_array($object)) $this->printCsv($object);
else $this->printContent([$object]);
}
}

20
src/output/TtyOutput.php Normal file
View File

@ -0,0 +1,20 @@
<?php
namespace nur\sery\output;
class TtyOutput extends Output {
use TOutputObjectAsCsv;
function print($value): void {
echo $value;
}
function printTag(?array $tag): void {
# Ne pas écrire les tags
#XXX convertir certains tags en couleurs?
}
function printCsv(?array $row): void {
#XXX rendre paramétrable les options de sortie
fputcsv(STDOUT, $row);
}
}

10
src/output/WebOutput.php Normal file
View File

@ -0,0 +1,10 @@
<?php
namespace nur\sery\output;
class WebOutput extends Output {
use TOutputObjectAsCsv;
function print($value): void {
echo $value;
}
}

23
src/schema/FormInput.php Normal file
View File

@ -0,0 +1,23 @@
<?php
namespace nur\sery\schema;
/**
* Class FormInput: accès à des paramètres de formulaire (POST ou GET, dans cet
* ordre)
*
* cette implémentation lit depuis les paramètres de formulaire et écrit dans
* une référence
*/
class FormInput extends Input {
function exists($key=null): bool {
if ($key === null) return false;
return array_key_exists($key, $_POST) || array_key_exists($key, $_GET);
}
function get($key=null) {
if ($key === null) return null;
if (array_key_exists($key, $_POST)) return $_POST[$key];
elseif (array_key_exists($key, $_GET)) return $_GET[$key];
else return null;
}
}

21
src/schema/GetInput.php Normal file
View File

@ -0,0 +1,21 @@
<?php
namespace nur\sery\schema;
/**
* Class GetInput: accès à des paramètres de formulaire de type GET uniquement
*
* cette implémentation lit depuis les paramètres de formulaire et écrit dans
* une référence
*/
class GetInput extends Input {
function exists($key=null): bool {
if ($key === null) return false;
return array_key_exists($key, $_GET);
}
function get($key=null) {
if ($key === null) return null;
if (array_key_exists($key, $_GET)) return $_GET[$key];
else return null;
}
}

32
src/schema/Input.php Normal file
View File

@ -0,0 +1,32 @@
<?php
namespace nur\sery\schema;
/**
* Class Input: accès à une valeur
*
* cette implémentation lit depuis et écrit dans une référence
*/
class Input {
function __construct(&$value=null) {
$this->value =& $value;
}
protected $value;
function exists($key=null): bool {
if ($key === null) return true;
return $this->value !== null && array_key_exists($key, $this->value);
}
function get($key=null) {
if ($key === null) return $this->value;
elseif ($this->value === null) return null;
elseif (!array_key_exists($key, $this->value)) return null;
else return $this->value[$key];
}
function set($value, $key=null): void {
if ($key === null) $this->value = $value;
else $this->value[$key] = $value;
}
}

21
src/schema/PostInput.php Normal file
View File

@ -0,0 +1,21 @@
<?php
namespace nur\sery\schema;
/**
* Class PostInput: accès à des paramètres de formulaire de type POST uniquement
*
* cette implémentation lit depuis les paramètres de formulaire et écrit dans
* une référence
*/
class PostInput extends Input {
function exists($key=null): bool {
if ($key === null) return false;
return array_key_exists($key, $_POST);
}
function get($key=null) {
if ($key === null) return null;
if (array_key_exists($key, $_POST)) return $_POST[$key];
else return null;
}
}

119
src/schema/Schema.php Normal file
View File

@ -0,0 +1,119 @@
<?php
namespace nur\sery\schema;
use nur\sery\StateException;
use nur\sery\schema\values\AssocValue;
use nur\sery\schema\values\IValue;
use nur\sery\schema\values\ScalarValue;
use nur\sery\schema\values\SeqValue;
class Schema {
/**
* créer une nouvelle instance de cette classe à partir d'un schéma non
* normalisé
*/
static function new(&$md, $schema): self {
if ($md === null) $md = new static($schema);
return $md;
}
/** obtenir une instance de cette classe à partir d'un schéma normalisé */
static function with(array $schema): self {
return new static($schema, false);
}
static function normalize($schema): array {
if ($schema === null) throw new SchemaException("schema is required");
elseif (!is_array($schema)) $schema = [$schema];
#XXX
return $schema;
}
const SCHEMA = null;
function __construct($schema=null, bool $normalize=true) {
if ($schema === null) $schema = static::SCHEMA;
if ($normalize) $schema = self::normalize($schema);
$this->schema = $schema;
}
/**
* @var array schéma normalisé
*
* il y a 3 formes pour un schéma:
* - schéma valeur scalaire [0 => type, 1 => default, ...]
* - schéma tableau séquentiel [schema]
* - schéma tableau associatif [key => schema, ...]
*/
protected $schema;
/**
* retourner true si le schéma est pour une valeur scalaire, false s'il s'agit
* du schéma d'un tableau (séquentiel ou associatif)
*/
function isScalar(): bool {
return array_key_exists(0, $this->schema) && array_key_exists(1, $this->schema);
}
/** retourner true si le schéma est pour un tableau séquentiel */
function isSeq(): bool {
return array_key_exists(0, $this->schema) && !array_key_exists(1, $this->schema);
}
/** retourner true si le schéma est pour un tableau associatif */
function isAssoc(): bool {
return !array_key_exists(0, $this->schema) && !array_key_exists(1, $this->schema);
}
/**
* @var bool true si toutes les clés du schéma doivent exister, avec leur
* valeur par défaut le cas échéant
*/
protected $ppEnsureKeys;
/** @var bool true si les clés doivent être dans l'ordre du schéma */
protected $ppOrderKeys;
/**
* @var bool true si les valeurs doivent être automatiquement vérifiées et
* corrigées
*/
protected $ppVerifix;
function ensureSchema(&$value, $key=null, ?array $params=null): void {
}
/** retourner true si la valeur est scalaire */
function getScalar(&$value, $key=null, ?ScalarValue &$scalar=null): bool {
if (!$this->isScalar()) return false;
if ($value instanceof Input) $input = $value;
else $input = new Input($value);
$scalar = new ScalarValue($input, $this->schema, $key);
return true;
}
/** retourner true la valeur est un tableau séquentiel */
function getSeq(&$value, $key=null, ?SeqValue &$seq=null): bool {
if (!$this->isSeq()) return false;
if ($value instanceof Input) $input = $value;
else $input = new Input($value);
$seq = new SeqValue($input, $this->schema, $key);
return true;
}
/** retourner true la valeur est un tableau associatif */
function getAssoc(&$value, $key=null, ?AssocValue &$assoc=null): bool {
if (!$this->isAssoc()) return false;
if ($value instanceof Input) $input = $value;
else $input = new Input($value);
$assoc = new AssocValue($input, $this->schema, $key);
return true;
}
function getValue(&$value, $key=null): IValue {
if ($this->getScalar($input, $key, $scalar)) return $scalar;
elseif ($this->getSeq($input, $key, $seq)) return $seq;
elseif ($this->getAssoc($input, $key, $assoc)) return $assoc;
else throw StateException::unexpected_state();
}
}

View File

@ -0,0 +1,7 @@
<?php
namespace nur\sery\schema;
use Exception;
class SchemaException extends Exception {
}

35
src/schema/ref_schema.php Normal file
View File

@ -0,0 +1,35 @@
<?php
namespace nur\sery\schema;
class ref_schema {
const SCHEMA_SCHEMA = [
"type" => ["string", null, "" => 0,
"required" => true,
"desc" => "type de la valeur",
],
"default" => [null, null, "" => 1,
"desc" => "valeur par défaut",
],
"key" => ["?key", null, "" => "",
"desc" => "clé de la valeur si elle est dans un tableau",
],
"required" => ["bool", false,
"desc" => "cette valeur est-elle requise?",
],
"formatter" => ["?callable", null,
"desc" => "signature de la fonction: (value, format, type) => string",
],
"parser" => ["?callable", null,
"desc" => "signature de la fonction: (value, type) => [value, result]",
],
"messages" => ["?array", null,
"desc" => "message à afficher en cas d'erreur d'analyse",
],
];
const MESSAGES = [
"absent" => "{key}: Vous devez spécifier cette valeur",
"null" => "{key}: Cette valeur ne doit pas être nulle",
"invalid" => "{key}: {orig}: cette valeur est invalide",
];
}

View File

@ -0,0 +1,25 @@
<?php
namespace nur\sery\schema\types;
use nur\sery\schema\values\Result;
/**
* Interface IType: un type de données
*/
interface IType {
/**
* indiquer si la valeur a été fournie par l'utilisateur (elle doit exister
* et être non vide)
*/
function available($value, $key=null): bool;
/**
* indiquer si la valeur spécifiée existe. cette méthode permet de distinguer
* les cas "fourni par l'utilisateur mais vide" et "non fourni par l'utilisateur"
*/
function exists($value, $key=null): bool;
function verifix(&$value, bool $throw, ?Result &$result);
function format($value, ?string $format);
}

View File

@ -0,0 +1,19 @@
<?php
namespace nur\sery\schema\values;
class AssocValue implements IValue {
use TValue;
function isScalar(?ScalarValue &$scalar=null): bool { return false; }
function isSeq(?SeqValue &$seq=null): bool { return false; }
function isAssoc(?AssocValue &$assoc=null): bool { $assoc = $this; return true; }
function ensureKeys(): bool {
}
function orderKeys(): bool {
}
/** @param Result[] $results */
function verifix(bool $throw=true, ?array &$results=null): bool {
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace nur\sery\schema\values;
use nur\sery\schema\types\IType;
interface IValue {
/** retourner true si cette valeur est scalaire */
function isScalar(?ScalarValue &$scalar=null): bool;
/** retourner true cette valeur est un tableau séquentiel */
function isSeq(?SeqValue &$seq=null): bool;
/** retourner true cette valeur est un tableau associatif */
function isAssoc(?AssocValue &$assoc=null): bool;
function reset(&$value, $key=null, bool $verifix=true): void;
/** retourner true si la valeur existe */
function exists(): bool;
/** retourner true si la valeur est valide */
function isValid(): bool;
/** retourner true si la valeur est dans sa forme normalisée */
function isNormalized();
/** retourner le type associé à cette valeur */
function getType(): IType;
}

View File

@ -0,0 +1,31 @@
<?php
namespace nur\sery\schema\values;
/**
* Class Result: résultat de l'analyse ou de la normalisation d'une valeur
*
* @property-read bool $valid
* @property-read string|int|null $key si la valeur est dans un tableau, clé de
* la valeur dans le tableau
* @property-read string|null $orig valeur originale avant analyse
* @property-read string|null $message message d'erreur si la valeur n'est pas
* valide
*/
class Result {
const KEYS = ["valid", "key", "orig", "message"];
function __construct(?array $result=null) {
if ($result === null) $result = ["valid" => false];
foreach (static::KEYS as $key) {
if (!array_key_exists($key, $result)) $result[$key] = null;
}
#XXX interpoler [message] avec les clés de $result
$this->result = $result;
}
protected $result;
function __get($name) {
return $this->result[$name];
}
}

View File

@ -0,0 +1,74 @@
<?php
namespace nur\sery\schema\values;
use nur\sery\schema\types\IType;
class ScalarValue implements IValue {
use TValue;
function __construct(&$value, array $schema, $key=null, bool $verifix=true) {
$this->schema = $schema;
$this->reset($value, $key, $verifix);
}
function isScalar(?ScalarValue &$scalar=null): bool { $scalar = $this; return true; }
function isSeq(?SeqValue &$seq=null): bool { return false; }
function isAssoc(?AssocValue &$assoc=null): bool { return false; }
function get($default=null) {
$key = $this->key;
if ($key === null) return $this->value;
elseif (array_key_exists($key, $this->value)) return $this->value[$key];
else return $default;
}
function set($value): self {
$key = $this->key;
if ($key === null) $this->value = $value;
else $this->value[$key] = $value;
return $this;
}
/** @var IType */
protected $type;
function type(): IType {
if ($this->type === null) $this->type = $this->md()->getType($this->key);
return $this->type;
}
/** @var ?Result résultat de l'analyse de la valeur */
protected $result;
function exists(): bool {
return $this->type()->exists($this->value, $this->key);
}
/**
* analyser, corriger éventuellement et normaliser la valeur
*
* si la valeur était déjà normalisée, retourner false.
*/
function verifix(bool $throw=true, ?Result &$result=null): bool {
$type = $this->type();
$key = $this->key;
if ($key === null) $modified = $type->verifix($this->value, $throw, $result);
else $modified = $type->verifix($this->value[$key], $throw, $result);
$this->result = $result;
return $modified;
}
function parse($value, bool $throw=true, ?Result &$result=null) {
$this->type()->verifix($value, $throw, $result);
$this->set($value);
$this->result = $result;
return $value;
}
function format(?string $format=null): string {
$type = $this->type();
$key = $this->key;
if ($key === null) return $type->format($this->value, $format);
else return $type->format($this->value[$key], $format);
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace nur\sery\schema\values;
class SeqValue implements IValue {
use TValue;
function isScalar(?ScalarValue &$scalar=null): bool { return false; }
function isSeq(?SeqValue &$seq=null): bool { $seq = $this; return true; }
function isAssoc(?AssocValue &$assoc=null): bool { return false; }
function ensureKeys(): bool {
}
function orderKeys(): bool {
}
/** @param Result[] $results */
function verifix(bool $throw=true, ?array &$results=null): bool {
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace nur\sery\schema\values;
use nur\sery\schema\Schema;
trait TValue {
/** @var array schéma normalisé de cette valeur */
protected $schema;
/** @var Schema */
protected $md;
function md(): Schema {
if ($this->md === null) $this->md = Schema::with($this->schema);
return $this->md;
}
/** @var mixed valeur ou référence d'un tableau contenant la valeur */
protected $value;
/** @var string|int clé de la valeur dans le tableau destination */
protected $key;
function reset(&$value, $key=null, bool $verifix=true): void {
$this->value =& $value;
$this->key = $key;
$this->result = null;
if ($verifix) $this->verifix();
}
}