1: <?php
2: /**
3: * Wei Framework
4: *
5: * @copyright Copyright (c) 2008-2015 Twin Huang
6: * @license http://opensource.org/licenses/mit-license.php MIT License
7: */
8:
9: namespace Wei
10: {
11: /**
12: * @see Wei\Base
13: */
14: require_once 'Base.php';
15:
16: /**
17: * The service container
18: *
19: * @author Twin Huang <twinhuang@qq.com>
20: *
21: * Cache
22: * @property Cache $cache A cache service proxy
23: * @method mixed cache($key, $value = null, $expire = 0) Retrieve or store an item by cache
24: * @property ArrayCache $arrayCache A cache service that stored data in PHP array
25: * @method mixed arrayCache($key, $value = null, $expire = 0) Retrieve or store an item by array cache
26: * @property Apc $apc A cache service that stored data in PHP APC
27: * @method mixed apc($key, $value = null, $expire = 0) Retrieve or store an item
28: * @property DbCache $dbCache A cache service that stored data in databases
29: * @method mixed dbCache($key, $value = null, $expire = 0) Retrieve or store an item by database cache
30: * @property FileCache $fileCache A cache service that stored data in files
31: * @method mixed fileCache($key, $value = null, $expire = 0) Retrieve or store an item by file
32: * @property PhpFileCache $phpFileCache A cache service that stored data as PHP variables in files
33: * @method mixed phpFileCache($key, $value = null, $expire = 0) Retrieve or store an item by file
34: * @property Memcache $memcache A cache service that stored data in Memcache
35: * @method mixed memcache($key, $value = null, $expire = 0) Retrieve or store an item by Memcache
36: * @property Memcached $memcached A cache service that stored data in Memcached
37: * @method mixed memcached($key, $value = null, $expire = 0) Retrieve or store an item by Memcached
38: * @property MongoCache $mongoCache A cache service that stores data in MongoDB
39: * @method mixed mongoCache($key, $value = null, $expire = 0) Retrieve or store an item by MongoDB
40: * @property Couchbase $couchbase A cache service base on Couchbase
41: * @method mixed couchbase($key, $value = null, $expire = 0) Retrieve or store an item by Couchbase
42: * @property Redis $redis A cache service that stores data in Redis
43: * @method mixed redis($key = null, $value = null, $expire = 0) Retrieve or store an item by Redis
44: * @property Bicache $bicache A two-level cache service
45: * @method mixed bicache($key, $value = null, $expire = 0) Retrieve or store an item by two-level cache
46: *
47: * Database
48: * @property Db $db A database service inspired by Doctrine DBAL
49: * @method \Wei\Record db($table = null) Create a new record object
50: *
51: * HTTP Client
52: * @property Http $http An alias of call service
53: * @method \Wei\Http http(array $options) Create a new HTTP service and execute
54: * @property Soap $soap A Soap client that works like HTTP service
55: * @method \Wei\Soap soap(array $options) Create a new Soap service and execute
56: *
57: * HTTP Request
58: * @property Request $request A service that handles the HTTP request data
59: * @method mixed request($name, $default = null) Returns a stringify request parameter value
60: * @property Cookie $cookie A object that handles the HTTP request and response cookies
61: * @method mixed cookie($key, $value = null, $options = array()) Get or set cookie
62: * @property Session $session A object that session parameters ($_SESSION)
63: * @method mixed session($name, $default = null) Returns a stringify session parameter value
64: * @property Ua $ua A object to detect user OS, device and browser name and version
65: * @method bool ua() Check if in the specified browser, OS or device
66: * @property Upload $upload A object that handles the uploaded files
67: * @method bool upload(array $options = array()) Upload a file
68: *
69: * HTTP Response
70: * @property Response $response A object that handles the HTTP response data
71: * @method \Wei\Response response($content = null, $status = null) Send response header and content
72: *
73: * View
74: * @property View $view A object that use to render PHP template
75: * @method string view($name = null, $vars = array()) Returns view object or render a PHP template
76: * @property Asset $asset A service to generate assets' URL
77: * @method string asset($file) Returns the asset URL by specified file
78: * @property E $e A object to escape HTML, javascript, CSS, HTML Attribute and URL for secure output
79: * @method string e($string, $type = 'html') Escapes a string by specified type for secure output
80: *
81: * Application
82: * @property App $app An MVC application service
83: * @method \Wei\App app(array $options = array()) Startup an MVC application
84: * @property WeChatApp $weChatApp A object handles WeChat(WeiXin) callback message
85: * @method \Wei\WeChatApp weChatApp() Start up WeChat application and output the matched rule message
86: * @property Router $router A service that parse the URL to request data
87: * @property Url $url A util object to build URL
88: * @method string url($uri) Build URL by specified uri and parameters
89: *
90: * Other
91: * @property Config $config A object to manage object configurations
92: * @property Counter $counter A counter service
93: * @property Env $env A object to detect the environment name and load configuration by environment name
94: * @method string env() Returns the environment name
95: * @property Error $error A object that handles exception and display pretty exception message
96: * @method \Wei\Error error($fn) Attach a handler to exception error
97: * @property Gravatar $gravatar A object that generates a Gravatar URL for a specified email address
98: * @method string gravatar($email, $size = null, $default = null, $rating = null) Generates a Gravatar URL for a specified email address
99: * @property Lock $lock A service that provide the functionality of exclusive Lock
100: * @method bool lock($key) Acquire a lock key
101: * @property Logger $logger A logger service, which is inspired by Monolog
102: * @method bool logger($level, $message) Logs with an arbitrary level
103: * @property Password $password A wrapper class for password hashing functions
104: * @property Pinyin $pinyin An util object that converts Chinese words to phonetic alphabets
105: * @method string pinyin($word) Converts Chinese words to phonetic alphabets
106: * @property SafeUrl $safeUrl Generate a URL with signature
107: * @property Uuid $uuid A util object that generates a RANDOM UUID(universally unique identifier)
108: * @method string uuid() generates a RANDOM UUID(universally unique identifier)
109: * @property T $t A translator object
110: * @method string t($message, array $parameters = array()) Translate the message
111: *
112: * Validation
113: * @method \Wei\Validate validate(array $option) Create a new validator and validate by specified options
114: *
115: * Data type and composition
116: * @property Validator\Alnum $isAlnum
117: * @method bool isAlnum($input) Check if the input contains letters (a-z) and digits (0-9)
118: * @property Validator\Alpha $isAlpha
119: * @method bool isAlpha($input) Check if the input contains only letters (a-z)
120: * @property Validator\Blank $isBlank
121: * @method bool isBlank($input) Check if the input is blank
122: * @property Validator\Contains $isContains
123: * @method bool isContains($input, $search, $regex = false) Check if the input is contains the specified string or pattern
124: * @property Validator\Decimal $isDecimal
125: * @method bool isDecimal($input) Check if the input is decimal
126: * @property Validator\Digit $isDigit
127: * @method bool isDigit($input) Check if the input contains only digits (0-9)
128: * @property Validator\DivisibleBy $isDivisibleBy
129: * @method bool isDivisibleBy($input, $divisor) Check if the input could be divisible by specified divisor
130: * @property Validator\DoubleByte $isDoubleByte
131: * @method bool isDoubleByte($input) Check if the input contains only double characters
132: * @property Validator\Present $isPresent
133: * @method bool isPresent($input) Check if the input is empty
134: * @property Validator\EndsWith $isEndsWith
135: * @method bool isEndsWith($input, $findMe, $case = false) Check if the input is ends with specified string
136: * @property Validator\In $isIn
137: * @method bool isIn($input, array $array, $strict = false) Check if the input is in specified array
138: * @property Validator\Lowercase $isLowercase
139: * @method bool isLowercase($input) Check if the input is lowercase
140: * @property Validator\Luhn $isLuhn
141: * @method bool isLuhn($input) Check if the input is valid by the Luhn algorithm
142: * @property Validator\NaturalNumber $isNaturalNumber
143: * @method bool isNaturalNumber($input) Check if the input is a natural number (integer that greater than or equals 0)
144: * @property Validator\Null $isNull
145: * @method bool isNull($input) Check if the input is null
146: * @property Validator\Number $isNumber
147: * @method bool isNumber($input) Check if the input is number
148: * @property Validator\PositiveInteger $isPositiveInteger
149: * @method bool isPositiveInteger($input) Check if the input is a positive integer (integer that greater than 0)
150: * @property Validator\Regex $isRegex
151: * @method bool isRegex($input, $pattern) Check if the input is valid by specified regular expression
152: * @property Validator\StartsWith $isStartsWith
153: * @method bool isStartsWith($input, $findMe, $case = false) Check if the input is starts with specified string
154: * @property Validator\Type $isType
155: * @method bool isType($input, $type) Check if the type of input is equals specified type name
156: * @property Validator\Uppercase $isUppercase
157: * @method bool isUppercase($input) Check if the input is uppercase
158: *
159: * Length
160: * @property Validator\Length $isLength
161: * @method bool isLength($input, $length, $max = null) Check if the length (or size) of input is equals specified length or in specified length range
162: * @property Validator\CharLength $isCharLength
163: * @method bool isCharLength($input, $length) Check if the characters length of input is equals specified length
164: * @property Validator\MinLength $isMinLength
165: * @method bool isMinLength($input, $min) Check if the length (or size) of input is greater than specified length
166: * @property Validator\MaxLength $isMaxLength
167: * @method bool isMaxLength($input, $max) Check if the length (or size) of input is lower than specified length
168: *
169: * Comparison
170: * @property Validator\EqualTo $isEqualTo
171: * @method bool isEqualTo($input, $value) Check if the input is equals to (==) the specified value
172: * @property Validator\IdenticalTo $identicalTo
173: * @method bool isIdenticalTo($input, $value) Check if the input is equals to (==) the specified value
174: * @property Validator\GreaterThan $isGreaterThan
175: * @method bool isGreaterThan($input, $value) Check if the input is greater than (>=) the specified value
176: * @property Validator\GreaterThanOrEqual $isGreaterThanOrEqual
177: * @method bool isGreaterThanOrEqual($input, $value) Check if the input is greater than or equal to (>=) the specified value
178: * @property Validator\LessThan $isLessThan
179: * @method bool isLessThan($input, $value) Check if the input is less than (<) the specified value
180: * @property Validator\LessThanOrEqual $isLessThanOrEqual
181: * @method bool isLessThanOrEqual($input, $value) Check if the input is less than or equal to (<=) the specified value
182: * @property Validator\Between $isBetween
183: * @method bool isBetween($input, $min, $max) Check if the input is between the specified minimum and maximum value
184: *
185: * Date and time
186: * @property Validator\Date $isDate
187: * @method bool isDate($input, $format = 'Y-m-d') Check if the input is a valid date
188: * @property Validator\DateTime $isDateTime
189: * @method bool isDateTime($input, $format = null) Check if the input is a valid datetime
190: * @property Validator\Time $isTime
191: * @method bool isTime($input, $format = 'H:i:s') Check if the input is a valid time
192: *
193: * File and directory
194: * @property Validator\Dir $isDir
195: * @method bool isDir($input) Check if the input is existing directory
196: * @property Validator\Exists $isExists
197: * @method bool isExists($input) Check if the input is existing file or directory
198: * @property Validator\File $isFile
199: * @method bool isFile($input, array $options) Check if the input is valid file
200: * @property Validator\Image $isImage
201: * @method bool isImage($input, array $options) Check if the input is valid image
202: *
203: * Network
204: * @property Validator\Email $isEmail
205: * @method bool isEmail($input) Check if the input is valid email address
206: * @property Validator\Ip $isIp
207: * @method bool isIp($input, array $options = array()) Check if the input is valid IP address
208: * @property Validator\Tld $isTld
209: * @method bool isTld($input) Check if the input is a valid top-level domain
210: * @property Validator\Url $isUrl
211: * @method bool isUrl($input, array $options = array()) Check if the input is valid URL address
212: * @property Validator\Uuid $isUuid
213: * @method bool isUuid($input) Check if the input is valid UUID(v4)
214: *
215: * Region
216: * @property Validator\CreditCard $isCreditCard
217: * @method bool isCreditCard($input, $type = null) Check if the input is valid credit card number
218: * @property Validator\Phone $isPhone
219: * @method bool isPhone($input) Check if the input is valid phone number, contains only digit, +, - and spaces
220: * @property Validator\Chinese $isChinese
221: * @method bool isChinese($input) Check if the input contains only Chinese characters
222: * @property Validator\IdCardCn $isIdCardCn
223: * @method bool isIdCardCn($input) Check if the input is valid Chinese identity card
224: * @property Validator\IdCardHk $isIdCardHk
225: * @method bool isIdCardHk($input) Check if the input is valid Hong Kong identity card
226: * @property Validator\IdCardMo $isIdCardMo
227: * @method bool isIdCardMo($input) Check if the input is valid Macau identity card
228: * @property Validator\IdCardTw $isIdCardTw
229: * @method bool isIdCardTw($input) Check if the input is valid Taiwan identity card
230: * @property Validator\PhoneCn $isPhoneCn
231: * @method bool isPhoneCn($input) Check if the input is valid Chinese phone number
232: * @property Validator\PlateNumberCn $isPlateNumberCn
233: * @method bool isPlateNumberCn($input) Check if the input is valid Chinese plate number
234: * @property Validator\PostcodeCn $isPostcodeCn
235: * @method bool isPostcodeCn($input) Check if the input is valid Chinese postcode
236: * @property Validator\QQ $isQQ
237: * @method bool isQQ($input) Check if the input is valid QQ number
238: * @property Validator\MobileCn $isMobileCn
239: * @method bool isMobileCn($input) Check if the input is valid Chinese mobile number
240: *
241: * Group
242: * @property Validator\AllOf $isAllOf
243: * @method bool isAllOf($input, array $rules) Check if the input is valid by all of the rules
244: * @property Validator\NoneOf $isNoneOf
245: * @method bool isNoneOf($input, array $rules) Check if the input is NOT valid by all of specified rules
246: * @property Validator\OneOf $isOneOf
247: * @method bool isOneOf($input, array $rules) Check if the input is valid by any of the rules
248: * @property Validator\SomeOf $isSomeOf
249: * @method bool isSomeOf($input, array $rules, $atLeast) Check if the input is valid by specified number of the rules
250: *
251: * Others
252: * @property Validator\RecordExists $isRecordExists
253: * @method bool isRecordExists($input, $table, $field = 'id') Check if the input is existing table record
254: * @property Validator\All $isAll
255: * @method bool isAll($input, array $rules) Check if all of the element in the input is valid by all specified rules
256: * @property Validator\Callback $isCallback
257: * @method bool isCallback($input, \Closure $fn, $message = null) Check if the input is valid by specified callback
258: * @property Validator\Color $isColor
259: * @method bool isColor($input) Check if the input is valid Hex color
260: * @property Validator\Password $isPassword
261: * @method bool isPassword($input, array $options = array()) Check if the input password is secure enough
262: */
263: class Wei extends Base
264: {
265: /**
266: * Version
267: */
268: const VERSION = '0.9.16';
269:
270: /**
271: * The configurations for all objects
272: *
273: * @var array
274: */
275: protected $configs = array();
276:
277: /**
278: * The name of current application
279: *
280: * @var string
281: */
282: protected $namespace;
283:
284: /**
285: * Whether in debug mode or not
286: *
287: * @var bool
288: */
289: protected $debug = true;
290:
291: /**
292: * The PHP configuration options that will be set when the service container constructing
293: *
294: * @var array
295: * @see http://www.php.net/manual/en/ini.php
296: * @see http://www.php.net/manual/en/function.ini-set.php
297: */
298: protected $inis = array();
299:
300: /**
301: * Whether enable class autoload or not
302: *
303: * @var bool
304: */
305: protected $autoload = true;
306:
307: /**
308: * The directories for autoload
309: *
310: * @var array
311: */
312: protected $autoloadMap = array();
313:
314: /**
315: * The service name to class name map
316: *
317: * @var array
318: */
319: protected $aliases = array();
320:
321: /**
322: * The service provider map
323: *
324: * @var array
325: */
326: protected $providers = array();
327:
328: /**
329: * The import configuration
330: *
331: * Format:
332: * array(
333: * array(
334: * 'dir' => 'lib/Wei/Validator',
335: * 'namespace' => 'Wei\Validator',
336: * 'format' => 'is%s',
337: * 'autoload' => false
338: * ),
339: * array(
340: * 'dir' => 'src/MyProject/Wei',
341: * 'namespace' => 'MyProject\Wei',
342: * 'format' => '%s',
343: * 'autoload' => true
344: * )
345: * );
346: * @var array
347: */
348: protected $import = array();
349:
350: /**
351: * The callback executes *before* service constructed
352: *
353: * @var callable
354: */
355: protected $beforeConstruct;
356:
357: /**
358: * The callback executes *after* service constructed
359: *
360: * @var callable
361: */
362: protected $afterConstruct;
363:
364: /**
365: * The services that will be instanced after service container constructed
366: *
367: * @var array
368: */
369: protected $preload = array();
370:
371: /**
372: * An array contains the instanced services
373: *
374: * @var Base[]
375: */
376: protected $services = array();
377:
378: /**
379: * The current service container
380: *
381: * @var Wei
382: */
383: protected static $container;
384:
385: /**
386: * Instance service container
387: *
388: * @param array $config
389: */
390: public function __construct(array $config = array())
391: {
392: // Set configurations for all services
393: $this->setConfig($config);
394:
395: $this->set('wei', $this);
396: $this->wei = $this;
397:
398: // Set all options
399: $options = get_object_vars($this);
400: if (isset($this->configs['wei'])) {
401: $options = array_merge($options, $this->configs['wei']);
402: }
403: $this->setOption($options);
404: }
405:
406: /**
407: * Get the service container instance
408: *
409: * @param array $config The array or file configuration
410: * @return $this
411: * @throws \InvalidArgumentException When the configuration parameter is not array or file
412: */
413: public static function getContainer($config = array())
414: {
415: // Most of time, it's called after instanced and without any arguments
416: if (!$config && static::$container) {
417: return static::$container;
418: }
419:
420: switch (true) {
421: case is_array($config):
422: break;
423:
424: case is_string($config) && file_exists($config):
425: $config = (array) require $config;
426: break;
427:
428: default:
429: throw new \InvalidArgumentException('Configuration should be array or file', 1010);
430: }
431:
432: if (!isset(static::$container)) {
433: static::$container = new static($config);
434: } else {
435: static::$container->setConfig($config);
436: }
437: return static::$container;
438: }
439:
440: /**
441: * Set the service container
442: *
443: * @param Wei $container
444: */
445: public static function setContainer(Wei $container = null)
446: {
447: static::$container = $container;
448: }
449:
450: /**
451: * Autoload the PSR-0 class
452: *
453: * @param string $class the name of the class
454: * @return bool
455: */
456: public function autoload($class)
457: {
458: $class = strtr($class, array('_' => DIRECTORY_SEPARATOR, '\\' => DIRECTORY_SEPARATOR)) . '.php';
459:
460: foreach ($this->autoloadMap as $prefix => $dir) {
461: // Autoload class from relative path like PSR-4 when prefix starts with "\"
462: if (isset($prefix[0]) && $prefix[0] == '\\' && 0 === strpos($class, ltrim($prefix, '\\'))) {
463: $file = $dir . DIRECTORY_SEPARATOR . substr($class, strlen($prefix));
464: if (file_exists($file)) {
465: require_once $file;
466: return true;
467: }
468: }
469:
470: // Allow empty class prefix
471: if (!$prefix || 0 === strpos($class, $prefix)) {
472: if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $class)) {
473: require_once $file;
474: return true;
475: }
476: }
477: }
478:
479: return false;
480: }
481:
482: /**
483: * Set service's configuration
484: *
485: * @param string|array $name
486: * @param mixed $value
487: * @return $this
488: */
489: public function setConfig($name, $value = null)
490: {
491: // Set array configurations
492: if (is_array($name)) {
493: foreach ($name as $key => $value) {
494: $this->setConfig($key, $value);
495: }
496: return $this;
497: }
498:
499: // Set one configuration
500: $names = explode(':', $name);
501: $first = $names[0];
502: $configs = &$this->configs;
503:
504: foreach ($names as $name) {
505: if (!is_array($configs)) {
506: $configs = array();
507: }
508: if (!isset($configs[$name])) {
509: $configs[$name] = array();
510: }
511: $configs = &$configs[$name];
512: }
513:
514: // Merge only first child node
515: if (is_array($configs) && is_array($value)) {
516: $configs = $value + $configs;
517: } else {
518: $configs = $value;
519: }
520:
521: /**
522: * Automatically create dependence map when configuration key contains "."
523: *
524: * $this->configs = array(
525: * 'mysql.db' => array(),
526: * );
527: * =>
528: * $this->providers['mysqlDb'] = 'mysql.db';
529: */
530: if (false !== strpos($first, '.')) {
531: $parts = explode('.', $first, 2);
532: $serviceName = $parts[0] . ucfirst($parts[1]);
533: if (!isset($this->providers[$serviceName])) {
534: $this->providers[$serviceName] = $first;
535: }
536: }
537:
538: // Set options for existing service
539: if (isset($this->services[$first])) {
540: $this->services[$first]->setOption($value);
541: }
542:
543: return $this;
544: }
545:
546: /**
547: * Get services' configuration
548: *
549: * @param string $name The name of configuration
550: * @param mixed $default The default value if configuration not found
551: * @return mixed
552: */
553: public function getConfig($name = null, $default = null)
554: {
555: if (is_null($name)) {
556: return $this->configs;
557: }
558:
559: if (false === strpos($name, ':')) {
560: return isset($this->configs[$name]) ? $this->configs[$name] : $default;
561: }
562:
563: $configs = &$this->configs;
564: foreach (explode(':', $name) as $key) {
565: if (is_array($configs) && isset($configs[$key])) {
566: $configs = &$configs[$key];
567: } else {
568: return $default;
569: }
570: }
571: return $configs;
572: }
573:
574: /**
575: * Get a service and call its "__invoke" method
576: *
577: * @param string $name The name of the service
578: * @param array $args The arguments for "__invoke" method
579: * @param array $providers The service providers map
580: * @return mixed
581: */
582: public function invoke($name, array $args = array(), $providers = array())
583: {
584: $service = $this->get($name, $providers);
585: return call_user_func_array(array($service, '__invoke'), $args);
586: }
587:
588: /**
589: * Get a service
590: *
591: * @param string $name The name of the service, without class prefix "Wei\"
592: * @param array $options The option properties for service
593: * @param array $providers The dependent configuration
594: * @throws \BadMethodCallException
595: * @return Base
596: */
597: public function get($name, array $options = array(), array $providers = array())
598: {
599: // Resolve the service name in dependent configuration
600: if (isset($providers[$name])) {
601: $name = $providers[$name];
602: }
603:
604: if (isset($this->providers[$name])) {
605: $name = $this->providers[$name];
606: }
607:
608: if (isset($this->services[$name])) {
609: return $this->services[$name];
610: }
611:
612: // Resolve the real service name and the config name($full)
613: $full = $name;
614: if (false !== ($pos = strpos($name, '.'))) {
615: $name = substr($name, $pos + 1);
616: }
617:
618: // Get the service class and instance
619: $class = $this->getClass($name);
620: if (class_exists($class)) {
621: // Trigger the before construct callback
622: $this->beforeConstruct && call_user_func($this->beforeConstruct, $this, $full, $name);
623:
624: // Load the service configuration and make sure "wei" option at first
625: $customOptions = $options + (array)$this->getConfig($full);
626: $options = array('wei' => $this) + (!isset($customOptions['namespace']) ? array('namespace' => $this->namespace) : array()) + $customOptions;
627: $this->services[$full] = new $class($options);
628:
629: // Trigger the after construct callback
630: $this->afterConstruct && call_user_func($this->afterConstruct, $this, $full, $name, $this->services[$full]);
631:
632: return $this->services[$full];
633: }
634:
635: // Build the error message
636: $traces = debug_backtrace();
637:
638: // $wei->notFound()
639: if (isset($traces[3]) && $name == $traces[3]['function']) {
640: // For call_user_func/call_user_func_array
641: $file = isset($traces[3]['file']) ? $traces[3]['file'] : $traces[4]['file'];
642: $line = isset($traces[3]['line']) ? $traces[3]['line'] : $traces[4]['line'];
643: throw new \BadMethodCallException(sprintf('Method "%s->%2$s" or object "%s" (class "%s") not found, called in file "%s" at line %s', $traces[3]['class'], $traces[3]['function'], $class, $file, $line), 1011);
644: // $wei->notFound
645: } elseif (isset($traces[1]) && '__get' == $traces[1]['function'] && $name == $traces[1]['args'][0]) {
646: throw new \BadMethodCallException(sprintf('Property or object "%s" (class "%s") not found, called in file "%s" at line %s', $traces[1]['args'][0], $class, $traces[1]['file'], $traces[1]['line']), 1012);
647: // $wei->get('notFound');
648: } else {
649: throw new \BadMethodCallException(sprintf('Property or method "%s" not found', $name), 1013);
650: }
651: }
652:
653: /**
654: * Returns all instanced services
655: *
656: * @return Base[]
657: */
658: public function getServices()
659: {
660: return $this->services;
661: }
662:
663: /**
664: * Check if the service is instanced
665: *
666: * @param string $name The name of service
667: * @return bool
668: */
669: public function isInstanced($name)
670: {
671: return isset($this->services[$name]);
672: }
673:
674: /**
675: * Initialize a new instance of service, with the specified name
676: *
677: * @param string $name The name of the service
678: * @param array $options The option properties for service
679: * @param array $providers The dependent configuration
680: * @return Base The instanced service
681: */
682: public function newInstance($name, array $options = array(), array $providers = array())
683: {
684: $name .= uniqid() . '.' . $name;
685: return $this->wei->get($name, $options, $providers);
686: }
687:
688: /**
689: * Add a service to the service container
690: *
691: * @param string $name The name of service
692: * @param object $service The object of service
693: * @return $this
694: */
695: public function set($name, $service)
696: {
697: $this->services[$name] = $service;
698: return $this;
699: }
700:
701: /**
702: * Remove the service by the specified name
703: *
704: * @param string $name The name of service
705: * @return bool
706: */
707: public function remove($name)
708: {
709: if (isset($this->services[$name])) {
710: unset($this->services[$name]);
711: return true;
712: }
713: return false;
714: }
715:
716: /**
717: * Get the service class by the given name
718: *
719: * @param string $name The name of service
720: * @return string
721: */
722: public function getClass($name)
723: {
724: if (isset($this->aliases[$name])) {
725: $class = $this->aliases[$name];
726: } elseif ('is' == substr($name, 0, 2) && strlen($name) > 2) {
727: $class = 'Wei\Validator\\' . ucfirst(substr($name, 2));
728: } else {
729: $class = 'Wei\\' . ucfirst($name);
730: }
731: return $class;
732: }
733:
734: /**
735: * Check if the service exists by the specified name, if the service exists,
736: * returns the full class name, else return false
737: *
738: * @param string $name The name of service
739: * @return string|false
740: */
741: public function has($name)
742: {
743: $class = $this->getClass($name);
744: return class_exists($class) ? $class : false;
745: }
746:
747: /**
748: * Set debug flag
749: *
750: * @param $bool
751: * @return $this
752: */
753: public function setDebug($bool)
754: {
755: $this->debug = (bool) $bool;
756: return $this;
757: }
758:
759: /**
760: * Whether in debug mode or not
761: *
762: * @return bool
763: */
764: public function isDebug()
765: {
766: return $this->debug;
767: }
768:
769: /**
770: * Whether enable autoload or not
771: *
772: * @param bool $enable
773: * @return $this
774: */
775: public function setAutoload($enable)
776: {
777: $this->autoload = (bool) $enable;
778: call_user_func(
779: $enable ? 'spl_autoload_register' : 'spl_autoload_unregister',
780: array($this, 'autoload')
781: );
782: return $this;
783: }
784:
785: /**
786: * Set autoload directories for autoload method
787: *
788: * @param array $map
789: * @throws \InvalidArgumentException
790: * @return $this
791: */
792: public function setAutoloadMap(array $map)
793: {
794: foreach ($map as &$dir) {
795: if (!is_dir($dir)) {
796: throw new \InvalidArgumentException(sprintf('Directory "%s" for autoloading is not found', $dir));
797: }
798: $dir = realpath($dir);
799: }
800:
801: // Automatic add PSR-4 autoloading for "\Wei" namespace
802: $map['\Wei'] = __DIR__;
803:
804: $this->autoloadMap = $map;
805: return $this;
806: }
807:
808: /**
809: * Sets the value of PHP configuration options
810: *
811: * @param array $inis
812: * @return $this
813: */
814: public function setInis(array $inis)
815: {
816: $this->inis = $inis + $this->inis;
817: foreach ($inis as $key => $value) {
818: ini_set($key, $value);
819: }
820: return $this;
821: }
822:
823: /**
824: * Merge service aliases
825: *
826: * @param array $aliases
827: * @return $this
828: */
829: public function setAliases(array $aliases)
830: {
831: $this->aliases = $aliases + $this->aliases;
832: return $this;
833: }
834:
835: /**
836: * Set service alias
837: *
838: * @param string $name The name of service
839: * @param string $class The class that the service reference to
840: * @return $this
841: */
842: public function setAlias($name, $class)
843: {
844: $this->aliases[$name] = $class;
845: return $this;
846: }
847:
848: /**
849: * Add a service to the service container
850: *
851: * @param string $name The name of service
852: * @param object $service The service service
853: * @return $this
854: */
855: public function __set($name, $service)
856: {
857: return $this->set($name, $service);
858: }
859:
860: /**
861: * Import classes in the specified directory as services
862: *
863: * @param string $dir The directory to search class
864: * @param string $namespace The prefix namespace of the class
865: * @param null $format The service name format, eg 'is%s'
866: * @param bool $autoload Whether add namespace and directory to `autoloadMap` or nor
867: * @throws \InvalidArgumentException When the first parameter is not a directory
868: * @return $this
869: */
870: public function import($dir, $namespace, $format = null, $autoload = false)
871: {
872: if (!is_dir($dir)) {
873: throw new \InvalidArgumentException(sprintf('Fail to import classes from non-exists directory "%s"', $dir), 1014);
874: }
875:
876: if ($autoload) {
877: $this->autoloadMap[$namespace] = dirname($dir);
878: }
879:
880: $files = glob($dir . '/*.php') ?: array();
881: foreach ($files as $file) {
882: $class = substr(basename($file), 0, -4);
883: $name = $format ? sprintf($format, $class) : $class;
884: $this->aliases[lcfirst($name)] = $namespace . '\\' . $class;
885: }
886:
887: return $this;
888: }
889:
890: /**
891: * Set the name of current application
892: *
893: * @param string $namespace
894: * @return $this
895: */
896: public function setNamespace($namespace)
897: {
898: $this->namespace = $namespace;
899: return $this;
900: }
901:
902: /**
903: * Returns the name of current application
904: *
905: * @return string
906: */
907: public function getNamespace()
908: {
909: return $this->namespace;
910: }
911:
912: /**
913: * Set import services
914: *
915: * @param array $import
916: * @return $this
917: */
918: protected function setImport(array $import = array())
919: {
920: $this->import = $import + $this->import;
921: foreach ($import as $option) {
922: $option += array(
923: 'dir' => null,
924: 'namespace' => null,
925: 'format' => null,
926: 'autoload' => false,
927: );
928: $this->import($option['dir'], $option['namespace'], $option['format'], $option['autoload']);
929: }
930: }
931:
932: /**
933: * Merge the dependence map
934: *
935: * @param array $providers
936: */
937: protected function setProviders(array $providers)
938: {
939: $this->providers = $providers + $this->providers;
940: }
941:
942: /**
943: * Instance preload services
944: *
945: * @param array $preload
946: */
947: protected function setPreload(array $preload)
948: {
949: $this->preload = array_merge($this->preload, $preload);
950: foreach ($preload as $service) {
951: $this->set($service, $this->get($service));
952: }
953: }
954:
955: /**
956: * Merge service objects
957: *
958: * @param array $services
959: */
960: protected function setServices(array $services)
961: {
962: $this->services = $services + $this->services;
963: }
964: }
965: }
966:
967: /**
968: * Define function in global namespace
969: */
970: namespace
971: {
972: /**
973: * Get the service container instance
974: *
975: * @return Wei\Wei
976: */
977: function wei()
978: {
979: return call_user_func_array(array('Wei\Wei', 'getContainer'), func_get_args());
980: }
981:
982: /**
983: * Get the service container instance
984: *
985: * @return Wei\Wei
986: * @deprecated Remove in 0.9.9
987: */
988: function widget()
989: {
990: return call_user_func_array(array('Wei\Wei', 'getContainer'), func_get_args());
991: }
992: }
993: