184 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			184 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| namespace nur\sery\wip\php\access;
 | |
| 
 | |
| use nulib\StateException;
 | |
| use nulib\str;
 | |
| use ReflectionClass;
 | |
| use ReflectionException;
 | |
| use ReflectionNamedType;
 | |
| use ReflectionProperty;
 | |
| use stdClass;
 | |
| 
 | |
| 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 ensureAssoc(array $keys, ?array $params=null): void {
 | |
|     # NOP
 | |
|   }
 | |
| 
 | |
|   function ensureKeys(array $defaults, ?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];
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   function ensureOrder(array $keys, ?array $params=null): void {
 | |
|     # NOP
 | |
|   }
 | |
| }
 |