1 <?php
2
3 /**
4 * @copyright Copyright (c) 2016 Junaid Atari
5 * @link http://junaidatari.com Website
6 * @see http://www.github.com/blacksmoke26/yii2-cdn
7 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
8 */
9
10 namespace yii2cdn;
11
12 use yii\base\InvalidParamException;
13 use \yii\base\InvalidConfigException;
14 use \yii\base\UnknownPropertyException;
15
16 /**
17 * Yii2 Component Section File object
18 *
19 * @package yii2cdn
20 * @author Junaid Atari <mj.atari@gmail.com>
21 *
22 * @access public
23 * @version 0.1
24 */
25 class Section {
26
27 /**
28 * Section base Url
29 * @var string
30 */
31 protected $baseUrl;
32
33 /**
34 * Component ID
35 * @var string
36 */
37 protected $componentId;
38
39 /**
40 * Section name
41 * @var string
42 */
43 protected $section;
44
45 /**
46 * Section files
47 * @var File[]
48 */
49 protected $files;
50
51 /**
52 * Section file class
53 * @var string
54 */
55 private $fileClass;
56
57 /**
58 * ComponentSection constructor.
59 *
60 * @param array $config Configuration object
61 */
62 public function __construct ( array $config ) {
63 $this->componentId = $config['componentId,'];
64 $this->section = $config['section'];
65 $this->baseUrl = $config['baseUrl'];
66 $this->fileClass = $config['fileClass'];
67
68 if ( !count($config['files']) ) {
69 return;
70 }
71
72 foreach ( $config['files'] as $id => $url ) {
73 $options = $_attributes = [];
74
75 if ( count($config['attributes']) ) {
76
77 foreach ( $config['attributes'] as $k => $v ) {
78
79 $inf = explode('/', $k);
80
81 if ( isset($attributes["@options/{$id}"]) ) {
82 $options = $attributes["@options/{$id}"];
83 unset($attributes["@options/{$id}"]);
84 continue;
85 } else if ( $id === $inf[1] ) {
86 $_attributes[ $inf[0] ] = $v;
87 }
88 }
89 }
90
91 // Create File(s) object
92 $sectionNode = \Yii::createObject($config['fileClass'], [[
93 'fileId'=>$id,
94 'fileUrl'=>$url,
95 'section'=>$this->section,
96 'component'=>$this->componentId,
97 'options'=>$options,
98 'attributes'=>$_attributes,
99 ]]);
100
101 $this->files[$id] = $sectionNode;
102 }
103 }
104
105 /**
106 * Get section name
107 * @return string
108 */
109 public function getName () {
110 return $this->section;
111 }
112
113 /**
114 * Get section's component or id
115 *
116 * @param boolean $asId (optional) True will return component id (default: false)
117 * @param string $property (optional) Property name of `cdn` defined in @app/config/main.php (default: 'cdn')
118 * @return Component|string Component object | Component ID
119 */
120 public function getComponent ( $asId = false, $property = 'cdn' ) {
121 return $asId ? $this->componentId : \Yii::$app->cdn->get ( $property )->get ( $this->componentId );
122 }
123
124 /**
125 * Get the section's base Url
126 *
127 * @param null|string|array $str (optional) Append string at end of url (/ is optional) | List of strings to append url with (default: null)
128 * @return string|array
129 */
130 public function getUrl ( $str = null ) {
131 if ( empty( $str) ) {
132 return $this->baseUrl;
133 }
134
135 if ( !is_string ( $str) && !is_array($str) ) {
136 throw new InvalidParamException ("Parameter {$str} should be string or an array.");
137 }
138
139 $list = is_string ( $str)
140 ? [$str]
141 : $str;
142
143 $newList = [];
144
145 foreach ( $list as $itm ) {
146 $newList[] = $this->baseUrl . (substr ( $itm,0,1) !== '/' ? '/' . $itm : $itm);
147 }
148
149 return count($newList) === 1
150 ? array_shift($newList)
151 : $newList;
152 }
153
154 /**
155 * Perform a callback on each file and get the filtered files
156 *
157 * @param callable $callback Perform a callback on each file
158 * <code>
159 * # $excluded : True if file has been skipped (found in 'excluded' list), FALSE anyway
160 * # $included : True if file has been found in 'includeOnly' list, FALSE anyway
161 * function (string &$fileUrl, string &$fileId, boolean $excluded, boolean $included ) {
162 * // returning false will skip the file for further process.
163 * }
164 * </code>
165 * @param array $options (optional) Addition options pass to function<br>
166 * Options are:
167 * <code>
168 * > boolean listOnly : Returning the files url list only (default: false)
169 * > boolean noIds : Returning the list without file ids (default: false)
170 * > boolean unique : True will return only unique files (default: true)
171 * > array excluded : List of Files id to be skipped (default: [])
172 * > array includeOnly : List of Files id to be included, other will be skipped (default: [])
173 * </code>
174 * @param boolean $throwException (optional) True will throw exception (default: true)
175 * @throws \yii\base\InvalidParamException Option 'callback' not a function
176 * @throws \yii\base\InvalidConfigException When option 'excluded' not an array
177 * @throws \yii\base\InvalidConfigException When option 'includeOnly' not an array
178 * @return File[]|array List of files | List of sections and their files [SECTION=>FILES_LIST][]
179 */
180 public function callback ( callable $callback, array $options = [ ], $throwException = true ) {
181 if ( !is_callable($callback) ) {
182 throw new \yii\base\InvalidParamException("Option 'callback' must be a function");
183 }
184
185 // @property: boolean listOnly
186 $listOnly = isset($options['listOnly']) ? boolval($options['listOnly']) : false;
187
188 // @property: boolean noIds
189 $noIds = isset($options['noIds']) ? boolval($options['noIds']) : false;
190
191 // @property: boolean unique
192 $unique = isset($options['unique']) ? boolval($options['unique']) : false;
193
194 $excluded = $includeOnly = [];
195
196 // @property: array excluded
197 if ( isset($options['excluded']) ) {
198 if ( !is_array($options['excluded']) && $throwException ) {
199 throw new InvalidConfigException("Option 'excluded' must be an array");
200 }
201
202 $excluded = $options['excluded'];
203 }
204
205 // @property: array includeOnly
206 if ( isset($options['includeOnly']) ) {
207 if ( !is_array($options['excluded']) && $throwException ) {
208 throw new InvalidConfigException("Option 'includeOnly' must be an array");
209 }
210
211 $includeOnly = $options['includeOnly'];
212 }
213
214 $files = $this->getFiles(false, $unique);
215
216 if ( !count($files) ) {
217 return [];
218 }
219
220 /** @var File $file */
221 foreach ( $files as $file ) {
222
223 $fileId = $file->getId();
224 $fileUrl = $file->getUrl();
225
226 // Skipped files if excluded list isn't empty and id isn't present
227 if ( in_array( $fileId, $excluded ) ) {
228
229 $op = is_callable($callback)
230 ? $callback( $fileUrl, $fileId, true, false )
231 : null;
232
233 if ( is_null($op) || $op === false ) {
234 continue;
235 }
236 }
237
238 $op = is_callable($callback)
239 ? $callback( $fileUrl, $fileId, false, false )
240 : null;
241
242 if ( $op === false ) {
243 continue;
244 }
245
246 // Skipped files if includeOnly list isn't empty and id isn't present
247 if ( count($includeOnly) && !in_array($fileId, $includeOnly) ) {
248 $op = is_callable($callback)
249 ? $callback( $fileUrl, $fileId, false, true )
250 : null;
251
252 if ( is_null($op) || $op === false ) {
253 continue;
254 }
255 }
256
257 $files[$fileId] = $listOnly ? $fileUrl : $file;
258 }
259
260 return $noIds ? array_values($files) : $files;
261 }
262
263 /**
264 * Get section files list
265 * @param boolean $asArray True will return files list only (default: false)
266 * @param boolean $unique (optional) True will remove duplicate elements (default: false)
267 * @return File[]|array List of section object/[key=>value] pair of section files
268 */
269 public function getFiles ( $asArray = false, $unique = false ) {
270 if ( !$asArray ) {
271 return $this->files;
272 }
273
274 $list = [];
275
276 if ( !count($this->files) ) {
277 return $list;
278 }
279
280 foreach ( $this->files as $file ) {
281 $list[$file->getId()] = $file->getUrl();
282 }
283
284 if ( $unique ) {
285 $list = array_unique($list);
286 }
287
288 return $list;
289 }
290
291 /**
292 * Get the file by ID
293 * @param string $id File ID
294 * @param bool $usUrl True will return file url instead of object (default: false)
295 * @param bool $throwException True will throw exception when file id not found (default: true)
296 * @throws \yii\base\UnknownPropertyException When file id not found
297 * @return \yii2cdn\File|string|null Section file | File Url | Null when not found
298 */
299 public function getFileById ( $id, $usUrl = false, $throwException = true ) {
300 if ( !array_key_exists($id, $this->files) ) {
301
302 if ( $throwException ) {
303 throw new UnknownPropertyException ( "Unknown file id '{$id}' given" );
304 }
305
306 return null;
307 }
308
309 return !$usUrl ? $this->files[$id] : $this->files[$id]->getUrl();
310 }
311
312 /**
313 * Register list of files/files object into current view by type
314 * @param string $type Register files as (e.g., js|css)
315 * @param array|null $list List of File[] array | simple list | Include all files
316 * @param array $options (optional) Additional options pass to file while registering files
317 * @param callable|null $callback (optional) Perform callback on each registering file
318 * <code>
319 * function (string &$fileUrl, string &$fileId, boolean $excluded, boolean $included ) {
320 * // some logic here ...
321 * }
322 * </code>
323 */
324 public function registerFilesAs ( $type, $list, array $options = [], callable $callback = null ) {
325 if ( !is_string($type) || empty($type) ) {
326 throw new InvalidParamException ("Type must be a string and cannot empty");
327 }
328
329 if ( is_null($list) ) {
330 $list = $this->getFiles();
331 }
332
333 $itr = new \ArrayIterator($list);
334
335 if ( !$itr->count() ) {
336 throw new InvalidParamException ('List cannot be empty');
337 }
338
339 while ( $itr->valid() ) {
340
341 $file = $itr->current();
342 $fileId = (is_int($itr->key()) ? null : $itr->key());
343
344 if ( !$itr->current() instanceof $this->fileClass ) {
345
346 /** @var File $file */
347 $file = \Yii::createObject($this->fileClass, [
348 [
349 'fileUrl' => $itr->current(),
350 'fileId' => $fileId
351 ]
352 ]);
353 }
354
355 if ( $type === 'css' ) {
356 $file->registerAsCssFile($options);
357 } else if ( $type === 'js' ) {
358 $file->registerAsJsFile($options);
359 } else if ( is_callable($callback) ) {
360 call_user_func_array($callback, [$file->getUrl(), $options, $file->getId()] );
361 }
362
363 $itr->next();
364 }
365 }
366 }
367