<pman>Intégration de la branche rel82-0.5.0
This commit is contained in:
		
						commit
						60df574a0e
					
				| @ -11,6 +11,6 @@ composer: | ||||
|   dist: | ||||
|     link: false | ||||
|     require-dev: | ||||
|       nulib/php: ^0.4.0p82 | ||||
|       nulib/spout: ^0.4.0p82 | ||||
|       nulib/phpss: ^0.4.0p82 | ||||
|       nulib/php: ^0.5.0p82 | ||||
|       nulib/spout: ^0.5.0p82 | ||||
|       nulib/phpss: ^0.5.0p82 | ||||
							
								
								
									
										6
									
								
								.idea/nur-ture.iml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								.idea/nur-ture.iml
									
									
									
										generated
									
									
									
								
							| @ -4,10 +4,8 @@ | ||||
|     <content url="file://$MODULE_DIR$"> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/nur_src" isTestSource="false" packagePrefix="nur\" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/nur_tests" isTestSource="true" packagePrefix="nur\" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" packagePrefix="nur\sery\wip\" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/src_app" isTestSource="false" packagePrefix="nur\sery\" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" packagePrefix="nur\sery\" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/src_glue" isTestSource="false" packagePrefix="nulib\" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" packagePrefix="nulib\" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" packagePrefix="nulib\" /> | ||||
|       <excludeFolder url="file://$MODULE_DIR$/vendor" /> | ||||
|     </content> | ||||
|     <orderEntry type="inheritedJdk" /> | ||||
|  | ||||
							
								
								
									
										11
									
								
								CHANGES.md
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								CHANGES.md
									
									
									
									
									
								
							| @ -1,3 +1,14 @@ | ||||
| ## Release 0.5.0p82 du 30/04/2025-05:35 | ||||
| 
 | ||||
| ## Release 0.5.0p74 du 30/04/2025-05:30 | ||||
| 
 | ||||
| * `2d73f4d` documenter showmorePlugin | ||||
| * `3b13ef1` possiblité de forcer la suppression | ||||
| * `3933fd1` corrections sur les controles | ||||
| * `4d238cc` maj doc | ||||
| * `f005692` déplacer nur/sery/wip et nur/sery dans nulib | ||||
| * `c4e02d5` afficher la version de l'application | ||||
| 
 | ||||
| ## Release 0.4.1p82 du 17/03/2025-17:23 | ||||
| 
 | ||||
| ## Release 0.4.1p74 du 17/03/2025-17:19 | ||||
|  | ||||
| @ -1 +1 @@ | ||||
| 0.4.1 | ||||
| 0.5.0 | ||||
|  | ||||
							
								
								
									
										7
									
								
								bin/json2yml.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										7
									
								
								bin/json2yml.php
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,7 @@ | ||||
| #!/usr/bin/php
 | ||||
| <?php | ||||
| require $_composer_autoload_path?? __DIR__.'/../vendor/autoload.php'; | ||||
| 
 | ||||
| use nulib\tools\Json2yamlApp; | ||||
| 
 | ||||
| Json2yamlApp::run(); | ||||
							
								
								
									
										7
									
								
								bin/yml2json.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										7
									
								
								bin/yml2json.php
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,7 @@ | ||||
| #!/usr/bin/php
 | ||||
| <?php | ||||
| require $_composer_autoload_path?? __DIR__.'/../vendor/autoload.php'; | ||||
| 
 | ||||
| use nulib\tools\Yaml2jsonApp; | ||||
| 
 | ||||
| Yaml2jsonApp::run(); | ||||
| @ -18,9 +18,9 @@ | ||||
| 		"php": "^8.2" | ||||
| 	}, | ||||
| 	"require-dev": { | ||||
| 		"nulib/php": "^0.4.0p82", | ||||
| 		"nulib/spout": "^0.4.0p82", | ||||
| 		"nulib/phpss": "^0.4.0p82", | ||||
| 		"nulib/php": "^0.5.0p82", | ||||
| 		"nulib/spout": "^0.5.0p82", | ||||
| 		"nulib/phpss": "^0.5.0p82", | ||||
| 		"nulib/tests": "^8.2", | ||||
| 		"ext-posix": "*", | ||||
| 		"ext-pcntl": "*", | ||||
| @ -52,9 +52,7 @@ | ||||
| 	}, | ||||
| 	"autoload": { | ||||
| 		"psr-4": { | ||||
| 			"nulib\\": "src_glue", | ||||
| 			"nur\\sery\\wip\\": "src", | ||||
| 			"nur\\sery\\": "src_app", | ||||
| 			"nulib\\": "src", | ||||
| 			"nur\\": "nur_src" | ||||
| 		}, | ||||
| 		"files": [ | ||||
| @ -63,13 +61,15 @@ | ||||
| 	}, | ||||
| 	"autoload-dev": { | ||||
| 		"psr-4": { | ||||
| 			"nur\\sery\\": "tests", | ||||
| 			"nulib\\": "tests", | ||||
| 			"nur\\": "nur_tests" | ||||
| 		} | ||||
| 	}, | ||||
| 	"bin": [ | ||||
| 		"bin/dumpser.php", | ||||
| 		"bin/csv2xlsx.php", | ||||
| 		"bin/json2yml.php", | ||||
| 		"bin/yml2json.php", | ||||
| 		"nur_bin/compctl.php", | ||||
| 		"nur_bin/compdep.php", | ||||
| 		"nur_bin/datectl.php", | ||||
|  | ||||
							
								
								
									
										50
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										50
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							| @ -4,7 +4,7 @@ | ||||
|         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", | ||||
|         "This file is @generated automatically" | ||||
|     ], | ||||
|     "content-hash": "e271d0728ec974a8964542dc16fc1b11", | ||||
|     "content-hash": "20d80ba2a043b1e083f352300c19da0f", | ||||
|     "packages": [], | ||||
|     "packages-dev": [ | ||||
|         { | ||||
| @ -273,16 +273,16 @@ | ||||
|         }, | ||||
|         { | ||||
|             "name": "myclabs/deep-copy", | ||||
|             "version": "1.13.0", | ||||
|             "version": "1.13.1", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/myclabs/DeepCopy.git", | ||||
|                 "reference": "024473a478be9df5fdaca2c793f2232fe788e414" | ||||
|                 "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414", | ||||
|                 "reference": "024473a478be9df5fdaca2c793f2232fe788e414", | ||||
|                 "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c", | ||||
|                 "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @ -321,7 +321,7 @@ | ||||
|             ], | ||||
|             "support": { | ||||
|                 "issues": "https://github.com/myclabs/DeepCopy/issues", | ||||
|                 "source": "https://github.com/myclabs/DeepCopy/tree/1.13.0" | ||||
|                 "source": "https://github.com/myclabs/DeepCopy/tree/1.13.1" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @ -329,7 +329,7 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2025-02-12T12:17:51+00:00" | ||||
|             "time": "2025-04-29T12:36:36+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "nikic/php-parser", | ||||
| @ -391,11 +391,11 @@ | ||||
|         }, | ||||
|         { | ||||
|             "name": "nulib/php", | ||||
|             "version": "0.4.0p82", | ||||
|             "version": "0.5.0p82", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://git.univ-reunion.fr/sda-php/nulib.git", | ||||
|                 "reference": "fd120298a91bddcaa3da2decd34b8f99853fb070" | ||||
|                 "reference": "9f8b6545e68079728bb7349487b99024bd7d5090" | ||||
|             }, | ||||
|             "require": { | ||||
|                 "ext-json": "*", | ||||
| @ -405,6 +405,8 @@ | ||||
|             "require-dev": { | ||||
|                 "ext-curl": "*", | ||||
|                 "ext-pcntl": "*", | ||||
|                 "ext-pdo": "*", | ||||
|                 "ext-pgsql": "*", | ||||
|                 "ext-posix": "*", | ||||
|                 "ext-sqlite3": "*", | ||||
|                 "nulib/tests": "^8.2" | ||||
| @ -433,18 +435,18 @@ | ||||
|                 } | ||||
|             ], | ||||
|             "description": "fonctions et classes essentielles", | ||||
|             "time": "2025-03-14T11:24:39+00:00" | ||||
|             "time": "2025-04-30T00:33:36+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "nulib/phpss", | ||||
|             "version": "0.4.0p82", | ||||
|             "version": "0.5.0p82", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://git.univ-reunion.fr/sda-php/nulib-phpss.git", | ||||
|                 "reference": "44b6507d02d62451fcf260b9af19ced7083dc674" | ||||
|                 "reference": "620e1b8eab68afc8bae94b5a936b7929c03bc734" | ||||
|             }, | ||||
|             "require": { | ||||
|                 "nulib/php": "^0.4.0p82", | ||||
|                 "nulib/php": "^0.5.0p82", | ||||
|                 "php": "^8.2", | ||||
|                 "phpoffice/phpspreadsheet": "^3.3.0" | ||||
|             }, | ||||
| @ -475,15 +477,15 @@ | ||||
|                 } | ||||
|             ], | ||||
|             "description": "wrapper pour phpoffice/phpspreadsheet", | ||||
|             "time": "2025-03-14T11:30:18+00:00" | ||||
|             "time": "2025-04-30T00:48:34+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "nulib/spout", | ||||
|             "version": "0.4.0p82", | ||||
|             "version": "0.5.0p82", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://git.univ-reunion.fr/sda-php/nulib-spout.git", | ||||
|                 "reference": "2b2af6be95070c1262740ab0242c96c1d86af568" | ||||
|                 "reference": "32fbed599e2e3cfc616df70fe51ba8ec4cb3aefd" | ||||
|             }, | ||||
|             "require": { | ||||
|                 "ext-dom": "*", | ||||
| @ -492,7 +494,7 @@ | ||||
|                 "ext-libxml": "*", | ||||
|                 "ext-xmlreader": "*", | ||||
|                 "ext-zip": "*", | ||||
|                 "nulib/php": "^0.4.0p82", | ||||
|                 "nulib/php": "^0.5.0p82", | ||||
|                 "php": "^8.2" | ||||
|             }, | ||||
|             "replace": { | ||||
| @ -533,7 +535,7 @@ | ||||
|                 } | ||||
|             ], | ||||
|             "description": "wrapper pour openspout/openspout", | ||||
|             "time": "2025-03-14T11:41:00+00:00" | ||||
|             "time": "2025-04-30T00:43:21+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "nulib/tests", | ||||
| @ -2493,16 +2495,16 @@ | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/yaml", | ||||
|             "version": "v7.2.3", | ||||
|             "version": "v7.2.5", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/yaml.git", | ||||
|                 "reference": "ac238f173df0c9c1120f862d0f599e17535a87ec" | ||||
|                 "reference": "4c4b6f4cfcd7e52053f0c8bfad0f7f30fb924912" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/yaml/zipball/ac238f173df0c9c1120f862d0f599e17535a87ec", | ||||
|                 "reference": "ac238f173df0c9c1120f862d0f599e17535a87ec", | ||||
|                 "url": "https://api.github.com/repos/symfony/yaml/zipball/4c4b6f4cfcd7e52053f0c8bfad0f7f30fb924912", | ||||
|                 "reference": "4c4b6f4cfcd7e52053f0c8bfad0f7f30fb924912", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @ -2545,7 +2547,7 @@ | ||||
|             "description": "Loads and dumps YAML files", | ||||
|             "homepage": "https://symfony.com", | ||||
|             "support": { | ||||
|                 "source": "https://github.com/symfony/yaml/tree/v7.2.3" | ||||
|                 "source": "https://github.com/symfony/yaml/tree/v7.2.5" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @ -2561,7 +2563,7 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2025-01-07T12:55:42+00:00" | ||||
|             "time": "2025-03-03T07:12:39+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "theseer/tokenizer", | ||||
|  | ||||
| @ -2,6 +2,6 @@ | ||||
| <?php | ||||
| require $_composer_autoload_path?? __DIR__.'/../vendor/autoload.php'; | ||||
| 
 | ||||
| use nur\sery\tools\SteamTrainApp; | ||||
| use nulib\tools\SteamTrainApp; | ||||
| 
 | ||||
| SteamTrainApp::run(); | ||||
|  | ||||
| @ -287,10 +287,10 @@ abstract class FileCachedValue extends Parametrable implements ArrayAccess, Coun | ||||
|   } | ||||
| 
 | ||||
|   /** supprimer le fichier s'il a expiré */ | ||||
|   function deleteExpired(): bool { | ||||
|   function deleteExpired(bool $force=false): bool { | ||||
|     try { | ||||
|       if ($this->shouldUpdate()) { | ||||
|         unlink($this->ppFile); | ||||
|       if ($force || $this->shouldUpdate()) { | ||||
|         @unlink($this->ppFile); | ||||
|         return true; | ||||
|       } | ||||
|     } finally { | ||||
|  | ||||
| @ -2,6 +2,7 @@ | ||||
| namespace nur\cli; | ||||
| 
 | ||||
| use Exception; | ||||
| use nulib\app; | ||||
| use nulib\app\RunFile; | ||||
| use nulib\ExitError; | ||||
| use nulib\ext\yaml; | ||||
| @ -16,7 +17,6 @@ use nur\config\ArrayConfig; | ||||
| use nur\msg; | ||||
| use nur\os; | ||||
| use nur\path; | ||||
| use nur\sery\app; | ||||
| 
 | ||||
| /** | ||||
|  * Class Application: application de base | ||||
|  | ||||
| @ -105,7 +105,7 @@ class ref_type { | ||||
|   /** comme {@link c} mais nullable */ | ||||
|   const NCONTENT = "?".self::CONTENT; | ||||
| 
 | ||||
|   /** comme {@link \nur\sery\FILE} mais nullable */ | ||||
|   /** comme {@link FILE} mais nullable */ | ||||
|   const NFILE = "?".self::FILE; | ||||
| 
 | ||||
|   /** comme {@link DATETIME} mais nullable */ | ||||
|  | ||||
| @ -599,16 +599,16 @@ class Form extends ComponentPrintable implements IParametrable, ArrayAccess, Cou | ||||
|   /** @var ?array */ | ||||
|   protected $hiddenControls; | ||||
| 
 | ||||
|   function addHiddenControl($control, ?string $name=null): self { | ||||
|     A::set($this->hiddenControls, $name, $control); | ||||
|   function addHiddenControl($control, ?string $id=null): self { | ||||
|     A::set($this->hiddenControls, $id, $control); | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   /** @var ?array */ | ||||
|   protected $controls; | ||||
| 
 | ||||
|   function addControl($control, ?string $name=null): self { | ||||
|     A::set($this->controls, $name, $control); | ||||
|   function addControl($control, ?string $id=null): self { | ||||
|     A::set($this->controls, $id, $control); | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
| @ -660,7 +660,8 @@ class Form extends ComponentPrintable implements IParametrable, ArrayAccess, Cou | ||||
|       $param["value"] = $value; | ||||
|       #XXX en attendant le formattage ci-dessus, forcer la format texte pour que
 | ||||
|       # la comparaison puisse se faire
 | ||||
|       $param["checked"] = strval($currentValue) === strval($value); | ||||
|       #XXX si $name est un tableau e.g values[] le test ci-dessous ne fonctionne pas
 | ||||
|       $param["checked"] ??= strval($currentValue) === strval($value); | ||||
|       break; | ||||
|     case self::NV: | ||||
|       if ($value === null) $value = $this->get($key, $default); | ||||
| @ -672,7 +673,7 @@ class Form extends ComponentPrintable implements IParametrable, ArrayAccess, Cou | ||||
| 
 | ||||
|     if ($params === null) $params = $param; | ||||
|     else A::update_n($params, $param); | ||||
|     return [new $controlClass($this, $params), $name]; | ||||
|     return [new $controlClass($this, $params), $key]; | ||||
|   } | ||||
| 
 | ||||
|   private function _prepareControls(): ?array { | ||||
|  | ||||
| @ -32,6 +32,7 @@ class nb { | ||||
|   } | ||||
| 
 | ||||
|   static final function menu($text, ?array $links=null, ?array $options=null): array { | ||||
|     $links = array_filter($links, function($link) { return $link !== null; }); | ||||
|     $item = ["item" => "menu", "links" => $links, "value" => $text]; | ||||
|     if ($options !== null) $item = array_merge($item, $options); | ||||
|     return $item; | ||||
|  | ||||
| @ -5,6 +5,23 @@ use nur\v\BasePlugin; | ||||
| use nur\v\v; | ||||
| use nur\v\vo; | ||||
| 
 | ||||
| /** | ||||
|  * Class showmorePlugin: un outil pour masquer par défaut un panneau de détails | ||||
|  * et donner la possibilité à l'utilisateur de l'afficher | ||||
|  * | ||||
|  * s'utilise de cette façon: | ||||
|  * <pre> | ||||
|  * $sm = new showmorePlugin(); | ||||
|  * // le tout doit être dans le container startc-endc
 | ||||
|  * $sm->printStartc(); | ||||
|  * // l'invite contient un lien pour afficher le panneau caché
 | ||||
|  * $sm->printInvite(); | ||||
|  * // le panneau caché est dans le container startp-endp
 | ||||
|  * $sm->printStartp(); | ||||
|  * $sm->printEndp(); | ||||
|  * $sm->printEndc(); | ||||
|  * </pre> | ||||
|  */ | ||||
| class showmorePlugin extends BasePlugin { | ||||
|   const HAVE_JQUERY = true; | ||||
| 
 | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| <?php | ||||
| namespace nur\v\vp; | ||||
| 
 | ||||
| use nulib\app; | ||||
| use nur\authz; | ||||
| use nur\b\authnz\IAuthzUser; | ||||
| use nur\config; | ||||
| @ -46,6 +47,21 @@ class NavigablePage extends AInitAuthzPage implements INavigablePage { | ||||
| 
 | ||||
|   const MENU_SULOGIN = true; | ||||
| 
 | ||||
|   protected function getAppVersionNbtext(): ?array { | ||||
|     $app = app::get(); | ||||
|     $projdir = $app->getProjdir(); | ||||
|     $versionfile =  "$projdir/VERSION.txt"; | ||||
|     if (file_exists($versionfile)) { | ||||
|       $name = $app->getName(); | ||||
|       $version = file_get_contents($versionfile); | ||||
|       return nb::text([ | ||||
|         "style" => "margin: 0 15px", | ||||
|         "$name v$version" | ||||
|       ]); | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   protected function getAuthzNbtext(IAuthzUser $user): array { | ||||
|     $username = $user->getUsername(); | ||||
|     $role = $user->getRole(); | ||||
| @ -95,6 +111,7 @@ class NavigablePage extends AInitAuthzPage implements INavigablePage { | ||||
|       $user = authz::get(); | ||||
|       navbar::nav(["align" => "right"], [ | ||||
|         nb::menu(icon::user($user->getShortName()), [ | ||||
|           $this->getAppVersionNbtext(), | ||||
|           $this->getAuthzNbtext($user), | ||||
|           $this->getLogoutNblink(), | ||||
|         ]), | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
| require(__DIR__.'/../../vendor/autoload.php'); | ||||
| 
 | ||||
| use nur\cli\Application; | ||||
| use nur\sery\output\msg; | ||||
| use nulib\output\msg; | ||||
| 
 | ||||
| class TestArgs4 extends Application { | ||||
|   protected $query; | ||||
|  | ||||
| @ -1,18 +1,13 @@ | ||||
| <?php | ||||
| namespace nur\sery; | ||||
| namespace nulib; | ||||
| 
 | ||||
| use nulib\A; | ||||
| use nulib\app\cli\Application; | ||||
| use nulib\app\LockFile; | ||||
| use nulib\app\RunFile; | ||||
| use nulib\cl; | ||||
| use nulib\ExitError; | ||||
| use nulib\os\path; | ||||
| use nulib\os\sh; | ||||
| use nulib\php\func; | ||||
| use nulib\str; | ||||
| use nulib\ValueException; | ||||
| use nur\cli\Application as nur_Application; | ||||
| use nur\sery\app\cli\Application; | ||||
| 
 | ||||
| class app { | ||||
|   private static function isa_Application($app): bool { | ||||
| @ -1,7 +1,8 @@ | ||||
| <?php | ||||
| namespace nur\sery\app\cli; | ||||
| namespace nulib\app\cli; | ||||
| 
 | ||||
| use Exception; | ||||
| use nulib\app; | ||||
| use nulib\app\RunFile; | ||||
| use nulib\ExitError; | ||||
| use nulib\ext\yaml; | ||||
| @ -13,7 +14,6 @@ use nulib\ValueException; | ||||
| use nur\cli\ArgsException; | ||||
| use nur\cli\ArgsParser; | ||||
| use nur\config; | ||||
| use nur\sery\app; | ||||
| 
 | ||||
| /** | ||||
|  * Class Application: application de base | ||||
| @ -1,5 +1,5 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\php\access; | ||||
| namespace nulib\php\access; | ||||
| 
 | ||||
| use nulib\cl; | ||||
| 
 | ||||
| @ -8,6 +8,18 @@ use nulib\cl; | ||||
|  * de {@link IAccess} | ||||
|  */ | ||||
| abstract class AbstractAccess implements IAccess { | ||||
|   const ALLOW_EMPTY = true; | ||||
| 
 | ||||
|   function __construct(?array $params=null) { | ||||
|     $this->allowEmpty = $params["allow_empty"] ?? static::ALLOW_EMPTY; | ||||
|   } | ||||
| 
 | ||||
|   protected bool $allowEmpty; | ||||
| 
 | ||||
|   function isAllowEmpty(): bool { | ||||
|     return $this->allowEmpty; | ||||
|   } | ||||
| 
 | ||||
|   function inc(): int { | ||||
|     $value = (int)$this->get(); | ||||
|     $this->set(++$value); | ||||
| @ -33,4 +45,16 @@ abstract class AbstractAccess implements IAccess { | ||||
|     cl::set($array, $key, $value); | ||||
|     $this->set($array); | ||||
|   } | ||||
| 
 | ||||
|   function ensureAssoc(array $keys, ?array $params=null): void { | ||||
|   } | ||||
| 
 | ||||
|   function ensureKeys(array $defaults, ?array $missings, ?array $params=null): void { | ||||
|   } | ||||
| 
 | ||||
|   function deleteMissings(array $missings, ?array $params=null): void { | ||||
|   } | ||||
| 
 | ||||
|   function ensureOrder(array $keys, ?array $params=null): void { | ||||
|   } | ||||
| } | ||||
|  | ||||
							
								
								
									
										8
									
								
								src/php/access/ArrayAccess.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/php/access/ArrayAccess.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| <?php | ||||
| namespace nulib\php\access; | ||||
| 
 | ||||
| class ArrayAccess extends KeyAccess { | ||||
|   const ALLOW_NULL = true; | ||||
|   const ALLOW_FALSE = false; | ||||
|   const PROTECT_DEST = true; | ||||
| } | ||||
							
								
								
									
										182
									
								
								src/php/access/ChainAccess.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								src/php/access/ChainAccess.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,182 @@ | ||||
| <?php | ||||
| namespace nulib\php\access; | ||||
| 
 | ||||
| use nulib\cl; | ||||
| use nulib\StateException; | ||||
| use ReflectionClass; | ||||
| use ReflectionException; | ||||
| 
 | ||||
| class ChainAccess extends AbstractAccess { | ||||
|   const ACCESS_AUTO = 0, ACCESS_KEY = 1, ACCESS_PROPERTY = 2; | ||||
| 
 | ||||
|   private static function unexpected_access_type(): StateException { | ||||
|     return StateException::unexpected_state("access_type"); | ||||
|   } | ||||
|    | ||||
|   function __construct(IAccess $access, $key, ?array $params=null) { | ||||
|     parent::__construct(); | ||||
|     $this->access = $access; | ||||
|     $this->key = $key; | ||||
|     $this->accessType = $params["access_type"] ?? self::ACCESS_AUTO; | ||||
|   } | ||||
| 
 | ||||
|   protected IAccess $access; | ||||
| 
 | ||||
|   /** @var null|int|string|array */ | ||||
|   protected $key; | ||||
| 
 | ||||
|   protected int $accessType; | ||||
| 
 | ||||
|   protected function _accessType($access, $key): int { | ||||
|     $accessType = $this->accessType; | ||||
|     if ($accessType === self::ACCESS_AUTO) { | ||||
|       if (is_object($access) && is_string($key)) { | ||||
|         $accessType = self::ACCESS_PROPERTY; | ||||
|       } else { | ||||
|         $accessType = self::ACCESS_KEY; | ||||
|       } | ||||
|     } | ||||
|     return $accessType; | ||||
|   } | ||||
| 
 | ||||
|   protected function _has(): bool { | ||||
|     $src = $this->access->get(); | ||||
|     $key = $this->key; | ||||
|     $accessType = $this->_accessType($src, $key); | ||||
|     if ($accessType === self::ACCESS_KEY) { | ||||
|       return cl::phas($src, $key); | ||||
|     } elseif ($accessType === self::ACCESS_PROPERTY) { | ||||
|       $class = new ReflectionClass($src); | ||||
|       return $class->hasProperty($key) || property_exists($src, $key); | ||||
|     } else { | ||||
|       throw self::unexpected_access_type(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   protected function _get($default=null) { | ||||
|     $src = $this->access->get(); | ||||
|     $key = $this->key; | ||||
|     $accessType = $this->_accessType($src, $key); | ||||
|     if ($accessType === self::ACCESS_KEY) { | ||||
|       return cl::pget($src, $key); | ||||
|     } elseif ($accessType === self::ACCESS_PROPERTY) { | ||||
|       $class = new ReflectionClass($src); | ||||
|       try { | ||||
|         $property = $class->getProperty($key); | ||||
|         $property->setAccessible(true); | ||||
|       } catch (ReflectionException $e) { | ||||
|         $property = null; | ||||
|       } | ||||
|       if ($property !== null) { | ||||
|         return $property->getValue($src); | ||||
|       } elseif (property_exists($src, $key)) { | ||||
|         return $src->$key; | ||||
|       } else { | ||||
|         return $default; | ||||
|       } | ||||
|     } else { | ||||
|       throw self::unexpected_access_type(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   protected function _pset(object $dest, $name, $value): void { | ||||
|     $class = new ReflectionClass($dest); | ||||
|     try { | ||||
|       $property = $class->getProperty($name); | ||||
|       $property->setAccessible(true); | ||||
|     } catch (ReflectionException $e) { | ||||
|       $property = null; | ||||
|     } | ||||
|     if ($property !== null) { | ||||
|       $property->setValue($dest, $value); | ||||
|     } else { | ||||
|       $dest->$name = $value; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function isAllowEmpty(): bool { | ||||
|     return $this->access->isAllowEmpty(); | ||||
|   } | ||||
| 
 | ||||
|   function exists(): bool { | ||||
|     if (!$this->access->exists()) return false; | ||||
|     if ($this->key === null) return true; | ||||
|     return $this->_has(); | ||||
|   } | ||||
| 
 | ||||
|   function available(): bool { | ||||
|     if (!$this->access->available()) return false; | ||||
|     if ($this->key === null) return true; | ||||
|     if (!$this->_has()) return false; | ||||
|     return $this->isAllowEmpty() || $this->_get() !== ""; | ||||
|   } | ||||
| 
 | ||||
|   function get($default=null) { | ||||
|     if ($this->key === null) { | ||||
|       return $this->access->get($default); | ||||
|     } | ||||
|     return $this->_get($default); | ||||
|   } | ||||
| 
 | ||||
|   function set($value): void { | ||||
|     if ($this->key === null) { | ||||
|       $this->access->set($value); | ||||
|       return; | ||||
|     } | ||||
|     $dest = $this->access->get(); | ||||
|     $key = $this->key; | ||||
|     $accessType = $this->_accessType($dest, $key); | ||||
|     if ($accessType === self::ACCESS_KEY) { | ||||
|       cl::pset($dest, $key, $value); | ||||
|       $this->access->set($dest); | ||||
|     } elseif ($accessType === self::ACCESS_PROPERTY) { | ||||
|       $this->_pset($dest, $key, $value); | ||||
|     } else { | ||||
|       throw self::unexpected_access_type(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function del(): void { | ||||
|     if ($this->key === null) { | ||||
|       $this->access->del(); | ||||
|       return; | ||||
|     } | ||||
|     $dest = $this->access->get(); | ||||
|     $key = $this->key; | ||||
|     $accessType = $this->_accessType($dest, $key); | ||||
|     if ($accessType === self::ACCESS_KEY) { | ||||
|       cl::pdel($dest, $key); | ||||
|       $this->access->set($dest); | ||||
|     } elseif ($accessType === self::ACCESS_PROPERTY) { | ||||
|       $this->_pset($dest, $key, null); | ||||
|     } else { | ||||
|       throw self::unexpected_access_type(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function addKey($key, ?array $params=null): IAccess { | ||||
|     if ($key === null) return $this; | ||||
|     $accessType = $params["access_type"] ?? $this->accessType; | ||||
|     if ($accessType === self::ACCESS_KEY && $accessType === $this->accessType) { | ||||
|       if ($this->key !== null) $key = cl::merge($this->key, $key); | ||||
|       return new ChainAccess($this->access, $key); | ||||
|     } else { | ||||
|       return new ChainAccess($this, $key); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function ensureAssoc(array $keys, ?array $params=null): void { | ||||
|     #XXX fonction de $accessType?
 | ||||
|     #$this->access->ensureAssoc($keys, $params);
 | ||||
|   } | ||||
| 
 | ||||
|   function ensureKeys(array $defaults, ?array $missings, ?array $params=null): void { | ||||
|     #XXX fonction de $accessType?
 | ||||
|     #$this->access->ensureKeys($defaults, $params);
 | ||||
|   } | ||||
| 
 | ||||
|   function ensureOrder(array $keys, ?array $params=null): void { | ||||
|     #XXX fonction de $accessType?
 | ||||
|     #$this->access->ensureOrder($keys, $params);
 | ||||
|   } | ||||
| } | ||||
| @ -1,5 +1,5 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\php\access; | ||||
| namespace nulib\php\access; | ||||
| 
 | ||||
| use nulib\cl; | ||||
| 
 | ||||
| @ -7,67 +7,99 @@ use nulib\cl; | ||||
|  * Class FormAccess: accès à une valeur de $_POST puis $_GET, dans cet ordre | ||||
|  */ | ||||
| class FormAccess extends AbstractAccess { | ||||
|   const ALLOW_EMPTY = false; | ||||
| 
 | ||||
|   function __construct($key, ?array $params=null) { | ||||
|     parent::__construct($params); | ||||
|     $this->key = $key; | ||||
|     $this->allowEmpty = $params["allow_empty"] ?? false; | ||||
|   } | ||||
| 
 | ||||
|   /** @var int|string */ | ||||
|   /** @var null|int|string|array */ | ||||
|   protected $key; | ||||
| 
 | ||||
|   protected bool $allowEmpty; | ||||
| 
 | ||||
|   function exists(): bool { | ||||
|   protected function _exists(array $first, ?array $second=null): bool { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return false; | ||||
|     return array_key_exists($key, $_POST) || array_key_exists($key, $_GET); | ||||
|     if ($key === null) return true; | ||||
|     if (cl::phas($first, $key)) return true; | ||||
|     return $second !== null && cl::phas($second, $key); | ||||
|   } | ||||
| 
 | ||||
|   public function available(): bool { | ||||
|   function exists(): bool { | ||||
|     return $this->_exists($_POST, $_GET); | ||||
|   } | ||||
| 
 | ||||
|   protected function _available(array $first, ?array $second=null): bool { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return false; | ||||
|     if (array_key_exists($key, $_POST)) { | ||||
|       return $this->allowEmpty || $_POST[$key] !== ""; | ||||
|     } elseif (array_key_exists($key, $_GET)) { | ||||
|       return $this->allowEmpty || $_GET[$key] !== ""; | ||||
|     if ($key === null) return true; | ||||
|     if (cl::phas($first, $key)) { | ||||
|       return $this->allowEmpty || cl::pget($first, $key) !== ""; | ||||
|     } elseif ($second !== null && cl::phas($second, $key)) { | ||||
|       return $this->allowEmpty || cl::pget($second, $key) !== ""; | ||||
|     } else { | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function get($default=null) { | ||||
|   public function available(): bool { | ||||
|     return $this->_available($_POST, $_GET); | ||||
|   } | ||||
| 
 | ||||
|   protected function _get($default, array $first, ?array $second=null) { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return $default; | ||||
|     if (array_key_exists($key, $_POST)) { | ||||
|       $value = $_POST[$key]; | ||||
|       if ($value === "" && !$this->allowEmpty) return $default; | ||||
|       return $value; | ||||
|     } elseif (array_key_exists($key, $_GET)) { | ||||
|       $value = $_GET[$key]; | ||||
|       if ($value === "" && !$this->allowEmpty) return $default; | ||||
|       return $value; | ||||
|     if ($key === null) return cl::merge($first, $second); | ||||
|     if (cl::phas($first, $key)) { | ||||
|       $value = cl::pget($first, $key); | ||||
|       if ($value !== "" || $this->allowEmpty) return $value; | ||||
|     } elseif ($second !== null && cl::phas($second, $key)) { | ||||
|       $value = cl::pget($second, $key); | ||||
|       if ($value !== "" || $this->allowEmpty) return $value; | ||||
|     } | ||||
|     return $default; | ||||
|   } | ||||
| 
 | ||||
|   function get($default=null) { | ||||
|     return $this->_get($default, $_POST, $_GET); | ||||
|   } | ||||
| 
 | ||||
|   function _set($value, array &$first, ?array &$second=null): void { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) { | ||||
|       # interdire la modification de la destination
 | ||||
|       return; | ||||
|     } | ||||
|     if ($second !== null && !cl::phas($first, $key) && cl::phas($second, $key)) { | ||||
|       cl::pset($second, $key, $value); | ||||
|     } else { | ||||
|       return $default; | ||||
|       cl::pset($first, $key, $value); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function set($value): void { | ||||
|     $this->_set($value, $_POST, $_GET); | ||||
|   } | ||||
| 
 | ||||
|   function _del(array &$first, ?array &$second=null): void { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return; | ||||
|     if (!array_key_exists($key, $_POST) && array_key_exists($key, $_GET)) { | ||||
|       cl::set($_GET, $key, $value); | ||||
|     if ($key === null) { | ||||
|       # interdire la modification de la destination
 | ||||
|       return; | ||||
|     } | ||||
|     if ($second !== null && !cl::phas($first, $key) && cl::phas($second, $key)) { | ||||
|       cl::pdel($second, $key); | ||||
|     } else { | ||||
|       cl::set($_POST, $key, $value); | ||||
|       cl::pdel($first, $key); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function del(): void { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return; | ||||
|     if (!array_key_exists($key, $_POST) && array_key_exists($key, $_GET)) { | ||||
|       cl::del($_GET, $key); | ||||
|     } else { | ||||
|       cl::del($_POST, $key); | ||||
|     } | ||||
|     $this->_del($_POST, $_GET); | ||||
|   } | ||||
| 
 | ||||
|   function addKey($key): self { | ||||
|     if ($key === null) return $this; | ||||
|     if ($this->key !== null) $key = cl::merge($this->key, $key); | ||||
|     return new static($key, [ | ||||
|       "allow_empty" => $this->allowEmpty | ||||
|     ]); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,49 +1,27 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\php\access; | ||||
| 
 | ||||
| use nulib\cl; | ||||
| namespace nulib\php\access; | ||||
| 
 | ||||
| /** | ||||
|  * Class GetAccess: accès à une valeur de $_GET | ||||
|  */ | ||||
| class GetAccess extends FormAccess { | ||||
|   function exists(): bool { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return false; | ||||
|     return array_key_exists($key, $_GET); | ||||
|     return $this->_exists($_GET); | ||||
|   } | ||||
| 
 | ||||
|   public function available(): bool { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return false; | ||||
|     if (array_key_exists($key, $_GET)) { | ||||
|       return $this->allowEmpty || $_GET[$key] !== ""; | ||||
|     } else { | ||||
|       return false; | ||||
|     } | ||||
|     return $this->_available($_GET); | ||||
|   } | ||||
| 
 | ||||
|   function get($default=null) { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return $default; | ||||
|     if (array_key_exists($key, $_GET)) { | ||||
|       $value = $_GET[$key]; | ||||
|       if ($value === "" && !$this->allowEmpty) return $default; | ||||
|       return $value; | ||||
|     } else { | ||||
|       return $default; | ||||
|     } | ||||
|     return $this->_get($default, $_GET); | ||||
|   } | ||||
| 
 | ||||
|   function set($value): void { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return; | ||||
|     cl::set($_GET, $key, $value); | ||||
|     $this->_set($value, $_GET); | ||||
|   } | ||||
| 
 | ||||
|   function del(): void { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return; | ||||
|     cl::del($_GET, $key); | ||||
|     $this->_del($_GET); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\php\access; | ||||
| namespace nulib\php\access; | ||||
| 
 | ||||
| /** | ||||
|  * Interface IAccess: abstraction d'un accès complet à une valeur | ||||
| @ -25,4 +25,32 @@ interface IAccess extends IGetter, ISetter, IDeleter { | ||||
|    * tableau si $key===null | ||||
|    */ | ||||
|   function append($value, $key=null): void; | ||||
| 
 | ||||
|   /** retourner une instance permettant d'accéder à $value[$key] */ | ||||
|   function addKey($key): IAccess; | ||||
| 
 | ||||
|   /** | ||||
|    * s'assurer que la destination est un tableau associatif en remplaçant les | ||||
|    * clés numériques par les clés correspondantes du tableau $keys | ||||
|    */ | ||||
|   function ensureAssoc(array $keys, ?array $params=null): void; | ||||
| 
 | ||||
|   /** | ||||
|    * s'assurer que toutes les clés mentionnées dans le tableau $defaults | ||||
|    * existent. si elles n'existent pas, ou si elles ont la valeur correspondante | ||||
|    * du tableau $missings, leur donner la valeur du tableau $defaults | ||||
|    */ | ||||
|   function ensureKeys(array $defaults, ?array $missings, ?array $params=null): void; | ||||
| 
 | ||||
|   /** | ||||
|    * supprimer toutes les clés dont la valeur est celle mentionnée dans le | ||||
|    * tableau $missings | ||||
|    */ | ||||
|   function deleteMissings(array $missings, ?array $params=null): void; | ||||
| 
 | ||||
|   /** | ||||
|    * s'assure que les clés de la destination sont dans l'ordre mentionné dans le | ||||
|    * tableau $keys. toutes les clés supplémentaires sont placées à la fin | ||||
|    */ | ||||
|   function ensureOrder(array $keys, ?array $params=null): void; | ||||
| } | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\php\access; | ||||
| namespace nulib\php\access; | ||||
| 
 | ||||
| /** | ||||
|  * Class IDeleter: une abstraction d'un objet qui permet de supprimer une valeur | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\php\access; | ||||
| namespace nulib\php\access; | ||||
| 
 | ||||
| /** | ||||
|  * Class IGetter: une abstraction d'un objet qui permet d'obtenir une valeur | ||||
| @ -11,6 +11,12 @@ interface IGetter { | ||||
|    */ | ||||
|   function exists(): bool; | ||||
| 
 | ||||
|   /** | ||||
|    * @return bool true si cet objet autorise les chaines vides. si c'est le cas, | ||||
|    * {@link exists()} et {@link available()} sont fonctionnellement identiques | ||||
|    */ | ||||
|   function isAllowEmpty(): bool; | ||||
| 
 | ||||
|   /** @return bool true si la valeur existe et est utilisable, false sinon */ | ||||
|   function available(): bool; | ||||
| 
 | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\php\access; | ||||
| namespace nulib\php\access; | ||||
| 
 | ||||
| /** | ||||
|  * Class ISetter: une abstraction d'un objet qui permet de modifier une valeur | ||||
|  | ||||
| @ -1,69 +1,202 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\php\access; | ||||
| namespace nulib\php\access; | ||||
| 
 | ||||
| use ArrayAccess; | ||||
| use nulib\cl; | ||||
| 
 | ||||
| /** | ||||
|  * Class KeyAccess: accès à une valeur d'une clé dans un tableau | ||||
|  * Class KeyAccess: accès | ||||
|  * - soit à une valeur d'un chemin de clé dans un tableau (si $key !== null) | ||||
|  * - soit à une valeur scalaire (si $key === null) | ||||
|  */ | ||||
| class KeyAccess extends AbstractAccess { | ||||
|   function __construct(&$array, $key, ?array $params=null) { | ||||
|     $this->array =& $array; | ||||
|   const ALLOW_NULL = null; | ||||
|   const ALLOW_FALSE = null; | ||||
|   const PROTECT_DEST = false; | ||||
| 
 | ||||
|   function __construct(&$dest, $key=null, ?array $params=null) { | ||||
|     parent::__construct($params); | ||||
|     $this->protectDest = $params["protect_dest"] ?? static::PROTECT_DEST; | ||||
|     $this->dest =& $dest; | ||||
|     $this->key = $key; | ||||
|     $this->allowNull = $params["allow_null"] ?? true; | ||||
|     $this->allowFalse = $params["allow_false"] ?? false; | ||||
|     $this->allowEmpty = $params["allow_empty"] ?? true; | ||||
|     $this->allowNull = $params["allow_null"] ?? static::ALLOW_NULL; | ||||
|     $this->allowFalse = $params["allow_false"] ?? static::ALLOW_FALSE; | ||||
|   } | ||||
| 
 | ||||
|   /** @var array|ArrayAccess */ | ||||
|   protected $array; | ||||
|   protected bool $protectDest; | ||||
| 
 | ||||
|   function reset(&$array): self { | ||||
|     $this->array =& $array; | ||||
|   /** @var mixed|array|ArrayAccess */ | ||||
|   protected $dest; | ||||
| 
 | ||||
|   /** @var null|int|string|array */ | ||||
|   protected $key; | ||||
| 
 | ||||
|   function reset(&$dest, $key=null): self { | ||||
|     $this->dest =& $dest; | ||||
|     $this->key = $key; | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   /** @var int|string */ | ||||
|   protected $key; | ||||
|   function resetKey($key=null): self { | ||||
|     $this->key = $key; | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   protected bool $allowNull; | ||||
|   protected ?bool $allowNull; | ||||
| 
 | ||||
|   protected bool $allowFalse; | ||||
|   protected function isAllowNull(): bool { | ||||
|     $allowNull = $this->allowNull; | ||||
|     if ($allowNull !== null) return $allowNull; | ||||
|     return $this->key !== null; | ||||
|   } | ||||
| 
 | ||||
|   protected bool $allowEmpty; | ||||
|   protected ?bool $allowFalse; | ||||
| 
 | ||||
|   protected function isAllowFalse(): bool { | ||||
|     $allowFalse = $this->allowFalse; | ||||
|     if ($allowFalse !== null) return $allowFalse; | ||||
|     return $this->key === null; | ||||
|   } | ||||
| 
 | ||||
|   function exists(): bool { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return false; | ||||
|     return cl::has($this->array, $key); | ||||
|     if ($key === null) { | ||||
|       return $this->isAllowNull() || $this->dest !== null; | ||||
|     } else { | ||||
|       return cl::phas($this->dest, $key); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function available(): bool { | ||||
|     if (!$this->exists()) return false; | ||||
|     $value = cl::get($this->array, $this->key); | ||||
|     if ($value === null) return $this->allowNull; | ||||
|     if ($value === false) return $this->allowFalse; | ||||
|     $key = $this->key; | ||||
|     if ($key === null) $value = $this->dest; | ||||
|     else $value = cl::pget($this->dest, $key); | ||||
|     if ($value === "") return $this->allowEmpty; | ||||
|     if ($value === null) return $this->isAllowNull(); | ||||
|     if ($value === false) return $this->isAllowFalse(); | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   function get($default=null) { | ||||
|     if ($this->key === null) return $default; | ||||
|     $value = cl::get($this->array, $this->key, $default); | ||||
|     if ($value === null && !$this->allowNull) return $default; | ||||
|     if ($value === false && !$this->allowFalse) return $default; | ||||
|     $key = $this->key; | ||||
|     if ($key === null) $value = $this->dest; | ||||
|     else $value = cl::pget($this->dest, $key, $default); | ||||
|     if ($value === "" && !$this->allowEmpty) return $default; | ||||
|     if ($value === null && !$this->isAllowNull()) return $default; | ||||
|     if ($value === false && !$this->isAllowFalse()) return $default; | ||||
|     return $value; | ||||
|   } | ||||
| 
 | ||||
|   function set($value): void { | ||||
|     if ($this->key === null) return; | ||||
|     cl::set($this->array, $this->key, $value); | ||||
|     $key = $this->key; | ||||
|     if ($key === null) { | ||||
|       if (!$this->protectDest) $this->dest = $value; | ||||
|     } else { | ||||
|       cl::pset($this->dest, $key, $value); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function del(): void { | ||||
|     if ($this->key === null) return; | ||||
|     cl::del($this->array, $this->key); | ||||
|     $key = $this->key; | ||||
|     if ($key === null) { | ||||
|       if (!$this->protectDest) $this->dest = null; | ||||
|     } else { | ||||
|       cl::pdel($this->dest, $key); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function addKey($key): self { | ||||
|     if ($key === null) return $this; | ||||
|     if ($this->key !== null) $key = cl::merge($this->key, $key); | ||||
|     return new KeyAccess($this->dest, $key, [ | ||||
|       "allow_empty" => $this->allowEmpty, | ||||
|       "allow_null" => $this->allowNull, | ||||
|       "allow_false" => $this->allowFalse, | ||||
|       "protect_dest" => $this->protectDest, | ||||
|     ]); | ||||
|   } | ||||
| 
 | ||||
|   function ensureAssoc(array $keys, ?array $params=null): void { | ||||
|     $dest =& $this->dest; | ||||
|     $prefix = $params["key_prefix"] ?? null; | ||||
|     $suffix = $params["key_suffix"] ?? null; | ||||
|     $index = 0; | ||||
|     foreach ($keys as $key) { | ||||
|       if ($prefix !== null || $suffix !== null) { | ||||
|         $destKey = "$prefix$key$suffix"; | ||||
|       } else { | ||||
|         # préserver les clés numériques
 | ||||
|         $destKey = $key; | ||||
|       } | ||||
|       if ($dest !== null && array_key_exists($destKey, $dest)) continue; | ||||
|       while (in_array($index, $keys, true)) { | ||||
|         $index++; | ||||
|       } | ||||
|       if ($dest !== null && array_key_exists($index, $dest)) { | ||||
|         $dest[$destKey] = $dest[$index]; | ||||
|         unset($dest[$index]); | ||||
|         $index++; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function ensureKeys(array $defaults, ?array $missings, ?array $params=null): void { | ||||
|     $dest =& $this->dest; | ||||
|     $keys = array_keys($defaults); | ||||
|     $prefix = $params["key_prefix"] ?? null; | ||||
|     $suffix = $params["key_suffix"] ?? null; | ||||
|     foreach ($keys as $key) { | ||||
|       $destKey = "$prefix$key$suffix"; | ||||
|       $haveMissing = $missings !== null && array_key_exists($key, $missings); | ||||
|       if ($dest === null || !array_key_exists($destKey, $dest)) { | ||||
|         $dest[$destKey] = $defaults[$key]; | ||||
|       } elseif ($haveMissing && $dest[$destKey] === $missings[$key]) { | ||||
|         $dest[$destKey] = $defaults[$key]; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function deleteMissings(array $missings, ?array $params=null): void { | ||||
|     $dest =& $this->dest; | ||||
|     $prefix = $params["key_prefix"] ?? null; | ||||
|     $suffix = $params["key_suffix"] ?? null; | ||||
|     foreach ($missings as $key => $missing) { | ||||
|       $destKey = "$prefix$key$suffix"; | ||||
|       if (array_key_exists($destKey, $dest) && $dest[$destKey] === $missing) { | ||||
|         unset($dest[$destKey]); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function ensureOrder(array $keys, ?array $params=null): void { | ||||
|     $dest =& $this->dest; | ||||
|     if ($dest === null) return; | ||||
| 
 | ||||
|     $prefix = $params["key_prefix"] ?? null; | ||||
|     $suffix = $params["key_suffix"] ?? null; | ||||
|     if ($prefix !== null || $suffix !== null) { | ||||
|       foreach ($keys as &$key) { | ||||
|         $key = "$prefix$key$suffix"; | ||||
|       }; unset($key); | ||||
|     } | ||||
| 
 | ||||
|     $destKeys = array_keys($dest); | ||||
|     $keyCount = count($keys); | ||||
|     if (array_slice($destKeys, 0, $keyCount) === $keys) { | ||||
|       # si le tableau a déjà les bonnes clés dans le bon ordre, rien à faire
 | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     $ordered = []; | ||||
|     foreach ($keys as $key) { | ||||
|       if (array_key_exists($key, $dest)) { | ||||
|         $ordered[$key] = $dest[$key]; | ||||
|         unset($dest[$key]); | ||||
|       } | ||||
|     } | ||||
|     $preserveKeys = $params["preserve_keys"] ?? false; | ||||
|     if ($preserveKeys) $dest = cl::merge2($ordered, $dest); | ||||
|     else $dest = array_merge($ordered, $dest); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,49 +1,27 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\php\access; | ||||
| 
 | ||||
| use nulib\cl; | ||||
| namespace nulib\php\access; | ||||
| 
 | ||||
| /** | ||||
|  * Class PostAccess: accès à une valeur de $_POST | ||||
|  */ | ||||
| class PostAccess extends FormAccess { | ||||
|   function exists(): bool { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return false; | ||||
|     return array_key_exists($key, $_POST); | ||||
|     return $this->_exists($_POST); | ||||
|   } | ||||
| 
 | ||||
|   public function available(): bool { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return false; | ||||
|     if (array_key_exists($key, $_POST)) { | ||||
|       return $this->allowEmpty || $_POST[$key] !== ""; | ||||
|     } else { | ||||
|       return false; | ||||
|     } | ||||
|     return $this->_available($_POST); | ||||
|   } | ||||
| 
 | ||||
|   function get($default=null) { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return $default; | ||||
|     if (array_key_exists($key, $_POST)) { | ||||
|       $value = $_POST[$key]; | ||||
|       if ($value === "" && !$this->allowEmpty) return $default; | ||||
|       return $value; | ||||
|     } else { | ||||
|       return $default; | ||||
|     } | ||||
|     return $this->_get($default, $_POST); | ||||
|   } | ||||
| 
 | ||||
|   function set($value): void { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return; | ||||
|     cl::set($_POST, $key, $value); | ||||
|     $this->_set($value, $_POST); | ||||
|   } | ||||
| 
 | ||||
|   function del(): void { | ||||
|     $key = $this->key; | ||||
|     if ($key === null) return; | ||||
|     cl::del($_POST, $key); | ||||
|     $this->_del($_POST); | ||||
|   } | ||||
| } | ||||
|  | ||||
							
								
								
									
										173
									
								
								src/php/access/PropertyAccess.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								src/php/access/PropertyAccess.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,173 @@ | ||||
| <?php | ||||
| namespace nulib\php\access; | ||||
| 
 | ||||
| use nulib\StateException; | ||||
| use nulib\str; | ||||
| use ReflectionClass; | ||||
| use ReflectionException; | ||||
| use ReflectionProperty; | ||||
| 
 | ||||
| class PropertyAccess extends AbstractAccess { | ||||
|   const PROTECT_DEST = true; | ||||
|   const MAP_NAMES = true; | ||||
|   const ALLOW_NULL = true; | ||||
|   const ALLOW_FALSE = false; | ||||
| 
 | ||||
|   function __construct(?object $dest, ?string $name=null, ?array $params=null) { | ||||
|     parent::__construct($params); | ||||
|     $this->protectDest = $params["protect_dest"] ?? static::PROTECT_DEST; | ||||
|     $this->mapNames = $params["map_names"] ?? static::MAP_NAMES; | ||||
|     $this->_setName($name); | ||||
|     $this->_setDest($dest); | ||||
|     $this->allowNull = $params["allow_null"] ?? static::ALLOW_NULL; | ||||
|     $this->allowFalse = $params["allow_false"] ?? static::ALLOW_FALSE; | ||||
|   } | ||||
| 
 | ||||
|   protected bool $protectDest; | ||||
| 
 | ||||
|   protected ?object $dest; | ||||
|   protected bool $mapNames; | ||||
| 
 | ||||
|   protected ?string $name; | ||||
| 
 | ||||
|   protected ?ReflectionProperty $property; | ||||
| 
 | ||||
|   private function _getName(string $key): string { | ||||
|     return $this->mapNames? str::us2camel($key): $key; | ||||
|   } | ||||
|   private function _setName(?string $name): void { | ||||
|     if ($name !== null) $name = $this->_getName($name); | ||||
|     $this->name = $name; | ||||
|   } | ||||
| 
 | ||||
|   private function _getProperty(?string $name, ?ReflectionClass $class, ?object $object=null): ?ReflectionProperty { | ||||
|     $property = null; | ||||
|     if ($class === null && $object !== null) { | ||||
|       $class = new ReflectionClass($object); | ||||
|     } | ||||
|     if ($class !== null && $name !== null) { | ||||
|       try { | ||||
|         $property = $class->getProperty($name); | ||||
|         $property->setAccessible(true); | ||||
|       } catch (ReflectionException $e) { | ||||
|       } | ||||
|     } | ||||
|     return $property; | ||||
|   } | ||||
| 
 | ||||
|   private function _setDest(?object $dest): void { | ||||
|     $this->dest = $dest; | ||||
|     $this->property = $this->_getProperty($this->name, null, $dest); | ||||
|   } | ||||
| 
 | ||||
|   function reset(?object $dest, ?string $name=null): self { | ||||
|     $this->_setName($name); | ||||
|     $this->_setDest($dest); | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   function resetKey($name=null): self { | ||||
|     $this->_setName($name); | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   protected bool $allowNull; | ||||
| 
 | ||||
|   protected bool $allowFalse; | ||||
| 
 | ||||
|   function exists(): bool { | ||||
|     $name = $this->name; | ||||
|     if ($this->dest === null) return false; | ||||
|     return $name === null | ||||
|       || $this->property !== null | ||||
|       || property_exists($this->dest, $name); | ||||
|   } | ||||
| 
 | ||||
|   protected function _get($default=null) { | ||||
|     $name = $this->name; | ||||
|     $property = $this->property; | ||||
|     if ($this->dest === null) { | ||||
|       return $default; | ||||
|     } elseif ($name === null) { | ||||
|       return $this->dest; | ||||
|     } elseif ($property !== null) { | ||||
|       return $property->getValue($this->dest); | ||||
|     } elseif (property_exists($this->dest, $name)) { | ||||
|       return $this->dest->$name; | ||||
|     } else { | ||||
|       return $default; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function available(): bool { | ||||
|     if (!$this->exists()) return false; | ||||
|     $value = $this->_get(); | ||||
|     if ($value === "") return $this->allowEmpty; | ||||
|     if ($value === null) return $this->allowNull; | ||||
|     if ($value === false) return $this->allowFalse; | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   function get($default=null) { | ||||
|     if (!$this->exists()) return $default; | ||||
|     $value = $this->_get(); | ||||
|     if ($value === "" && !$this->allowEmpty) return $default; | ||||
|     if ($value === null && !$this->allowNull) return $default; | ||||
|     if ($value === false && !$this->allowFalse) return $default; | ||||
|     return $value; | ||||
|   } | ||||
| 
 | ||||
|   protected function _set($value): void { | ||||
|     $name = $this->name; | ||||
|     $property = $this->property; | ||||
|     if ($this->dest === null) { | ||||
|       throw StateException::unexpected_state("dest is null"); | ||||
|     } elseif ($name === null) { | ||||
|       if (!$this->protectDest) $this->_setDest($value); | ||||
|     } elseif ($property !== null) { | ||||
|       $property->setValue($this->dest, $value); | ||||
|     } else { | ||||
|       $this->dest->$name = $value; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function set($value): void { | ||||
|     $this->_set($value); | ||||
|   } | ||||
| 
 | ||||
|   function del(): void { | ||||
|     $this->_set(null); | ||||
|   } | ||||
| 
 | ||||
|   function addKey($key): IAccess { | ||||
|     if ($key === null) return $this; | ||||
|     return new ChainAccess($this, $key); | ||||
|   } | ||||
| 
 | ||||
|   function ensureKeys(array $defaults, ?array $missings, ?array $params=null): void { | ||||
|     $dest = $this->dest; | ||||
|     if ($dest === null) { | ||||
|       # comme ne connait pas la classe de l'objet destination, on n'essaie pas
 | ||||
|       # de le créer
 | ||||
|       return; | ||||
|     } | ||||
|     $class = new ReflectionClass($dest); | ||||
|     $keys = array_keys($defaults); | ||||
|     $prefix = $params["key_prefix"] ?? null; | ||||
|     $suffix = $params["key_suffix"] ?? null; | ||||
|     foreach ($keys as $key) { | ||||
|       $name = $this->_getName("$prefix$key$suffix"); | ||||
|       $property = $this->_getProperty($name, $class); | ||||
|       if ($property !== null) { | ||||
|         $type = $property->getType(); | ||||
|         if ($type !== null && !$property->isInitialized($dest) && $type->allowsNull()) { | ||||
|           # initialiser avec null au lieu de $defaults[$key] pour respecter le
 | ||||
|           # type de la propriété
 | ||||
|           $property->setValue($dest, null); | ||||
|         } | ||||
|       } elseif (!property_exists($dest, $name)) { | ||||
|         $dest->$name = $defaults[$key]; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -1,5 +1,5 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\php\access; | ||||
| namespace nulib\php\access; | ||||
| 
 | ||||
| /** | ||||
|  * Class ShadowAccess: accès en lecture depuis une instance de {@link IAccess} | ||||
| @ -16,6 +16,7 @@ namespace nur\sery\wip\php\access; | ||||
|  */ | ||||
| class ShadowAccess extends AbstractAccess { | ||||
|   function __construct(IAccess $reader, IAccess $writer) { | ||||
|     parent::__construct(); | ||||
|     $this->reader = $reader; | ||||
|     $this->writer = $writer; | ||||
|     $this->getter = $reader; | ||||
| @ -27,6 +28,10 @@ class ShadowAccess extends AbstractAccess { | ||||
| 
 | ||||
|   protected IGetter $getter; | ||||
| 
 | ||||
|   public function isAllowEmpty(): bool { | ||||
|     return $this->getter->isAllowEmpty(); | ||||
|   } | ||||
| 
 | ||||
|   function exists(): bool { | ||||
|     return $this->getter->exists(); | ||||
|   } | ||||
| @ -48,4 +53,20 @@ class ShadowAccess extends AbstractAccess { | ||||
|     $this->writer->del(); | ||||
|     $this->getter = $this->reader; | ||||
|   } | ||||
| 
 | ||||
|   function addKey($key): IAccess { | ||||
|     return new ChainAccess($this, $key); | ||||
|   } | ||||
| 
 | ||||
|   function ensureAssoc(array $keys, ?array $params=null): void { | ||||
|     $this->writer->ensureAssoc($keys, $params); | ||||
|   } | ||||
| 
 | ||||
|   function ensureKeys(array $defaults, ?array $missings, ?array $params=null): void { | ||||
|     $this->writer->ensureKeys($defaults, $missings, $params); | ||||
|   } | ||||
| 
 | ||||
|   function ensureOrder(array $keys, ?array $params=null): void { | ||||
|     $this->writer->ensureOrder($keys, $params); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,56 +1,8 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\php\access; | ||||
| namespace nulib\php\access; | ||||
| 
 | ||||
| /** | ||||
|  * Class ValueAccess: accès à une valeur unitaire | ||||
|  */ | ||||
| class ValueAccess extends AbstractAccess { | ||||
|   function __construct(&$value, ?array $params=null) { | ||||
|     $this->value =& $value; | ||||
|     $this->allowNull = $params["allow_null"] ?? false; | ||||
|     $this->allowFalse = $params["allow_false"] ?? true; | ||||
|     $this->allowEmpty = $params["allow_empty"] ?? true; | ||||
|   } | ||||
| 
 | ||||
|   /** @var mixed */ | ||||
|   protected $value; | ||||
| 
 | ||||
|   function reset(&$value): self { | ||||
|     $this->value =& $value; | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   protected bool $allowNull; | ||||
| 
 | ||||
|   protected bool $allowFalse; | ||||
| 
 | ||||
|   protected bool $allowEmpty; | ||||
| 
 | ||||
|   function exists(): bool { | ||||
|     return $this->allowNull || $this->value !== null; | ||||
|   } | ||||
| 
 | ||||
|   function available(): bool { | ||||
|     if (!$this->exists()) return false; | ||||
|     $value = $this->value; | ||||
|     if ($value === false) return $this->allowFalse; | ||||
|     if ($value === "") return $this->allowEmpty; | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   function get($default=null) { | ||||
|     $value = $this->value; | ||||
|     if ($value === null && !$this->allowNull) return $default; | ||||
|     if ($value === false && !$this->allowFalse) return $default; | ||||
|     if ($value === "" && !$this->allowEmpty) return $default; | ||||
|     return $value; | ||||
|   } | ||||
| 
 | ||||
|   function set($value): void { | ||||
|     $this->value = $value; | ||||
|   } | ||||
| 
 | ||||
|   function del(): void { | ||||
|     $this->value = null; | ||||
|   } | ||||
| class ValueAccess extends KeyAccess { | ||||
|   const ALLOW_NULL = false; | ||||
|   const ALLOW_FALSE = true; | ||||
|   const PROTECT_DEST = false; | ||||
| } | ||||
|  | ||||
| @ -1,11 +1,11 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\php\coll; | ||||
| namespace nulib\php\coll; | ||||
| 
 | ||||
| use Iterator; | ||||
| use IteratorAggregate; | ||||
| use nulib\cl; | ||||
| use nulib\php\func; | ||||
| use nur\sery\wip\php\iter; | ||||
| use nulib\php\iter; | ||||
| use Traversable; | ||||
| 
 | ||||
| /** | ||||
|  | ||||
| @ -1,11 +1,10 @@ | ||||
| <?php # -*- coding: utf-8 mode: php -*- vim:sw=2:sts=2:et:ai:si:sta:fenc=utf-8
 | ||||
| namespace nur\sery\wip\php; | ||||
| namespace nulib\php; | ||||
| 
 | ||||
| use Exception; | ||||
| use Generator; | ||||
| use Iterator; | ||||
| use IteratorAggregate; | ||||
| use nulib\php\ICloseable; | ||||
| use nulib\StopException; | ||||
| use nulib\ValueException; | ||||
| use Traversable; | ||||
|  | ||||
							
								
								
									
										67
									
								
								src/schema/ConsolidatedResult.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/schema/ConsolidatedResult.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,67 @@ | ||||
| <?php | ||||
| namespace nulib\schema; | ||||
| 
 | ||||
| use nulib\ref\schema\ref_analyze; | ||||
| use nulib\ValueException; | ||||
| 
 | ||||
| class ConsolidatedResult extends Result { | ||||
|   protected int $highestResult; | ||||
| 
 | ||||
|   function reset(): void { | ||||
|     parent::reset(); | ||||
|     $this->highestResult = -1; | ||||
|   } | ||||
| 
 | ||||
|   protected function _addMessage(Wrapper $wrapper, $prefix=null): void { | ||||
|     $result = $wrapper->getResult(); | ||||
|     $message = $this->message; | ||||
|     if ($message) $message .= "\n"; | ||||
|     if ($prefix !== null) $message .= "$prefix: "; | ||||
|     $message .= $result->message; | ||||
|     $this->message = $message; | ||||
|   } | ||||
| 
 | ||||
|   function addMissingMessage(Wrapper $wrapper, $prefix=null): void { | ||||
|     if ($this->highestResult < ref_analyze::MISSING) { | ||||
|       $this->present = false; | ||||
|       $this->available = false; | ||||
|       $this->null = false; | ||||
|       $this->valid = false; | ||||
|       $this->messageKey = "missing"; | ||||
|     } | ||||
|     $this->_addMessage($wrapper, $prefix); | ||||
|   } | ||||
| 
 | ||||
|   function addUnavailableMessage(Wrapper $wrapper, $prefix=null): void { | ||||
|     if ($this->highestResult < ref_analyze::UNAVAILABLE) { | ||||
|       $this->present = true; | ||||
|       $this->available = false; | ||||
|       $this->null = false; | ||||
|       $this->valid = false; | ||||
|       $this->messageKey = "unavailable"; | ||||
|     } | ||||
|     $this->_addMessage($wrapper, $prefix); | ||||
|   } | ||||
| 
 | ||||
|   function addNullMessage(Wrapper $wrapper, $prefix=null): void { | ||||
|     if ($this->highestResult < ref_analyze::NULL) { | ||||
|       $this->present = true; | ||||
|       $this->available = true; | ||||
|       $this->null = true; | ||||
|       $this->valid = false; | ||||
|       $this->messageKey = "null"; | ||||
|     } | ||||
|     $this->_addMessage($wrapper, $prefix); | ||||
|   } | ||||
| 
 | ||||
|   function addInvalidMessage(Wrapper $wrapper, $prefix=null): void { | ||||
|     if ($this->highestResult < ref_analyze::INVALID) { | ||||
|       $this->present = true; | ||||
|       $this->available = true; | ||||
|       $this->null = false; | ||||
|       $this->valid = false; | ||||
|       $this->messageKey = "invalid"; | ||||
|     } | ||||
|     $this->_addMessage($wrapper, $prefix); | ||||
|   } | ||||
| } | ||||
| @ -1,5 +1,5 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema; | ||||
| namespace nulib\schema; | ||||
| 
 | ||||
| class OldSchema { | ||||
|   /** | ||||
|  | ||||
| @ -1,10 +1,11 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema; | ||||
| namespace nulib\schema; | ||||
| 
 | ||||
| use IteratorAggregate; | ||||
| use nur\sery\wip\schema\_assoc\AssocResult; | ||||
| use nur\sery\wip\schema\_list\ListResult; | ||||
| use nur\sery\wip\schema\_scalar\ScalarResult; | ||||
| use nulib\cl; | ||||
| use nulib\ref\schema\ref_analyze; | ||||
| use nulib\ref\schema\ref_schema; | ||||
| use nulib\ValueException; | ||||
| use Throwable; | ||||
| 
 | ||||
| /** | ||||
|  * Class Result: résultat de l'analyse ou de la normalisation d'une valeur | ||||
| @ -17,48 +18,140 @@ use nur\sery\wip\schema\_scalar\ScalarResult; | ||||
|  * @property bool $normalized si la valeur est valide, est-elle normalisée? | ||||
|  * @property string|null $messageKey clé de message si la valeur n'est pas valide | ||||
|  * @property string|null $message message si la valeur n'est pas valide | ||||
|  * @property Throwable|null $exception l'exception qui a fait échouer la | ||||
|  * validation le cas échéant | ||||
|  * @property string|null $origValue valeur originale avant extraction et analyse | ||||
|  * @property mixed|null $normalizedValue la valeur normalisée si elle est | ||||
|  *  disponible, null sinon. ce champ est utilisé comme optimisation si la valeur | ||||
|  *  normalisée a déjà été calculée | ||||
|  */ | ||||
| abstract class Result implements IteratorAggregate { | ||||
|   const KEYS = [ | ||||
|     "resultAvailable", | ||||
|     "present", "available", "null", "valid", "normalized", | ||||
|     "messageKey", "message", | ||||
|     "origValue", "normalizedValue", | ||||
|   ]; | ||||
| 
 | ||||
| class Result { | ||||
|   function __construct() { | ||||
|     $this->reset(); | ||||
|   } | ||||
| 
 | ||||
|   function isAssoc(?AssocResult &$result=null): bool { return false; } | ||||
|   function isList(?ListResult &$result=null): bool { return false; } | ||||
|   function isScalar(?ScalarResult &$result=null): bool { return false; } | ||||
| 
 | ||||
|   /** | ||||
|    * Obtenir la liste des clés valides pour les valeurs accessibles via cet | ||||
|    * objet | ||||
|    */ | ||||
|   abstract function getKeys(): array; | ||||
| 
 | ||||
|   /** | ||||
|    * sélectionner le résultat associé à la clé spécifiée | ||||
|    * | ||||
|    * @param string|int|null $key | ||||
|    * @return Result $this | ||||
|    */ | ||||
|   abstract function select($key): Result; | ||||
| 
 | ||||
|   function getIterator() { | ||||
|     foreach ($this->getKeys() as $key) { | ||||
|       yield $key => $this->select($key); | ||||
|     } | ||||
|     $this->select(null); | ||||
|   } | ||||
|   public bool $resultAvailable; | ||||
|   public bool $present; | ||||
|   public bool $available; | ||||
|   public bool $null; | ||||
|   public bool $valid; | ||||
|   public bool $normalized; | ||||
|   public ?string $messageKey; | ||||
|   public ?string $message; | ||||
|   public ?Throwable $exception; | ||||
|   public $origValue; | ||||
|   public $normalizedValue; | ||||
| 
 | ||||
|   /** réinitialiser tous les objets résultats accessibles via cet objet */ | ||||
|   abstract function reset(): void; | ||||
|   function reset(): void { | ||||
|     $this->resultAvailable = false; | ||||
|     $this->present = false; | ||||
|     $this->available = false; | ||||
|     $this->null = false; | ||||
|     $this->valid = false; | ||||
|     $this->normalized = false; | ||||
|     $this->messageKey = null; | ||||
|     $this->message = null; | ||||
|     $this->exception = null; | ||||
|     $this->origValue = null; | ||||
|     $this->normalizedValue = null; | ||||
|   } | ||||
| 
 | ||||
|   protected function getMessage(string $key,  Schema $schema): string { | ||||
|     $message = cl::get($schema->messages, $key); | ||||
|     if ($message !== null) return $message; | ||||
|     return cl::get(ref_schema::MESSAGES, $key); | ||||
|   } | ||||
| 
 | ||||
|   function setMissing( Schema $schema): int { | ||||
|     $this->resultAvailable = true; | ||||
|     $this->present = false; | ||||
|     $this->available = false; | ||||
|     if (!$schema->required) { | ||||
|       $this->null = false; | ||||
|       $this->valid = true; | ||||
|       $this->normalized = true; | ||||
|       return ref_analyze::NORMALIZED; | ||||
|     } else { | ||||
|       $this->messageKey = $messageKey = "missing"; | ||||
|       $this->message = $this->getMessage($messageKey, $schema); | ||||
|       return ref_analyze::MISSING; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function setUnavailable( Schema $schema): int { | ||||
|     $this->resultAvailable = true; | ||||
|     $this->present = true; | ||||
|     $this->available = false; | ||||
|     if (!$schema->required) { | ||||
|       $this->null = false; | ||||
|       $this->valid = true; | ||||
|       $this->normalized = true; | ||||
|       return ref_analyze::NORMALIZED; | ||||
|     } else { | ||||
|       $this->messageKey = $messageKey = "unavailable"; | ||||
|       $this->message = $this->getMessage($messageKey, $schema); | ||||
|       return ref_analyze::UNAVAILABLE; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function setNull( Schema $schema): int { | ||||
|     $this->resultAvailable = true; | ||||
|     $this->present = true; | ||||
|     $this->available = true; | ||||
|     $this->null = true; | ||||
|     if ($schema->nullable) { | ||||
|       $this->valid = true; | ||||
|       $this->normalized = true; | ||||
|       return ref_analyze::NORMALIZED; | ||||
|     } else { | ||||
|       $this->messageKey = $messageKey = "null"; | ||||
|       $this->message = $this->getMessage($messageKey, $schema); | ||||
|       return ref_analyze::NULL; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function setInvalid($value,  Schema $schema, ?Throwable $exception=null): int { | ||||
|     $this->resultAvailable = true; | ||||
|     $this->present = true; | ||||
|     $this->available = true; | ||||
|     $this->null = false; | ||||
|     $this->valid = false; | ||||
|     $this->origValue = $value; | ||||
|     $this->messageKey = $messageKey = "invalid"; | ||||
|     $message = null; | ||||
|     if ($exception !== null) $message = ValueException::get_message($exception); | ||||
|     if (!$message) $message = $this->getMessage($messageKey, $schema); | ||||
|     $this->message = $message; | ||||
|     $this->exception = $exception; | ||||
|     return ref_analyze::INVALID; | ||||
|   } | ||||
| 
 | ||||
|   function setValid($normalizedValue=null): int { | ||||
|     $this->resultAvailable = true; | ||||
|     $this->present = true; | ||||
|     $this->available = true; | ||||
|     $this->null = false; | ||||
|     $this->valid = true; | ||||
|     $this->normalizedValue = $normalizedValue; | ||||
|     return ref_analyze::VALID; | ||||
|   } | ||||
| 
 | ||||
|   function setNormalized(): int { | ||||
|     $this->resultAvailable = true; | ||||
|     $this->present = true; | ||||
|     $this->available = true; | ||||
|     $this->null = false; | ||||
|     $this->valid = true; | ||||
|     $this->normalized = true; | ||||
|     return ref_analyze::NORMALIZED; | ||||
|   } | ||||
| 
 | ||||
|   function throw(bool $throw): void { | ||||
|     if ($throw) { | ||||
|       $exception = $this->exception; | ||||
|       if ($exception !== null) throw $exception; | ||||
|       else throw new ValueException($this->message); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,22 +1,45 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema; | ||||
| namespace nulib\schema; | ||||
| 
 | ||||
| use ArrayAccess; | ||||
| use nulib\AccessException; | ||||
| use nulib\cl; | ||||
| use nulib\ref\schema\ref_schema; | ||||
| use nulib\ref\schema\ref_types; | ||||
| use nur\sery\wip\schema\_assoc\AssocSchema; | ||||
| use nur\sery\wip\schema\_list\ListSchema; | ||||
| use nur\sery\wip\schema\_scalar\ScalarSchema; | ||||
| use nur\sery\wip\schema\types\IType; | ||||
| use nur\sery\wip\schema\types\tarray; | ||||
| use nur\sery\wip\schema\types\tbool; | ||||
| use nur\sery\wip\schema\types\tcallable; | ||||
| use nur\sery\wip\schema\types\tcontent; | ||||
| use nur\sery\wip\schema\types\tpkey; | ||||
| use nur\sery\wip\schema\types\trawstring; | ||||
| use nulib\schema\_assoc\AssocSchema; | ||||
| use nulib\schema\_list\ListSchema; | ||||
| use nulib\schema\_scalar\ScalarSchema; | ||||
| use nulib\schema\types\IType; | ||||
| use nulib\schema\types\tarray; | ||||
| use nulib\schema\types\tbool; | ||||
| use nulib\schema\types\tcontent; | ||||
| use nulib\schema\types\tfunc; | ||||
| use nulib\schema\types\tpkey; | ||||
| use nulib\schema\types\trawstring; | ||||
| 
 | ||||
| /** | ||||
|  * Class Schema | ||||
|  * | ||||
|  * @property-read array|IType $type | ||||
|  * @property-read mixed $default | ||||
|  * @property-read string|null $title | ||||
|  * @property-read bool $required | ||||
|  * @property-read bool $nullable | ||||
|  * @property-read string|array|null $desc | ||||
|  * @property-read callable|null $analyzerFunc | ||||
|  * @property-read callable|null $extractorFunc | ||||
|  * @property-read callable|null $parserFunc | ||||
|  * @property-read callable|null $normalizerFunc | ||||
|  * @property-read array|null $messages | ||||
|  * @property-read callable|null $formatterFunc | ||||
|  * @property-read mixed $format | ||||
|  * @property-read array $nature | ||||
|  * @property-read array|null $schema | ||||
|  * @property-read string|int|null $name | ||||
|  * @property-read string|array|null $pkey | ||||
|  * @property-read string|null $header | ||||
|  * @property-read bool|null $computed | ||||
|  */ | ||||
| abstract class Schema implements ArrayAccess { | ||||
|   /** | ||||
|    * créer le cas échéant une nouvelle instance de {@link Schema} à partir d'une | ||||
| @ -27,7 +50,7 @@ abstract class Schema implements ArrayAccess { | ||||
|    * l'instance de Schema nouvelle créée | ||||
|    * - sinon, prendre $definition comme définition | ||||
|    */ | ||||
|   static function ns(&$schema, $definition=null, $definitionKey=null, bool $normalize=true): self { | ||||
|   static function ns($definition=null, $definitionKey=null, &$schema=null, bool $normalize=true): self { | ||||
|     if (is_array($schema)) { | ||||
|       $definition = $schema; | ||||
|       $schema = null; | ||||
| @ -51,13 +74,13 @@ abstract class Schema implements ArrayAccess { | ||||
|    * variable $value (si $valueKey===null) ou $value[$valueKey] si $valueKey | ||||
|    * n'est pas null | ||||
|    */ | ||||
|   static function nw(&$value=null, $valueKey=null, &$schema=null, $definition=null, ?Wrapper &$wrapper=null): Wrapper { | ||||
|   static function nw(&$value=null, $valueKey=null, $definition=null, &$schema=null, ?Wrapper &$wrapper=null): Wrapper { | ||||
|     if ($definition === null) { | ||||
|       # bien que techniquement, $definition peut être null (il s'agit alors du
 | ||||
|       # schéma d'un scalaire quelconque), on ne l'autorise pas ici
 | ||||
|       throw SchemaException::invalid_schema("definition is required"); | ||||
|     } | ||||
|     return self::ns($schema, $definition)->getWrapper($value, $valueKey, $wrapper); | ||||
|     return self::ns($definition, null, $schema)->getWrapper($value, $valueKey, null, $wrapper); | ||||
|   } | ||||
| 
 | ||||
|   protected static function have_nature(array $definition, ?string &$nature=null): bool { | ||||
| @ -75,18 +98,18 @@ abstract class Schema implements ArrayAccess { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   protected static function _normalize(&$definition, $definitionKey=null): void { | ||||
|   protected static function _normalize_definition(&$definition, $definitionKey=null, ?array $natureMetaschema=null): void { | ||||
|     if (!is_array($definition)) $definition = [$definition]; | ||||
|     # s'assurer que toutes les clés existent avec leur valeur par défaut
 | ||||
|     $index = 0; | ||||
|     foreach (array_keys(ref_schema::SCALAR_METASCHEMA) as $key) { | ||||
|     foreach (array_keys(ref_schema::VALUE_METASCHEMA) as $key) { | ||||
|       if (!array_key_exists($key, $definition)) { | ||||
|         if (array_key_exists($index, $definition)) { | ||||
|           $definition[$key] = $definition[$index]; | ||||
|           unset($definition[$index]); | ||||
|           $index++; | ||||
|         } else { | ||||
|           $definition[$key] = ref_schema::SCALAR_METASCHEMA[$key][1]; | ||||
|           $definition[$key] = ref_schema::VALUE_METASCHEMA[$key][1]; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
| @ -138,6 +161,12 @@ abstract class Schema implements ArrayAccess { | ||||
|     # nature
 | ||||
|     $nature = $definition[""]; | ||||
|     tarray::ensure_array($nature); | ||||
|     $natureMetaschema ??= ref_schema::NATURE_METASCHEMA; | ||||
|     foreach (array_keys($natureMetaschema) as $key) { | ||||
|       if (!array_key_exists($key, $nature)) { | ||||
|         $nature[$key] = $natureMetaschema[$key][1]; | ||||
|       } | ||||
|     } | ||||
|     $definition[""] = $nature; | ||||
|     # name, pkey, header
 | ||||
|     $name = $definition["name"]; | ||||
| @ -158,22 +187,22 @@ abstract class Schema implements ArrayAccess { | ||||
|     tbool::ensure_bool($definition["required"]); | ||||
|     tbool::ensure_bool($definition["nullable"]); | ||||
|     tcontent::ensure_ncontent($definition["desc"]); | ||||
|     tcallable::ensure_ncallable($definition["analyzer_func"]); | ||||
|     tcallable::ensure_ncallable($definition["extractor_func"]); | ||||
|     tcallable::ensure_ncallable($definition["parser_func"]); | ||||
|     tcallable::ensure_ncallable($definition["normalizer_func"]); | ||||
|     tfunc::ensure_nfunc($definition["analyzer_func"]); | ||||
|     tfunc::ensure_nfunc($definition["extractor_func"]); | ||||
|     tfunc::ensure_nfunc($definition["parser_func"]); | ||||
|     tfunc::ensure_nfunc($definition["normalizer_func"]); | ||||
|     tarray::ensure_narray($definition["messages"]); | ||||
|     tcallable::ensure_ncallable($definition["formatter_func"]); | ||||
|     tfunc::ensure_nfunc($definition["formatter_func"]); | ||||
|     tbool::ensure_nbool($definition["computed"]); | ||||
| 
 | ||||
|     switch ($nature[0] ?? null) { | ||||
|     case "assoc": | ||||
|       foreach ($definition["schema"] as $key => &$keydef) { | ||||
|         self::_normalize($keydef, $key); | ||||
|         self::_normalize_definition($keydef, $key); | ||||
|       }; unset($keydef); | ||||
|       break; | ||||
|     case "list": | ||||
|       self::_normalize($definition["schema"]); | ||||
|       self::_normalize_definition($definition["schema"]); | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
| @ -223,11 +252,11 @@ abstract class Schema implements ArrayAccess { | ||||
|     case "assoc": | ||||
|       foreach ($definition["schema"] as &$keydef) { | ||||
|         self::_ensure_schema_instances($keydef); | ||||
|         Schema::ns($keydef, null, null, false); | ||||
|         Schema::ns(null, null, $keydef, false); | ||||
|       }; unset($keydef); | ||||
|       break; | ||||
|     case "list": | ||||
|       Schema::ns($definition["schema"], null, null, false); | ||||
|       Schema::ns(null, null, $definition["schema"], false); | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
| @ -246,14 +275,16 @@ abstract class Schema implements ArrayAccess { | ||||
|     return $this->_definition; | ||||
|   } | ||||
| 
 | ||||
|   /** retourner true si le schéma est de nature tableau associatif */ | ||||
|   function isAssoc(?AssocSchema &$schema=null): bool { return false; } | ||||
|   /** retourner true si le schéma est de nature liste */ | ||||
|   function isList(?ListSchema &$schema=null): bool { return false; } | ||||
|   /** retourner true si le schéma est de nature scalaire */ | ||||
|   function isScalar(?ScalarSchema &$schema=null): bool { return false; } | ||||
|   /** | ||||
|    * retourner la liste des clés valides pour l'accès aux valeurs et résultats | ||||
|    */ | ||||
|   abstract function getKeys(): array; | ||||
| 
 | ||||
|   abstract function getWrapper(&$value=null, $valueKey=null, ?Wrapper &$wrapper=null): Wrapper; | ||||
|   abstract function getSchema($key=false): Schema; | ||||
| 
 | ||||
|   abstract protected function newWrapper(): Wrapper; | ||||
| 
 | ||||
|   abstract function getWrapper(&$value=null, $valueKey=null, ?array $params=null, ?Wrapper &$wrapper=null): Wrapper; | ||||
| 
 | ||||
|   #############################################################################
 | ||||
|   # key & properties
 | ||||
| @ -272,7 +303,15 @@ abstract class Schema implements ArrayAccess { | ||||
|     throw AccessException::read_only(null, $offset); | ||||
|   } | ||||
| 
 | ||||
|   const _PROPERTY_PKEYS = []; | ||||
|   const _PROPERTY_PKEYS = [ | ||||
|     "analyzerFunc" => "analyzer_func", | ||||
|     "extractorFunc" => "extractor_func", | ||||
|     "parserFunc" => "parser_func", | ||||
|     "normalizerFunc" => "normalizer_func", | ||||
|     "formatterFunc" => "formatter_func", | ||||
|     "nature" => ["", 0], | ||||
|   ]; | ||||
| 
 | ||||
|   function __get($name) { | ||||
|     $pkey = cl::get(static::_PROPERTY_PKEYS, $name, $name); | ||||
|     return cl::pget($this->definition, $pkey); | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema; | ||||
| namespace nulib\schema; | ||||
| 
 | ||||
| use Exception; | ||||
| 
 | ||||
|  | ||||
| @ -1,20 +1,29 @@ | ||||
| # nulib\schema | ||||
| 
 | ||||
| * instance de WrapperContext directement dans le schéma  | ||||
| * plus de {key} ni {orig} dans messages | ||||
|   * les messages standard ne sont utilisés que s'il n'y a pas de message dans | ||||
|     l'exception | ||||
|   * si instance de UserException, prendre le message "non technique" pour | ||||
|     résultat | ||||
| * rajouter l'attribut "size" pour spécifier la taille maximale des valeurs | ||||
|   * cela pourrait servir pour générer automatiquement des tables SQL | ||||
|   * ou pour modéliser un schéma FSV | ||||
| 
 | ||||
| * support allowed_values | ||||
| 
 | ||||
| * valeurs composite/computed | ||||
|   * analyse / vérification de la valeur complète après calcul du résultat, si | ||||
|     tous les résultats sont bons | ||||
|   * calcul des valeurs composites/computed par une fonction avant/après l'analyse | ||||
|     globale si résultat ok | ||||
|   * fonction getter_func, setter_func, deleter_func pour les propriétés de type | ||||
|     computed | ||||
| * tdate et tdatetime. qu'en est-il des autres classes (delay, etc.) | ||||
|   * possibilité de spécifier le format de la date à analyser  | ||||
|   * parse_format pour spécifier le format d'analyse au lieu de l'auto-détecter | ||||
| 
 | ||||
| * ScalarSchema::from_property() | ||||
| 
 | ||||
| * l'argument $format de AssocWrapper::format() est un tableau associatif | ||||
|   `[$key => $format]` | ||||
|   cela permet de spécifier des format spécifiques pour certains champs. | ||||
|   * cela signifie que la valeur de retour n'est pas string :-( | ||||
|     retourner string|array | ||||
| 
 | ||||
| * dans AssocSchema, support `[key_prefix]` qui permet de spécifier un préfixe | ||||
|   commun aux champs dans le tableau destination, e.g | ||||
|   ~~~php | ||||
| @ -58,6 +67,8 @@ | ||||
| 
 | ||||
|   la définition de ces "circonstances" est encore à faire: soit un paramètre | ||||
|   lors de la définition du schéma, soit un truc magique du genre "toutes les | ||||
|   valeurs séquentielles sont des clés du schéma" | ||||
|   valeurs séquentielles sont des clés du schéma", soit un mode automatique | ||||
|   activé par un paramètre où une valeur "val" devient "val"=>true si la clé | ||||
|   "val" existe dans le schéma | ||||
| 
 | ||||
| -*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary | ||||
| @ -1,20 +1,105 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema; | ||||
| namespace nulib\schema; | ||||
| 
 | ||||
| use ArrayAccess; | ||||
| use IteratorAggregate; | ||||
| use nur\sery\wip\schema\_assoc\AssocWrapper; | ||||
| use nur\sery\wip\schema\_list\ListWrapper; | ||||
| use nur\sery\wip\schema\_scalar\ScalarWrapper; | ||||
| use nur\sery\wip\schema\types\IType; | ||||
| use nulib\php\func; | ||||
| use nulib\schema\input\Input; | ||||
| use nulib\schema\types\IType; | ||||
| use nulib\ValueException; | ||||
| 
 | ||||
| abstract class Wrapper implements ArrayAccess, IteratorAggregate { | ||||
|   function isAssoc(?AssocWrapper &$wrapper=null): bool { return false; } | ||||
|   function isList(?ListWrapper &$wrapper=null): bool { return false; } | ||||
|   function isScalar(?ScalarWrapper &$wrapper=null): bool { return false; } | ||||
|   protected WrapperContext $context; | ||||
| 
 | ||||
|   /** spécifier la valeur destination gérée par cet objet */ | ||||
|   abstract function reset(&$value, $valueKey=null, ?bool $verifix=null): self; | ||||
|   /** changer les paramètres de gestion des valeurs */ | ||||
|   function resetParams(?array $params): void { | ||||
|     $this->context->resetParams($params); | ||||
|   } | ||||
| 
 | ||||
|   protected function resetContext(bool $resetSelectedKey): void { | ||||
|     $context = $this->context; | ||||
|     $type = $context->schema->type; | ||||
|     if (is_array($type)) $type = $type[0]; | ||||
|     if (is_string($type)) $type = types::get($context->schema->nullable, $type); | ||||
|     $context->type = $type; | ||||
|     $context->result->reset(); | ||||
|     $context->analyzed = false; | ||||
|     $context->normalized = false; | ||||
|   } | ||||
| 
 | ||||
|   protected function afterModify(?array $params, bool $resetSelectedKey=false): void { | ||||
|     $context = $this->context; | ||||
|     $this->resetContext($resetSelectedKey); | ||||
|     if ($params["analyze"] ?? $context->analyze) { | ||||
|       $this->analyze($params); | ||||
|     } | ||||
|     if ($context->analyzed && ($params["normalize"] ?? $context->normalize)) { | ||||
|       $this->normalize($params); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   protected function newInput(&$value): Input { | ||||
|     return new Input($value); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * spécifier la valeur destination gérée par cet objet. | ||||
|    * | ||||
|    * @param ?array $params paramètres spécifique à cet appel, qui peuvent être | ||||
|    * différent des paramètres par défaut | ||||
|    */ | ||||
|   function reset(&$value, $valueKey=null, ?array $params=null): Wrapper { | ||||
|     $context = $this->context; | ||||
|     if ($value instanceof Input) $input = $value; | ||||
|     else $input = $this->newInput($value); | ||||
|     $context->input = $input; | ||||
|     $context->valueKey = $valueKey; | ||||
|     $this->afterModify($params, true); | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   /** analyser la valeur */ | ||||
|   abstract static function _analyze(WrapperContext $context, Wrapper $wrapper, ?array $params): int; | ||||
| 
 | ||||
|   function analyze(?array $params=null): bool { | ||||
|     $context = $this->context; | ||||
|     $reanalyze = $params["reanalyze"] ?? false; | ||||
|     if ($context->analyzed && !$reanalyze) return false; | ||||
| 
 | ||||
|     static::_analyze($context, $this, $params); | ||||
|     $context->analyzed = true; | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   /** normaliser la valeur */ | ||||
|   abstract static function _normalize(WrapperContext $context, Wrapper $wrapper, ?array $params): bool; | ||||
| 
 | ||||
|   protected function getConsolidatedResult(): Result { | ||||
|     return $this->context->result; | ||||
|   } | ||||
| 
 | ||||
|   function normalize(?array $params=null): bool { | ||||
|     $context = $this->context; | ||||
| 
 | ||||
|     // il faut que la valeur soit analysée avant de la normaliser
 | ||||
|     static::analyze($params); | ||||
|     if (!$context->analyzed) return false; | ||||
| 
 | ||||
|     $renormalize = $params["renormalize"] ?? false; | ||||
|     if ($renormalize || !$context->normalized) { | ||||
|       $modified = static::_normalize($context, $this, $params); | ||||
|       $context->normalized = true; | ||||
|     } else { | ||||
|       $modified = false; | ||||
|     } | ||||
| 
 | ||||
|     $result = $this->getConsolidatedResult(); | ||||
|     if (!$result->valid) { | ||||
|       $result->throw($params["throw"] ?? $context->throw); | ||||
|     } | ||||
| 
 | ||||
|     return $modified; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Obtenir la liste des clés valides pour les valeurs accessibles via cet | ||||
| @ -25,50 +110,113 @@ abstract class Wrapper implements ArrayAccess, IteratorAggregate { | ||||
|   /** | ||||
|    * sélectionner le wrapper associé à la clé spécifiée | ||||
|    * | ||||
|    * $key peut valoir: | ||||
|    * - false pour la clé courante (ne pas changer la sélection) | ||||
|    * - null ou "" le wrapper de la valeur principale | ||||
|    * - ou toute autre valeur présente dans {@link getKeys()} pour les valeurs | ||||
|    * accessible via cet objet | ||||
|    * | ||||
|    * @param string|int|null $key | ||||
|    * @return Wrapper $this | ||||
|    */ | ||||
|   abstract function select($key): Wrapper; | ||||
|   abstract function select($key=null): Wrapper; | ||||
| 
 | ||||
|   function getIterator() { | ||||
|     foreach ($this->getKeys() as $key) { | ||||
|       yield $key => $this->select($key); | ||||
|     } | ||||
|     $this->select(null); | ||||
|     $this->select(); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * obtenir le résultat de l'appel d'une des fonctions {@link set()} ou | ||||
|    * {@link unset()} | ||||
|    * obtenir le résultat de l'analyse de la valeur du wrapper sélectionné | ||||
|    * | ||||
|    * cette fonction doit être appelée après {@link set()} ou {@link unset()} et | ||||
|    * après que le wrapper aie été sélectionné avec {@link select()} | ||||
|    * | ||||
|    * $key peut valoir: | ||||
|    * - false pour la clé sélectionnée avec {@link select()} | ||||
|    * - null pour le résultat consolidé | ||||
|    * - "" pour le résultat de l'analyse de la valeur principale | ||||
|    * - ou toute autre valeur présente dans {@link getKeys()} pour le résultat | ||||
|    * de l'analyse des valeurs correspondantes | ||||
|    */ | ||||
|   abstract function getResult(): Result; | ||||
|   function getResult($key=false): Result { | ||||
|     if ($key === false || $key === "") return $this->context->result; | ||||
|     if ($key === null) return $this->getConsolidatedResult(); | ||||
|     throw ValueException::invalid_key($key); | ||||
|   } | ||||
| 
 | ||||
|   /** retourner true si la valeur existe */ | ||||
|   abstract function isPresent(): bool; | ||||
|   function isPresent($key=false): bool { | ||||
|     return $this->getResult($key)->present; | ||||
|   } | ||||
| 
 | ||||
|   /** retourner le type associé à la valeur */ | ||||
|   abstract function getType(): IType; | ||||
|   function getType($key=false): IType { | ||||
|     return $this->context->type; | ||||
|   } | ||||
| 
 | ||||
|   /** retourner true si la valeur est disponible */ | ||||
|   abstract function isAvailable(): bool; | ||||
|   function isAvailable($key=false): bool { | ||||
|     return $this->getResult($key)->available; | ||||
|   } | ||||
| 
 | ||||
|   /** retourner true si la valeur est nulle */ | ||||
|   function isNull($key=false): bool { | ||||
|     return $this->getResult($key)->null; | ||||
|   } | ||||
| 
 | ||||
|   /** retourner true si la valeur est valide */ | ||||
|   abstract function isValid(): bool; | ||||
|   function isValid($key=false): bool { | ||||
|     return $this->getResult($key)->valid; | ||||
|   } | ||||
| 
 | ||||
|   /** retourner true si la valeur est dans sa forme normalisée */ | ||||
|   abstract function isNormalized(): bool; | ||||
|   function isNormalized($key=false): bool { | ||||
|     return $this->getResult($key)->normalized; | ||||
|   } | ||||
| 
 | ||||
|   /** obtenir la valeur */ | ||||
|   abstract function get($default=null); | ||||
|   function get($default=null, $key=false) { | ||||
|     $context = $this->context; | ||||
|     if (!$context->result->available) return $default; | ||||
|     return $context->input->get($context->valueKey); | ||||
|   } | ||||
| 
 | ||||
|   /** remplacer la valeur */ | ||||
|   abstract function set($value): self; | ||||
|   function set($value, ?array $params=null, $key=false): self { | ||||
|     $context = $this->context; | ||||
|     $context->input->set($value, $context->valueKey); | ||||
|     $this->afterModify($params); | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   /** supprimer la valeur */ | ||||
|   abstract function unset(): self; | ||||
|   function unset(?array $params=null, $key=false): self { | ||||
|     $context = $this->context; | ||||
|     $context->input->unset($context->valueKey); | ||||
|     $this->afterModify($params); | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   protected function _format(WrapperContext $context, $format=null): string { | ||||
|     $value = $context->input->get($context->valueKey); | ||||
|     /** @var func $formatterFunc */ | ||||
|     $formatterFunc = $context->schema->formatterFunc; | ||||
|     if ($formatterFunc !== null) { | ||||
|       # la fonction formatter n'a pas forcément accès au format de la définition
 | ||||
|       # le lui fournir ici
 | ||||
|       $format ??= $context->schema->format; | ||||
|       return $formatterFunc->invoke([$value, $format, $context, $this]); | ||||
|     } else { | ||||
|       # on assume que le type a été initialisé avec le format de la définition
 | ||||
|       # le cas échéant
 | ||||
|       return $context->type->format($value, $format); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** formatter la valeur pour affichage */ | ||||
|   abstract function format($format=null): string; | ||||
|   function format($format=null, $key=false): string { | ||||
|     return $this->_format($this->context, $format); | ||||
|   } | ||||
| 
 | ||||
|   #############################################################################
 | ||||
|   # key & properties
 | ||||
| @ -78,14 +226,14 @@ abstract class Wrapper implements ArrayAccess, IteratorAggregate { | ||||
|   } | ||||
| 
 | ||||
|   function offsetGet($offset) { | ||||
|     return $this->select($offset); | ||||
|     return $this->get(null, $offset); | ||||
|   } | ||||
| 
 | ||||
|   function offsetSet($offset, $value): void { | ||||
|     $this->select($offset)->set($value); | ||||
|     $this->set($value, null, $offset); | ||||
|   } | ||||
| 
 | ||||
|   function offsetUnset($offset): void { | ||||
|     $this->select($offset)->unset(); | ||||
|     $this->unset(null, $offset); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,30 +1,44 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema; | ||||
| namespace nulib\schema; | ||||
| 
 | ||||
| use nur\sery\wip\schema\input\Input; | ||||
| use nur\sery\wip\schema\types\IType; | ||||
| use nulib\ref\schema\ref_schema; | ||||
| use nulib\schema\input\Input; | ||||
| use nulib\schema\types\IType; | ||||
| 
 | ||||
| class WrapperContext { | ||||
|   function __construct(Schema $schema, Wrapper $wrapper, Input $input, $valueKey, Result $result) { | ||||
| 
 | ||||
|   function __construct(Schema $schema, ?Input $input, $valueKey, ?array $params) { | ||||
|     $this->resetParams($params); | ||||
|     $this->schema = $schema; | ||||
|     $this->wrapper = $wrapper; | ||||
|     $this->input = $input; | ||||
|     $this->result = $result; | ||||
|     $this->type = null; | ||||
|     $this->origValue = null; | ||||
|     $this->value = null; | ||||
|     if ($input !== null) $this->input = $input; | ||||
|     $this->valueKey = $valueKey; | ||||
|   } | ||||
| 
 | ||||
|   public ?array $params; | ||||
|   public bool $analyze, $analyzed = false; | ||||
|   public bool $normalize, $normalized = false; | ||||
|   public ?bool $throw; | ||||
| 
 | ||||
|   function resetParams(?array $params): void { | ||||
|     $this->params = $params; | ||||
|     $this->analyze = $params["analyze"] ?? ref_schema::PARAMS_SCHEMA["analyze"][1]; | ||||
|     $this->normalize = $params["normalize"] ?? ref_schema::PARAMS_SCHEMA["normalize"][1]; | ||||
|     $this->throw = $params["throw"] ?? ref_schema::PARAMS_SCHEMA["throw"][1]; | ||||
|   } | ||||
| 
 | ||||
|   /** schéma de la valeur */ | ||||
|   public Schema $schema; | ||||
|   public Wrapper $wrapper; | ||||
|   /** source et destination de la valeur */ | ||||
|   public Input $input; | ||||
|   public Result $result; | ||||
|   public ?IType $type; | ||||
|   /** @var mixed */ | ||||
|   public $origValue; | ||||
|   /** @var mixed */ | ||||
|   public $value; | ||||
|   /** @var int|string|null */ | ||||
|   /** @var string|int|null clé de la valeur dans le tableau destination */ | ||||
|   public $valueKey; | ||||
|   /** @var mixed */ | ||||
|   public $origValue = null; | ||||
|   /** @var mixed */ | ||||
|   public $value = null; | ||||
| 
 | ||||
|   /** type de la valeur de la clé sélectionnée après analyse */ | ||||
|   public ?IType $type = null; | ||||
|   /** résultat de l'analyse de la valeur de la clé sélectionnée */ | ||||
|   public ?Result $result = null; | ||||
| } | ||||
|  | ||||
| @ -1,53 +0,0 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\_assoc; | ||||
| 
 | ||||
| use nulib\ValueException; | ||||
| use nur\sery\wip\schema\Result; | ||||
| 
 | ||||
| class AssocResult extends Result { | ||||
|   function __construct(Result $arrayResult, array &$keyResults) { | ||||
|     $this->arrayResult = $arrayResult; | ||||
|     $this->keyResults =& $keyResults; | ||||
|     $this->result =& $this->arrayResult; | ||||
|     parent::__construct(); | ||||
|   } | ||||
| 
 | ||||
|   function isAssoc(?AssocResult &$result=null): bool { $result = $this; return true;} | ||||
| 
 | ||||
|   protected Result $arrayResult; | ||||
| 
 | ||||
|   /** @var Result[] */ | ||||
|   protected array $keyResults; | ||||
| 
 | ||||
|   function getKeys(): array { | ||||
|     return array_keys($this->keyResults); | ||||
|   } | ||||
| 
 | ||||
|   protected Result $result; | ||||
| 
 | ||||
|   function select($key): Result { | ||||
|     if ($key === null) { | ||||
|       $this->result =& $this->arrayResult; | ||||
|     } elseif (array_key_exists($key, $this->keyResults)) { | ||||
|       $this->result =& $this->keyResults[$key]; | ||||
|     } else { | ||||
|       throw ValueException::invalid_key($key); | ||||
|     } | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   function reset(): void { | ||||
|     $this->arrayResult->reset(); | ||||
|     foreach ($this->keyResults as $result) { | ||||
|       $result->reset(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function __get(string $name) { | ||||
|     return $this->result[$name]; | ||||
|   } | ||||
| 
 | ||||
|   function __set(string $name, $value): void { | ||||
|     $this->result[$name] = $value; | ||||
|   } | ||||
| } | ||||
| @ -1,21 +1,19 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\_assoc; | ||||
| namespace nulib\schema\_assoc; | ||||
| 
 | ||||
| use nulib\cl; | ||||
| use nulib\ref\schema\ref_schema; | ||||
| use nur\sery\wip\schema\Schema; | ||||
| use nur\sery\wip\schema\Wrapper; | ||||
| use nulib\schema\Schema; | ||||
| use nulib\schema\Wrapper; | ||||
| use nulib\ValueException; | ||||
| 
 | ||||
| /** | ||||
|  * Class AssocSchema | ||||
|  */ | ||||
| class AssocSchema extends Schema { | ||||
|   /** @var array meta-schema d'un schéma de nature tableau associatif */ | ||||
|   const METASCHEMA = ref_schema::ASSOC_METASCHEMA; | ||||
| 
 | ||||
|   /** | ||||
|    * indiquer si $definition est une définition de schéma de nature tableau | ||||
|    * associatif que {@link normalize()} pourrait normaliser | ||||
|    * associatif que {@link normalize_definition()} pourrait normaliser | ||||
|    */ | ||||
|   static function isa_definition($definition): bool { | ||||
|     if (!is_array($definition)) return false; | ||||
| @ -27,7 +25,7 @@ class AssocSchema extends Schema { | ||||
|     return !cl::have_num_keys($definition); | ||||
|   } | ||||
| 
 | ||||
|   static function normalize($definition, $definitionKey=null): array { | ||||
|   static function normalize_definition($definition, $definitionKey=null): array { | ||||
|     if (!is_array($definition)) $definition = [$definition]; | ||||
|     if (!self::have_nature($definition)) { | ||||
|       $definition = [ | ||||
| @ -36,7 +34,8 @@ class AssocSchema extends Schema { | ||||
|         "schema" => $definition, | ||||
|       ]; | ||||
|     } | ||||
|     self::_normalize($definition, $definitionKey); | ||||
|     $natureMetaschema = array_merge(ref_schema::NATURE_METASCHEMA, ref_schema::ASSOC_NATURE_METASCHEMA); | ||||
|     self::_normalize_definition($definition, $definitionKey, $natureMetaschema); | ||||
|     self::_ensure_nature($definition, "assoc", "array"); | ||||
|     return $definition; | ||||
|   } | ||||
| @ -44,25 +43,54 @@ class AssocSchema extends Schema { | ||||
|   function __construct($definition=null, $definitionKey=null, bool $normalize=true) { | ||||
|     if ($definition === null) $definition = static::SCHEMA; | ||||
|     if ($normalize) { | ||||
|       $definition = self::normalize($definition, $definitionKey); | ||||
|       $definition = self::normalize_definition($definition, $definitionKey); | ||||
|       $this->_definition = $definition; | ||||
|       self::_ensure_type($definition); | ||||
|       self::_ensure_schema_instances($definition); | ||||
|     } else { | ||||
|       # ici, $definition contient un schema déjà instancié, mais c'est le mieux
 | ||||
|       # qu'on puisse faire
 | ||||
|       $this->_definition = $definition; | ||||
|     } | ||||
|     $this->definition = $definition; | ||||
|     $keys = []; | ||||
|     foreach ($definition["schema"] as $key => $schema) { | ||||
|       if (!$schema["computed"]) $keys[] = $key; | ||||
|     } | ||||
|     $this->keys = $keys; | ||||
|   } | ||||
| 
 | ||||
|   function isAssoc(?AssocSchema &$schema=null): bool { | ||||
|     $schema = $this; | ||||
|     return true; | ||||
|   protected array $keys; | ||||
| 
 | ||||
|   function getKeys(): array { | ||||
|     return $this->keys; | ||||
|   } | ||||
| 
 | ||||
|   function getSchema($key=false): Schema { | ||||
|     if ($key === null || $key === false) return $this; | ||||
|     $schema = $this->definition["schema"][$key] ?? null; | ||||
|     if ($schema === null) throw ValueException::invalid_key($key); | ||||
|     return $schema; | ||||
|   } | ||||
| 
 | ||||
|   protected function newWrapper(): AssocWrapper { | ||||
|     return new AssocWrapper($this); | ||||
|   } | ||||
| 
 | ||||
|   function getWrapper(&$array=null, $arrayKey=null, ?Wrapper &$wrapper=null): AssocWrapper { | ||||
|   function getWrapper(&$value=null, $valueKey=null, ?array $params=null, ?Wrapper &$wrapper=null): AssocWrapper { | ||||
|     # si pas de valeur ni de wrapper, pas d'analyse et donc pas d'exception
 | ||||
|     # cf le code similaire dans AssocWrapper::__construct()
 | ||||
|     $dontAnalyze = $value === null && $wrapper === null; | ||||
|     if (!($wrapper instanceof AssocWrapper)) $wrapper = $this->newWrapper(); | ||||
|     return $wrapper->reset($array, $arrayKey); | ||||
| 
 | ||||
|     # la nature du schéma peut contenir des paramètres par défaut
 | ||||
|     $nature = $this->definition[""]; | ||||
|     foreach (array_keys(ref_schema::ASSOC_PARAMS_SCHEMA) as $paramKey) { | ||||
|       $paramValue = $nature[$paramKey] ?? null; | ||||
|       if ($paramValue !== null) $params[$paramKey] = $paramValue; | ||||
|     } | ||||
|     if ($params !== null) $wrapper->resetParams($params); | ||||
| 
 | ||||
|     return $wrapper->reset($value, $valueKey, $dontAnalyze? ["analyze" => false]: null); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,140 +1,206 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\_assoc; | ||||
| namespace nulib\schema\_assoc; | ||||
| 
 | ||||
| use nulib\cl; | ||||
| use nulib\ref\schema\ref_analyze; | ||||
| use nulib\schema\_scalar\ScalarWrapper; | ||||
| use nulib\schema\ConsolidatedResult; | ||||
| use nulib\schema\input\Input; | ||||
| use nulib\schema\Result; | ||||
| use nulib\schema\types\IType; | ||||
| use nulib\schema\Wrapper; | ||||
| use nulib\schema\WrapperContext; | ||||
| use nulib\ValueException; | ||||
| use nur\sery\wip\schema\_scalar\ScalarResult; | ||||
| use nur\sery\wip\schema\_scalar\ScalarWrapper; | ||||
| use nur\sery\wip\schema\input\Input; | ||||
| use nur\sery\wip\schema\Result; | ||||
| use nur\sery\wip\schema\types\IType; | ||||
| use nur\sery\wip\schema\Wrapper; | ||||
| 
 | ||||
| class AssocWrapper extends Wrapper { | ||||
|   function __construct(AssocSchema $schema, &$array=null, $arrayKey=null, ?array $params=null) { | ||||
|     $verifix = $params["verifix"] ?? true; | ||||
|     $throw = $params["throw"] ?? null; | ||||
|     if ($array !== null && $throw === null) { | ||||
|       # Si $value est null, ne pas lancer d'exception, parce qu'on considère que
 | ||||
|       # c'est une initialisation sans conséquences
 | ||||
|       $throw = true; | ||||
|   function __construct(AssocSchema $schema, &$value=null, $valueKey=null, ?array $params=null) { | ||||
|     $this->context = $context = new AssocWrapperContext($schema, null, null, $params); | ||||
|     $paramsNoThrow = cl::merge($params, [ | ||||
|       "throw" => false, | ||||
|     ]); | ||||
| 
 | ||||
|     $keys = $schema->getKeys(); | ||||
|     $keyWrappers = []; | ||||
|     foreach ($keys as $key) { | ||||
|       $keyDummy = null; | ||||
|       $keyWrappers[$key] = $schema->getSchema($key)->getWrapper($keyDummy, null, $paramsNoThrow); | ||||
|     } | ||||
|     $context->keys = $keys; | ||||
|     $context->keyWrappers = $keyWrappers; | ||||
| 
 | ||||
|     $arrayDummy = null; | ||||
|     $context->arrayWrapper = new ScalarWrapper($schema, $arrayDummy, null, $paramsNoThrow, $context); | ||||
| 
 | ||||
|     $context->consolidatedResult = new ConsolidatedResult(); | ||||
| 
 | ||||
|     if ($value !== null) { | ||||
|       # n'initialiser que si $value n'est pas null
 | ||||
|       $this->reset($value, $valueKey); | ||||
|     } | ||||
|     $this->schema = $schema; | ||||
|     $this->verifix = $verifix; | ||||
|     $this->throw = $throw ?? false; | ||||
|     $this->result = new AssocResult(); | ||||
|     $this->reset($array, $arrayKey); | ||||
|     $this->throw = $throw ?? true; | ||||
|   } | ||||
| 
 | ||||
|   function isAssoc(?AssocWrapper &$wrapper=null): bool { $wrapper = $this; return true; } | ||||
|   /** @var AssocWrapperContext  */ | ||||
|   protected WrapperContext $context; | ||||
| 
 | ||||
|   protected bool $verifix; | ||||
| 
 | ||||
|   protected bool $throw; | ||||
| 
 | ||||
|   /** schéma de ce tableau */ | ||||
|   protected AssocSchema $schema; | ||||
| 
 | ||||
|   /** source et destination de la valeur */ | ||||
|   protected Input $input; | ||||
| 
 | ||||
|   /** @var string|int|null clé du tableau dans le tableau destination */ | ||||
|   protected $arrayKey; | ||||
| 
 | ||||
|   protected IType $arrayType; | ||||
| 
 | ||||
|   protected ScalarResult $arrayResult; | ||||
| 
 | ||||
|   /** @var IType[] */ | ||||
|   protected array $keyTypes; | ||||
| 
 | ||||
|   /** @var Result[] */ | ||||
|   protected array $keyResults; | ||||
| 
 | ||||
|   protected AssocResult $result; | ||||
| 
 | ||||
|   protected ?array $keys; | ||||
| 
 | ||||
|   protected ?array $wrappers; | ||||
| 
 | ||||
|   protected function newInput(&$value): Input { | ||||
|     return new Input($value); | ||||
|   protected function resetContext(bool $resetSelectedKey): void { | ||||
|     parent::resetContext($resetSelectedKey); | ||||
|     $context = $this->context; | ||||
|     $context->arrayWrapper->getResult()->reset(); | ||||
|     foreach ($context->keyWrappers as $wrapper) { | ||||
|       $wrapper->getResult()->reset(); | ||||
|     } | ||||
|     if ($resetSelectedKey) $context->selectedKey = null; | ||||
|   } | ||||
| 
 | ||||
|   function reset(&$array, $arrayKey=null, ?bool $verifix=null): Wrapper { | ||||
|     if ($array instanceof Input) $input = $array; | ||||
|     else $input = $this->newInput($array); | ||||
|     $this->input = $input; | ||||
|     $this->arrayKey = $arrayKey; | ||||
|     $this->analyze(); | ||||
|     if ($verifix ?? $this->verifix) $this->verifix(); | ||||
|   function reset(&$value, $valueKey=null, ?array $params=null): Wrapper { | ||||
|     $context = $this->context; | ||||
|     if ($value instanceof Input) $input = $value; | ||||
|     else $input = $this->newInput($value); | ||||
|     $context->input = $input; | ||||
|     $context->valueKey = $valueKey; | ||||
|     foreach ($context->keyWrappers as $key => $keyWrapper) { | ||||
|       $keyInput = $input->addKey($valueKey); | ||||
|       $keyWrapper->reset($keyInput, $key, ["analyze" => false]); | ||||
|     } | ||||
|     $this->afterModify($params, true); | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   function getKeys(): array { | ||||
|     return $this->keys; | ||||
|     return $this->context->keys; | ||||
|   } | ||||
| 
 | ||||
|   function select($key=null): ScalarWrapper { | ||||
|     $wrapper = $this->wrappers[$key] ?? null; | ||||
|     if ($key !== null) return $wrapper; | ||||
|     throw ValueException::invalid_key($key); | ||||
|   protected function _getWrapper($key): Wrapper { | ||||
|     $context = $this->context; | ||||
|     if ($key === null || $key === "") return $context->arrayWrapper; | ||||
|     $wrapper = $context->keyWrappers[$key] ?? null; | ||||
|     if ($wrapper === null) throw ValueException::invalid_key($key); | ||||
|     return $wrapper; | ||||
|   } | ||||
| 
 | ||||
|   /** @param Result[] $results */ | ||||
|   function verifix(?bool $throw=null, ?array &$results=null): bool { | ||||
|   /** @param string|int|null $key */ | ||||
|   function select($key=null): Wrapper { | ||||
|     $wrapper = $this->_getWrapper($key); | ||||
|     $this->context->selectedKey = $key; | ||||
|     return $wrapper; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * @param AssocWrapperContext $context | ||||
|    * @param AssocWrapper $wrapper | ||||
|    */ | ||||
|   static function _analyze(WrapperContext $context, Wrapper $wrapper, ?array $params): int { | ||||
|     if ($params["ensure_array"] ?? $context->ensureArray) { | ||||
|       $valueKey = $context->valueKey; | ||||
|       $array = $context->input->get($valueKey); | ||||
|       if ($array === null) $context->input->set([], $valueKey); | ||||
|     } | ||||
| 
 | ||||
|   function getResult(): AssocResult { | ||||
|     return $this->result; | ||||
|     if ($params["ensure_assoc"] ?? $context->ensureAssoc) { | ||||
|       $context->input->ensureAssoc($context->schema->getKeys()); | ||||
|     } | ||||
| 
 | ||||
|     $what = ScalarWrapper::_analyze($context, $wrapper, $params); | ||||
|     if (!$context->result->valid) return $what; | ||||
| 
 | ||||
|     $result = $context->consolidatedResult; | ||||
|     $result->setValid(); | ||||
|     foreach ($context->keyWrappers as $key => $keyWrapper) { | ||||
|       $keyWrapper->analyze($params); | ||||
|       if ($keyWrapper->isValid()) continue; | ||||
|       $what = ref_analyze::INVALID; | ||||
|       #XXX pour $prefix, utiliser si possible la description ou une autre valeur
 | ||||
|       # "user-friendly". possibilité de sélectionner la valeur à utiliser avec
 | ||||
|       # $params?
 | ||||
|       $prefix = $key; | ||||
|       if (!$keyWrapper->isPresent()) { | ||||
|         $result->addMissingMessage($keyWrapper, $prefix); | ||||
|       } elseif (!$keyWrapper->isAvailable()) { | ||||
|         $result->addUnavailableMessage($keyWrapper, $prefix); | ||||
|       } elseif ($keyWrapper->isNull()) { | ||||
|         $result->addNullMessage($keyWrapper, $prefix); | ||||
|       } else { | ||||
|         $result->addInvalidMessage($keyWrapper, $prefix); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     return $what; | ||||
|   } | ||||
| 
 | ||||
|   function isPresent(): bool { | ||||
|     return $this->result->present; | ||||
|   /** | ||||
|    * @param AssocWrapperContext $context | ||||
|    * @param AssocWrapper $wrapper | ||||
|    */ | ||||
|   static function _normalize(WrapperContext $context, Wrapper $wrapper, ?array $params): bool { | ||||
|     $schema = $context->schema; | ||||
|     $keys = $schema->getKeys(); | ||||
| 
 | ||||
|     $defaults = []; | ||||
|     $missings = null; | ||||
|     foreach ($keys as $key) { | ||||
|       $type = $wrapper->getType($key); | ||||
|       $default = $schema->getSchema($key)->default; | ||||
|       if ($default === null) $default = $type->getNullValue(); | ||||
|       $defaults[$key] = $default; | ||||
|       $missing = $type->getMissingValue($valid); | ||||
|       if ($valid) $missings[$key] = $missing; | ||||
|     } | ||||
|     if ($params["ensure_keys"] ?? $context->ensureKeys) { | ||||
|       $context->input->ensureKeys($defaults, $missings, $params); | ||||
|     } else { | ||||
|       $context->input->deleteMissings($missings, $params); | ||||
|     } | ||||
| 
 | ||||
|     if ($params["ensure_order"] ?? $context->ensureOrder) { | ||||
|       $context->input->ensureOrder($keys, $params); | ||||
|     } | ||||
| 
 | ||||
|     $modified = ScalarWrapper::_normalize($context, $wrapper, $params); | ||||
|     foreach ($context->keyWrappers as $keyWrapper) { | ||||
|       if ($keyWrapper->normalize($params)) $modified = true; | ||||
|     } | ||||
|     return $modified; | ||||
|   } | ||||
| 
 | ||||
|   function getType(): IType { | ||||
|     return $this->arrayType; | ||||
|   protected function getConsolidatedResult(): Result { | ||||
|     return $this->context->consolidatedResult; | ||||
|   } | ||||
| 
 | ||||
|   function isAvailable(): bool { | ||||
|     return $this->result->available; | ||||
|   function getResult($key=false): Result { | ||||
|     if ($key === null) return $this->getConsolidatedResult(); | ||||
|     if ($key === false) $key = $this->context->selectedKey; | ||||
|     return $this->_getWrapper($key)->getResult(); | ||||
|   } | ||||
| 
 | ||||
|   function isValid(): bool { | ||||
|     return $this->result->valid; | ||||
|   function getType($key=false): IType { | ||||
|     if ($key === false) $key = $this->context->selectedKey; | ||||
|     return $this->_getWrapper($key)->getType(); | ||||
|   } | ||||
| 
 | ||||
|   function isNormalized(): bool { | ||||
|     return $this->result->normalized; | ||||
|   function get($default=null, $key=false) { | ||||
|     $context = $this->context; | ||||
|     if (!$context->arrayWrapper->isAvailable()) return $default; | ||||
|     if ($key === false) $key = $context->selectedKey; | ||||
|     return $this->_getWrapper($key)->get($default); | ||||
|   } | ||||
| 
 | ||||
|   function get($default=null) { | ||||
|     if ($this->result->available) return $this->input->get($this->arrayKey); | ||||
|     else return $default; | ||||
|   } | ||||
| 
 | ||||
|   function set($value, ?bool $verifix=null): AssocWrapper { | ||||
|     $this->input->set($value, $this->arrayKey); | ||||
|     $this->analyze(); | ||||
|     if ($verifix ?? $this->verifix) $this->verifix(); | ||||
|   function set($value, ?array $params=null, $key=false): Wrapper { | ||||
|     $context = $this->context; | ||||
|     if ($key === false) $key = $context->selectedKey; | ||||
|     $this->_getWrapper($key)->set($value); | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   function unset(?bool $verifix=null): AssocWrapper { | ||||
|     $this->input->unset($this->arrayKey); | ||||
|     $this->analyze(); | ||||
|     if ($verifix ?? $this->verifix) $this->verifix(); | ||||
|   function unset(?array $params=null, $key=false): Wrapper { | ||||
|     $context = $this->context; | ||||
|     if ($key === false) $key = $context->selectedKey; | ||||
|     $this->_getWrapper($key)->unset(); | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   function format($format = null): string { | ||||
|     // TODO: Implement format() method.
 | ||||
|   } | ||||
| 
 | ||||
|   function ensureKeys(): bool { | ||||
|   } | ||||
|   function orderKeys(): bool { | ||||
|   function format($format=null, $key=false): string { | ||||
|     $context = $this->context; | ||||
|     if ($key === false) $key = $context->selectedKey; | ||||
|     return $this->_getWrapper($key)->format($format); | ||||
|   } | ||||
| } | ||||
|  | ||||
							
								
								
									
										38
									
								
								src/schema/_assoc/AssocWrapperContext.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/schema/_assoc/AssocWrapperContext.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | ||||
| <?php | ||||
| namespace nulib\schema\_assoc; | ||||
| 
 | ||||
| use nulib\ref\schema\ref_schema; | ||||
| use nulib\schema\_scalar\ScalarWrapper; | ||||
| use nulib\schema\ConsolidatedResult; | ||||
| use nulib\schema\Wrapper; | ||||
| use nulib\schema\WrapperContext; | ||||
| 
 | ||||
| class AssocWrapperContext extends WrapperContext { | ||||
| 
 | ||||
|   public bool $ensureArray; | ||||
|   public bool $ensureAssoc; | ||||
|   public bool $ensureKeys; | ||||
|   public bool $ensureOrder; | ||||
| 
 | ||||
|   public function resetParams(?array $params): void { | ||||
|     parent::resetParams($params); | ||||
|     $this->ensureArray = $params["ensure_array"] ?? ref_schema::ASSOC_PARAMS_SCHEMA["ensure_array"][1]; | ||||
|     $this->ensureAssoc = $params["ensure_assoc"] ?? ref_schema::ASSOC_PARAMS_SCHEMA["ensure_assoc"][1]; | ||||
|     $this->ensureKeys = $params["ensure_keys"] ?? ref_schema::ASSOC_PARAMS_SCHEMA["ensure_keys"][1]; | ||||
|     $this->ensureOrder = $params["ensure_order"] ?? ref_schema::ASSOC_PARAMS_SCHEMA["ensure_order"][1]; | ||||
|   } | ||||
| 
 | ||||
|   public ?ScalarWrapper $arrayWrapper = null; | ||||
| 
 | ||||
|   /** liste des clés valides */ | ||||
|   public array $keys; | ||||
| 
 | ||||
|   /** @var Wrapper[] */ | ||||
|   public array $keyWrappers; | ||||
| 
 | ||||
|   /** @var string|int|null clé sélectionnée */ | ||||
|   public $selectedKey = null; | ||||
| 
 | ||||
|   /** résultat consolidé de l'analyse du tableau et de ses composants */ | ||||
|   public ConsolidatedResult $consolidatedResult; | ||||
| } | ||||
| @ -1,53 +0,0 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\_list; | ||||
| 
 | ||||
| use nulib\ValueException; | ||||
| use nur\sery\wip\schema\Result; | ||||
| 
 | ||||
| class ListResult extends Result { | ||||
|   function __construct(Result $arrayResult, array &$keyResults) { | ||||
|     $this->arrayResult = $arrayResult; | ||||
|     $this->keyResults =& $keyResults; | ||||
|     $this->result =& $this->arrayResult; | ||||
|       parent::__construct(); | ||||
|   } | ||||
| 
 | ||||
|   function isList(?ListResult &$result=null): bool { $result = $this; return true;} | ||||
| 
 | ||||
|   protected Result $arrayResult; | ||||
| 
 | ||||
|   /** @var Result[] */ | ||||
|   protected array $keyResults; | ||||
| 
 | ||||
|   function getKeys(): array { | ||||
|     return array_keys($this->keyResults); | ||||
|   } | ||||
| 
 | ||||
|   protected Result $result; | ||||
| 
 | ||||
|   function select($key): Result { | ||||
|     if ($key === null) { | ||||
|       $this->result =& $this->arrayResult; | ||||
|     } elseif (array_key_exists($key, $this->keyResults)) { | ||||
|       $this->result =& $this->keyResults[$key]; | ||||
|     } else { | ||||
|       throw ValueException::invalid_key($key); | ||||
|     } | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   function reset(): void { | ||||
|     $this->arrayResult->reset(); | ||||
|     foreach ($this->keyResults as $result) { | ||||
|       $result->reset(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function __get(string $name) { | ||||
|     return $this->result[$name]; | ||||
|   } | ||||
| 
 | ||||
|   function __set(string $name, $value): void { | ||||
|     $this->result[$name] = $value; | ||||
|   } | ||||
| } | ||||
| @ -1,9 +1,10 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\_list; | ||||
| namespace nulib\schema\_list; | ||||
| 
 | ||||
| use nulib\ref\schema\ref_schema; | ||||
| use nur\sery\wip\schema\Schema; | ||||
| use nur\sery\wip\schema\Wrapper; | ||||
| use nulib\schema\Schema; | ||||
| use nulib\schema\Wrapper; | ||||
| use nulib\ValueException; | ||||
| 
 | ||||
| class ListSchema extends Schema { | ||||
|   /** @var array meta-schema d'un schéma de nature liste */ | ||||
| @ -34,7 +35,7 @@ class ListSchema extends Schema { | ||||
|         "schema" => $definition[0], | ||||
|       ]; | ||||
|     } | ||||
|     self::_normalize($definition, $definitionKey); | ||||
|     self::_normalize_definition($definition, $definitionKey); | ||||
|     self::_ensure_nature($definition, "list", "array"); | ||||
|     return $definition; | ||||
|   } | ||||
| @ -50,17 +51,26 @@ class ListSchema extends Schema { | ||||
|     $this->definition = $definition; | ||||
|   } | ||||
| 
 | ||||
|   function isList(?ListSchema &$schema=null): bool { | ||||
|     $schema = $this; | ||||
|     return true; | ||||
|   const KEYS = [null]; | ||||
| 
 | ||||
|   function getKeys(): array { | ||||
|     return self::KEYS; | ||||
|   } | ||||
| 
 | ||||
|   public function getSchema($key=false): Schema { | ||||
|     if ($key !== null) throw ValueException::invalid_key($key); | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   protected function newWrapper(): ListWrapper { | ||||
|     return new ListWrapper($this); | ||||
|   } | ||||
| 
 | ||||
|   function getWrapper(&$value=null, $valueKey=null, ?Wrapper &$wrapper=null): ListWrapper { | ||||
|   function getWrapper(&$value=null, $valueKey=null, ?array $params = null, ?Wrapper &$wrapper=null): ListWrapper { | ||||
|     # si pas de valeur ni de wrapper, pas de vérification et donc pas d'exception
 | ||||
|     # cf le code similaire dans ScalarWrapper::__construct()
 | ||||
|     $verifix = $value !== null || $wrapper !== null; | ||||
|     if (!($wrapper instanceof ListWrapper)) $wrapper = $this->newWrapper(); | ||||
|     return $wrapper->reset($value, $valueKey); | ||||
|     return $wrapper->reset($value, $valueKey, $verifix); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,12 +1,10 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\_list; | ||||
| namespace nulib\schema\_list; | ||||
| 
 | ||||
| use nur\sery\wip\schema\Result; | ||||
| use nur\sery\wip\schema\Wrapper; | ||||
| use nulib\schema\Result; | ||||
| use nulib\schema\Wrapper; | ||||
| 
 | ||||
| abstract/*XXX*/ class ListWrapper extends Wrapper { | ||||
|   function isList(?ListWrapper &$wrapper=null): bool { $wrapper = $this; return true; } | ||||
| 
 | ||||
|   function ensureKeys(): bool { | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -1,164 +0,0 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\_scalar; | ||||
| 
 | ||||
| use nulib\cl; | ||||
| use nulib\ref\schema\ref_analyze; | ||||
| use nulib\ref\schema\ref_schema; | ||||
| use nulib\ValueException; | ||||
| use nur\sery\wip\schema\Result; | ||||
| use Throwable; | ||||
| 
 | ||||
| /** | ||||
|  * Class ScalarResult: résultat de l'analyse ou de la normalisation d'une valeur | ||||
|  */ | ||||
| class ScalarResult extends Result { | ||||
|   function isScalar(?ScalarResult &$result=null): bool { $result = $this; return true; } | ||||
| 
 | ||||
|   function getKeys(): array { | ||||
|     return [null]; | ||||
|   } | ||||
| 
 | ||||
|   function select($key): Result { | ||||
|     if ($key !== null) throw ValueException::invalid_key($key); | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   /** @var array  */ | ||||
|   protected $result; | ||||
|    | ||||
|   function reset(): void { | ||||
|     $this->result = array_merge( | ||||
|       array_fill_keys(static::KEYS, null), [ | ||||
|       "resultAvailable" => false, | ||||
|       "present" => false, | ||||
|       "available" => false, | ||||
|       "null" => false, | ||||
|       "valid" => false, | ||||
|       "normalized" => false, | ||||
|     ]); | ||||
|   } | ||||
| 
 | ||||
|   function __get(string $name) { | ||||
|     return $this->result[$name]; | ||||
|   } | ||||
| 
 | ||||
|   function __set(string $name, $value): void { | ||||
|     $this->result[$name] = $value; | ||||
|   } | ||||
| 
 | ||||
|   protected static function replace_key(string &$message, ?string $key): void { | ||||
|     if ($key) { | ||||
|       $message = str_replace("{key}", $key, $message); | ||||
|     } else { | ||||
|       $message = str_replace("{key}: ", "", $message); | ||||
|       $message = str_replace("cette valeur", "la valeur", $message); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   protected static function replace_orig(string &$message, $origValue): void { | ||||
|     $message = str_replace("{orig}", strval($origValue), $message); | ||||
|   } | ||||
| 
 | ||||
|   protected function getMessage(string $key, ScalarSchema $schema): string { | ||||
|     $message = cl::get($schema->messages, $key); | ||||
|     if ($message !== null) return $message; | ||||
|     return cl::get(ref_schema::MESSAGES, $key); | ||||
|   } | ||||
| 
 | ||||
|   function setMissing(ScalarSchema $schema): int { | ||||
|     $this->resultAvailable = true; | ||||
|     $this->present = false; | ||||
|     $this->available = false; | ||||
|     if (!$schema->required) { | ||||
|       $this->null = false; | ||||
|       $this->valid = true; | ||||
|       $this->normalized = true; | ||||
|       return ref_analyze::NORMALIZED; | ||||
|     } else { | ||||
|       $messageKey = $this->messageKey = "missing"; | ||||
|       $message = $this->getMessage($messageKey, $schema); | ||||
|       self::replace_key($message, $schema->name); | ||||
|       $this->message = $message; | ||||
|       return ref_analyze::MISSING; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function setUnavailable(ScalarSchema $schema): int { | ||||
|     $this->resultAvailable = true; | ||||
|     $this->present = true; | ||||
|     $this->available = false; | ||||
|     if (!$schema->required) { | ||||
|       $this->null = false; | ||||
|       $this->valid = true; | ||||
|       $this->normalized = true; | ||||
|       return ref_analyze::NORMALIZED; | ||||
|     } else { | ||||
|       $messageKey = $this->messageKey = "unavailable"; | ||||
|       $message = $this->getMessage($messageKey, $schema); | ||||
|       self::replace_key($message, $schema->name); | ||||
|       $this->message = $message; | ||||
|       return ref_analyze::UNAVAILABLE; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function setNull(ScalarSchema $schema): int { | ||||
|     $this->resultAvailable = true; | ||||
|     $this->present = true; | ||||
|     $this->available = true; | ||||
|     $this->null = true; | ||||
|     if ($schema->nullable) { | ||||
|       $this->valid = true; | ||||
|       $this->normalized = true; | ||||
|       return ref_analyze::NORMALIZED; | ||||
|     } else { | ||||
|       $messageKey = $this->messageKey = "null"; | ||||
|       $message = $this->getMessage($messageKey, $schema); | ||||
|       self::replace_key($message, $schema->name); | ||||
|       $this->message = $message; | ||||
|       return ref_analyze::NULL; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function setInvalid($value, ScalarSchema $schema, ?Throwable $t=null): int { | ||||
|     $this->resultAvailable = true; | ||||
|     $this->present = true; | ||||
|     $this->available = true; | ||||
|     $this->null = false; | ||||
|     $this->valid = false; | ||||
|     $this->origValue = $value; | ||||
|     $messageKey = $this->messageKey = "invalid"; | ||||
|     $message = $this->getMessage($messageKey, $schema); | ||||
|     self::replace_key($message, $schema->name); | ||||
|     self::replace_orig($message, $schema->orig); | ||||
|     if ($t !== null) { | ||||
|       $tmessage = ValueException::get_message($t); | ||||
|       if ($tmessage) $message .= ": $tmessage"; | ||||
|     } | ||||
|     $this->message = $message; | ||||
|     return ref_analyze::INVALID; | ||||
|   } | ||||
| 
 | ||||
|   function setValid($normalizedValue=null): int { | ||||
|     $this->resultAvailable = true; | ||||
|     $this->present = true; | ||||
|     $this->available = true; | ||||
|     $this->null = false; | ||||
|     $this->valid = true; | ||||
|     $this->normalizedValue = $normalizedValue; | ||||
|     return ref_analyze::VALID; | ||||
|   } | ||||
| 
 | ||||
|   function setNormalized(): int { | ||||
|     $this->resultAvailable = true; | ||||
|     $this->present = true; | ||||
|     $this->available = true; | ||||
|     $this->null = false; | ||||
|     $this->valid = true; | ||||
|     $this->normalized = true; | ||||
|     return ref_analyze::NORMALIZED; | ||||
|   } | ||||
| 
 | ||||
|   function throw(bool $throw): void { | ||||
|     if ($throw) throw new ValueException($this->message); | ||||
|   } | ||||
| } | ||||
| @ -1,41 +1,18 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\_scalar; | ||||
| namespace nulib\schema\_scalar; | ||||
| 
 | ||||
| use nulib\ref\schema\ref_schema; | ||||
| use nur\sery\wip\schema\Schema; | ||||
| use nur\sery\wip\schema\types\IType; | ||||
| use nur\sery\wip\schema\Wrapper; | ||||
| use nulib\schema\Schema; | ||||
| use nulib\schema\Wrapper; | ||||
| use nulib\ValueException; | ||||
| 
 | ||||
| /** | ||||
|  * Class ScalarSchema | ||||
|  *  | ||||
|  * @property-read array|IType $type | ||||
|  * @property-read mixed $default | ||||
|  * @property-read string|null $title | ||||
|  * @property-read bool $required | ||||
|  * @property-read bool $nullable | ||||
|  * @property-read string|array|null $desc | ||||
|  * @property-read callable|null $analyzerFunc | ||||
|  * @property-read callable|null $extractorFunc | ||||
|  * @property-read callable|null $parserFunc | ||||
|  * @property-read callable|null $normalizerFunc | ||||
|  * @property-read array|null $messages | ||||
|  * @property-read callable|null $formatterFunc | ||||
|  * @property-read mixed $format | ||||
|  * @property-read array $nature | ||||
|  * @property-read array|null $schema | ||||
|  * @property-read string|int|null $name | ||||
|  * @property-read string|array|null $pkey | ||||
|  * @property-read string|null $header | ||||
|  * @property-read bool|null $composite | ||||
|  */ | ||||
| class ScalarSchema extends Schema { | ||||
|   /** @var array meta-schema d'un schéma de nature scalaire */ | ||||
|   const METASCHEMA = ref_schema::SCALAR_METASCHEMA; | ||||
| 
 | ||||
|   /** | ||||
|    * indiquer si $definition est une définition de schéma scalaire que | ||||
|    * {@link normalize()} pourrait normaliser | ||||
|    * {@link normalize_definition()} pourrait normaliser | ||||
|    */ | ||||
|   static function isa_definition($definition): bool { | ||||
|     # chaine ou null
 | ||||
| @ -64,8 +41,9 @@ class ScalarSchema extends Schema { | ||||
|     return $haveIndex0 && $count > 1; | ||||
|   } | ||||
| 
 | ||||
|   static function normalize($definition, $definitionKey=null): array { | ||||
|     self::_normalize($definition, $definitionKey); | ||||
|   static function normalize_definition($definition, $definitionKey=null): array { | ||||
|     $natureMetaschema = array_merge(ref_schema::NATURE_METASCHEMA, ref_schema::SCALAR_NATURE_METASCHEMA); | ||||
|     self::_normalize_definition($definition, $definitionKey, $natureMetaschema); | ||||
|     self::_ensure_nature($definition, "scalar"); | ||||
|     return $definition; | ||||
|   } | ||||
| @ -73,40 +51,47 @@ class ScalarSchema extends Schema { | ||||
|   function __construct($definition=null, $definitionKey=null, bool $normalize=true) { | ||||
|     if ($definition === null) $definition = static::SCHEMA; | ||||
|     if ($normalize) { | ||||
|       $definition = self::normalize($definition, $definitionKey); | ||||
|       $definition = self::normalize_definition($definition, $definitionKey); | ||||
|       $this->_definition = $definition; | ||||
|       self::_ensure_type($definition); | ||||
|       self::_ensure_schema_instances($definition); | ||||
|     } else { | ||||
|       # ici, $definition contient un schema déjà instancié, mais c'est le mieux
 | ||||
|       # qu'on puisse faire
 | ||||
|       $this->_definition = $definition; | ||||
|     } | ||||
|     $this->definition = $definition; | ||||
|   } | ||||
| 
 | ||||
|   function isScalar(?ScalarSchema &$schema=null): bool { | ||||
|     $schema = $this; | ||||
|     return true; | ||||
|   const KEYS = []; | ||||
| 
 | ||||
|   function getKeys(): array { | ||||
|     return self::KEYS; | ||||
|   } | ||||
| 
 | ||||
|   function getSchema($key=false): Schema { | ||||
|     if ($key === false || $key === null || $key === "") return $this; | ||||
|     throw ValueException::invalid_key($key); | ||||
|   } | ||||
| 
 | ||||
|   protected function newWrapper(): ScalarWrapper { | ||||
|     return new ScalarWrapper($this); | ||||
|   } | ||||
| 
 | ||||
|   function getWrapper(&$value=null, $valueKey=null, ?Wrapper &$wrapper=null): ScalarWrapper { | ||||
|   function getWrapper(&$value=null, $valueKey=null, ?array $params=null, ?Wrapper &$wrapper=null): ScalarWrapper { | ||||
|     # si pas de valeur ni de wrapper, pas de vérification et donc pas d'exception
 | ||||
|     # cf le code similaire dans ScalarWrapper::__construct()
 | ||||
|     $verifix = $value !== null || $wrapper !== null; | ||||
|     $dontAnalyze = $value === null && $wrapper === null; | ||||
|     if (!($wrapper instanceof ScalarWrapper)) $wrapper = $this->newWrapper(); | ||||
|     return $wrapper->reset($value, $valueKey, $verifix); | ||||
| 
 | ||||
|     # la nature du schéma peut contenir des paramètres par défaut
 | ||||
|     $nature = $this->definition[""]; | ||||
|     foreach (array_keys(ref_schema::SCALAR_PARAMS_SCHEMA) as $paramKey) { | ||||
|       $paramValue = $nature[$paramKey] ?? null; | ||||
|       if ($paramValue !== null) $params[$paramKey] = $paramValue; | ||||
|     } | ||||
|     if ($params !== null) $wrapper->resetParams($params); | ||||
| 
 | ||||
|     return $wrapper->reset($value, $valueKey, $dontAnalyze? ["analyze" => false]: null); | ||||
|   } | ||||
| 
 | ||||
|   #############################################################################
 | ||||
|   # key & properties
 | ||||
| 
 | ||||
|   const _PROPERTY_PKEYS = [ | ||||
|     "analyzerFunc" => "analyzer_func", | ||||
|     "extractorFunc" => "extractor_func", | ||||
|     "parserFunc" => "parser_func", | ||||
|     "normalizerFunc" => "normalizer_func", | ||||
|     "formatterFunc" => "formatter_func", | ||||
|     "nature" => ["", 0], | ||||
|   ]; | ||||
| } | ||||
|  | ||||
| @ -1,85 +1,56 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\_scalar; | ||||
| namespace nulib\schema\_scalar; | ||||
| 
 | ||||
| use nulib\php\func; | ||||
| use nulib\ref\schema\ref_analyze; | ||||
| use nulib\schema\Result; | ||||
| use nulib\schema\Schema; | ||||
| use nulib\schema\types; | ||||
| use nulib\schema\types\IType; | ||||
| use nulib\schema\Wrapper; | ||||
| use nulib\schema\WrapperContext; | ||||
| use nulib\ValueException; | ||||
| use nur\sery\wip\schema\WrapperContext; | ||||
| use nur\sery\wip\schema\input\Input; | ||||
| use nur\sery\wip\schema\types; | ||||
| use nur\sery\wip\schema\types\IType; | ||||
| use nur\sery\wip\schema\Wrapper; | ||||
| 
 | ||||
| /** | ||||
|  * Class ScalarWrapper | ||||
|  * | ||||
|  * @method ScalarWrapper reset(&$value, $valueKey=null, ?array $params=null) | ||||
|  * @method self set($value, ?array $params=null, $key=false) | ||||
|  * @method self unset(?array $params=null, $key=false) | ||||
|  */ | ||||
| class ScalarWrapper extends Wrapper { | ||||
|   function __construct(ScalarSchema $schema, &$value=null, $valueKey=null, ?array $params=null) { | ||||
|     $verifix = $params["verifix"] ?? true; | ||||
|     $throw = $params["throw"] ?? null; | ||||
|     if ($value !== null && $throw === null) { | ||||
|       # Si $value est null, ne pas lancer d'exception, parce qu'on considère que
 | ||||
|       # c'est une initialisation sans conséquences
 | ||||
|       $throw = true; | ||||
|   function __construct(Schema $schema, &$value=null, $valueKey=null, ?array $params=null, ?WrapperContext $context=null) { | ||||
|     if ($context === null) $context = new WrapperContext($schema, null, null, $params); | ||||
|     $context->result = new Result(); | ||||
|     $this->context = $context; | ||||
| 
 | ||||
|     if ($value !== null) { | ||||
|       # n'initialiser que si $value n'est pas null
 | ||||
|       $this->reset($value, $valueKey); | ||||
|     } else { | ||||
|       # il faut au moins que le type soit disponible
 | ||||
|       $this->resetContext(false); | ||||
|     } | ||||
|     $this->verifix = $verifix; | ||||
|     $this->throw = $throw ?? false; | ||||
|     $this->schema = $schema; | ||||
|     $this->result = new ScalarResult(); | ||||
|     $this->reset($value, $valueKey); | ||||
|     $this->throw = $throw ?? true; | ||||
|   } | ||||
| 
 | ||||
|   function isScalar(?ScalarWrapper &$wrapper=null): bool { $wrapper = $this; return true; } | ||||
| 
 | ||||
|   protected bool $verifix; | ||||
| 
 | ||||
|   protected bool $throw; | ||||
| 
 | ||||
|   /** schéma de cette valeur */ | ||||
|   protected ScalarSchema $schema; | ||||
| 
 | ||||
|   /** source et destination de la valeur */ | ||||
|   protected Input $input; | ||||
| 
 | ||||
|   /** @var string|int|null clé de la valeur dans le tableau destination */ | ||||
|   protected $valueKey; | ||||
| 
 | ||||
|   /** type de la valeur après analyse */ | ||||
|   protected ?IType $type; | ||||
| 
 | ||||
|   /** résultat de l'analyse de la valeur */ | ||||
|   protected ScalarResult $result; | ||||
| 
 | ||||
|   protected function newInput(&$value): Input { | ||||
|     return new Input($value); | ||||
|   } | ||||
| 
 | ||||
|   function reset(&$value, $valueKey=null, ?bool $verifix=null): Wrapper { | ||||
|     if ($value instanceof Input) $input = $value; | ||||
|     else $input = $this->newInput($value); | ||||
|     $this->input = $input; | ||||
|     $this->valueKey = $valueKey; | ||||
|     $this->type = null; | ||||
|     $this->analyze(); | ||||
|     if ($verifix ?? $this->verifix) $this->verifix(); | ||||
|     return $this; | ||||
|   } | ||||
|   protected WrapperContext $context; | ||||
| 
 | ||||
|   function getKeys(): array { | ||||
|     return [null]; | ||||
|     return ScalarSchema::KEYS; | ||||
|   } | ||||
| 
 | ||||
|   /** @param string|int|null $key */ | ||||
|   function select($key): ScalarWrapper { | ||||
|     if ($key !== null) throw ValueException::invalid_key($key); | ||||
|     return $this; | ||||
|   function select($key=null): ScalarWrapper { | ||||
|     if ($key === null || $key === "") return $this; | ||||
|     throw ValueException::invalid_key($key); | ||||
|   } | ||||
| 
 | ||||
|   /** analyser la valeur et résoudre son type */ | ||||
|   protected function analyze0(WrapperContext $context): int { | ||||
|   protected static function _analyze0(WrapperContext $context): int { | ||||
|     /** @var ScalarSchema $schema */ | ||||
|     $schema = $context->schema; | ||||
|     $input = $context->input; | ||||
|     $valueKey = $context->valueKey; | ||||
|     /** @var ScalarResult $result */ | ||||
|     $result = $context->result; | ||||
| 
 | ||||
|     $default = $schema->default; | ||||
| @ -112,7 +83,7 @@ class ScalarWrapper extends Wrapper { | ||||
|           $args = $name; | ||||
|           $name = $key; | ||||
|         } | ||||
|         $type = types::get($schema->nullable, $name, $args, $this->schema->getDefinition()); | ||||
|         $type = types::get($schema->nullable, $name, $args, $schema->getDefinition()); | ||||
|         if ($firstType === null) $firstType = $type; | ||||
|         $types[] = $type; | ||||
|         if ($type->isAvailable($input, $valueKey)) { | ||||
| @ -140,7 +111,7 @@ class ScalarWrapper extends Wrapper { | ||||
|         $type = $firstType; | ||||
|       } | ||||
|     } | ||||
|     $context->type = $this->type = $type; | ||||
|     $context->type = $type; | ||||
| 
 | ||||
|     if (!$type->isAvailable($input, $valueKey)) { | ||||
|       if ($default !== null) { | ||||
| @ -152,6 +123,16 @@ class ScalarWrapper extends Wrapper { | ||||
|     } | ||||
| 
 | ||||
|     $value = $input->get($valueKey); | ||||
|     $missing = $type->getMissingValue($haveMissing); | ||||
|     if ($haveMissing && $value === $missing) { | ||||
|       if ($default !== null) { | ||||
|         $input->set($default, $valueKey); | ||||
|         return $result->setNormalized(); | ||||
|       } else { | ||||
|         return $result->setMissing($schema); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     $context->origValue = $context->value = $value; | ||||
|     if ($type->isNull($value)) { | ||||
|       return $result->setNull($schema); | ||||
| @ -165,25 +146,27 @@ class ScalarWrapper extends Wrapper { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   protected function analyze(): int { | ||||
|     $schema = $this->schema; | ||||
|     $input = $this->input; | ||||
|     $valueKey = $this->valueKey; | ||||
|     $result = $this->result; | ||||
|     $result->reset(); | ||||
|     $context = new WrapperContext($schema, $this, $input, $valueKey, $result); | ||||
|   /** | ||||
|    * @param ScalarWrapper $wrapper | ||||
|    */ | ||||
|   static function _analyze(WrapperContext $context, Wrapper $wrapper, ?array $params): int { | ||||
|     /** @var ScalarSchema $schema */ | ||||
|     $schema = $context->schema; | ||||
|     $input = $context->input; | ||||
|     $valueKey = $context->valueKey; | ||||
|     $result = $context->result; | ||||
| 
 | ||||
|     /** @var func $analyzerFunc */ | ||||
|     $analyzerFunc = $schema->analyzerFunc; | ||||
|     if ($analyzerFunc !== null) $what = $analyzerFunc->invoke([$context]); | ||||
|     else $what = $this->analyze0($context); | ||||
|     if ($analyzerFunc !== null) $what = $analyzerFunc->invoke([$context, $wrapper]); | ||||
|     else $what = self::_analyze0($context); | ||||
|     if ($what !== ref_analyze::STRING) return $what; | ||||
| 
 | ||||
|     $value = $context->value; | ||||
|     try { | ||||
|       /** @var func $extractorFunc */ | ||||
|       $extractorFunc = $schema->extractorFunc; | ||||
|       if ($extractorFunc !== null) $extracted = $extractorFunc->invoke([$value, $context]); | ||||
|       if ($extractorFunc !== null) $extracted = $extractorFunc->invoke([$value, $context, $wrapper]); | ||||
|       else $extracted = $context->type->extract($value); | ||||
|       $context->value = $extracted; | ||||
|     } catch (ValueException $e) { | ||||
| @ -194,7 +177,7 @@ class ScalarWrapper extends Wrapper { | ||||
|     try { | ||||
|       /** @var func $parserFunc */ | ||||
|       $parserFunc = $schema->parserFunc; | ||||
|       if ($parserFunc !== null) $parsed = $parserFunc->invoke([$extracted, $context]); | ||||
|       if ($parserFunc !== null) $parsed = $parserFunc->invoke([$extracted, $context, $wrapper]); | ||||
|       else $parsed = $context->type->parse($extracted); | ||||
|       $context->value = $parsed; | ||||
|     } catch (ValueException $e) { | ||||
| @ -211,107 +194,52 @@ class ScalarWrapper extends Wrapper { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function verifix(?bool $throw=null): bool { | ||||
|     $result = $this->result; | ||||
|     $valueKey = $this->valueKey; | ||||
|     $verifix = false; | ||||
|   /** | ||||
|    * @param ScalarWrapper $wrapper | ||||
|    */ | ||||
|   static function _normalize(WrapperContext $context, Wrapper $wrapper, ?array $params): bool { | ||||
|     /** @var ScalarSchema $schema */ | ||||
|     $schema = $context->schema; | ||||
|     $input = $context->input; | ||||
|     $valueKey = $context->valueKey; | ||||
|     $result = $context->result; | ||||
| 
 | ||||
|     $normalize = false; | ||||
|     $modified = false; | ||||
|     if ($result->resultAvailable) { | ||||
|       if ($result->null) { | ||||
|         # forcer la valeur null, parce que la valeur actuelle est peut-être une
 | ||||
|         # valeur assimilée à null
 | ||||
|         $this->input->set(null, $valueKey); | ||||
|         $input->set(null, $valueKey); | ||||
|       } elseif ($result->valid && !$result->normalized) { | ||||
|         $normalizedValue = $result->normalizedValue; | ||||
|         if ($normalizedValue !== null) { | ||||
|           # la valeur normalisée est disponible
 | ||||
|           $this->input->set($normalizedValue); | ||||
|           $input->set($normalizedValue, $valueKey); | ||||
|           $result->normalizedValue = null; | ||||
|           $modified = true; | ||||
|         } else { | ||||
|           # normaliser la valeur
 | ||||
|           $verifix = true; | ||||
|           $normalize = true; | ||||
|         } | ||||
|       } | ||||
|     } else { | ||||
|       $verifix = true; | ||||
|       $normalize = true; | ||||
|     } | ||||
| 
 | ||||
|     if ($verifix) { | ||||
|       $value = $this->input->get($valueKey); | ||||
|       $schema = $this->schema; | ||||
|     if ($normalize) { | ||||
|       $value = $input->get($valueKey); | ||||
|       /** @var func $normalizerFunc */ | ||||
|       $normalizerFunc = $schema->normalizerFunc; | ||||
|       if ($normalizerFunc !== null) { | ||||
|         $context = new WrapperContext($schema, $this, $this->input, $valueKey, $result); | ||||
|         $orig = $value; | ||||
|         $value = $normalizerFunc->invoke([$orig, $context]); | ||||
|         $value = $normalizerFunc->invoke([$orig, $context, $wrapper]); | ||||
|         $modified = $value !== $orig; | ||||
|       } else { | ||||
|         $modified = $this->type->verifix($value, $result, $this->schema); | ||||
|         $modified = $context->type->normalize($value, $result, $schema); | ||||
|       } | ||||
|       if ($result->valid) $this->input->set($value, $valueKey); | ||||
|       if ($result->valid) $input->set($value, $valueKey); | ||||
|     } | ||||
|     if (!$result->valid) $result->throw($throw ?? $this->throw); | ||||
|     return $modified; | ||||
|   } | ||||
| 
 | ||||
|   function getResult(): ScalarResult { | ||||
|     return $this->result; | ||||
|   } | ||||
| 
 | ||||
|   function isPresent(): bool { | ||||
|     return $this->result->present; | ||||
|   } | ||||
| 
 | ||||
|   function getType(): IType { | ||||
|     return $this->type; | ||||
|   } | ||||
| 
 | ||||
|   function isAvailable(): bool { | ||||
|     return $this->result->available; | ||||
|   } | ||||
| 
 | ||||
|   function isValid(): bool { | ||||
|     return $this->result->valid; | ||||
|   } | ||||
| 
 | ||||
|   function isNormalized(): bool { | ||||
|     return $this->result->normalized; | ||||
|   } | ||||
| 
 | ||||
|   function get($default=null) { | ||||
|     if ($this->result->available) return $this->input->get($this->valueKey); | ||||
|     else return $default; | ||||
|   } | ||||
| 
 | ||||
|   function set($value, ?bool $verifix=null): ScalarWrapper { | ||||
|     $this->input->set($value, $this->valueKey); | ||||
|     $this->analyze(); | ||||
|     if ($verifix ?? $this->verifix) $this->verifix(); | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   function unset(?bool $verifix=null): ScalarWrapper { | ||||
|     $this->input->unset($this->valueKey); | ||||
|     $this->analyze(); | ||||
|     if ($verifix ?? $this->verifix) $this->verifix(); | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   function format($format=null): string { | ||||
|     $value = $this->input->get($this->valueKey); | ||||
|     /** @var func $formatterFunc */ | ||||
|     $formatterFunc = $this->schema->formatterFunc; | ||||
|     if ($formatterFunc !== null) { | ||||
|       # la fonction formatter n'a pas forcément accès au format de la définition
 | ||||
|       # le lui fournir ici
 | ||||
|       $format ??= $this->schema->format; | ||||
|       return $formatterFunc->invoke([$value, $format]); | ||||
|     } else { | ||||
|       # on assume que le type a été initialisé avec le format de la définition
 | ||||
|       # le cas échéant
 | ||||
|       return $this->type->format($value, $format); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,12 +1,11 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\input; | ||||
| namespace nulib\schema\input; | ||||
| 
 | ||||
| #XXX implémenter le renommage de paramètres et faire des méthodes pour
 | ||||
| # construire des querystring et paramètres de formulaires
 | ||||
| use nur\sery\wip\php\access\FormAccess; | ||||
| use nur\sery\wip\php\access\IAccess; | ||||
| use nur\sery\wip\php\access\KeyAccess; | ||||
| use nur\sery\wip\php\access\ShadowAccess; | ||||
| use nulib\php\access\FormAccess; | ||||
| use nulib\php\access\IAccess; | ||||
| use nulib\php\access\ShadowAccess; | ||||
| 
 | ||||
| /** | ||||
|  * Class FormInput: accès à des paramètres de formulaire (POST ou GET, dans cet | ||||
| @ -18,15 +17,14 @@ use nur\sery\wip\php\access\ShadowAccess; | ||||
| class FormInput extends Input { | ||||
|   const ALLOW_EMPTY = false; | ||||
| 
 | ||||
|   protected function formAccess($key): IAccess { | ||||
|     return new FormAccess($key, [ | ||||
|       "allow_empty" => $this->allowEmpty, | ||||
|     ]); | ||||
|   function __construct(&$dest=null, ?array $params=null) { | ||||
|     parent::__construct($dest, $params); | ||||
|     $this->access = new ShadowAccess($this->formAccess($this->access), $this->access); | ||||
|   } | ||||
| 
 | ||||
|   protected function access($key): IAccess { | ||||
|     return $this->keyAccess[$key] ??= new ShadowAccess($this->formAccess($key), new KeyAccess($this->value, $key, [ | ||||
|       "allow_empty" => $this->allowEmpty, | ||||
|     ])); | ||||
|   protected function formAccess(IAccess $access): IAccess { | ||||
|     return new FormAccess(null, [ | ||||
|       "allow_empty" => $access->isAllowEmpty(), | ||||
|     ]); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,8 +1,8 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\input; | ||||
| namespace nulib\schema\input; | ||||
| 
 | ||||
| use nur\sery\wip\php\access\GetAccess; | ||||
| use nur\sery\wip\php\access\IAccess; | ||||
| use nulib\php\access\GetAccess; | ||||
| use nulib\php\access\IAccess; | ||||
| 
 | ||||
| /** | ||||
|  * Class GetInput: accès à des paramètres de formulaire de type GET uniquement | ||||
| @ -11,9 +11,9 @@ use nur\sery\wip\php\access\IAccess; | ||||
|  * une référence | ||||
|  */ | ||||
| class GetInput extends FormInput { | ||||
|   protected function formAccess($key): IAccess { | ||||
|     return new GetAccess($key, [ | ||||
|       "allow_empty" => $this->allowEmpty, | ||||
|   protected function formAccess(IAccess $access): IAccess { | ||||
|     return new GetAccess(null, [ | ||||
|       "allow_empty" => $access->isAllowEmpty(), | ||||
|     ]); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,9 +1,11 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\input; | ||||
| namespace nulib\schema\input; | ||||
| 
 | ||||
| use nur\sery\wip\php\access\IAccess; | ||||
| use nur\sery\wip\php\access\KeyAccess; | ||||
| use nur\sery\wip\php\access\ValueAccess; | ||||
| use nulib\php\access\IAccess; | ||||
| use nulib\php\access\KeyAccess; | ||||
| use nulib\php\access\PropertyAccess; | ||||
| use nulib\ref\schema\ref_input; | ||||
| use nulib\StateException; | ||||
| 
 | ||||
| /** | ||||
|  * Class Input: accès à une valeur | ||||
| @ -13,54 +15,76 @@ use nur\sery\wip\php\access\ValueAccess; | ||||
| class Input { | ||||
|   const ALLOW_EMPTY = true; | ||||
| 
 | ||||
|   function __construct(&$value=null, ?array $params=null) { | ||||
|     $this->value =& $value; | ||||
|     $this->allowEmpty = $params["allow_empty"] ?? static::ALLOW_EMPTY; | ||||
|   private static function unexpected_access_type(): StateException { | ||||
|     return StateException::unexpected_state("access_type"); | ||||
|   } | ||||
| 
 | ||||
|   /** @var mixed */ | ||||
|   protected $value; | ||||
|   function __construct(&$dest=null, ?array $params=null) { | ||||
|     $accessType = $params["access_type"] ?? ref_input::ACCESS_AUTO; | ||||
|     if ($accessType === ref_input::ACCESS_AUTO) { | ||||
|       $accessType = is_object($dest)? ref_input::ACCESS_PROPERTY : ref_input::ACCESS_KEY; | ||||
|     } | ||||
| 
 | ||||
|   /** | ||||
|    * @var bool comment considérer une chaine vide: "" si allowEmpty, null sinon | ||||
|    */ | ||||
|   protected $allowEmpty; | ||||
| 
 | ||||
|   protected ?ValueAccess $valueAccess = null; | ||||
|   protected ?array $keyAccess = null; | ||||
| 
 | ||||
|   protected function access($key): IAccess { | ||||
|     if ($key === null) { | ||||
|       return $this->valueAccess ??= new ValueAccess($this->value, [ | ||||
|     $allowEmpty = $params["allow_empty"] ?? static::ALLOW_EMPTY; | ||||
|     if ($accessType == ref_input::ACCESS_PROPERTY) { | ||||
|       $this->access = new PropertyAccess($dest, null, [ | ||||
|         "allow_empty" => $allowEmpty, | ||||
|         "allow_null" => true, | ||||
|       ]); | ||||
|     } elseif ($accessType == ref_input::ACCESS_KEY) { | ||||
|       $this->access = new KeyAccess($dest, null, [ | ||||
|         "allow_empty" => $allowEmpty, | ||||
|         "allow_null" => true, | ||||
|         "allow_empty" => $this->allowEmpty, | ||||
|       ]); | ||||
|     } else { | ||||
|       return $this->keyAccess[$key] ??= new KeyAccess($this->value, $key, [ | ||||
|         "allow_empty" => $this->allowEmpty, | ||||
|       ]); | ||||
|       throw self::unexpected_access_type(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   protected IAccess $access; | ||||
| 
 | ||||
|   /** tester si la valeur existe sans tenir compte de $allowEmpty */ | ||||
|   function isPresent($key=null): bool { | ||||
|     return $this->access($key)->exists(); | ||||
|     return $this->access->resetKey($key)->exists(); | ||||
|   } | ||||
| 
 | ||||
|   /** tester si la valeur est disponible en tenant compte de $allowEmpty */ | ||||
|   function isAvailable($key=null): bool { | ||||
|     return $this->access($key)->available(); | ||||
|     return $this->access->resetKey($key)->available(); | ||||
|   } | ||||
| 
 | ||||
|   function get($key=null) { | ||||
|     return $this->access($key)->get(); | ||||
|     return $this->access->resetKey($key)->get(); | ||||
|   } | ||||
| 
 | ||||
|   function set($value, $key=null): void { | ||||
|     $this->access($key)->set($value); | ||||
|     $this->access->resetKey($key)->set($value); | ||||
|   } | ||||
| 
 | ||||
|   function unset($key=null): void { | ||||
|     $this->access($key)->del(); | ||||
|     $this->access->resetKey($key)->del(); | ||||
|   } | ||||
| 
 | ||||
|   function addKey($key): Input { | ||||
|     if ($key === null) return $this; | ||||
|     $input = clone $this; | ||||
|     $input->access = $this->access->addKey($key); | ||||
|     return $input; | ||||
|   } | ||||
| 
 | ||||
|   function ensureAssoc(array $keys, ?array $params=null): void { | ||||
|     $this->access->ensureAssoc($keys, $params); | ||||
|   } | ||||
| 
 | ||||
|   function ensureKeys(array $defaults, ?array $missings, ?array $params=null): void { | ||||
|     $this->access->ensureKeys($defaults, $missings, $params); | ||||
|   } | ||||
| 
 | ||||
|   function deleteMissings(array $missings, ?array $params=null): void { | ||||
|     $this->access->deleteMissings($missings, $params); | ||||
|   } | ||||
| 
 | ||||
|   function ensureOrder(array $keys, ?array $params=null): void { | ||||
|     $this->access->ensureOrder($keys, $params); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,8 +1,8 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\input; | ||||
| namespace nulib\schema\input; | ||||
| 
 | ||||
| use nur\sery\wip\php\access\IAccess; | ||||
| use nur\sery\wip\php\access\PostAccess; | ||||
| use nulib\php\access\IAccess; | ||||
| use nulib\php\access\PostAccess; | ||||
| 
 | ||||
| /** | ||||
|  * Class PostInput: accès à des paramètres de formulaire de type POST uniquement | ||||
| @ -11,9 +11,9 @@ use nur\sery\wip\php\access\PostAccess; | ||||
|  * une référence | ||||
|  */ | ||||
| class PostInput extends FormInput { | ||||
|   protected function formAccess($key): IAccess { | ||||
|     return new PostAccess($key, [ | ||||
|       "allow_empty" => $this->allowEmpty, | ||||
|   protected function formAccess(IAccess $access): IAccess { | ||||
|     return new PostAccess(null, [ | ||||
|       "allow_empty" => $access->isAllowEmpty(), | ||||
|     ]); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,22 +1,22 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema; | ||||
| namespace nulib\schema; | ||||
| 
 | ||||
| use nulib\schema\types\IType; | ||||
| use nulib\schema\types\Registry; | ||||
| use nulib\schema\types\tarray; | ||||
| use nulib\schema\types\tbool; | ||||
| use nulib\schema\types\tcontent; | ||||
| use nulib\schema\types\tfloat; | ||||
| use nulib\schema\types\tfunc; | ||||
| use nulib\schema\types\tint; | ||||
| use nulib\schema\types\tkey; | ||||
| use nulib\schema\types\tmixed; | ||||
| use nulib\schema\types\tpkey; | ||||
| use nulib\schema\types\traw; | ||||
| use nulib\schema\types\trawstring; | ||||
| use nulib\schema\types\tstring; | ||||
| use nulib\schema\types\ttext; | ||||
| use nulib\ValueException; | ||||
| use nur\sery\wip\schema\types\IType; | ||||
| use nur\sery\wip\schema\types\Registry; | ||||
| use nur\sery\wip\schema\types\tarray; | ||||
| use nur\sery\wip\schema\types\tbool; | ||||
| use nur\sery\wip\schema\types\tcallable; | ||||
| use nur\sery\wip\schema\types\tcontent; | ||||
| use nur\sery\wip\schema\types\tfloat; | ||||
| use nur\sery\wip\schema\types\tint; | ||||
| use nur\sery\wip\schema\types\tkey; | ||||
| use nur\sery\wip\schema\types\tmixed; | ||||
| use nur\sery\wip\schema\types\tpkey; | ||||
| use nur\sery\wip\schema\types\traw; | ||||
| use nur\sery\wip\schema\types\trawstring; | ||||
| use nur\sery\wip\schema\types\tstring; | ||||
| use nur\sery\wip\schema\types\ttext; | ||||
| 
 | ||||
| /** | ||||
|  * Class types: classe outil pour gérer le registre de types | ||||
| @ -47,7 +47,7 @@ class types { | ||||
|   static function int(bool $nullable=true): tint { return self::get($nullable, "int"); } | ||||
|   static function float(bool $nullable=true): tfloat { return self::get($nullable, "float"); } | ||||
|   static function array(bool $nullable=true): tarray { return self::get($nullable, "array"); } | ||||
|   static function callable(bool $nullable=true): tcallable { return self::get($nullable, "callable"); } | ||||
|   static function callable(bool $nullable=true): tfunc { return self::get($nullable, "callable"); } | ||||
|   static function raw(bool $nullable=true): traw { return self::get($nullable, "raw"); } | ||||
|   static function mixed(bool $nullable=true): tmixed { return self::get($nullable, "mixed"); } | ||||
|   static function key(bool $nullable=true): tkey { return self::get($nullable, "key"); } | ||||
|  | ||||
| @ -1,10 +1,10 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\types; | ||||
| namespace nulib\schema\types; | ||||
| 
 | ||||
| use nulib\schema\input\Input; | ||||
| use nulib\schema\Result; | ||||
| use nulib\schema\Schema; | ||||
| use nulib\ValueException; | ||||
| use nur\sery\wip\schema\input\Input; | ||||
| use nur\sery\wip\schema\Result; | ||||
| use nur\sery\wip\schema\Schema; | ||||
| 
 | ||||
| /** | ||||
|  * Interface IType: un type de données | ||||
| @ -23,7 +23,7 @@ interface IType { | ||||
| 
 | ||||
|   /** | ||||
|    * @return string la classe des objets gérés par ce format: le type attendu | ||||
|    * par {@link format()} et le type retourné par {@link verifix()} | ||||
|    * par {@link format()} et le type retourné par {@link normalize()} | ||||
|    * | ||||
|    * Les valeurs "mixed", "bool", "float", "int", "string" et "array" peuvent | ||||
|    * aussi être retournées, bien qu'elles ne soient pas à proprement parler des | ||||
| @ -48,26 +48,25 @@ interface IType { | ||||
|   function getPhpType(bool $allowNullable=true): ?string; | ||||
| 
 | ||||
|   /** | ||||
|    * indiquer si c'est le type d'une valeur qui ne peut prendre que 2 états: une | ||||
|    * "vraie" et une "fausse" | ||||
|    * obtenir la valeur "inexistante" pour les objets de ce type | ||||
|    * | ||||
|    * si $valid reçoit la valeur false, il faut ignorer la valeur de retour: | ||||
|    * cela veut dire qu'il n'y a pas de valeur "inexistant" pour les valeurs de | ||||
|    * ce type | ||||
|    */ | ||||
|   function is2States(): bool; | ||||
|   function getMissingValue(?bool &$valid=null); | ||||
| 
 | ||||
|   /** obtenir la valeur "nulle" pour les objets de ce type */ | ||||
|   function getNullValue(); | ||||
| 
 | ||||
|   /** | ||||
|    * Si {@link is2States()} est vrai, retourner les deux valeurs [faux, vrai] | ||||
|    * si c'est le type d'une valeur qui ne prendre qu'une liste prédéterminée | ||||
|    * d'états spécifiques, retourner le nombre d'états possibles, et mettre à | ||||
|    * jour $states avec les valeurs possibles | ||||
|    * | ||||
|    * sinon, retourner 0 et ne pas mettre $states à jour | ||||
|    */ | ||||
|   function get2States(): array; | ||||
| 
 | ||||
|   /** | ||||
|    * indiquer si c'est le type d'une valeur qui ne peut prendre que 3 états: une | ||||
|    * "vraie", une "fausse", et une "indéterminée" | ||||
|    */ | ||||
|   function is3States(): bool; | ||||
| 
 | ||||
|   /** | ||||
|    * Si {@link is3States()} est vrai, retourner les 3 valeurs [faux, vrai, undef] | ||||
|    */ | ||||
|   function get3States(): array; | ||||
|   function getNbStates(?array &$states=null): int; | ||||
| 
 | ||||
|   /** la donnée $input($valueKey) est-elle disponible? */ | ||||
|   function isAvailable(Input $input, $valueKey): bool; | ||||
| @ -98,15 +97,18 @@ interface IType { | ||||
|   function parse(string $value); | ||||
| 
 | ||||
|   /** | ||||
|    * analyser, corriger éventuellement et normaliser la valeur | ||||
|    * | ||||
|    * NB: si $value est un string. elle doit avoir déjà été traitée au préalable | ||||
|    * normaliser la valeur. elle *doit* déjà être valide. | ||||
|    * Si $value est un string. elle *doit* avoir déjà été traitée au préalable | ||||
|    * par extract() et parse() | ||||
|    * | ||||
|    * si la valeur était déjà normalisée, ou si une erreur s'est produite, | ||||
|    * retourner false. | ||||
|    * - si $result indique que la valeur est déjà normalisée, cette méthode ne | ||||
|    * fait rien | ||||
|    * - si la valeur était déjà normalisée, mettre à jour $result pour indiquer | ||||
|    * que la valeur est normalisée et retourner false | ||||
|    * - sinon, retourner true pour indiquer qu'il a fallut normaliser la valeur. | ||||
|    * $result n'est pas modifié | ||||
|    */ | ||||
|   function verifix(&$value, Result $result, Schema $schema): bool; | ||||
|   function normalize(&$value, Result $result, Schema $schema): bool; | ||||
| 
 | ||||
|   /** | ||||
|    * formatter la valeur pour affichage. si $value n'est pas null, elle est | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\types; | ||||
| namespace nulib\schema\types; | ||||
| 
 | ||||
| use nulib\cl; | ||||
| use nulib\php\func; | ||||
| @ -12,10 +12,9 @@ class Registry { | ||||
|     "text" => ttext::class, | ||||
|     "bool" => tbool::class, "boolean" => tbool::class, | ||||
|     "int" => tint::class, "integer" => tint::class, | ||||
|     "float" => tfloat::class, "flt" => tfloat::class, | ||||
|     "double" => tfloat::class, "dbl" => tfloat::class, | ||||
|     "float" => tfloat::class, "flt" => tfloat::class, "double" => tfloat::class, "dbl" => tfloat::class, | ||||
|     "array" => tarray::class, | ||||
|     "callable" => tcallable::class, | ||||
|     "func" => tfunc::class, "function" => tfunc::class, "callable" => tfunc::class, | ||||
|     # types spéciaux
 | ||||
|     "raw" => traw::class, | ||||
|     "mixed" => tmixed::class, | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\types; | ||||
| namespace nulib\schema\types; | ||||
| 
 | ||||
| abstract class _tformatable extends _tsimple { | ||||
|   const FORMAT = null; | ||||
|  | ||||
| @ -1,9 +1,8 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\types; | ||||
| namespace nulib\schema\types; | ||||
| 
 | ||||
| use nulib\StateException; | ||||
| use nulib\schema\input\Input; | ||||
| use nur\prop; | ||||
| use nur\sery\wip\schema\input\Input; | ||||
| use nur\str; | ||||
| 
 | ||||
| abstract class _tsimple implements IType { | ||||
| @ -43,20 +42,13 @@ abstract class _tsimple implements IType { | ||||
|     return $phpType; | ||||
|   } | ||||
| 
 | ||||
|   function is2States(): bool { | ||||
|   function getMissingValue(?bool &$valid=null) { | ||||
|     $valid = true; | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   function get2States(): array { | ||||
|     throw StateException::not_implemented(); | ||||
|   } | ||||
| 
 | ||||
|   function is3States(): bool { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   function get3States(): array { | ||||
|     throw StateException::not_implemented(); | ||||
|   function getNbStates(?array &$states=null): int { | ||||
|     return 0; | ||||
|   } | ||||
| 
 | ||||
|   function isAvailable(Input $input, $valueKey): bool { | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\types; | ||||
| namespace nulib\schema\types; | ||||
| 
 | ||||
| use nulib\str; | ||||
| 
 | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\types; | ||||
| namespace nulib\schema\types; | ||||
| 
 | ||||
| abstract class _tunion extends _tsimple { | ||||
|   function getPhpType(bool $allowNullable=true): ?string { | ||||
|  | ||||
| @ -1,12 +1,10 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\types; | ||||
| namespace nulib\schema\types; | ||||
| 
 | ||||
| use nulib\cl; | ||||
| use nulib\ValueException; | ||||
| use nur\sery\wip\schema\_scalar\ScalarResult; | ||||
| use nur\sery\wip\schema\_scalar\ScalarSchema; | ||||
| use nur\sery\wip\schema\Result; | ||||
| use nur\sery\wip\schema\Schema; | ||||
| use nulib\schema\_scalar\ScalarSchema; | ||||
| use nulib\schema\Result; | ||||
| use nulib\schema\Schema; | ||||
| 
 | ||||
| class tarray extends _tstring { | ||||
|   const NAME = "array"; | ||||
| @ -35,9 +33,13 @@ class tarray extends _tstring { | ||||
|     return "array"; | ||||
|   } | ||||
| 
 | ||||
|   function getNullValue() { | ||||
|     return $this->nullable? null: []; | ||||
|   } | ||||
| 
 | ||||
|   function isValid($value, ?bool &$normalized=null): bool { | ||||
|     $normalized = is_array($value); | ||||
|     return is_scalar($value) || is_array($value); | ||||
|     return $normalized || is_scalar($value); | ||||
|   } | ||||
| 
 | ||||
|   function parse(string $value) { | ||||
| @ -46,26 +48,16 @@ class tarray extends _tstring { | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * @var ScalarResult $result | ||||
|    * @var ScalarSchema $schema | ||||
|    */ | ||||
|   function verifix(&$value, Result $result, Schema $schema): bool { | ||||
|     if (is_array($value)) { | ||||
|   function normalize(&$value, Result $result, Schema $schema): bool { | ||||
|     if ($result->normalized) { | ||||
|     } elseif (is_array($value)) { | ||||
|       $result->setNormalized(); | ||||
|       return false; | ||||
|     } elseif (is_string($value)) { | ||||
|       try { | ||||
|         $value = $this->parse($value); | ||||
|         $result->setValid(); | ||||
|         return true; | ||||
|       } catch (ValueException $e) { | ||||
|       } | ||||
|     } elseif (is_scalar($value)) { | ||||
|       $value = cl::with($value); | ||||
|       $result->setValid(); | ||||
|       return true; | ||||
|     } | ||||
|     $result->setInvalid($value, $schema); | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -1,14 +1,13 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\types; | ||||
| namespace nulib\schema\types; | ||||
| 
 | ||||
| use nulib\cl; | ||||
| use nulib\schema\_scalar\ScalarSchema; | ||||
| use nulib\schema\input\Input; | ||||
| use nulib\schema\Result; | ||||
| use nulib\schema\Schema; | ||||
| use nulib\ValueException; | ||||
| use nur\prop; | ||||
| use nur\sery\wip\schema\_scalar\ScalarResult; | ||||
| use nur\sery\wip\schema\_scalar\ScalarSchema; | ||||
| use nur\sery\wip\schema\input\Input; | ||||
| use nur\sery\wip\schema\Result; | ||||
| use nur\sery\wip\schema\Schema; | ||||
| 
 | ||||
| class tbool extends _tformatable { | ||||
|   const NAME = "bool"; | ||||
| @ -60,20 +59,23 @@ class tbool extends _tformatable { | ||||
|     return "bool"; | ||||
|   } | ||||
| 
 | ||||
|   function is2States(): bool { | ||||
|     return !$this->nullable; | ||||
|   function getMissingValue(?bool &$valid=null) { | ||||
|     $valid = false; | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   function get2States(): array { | ||||
|     return [false, true]; | ||||
|   function getNullValue() { | ||||
|     return $this->nullable? null: false; | ||||
|   } | ||||
| 
 | ||||
|   function is3States(): bool { | ||||
|     return $this->nullable; | ||||
|   } | ||||
| 
 | ||||
|   function get3States(): array { | ||||
|     return [false, true, null]; | ||||
|   public function getNbStates(?array &$states=null): int { | ||||
|     if ($this->nullable) { | ||||
|       $states = [false, true, null]; | ||||
|       return 3; | ||||
|     } else { | ||||
|       $states = [false, true]; | ||||
|       return 2; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function isAvailable(Input $input, $valueKey): bool { | ||||
| @ -96,26 +98,16 @@ class tbool extends _tformatable { | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * @var ScalarResult $result | ||||
|    * @var ScalarSchema $schema | ||||
|    */ | ||||
|   function verifix(&$value, Result $result, Schema $schema): bool { | ||||
|     if (is_bool($value)) { | ||||
|   function normalize(&$value, Result $result, Schema $schema): bool { | ||||
|     if ($result->normalized) { | ||||
|     } elseif (is_bool($value)) { | ||||
|       $result->setNormalized(); | ||||
|       return false; | ||||
|     } elseif (is_string($value)) { | ||||
|       try { | ||||
|         $value = $this->parse($value); | ||||
|         $result->setValid(); | ||||
|         return true; | ||||
|       } catch (ValueException $e) { | ||||
|       } | ||||
|     } elseif (is_scalar($value)) { | ||||
|       $value = boolval($value); | ||||
|       $result->setValid(); | ||||
|       return true; | ||||
|     } | ||||
|     $result->setInvalid($value, $schema); | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -1,68 +0,0 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\types; | ||||
| 
 | ||||
| use Exception; | ||||
| use nulib\php\func; | ||||
| use nulib\ValueException; | ||||
| use nur\sery\wip\schema\_scalar\ScalarResult; | ||||
| use nur\sery\wip\schema\_scalar\ScalarSchema; | ||||
| use nur\sery\wip\schema\Result; | ||||
| use nur\sery\wip\schema\Schema; | ||||
| 
 | ||||
| class tcallable extends _tsimple { | ||||
|   const NAME = "callable"; | ||||
| 
 | ||||
|   const ALIASES = ["func", "function"]; | ||||
| 
 | ||||
|   static function ensure_callable(&$callable): void { | ||||
|     $callable = func::ensure($callable); | ||||
|   } | ||||
| 
 | ||||
|   static function ensure_ncallable(&$callable): void { | ||||
|     if ($callable !== null) self::ensure_callable($callable); | ||||
|   } | ||||
| 
 | ||||
|   function getClass(): string { | ||||
|     return func::class; | ||||
|   } | ||||
| 
 | ||||
|   function isValid($value, ?bool &$normalized=null): bool { | ||||
|     $normalized = is_callable($value); | ||||
|     return func::check($value); | ||||
|   } | ||||
| 
 | ||||
|   function parse(string $value) { | ||||
|     try { | ||||
|       return func::ensure($value); | ||||
|     } catch (Exception $e) { | ||||
|       throw new ValueException(null, null, 0, $e); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * @var ScalarResult $result | ||||
|    * @var ScalarSchema $schema | ||||
|    */ | ||||
|   function verifix(&$value, Result $result, Schema $schema): bool { | ||||
|     if ($value instanceof func) { | ||||
|       $result->setNormalized(); | ||||
|       return false; | ||||
|     } elseif (is_callable($value)) { | ||||
|       $value = func::with($value); | ||||
|       $result->setNormalized(); | ||||
|       return true; | ||||
|     } elseif (is_string($value)) { | ||||
|       try { | ||||
|         $value = $this->parse($value); | ||||
|         $result->setValid(); | ||||
|         return true; | ||||
|       } catch (ValueException $e) { | ||||
|       } | ||||
|     } | ||||
|     $result->setInvalid($value, $schema); | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   function format($value, $format=null): string { | ||||
|   } | ||||
| } | ||||
| @ -1,11 +1,10 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\types; | ||||
| namespace nulib\schema\types; | ||||
| 
 | ||||
| use nulib\php\content\c; | ||||
| use nur\sery\wip\schema\_scalar\ScalarResult; | ||||
| use nur\sery\wip\schema\_scalar\ScalarSchema; | ||||
| use nur\sery\wip\schema\Result; | ||||
| use nur\sery\wip\schema\Schema; | ||||
| use nulib\schema\_scalar\ScalarSchema; | ||||
| use nulib\schema\Result; | ||||
| use nulib\schema\Schema; | ||||
| 
 | ||||
| abstract class tcontent extends _tunion { | ||||
|   const NAME = "content"; | ||||
| @ -23,9 +22,13 @@ abstract class tcontent extends _tunion { | ||||
|     return "string|array"; | ||||
|   } | ||||
| 
 | ||||
|   function getNullValue() { | ||||
|     return $this->nullable? null: []; | ||||
|   } | ||||
| 
 | ||||
|   function isValid($value, ?bool &$normalized=null): bool { | ||||
|     $normalized = is_string($value) || is_array($value); | ||||
|     return is_scalar($value) || is_array($value); | ||||
|     return $normalized || is_scalar($value); | ||||
|   } | ||||
| 
 | ||||
|   function parse(string $value) { | ||||
| @ -33,21 +36,17 @@ abstract class tcontent extends _tunion { | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * @var ScalarResult $result | ||||
|    * @var ScalarSchema $schema | ||||
|    */ | ||||
|   function verifix(&$value, Result $result, Schema $schema): bool { | ||||
|     if (is_string($value) || is_array($value)) { | ||||
|   function normalize(&$value, Result $result, Schema $schema): bool { | ||||
|     if ($result->normalized) { | ||||
|     } elseif (is_string($value) || is_array($value)) { | ||||
|       $result->setNormalized(); | ||||
|       return false; | ||||
|     } elseif (is_scalar($value)) { | ||||
|       $value = strval($value); | ||||
|       $result->setValid(); | ||||
|       return true; | ||||
|     } else { | ||||
|       $result->setInvalid($value, $schema); | ||||
|       return false; | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   function format($value, $format=null): string { | ||||
|  | ||||
| @ -1,11 +1,10 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\types; | ||||
| namespace nulib\schema\types; | ||||
| 
 | ||||
| use nulib\schema\_scalar\ScalarSchema; | ||||
| use nulib\schema\Result; | ||||
| use nulib\schema\Schema; | ||||
| use nulib\ValueException; | ||||
| use nur\sery\wip\schema\_scalar\ScalarResult; | ||||
| use nur\sery\wip\schema\_scalar\ScalarSchema; | ||||
| use nur\sery\wip\schema\Result; | ||||
| use nur\sery\wip\schema\Schema; | ||||
| 
 | ||||
| class tfloat extends _tformatable { | ||||
|   const NAME = "float"; | ||||
| @ -24,6 +23,10 @@ class tfloat extends _tformatable { | ||||
|     return "float"; | ||||
|   } | ||||
| 
 | ||||
|   function getNullValue() { | ||||
|     return $this->nullable? null: 0.0; | ||||
|   } | ||||
| 
 | ||||
|   function isValid($value, ?bool &$normalized=null): bool { | ||||
|     $normalized = is_float($value); | ||||
|     return is_scalar($value); | ||||
| @ -40,26 +43,16 @@ class tfloat extends _tformatable { | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * @var ScalarResult $result | ||||
|    * @var ScalarSchema $schema | ||||
|    */ | ||||
|   function verifix(&$value, Result $result, Schema $schema): bool { | ||||
|     if (is_float($value)) { | ||||
|   function normalize(&$value, Result $result, Schema $schema): bool { | ||||
|     if ($result->normalized) { | ||||
|     } elseif (is_float($value)) { | ||||
|       $result->setNormalized(); | ||||
|       return false; | ||||
|     } elseif (is_string($value)) { | ||||
|       try { | ||||
|         $value = $this->parse($value); | ||||
|         $result->setValid(); | ||||
|         return true; | ||||
|       } catch (ValueException $e) { | ||||
|       } | ||||
|     } elseif (is_scalar($value)) { | ||||
|       $value = floatval($value); | ||||
|       $result->setValid(); | ||||
|       return true; | ||||
|     } | ||||
|     $result->setInvalid($value, $schema); | ||||
|     return false; | ||||
|   } | ||||
| } | ||||
|  | ||||
							
								
								
									
										61
									
								
								src/schema/types/tfunc.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/schema/types/tfunc.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,61 @@ | ||||
| <?php | ||||
| namespace nulib\schema\types; | ||||
| 
 | ||||
| use Exception; | ||||
| use nulib\php\func; | ||||
| use nulib\schema\_scalar\ScalarSchema; | ||||
| use nulib\schema\Result; | ||||
| use nulib\schema\Schema; | ||||
| use nulib\ValueException; | ||||
| 
 | ||||
| class tfunc extends _tsimple { | ||||
|   const NAME = "func"; | ||||
| 
 | ||||
|   const ALIASES = ["function", "callable"]; | ||||
| 
 | ||||
|   static function ensure_func(&$func): void { | ||||
|     $func = func::ensure($func); | ||||
|   } | ||||
| 
 | ||||
|   static function ensure_nfunc(&$func): void { | ||||
|     if ($func !== null) self::ensure_func($func); | ||||
|   } | ||||
| 
 | ||||
|   function getClass(): string { | ||||
|     return func::class; | ||||
|   } | ||||
| 
 | ||||
|   function getNullValue() { | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   function isValid($value, ?bool &$normalized=null): bool { | ||||
|     $normalized = $value instanceof func; | ||||
|     return func::check($value); | ||||
|   } | ||||
| 
 | ||||
|   function parse(string $value) { | ||||
|     try { | ||||
|       return func::ensure($value); | ||||
|     } catch (Exception $e) { | ||||
|       throw new ValueException(null, null, 0, $e); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * @var ScalarSchema $schema | ||||
|    */ | ||||
|   function normalize(&$value, Result $result, Schema $schema): bool { | ||||
|     if ($result->normalized) { | ||||
|     } elseif ($value instanceof func) { | ||||
|       $result->setNormalized(); | ||||
|     } elseif (is_callable($value)) { | ||||
|       $value = func::with($value); | ||||
|       return true; | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   function format($value, $format=null): string { | ||||
|   } | ||||
| } | ||||
| @ -1,12 +1,10 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\types; | ||||
| namespace nulib\schema\types; | ||||
| 
 | ||||
| use nulib\schema\_scalar\ScalarSchema; | ||||
| use nulib\schema\Result; | ||||
| use nulib\schema\Schema; | ||||
| use nulib\ValueException; | ||||
| use nur\sery\wip\schema\_scalar\ScalarResult; | ||||
| use nur\sery\wip\schema\_scalar\ScalarSchema; | ||||
| use nur\sery\wip\schema\input\Input; | ||||
| use nur\sery\wip\schema\Result; | ||||
| use nur\sery\wip\schema\Schema; | ||||
| 
 | ||||
| class tgeneric extends _tsimple { | ||||
|   function __construct(string $class, bool $nullable, ?array $params=null) { | ||||
| @ -20,8 +18,8 @@ class tgeneric extends _tsimple { | ||||
|     return $this->class; | ||||
|   } | ||||
| 
 | ||||
|   function isAvailable(Input $input, $valueKey): bool { | ||||
|     return $input->isAvailable($valueKey); | ||||
|   function getNullValue() { | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   public function isNull($value): bool { | ||||
| @ -29,8 +27,8 @@ class tgeneric extends _tsimple { | ||||
|   } | ||||
| 
 | ||||
|   function isValid($value, ?bool &$normalized=null): bool { | ||||
|     $normalized = true; | ||||
|     return $value instanceof $this->class; | ||||
|     $normalized = $value instanceof $this->class; | ||||
|     return $normalized; | ||||
|   } | ||||
| 
 | ||||
|   function parse(string $value) { | ||||
| @ -38,11 +36,10 @@ class tgeneric extends _tsimple { | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * @var ScalarResult $result | ||||
|    * @var ScalarSchema $schema | ||||
|    */ | ||||
|   function verifix(&$value, Result $result, Schema $schema): bool { | ||||
|     $result->setNormalized(); | ||||
|   function normalize(&$value, Result $result, Schema $schema): bool { | ||||
|     if (!$result->normalized) $result->setNormalized(); | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -1,11 +1,10 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\types; | ||||
| namespace nulib\schema\types; | ||||
| 
 | ||||
| use nulib\schema\_scalar\ScalarSchema; | ||||
| use nulib\schema\Result; | ||||
| use nulib\schema\Schema; | ||||
| use nulib\ValueException; | ||||
| use nur\sery\wip\schema\_scalar\ScalarResult; | ||||
| use nur\sery\wip\schema\_scalar\ScalarSchema; | ||||
| use nur\sery\wip\schema\Result; | ||||
| use nur\sery\wip\schema\Schema; | ||||
| 
 | ||||
| class tint extends _tformatable { | ||||
|   const NAME = "int"; | ||||
| @ -26,6 +25,10 @@ class tint extends _tformatable { | ||||
|     return "int"; | ||||
|   } | ||||
| 
 | ||||
|   function getNullValue() { | ||||
|     return $this->nullable? null: 0; | ||||
|   } | ||||
| 
 | ||||
|   function isValid($value, ?bool &$normalized=null): bool { | ||||
|     $normalized = is_int($value); | ||||
|     return is_scalar($value); | ||||
| @ -42,26 +45,16 @@ class tint extends _tformatable { | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * @var ScalarResult $result | ||||
|    * @var ScalarSchema $schema | ||||
|    */ | ||||
|   function verifix(&$value, Result $result, Schema $schema): bool { | ||||
|     if (is_int($value)) { | ||||
|   function normalize(&$value, Result $result, Schema $schema): bool { | ||||
|     if ($result->normalized) { | ||||
|     } elseif (is_int($value)) { | ||||
|       $result->setNormalized(); | ||||
|       return false; | ||||
|     } elseif (is_string($value)) { | ||||
|       try { | ||||
|         $value = $this->parse($value); | ||||
|         $result->setValid(); | ||||
|         return true; | ||||
|       } catch (ValueException $e) { | ||||
|       } | ||||
|     } elseif (is_scalar($value)) { | ||||
|       $value = intval($value); | ||||
|       $result->setValid(); | ||||
|       return true; | ||||
|     } | ||||
|     $result->setInvalid($value, $schema); | ||||
|     return false; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,10 +1,9 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\types; | ||||
| namespace nulib\schema\types; | ||||
| 
 | ||||
| use nur\sery\wip\schema\_scalar\ScalarResult; | ||||
| use nur\sery\wip\schema\_scalar\ScalarSchema; | ||||
| use nur\sery\wip\schema\Result; | ||||
| use nur\sery\wip\schema\Schema; | ||||
| use nulib\schema\_scalar\ScalarSchema; | ||||
| use nulib\schema\Result; | ||||
| use nulib\schema\Schema; | ||||
| 
 | ||||
| class tkey extends _tunion { | ||||
|   const NAME = "key"; | ||||
| @ -23,9 +22,13 @@ class tkey extends _tunion { | ||||
|     return "string|int"; | ||||
|   } | ||||
| 
 | ||||
|   function getNullValue() { | ||||
|     return $this->nullable? null: ""; | ||||
|   } | ||||
| 
 | ||||
|   function isValid($value, ?bool &$normalized=null): bool { | ||||
|     $normalized = is_string($value) || is_int($value); | ||||
|     return is_scalar($value); | ||||
|     return $normalized || is_scalar($value); | ||||
|   } | ||||
| 
 | ||||
|   function parse(string $value) { | ||||
| @ -33,21 +36,17 @@ class tkey extends _tunion { | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * @var ScalarResult $result | ||||
|    * @var ScalarSchema $schema | ||||
|    */ | ||||
|   function verifix(&$value, Result $result, Schema $schema): bool { | ||||
|     if (is_string($value) || is_int($value)) { | ||||
|   function normalize(&$value, Result $result, Schema $schema): bool { | ||||
|     if ($result->normalized) { | ||||
|     } elseif (is_string($value) || is_int($value)) { | ||||
|       $result->setNormalized(); | ||||
|       return false; | ||||
|     } elseif (is_scalar($value)) { | ||||
|       $value = strval($value); | ||||
|       $result->setValid(); | ||||
|       return true; | ||||
|     } else { | ||||
|       $result->setInvalid($value, $schema); | ||||
|       return false; | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   function format($value, $format=null): string { | ||||
|  | ||||
| @ -1,11 +1,10 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\types; | ||||
| namespace nulib\schema\types; | ||||
| 
 | ||||
| use nur\sery\wip\schema\_scalar\ScalarResult; | ||||
| use nur\sery\wip\schema\_scalar\ScalarSchema; | ||||
| use nur\sery\wip\schema\input\Input; | ||||
| use nur\sery\wip\schema\Result; | ||||
| use nur\sery\wip\schema\Schema; | ||||
| use nulib\schema\_scalar\ScalarSchema; | ||||
| use nulib\schema\input\Input; | ||||
| use nulib\schema\Result; | ||||
| use nulib\schema\Schema; | ||||
| 
 | ||||
| class tmixed extends _tsimple { | ||||
|   const NAME = "mixed"; | ||||
| @ -14,6 +13,15 @@ class tmixed extends _tsimple { | ||||
|     return "mixed"; | ||||
|   } | ||||
| 
 | ||||
|   function getMissingValue(?bool &$valid=null) { | ||||
|     $valid = false; | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   function getNullValue() { | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   function isAvailable(Input $input, $valueKey): bool { | ||||
|     return $input->isAvailable($valueKey); | ||||
|   } | ||||
| @ -32,11 +40,10 @@ class tmixed extends _tsimple { | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * @var ScalarResult $result | ||||
|    * @var ScalarSchema $schema | ||||
|    */ | ||||
|   function verifix(&$value, Result $result, Schema $schema): bool { | ||||
|     $result->setNormalized(); | ||||
|   function normalize(&$value, Result $result, Schema $schema): bool { | ||||
|     if (!$result->normalized) $result->setNormalized(); | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -1,10 +1,9 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\types; | ||||
| namespace nulib\schema\types; | ||||
| 
 | ||||
| use nur\sery\wip\schema\_scalar\ScalarResult; | ||||
| use nur\sery\wip\schema\_scalar\ScalarSchema; | ||||
| use nur\sery\wip\schema\Result; | ||||
| use nur\sery\wip\schema\Schema; | ||||
| use nulib\schema\_scalar\ScalarSchema; | ||||
| use nulib\schema\Result; | ||||
| use nulib\schema\Schema; | ||||
| 
 | ||||
| class tpkey extends _tunion { | ||||
|   const NAME = "pkey"; | ||||
| @ -28,9 +27,13 @@ class tpkey extends _tunion { | ||||
|     return "string|int|array"; | ||||
|   } | ||||
| 
 | ||||
|   function getNullValue() { | ||||
|     return $this->nullable? null: []; | ||||
|   } | ||||
| 
 | ||||
|   function isValid($value, ?bool &$normalized=null): bool { | ||||
|     $normalized = is_string($value) || is_int($value) || is_array($value); | ||||
|     return is_scalar($value) || is_array($value); | ||||
|     return $normalized || is_scalar($value); | ||||
|   } | ||||
| 
 | ||||
|   function parse(string $value) { | ||||
| @ -38,21 +41,17 @@ class tpkey extends _tunion { | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * @var ScalarResult $result | ||||
|    * @var ScalarSchema $schema | ||||
|    */ | ||||
|   function verifix(&$value, Result $result, Schema $schema): bool { | ||||
|     if (is_string($value) || is_int($value) || is_array($value)) { | ||||
|   function normalize(&$value, Result $result, Schema $schema): bool { | ||||
|     if ($result->normalized) { | ||||
|     } elseif (is_string($value) || is_int($value) || is_array($value)) { | ||||
|       $result->setNormalized(); | ||||
|       return false; | ||||
|     } elseif (is_scalar($value)) { | ||||
|       $value = strval($value); | ||||
|       $result->setValid(); | ||||
|       return true; | ||||
|     } else { | ||||
|       $result->setInvalid($value, $schema); | ||||
|       return false; | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   function format($value, $format=null): string { | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\types; | ||||
| namespace nulib\schema\types; | ||||
| 
 | ||||
| use nur\sery\wip\schema\input\Input; | ||||
| use nulib\schema\input\Input; | ||||
| 
 | ||||
| class traw extends tmixed { | ||||
|   const NAME = "raw"; | ||||
|  | ||||
| @ -1,11 +1,10 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\types; | ||||
| namespace nulib\schema\types; | ||||
| 
 | ||||
| use nulib\schema\_scalar\ScalarSchema; | ||||
| use nulib\schema\Result; | ||||
| use nulib\schema\Schema; | ||||
| use nulib\str; | ||||
| use nur\sery\wip\schema\_scalar\ScalarResult; | ||||
| use nur\sery\wip\schema\_scalar\ScalarSchema; | ||||
| use nur\sery\wip\schema\Result; | ||||
| use nur\sery\wip\schema\Schema; | ||||
| 
 | ||||
| class trawstring extends _tstring { | ||||
|   const NAME = "rawstring"; | ||||
| @ -24,6 +23,10 @@ class trawstring extends _tstring { | ||||
|     return "string"; | ||||
|   } | ||||
| 
 | ||||
|   function getNullValue() { | ||||
|     return $this->nullable? null: ""; | ||||
|   } | ||||
| 
 | ||||
|   function isNull($value): bool { | ||||
|     return $value === null; | ||||
|   } | ||||
| @ -41,21 +44,17 @@ class trawstring extends _tstring { | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * @var ScalarResult $result | ||||
|    * @var ScalarSchema $schema | ||||
|    */ | ||||
|   function verifix(&$value, Result $result, Schema $schema): bool { | ||||
|     if (is_string($value)) { | ||||
|   function normalize(&$value, Result $result, Schema $schema): bool { | ||||
|     if ($result->normalized) { | ||||
|     } elseif (is_string($value)) { | ||||
|       $result->setNormalized(); | ||||
|       return false; | ||||
|     } elseif (is_scalar($value)) { | ||||
|       $value = strval($value); | ||||
|       $result->setValid(); | ||||
|       return true; | ||||
|     } else { | ||||
|       $result->setInvalid($value, $schema); | ||||
|       return false; | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   function format($value, $format=null): string { | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\types; | ||||
| namespace nulib\schema\types; | ||||
| 
 | ||||
| class tstring extends trawstring { | ||||
|   const NAME = "string"; | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\types; | ||||
| namespace nulib\schema\types; | ||||
| 
 | ||||
| class ttext extends trawstring { | ||||
|   const NAME = "text"; | ||||
|  | ||||
							
								
								
									
										23
									
								
								src/tools/Json2yamlApp.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/tools/Json2yamlApp.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | ||||
| <?php | ||||
| namespace nulib\tools; | ||||
| 
 | ||||
| use nulib\app\cli\Application; | ||||
| use nulib\ext\json; | ||||
| use nulib\ext\yaml; | ||||
| use nulib\os\path; | ||||
| 
 | ||||
| class Json2yamlApp extends Application { | ||||
|   private $args; | ||||
| 
 | ||||
|   function main() { | ||||
|     $input = $this->args[0] ?? null; | ||||
|     if ($input === null || $input === "-") { | ||||
|       $output = null; | ||||
|     } else { | ||||
|       $output = path::ensure_ext($input, ".yml", ".json"); | ||||
|     } | ||||
| 
 | ||||
|     $data = json::load($input); | ||||
|     yaml::dump($data, $output); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										23
									
								
								src/tools/Yaml2jsonApp.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/tools/Yaml2jsonApp.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | ||||
| <?php | ||||
| namespace nulib\tools; | ||||
| 
 | ||||
| use nulib\app\cli\Application; | ||||
| use nulib\ext\json; | ||||
| use nulib\ext\yaml; | ||||
| use nulib\os\path; | ||||
| 
 | ||||
| class Yaml2jsonApp extends Application { | ||||
|   private $args; | ||||
| 
 | ||||
|   function main() { | ||||
|     $input = $this->args[0] ?? null; | ||||
|     if ($input === null || $input === "-") { | ||||
|       $output = null; | ||||
|     } else { | ||||
|       $output = path::ensure_ext($input, ".json", [".yml", ".yaml"]); | ||||
|     } | ||||
| 
 | ||||
|     $data = yaml::load($input); | ||||
|     json::dump($data, $output); | ||||
|   } | ||||
| } | ||||
| @ -1,5 +1,5 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\web\content; | ||||
| namespace nulib\web\content; | ||||
| 
 | ||||
| 
 | ||||
| use nulib\A; | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\web\content; | ||||
| namespace nulib\web\content; | ||||
| 
 | ||||
| /** | ||||
|  * Class v: classe outil pour gérer du contenu pour le web | ||||
|  | ||||
| @ -1,5 +0,0 @@ | ||||
| <?php | ||||
| namespace nulib; | ||||
| 
 | ||||
| class app extends \nur\sery\app { | ||||
| } | ||||
| @ -1,5 +0,0 @@ | ||||
| <?php | ||||
| namespace nulib\app\cli; | ||||
| 
 | ||||
| abstract class Application extends \nur\sery\app\cli\Application { | ||||
| } | ||||
							
								
								
									
										132
									
								
								tests/appTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								tests/appTest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,132 @@ | ||||
| <?php | ||||
| namespace nulib { | ||||
|   use nulib\tests\TestCase; | ||||
|   use nulib\impl\config; | ||||
|   use nulib\impl\myapp; | ||||
|   use nulib\impl\MyApplication1; | ||||
|   use nulib\impl\MyApplication2; | ||||
| 
 | ||||
|   class appTest extends TestCase { | ||||
|     function testWith() { | ||||
|       $projdir = config::get_projdir(); | ||||
|       $cwd = getcwd(); | ||||
| 
 | ||||
|       myapp::reset(); | ||||
|       $app1 = myapp::with(MyApplication1::class); | ||||
|       self::assertSame([ | ||||
|         "projdir" => $projdir, | ||||
|         "vendor" => [ | ||||
|           "bindir" => "$projdir/vendor/bin", | ||||
|           "autoload" => "$projdir/vendor/autoload.php", | ||||
|         ], | ||||
|         "appcode" => "nur-sery", | ||||
|         "cwd" => $cwd, | ||||
|         "datadir" => "$projdir/devel", | ||||
|         "etcdir" => "$projdir/devel/etc", | ||||
|         "vardir" => "$projdir/devel/var", | ||||
|         "logdir" => "$projdir/devel/log", | ||||
|         "profile" => "devel", | ||||
|         "appgroup" => null, | ||||
|         "name" => "my-application1", | ||||
|         "title" => null, | ||||
|       ], $app1->getParams()); | ||||
| 
 | ||||
|       $app2 = myapp::with(MyApplication2::class, $app1); | ||||
|       self::assertSame([ | ||||
|         "projdir" => $projdir, | ||||
|         "vendor" => [ | ||||
|           "bindir" => "$projdir/vendor/bin", | ||||
|           "autoload" => "$projdir/vendor/autoload.php", | ||||
|         ], | ||||
|         "appcode" => "nur-sery", | ||||
|         "cwd" => $cwd, | ||||
|         "datadir" => "$projdir/devel", | ||||
|         "etcdir" => "$projdir/devel/etc", | ||||
|         "vardir" => "$projdir/devel/var", | ||||
|         "logdir" => "$projdir/devel/log", | ||||
|         "profile" => "devel", | ||||
|         "appgroup" => null, | ||||
|         "name" => "my-application2", | ||||
|         "title" => null, | ||||
|       ], $app2->getParams()); | ||||
|     } | ||||
| 
 | ||||
|     function testInit() { | ||||
|       $projdir = config::get_projdir(); | ||||
|       $cwd = getcwd(); | ||||
| 
 | ||||
|       myapp::reset(); | ||||
|       myapp::init(MyApplication1::class); | ||||
|       self::assertSame([ | ||||
|         "projdir" => $projdir, | ||||
|         "vendor" => [ | ||||
|           "bindir" => "$projdir/vendor/bin", | ||||
|           "autoload" => "$projdir/vendor/autoload.php", | ||||
|         ], | ||||
|         "appcode" => "nur-sery", | ||||
|         "cwd" => $cwd, | ||||
|         "datadir" => "$projdir/devel", | ||||
|         "etcdir" => "$projdir/devel/etc", | ||||
|         "vardir" => "$projdir/devel/var", | ||||
|         "logdir" => "$projdir/devel/log", | ||||
|         "profile" => "devel", | ||||
|         "appgroup" => null, | ||||
|         "name" => "my-application1", | ||||
|         "title" => null, | ||||
|       ], myapp::get()->getParams()); | ||||
| 
 | ||||
|       myapp::init(MyApplication2::class); | ||||
|       self::assertSame([ | ||||
|         "projdir" => $projdir, | ||||
|         "vendor" => [ | ||||
|           "bindir" => "$projdir/vendor/bin", | ||||
|           "autoload" => "$projdir/vendor/autoload.php", | ||||
|         ], | ||||
|         "appcode" => "nur-sery", | ||||
|         "cwd" => $cwd, | ||||
|         "datadir" => "$projdir/devel", | ||||
|         "etcdir" => "$projdir/devel/etc", | ||||
|         "vardir" => "$projdir/devel/var", | ||||
|         "logdir" => "$projdir/devel/log", | ||||
|         "profile" => "devel", | ||||
|         "appgroup" => null, | ||||
|         "name" => "my-application2", | ||||
|         "title" => null, | ||||
|       ], myapp::get()->getParams()); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| namespace nulib\impl { | ||||
| 
 | ||||
|   use nulib\app\cli\Application; | ||||
|   use nulib\os\path; | ||||
|   use nulib\app; | ||||
| 
 | ||||
|   class config { | ||||
|     const PROJDIR = __DIR__.'/..'; | ||||
| 
 | ||||
|     static function get_projdir(): string { | ||||
|       return path::abspath(self::PROJDIR); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   class myapp extends app { | ||||
|     static function reset(): void { | ||||
|       self::$app = null; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   class MyApplication1 extends Application { | ||||
|     const PROJDIR = config::PROJDIR; | ||||
| 
 | ||||
|     function main() { | ||||
|     } | ||||
|   } | ||||
|   class MyApplication2 extends Application { | ||||
|     const PROJDIR = null; | ||||
| 
 | ||||
|     function main() { | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										215
									
								
								tests/php/access/KeyAccessTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								tests/php/access/KeyAccessTest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,215 @@ | ||||
| <?php | ||||
| namespace nulib\php\access; | ||||
| 
 | ||||
| use nulib\tests\TestCase; | ||||
| use stdClass; | ||||
| 
 | ||||
| class KeyAccessTest extends TestCase { | ||||
|   function testValueAccess() { | ||||
|     $default = new stdClass(); | ||||
| 
 | ||||
|     #
 | ||||
|     $i = null; | ||||
|     $a = new KeyAccess($i); | ||||
|     self::assertFalse($a->exists()); | ||||
|     self::assertFalse($a->available()); | ||||
|     self::assertSame($default, $a->get($default)); | ||||
| 
 | ||||
|     $i = false; | ||||
|     $a = new KeyAccess($i); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertTrue($a->available()); | ||||
|     self::assertSame(false, $a->get($default)); | ||||
| 
 | ||||
|     $i = ""; | ||||
|     $a = new KeyAccess($i); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertTrue($a->available()); | ||||
|     self::assertSame("", $a->get($default)); | ||||
| 
 | ||||
|     #
 | ||||
|     $i = null; | ||||
|     $a = new KeyAccess($i, null, ["allow_null" => false]); | ||||
|     self::assertFalse($a->exists()); | ||||
|     self::assertFalse($a->available()); | ||||
|     self::assertSame($default, $a->get($default)); | ||||
| 
 | ||||
|     $i = null; | ||||
|     $a = new KeyAccess($i, null, ["allow_null" => true]); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertTrue($a->available()); | ||||
|     self::assertSame(null, $a->get($default)); | ||||
| 
 | ||||
|     #
 | ||||
|     $i = false; | ||||
|     $a = new KeyAccess($i, null, ["allow_false" => false]); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertFalse($a->available()); | ||||
|     self::assertSame($default, $a->get($default)); | ||||
| 
 | ||||
|     $i = false; | ||||
|     $a = new KeyAccess($i, null, ["allow_false" => true]); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertTrue($a->available()); | ||||
|     self::assertSame(false, $a->get($default)); | ||||
| 
 | ||||
|     #
 | ||||
|     $i = ""; | ||||
|     $a = new KeyAccess($i, null, ["allow_empty" => false]); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertFalse($a->available()); | ||||
|     self::assertSame($default, $a->get($default)); | ||||
| 
 | ||||
|     $i = ""; | ||||
|     $a = new KeyAccess($i, null, ["allow_empty" => true]); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertTrue($a->available()); | ||||
|     self::assertSame("", $a->get($default)); | ||||
|   } | ||||
|    | ||||
|   function testArrayAccess() { | ||||
|     $default = new stdClass(); | ||||
|     $array = ["null" => null, "false" => false, "empty" => ""]; | ||||
| 
 | ||||
|     #
 | ||||
|     $a = new KeyAccess($array, "inexistant"); | ||||
|     self::assertFalse($a->exists()); | ||||
|     self::assertFalse($a->available()); | ||||
|     self::assertSame($default, $a->get($default)); | ||||
| 
 | ||||
|     $a = new KeyAccess($array, "null"); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertTrue($a->available()); | ||||
|     self::assertSame(null, $a->get($default)); | ||||
| 
 | ||||
|     $a = new KeyAccess($array, "false"); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertFalse($a->available()); | ||||
|     self::assertSame($default, $a->get($default)); | ||||
| 
 | ||||
|     $a = new KeyAccess($array, "empty"); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertTrue($a->available()); | ||||
|     self::assertSame("", $a->get($default)); | ||||
| 
 | ||||
|     #
 | ||||
|     $a = new KeyAccess($array, "null", ["allow_null" => false]); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertFalse($a->available()); | ||||
|     self::assertSame($default, $a->get($default)); | ||||
| 
 | ||||
|     $a = new KeyAccess($array, "null", ["allow_null" => true]); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertTrue($a->available()); | ||||
|     self::assertSame(null, $a->get($default)); | ||||
| 
 | ||||
|     #
 | ||||
|     $a = new KeyAccess($array, "false", ["allow_false" => false]); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertFalse($a->available()); | ||||
|     self::assertSame($default, $a->get($default)); | ||||
| 
 | ||||
|     $a = new KeyAccess($array, "false", ["allow_false" => true]); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertTrue($a->available()); | ||||
|     self::assertSame(false, $a->get($default)); | ||||
| 
 | ||||
|     #
 | ||||
|     $a = new KeyAccess($array, "empty", ["allow_empty" => false]); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertFalse($a->available()); | ||||
|     self::assertSame($default, $a->get($default)); | ||||
| 
 | ||||
|     $a = new KeyAccess($array, "empty", ["allow_empty" => true]); | ||||
|     self::assertTrue($a->exists()); | ||||
|     self::assertTrue($a->available()); | ||||
|     self::assertSame("", $a->get($default)); | ||||
|   } | ||||
| 
 | ||||
|   private function _ensureAssoc(?array $orig, ?array $expected, array $keys, ?array $params=null) { | ||||
|     $v = $orig; $a = new KeyAccess($v); | ||||
|     $a->ensureAssoc($keys, $params); | ||||
|     self::assertSame($expected, $v); | ||||
|   } | ||||
|   function testEnsureAssoc() { | ||||
|     $keys = ["a", "b", "c"]; | ||||
| 
 | ||||
|     $this->_ensureAssoc(null, null, $keys); | ||||
|     $this->_ensureAssoc([], [], $keys); | ||||
|     $this->_ensureAssoc([1], ["a" => 1], $keys); | ||||
|     $this->_ensureAssoc([1, 2, 3], ["a" => 1, "b" => 2, "c" => 3], $keys); | ||||
|     $this->_ensureAssoc([1, 2, 3, 4], [3 => 4, "a" => 1, "b" => 2, "c" => 3], $keys); | ||||
|     $this->_ensureAssoc(["c" => 3, 1], ["c" => 3, "a" => 1], $keys); | ||||
|     $this->_ensureAssoc(["c" => 3, "b" => 2, 1], ["c" => 3, "b" => 2, "a" => 1], $keys); | ||||
|     $this->_ensureAssoc(["c" => 3, "b" => 2, "a" => 1], ["c" => 3, "b" => 2, "a" => 1], $keys); | ||||
|     $this->_ensureAssoc(["a" => 1, 2], ["a" => 1, "b" => 2], $keys); | ||||
|     $this->_ensureAssoc([2, "a" => 1], ["a" => 1, "b" => 2], $keys); | ||||
| 
 | ||||
|     $keys = [0, "a", "b"]; | ||||
|     $this->_ensureAssoc([1], [1], $keys); | ||||
|     $this->_ensureAssoc([1, 2], [1, "a" => 2], $keys); | ||||
|   } | ||||
| 
 | ||||
|   private function _ensureKeys(?array $orig, ?array $expected, array $defaults, ?array $params=null) { | ||||
|     $v = $orig; $a = new KeyAccess($v); | ||||
|     $a->ensureKeys($defaults, $missings, $params); | ||||
|     self::assertSame($expected, $v); | ||||
|   } | ||||
|   function testEnsureKeys() { | ||||
|     $defaults = ["a" => false, "b" => false, "c" => false]; | ||||
| 
 | ||||
|     $this->_ensureKeys(null, ["a" => false, "b" => false, "c" => false], $defaults); | ||||
|     $this->_ensureKeys([], ["a" => false, "b" => false, "c" => false], $defaults); | ||||
|     $this->_ensureKeys(["a" => 1], ["a" => 1, "b" => false, "c" => false], $defaults); | ||||
|     $this->_ensureKeys(["a" => 1, "b" => 2, "c" => 3], ["a" => 1, "b" => 2, "c" => 3], $defaults); | ||||
|     $this->_ensureKeys(["x"], ["x", "a" => false, "b" => false, "c" => false], $defaults); | ||||
|     $this->_ensureKeys(["x", "a" => 1], ["x", "a" => 1, "b" => false, "c" => false], $defaults); | ||||
|     $this->_ensureKeys(["a" => 1, "x"], ["a" => 1, "x", "b" => false, "c" => false], $defaults); | ||||
|     $this->_ensureKeys(["a" => 1, "b" => 2, "c" => 3, "x"], ["a" => 1, "b" => 2, "c" => 3, "x"], $defaults); | ||||
|   } | ||||
| 
 | ||||
|   private function _ensureOrder(?array $orig, ?array $expected, array $keys, ?array $params=null) { | ||||
|     $v = $orig; $a = new KeyAccess($v); | ||||
|     $a->ensureOrder($keys, $params); | ||||
|     self::assertSame($expected, $v); | ||||
|   } | ||||
|   function testEnsureOrder() { | ||||
|     $keys = ["a", "b", "c"]; | ||||
| 
 | ||||
|     $this->_ensureOrder(null, null, $keys); | ||||
|     $this->_ensureOrder([], [], $keys); | ||||
|     $this->_ensureOrder([1], [1], $keys); | ||||
|     $this->_ensureOrder(["b" => 2, "a" => 1], ["a" => 1, "b" => 2], $keys); | ||||
|     $this->_ensureOrder(["c" => 3, "a" => 1], ["a" => 1, "c" => 3], $keys); | ||||
|   } | ||||
| 
 | ||||
|   private function _ensureAssocKeysOrder(?array $orig, ?array $expected, array $defaults, ?array $params=null) { | ||||
|     $v = $orig; $a = new KeyAccess($v); | ||||
|     $keys = array_keys($defaults); | ||||
|     $a->ensureAssoc($keys, $params); | ||||
|     $a->ensureKeys($defaults, $missings, $params); | ||||
|     $a->ensureOrder($keys, $params); | ||||
|     self::assertSame($expected, $v); | ||||
|   } | ||||
|   function testEnsureAssocKeysOrder() { | ||||
|     $defaults = ["a" => false, "b" => false, "c" => false]; | ||||
| 
 | ||||
|     $this->_ensureAssocKeysOrder(null, ["a" => false, "b" => false, "c" => false], $defaults); | ||||
|     $this->_ensureAssocKeysOrder([], ["a" => false, "b" => false, "c" => false], $defaults); | ||||
|     $this->_ensureAssocKeysOrder([1], ["a" => 1, "b" => false, "c" => false], $defaults); | ||||
|     $this->_ensureAssocKeysOrder([1, 2, 3], ["a" => 1, "b" => 2, "c" => 3], $defaults); | ||||
|     $this->_ensureAssocKeysOrder([1, 2, 3, 4], ["a" => 1, "b" => 2, "c" => 3, 4], $defaults); | ||||
|     $this->_ensureAssocKeysOrder([1, 2, 3, 4], ["a" => 1, "b" => 2, "c" => 3, 3 => 4], $defaults, [ | ||||
|       "preserve_keys" => true, | ||||
|     ]); | ||||
|     $this->_ensureAssocKeysOrder(["c" => 3, 1], ["a" => 1, "b" => false, "c" => 3], $defaults); | ||||
|     $this->_ensureAssocKeysOrder(["c" => 3, "b" => 2, 1], ["a" => 1, "b" => 2, "c" => 3], $defaults); | ||||
|     $this->_ensureAssocKeysOrder(["c" => 3, "b" => 2, "a" => 1], ["a" => 1, "b" => 2, "c" => 3], $defaults); | ||||
|     $this->_ensureAssocKeysOrder(["a" => 1, 2], ["a" => 1, "b" => 2, "c" => false], $defaults); | ||||
|     $this->_ensureAssocKeysOrder([2, "a" => 1], ["a" => 1, "b" => 2, "c" => false], $defaults); | ||||
| 
 | ||||
|     $this->_ensureAssocKeysOrder([1], ["x_a" => 1, "x_b" => false, "x_c" => false], $defaults, [ | ||||
|       "key_prefix" => "x_", | ||||
|     ]); | ||||
|   } | ||||
| } | ||||
| @ -1,5 +1,5 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\php\coll; | ||||
| namespace nulib\php\coll; | ||||
| 
 | ||||
| use Exception; | ||||
| use nulib\cl; | ||||
| @ -1,9 +1,9 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\php\content; | ||||
| namespace nulib\php\content; | ||||
| 
 | ||||
| use nulib\php\content\c; | ||||
| use nur\sery\wip\php\content\impl\html; | ||||
| use nur\sery\wip\web\content\v; | ||||
| use nulib\php\content\impl\html; | ||||
| use nulib\web\content\v; | ||||
| use PHPUnit\Framework\TestCase; | ||||
| 
 | ||||
| class cTest extends TestCase { | ||||
| @ -1,5 +1,5 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\php\content\impl; | ||||
| namespace nulib\php\content\impl; | ||||
| 
 | ||||
| use nulib\php\content\IContent; | ||||
| 
 | ||||
| @ -1,5 +1,5 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\php\content\impl; | ||||
| namespace nulib\php\content\impl; | ||||
| 
 | ||||
| use nulib\php\content\IPrintable; | ||||
| 
 | ||||
| @ -1,5 +1,5 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\php\content\impl; | ||||
| namespace nulib\php\content\impl; | ||||
| 
 | ||||
| use nulib\php\content\c; | ||||
| use nulib\php\content\IContent; | ||||
| @ -1,5 +1,5 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\php\content\impl; | ||||
| namespace nulib\php\content\impl; | ||||
| 
 | ||||
| class html { | ||||
|   const H1 = [self::class, "h1"]; | ||||
							
								
								
									
										396
									
								
								tests/schema/_assoc/AssocSchemaTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										396
									
								
								tests/schema/_assoc/AssocSchemaTest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,396 @@ | ||||
| <?php | ||||
| namespace nulib\schema\_assoc; | ||||
| 
 | ||||
| use nulib\ext\yaml; | ||||
| use nulib\tests\TestCase; | ||||
| use nulib\ValueException; | ||||
| use nulib\schema\_scalar\ScalarSchemaTest; | ||||
| use nulib\schema\Schema; | ||||
| use PHPStan\Type\Enum\EnumCaseObjectType; | ||||
| 
 | ||||
| class AssocSchemaTest extends TestCase { | ||||
|   const NULL_SCHEMA = [ | ||||
|     "" => [ | ||||
|       "assoc", | ||||
|       "compute_func" => null, | ||||
|       "validate_func" => null, | ||||
|       "ensure_array" => null, | ||||
|       "ensure_assoc" => null, | ||||
|       "ensure_keys" => null, | ||||
|       "ensure_order" => null, | ||||
|     ], | ||||
|     "schema" => null, | ||||
|     "type" => [null], | ||||
|     "default" => null, | ||||
|     "title" => null, | ||||
|     "required" => false, | ||||
|     "nullable" => true, | ||||
|     "desc" => null, | ||||
|     "analyzer_func" => null, | ||||
|     "extractor_func" => null, | ||||
|     "parser_func" => null, | ||||
|     "normalizer_func" => null, | ||||
|     "messages" => null, | ||||
|     "formatter_func" => null, | ||||
|     "format" => null, | ||||
|     "size" => null, | ||||
|     "precision" => null, | ||||
|     "name" => null, | ||||
|     "pkey" => null, | ||||
|     "header" => null, | ||||
|     "computed" => null, | ||||
|   ]; | ||||
| 
 | ||||
|   static function schema(array $definition, array $keyDefinitions): array { | ||||
|     $definition = array_merge(self::NULL_SCHEMA, $definition, ["schema" => []]); | ||||
|     foreach ($keyDefinitions as $key => $keydef) { | ||||
|       $definition["schema"][$key] = array_merge(ScalarSchemaTest::NULL_SCHEMA, $keydef); | ||||
|     }; unset($subdef); | ||||
|     return $definition; | ||||
|   } | ||||
| 
 | ||||
|   function testNormalize() { | ||||
|     self::assertSame(self::schema([ | ||||
|       "type" => ["array"], "nullable" => true, | ||||
|     ], [ | ||||
|       "s" => [ | ||||
|         "type" => ["string"], "nullable" => false, | ||||
|         "name" => "s", "pkey" => "s", "header" => "s", | ||||
|       ], | ||||
|     ]), AssocSchema::normalize_definition(["s" => "string"])); | ||||
| 
 | ||||
|     self::assertSame(self::schema([ | ||||
|       "type" => ["array"], "nullable" => true, | ||||
|     ], [ | ||||
|       "s" => [ | ||||
|         "type" => ["string"], "nullable" => false, | ||||
|         "name" => "s", "pkey" => "s", "header" => "s", | ||||
|       ], | ||||
|       "i" => [ | ||||
|         "type" => ["int"], "nullable" => false, | ||||
|         "name" => "i", "pkey" => "i", "header" => "i", | ||||
|       ], | ||||
|       "b" => [ | ||||
|         "type" => ["bool"], "nullable" => false, | ||||
|         "name" => "b", "pkey" => "b", "header" => "b", | ||||
|       ], | ||||
|     ]), AssocSchema::normalize_definition([ | ||||
|       "s" => "string", | ||||
|       "i" => "int", | ||||
|       "b" => "bool", | ||||
|     ])); | ||||
|   } | ||||
| 
 | ||||
|   function testConstructor() { | ||||
|     $schema = new AssocSchema([ | ||||
|       "s" => "string", | ||||
|       "i" => "int", | ||||
|       "b" => "bool", | ||||
|     ]); | ||||
|     self::assertSame(self::schema([ | ||||
|       "type" => ["array"], "nullable" => true, | ||||
|     ], [ | ||||
|       "s" => [ | ||||
|         "type" => ["string"], "nullable" => false, | ||||
|         "name" => "s", "pkey" => "s", "header" => "s", | ||||
|       ], | ||||
|       "i" => [ | ||||
|         "type" => ["int"], "nullable" => false, | ||||
|         "name" => "i", "pkey" => "i", "header" => "i", | ||||
|       ], | ||||
|       "b" => [ | ||||
|         "type" => ["bool"], "nullable" => false, | ||||
|         "name" => "b", "pkey" => "b", "header" => "b", | ||||
|       ], | ||||
|     ]), $schema->getDefinition()); | ||||
|     //yaml::dump($schema->getDefinition());
 | ||||
|   } | ||||
| 
 | ||||
|   function testWrapper() { | ||||
|     $schema = new AssocSchema([ | ||||
|       "s" => "?string", | ||||
|       "i" => "?int", | ||||
|       "b" => "?bool", | ||||
|     ]); | ||||
|     $array = ["s" => "  string  ", "i" => "  42 ", "b" => false]; | ||||
|     $schema->getWrapper($array); | ||||
|     self::assertSame([ | ||||
|       "s" => "string", | ||||
|       "i" => 42, | ||||
|       "b" => false, | ||||
|     ], $array); | ||||
| 
 | ||||
|     ###########################################################################
 | ||||
|     $schema = new AssocSchema([ | ||||
|       "s" => "string", | ||||
|       "i" => "int", | ||||
|       "b" => "bool", | ||||
|     ]); | ||||
| 
 | ||||
|     $array = ["s" => "  string  "]; | ||||
|     $schema->getWrapper($array); | ||||
|     self::assertSame([ | ||||
|       "s" => "string", | ||||
|       "i" => 0, | ||||
|       "b" => false, | ||||
|     ], $array); | ||||
| 
 | ||||
|     $array = ["b" => false, "s" => "  string  "]; | ||||
|     $schema->getWrapper($array); | ||||
|     self::assertSame([ | ||||
|       "s" => "string", | ||||
|       "i" => 0, | ||||
|       "b" => false, | ||||
|     ], $array); | ||||
| 
 | ||||
|     $array = ["s" => "  string  "]; | ||||
|     $schema->getWrapper($array, null, ["ensure_order" => false]); | ||||
|     self::assertSame([ | ||||
|       "s" => "string", | ||||
|       "i" => 0, | ||||
|       "b" => false, | ||||
|     ], $array); | ||||
| 
 | ||||
|     $array = ["b" => false, "s" => "  string  "]; | ||||
|     $schema->getWrapper($array, null, ["ensure_order" => false]); | ||||
|     self::assertSame([ | ||||
|       "b" => false, | ||||
|       "s" => "string", | ||||
|       "i" => 0, | ||||
|     ], $array); | ||||
| 
 | ||||
|     $array = ["s" => "  string  "]; | ||||
|     $schema->getWrapper($array, null, ["ensure_keys" => false]); | ||||
|     self::assertSame([ | ||||
|       "s" => "string", | ||||
|     ], $array); | ||||
| 
 | ||||
|     $array = ["b" => false, "s" => "  string  "]; | ||||
|     $schema->getWrapper($array, null, ["ensure_keys" => false]); | ||||
|     self::assertSame([ | ||||
|       "s" => "string", | ||||
|       "b" => false, | ||||
|     ], $array); | ||||
| 
 | ||||
|     // false équivaut à absent, sauf pour "b" qui est de type bool
 | ||||
|     $array = ["s" => false, "i" => false, "b" => false]; | ||||
|     $schema->getWrapper($array, null, ["ensure_keys" => true]); | ||||
|     self::assertSame([ | ||||
|       "s" => "", | ||||
|       "i" => 0, | ||||
|       "b" => false, | ||||
|     ], $array); | ||||
| 
 | ||||
|     $array = ["s" => false, "i" => false, "b" => false]; | ||||
|     $schema->getWrapper($array, null, ["ensure_keys" => false]); | ||||
|     self::assertSame([ | ||||
|       "b" => false, | ||||
|     ], $array); | ||||
|   } | ||||
| 
 | ||||
|   const STRING_SCHEMA = [ | ||||
|     "s" => "string", | ||||
|     "f" => "string", | ||||
|     "m" => "string", | ||||
|   ]; | ||||
| 
 | ||||
|   const NSTRING_SCHEMA = [ | ||||
|     "s" => "?string", | ||||
|     "f" => "?string", | ||||
|     "m" => "?string", | ||||
|   ]; | ||||
| 
 | ||||
|   const RSTRING_SCHEMA = [ | ||||
|     "s" => ["string", "required" => true], | ||||
|     "f" => ["string", "required" => true], | ||||
|     "m" => ["string", "required" => true], | ||||
|   ]; | ||||
| 
 | ||||
|   const RNSTRING_SCHEMA = [ | ||||
|     "s" => ["?string", "required" => true], | ||||
|     "f" => ["?string", "required" => true], | ||||
|     "m" => ["?string", "required" => true], | ||||
|   ]; | ||||
| 
 | ||||
|   const STRINGS = ["s" => "string", "f" => false]; | ||||
|   const NSTRINGS = ["s" => null, "f" => null]; | ||||
| 
 | ||||
|   function testString() { | ||||
|     /** @var AssocSchema $schema */ | ||||
|     $schema = Schema::ns(self::STRING_SCHEMA); | ||||
| 
 | ||||
|     $array = self::STRINGS; | ||||
|     $wrapper = $schema->getWrapper($array, null, ["throw" => false]); | ||||
|     self::assertSame(["s" => "string", "f" => "", "m" => ""], $array); | ||||
|     $result = $wrapper->getResult("s"); | ||||
|     self::assertTrue($result->normalized); | ||||
|     $result = $wrapper->getResult("f"); | ||||
|     self::assertTrue($result->present); | ||||
|     self::assertFalse($result->available); | ||||
|     $result = $wrapper->getResult("m"); | ||||
|     self::assertFalse($result->present); | ||||
| 
 | ||||
|     self::assertNotException(function() use ($schema) { | ||||
|       $array = self::STRINGS; | ||||
|       $schema->getWrapper($array); | ||||
|     }); | ||||
| 
 | ||||
|     $array = self::NSTRINGS; | ||||
|     $wrapper = $schema->getWrapper($array, null, ["throw" => false]); | ||||
|     self::assertSame(["s" => null, "f" => null, "m" => ""], $array); | ||||
|     $result = $wrapper->getResult("s"); | ||||
|     self::assertFalse($result->valid); | ||||
|     self::assertSame("null", $result->messageKey); | ||||
|     $result = $wrapper->getResult("f"); | ||||
|     self::assertFalse($result->valid); | ||||
|     self::assertSame("null", $result->messageKey); | ||||
|     $result = $wrapper->getResult("m"); | ||||
|     self::assertFalse($result->present); | ||||
| 
 | ||||
|     self::assertException(ValueException::class, function() use ($schema) { | ||||
|       $array = self::NSTRINGS; | ||||
|       $schema->getWrapper($array); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   function testNstring() { | ||||
|     /** @var AssocSchema $schema */ | ||||
|     $schema = Schema::ns(self::NSTRING_SCHEMA); | ||||
| 
 | ||||
|     $array = self::STRINGS; | ||||
|     $wrapper = $schema->getWrapper($array, null, ["throw" => false]); | ||||
|     self::assertSame(["s" => "string", "f" => null, "m" => null], $array); | ||||
|     $result = $wrapper->getResult("s"); | ||||
|     self::assertTrue($result->normalized); | ||||
|     $result = $wrapper->getResult("f"); | ||||
|     self::assertTrue($result->present); | ||||
|     self::assertFalse($result->available); | ||||
|     $result = $wrapper->getResult("m"); | ||||
|     self::assertFalse($result->present); | ||||
| 
 | ||||
|     self::assertNotException(function() use ($schema) { | ||||
|       $array = self::STRINGS; | ||||
|       $schema->getWrapper($array); | ||||
|     }); | ||||
| 
 | ||||
|     $array = self::NSTRINGS; | ||||
|     $wrapper = $schema->getWrapper($array, null, ["throw" => false]); | ||||
|     self::assertSame(["s" => null, "f" => null, "m" => null], $array); | ||||
|     $result = $wrapper->getResult("s"); | ||||
|     self::assertTrue($result->normalized); | ||||
|     $result = $wrapper->getResult("f"); | ||||
|     self::assertTrue($result->normalized); | ||||
|     $result = $wrapper->getResult("m"); | ||||
|     self::assertFalse($result->present); | ||||
| 
 | ||||
|     self::assertNotException(function() use ($schema) { | ||||
|       $array = self::NSTRINGS; | ||||
|       $schema->getWrapper($array); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   function testRstring() { | ||||
|     /** @var AssocSchema $schema */ | ||||
|     $schema = Schema::ns(self::RSTRING_SCHEMA); | ||||
| 
 | ||||
|     $array = self::STRINGS; | ||||
|     $wrapper = $schema->getWrapper($array, null, ["throw" => false]); | ||||
|     self::assertSame(["s" => "string", "f" => "", "m" => ""], $array); | ||||
|     $result = $wrapper->getResult("s"); | ||||
|     self::assertTrue($result->normalized); | ||||
|     $result = $wrapper->getResult("f"); | ||||
|     self::assertTrue($result->present); | ||||
|     self::assertFalse($result->available); | ||||
|     self::assertSame("unavailable", $result->messageKey); | ||||
|     $result = $wrapper->getResult("m"); | ||||
|     self::assertFalse($result->present); | ||||
|     self::assertSame("missing", $result->messageKey); | ||||
| 
 | ||||
|     self::assertException(ValueException::class, function() use ($schema) { | ||||
|       $array = self::STRINGS; | ||||
|       $schema->getWrapper($array); | ||||
|     }); | ||||
| 
 | ||||
|     $array = self::NSTRINGS; | ||||
|     $wrapper = $schema->getWrapper($array, null, ["throw" => false]); | ||||
|     self::assertSame(["s" => null, "f" => null, "m" => ""], $array); | ||||
|     $result = $wrapper->getResult("s"); | ||||
|     self::assertFalse($result->valid); | ||||
|     self::assertSame("null", $result->messageKey); | ||||
|     $result = $wrapper->getResult("f"); | ||||
|     self::assertFalse($result->valid); | ||||
|     self::assertSame("null", $result->messageKey); | ||||
|     $result = $wrapper->getResult("m"); | ||||
|     self::assertFalse($result->present); | ||||
|     self::assertSame("missing", $result->messageKey); | ||||
| 
 | ||||
|     self::assertException(ValueException::class, function() use ($schema) { | ||||
|       $array = self::NSTRINGS; | ||||
|       $schema->getWrapper($array); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   function testRnstring() { | ||||
|     /** @var AssocSchema $schema */ | ||||
|     $schema = Schema::ns(self::RNSTRING_SCHEMA); | ||||
| 
 | ||||
|     $array = self::STRINGS; | ||||
|     $wrapper = $schema->getWrapper($array, null, ["throw" => false]); | ||||
|     self::assertSame(["s" => "string", "f" => null, "m" => null], $array); | ||||
|     $result = $wrapper->getResult("s"); | ||||
|     self::assertTrue($result->normalized); | ||||
|     $result = $wrapper->getResult("f"); | ||||
|     self::assertTrue($result->present); | ||||
|     self::assertFalse($result->available); | ||||
|     self::assertSame("unavailable", $result->messageKey); | ||||
|     $result = $wrapper->getResult("m"); | ||||
|     self::assertFalse($result->present); | ||||
|     self::assertSame("missing", $result->messageKey); | ||||
| 
 | ||||
|     self::assertException(ValueException::class, function() use ($schema) { | ||||
|       $array = self::STRINGS; | ||||
|       $schema->getWrapper($array); | ||||
|     }); | ||||
| 
 | ||||
|     $array = self::NSTRINGS; | ||||
|     $wrapper = $schema->getWrapper($array, null, ["throw" => false]); | ||||
|     self::assertSame(["s" => null, "f" => null, "m" => null], $array); | ||||
|     $result = $wrapper->getResult("s"); | ||||
|     self::assertTrue($result->normalized); | ||||
|     $result = $wrapper->getResult("f"); | ||||
|     self::assertTrue($result->normalized); | ||||
|     $result = $wrapper->getResult("m"); | ||||
|     self::assertFalse($result->present); | ||||
|     self::assertSame("missing", $result->messageKey); | ||||
| 
 | ||||
|     self::assertException(ValueException::class, function() use ($schema) { | ||||
|       $array = self::NSTRINGS; | ||||
|       $schema->getWrapper($array); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   function testMessage() { | ||||
|     $schema = new AssocSchema([ | ||||
|       "rs" => ["string", "required" => true], | ||||
|       "i" => ["int"], | ||||
|     ]); | ||||
| 
 | ||||
|     $value = []; | ||||
|     $result = $schema->getWrapper($value, null, ["throw" => false])->getResult(null); | ||||
|     $expectedMessage = <<<EOT | ||||
| rs: vous devez spécifier cette valeur | ||||
| EOT; | ||||
|     self::assertSame($expectedMessage, $result->message); | ||||
| 
 | ||||
|     $value = [ | ||||
|       "rs" => null, | ||||
|       "i" => "abc", | ||||
|     ]; | ||||
|     $result = $schema->getWrapper($value, null, ["throw" => false])->getResult(null); | ||||
|     $expectedMessage = <<<EOT | ||||
| rs: cette valeur ne doit pas être nulle | ||||
| i: une valeur numérique entière est attendue | ||||
| EOT; | ||||
|     self::assertSame($expectedMessage, $result->message); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										70
									
								
								tests/schema/_scalar/ScalarSchemaTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								tests/schema/_scalar/ScalarSchemaTest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,70 @@ | ||||
| <?php | ||||
| namespace nulib\schema\_scalar; | ||||
| 
 | ||||
| use nulib\tests\TestCase; | ||||
| use nulib\schema\SchemaException; | ||||
| 
 | ||||
| class ScalarSchemaTest extends TestCase { | ||||
|   const NULL_SCHEMA = [ | ||||
|     "type" => [null], | ||||
|     "default" => null, | ||||
|     "title" => null, | ||||
|     "required" => false, | ||||
|     "nullable" => true, | ||||
|     "desc" => null, | ||||
|     "analyzer_func" => null, | ||||
|     "extractor_func" => null, | ||||
|     "parser_func" => null, | ||||
|     "normalizer_func" => null, | ||||
|     "messages" => null, | ||||
|     "formatter_func" => null, | ||||
|     "format" => null, | ||||
|     "size" => null, | ||||
|     "precision" => null, | ||||
|     "" => [ | ||||
|       "scalar", | ||||
|       "compute_func" => null, | ||||
|       "validate_func" => null, | ||||
|     ], | ||||
|     "schema" => null, | ||||
|     "name" => null, | ||||
|     "pkey" => null, | ||||
|     "header" => null, | ||||
|     "computed" => null, | ||||
|   ]; | ||||
| 
 | ||||
|   static function schema(array $schema): array { | ||||
|     return array_merge(self::NULL_SCHEMA, $schema); | ||||
|   } | ||||
| 
 | ||||
|   function testNormalize() { | ||||
|     self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize_definition(null)); | ||||
|     self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize_definition([])); | ||||
|     self::assertSame(self::NULL_SCHEMA, ScalarSchema::normalize_definition([null])); | ||||
|     self::assertException(SchemaException::class, function () { | ||||
|       ScalarSchema::normalize_definition([[]]); | ||||
|     }); | ||||
|     self::assertException(SchemaException::class, function () { | ||||
|       ScalarSchema::normalize_definition([[null]]); | ||||
|     }); | ||||
| 
 | ||||
|     $string = self::schema(["type" => ["string"], "nullable" => false]); | ||||
|     self::assertSame($string, ScalarSchema::normalize_definition("string")); | ||||
|     self::assertSame($string, ScalarSchema::normalize_definition(["string"])); | ||||
| 
 | ||||
|     $nstring = self::schema(["type" => ["string"]]); | ||||
|     self::assertSame($nstring, ScalarSchema::normalize_definition(["?string"])); | ||||
|     self::assertSame($nstring, ScalarSchema::normalize_definition(["?string|null"])); | ||||
|     self::assertSame($nstring, ScalarSchema::normalize_definition(["string|null"])); | ||||
|     self::assertSame($nstring, ScalarSchema::normalize_definition([["?string", "null"]])); | ||||
|     self::assertSame($nstring, ScalarSchema::normalize_definition([["string", "null"]])); | ||||
|     self::assertSame($nstring, ScalarSchema::normalize_definition([["string", null]])); | ||||
| 
 | ||||
|     $key = self::schema(["type" => ["string", "int"], "nullable" => false]); | ||||
|     self::assertSame($key, ScalarSchema::normalize_definition("string|int")); | ||||
| 
 | ||||
|     $nkey = self::schema(["type" => ["string", "int"], "nullable" => true]); | ||||
|     self::assertSame($nkey, ScalarSchema::normalize_definition("?string|int")); | ||||
|     self::assertSame($nkey, ScalarSchema::normalize_definition("string|?int")); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										296
									
								
								tests/schema/_scalar/ScalarWrapperTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										296
									
								
								tests/schema/_scalar/ScalarWrapperTest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,296 @@ | ||||
| <?php | ||||
| namespace nulib\schema\_scalar; | ||||
| 
 | ||||
| use nulib\tests\TestCase; | ||||
| use nulib\ValueException; | ||||
| use nulib\schema\input\Input; | ||||
| use stdClass; | ||||
| 
 | ||||
| class ScalarWrapperTest extends TestCase { | ||||
|   function checkValue(ScalarWrapper $wrapper, $value, bool $present, bool $available, bool $valid, bool $normalized): void { | ||||
|     self::assertSame($value, $wrapper->get(), "value"); | ||||
|     self::assertSame($present, $wrapper->isPresent(), "present"); | ||||
|     self::assertSame($available, $wrapper->isAvailable(), "available"); | ||||
|     self::assertSame($valid, $wrapper->isValid(), "valid"); | ||||
|     self::assertSame($normalized, $wrapper->isNormalized(), "normalized"); | ||||
|   } | ||||
| 
 | ||||
|   function checkNormalize(ScalarSchema $schema, $orig, bool $normalize, $value, bool $present, bool $available, bool $valid, bool $normalized, ?array $inputParams=null): void { | ||||
|     $wrapper = $schema->getWrapper(); | ||||
|     $wrapper->resetParams(["normalize" => $normalize]); | ||||
|     if ($inputParams !== null) $input = new Input($orig, $inputParams); | ||||
|     else $input = $orig; | ||||
|     $wrapper->reset($input); | ||||
|     $this->checkValue($wrapper, $value, $present, $available, $valid, $normalized); | ||||
|   } | ||||
| 
 | ||||
|   function checkException(ScalarSchema $schema, $orig, bool $normalize, string $exceptionClass, ?array $inputParams=null) { | ||||
|     $wrapper = $schema->getWrapper(); | ||||
|     if ($inputParams !== null) $orig = new Input($orig, $inputParams); | ||||
|     self::assertException($exceptionClass, function() use ($wrapper, &$orig, $normalize) { | ||||
|       $wrapper->resetParams(["normalize" => $normalize]); | ||||
|       $wrapper->reset($orig); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   function testRaw() { | ||||
|     $schema = new ScalarSchema(); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, false, false, false, true, true, true, true); | ||||
|     $this->checkNormalize($schema, false, true, false, true, true, true, true); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, null, false, null, true, true, true, true); | ||||
|     $this->checkNormalize($schema, null, true, null, true, true, true, true); | ||||
| 
 | ||||
|     $obj = new stdClass(); | ||||
|     $this->checkNormalize($schema, $obj, false, $obj, true, true, true, true); | ||||
|     $this->checkNormalize($schema, $obj, true, $obj, true, true, true, true); | ||||
| 
 | ||||
|     $schema = new ScalarSchema("raw"); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, false, false, false, true, true, true, true); | ||||
|     $this->checkNormalize($schema, false, true, false, true, true, true, true); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, null, false, null, true, true, true, true); | ||||
|     $this->checkNormalize($schema, null, true, null, true, true, true, true); | ||||
| 
 | ||||
|     $obj = new stdClass(); | ||||
|     $this->checkNormalize($schema, $obj, false, $obj, true, true, true, true); | ||||
|     $this->checkNormalize($schema, $obj, true, $obj, true, true, true, true); | ||||
|   } | ||||
| 
 | ||||
|   function testMixed() { | ||||
|     $schema = new ScalarSchema("mixed"); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, false, false, false, true, true, true, true); | ||||
|     $this->checkNormalize($schema, false, true, false, true, true, true, true); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, null, false, null, true, true, false, false); | ||||
|     $this->checkException($schema, null, true, ValueException::class); | ||||
| 
 | ||||
|     $obj = new stdClass(); | ||||
|     $this->checkNormalize($schema, $obj, false, $obj, true, true, true, true); | ||||
|     $this->checkNormalize($schema, $obj, true, $obj, true, true, true, true); | ||||
| 
 | ||||
|     $schema = new ScalarSchema("?mixed"); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, false, false, false, true, true, true, true); | ||||
|     $this->checkNormalize($schema, false, true, false, true, true, true, true); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, null, false, null, true, true, true, true); | ||||
|     $this->checkNormalize($schema, null, true, null, true, true, true, true); | ||||
| 
 | ||||
|     $obj = new stdClass(); | ||||
|     $this->checkNormalize($schema, $obj, false, $obj, true, true, true, true); | ||||
|     $this->checkNormalize($schema, $obj, true, $obj, true, true, true, true); | ||||
|   } | ||||
| 
 | ||||
|   function testRawstring() { | ||||
|     $schema = new ScalarSchema("rawstring"); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, false, false, null, true, false, true, true); | ||||
|     $this->checkNormalize($schema, false, true, null, true, false, true, true); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, null, false, null, true, true, false, false); | ||||
|     $this->checkException($schema, null, true, ValueException::class); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, "", false, "", true, true, true, true); | ||||
|     $this->checkNormalize($schema, "", true, "", true, true, true, true); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, "   ", false, "   ", true, true, true, true); | ||||
|     $this->checkNormalize($schema, "   ", true, "   ", true, true, true, true); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, "text", false, "text", true, true, true, true); | ||||
|     $this->checkNormalize($schema, "text", true, "text", true, true, true, true); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, "  text  ", false, "  text  ", true, true, true, true); | ||||
|     $this->checkNormalize($schema, "  text  ", true, "  text  ", true, true, true, true); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, true, false, true, true, true, true, false); | ||||
|     $this->checkNormalize($schema, true, true, "1", true, true, true, false); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, 42, false, 42, true, true, true, false); | ||||
|     $this->checkNormalize($schema, 42, true, "42", true, true, true, false); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, [], false, [], true, true, false, false); | ||||
|     $this->checkException($schema, [], true, ValueException::class); | ||||
| 
 | ||||
|     ## Tester valeur par défaut
 | ||||
|     $schema = new ScalarSchema(["rawstring", null]); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, false, false, null, true, false, true, true); | ||||
|     $this->checkNormalize($schema, false, true, null, true, false, true, true); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, null, false, null, true, true, false, false); | ||||
|     $this->checkException($schema, null, true, ValueException::class); | ||||
| 
 | ||||
|     $schema = new ScalarSchema(["rawstring", "default"]); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, false, false, "default", true, true, true, true); | ||||
|     $this->checkNormalize($schema, false, true, "default", true, true, true, true); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, null, false, null, true, true, false, false); | ||||
|     $this->checkException($schema, null, true, ValueException::class); | ||||
| 
 | ||||
|     ## Tester nullable
 | ||||
|     $schema = new ScalarSchema("?rawstring"); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, null, false, null, true, true, true, true); | ||||
|     $this->checkNormalize($schema, null, true, null, true, true, true, true); | ||||
| 
 | ||||
|     ## Tester required
 | ||||
|     $schema = new ScalarSchema(["rawstring", "required" => true]); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, false, false, null, true, false, false, false); | ||||
|     $this->checkException($schema, false, true, ValueException::class); | ||||
| 
 | ||||
|     ## Tester allow_empty === false
 | ||||
|     $inputParams = ["allow_empty" => false]; | ||||
|     $schema = new ScalarSchema("rawstring"); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, null, false, null, true, true, false, false, $inputParams); | ||||
|     $this->checkException($schema, null, true, ValueException::class, $inputParams); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, "", false, null, true, false, true, true, $inputParams); | ||||
|     $this->checkNormalize($schema, "", true, null, true, false, true, true, $inputParams); | ||||
| 
 | ||||
|     $schema = new ScalarSchema("?rawstring"); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, null, false, null, true, true, true, true, $inputParams); | ||||
|     $this->checkNormalize($schema, null, true, null, true, true, true, true, $inputParams); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, "", false, null, true, false, true, true, $inputParams); | ||||
|     $this->checkNormalize($schema, "", true, null, true, false, true, true, $inputParams); | ||||
|   } | ||||
| 
 | ||||
|   function testString() { | ||||
|     $schema = new ScalarSchema("string"); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, false, false, null, true, false, true, true); | ||||
|     $this->checkNormalize($schema, false, true, null, true, false, true, true); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, null, false, null, true, true, false, false); | ||||
|     $this->checkException($schema, null, true, ValueException::class); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, "", false, "", true, true, true, true); | ||||
|     $this->checkNormalize($schema, "", true, "", true, true, true, true); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, "   ", false, "", true, true, true, false); | ||||
|     $this->checkNormalize($schema, "   ", true, "", true, true, true, false); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, "text", false, "text", true, true, true, true); | ||||
|     $this->checkNormalize($schema, "text", true, "text", true, true, true, true); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, "  text  ", false, "text", true, true, true, false); | ||||
|     $this->checkNormalize($schema, "  text  ", true, "text", true, true, true, false); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, true, false, true, true, true, true, false); | ||||
|     $this->checkNormalize($schema, true, true, "1", true, true, true, false); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, 42, false, 42, true, true, true, false); | ||||
|     $this->checkNormalize($schema, 42, true, "42", true, true, true, false); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, [], false, [], true, true, false, false); | ||||
|     $this->checkException($schema, [], true, ValueException::class); | ||||
| 
 | ||||
|     ## Tester nullable
 | ||||
|     $schema = new ScalarSchema("?string"); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, null, false, null, true, true, true, true); | ||||
|     $this->checkNormalize($schema, null, true, null, true, true, true, true); | ||||
| 
 | ||||
|     ## Tester required
 | ||||
|     $schema = new ScalarSchema(["string", "required" => true]); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, false, false, null, true, false, false, false); | ||||
|     $this->checkException($schema, false, true, ValueException::class); | ||||
| 
 | ||||
|     ## Tester allow_empty === false
 | ||||
|     $inputParams = ["allow_empty" => false]; | ||||
|     $schema = new ScalarSchema("string"); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, null, false, null, true, true, false, false, $inputParams); | ||||
|     $this->checkException($schema, null, true, ValueException::class, $inputParams); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, "", false, null, true, false, true, true, $inputParams); | ||||
|     $this->checkNormalize($schema, "", true, null, true, false, true, true, $inputParams); | ||||
| 
 | ||||
|     $schema = new ScalarSchema("?string"); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, null, false, null, true, true, true, true, $inputParams); | ||||
|     $this->checkNormalize($schema, null, true, null, true, true, true, true, $inputParams); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, "", false, null, true, false, true, true, $inputParams); | ||||
|     $this->checkNormalize($schema, "", true, null, true, false, true, true, $inputParams); | ||||
|   } | ||||
| 
 | ||||
|   function testInt() { | ||||
|     $schema = new ScalarSchema("int"); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, false, false, null, true, false, true, true); | ||||
|     $this->checkNormalize($schema, false, true, null, true, false, true, true); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, null, false, null, true, true, false, false); | ||||
|     $this->checkException($schema, null, true, ValueException::class); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, 42, false, 42, true, true, true, true); | ||||
|     $this->checkNormalize($schema, 42, true, 42, true, true, true, true); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, "42", false, "42", true, true, true, false); | ||||
|     $this->checkNormalize($schema, "42", true, 42, true, true, true, false); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, "42.5", false, "42.5", true, true, true, false); | ||||
|     $this->checkNormalize($schema, "42.5", true, 42, true, true, true, false); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, "42,5", false, "42,5", true, true, true, false); | ||||
|     $this->checkNormalize($schema, "42,5", true, 42, true, true, true, false); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, "  42  ", false, "42", true, true, true, false); | ||||
|     $this->checkNormalize($schema, "  42  ", true, 42, true, true, true, false); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, "", false, "", true, true, false, false); | ||||
|     $this->checkException($schema, "", true, ValueException::class); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, "   ", false, "   ", true, true, false, false); | ||||
|     $this->checkException($schema, "   ", true, ValueException::class); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, "text", false, "text", true, true, false, false); | ||||
|     $this->checkException($schema, "text", true, ValueException::class); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, true, false, true, true, true, true, false); | ||||
|     $this->checkNormalize($schema, true, true, 1, true, true, true, false); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, [], false, [], true, true, false, false); | ||||
|     $this->checkException($schema, [], true, ValueException::class); | ||||
| 
 | ||||
|     ## Tester nullable
 | ||||
|     $schema = new ScalarSchema("?int"); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, null, false, null, true, true, true, true); | ||||
|     $this->checkNormalize($schema, null, true, null, true, true, true, true); | ||||
| 
 | ||||
|     ## Tester required
 | ||||
|     $schema = new ScalarSchema(["int", "required" => true]); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, false, false, null, true, false, false, false); | ||||
|     $this->checkException($schema, false, true, ValueException::class); | ||||
| 
 | ||||
|     ## Tester allow_empty === false
 | ||||
|     $inputParams = ["allow_empty" => false]; | ||||
|     $schema = new ScalarSchema("int"); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, null, false, null, true, true, false, false, $inputParams); | ||||
|     $this->checkException($schema, null, true, ValueException::class, $inputParams); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, "", false, null, true, false, true, true, $inputParams); | ||||
|     $this->checkNormalize($schema, "", true, null, true, false, true, true, $inputParams); | ||||
| 
 | ||||
|     $schema = new ScalarSchema("?int"); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, null, false, null, true, true, true, true, $inputParams); | ||||
|     $this->checkNormalize($schema, null, true, null, true, true, true, true, $inputParams); | ||||
| 
 | ||||
|     $this->checkNormalize($schema, "", false, null, true, false, true, true, $inputParams); | ||||
|     $this->checkNormalize($schema, "", true, null, true, false, true, true, $inputParams); | ||||
|   } | ||||
| } | ||||
| @ -1,10 +1,10 @@ | ||||
| <?php | ||||
| namespace nur\sery\wip\schema\types; | ||||
| namespace nulib\schema\types; | ||||
| 
 | ||||
| use Exception; | ||||
| use nulib\tests\TestCase; | ||||
| use nur\sery\wip\schema\_scalar\ScalarWrapper; | ||||
| use nur\sery\wip\schema\Schema; | ||||
| use nulib\schema\_scalar\ScalarWrapper; | ||||
| use nulib\schema\Schema; | ||||
| 
 | ||||
| class boolTest extends TestCase { | ||||
|   function commonTests($wrapper, &$value, callable $wrapperSetter): void { | ||||
| @ -59,7 +59,7 @@ class boolTest extends TestCase { | ||||
| 
 | ||||
|   function testBool() { | ||||
|     /** @var ScalarWrapper $wrapper */ | ||||
|     Schema::nw($value, null, $schema, "bool", $wrapper); | ||||
|     Schema::nw($value, null, "bool", $schema, $wrapper); | ||||
|     $wrapperSetter = function($value) use($wrapper) { | ||||
|       return function() use($wrapper, $value) { | ||||
|         $wrapper->set($value); | ||||
| @ -75,7 +75,7 @@ class boolTest extends TestCase { | ||||
| 
 | ||||
|   function testNbool() { | ||||
|     /** @var ScalarWrapper $wrapper */ | ||||
|     Schema::nw($value, null, $schema, "?bool", $wrapper); | ||||
|     Schema::nw($value, null, "?bool", $schema, $wrapper); | ||||
|     $wrapperSetter = function($value) use($wrapper) { | ||||
|       return function() use($wrapper, $value) { | ||||
|         $wrapper->set($value); | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user