<?php
/**
 * Link         :   http://www.phpcorner.net
 * User         :   qingbing<780042175@qq.com>
 * Date         :   2018-10-27
 * Version      :   1.0
 */

namespace Abstracts;

use Helper\CList;
use Helper\Exception;

/**
 * 所有类的基类
 * Class Base
 * @package Helper
 */
abstract class Base
{
    private $_events = [];

    /**
     * 通过给定的值为对象实例赋值
     * @param array $configs
     */
    public function configure(array $configs)
    {
        foreach ($configs as $property => $value) {
            $this->{$property} = $value;
        }
    }

    /**
     * __get：魔术方法，当直接访问属性不存在时被唤醒
     * @param string $property
     * @return mixed
     * @throws Exception
     */
    public function __get($property)
    {
        if (method_exists($this, $getter = 'get' . $property)) {
            return $this->$getter();
        }

        if (method_exists($this, 'set' . $property)) {
            throw new Exception(str_cover('尝试读取write-only属性"{class}.{property}"。', [
                '{class}' => get_class($this),
                '{property}' => $property,
            ]), 100100201);
        } else {
            throw new Exception(str_cover('尝试读取不存在的属性"{class}.{property}"', [
                '{class}' => get_class($this),
                '{property}' => $property,
            ]), 100100202);
        }
    }

    /**
     * __set：魔术方法，当直接设置不存在属性时被唤醒
     * @param string $property
     * @param mixed $value
     * @throws Exception
     */
    public function __set($property, $value)
    {
        if (method_exists($this, $setter = 'set' . $property)) {
            $this->$setter($value);
        } else if (method_exists($this, 'get' . $property)) {
            throw new Exception(str_cover('尝试写入read-only属性"{class}.{property}"', [
                '{class}' => get_class($this),
                '{property}' => $property,
            ]), 100100203);
        } else {
            throw new Exception(str_cover('尝试写入不存在的属性"{class}.{property}"', [
                '{class}' => get_class($this),
                '{property}' => $property,
            ]), 100100204);
        }
    }

    /**
     * __isset：魔术方法，当用isset函数作用于类实例属性时被唤醒，检查属性是否存在
     * @param string $property
     * @return bool
     */
    public function __isset($property)
    {
        if (method_exists($this, $getter = 'get' . $property)) {
            return $this->$getter() !== null;
        } else {
            return false;
        }
    }

    /**
     * __unset：魔术方法，当用unset函数作用于类实例属性时被唤醒，设置属性为空
     * @param string $property
     * @throws Exception
     */
    public function __unset($property)
    {
        if (method_exists($this, $setter = 'set' . $property)) {
            $this->$setter(null);
        } else if (method_exists($this, 'get' . $property)) {
            throw new Exception(str_cover('尝试删除read-only属性"{class}.{property}"', [
                '{class}' => get_class($this),
                '{property}' => $property,
            ]), 100100205);
        }
    }

    /**
     * 判断属性是否可读
     * @param string $property
     * @return bool
     */
    public function canGetProperty($property)
    {
        return method_exists($this, 'get' . $property);
    }

    /**
     * 判断属性是否可写
     * @param string $property
     * @return bool
     */
    public function canSetProperty($property)
    {
        return method_exists($this, 'set' . $property);
    }

    /**
     * 检查属性是否存在
     * @param string $property
     * @return bool
     */
    public function hasProperty($property)
    {
        return method_exists($this, 'get' . $property) || method_exists($this, 'set' . $property);
    }

    /**
     * 判断事件名称是否存在
     * @param string $name
     * @return bool
     */
    public function hasEvent($name)
    {
        return method_exists($this, "on{$name}");
    }

    /**
     * 判断事件是否有有效句柄
     * @param string $name
     * @return bool
     */
    public function hasEventHandler($name)
    {
        $name = strtolower($name);
        return isset($this->_events[$name]) && count($this->_events[$name]) > 0;
    }

    /**
     * 获取事件句柄
     * @param string $name
     * @return CList
     * @throws Exception
     */
    public function getEventHandlers($name)
    {
        if ($this->hasEvent($name)) {
            $name = strtolower($name);
            if (!isset($this->_events[$name])) {
                $this->_events[$name] = new CList();
            }
            return $this->_events[$name];
        } else {
            throw new Exception(str_cover('事件"{class}.{event}"未定义', [
                '{class}' => get_class($this),
                '{event}' => $name,
            ]), 100100206);
        }
    }

    /**
     * 添加一个句柄事件
     * @param string $name
     * @param mixed $handler
     * @throws Exception
     */
    public function attachEventHandler($name, $handler)
    {
        $this->getEventHandlers($name)->push($handler);
    }

    /**
     * 解除事件句柄
     * @param string $name 事件名称
     * @param mixed $handler 事件句柄
     * @return bool
     * @throws Exception
     */
    public function detachEventHandler($name, $handler)
    {
        if ($this->hasEventHandler($name)) {
            return false !== $this->getEventHandlers($name)->remove($handler);
        }
        return false;
    }

    /**
     * 唤起事件并执行
     * @param string $name
     * @param object $event
     * @throws Exception
     */
    public function raiseEvent($name, $event)
    {
        $list = $this->getEventHandlers($name);
        foreach ($list as $handler) {
            if (is_string($name)) {
                call_user_func($handler, $event);
            } else if (is_callable($handler, true)) {
                if (is_array($handler)) {
                    // [object, methodName]
                    list($object, $method) = $handler;
                    if (is_string($object)) {
                        // 静态方法调用
                        call_user_func($handler, $event);
                    } else if (method_exists($object, $method)) {
                        // 实例方法
                        $object->$method($event);
                    } else {
                        throw new Exception(str_cover('事件"{class}.{event}"包含了无效的事件句柄"{handler}"', [
                            '{class}' => get_class($this),
                            '{event}' => $name,
                            '{handler}' => $handler[1],
                        ]), 100100207);
                    }
                } else {
                    // PHP 5.3: anonymous function
                    call_user_func($handler, $event);
                }
            }
        }
    }
}