Yii2 프레임워크 원본 연구 2 - Component

14722 단어
Component는 Object에서 계승된 것이기 때문에 그는 속성이라는 특성을 가지고 있다. 이를 바탕으로 구성 요소는 두 가지 기능이 강한 특성을 제공했다. 그것이 바로 이벤트와 행위이다.즉, 하나의 클래스가Component류를 계승한다면 그는 이러한 특성을 가지고 이 클래스의 대상에게 사건과 행위를 귀속시킬 수 있다.
사건의 작용은 특정한 장소에서 특정한 코드를 집행하는 것이다.일반적으로 이벤트에는 다음과 같은 요소가 포함됩니다.
  • 이게 무슨 사건이야
  • 누가 사건을 촉발했는지
  • 누가 사건을 처리합니까
  • 이 사건을 어떻게 처리합니까
  • 이벤트 처리 관련 데이터는 무엇입니까
  • 행위의 작용은 특정한 대상이 특정한 방법과 속성을 가지게 하는 것이다. 이런 방법과 속성은 하나의 행위에 봉인되어 있다. 이 행위가 특정한 유형에 의존할 때 이 유형은 이 행위가 제공하는 속성과 방법을 가지게 된다.
    구성 요소가 어떻게 이 두 가지 특성을 실현하는지 이해하기 위해서는 먼저Component의 원본 코드를 보아야 한다
    class Component extends Object
    {
        private $_events = [];
        private $_behaviors;
        public function __get($name)
        public function __set($name, $value)
        public function __isset($name)
        public function __unset($name)
        public function __call($name, $params)
        public function __clone()
        {
            $this->_events = [];
            $this->_behaviors = null;
        }
        public function hasProperty($name, $checkVars = true, $checkBehaviors = true)
        public function canGetProperty($name, $checkVars = true, $checkBehaviors = true)
        public function canSetProperty($name, $checkVars = true, $checkBehaviors = true)
        public function hasMethod($name, $checkBehaviors = true)
        public function behaviors()
        {
            return [];
        }
        public function hasEventHandlers($name)
        public function on($name, $handler, $data = null, $append = true)
        public function off($name, $handler = null)
        public function trigger($name, Event $event = null)
        public function getBehavior($name)
        public function getBehaviors()
        public function attachBehavior($name, $behavior)
        public function attachBehaviors($behaviors)
        public function detachBehavior($name)
        public function detachBehaviors()
        public function ensureBehaviors()
        private function attachBehaviorInternal($name, $behavior)
    }
    
    

    어떻게 보니Component가 Object 클래스의 방법을 모두 다시 쓴 것을 발견했다. 그래.그럼 속성이라는 특성을 먼저 봅시다.

    등록 정보


    Component류는 구조 방법이 없기 때문에 초기화 과정은 Object류와 같고 속성에 대한 조작도 마술 방법으로 포지셔닝된다set () 또는get () 안에 하나씩 보기:
        public function __set($name, $value)
        {
            $setter = 'set' . $name;
            if (method_exists($this, $setter)) {
                $this->$setter($value);
                return;
            } elseif (strncmp($name, 'on ', 3) === 0) {
                $this->on(trim(substr($name, 3)), $value);
                return;
            } elseif (strncmp($name, 'as ', 3) === 0) {
                $name = trim(substr($name, 3));
                $this->attachBehavior($name, $value instanceof Behavior ? $value : Yii::createObject($value));
                return;
            } else {
                $this->ensureBehaviors();
                foreach ($this->_behaviors as $behavior) {
                    if ($behavior->canSetProperty($name)) {
                        $behavior->$name = $value;
                        return;
                    }
                }
            }
            if (method_exists($this, 'get' . $name)) {
                throw 
            } else {
                throw 
            }
        }
    

    component의 설정 그룹에서 이벤트와 행위를 설정할 수 있기 때문에, on+빈칸은 이벤트를 표시하고, as+빈칸은 행위를 표시합니다.행위의 속성도 구성 요소의 속성이기 때문에 행위에서 상응하는 속성을 찾을 수 있다.
        public function __get($name)
        {
            $getter = 'get' . $name;
            if (method_exists($this, $getter)) {
                // read property, e.g. getName()
                return $this->$getter();
            } else {
                // behavior property
                $this->ensureBehaviors();
                foreach ($this->_behaviors as $behavior) {
                    if ($behavior->canGetProperty($name)) {
                        return $behavior->$name;
                    }
                }
            }
            if (method_exists($this, 'set' . $name)) {
                throw
            } else {
                throw 
            }
        }
    

    __get () 함수에서 행동에서 해당하는 속성을 찾을 수 있습니다.

    이벤트


    처음에 사건의 기본 개념을 말했는데, 이제 구체적으로 어떤 방법이 있는지 살펴보자.먼저 component 클래스에서 모든 이벤트를 저장하는 그룹을 정의합니다.
        private $_events = [];//name => handlers
    

    여기의handlers는 하나의 이벤트에 많은 이벤트 처리 함수가 있을 수 있기 때문에 하나의 그룹입니다.그룹의 모든 항목은 [$handler, $data]의 구조입니다. 그 중에서 $handler의 구조는 다음과 같습니다.
          function ($event) { ... }         // anonymous function
          [$object, 'handleClick']          // $object->handleClick()
          ['Page', 'handleClick']           // Page::handleClick()
          'handleClick'                     // global function handleClick()
    

    왜 이렇게 네 가지죠?뒤에 Trigger 함수에서call 을 호출한 것을 보실 수 있습니다user_func 함수, 이 함수는 이 네 가지 방식으로 방법을 실행할 수 있습니다.

    이벤트 바인딩 및 해제


    귀속 이벤트가 진행하는 동작은 이벤트의 이름과 이벤트 처리 함수를 대응시키고 이 대응 관계를 이벤트 그룹에 넣는 것입니다.방법은 다음과 같습니다.
        public function on($name, $handler, $data = null, $append = true)
        {
            $this->ensureBehaviors();
            if ($append || empty($this->_events[$name])) {
                $this->_events[$name][] = [$handler, $data];
            } else {
                array_unshift($this->_events[$name], [$handler, $data]);
            }
        }
    

    상응하는 이벤트의 해제는 이벤트와 처리 함수의 관계를 이벤트 그룹에서 제거하는 것이다.$handler가 비어 있으면 이 이벤트의 모든 시간 처리 함수가 삭제됩니다. 해당 함수는 다음과 같습니다.
        public function off($name, $handler = null)
        {
            $this->ensureBehaviors();
            if (empty($this->_events[$name])) {
                return false;
            }
            if ($handler === null) {
                unset($this->_events[$name]);
                return true;
            } else {
                $removed = false;
                foreach ($this->_events[$name] as $i => $event) {
                    if ($event[0] === $handler) {
                        unset($this->_events[$name][$i]);
                        $removed = true;
                    }
                }
                if ($removed) {
                    // unset ,key , key 
                    $this->_events[$name] = array_values($this->_events[$name]);
                }
                return $removed;
            }
        }
    

    이벤트 트리거


    이벤트가 터치된 후 발생하는 일은 모든 연결된 이벤트 처리 함수를 실행하는 것입니다. 구체적으로는 이벤트[$name]를 옮겨다니며 데이터를handler에 전달하고 실행합니다.
        public function trigger($name, Event $event = null)
        {
            $this->ensureBehaviors();
            if (!empty($this->_events[$name])) {
                if ($event === null) {
                    $event = new Event;
                }
                if ($event->sender === null) {
                    $event->sender = $this;
                }
                $event->handled = false;
                $event->name = $name;
                foreach ($this->_events[$name] as $handler) {
                    $event->data = $handler[1];
                    call_user_func($handler[0], $event);
                    // stop further handling if the event is handled
                    if ($event->handled) {
                        return;
                    }
                }
            }
            // invoke class-level attached handlers
            Event::trigger($this, $name, $event);
        }
    

    주의해야 할 점은 모든 이벤트 처리 함수를 순환적으로 실행할 때, $event->handled를true로 설정하면 나머지handler가 실행되지 않는다는 것입니다.이벤트::trigger () 이 함수는 클래스 이벤트를 트리거하는 데 사용됩니다.

    이벤트 클래스


    이 클래스는 이미 여러 번 접했고 이 클래스의 사용 장면을 정리한 결과 그는 주로 두 가지 용도가 있음을 발견했다.
  • 는 이벤트 처리 함수에 정보를 전달하는 데 사용된다.
  • 트리거 이벤트에 사용됩니다.

  • 앞서 말한 이벤트의 귀속, 해제 작업은 모두 특정한 실례화된 대상을 바탕으로 하는 것이다. 만약에 특정한 클래스가 실례화되어 많은 대상이 나왔다면 지금 모든 대상을 특정한 이벤트에 귀속시키려면 이런 대상을 순서대로 귀속시켜야 한다. 이렇게 하는 것이 번거롭지 않다. 이럴 때 Event 클래스가 제공하는 메커니즘을 사용하여 특정한 이벤트를 귀속시킬 수 있다.이 종류의 실례화에서 나온 모든 대상은 이 사건을 촉발할 수 있다.이제 이벤트 클래스의 코드를 살펴보겠습니다.
    class Event extends Object
    {
        public $name;
        public $sender;
        public $handled = false;
        public $data;
        private static $_events = [];
        public static function on($class, $name, $handler, $data = null, $append = true)
        {
            $class = ltrim($class, '\\');
            if ($append || empty(self::$_events[$name][$class])) {
                self::$_events[$name][$class][] = [$handler, $data];
            } else {
                array_unshift(self::$_events[$name][$class], [$handler, $data]);
            }
        }
        public static function off($class, $name, $handler = null)
        {
            $class = ltrim($class, '\\');
            if (empty(self::$_events[$name][$class])) {
                return false;
            }
            if ($handler === null) {
                unset(self::$_events[$name][$class]);
                return true;
            } else {
                $removed = false;
                foreach (self::$_events[$name][$class] as $i => $event) {
                    if ($event[0] === $handler) {
                        unset(self::$_events[$name][$class][$i]);
                        $removed = true;
                    }
                }
                if ($removed) {
                    self::$_events[$name][$class] = array_values(self::$_events[$name][$class]);
                }
    
                return $removed;
            }
        }
        public static function hasHandlers($class, $name)
        {
            if (empty(self::$_events[$name])) {
                return false;
            }
            if (is_object($class)) {
                $class = get_class($class);
            } else {
                $class = ltrim($class, '\\');
            }
            do {
                if (!empty(self::$_events[$name][$class])) {
                    return true;
                }
            } while (($class = get_parent_class($class)) !== false);
    
            return false;
        }
        public static function trigger($class, $name, $event = null)
        {
            if (empty(self::$_events[$name])) {
                return;
            }
            if ($event === null) {
                $event = new static;
            }
            $event->handled = false;
            $event->name = $name;
    
            if (is_object($class)) {
                if ($event->sender === null) {
                    $event->sender = $class;
                }
                $class = get_class($class);
            } else {
                $class = ltrim($class, '\\');
            }
            do {
                if (!empty(self::$_events[$name][$class])) {
                    foreach (self::$_events[$name][$class] as $handler) {
                        $event->data = $handler[1];
                        call_user_func($handler[0], $event);
                        if ($event->handled) {
                            return;
                        }
                    }
                }
            } while (($class = get_parent_class($class)) !== false);
        }
    }
    

    이벤트 클래스 중 1$이벤트 그룹, 안에 저장된 내용은Component의 내용과 같지만, 클래스 이름에 따라 해당하는 클래스 이벤트를 찾아야 하기 때문에 현재 그룹에 한 층이 더 있습니다: $events[$name][$class][] = [$handler, $data]; 등록 클래스 이벤트:
    Event::on(  Worker::className(),               //   
                Worker::EVENT_OFF_DUTY,            //   
                function ($event) {                //   
                    echo $event->sender . '  '; 
                }
    );
    

    트리거 클래스 이벤트, $this의 역할은 누가 트리거했는지 알고 그 대상에 따라 클래스의 이름을 얻는 것입니다.
    Event::trigger($this, $name, $event); 
    

    비헤이비어


    행위는 하나의 클래스입니다. 새로운 행위를 만들려면 먼저 계승yii\base\Behavior의 클래스를 새로 만들어야 합니다. 그리고 이 행위를Component나 그 하위 클래스를 계승하는 다른 클래스에 의존해야 합니다. 이 클래스가 이 행위를 하면 이 행위가 가지고 있는 속성과 방법이 있습니다.첨부하는 과정은 바로 이런 종류의attach방법을 호출하는 것이고 상응하는 해제 과정은 그detach방법을 호출하는 것이다.귀속할 때 행위의 이벤트를 소유자에게 등록합니다. 이 소유자는 반드시Component입니다.먼저 Behavior 클래스를 살펴보겠습니다.
    class Behavior extends Object
    {
        public $owner;
    
    
        /**
         * Declares event handlers for the [[owner]]'s events.
         * - method in this behavior: `'handleClick'`, equivalent to `[$this, 'handleClick']`
         * - object method: `[$object, 'handleClick']`
         * - static method: `['Page', 'handleClick']`
         * - anonymous function: `function ($event) { ... }`
        */
        public function events()
        {
            return [];
        }
        public function attach($owner)
        {
            $this->owner = $owner;
            foreach ($this->events() as $event => $handler) {
                $owner->on($event, is_string($handler) ? [$this, $handler] : $handler);
            }
        }
        public function detach()
        {
            if ($this->owner) {
                foreach ($this->events() as $event => $handler) {
                    $this->owner->off($event, is_string($handler) ? [$this, $handler] : $handler);
                }
                $this->owner = null;
            }
        }
    }
    

    구성 요소의 행위 제어


    어셈블리에 변수 $behaviors는 모든 행동을 저장하는 데 사용됩니다. 이것은 하나의 그룹 (behavior name = > behavior) 이고, 여기의behavior는 하나의 클래스를 표시합니다. $behaviors가null일 때 초기화되지 않았음을 설명합니다.
        public function ensureBehaviors()
        {
            if ($this->_behaviors === null) {
                $this->_behaviors = [];
                foreach ($this->behaviors() as $name => $behavior) {
                    $this->attachBehaviorInternal($name, $behavior);
                }
            }
        }
    

    이 함수는 방금 만났는데 모든 행위를 초기화하는 과정이다. 먼저 함수를 호출하여 모든 행위를 얻은 다음에 함수attachBehaviorInternal을 순서대로 실행한다.한 가지 설명이 필요합니다.set () 함수에서 as+ 공백의 속성을 특수 처리하여 하나의 행위로 간주한 것을 보면 이때 attach Behavior 함수를 호출하여 이 행위를 attach 처리하고 이 함수에서 먼저 ensure Behaviors, 즉 먼저 behaviors () 함수 정의를 초기화하는 행위를 호출했다.같은 이름의 행위가 나타날 때 후자는 전자를 덮어쓰기 때문에 설정된 그룹에서 설정된 행위의 우선순위는behaviors () 함수가 정의한 행위보다 높습니다.

    비헤이비어attach 프로세스


    attach의 행위는 모두 두 가지 출처가 있는데 하나는 설정 수조에서 as+빈칸을 이용하여 정의한 것이고 하나는 behaviors() 함수에서 되돌아오는 것이며 최종적으로 하나의 함수를 호출할 것이다.
        private function attachBehaviorInternal($name, $behavior)
        {
            if (!($behavior instanceof Behavior)) {
                $behavior = Yii::createObject($behavior);
            }
            if (is_int($name)) {
                $behavior->attach($this);
                $this->_behaviors[] = $behavior;
            } else {
                if (isset($this->_behaviors[$name])) {
                    $this->_behaviors[$name]->detach();
                }
                $behavior->attach($this);
                $this->_behaviors[$name] = $behavior;
            }
            return $behavior;
        }
    

    만약 하나의 행위의name가 정수라면 이 행위는 단지 이 행위의attach 함수를 지성했을 뿐 그 속성과 방법은 주체에 의존하지 않는다.첨부하는 과정은 우선 Behavior를 실례화한 다음에 그 값을 에 부여하는 것이다behaviors 그룹, 같은 이름의 행동이 존재하면 덮어씁니다.

    detach 프로세스


    주로 두 단계로 $behavior 대상을 $에서behavior에서 제거하고 $behavior의detach () 방법을 호출합니다
        public function detachBehavior($name)
        {
            $this->ensureBehaviors();
            if (isset($this->_behaviors[$name])) {
                $behavior = $this->_behaviors[$name];
                unset($this->_behaviors[$name]);
                $behavior->detach();
                return $behavior;
            } else {
                return null;
            }
        }
    

    좋은 웹페이지 즐겨찾기