1: <?php namespace Laravel; defined('DS') or die('No direct script access.');
2:
3: use Laravel\Routing\Router;
4: use FilesystemIterator as fIterator;
5:
6: class Bundle {
7:
8: /**
9: * All of the application's bundles.
10: *
11: * @var array
12: */
13: public static $bundles = array();
14:
15: /**
16: * A cache of the parsed bundle elements.
17: *
18: * @var array
19: */
20: public static $elements = array();
21:
22: /**
23: * All of the bundles that have been started.
24: *
25: * @var array
26: */
27: public static $started = array();
28:
29: /**
30: * All of the bundles that have their routes files loaded.
31: *
32: * @var array
33: */
34: public static $routed = array();
35:
36: /**
37: * Register the bundle for the application.
38: *
39: * @param string $bundle
40: * @param array $config
41: * @return void
42: */
43: public static function register($bundle, $config = array())
44: {
45: $defaults = array('handles' => null, 'auto' => false);
46:
47: // If the given configuration is actually a string, we will assume it is a
48: // location and set the bundle name to match it. This is common for most
49: // bundles that simply live in the root bundle directory.
50: if (is_string($config))
51: {
52: $bundle = $config;
53:
54: $config = array('location' => $bundle);
55: }
56:
57: // If no location is set, we will set the location to match the name of
58: // the bundle. This is for bundles that are installed on the root of
59: // the bundle directory so a location was not set.
60: if ( ! isset($config['location']))
61: {
62: $config['location'] = $bundle;
63: }
64:
65: static::$bundles[$bundle] = array_merge($defaults, $config);
66:
67: // It is possible for the developer to specify auto-loader mappings
68: // directly on the bundle registration. This provides a convenient
69: // way to register mappings without a bootstrap.
70: if (isset($config['autoloads']))
71: {
72: static::autoloads($bundle, $config);
73: }
74: }
75:
76: /**
77: * Load a bundle by running its start-up script.
78: *
79: * If the bundle has already been started, no action will be taken.
80: *
81: * @param string $bundle
82: * @return void
83: */
84: public static function start($bundle)
85: {
86: if (static::started($bundle)) return;
87:
88: if ( ! static::exists($bundle))
89: {
90: throw new \Exception("Bundle [$bundle] has not been installed.");
91: }
92:
93: // Each bundle may have a start script which is responsible for preparing
94: // the bundle for use by the application. The start script may register
95: // any classes the bundle uses with the auto-loader class, etc.
96: if ( ! is_null($starter = static::option($bundle, 'starter')))
97: {
98: $starter();
99: }
100: elseif (file_exists($path = static::path($bundle).'start'.EXT))
101: {
102: require $path;
103: }
104:
105: // Each bundle may also have a "routes" file which is responsible for
106: // registering the bundle's routes. This is kept separate from the
107: // start script for reverse routing efficiency purposes.
108: static::routes($bundle);
109:
110: Event::fire("laravel.started: {$bundle}");
111:
112: static::$started[] = strtolower($bundle);
113: }
114:
115: /**
116: * Load the "routes" file for a given bundle.
117: *
118: * @param string $bundle
119: * @return void
120: */
121: public static function routes($bundle)
122: {
123: if (static::routed($bundle)) return;
124:
125: $path = static::path($bundle).'routes'.EXT;
126:
127: // By setting the bundle property on the router, the router knows what
128: // value to replace the (:bundle) place-holder with when the bundle
129: // routes are added, keeping the routes flexible.
130: Router::$bundle = static::option($bundle, 'handles');
131:
132: if ( ! static::routed($bundle) and file_exists($path))
133: {
134: static::$routed[] = $bundle;
135:
136: require $path;
137: }
138: }
139:
140: /**
141: * Register the auto-loading configuration for a bundle.
142: *
143: * @param string $bundle
144: * @param array $config
145: * @return void
146: */
147: protected static function autoloads($bundle, $config)
148: {
149: $path = rtrim(Bundle::path($bundle), DS);
150:
151: foreach ($config['autoloads'] as $type => $mappings)
152: {
153: // When registering each type of mapping we'll replace the (:bundle)
154: // place-holder with the path to the bundle's root directory, so
155: // the developer may dryly register the mappings.
156: $mappings = array_map(function($mapping) use ($path)
157: {
158: return str_replace('(:bundle)', $path, $mapping);
159:
160: }, $mappings);
161:
162: // Once the mappings are formatted, we will call the Autoloader
163: // function matching the mapping type and pass in the array of
164: // mappings so they can be registered and used.
165: Autoloader::$type($mappings);
166: }
167: }
168:
169: /**
170: * Disable a bundle for the current request.
171: *
172: * @param string $bundle
173: * @return void
174: */
175: public static function disable($bundle)
176: {
177: unset(static::$bundles[$bundle]);
178: }
179:
180: /**
181: * Determine which bundle handles the given URI.
182: *
183: * The default bundle is returned if no other bundle is assigned.
184: *
185: * @param string $uri
186: * @return string
187: */
188: public static function handles($uri)
189: {
190: $uri = rtrim($uri, '/').'/';
191:
192: foreach (static::$bundles as $key => $value)
193: {
194: if (isset($value['handles']) and starts_with($uri, $value['handles'].'/') or $value['handles'] == '/')
195: {
196: return $key;
197: }
198: }
199:
200: return DEFAULT_BUNDLE;
201: }
202:
203: /**
204: * Determine if a bundle exists within the bundles directory.
205: *
206: * @param string $bundle
207: * @return bool
208: */
209: public static function exists($bundle)
210: {
211: return $bundle == DEFAULT_BUNDLE or in_array(strtolower($bundle), static::names());
212: }
213:
214: /**
215: * Determine if a given bundle has been started for the request.
216: *
217: * @param string $bundle
218: * @return void
219: */
220: public static function started($bundle)
221: {
222: return in_array(strtolower($bundle), static::$started);
223: }
224:
225: /**
226: * Determine if a given bundle has its routes file loaded.
227: *
228: * @param string $bundle
229: * @return void
230: */
231: public static function routed($bundle)
232: {
233: return in_array(strtolower($bundle), static::$routed);
234: }
235:
236: /**
237: * Get the identifier prefix for the bundle.
238: *
239: * @param string $bundle
240: * @return string
241: */
242: public static function prefix($bundle)
243: {
244: return ($bundle !== DEFAULT_BUNDLE) ? "{$bundle}::" : '';
245: }
246:
247: /**
248: * Get the class prefix for a given bundle.
249: *
250: * @param string $bundle
251: * @return string
252: */
253: public static function class_prefix($bundle)
254: {
255: return ($bundle !== DEFAULT_BUNDLE) ? Str::classify($bundle).'_' : '';
256: }
257:
258: /**
259: * Return the root bundle path for a given bundle.
260: *
261: * <code>
262: * // Returns the bundle path for the "admin" bundle
263: * $path = Bundle::path('admin');
264: *
265: * // Returns the path('app') constant as the default bundle
266: * $path = Bundle::path('application');
267: * </code>
268: *
269: * @param string $bundle
270: * @return string
271: */
272: public static function path($bundle)
273: {
274: if (is_null($bundle) or $bundle === DEFAULT_BUNDLE)
275: {
276: return path('app');
277: }
278: elseif ($location = array_get(static::$bundles, $bundle.'.location'))
279: {
280: // If the bundle location starts with "path: ", we will assume that a raw
281: // path has been specified and will simply return it. Otherwise, we'll
282: // prepend the bundle directory path onto the location and return.
283: if (starts_with($location, 'path: '))
284: {
285: return str_finish(substr($location, 6), DS);
286: }
287: else
288: {
289: return str_finish(path('bundle').$location, DS);
290: }
291: }
292: }
293:
294: /**
295: * Return the root asset path for the given bundle.
296: *
297: * @param string $bundle
298: * @return string
299: */
300: public static function assets($bundle)
301: {
302: if (is_null($bundle)) return static::assets(DEFAULT_BUNDLE);
303:
304: return ($bundle != DEFAULT_BUNDLE) ? "/bundles/{$bundle}/" : '/';
305: }
306:
307: /**
308: * Get the bundle name from a given identifier.
309: *
310: * <code>
311: * // Returns "admin" as the bundle name for the identifier
312: * $bundle = Bundle::name('admin::home.index');
313: * </code>
314: *
315: * @param string $identifier
316: * @return string
317: */
318: public static function name($identifier)
319: {
320: list($bundle, $element) = static::parse($identifier);
321:
322: return $bundle;
323: }
324:
325: /**
326: * Get the element name from a given identifier.
327: *
328: * <code>
329: * // Returns "home.index" as the element name for the identifier
330: * $bundle = Bundle::bundle('admin::home.index');
331: * </code>
332: *
333: * @param string $identifier
334: * @return string
335: */
336: public static function element($identifier)
337: {
338: list($bundle, $element) = static::parse($identifier);
339:
340: return $element;
341: }
342:
343: /**
344: * Reconstruct an identifier from a given bundle and element.
345: *
346: * <code>
347: * // Returns "admin::home.index"
348: * $identifier = Bundle::identifier('admin', 'home.index');
349: *
350: * // Returns "home.index"
351: * $identifier = Bundle::identifier('application', 'home.index');
352: * </code>
353: *
354: * @param string $bundle
355: * @param string $element
356: * @return string
357: */
358: public static function identifier($bundle, $element)
359: {
360: return (is_null($bundle) or $bundle == DEFAULT_BUNDLE) ? $element : $bundle.'::'.$element;
361: }
362:
363: /**
364: * Return the bundle name if it exists, else return the default bundle.
365: *
366: * @param string $bundle
367: * @return string
368: */
369: public static function resolve($bundle)
370: {
371: return (static::exists($bundle)) ? $bundle : DEFAULT_BUNDLE;
372: }
373:
374: /**
375: * Parse an element identifier and return the bundle name and element.
376: *
377: * <code>
378: * // Returns array(null, 'admin.user')
379: * $element = Bundle::parse('admin.user');
380: *
381: * // Parses "admin::user" and returns array('admin', 'user')
382: * $element = Bundle::parse('admin::user');
383: * </code>
384: *
385: * @param string $identifier
386: * @return array
387: */
388: public static function parse($identifier)
389: {
390: // The parsed elements are cached so we don't have to reparse them on each
391: // subsequent request for the parsed element. So if we've already parsed
392: // the given element, we'll just return the cached copy as the value.
393: if (isset(static::$elements[$identifier]))
394: {
395: return static::$elements[$identifier];
396: }
397:
398: if (strpos($identifier, '::') !== false)
399: {
400: $element = explode('::', strtolower($identifier));
401: }
402: // If no bundle is in the identifier, we will insert the default bundle
403: // since classes like Config and Lang organize their items by bundle.
404: // The application folder essentially behaves as a default bundle.
405: else
406: {
407: $element = array(DEFAULT_BUNDLE, strtolower($identifier));
408: }
409:
410: return static::$elements[$identifier] = $element;
411: }
412:
413: /**
414: * Get the information for a given bundle.
415: *
416: * @param string $bundle
417: * @return object
418: */
419: public static function get($bundle)
420: {
421: return array_get(static::$bundles, $bundle);
422: }
423:
424: /**
425: * Get an option for a given bundle.
426: *
427: * @param string $bundle
428: * @param string $option
429: * @param mixed $default
430: * @return mixed
431: */
432: public static function option($bundle, $option, $default = null)
433: {
434: $bundle = static::get($bundle);
435:
436: if (is_null($bundle))
437: {
438: return value($default);
439: }
440:
441: return array_get($bundle, $option, $default);
442: }
443:
444: /**
445: * Get all of the installed bundles for the application.
446: *
447: * @return array
448: */
449: public static function all()
450: {
451: return static::$bundles;
452: }
453:
454: /**
455: * Get all of the installed bundle names.
456: *
457: * @return array
458: */
459: public static function names()
460: {
461: return array_keys(static::$bundles);
462: }
463:
464: /**
465: * Expand given bundle path of form "[bundle::]path/...".
466: *
467: * @param string $path
468: * @return string
469: */
470: public static function expand($path)
471: {
472: list($bundle, $element) = static::parse($path);
473: return static::path($bundle).$element;
474: }
475:
476: }