Hiểu về XenForo Class Proxy System

XenForo Class Proxy System là gì ?
Nói chung, hệ thống này cho phép một vài add-ons mở rộng class trong XenForo, sử dụng hệ thống event listener (lắng nghe sự kiện) của Xenforo, cho phép một chuỗi mở rộng sẽ được xây dựng (với các liên kết cuối cùng trong chuỗi là các lớp được khởi tạo khi XenForo tạo các lớp cần thiết).Listener (Lắng nghe)
Để ví dụ, chúng ta sẽ làm việc với listener sau đây, được thêm vào một class vào chuỗi mở rộng XenForo_ControllerPublic_Thread (điều này đang được thực thi trên load_class_controller):

<?php

class MyAddon_Listener {
  public static function loadClassController($class, array &$extend) {
    if($class == "XenForo_ControllerPublic_Thread") {
      $extend[] = "MyAddon_ControllerPublic_Thread";
    }
  }
}

 

Vì vậy, nếu chúng ta nhìn vào hàm này, chúng ta có thể thấy rằng $extend được truyền theo tham chiếu, có nghĩa là bất kỳ sự mở rộng của listener trên các event (sự kiện) load_class_controller sẽ nhận và sửa đổi cùng một mảng $extend. Vì vậy, nếu listener này thực hiện cuối cùng, và hai mở rộng khác XenForo_ControllerPublic_Thread, mảng sẽ chứa các yếu tố sau đây (theo thứ tự):

  • RandomAddon_ControllerPublic_Thread
  • YourAddon_ControllerPublic_Thread
  • MyAddon_ControllerPublic_Thread

Điều này có nghĩa, là khi XenForo cố gắng để khởi tạo đối tượng XenForo_ControllerPublic_Thread nó thực sự sẽ khởi tạo một cá thể của MyAddon_ControllerPublic_Thread.
What is the XenForo Class Proxy System?
In a nutshell, this system allows several add-ons to extend the same class within XenForo using its code event listener system, which allows an extension chain to be constructed (with the last link in the chain being the class that is instantiated when XenForo creates the necessary class).

The Listener
For this example, we will be working with the following listener, which is adding an example class to XenForo_ControllerPublic_Thread‘s extension chain (this is being executed onload_class_controller):

<?php

class MyAddon_Listener {
  public static function loadClassController($class, array &$extend) {
    if($class == "XenForo_ControllerPublic_Thread") {
      $extend[] = "MyAddon_ControllerPublic_Thread";
    }
  }
}

 

So, if we look into this function we can see that $extend is passed by reference, meaning that any execution of a listener on the load_class_controller code event will receive and modify the same $extend array. So, if this listener executes last, and two others extend XenForo_ControllerPublic_Thread, the array will contain the following elements (in this order):

  • RandomAddon_ControllerPublic_Thread
  • YourAddon_ControllerPublic_Thread
  • MyAddon_ControllerPublic_Thread

What this means, is that when XenForo attempts to instantiate XenForo_ControllerPublic_Thread it will actually instantiate an instance of MyAddon_ControllerPublic_Thread.

Resolving the Issue of Multiple Inheritance
PHP does not have multiple inheritance classes. Each class needs exactly one class to extend, and any class extending a class will not receive any other classes (besides the class its extending’s ancestors) functionality. This poses a problem when 2 or more add-ons want to extend the same functionality within XenForo. It isn’t possible to have all three instances of the extended XenForo_ControllerPublic_Thread running and have it function properly (for example, getting all 3 changes to return to XenForo_Application to properly display on the front end) without running all three classes in the same execution tree.

The Solution
The solution is the Proxy Class system. Any class that is being used as an extension in the listener system should extend a non-existant class of the formatXFCP_ClassName. This is the Class Proxy. So, for MyAddon_ControllerPublic_Thread, I want to extend XFCP_MyAddon_ControllerPublic_Thread. So, the code for MyAddon_ControllerPublic_Thread will look like the following:

<?php
class MyAddon_ControllerPublic_Thread extends XFCP_MyAddon_ControllerPublic_Thread {
  public function actionMyAction() {
    // add-on code
  }
}

The magic of this solution occurs in XenForo_Application::resolveDynamicClass(). What happens is that XenForo fires the necessary code events (in this example, load_class_controller) and retrieves the extension tree for it (the list provided above). What it then does is create the necessary proxy classes and load the necessary extensions into the current execution tree. At the end of this, it returns the last link in the chain to whatever called it to instantiate. By the end, the extension tree will look like this (including XenForo classes), with each list item extending the previous list item:

  • XenForo_Controller
  • XenForo_ControllerPublic_Abstract
  • XenForo_ControllerPublic_Thread
  • XFCP_RandomAddon_ControllerPublic_Thread
  • RandomAddon_ControllerPublic_Thread
  • XFCP_YourAddon_ControllerPublic_Thread
  • YouAddon_ControllerPublic_Thread
  • XFCP_MyAddon_ControllerPublic_Thread
  • MyAddon_ControllerPublic_Thread

The classes that XenForo creates for the proxies are empty classes that extend the previous link in the chain. An example for XFCP_YourAddon_ControllerPublic_Thread:

<?php
class XFCP_YourAddon_ControllerPublic_Thread extends RandomAddon_ControllerPublic_Thread {}

Things to Note While using the Class Proxy System

  • parent::function() should always be called and used (ie, getting the returned view and adding in parameters) and be the return of your extended function. This will allow any other add-ons that function on the same function to properly work with your add-on.
  • No add-on should extend a XenForo class directly (unless the class proxy system doesn’t function).
  • If you need your class to execute early in the execution chain, be sure to set your event listener’s execution order to a low number. XenForo defaults this to 10 and will execute them with lowest to highest execution order.

Further Reading
http://xenforo.com/community/threads/how-to-add-more-actions-to-an-existing-controller.11202/

Leave a Reply

Your email address will not be published. Required fields are marked *