A Decoupling Course of "Walking on the Way of Scholes Learning"

Keywords: PHP Attribute

Summary

Looking at the title, I don't know what the author wants to say. Recently, when looking at Swoole, I encountered a decoupling problem in encapsulating the framework. Decoupling is not unfamiliar to everyone. This decoupling is a demo about listening events and heartbeat detection. Let's look directly at the problem.

Solutions

I want to add event listeners when Swoole starts, such as the registry in RPC, which services I start and provide similar scenarios, etc., but this is just an event listener, what if more? For example, when I receive a request, receive a message, close the connection, etc., you need to create a lot of time to listen, this time you may start all the events to register, and then see how the code is simple and rude implementation.

<?php

namespace LoyaltyLu\Core;

use LoyaltyLu\Core\Event\Event;
use  Swoole\Http\Server;

class Http
{
    ...
    
    public function start()
    {
        $reload = Reload::get_instance();
        $reload->watch = [CONFIG_PATH, FRAME_PATH, APP_PATH];
        $reload->md5Flag = $reload->getMd5();
        #Actively Collect Existing Events Collect Listener Directory
        $this->collecEvent();
        //timer
        swoole_timer_tick(3000, function () use ($reload) {
            if ($reload->reload()) {
                $this->server->reload(); //restart
            }
        });
        Event::trigger('start', ['sss']);
    }
    
    /**
     * Collect events
     */
    public function collecEvent()
    {
        $files = glob(EVENT_PATH . "/*.php");
        if (!empty($files)) {
            foreach ($files as $dir => $fileName) {
                include "{$fileName}";
                $fileName = explode('/', $fileName);
                $className = explode('.', end($fileName))[0];
                $nameSpace = 'App\\Listener\\' . $className;#Configurable file

                if (class_exists($nameSpace)) {
                    $obj = new $nameSpace;
                    #Get the event name you defined and read the class document annotations using reflection
                    $re = new \ReflectionClass($obj);
                    if (strlen($re->getDocComment()) < 2) {
                        throw new \Exception('Event names are not defined in accordance with the specification');
                    } else {
                        preg_match("/@Listener\((.*)\)/i", $re->getDocComment(), $eventName);
                        if (empty($eventName)) {
                            throw new \Exception('Event names are not defined in accordance with the specification');
                        }
                        #Registered events
                        Event::register($eventName[1], [$obj, 'handle']);
                    }
                }
            }
        }
    }
...
}
  • Call collecEvent method when Swoole is started
  • ColecEvent is responsible for collecting, registering methods in specified directories and namespaces
  • EVENT_PATH = APP_PATH.'/listener'
  • Loop through all files in the directory
  • Get the file name
  • Splicing namespaces (namespaces can be placed in configuration files such as Config)
  • class_exists checks for class existence
  • ReflectionClass here uses the form of reflection and annotation to determine compliance.
  • getDocComment() Gets the annotation content
  • Regular /@Listener((*))/ I matches the events that need to be monitored
  • Perform Event::register($event,$callback) for event registration

Next, look at the Event class:

class Event
{
    public static $events = [];

    //Event registration

    /**
     * @param $event Event name
     * @param $callback event callbacks
     */
    public static function register($event, $callback)
    {
        $event = strtolower($event);//Case insensitive
        if (!isset(self::$events[$event])) {
            self::$events[$event] = [];
        }
        self::$events[$event] = ['callback' => $callback];
    }

    //Event triggering
    public static function trigger($event, $params = [])
    {
        $event = strtolower($event);//Case insensitive
        if (isset(self::$events[$event])) {
            call_user_func(self::$events[$event]['callback'], $params);
            return true;
        }
        return false;
    }
}
  • Event contains two static methods: event registration and event triggering
  • Event registration implements putting said events into the $event attribute
  • Event Trigger: Verify that the event exists and call_user_func() executes the callback function

Look again at how to declare an event file:

<?php


namespace App\Listener;


/**
 * Class StartListener
 * @package App\Listener
 * @Listener(start)
 */
class StartListener
{
    public function handle($params)
    {
        go(function () {//Creating Ctrip Environment
            //Upgraded websockt
            $cli = new \Swoole\Coroutine\Http\Client('127.0.0.1', 9600);
            $ret = $cli->upgrade('/');
            if ($ret) {
//                Config::get()
                $data = [
                    'method'      => 'register',
                    'serviceName' => "Server",
                    'ip'          => '0.0.0.0',
                    'port'        => 9800,
                ];
                $cli->push(json_encode($data));
                //Heart beat management
                swoole_timer_tick(3000, function () use ($cli) {
                    if ($cli->errCode == 0) {
                        $cli->push('11', WEBSOCKET_OPCODE_PING);
                    }
                });
            }
        });
    }
}
  • We should inherit an interface class here. We must implement handle() method. We can implement it by ourselves.
  • handle() method implements a function similar to notification registry registration service
  • swoole_timer_tick() uses millisecond timer to process heartbeat
  • Document annotations for reflex grabbing
/**
 * Class StartListener
 * @package App\Listener
 * @Listener(start)
 */

call

public function start()
    {
        ...
        #Actively Collect Existing Events Collect Listener Directory
        $this->collecEvent();
        ...
        Event::trigger('start', ['sss']);
    }

Thanks for watching

Thank you for your patience. I hope it will be helpful to you. I also hope you can provide different opinions and find more effective ways to complete and learn together. Thank you.

Posted by beboni on Wed, 24 Jul 2019 03:13:22 -0700