1: <?php
2: namespace Sleepy;
3:
4: /**
5: * Adds Hooks and Filters
6: *
7: * You can create modules to hooks by adding php files into the
8: * *\app\modules\enabled* directory.
9: *
10: * ## Usage
11: * <code>
12: * // add a hook point
13: * $content = Hook::addFilter('update_content', $_POST['content']);
14: *
15: * // Add a module to the hook point--in /modules/<moduleName.php>
16: * function clean_html ($html) {
17: * $c = htmlentities(trim($html), ENT_NOQUOTES, "UTF-8", false);
18: * return $c;
19: * }
20: *
21: * Hook::applyFilter("update_content", "clean_html");
22: * </code>
23: *
24: * ## Changelog
25: *
26: * ### Version 1.2
27: * * Updated privacy prefix (_) for consistency
28: * * Fixed Hook::_load method for teamsite bug
29: *
30: * ### Version 1.1
31: * * Added the date section to the documentation
32: *
33: * ### Version 1.0
34: * * static class pattern fixes
35: * * multiple module directories
36: * * crawls subdirectories of module directories
37: *
38: * @date July 18, 2016
39: * @author Jaime A. Rodriguez <hi.i.am.jaime@gmail.com>
40: * @version 1.2
41: * @license http://opensource.org/licenses/MIT
42: *
43: * @todo devise a better way of passing multiple parameters to hooks, perhaps
44: * use objects instead of arrays
45: */
46: class Hook {
47:
48: /**
49: * Has this been initialized?
50: *
51: * @var bool
52: * @private
53: */
54: private static $_initialized = false;
55:
56: /**
57: * An array of filters
58: *
59: * @var _Filter[]
60: * @private
61: */
62: private static $_filters = array();
63:
64: /**
65: * The directories where the modules are stored
66: *
67: * @var string
68: */
69: public static $directories = array();
70:
71: /**
72: * Prevent class from being cloned
73: *
74: * @private
75: */
76: private function __clone() {}
77:
78: /**
79: * The constructor is private to ensure we only have one instance
80: *
81: * @private
82: */
83: private function __construct() {}
84:
85: /**
86: * Return instance or create initial instance
87: *
88: * @private
89: * @static
90: * @return object
91: */
92: private static function _initialize() {
93: if (!self::$_initialized) {
94: self::$directories[] = DIRBASE . '/modules/';
95: self::$_initialized = true;
96: self::_load();
97: }
98: }
99:
100: /**
101: * Loads all the modules
102: *
103: * @private
104: * @static
105: * @return void
106: */
107: private static function _load() {
108: $directories = self::$directories;
109:
110: // get all subdirectories
111: foreach (self::$directories as $directory) {
112: $subdirectories = glob($directory . '/*' , GLOB_ONLYDIR);
113:
114: if (is_array($subdirectories)) {
115: $directories = array_merge($directories, $subdirectories);
116: }
117: }
118:
119: // include all php files
120: foreach ($directories as $directory) {
121: $files = glob($directory . '/*.php');
122:
123: if (!is_array($files)) {
124: continue;
125: }
126:
127: foreach($files as $file) {
128: if (strpos($file, '_test.php') !== false) {
129: continue;
130: }
131:
132: require_once($file);
133: }
134: }
135: }
136:
137: /**
138: * Adds a new filter to a filter-type hook point
139: *
140: * @param string $name [description]
141: * @param string $function [description]
142: * @param int $args [description]
143: * @static
144: * @return void
145: */
146: public static function applyFilter($name, $function) {
147: self::_initialize();
148:
149: $args = func_get_args();
150:
151: array_shift($args);
152: array_shift($args);
153:
154: if (!isset(self::$_filters[$name])) {
155: self::$_filters[$name] = new _Filter ($name);
156: }
157:
158: // add the function to the filter
159: self::$_filters[$name]->add($function, $args);
160: }
161:
162: /**
163: * Adds a new filter-type hook point
164: *
165: * @param mixed $name [description]
166: * @param string $value [description]
167: * @static
168: * @return void
169: */
170: public static function addFilter($name, $value) {
171: self::_initialize();
172:
173: // If there are no functions to run
174: if (!isset(self::$_filters[$name])) {
175: if (is_array($value)) {
176: return $value[0];
177: } else {
178: return $value;
179: }
180: }
181:
182: foreach (self::$_filters[$name]->functions as $function => $args) {
183: if (is_array($value)) {
184: $returned = call_user_func_array($function, $value);
185: } else {
186: $returned = call_user_func($function, $value);
187: }
188: }
189:
190: return $returned;
191: }
192:
193: /**
194: * Adds a new function to a action-type hook point
195: *
196: * @param string $name Name of filter
197: * @param string $function Function to call
198: * @static
199: * @return void
200: */
201: public static function doAction($name, $function) {
202: call_user_func_array('self::applyFilter', func_get_args());
203: }
204:
205: /**
206: * Adds a new action-type hook point
207: *
208: * @param string $name [description]
209: * @static
210: * @return void
211: */
212: public static function addAction($name) {
213: self::addFilter($name, '');
214: }
215: }
216:
217: /**
218: * Private class used by the Hooks class
219: *
220: * The class stores the filters. It has properties to store the name of the
221: * filter as well the functions that should run when the filters are stored.
222: * The filters property is an array. The key is the name of the
223: * function and value is the arguments. Currently we do not make any use of the
224: * arguments.
225: *
226: * ### Usage
227: *
228: * This class is private and should not be used outside of the Hooks class
229: *
230: * @param string $name name of the filter
231: *
232: * @date September 31, 2014
233: * @author Jaime A. Rodriguez <hi.i.am.jaime@gmail.com>
234: * @version 0.4
235: * @license http://opensource.org/licenses/MIT
236: * @internal
237: */
238:
239: class _Filter {
240: /**
241: * The name of the filter
242: *
243: * @var string
244: */
245: public $name;
246:
247: /**
248: * A list of functions to execute
249: *
250: * @var [string[]]
251: */
252: public $functions;
253:
254: /**
255: * Constructor
256: *
257: * @param string $name The name of the filter
258: */
259: public function __construct($name) {
260: $this->name = $name;
261: }
262:
263: /**
264: * Adds a function to this filter
265: *
266: * @param string $function The function to call
267: * @param array $args An array of parameters
268: */
269: public function add($function, $args) {
270: $this->functions[$function] = $args;
271: }
272: }