1 <?php
2 /**
3 * UIX UI Loader
4 *
5 * @package uix
6 * @author David Cramer
7 * @license GPL-2.0+
8 * @link
9 * @copyright 2016 David Cramer
10 */
11 namespace uix;
12
13 /**
14 * UI loader and handler class. This forms a single instance with UI objects attached
15 *
16 * @package uix
17 * @author David Cramer
18 */
19 class ui{
20
21
22 /**
23 * Array of definitions locations
24 *
25 * @since 1.0.0
26 * @access protected
27 * @var array
28 */
29 protected $locations = array();
30
31 /**
32 * Array of object instances
33 *
34 * @since 1.0.0
35 * @access public
36 * @var array
37 */
38 public $ui;
39
40 /**
41 * Array of post and get data
42 *
43 * @since 1.0.0
44 * @access private
45 * @var array
46 */
47 protected $data = array();
48
49 /**
50 * Holds instance
51 *
52 * @since 1.0.0
53 * @access protected
54 * @var object/UI
55 */
56 protected static $instance = null;
57
58 /**
59 * UI structure auto load
60 *
61 * @since 1.0.0
62 * @access public
63 */
64 public function auto_load() {
65 /**
66 * do UI loader locations
67 *
68 * @param ui $this Current instance of this class
69 */
70 do_action( 'uix_register', $this );
71
72 // go over each locations
73 foreach( $this->locations as $type => $paths ){
74
75 if( $this->is_callable( $type ) )
76 $this->process_paths( $type, $paths );
77
78 }
79
80 }
81
82 /**
83 * Add a single structure object
84 *
85 * @since 1.0.0
86 * @param string $type The type of object to add
87 * @param array $paths array of paths to process and add
88 */
89 private function process_paths( $type, $paths ){
90
91 foreach( $paths as $path ) {
92 $has_struct = $this->get_file_structure( $path );
93 if( is_array( $has_struct ) )
94 $this->add_objects( $type, $has_struct );
95
96 }
97 }
98
99 /**
100 * Add a single structure object
101 *
102 * @since 1.0.0
103 * @param string $type The type of object to add
104 * @param string $slug The objects slug to add
105 * @param array $structure The objects structure
106 * @param object $parent object
107 * @return object The instance of the object type or null if invalid
108 */
109 public function add( $type, $slug, $structure, $parent = null ) {
110 $init = $this->get_register_callback( $type );
111 if( null !== $init ){
112 $object = call_user_func_array( $init, array( $slug, $structure, $parent ) );
113 $this->ui->{$type}[ $slug ] = $object;
114 return $object;
115 }
116 return null;
117 }
118
119 /**
120 * Returns a callback for registering the object or null if invalid type
121 *
122 * @since 1.0.0
123 * @param string $type The type of object to get register callback for
124 * @return array|null Callback array for registering an object or null if invalid
125 */
126 public function get_register_callback( $type ) {
127 $init = array( '\uix\ui\\' . $type, 'register' );
128 if( !is_callable( $init ) ){
129 return null;
130 }
131 return $init;
132 }
133
134 /**
135 * Checks if the object type is callable
136 *
137 * @since 1.0.0
138 * @param string $type The type of object to check
139 * @return bool
140 */
141 public function is_callable( $type ) {
142 $init = array( '\uix\ui\\' . $type, 'register' );
143 return is_callable( $init );
144 }
145
146 /**
147 * Registers multiple objects
148 *
149 * @since 1.0.0
150 * @param string $type The type of object to add
151 * @param array $objects The objects structure
152 * @param object $parent object
153 */
154 public function add_objects( $type, array $objects, $parent = null ) {
155 foreach( $objects as $slug => $struct ){
156 if( is_array( $struct ) )
157 $this->add( $type, $slug, $struct, $parent);
158 }
159 }
160
161
162
163 /**
164 * Return an instance of this class.
165 * @codeCoverageIgnore
166 * @since 1.0.0
167 * @param array $request_data Current REQUEST superglobals
168 * @return ui A single instance of this class
169 */
170 public static function get_instance( $request_data ) {
171
172 // If the single instance hasn't been set, set it now.
173 if ( isset( self::$instance ) ) {
174 self::$instance->data = $request_data;
175 }else{
176 self::$instance = new self;
177 self::$instance->data = $request_data;
178 self::$instance->auto_load();
179 }
180
181 return self::$instance;
182
183 }
184
185 /**
186 * Register the UIX object paths for autoloading
187 *
188 * @since 1.0.0
189 *
190 * @param array|string $arr path, or array of paths to structures to autoload
191 */
192 public function register( $arr ) {
193 // set error handler for catching file location errors
194 set_error_handler( array( $this, 'silent_warning' ), E_WARNING );
195 // determin how the structure works.
196 foreach( (array) $arr as $key => $value )
197 $this->locations = array_merge_recursive( $this->locations, $this->get_files_from_folders( trailingslashit( $value ) ) );
198
199 // restore original handler
200 restore_error_handler();
201 }
202
203 /**
204 * Handy method to get request vars
205 *
206 * @since 1.0.0
207 *
208 * @param string $type Request type to get
209 * @return array Request vars array
210 */
211 public function request_vars( $type ) {
212 return $this->data[ $type ];
213 }
214
215
216 /**
217 * Gets the file structures and converts it if needed
218 *
219 * @since 1.0.0
220 * @access private
221 * @param string $path The file path to load
222 * @return array|bool object structure array or false if invalid
223 */
224 private function get_file_structure( $path ){
225 ob_start();
226 $content = include $path;
227 $has_output = ob_get_clean();
228 // did stuff output
229 if( !empty( $has_output ) )
230 $content = json_decode( $has_output, ARRAY_A );
231
232 return $content;
233 }
234
235
236 /**
237 * Opens a location and gets the folders to check
238 *
239 * @since 1.0.0
240 * @access private
241 * @param string $path The file patch to examine and to fetch contents from
242 * @return array List of folders
243 */
244 private function get_folder_contents( $path ) {
245
246 $items = array();
247 if( $uid = opendir( $path ) ) {
248 while (($item = readdir($uid)) !== false) {
249 if ( substr($item, 0, 1) != '.' )
250 $items[ $item ] = $path . $item;
251
252 }
253 closedir( $uid );
254 }
255
256 return $items;
257 }
258
259 /**
260 * Opens a location and gets the file to load for each folder
261 *
262 * @since 1.0.0
263 * @access private
264 * @param string $path The file patch to examine and to fetch contents from
265 * @return array List of folders and files
266 */
267 private function get_files_from_folders( $path ) {
268
269 $items = $this::get_folder_contents( $path );
270
271 foreach ( $items as $type => &$location ){
272 $location = $this::get_folder_contents( trailingslashit( $location ) );
273 sort($location);
274 }
275
276 return $items;
277 }
278
279 /**
280 * Handles E_WARNING error notices whan the file loader runs.
281 *
282 *
283 * @since 1.0.0
284 *
285 * @link http://php.net/manual/en/function.set-error-handler.php
286 * @param int $errno Contains the level of the error raised, as an integer.
287 * @param string $errstr Contains the error message.
288 * @param string $errfile Which contains the filename that the error was raised in.
289 * @param int $errline which contains the line number the error was raised at.
290 */
291 public function silent_warning( $errno, $errstr, $errfile, $errline ) {
292 $this->add( 'notice', 'notice_' . $errno . '-' . $errline, array(
293 'description' => '<strong>' . __( 'Warning' ) . '</strong>: ' . $errstr . '<br>on ' . $errfile .' line ' . $errline,
294 'state' => 'warning'
295 ) );
296 }
297
298
299 /**
300 * Sets assets to be enqueued for this instance.
301 *
302 * @param array $assets the asset to enqueue where the key is the type and the value the asset
303 */
304 public function set_assets( $assets ){
305
306 foreach ( $assets as $type => $asset )
307 $this->enqueue( $asset, $type );
308
309
310 }
311
312 /**
313 * enqueue a set of styles and scripts
314 *
315 * @since 1.0.0
316 * @access protected
317 * @param array $set Array of assets to be enqueued
318 * @param string $type The type of asset
319 */
320 protected function enqueue( $set, $type ){
321 // go over the set to see if it has styles or scripts
322
323 $enqueue_type = 'wp_enqueue_' . $type;
324
325 foreach( $set as $key => $item ){
326
327 if( is_int( $key ) ){
328 $enqueue_type( $item );
329 continue;
330 }
331
332 $args = $this->build_asset_args( $item );
333 $enqueue_type( $key, $args['src'], $args['deps'], $args['ver'], $args['in_footer'] );
334
335 }
336
337 }
338
339
340 /**
341 * Checks the asset type
342 *
343 * @since 1.0.0
344 * @access private
345 * @param array|string $asset Asset structure, slug or path to build
346 * @return array Params for enqueuing the asset
347 */
348 private function build_asset_args( $asset ){
349
350 // setup default args for array type includes
351 $args = array(
352 "src" => $asset,
353 "deps" => array(),
354 "ver" => false,
355 "in_footer" => false,
356 "media" => false
357 );
358
359 if( is_array( $asset ) )
360 $args = array_merge( $args, $asset );
361
362 // add uix dep
363 $args['deps'][] = 'uix';
364
365 return $args;
366 }
367
368
369 }