1: <?php namespace Illuminate\Support;
2:
3: use ReflectionClass;
4:
5: abstract class ServiceProvider {
6:
7: /**
8: * The application instance.
9: *
10: * @var \Illuminate\Foundation\Application
11: */
12: protected $app;
13:
14: /**
15: * Indicates if loading of the provider is deferred.
16: *
17: * @var bool
18: */
19: protected $defer = false;
20:
21: /**
22: * Create a new service provider instance.
23: *
24: * @param \Illuminate\Foundation\Application $app
25: * @return void
26: */
27: public function __construct($app)
28: {
29: $this->app = $app;
30: }
31:
32: /**
33: * Bootstrap the application events.
34: *
35: * @return void
36: */
37: public function boot() {}
38:
39: /**
40: * Register the service provider.
41: *
42: * @return void
43: */
44: abstract public function register();
45:
46: /**
47: * Register the package's component namespaces.
48: *
49: * @param string $package
50: * @param string $namespace
51: * @param string $path
52: * @return void
53: */
54: public function package($package, $namespace = null, $path = null)
55: {
56: $namespace = $this->getPackageNamespace($package, $namespace);
57:
58: // In this method we will register the configuration package for the package
59: // so that the configuration options cleanly cascade into the application
60: // folder to make the developers lives much easier in maintaining them.
61: $path = $path ?: $this->guessPackagePath();
62:
63: $config = $path.'/config';
64:
65: if ($this->app['files']->isDirectory($config))
66: {
67: $this->app['config']->package($package, $config, $namespace);
68: }
69:
70: // Next we will check for any "language" components. If language files exist
71: // we will register them with this given package's namespace so that they
72: // may be accessed using the translation facilities of the application.
73: $lang = $path.'/lang';
74:
75: if ($this->app['files']->isDirectory($lang))
76: {
77: $this->app['translator']->addNamespace($namespace, $lang);
78: }
79:
80: // Next, we will see if the application view folder contains a folder for the
81: // package and namespace. If it does, we'll give that folder precedence on
82: // the loader list for the views so the package views can be overridden.
83: $appView = $this->getAppViewPath($package, $namespace);
84:
85: if ($this->app['files']->isDirectory($appView))
86: {
87: $this->app['view']->addNamespace($namespace, $appView);
88: }
89:
90: // Finally we will register the view namespace so that we can access each of
91: // the views available in this package. We use a standard convention when
92: // registering the paths to every package's views and other components.
93: $view = $path.'/views';
94:
95: if ($this->app['files']->isDirectory($view))
96: {
97: $this->app['view']->addNamespace($namespace, $view);
98: }
99: }
100:
101: /**
102: * Guess the package path for the provider.
103: *
104: * @return string
105: */
106: public function guessPackagePath()
107: {
108: $reflect = new ReflectionClass($this);
109:
110: // We want to get the class that is closest to this base class in the chain of
111: // classes extending it. That should be the original service provider given
112: // by the package and should allow us to guess the location of resources.
113: $chain = $this->getClassChain($reflect);
114:
115: $path = $chain[count($chain) - 2]->getFileName();
116:
117: return realpath(dirname($path).'/../../');
118: }
119:
120: /**
121: * Get a class from the ReflectionClass inheritance chain.
122: *
123: * @param ReflectionClass $reflection
124: * @return array
125: */
126: protected function getClassChain(ReflectionClass $reflect)
127: {
128: $classes = array();
129:
130: // This loop essentially walks the inheritance chain of the classes starting
131: // at the most "childish" class and walks back up to this class. Once we
132: // get to the end of the chain we will bail out and return the offset.
133: while ($reflect !== false)
134: {
135: $classes[] = $reflect;
136:
137: $reflect = $reflect->getParentClass();
138: }
139:
140: return $classes;
141: }
142:
143: /**
144: * Determine the namespace for a package.
145: *
146: * @param string $package
147: * @param string $namespace
148: * @return string
149: */
150: protected function getPackageNamespace($package, $namespace)
151: {
152: if (is_null($namespace))
153: {
154: list($vendor, $namespace) = explode('/', $package);
155: }
156:
157: return $namespace;
158: }
159:
160: /**
161: * Register the package's custom Artisan commands.
162: *
163: * @param array $commands
164: * @return void
165: */
166: public function commands($commands)
167: {
168: $commands = is_array($commands) ? $commands : func_get_args();
169:
170: // To register the commands with Artisan, we will grab each of the arguments
171: // passed into the method and listen for Artisan "start" event which will
172: // give us the Artisan console instance which we will give commands to.
173: $events = $this->app['events'];
174:
175: $events->listen('artisan.start', function($artisan) use ($commands)
176: {
177: $artisan->resolveCommands($commands);
178: });
179: }
180:
181: /**
182: * Get the application package view path.
183: *
184: * @param string $package
185: * @param string $namespace
186: * @return string
187: */
188: protected function getAppViewPath($package, $namespace)
189: {
190: return $this->app['path']."/views/packages/{$package}/{$namespace}";
191: }
192:
193: /**
194: * Get the services provided by the provider.
195: *
196: * @return array
197: */
198: public function provides()
199: {
200: return array();
201: }
202:
203: /**
204: * Determine if the provider is deferred.
205: *
206: * @return bool
207: */
208: public function isDeferred()
209: {
210: return $this->defer;
211: }
212:
213: }
214: