Does PHP have an answer to Java style class generics?

前端 未结 8 1500
天命终不由人
天命终不由人 2020-12-24 01:09

Upon building an MVC framework in PHP I ran into a problem which could be solved easily using Java style generics. An abstract Controller class might look something like thi

8条回答
  •  暖寄归人
    2020-12-24 02:11

    To provide a high level of static code-analysis, strict typing and usability, i came up with this solution: https://gist.github.com/rickhub/aa6cb712990041480b11d5624a60b53b

    /**
     * Class GenericCollection
     */
    class GenericCollection implements \IteratorAggregate, \ArrayAccess{
        /**
         * @var string
         */
        private $type;
    
        /**
         * @var array
         */
        private $items = [];
    
        /**
         * GenericCollection constructor.
         *
         * @param string $type
         */
        public function __construct(string $type){
            $this->type = $type;
        }
    
        /**
         * @param $item
         *
         * @return bool
         */
        protected function checkType($item): bool{
            $type = $this->getType();
            return $item instanceof $type;
        }
    
        /**
         * @return string
         */
        public function getType(): string{
            return $this->type;
        }
    
        /**
         * @param string $type
         *
         * @return bool
         */
        public function isType(string $type): bool{
            return $this->type === $type;
        }
    
        #region IteratorAggregate
    
        /**
         * @return \Traversable|$type
         */
        public function getIterator(): \Traversable{
            return new \ArrayIterator($this->items);
        }
    
        #endregion
    
        #region ArrayAccess
    
        /**
         * @param mixed $offset
         *
         * @return bool
         */
        public function offsetExists($offset){
            return isset($this->items[$offset]);
        }
    
        /**
         * @param mixed $offset
         *
         * @return mixed|null
         */
        public function offsetGet($offset){
            return isset($this->items[$offset]) ? $this->items[$offset] : null;
        }
    
        /**
         * @param mixed $offset
         * @param mixed $item
         */
        public function offsetSet($offset, $item){
            if(!$this->checkType($item)){
                throw new \InvalidArgumentException('invalid type');
            }
            $offset !== null ? $this->items[$offset] = $item : $this->items[] = $item;
        }
    
        /**
         * @param mixed $offset
         */
        public function offsetUnset($offset){
            unset($this->items[$offset]);
        }
    
        #endregion
    }
    
    
    /**
     * Class Item
     */
    class Item{
        /**
         * @var int
         */
        public $id = null;
    
        /**
         * @var string
         */
        public $data = null;
    
        /**
         * Item constructor.
         *
         * @param int    $id
         * @param string $data
         */
        public function __construct(int $id, string $data){
            $this->id = $id;
            $this->data = $data;
        }
    }
    
    
    /**
     * Class ItemCollection
     */
    class ItemCollection extends GenericCollection{
        /**
         * ItemCollection constructor.
         */
        public function __construct(){
            parent::__construct(Item::class);
        }
    
        /**
         * @return \Traversable|Item[]
         */
        public function getIterator(): \Traversable{
            return parent::getIterator();
        }
    }
    
    
    /**
     * Class ExampleService
     */
    class ExampleService{
        /**
         * @var ItemCollection
         */
        private $items = null;
    
        /**
         * SomeService constructor.
         *
         * @param ItemCollection $items
         */
        public function __construct(ItemCollection $items){
            $this->items = $items;
        }
    
        /**
         * @return void
         */
        public function list(){
            foreach($this->items as $item){
                echo $item->data;
            }
        }
    }
    
    
    /**
     * Usage
     */
    $collection = new ItemCollection;
    $collection[] = new Item(1, 'foo');
    $collection[] = new Item(2, 'bar');
    $collection[] = new Item(3, 'foobar');
    
    $collection[] = 42; // InvalidArgumentException: invalid type
    
    $service = new ExampleService($collection);
    $service->list();
    

    Even if something like this would feel so much better:

    class ExampleService{
        public function __construct(Collection $items){
            // ..
        }
    }
    

    Hope generics will get into PHP soon.

提交回复
热议问题