PageRenderTime 49ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/framework/base/CComponent.php

https://bitbucket.org/dinhtrung/yiicorecms/
PHP | 698 lines | 294 code | 30 blank | 374 comment | 81 complexity | 7525eda6d9a1460bcdbeb14ad84942d6 MD5 | raw file
Possible License(s): GPL-3.0, BSD-3-Clause, CC0-1.0, BSD-2-Clause, GPL-2.0, LGPL-2.1, LGPL-3.0
  1. <?php
  2. /**
  3. * This file contains the foundation classes for component-based and event-driven programming.
  4. *
  5. * @author Qiang Xue <qiang.xue@gmail.com>
  6. * @link http://www.yiiframework.com/
  7. * @copyright Copyright &copy; 2008-2011 Yii Software LLC
  8. * @license http://www.yiiframework.com/license/
  9. */
  10. /**
  11. * CComponent is the base class for all components.
  12. *
  13. * CComponent implements the protocol of defining, using properties and events.
  14. *
  15. * A property is defined by a getter method, and/or a setter method.
  16. * Properties can be accessed in the way like accessing normal object members.
  17. * Reading or writing a property will cause the invocation of the corresponding
  18. * getter or setter method, e.g
  19. * <pre>
  20. * $a=$component->text; // equivalent to $a=$component->getText();
  21. * $component->text='abc'; // equivalent to $component->setText('abc');
  22. * </pre>
  23. * The signatures of getter and setter methods are as follows,
  24. * <pre>
  25. * // getter, defines a readable property 'text'
  26. * public function getText() { ... }
  27. * // setter, defines a writable property 'text' with $value to be set to the property
  28. * public function setText($value) { ... }
  29. * </pre>
  30. *
  31. * An event is defined by the presence of a method whose name starts with 'on'.
  32. * The event name is the method name. When an event is raised, functions
  33. * (called event handlers) attached to the event will be invoked automatically.
  34. *
  35. * An event can be raised by calling {@link raiseEvent} method, upon which
  36. * the attached event handlers will be invoked automatically in the order they
  37. * are attached to the event. Event handlers must have the following signature,
  38. * <pre>
  39. * function eventHandler($event) { ... }
  40. * </pre>
  41. * where $event includes parameters associated with the event.
  42. *
  43. * To attach an event handler to an event, see {@link attachEventHandler}.
  44. * You can also use the following syntax:
  45. * <pre>
  46. * $component->onClick=$callback; // or $component->onClick->add($callback);
  47. * </pre>
  48. * where $callback refers to a valid PHP callback. Below we show some callback examples:
  49. * <pre>
  50. * 'handleOnClick' // handleOnClick() is a global function
  51. * array($object,'handleOnClick') // using $object->handleOnClick()
  52. * array('Page','handleOnClick') // using Page::handleOnClick()
  53. * </pre>
  54. *
  55. * To raise an event, use {@link raiseEvent}. The on-method defining an event is
  56. * commonly written like the following:
  57. * <pre>
  58. * public function onClick($event)
  59. * {
  60. * $this->raiseEvent('onClick',$event);
  61. * }
  62. * </pre>
  63. * where <code>$event</code> is an instance of {@link CEvent} or its child class.
  64. * One can then raise the event by calling the on-method instead of {@link raiseEvent} directly.
  65. *
  66. * Both property names and event names are case-insensitive.
  67. *
  68. * Starting from version 1.0.2, CComponent supports behaviors. A behavior is an
  69. * instance of {@link IBehavior} which is attached to a component. The methods of
  70. * the behavior can be invoked as if they belong to the component. Multiple behaviors
  71. * can be attached to the same component.
  72. *
  73. * To attach a behavior to a component, call {@link attachBehavior}; and to detach the behavior
  74. * from the component, call {@link detachBehavior}.
  75. *
  76. * A behavior can be temporarily enabled or disabled by calling {@link enableBehavior}
  77. * or {@link disableBehavior}, respectively. When disabled, the behavior methods cannot
  78. * be invoked via the component.
  79. *
  80. * Starting from version 1.1.0, a behavior's properties (either its public member variables or
  81. * its properties defined via getters and/or setters) can be accessed through the component it
  82. * is attached to.
  83. *
  84. * @author Qiang Xue <qiang.xue@gmail.com>
  85. * @version $Id: CComponent.php 3204 2011-05-05 21:36:32Z alexander.makarow $
  86. * @package system.base
  87. * @since 1.0
  88. */
  89. class CComponent
  90. {
  91. private $_e;
  92. private $_m;
  93. /**
  94. * Returns a property value, an event handler list or a behavior based on its name.
  95. * Do not call this method. This is a PHP magic method that we override
  96. * to allow using the following syntax to read a property or obtain event handlers:
  97. * <pre>
  98. * $value=$component->propertyName;
  99. * $handlers=$component->eventName;
  100. * </pre>
  101. * @param string $name the property name or event name
  102. * @return mixed the property value, event handlers attached to the event, or the named behavior (since version 1.0.2)
  103. * @throws CException if the property or event is not defined
  104. * @see __set
  105. */
  106. public function __get($name)
  107. {
  108. $getter='get'.$name;
  109. if(method_exists($this,$getter))
  110. return $this->$getter();
  111. else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
  112. {
  113. // duplicating getEventHandlers() here for performance
  114. $name=strtolower($name);
  115. if(!isset($this->_e[$name]))
  116. $this->_e[$name]=new CList;
  117. return $this->_e[$name];
  118. }
  119. else if(isset($this->_m[$name]))
  120. return $this->_m[$name];
  121. else if(is_array($this->_m))
  122. {
  123. foreach($this->_m as $object)
  124. {
  125. if($object->getEnabled() && (property_exists($object,$name) || $object->canGetProperty($name)))
  126. return $object->$name;
  127. }
  128. }
  129. throw new CException(Yii::t('yii','Property "{class}.{property}" is not defined.',
  130. array('{class}'=>get_class($this), '{property}'=>$name)));
  131. }
  132. /**
  133. * Sets value of a component property.
  134. * Do not call this method. This is a PHP magic method that we override
  135. * to allow using the following syntax to set a property or attach an event handler
  136. * <pre>
  137. * $this->propertyName=$value;
  138. * $this->eventName=$callback;
  139. * </pre>
  140. * @param string $name the property name or the event name
  141. * @param mixed $value the property value or callback
  142. * @return mixed
  143. * @throws CException if the property/event is not defined or the property is read only.
  144. * @see __get
  145. */
  146. public function __set($name,$value)
  147. {
  148. $setter='set'.$name;
  149. if(method_exists($this,$setter))
  150. return $this->$setter($value);
  151. else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
  152. {
  153. // duplicating getEventHandlers() here for performance
  154. $name=strtolower($name);
  155. if(!isset($this->_e[$name]))
  156. $this->_e[$name]=new CList;
  157. return $this->_e[$name]->add($value);
  158. }
  159. else if(is_array($this->_m))
  160. {
  161. foreach($this->_m as $object)
  162. {
  163. if($object->getEnabled() && (property_exists($object,$name) || $object->canSetProperty($name)))
  164. return $object->$name=$value;
  165. }
  166. }
  167. if(method_exists($this,'get'.$name))
  168. throw new CException(Yii::t('yii','Property "{class}.{property}" is read only.',
  169. array('{class}'=>get_class($this), '{property}'=>$name)));
  170. else
  171. throw new CException(Yii::t('yii','Property "{class}.{property}" is not defined.',
  172. array('{class}'=>get_class($this), '{property}'=>$name)));
  173. }
  174. /**
  175. * Checks if a property value is null.
  176. * Do not call this method. This is a PHP magic method that we override
  177. * to allow using isset() to detect if a component property is set or not.
  178. * @param string $name the property name or the event name
  179. * @return boolean
  180. * @since 1.0.1
  181. */
  182. public function __isset($name)
  183. {
  184. $getter='get'.$name;
  185. if(method_exists($this,$getter))
  186. return $this->$getter()!==null;
  187. else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
  188. {
  189. $name=strtolower($name);
  190. return isset($this->_e[$name]) && $this->_e[$name]->getCount();
  191. }
  192. else if(is_array($this->_m))
  193. {
  194. if(isset($this->_m[$name]))
  195. return true;
  196. foreach($this->_m as $object)
  197. {
  198. if($object->getEnabled() && (property_exists($object,$name) || $object->canGetProperty($name)))
  199. return true;
  200. }
  201. }
  202. return false;
  203. }
  204. /**
  205. * Sets a component property to be null.
  206. * Do not call this method. This is a PHP magic method that we override
  207. * to allow using unset() to set a component property to be null.
  208. * @param string $name the property name or the event name
  209. * @throws CException if the property is read only.
  210. * @return mixed
  211. * @since 1.0.1
  212. */
  213. public function __unset($name)
  214. {
  215. $setter='set'.$name;
  216. if(method_exists($this,$setter))
  217. $this->$setter(null);
  218. else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
  219. unset($this->_e[strtolower($name)]);
  220. else if(is_array($this->_m))
  221. {
  222. if(isset($this->_m[$name]))
  223. $this->detachBehavior($name);
  224. else
  225. {
  226. foreach($this->_m as $object)
  227. {
  228. if($object->getEnabled())
  229. {
  230. if(property_exists($object,$name))
  231. return $object->$name=null;
  232. else if($object->canSetProperty($name))
  233. return $object->$setter(null);
  234. }
  235. }
  236. }
  237. }
  238. else if(method_exists($this,'get'.$name))
  239. throw new CException(Yii::t('yii','Property "{class}.{property}" is read only.',
  240. array('{class}'=>get_class($this), '{property}'=>$name)));
  241. }
  242. /**
  243. * Calls the named method which is not a class method.
  244. * Do not call this method. This is a PHP magic method that we override
  245. * to implement the behavior feature.
  246. * @param string $name the method name
  247. * @param array $parameters method parameters
  248. * @return mixed the method return value
  249. * @since 1.0.2
  250. */
  251. public function __call($name,$parameters)
  252. {
  253. if($this->_m!==null)
  254. {
  255. foreach($this->_m as $object)
  256. {
  257. if($object->getEnabled() && method_exists($object,$name))
  258. return call_user_func_array(array($object,$name),$parameters);
  259. }
  260. }
  261. if(class_exists('Closure', false) && $this->canGetProperty($name) && $this->$name instanceof Closure)
  262. return call_user_func_array($this->$name, $parameters);
  263. throw new CException(Yii::t('yii','{class} and its behaviors do not have a method or closure named "{name}".',
  264. array('{class}'=>get_class($this), '{name}'=>$name)));
  265. }
  266. /**
  267. * Returns the named behavior object.
  268. * The name 'asa' stands for 'as a'.
  269. * @param string $behavior the behavior name
  270. * @return IBehavior the behavior object, or null if the behavior does not exist
  271. * @since 1.0.2
  272. */
  273. public function asa($behavior)
  274. {
  275. return isset($this->_m[$behavior]) ? $this->_m[$behavior] : null;
  276. }
  277. /**
  278. * Attaches a list of behaviors to the component.
  279. * Each behavior is indexed by its name and should be an instance of
  280. * {@link IBehavior}, a string specifying the behavior class, or an
  281. * array of the following structure:
  282. * <pre>
  283. * array(
  284. * 'class'=>'path.to.BehaviorClass',
  285. * 'property1'=>'value1',
  286. * 'property2'=>'value2',
  287. * )
  288. * </pre>
  289. * @param array $behaviors list of behaviors to be attached to the component
  290. * @since 1.0.2
  291. */
  292. public function attachBehaviors($behaviors)
  293. {
  294. foreach($behaviors as $name=>$behavior)
  295. $this->attachBehavior($name,$behavior);
  296. }
  297. /**
  298. * Detaches all behaviors from the component.
  299. * @since 1.0.2
  300. */
  301. public function detachBehaviors()
  302. {
  303. if($this->_m!==null)
  304. {
  305. foreach($this->_m as $name=>$behavior)
  306. $this->detachBehavior($name);
  307. $this->_m=null;
  308. }
  309. }
  310. /**
  311. * Attaches a behavior to this component.
  312. * This method will create the behavior object based on the given
  313. * configuration. After that, the behavior object will be initialized
  314. * by calling its {@link IBehavior::attach} method.
  315. * @param string $name the behavior's name. It should uniquely identify this behavior.
  316. * @param mixed $behavior the behavior configuration. This is passed as the first
  317. * parameter to {@link YiiBase::createComponent} to create the behavior object.
  318. * @return IBehavior the behavior object
  319. * @since 1.0.2
  320. */
  321. public function attachBehavior($name,$behavior)
  322. {
  323. if(!($behavior instanceof IBehavior))
  324. $behavior=Yii::createComponent($behavior);
  325. $behavior->setEnabled(true);
  326. $behavior->attach($this);
  327. return $this->_m[$name]=$behavior;
  328. }
  329. /**
  330. * Detaches a behavior from the component.
  331. * The behavior's {@link IBehavior::detach} method will be invoked.
  332. * @param string $name the behavior's name. It uniquely identifies the behavior.
  333. * @return IBehavior the detached behavior. Null if the behavior does not exist.
  334. * @since 1.0.2
  335. */
  336. public function detachBehavior($name)
  337. {
  338. if(isset($this->_m[$name]))
  339. {
  340. $this->_m[$name]->detach($this);
  341. $behavior=$this->_m[$name];
  342. unset($this->_m[$name]);
  343. return $behavior;
  344. }
  345. }
  346. /**
  347. * Enables all behaviors attached to this component.
  348. * @since 1.0.2
  349. */
  350. public function enableBehaviors()
  351. {
  352. if($this->_m!==null)
  353. {
  354. foreach($this->_m as $behavior)
  355. $behavior->setEnabled(true);
  356. }
  357. }
  358. /**
  359. * Disables all behaviors attached to this component.
  360. * @since 1.0.2
  361. */
  362. public function disableBehaviors()
  363. {
  364. if($this->_m!==null)
  365. {
  366. foreach($this->_m as $behavior)
  367. $behavior->setEnabled(false);
  368. }
  369. }
  370. /**
  371. * Enables an attached behavior.
  372. * A behavior is only effective when it is enabled.
  373. * A behavior is enabled when first attached.
  374. * @param string $name the behavior's name. It uniquely identifies the behavior.
  375. * @since 1.0.2
  376. */
  377. public function enableBehavior($name)
  378. {
  379. if(isset($this->_m[$name]))
  380. $this->_m[$name]->setEnabled(true);
  381. }
  382. /**
  383. * Disables an attached behavior.
  384. * A behavior is only effective when it is enabled.
  385. * @param string $name the behavior's name. It uniquely identifies the behavior.
  386. * @since 1.0.2
  387. */
  388. public function disableBehavior($name)
  389. {
  390. if(isset($this->_m[$name]))
  391. $this->_m[$name]->setEnabled(false);
  392. }
  393. /**
  394. * Determines whether a property is defined.
  395. * A property is defined if there is a getter or setter method
  396. * defined in the class. Note, property names are case-insensitive.
  397. * @param string $name the property name
  398. * @return boolean whether the property is defined
  399. * @see canGetProperty
  400. * @see canSetProperty
  401. */
  402. public function hasProperty($name)
  403. {
  404. return method_exists($this,'get'.$name) || method_exists($this,'set'.$name);
  405. }
  406. /**
  407. * Determines whether a property can be read.
  408. * A property can be read if the class has a getter method
  409. * for the property name. Note, property name is case-insensitive.
  410. * @param string $name the property name
  411. * @return boolean whether the property can be read
  412. * @see canSetProperty
  413. */
  414. public function canGetProperty($name)
  415. {
  416. return method_exists($this,'get'.$name);
  417. }
  418. /**
  419. * Determines whether a property can be set.
  420. * A property can be written if the class has a setter method
  421. * for the property name. Note, property name is case-insensitive.
  422. * @param string $name the property name
  423. * @return boolean whether the property can be written
  424. * @see canGetProperty
  425. */
  426. public function canSetProperty($name)
  427. {
  428. return method_exists($this,'set'.$name);
  429. }
  430. /**
  431. * Determines whether an event is defined.
  432. * An event is defined if the class has a method named like 'onXXX'.
  433. * Note, event name is case-insensitive.
  434. * @param string $name the event name
  435. * @return boolean whether an event is defined
  436. */
  437. public function hasEvent($name)
  438. {
  439. return !strncasecmp($name,'on',2) && method_exists($this,$name);
  440. }
  441. /**
  442. * Checks whether the named event has attached handlers.
  443. * @param string $name the event name
  444. * @return boolean whether an event has been attached one or several handlers
  445. */
  446. public function hasEventHandler($name)
  447. {
  448. $name=strtolower($name);
  449. return isset($this->_e[$name]) && $this->_e[$name]->getCount()>0;
  450. }
  451. /**
  452. * Returns the list of attached event handlers for an event.
  453. * @param string $name the event name
  454. * @return CList list of attached event handlers for the event
  455. * @throws CException if the event is not defined
  456. */
  457. public function getEventHandlers($name)
  458. {
  459. if($this->hasEvent($name))
  460. {
  461. $name=strtolower($name);
  462. if(!isset($this->_e[$name]))
  463. $this->_e[$name]=new CList;
  464. return $this->_e[$name];
  465. }
  466. else
  467. throw new CException(Yii::t('yii','Event "{class}.{event}" is not defined.',
  468. array('{class}'=>get_class($this), '{event}'=>$name)));
  469. }
  470. /**
  471. * Attaches an event handler to an event.
  472. *
  473. * An event handler must be a valid PHP callback, i.e., a string referring to
  474. * a global function name, or an array containing two elements with
  475. * the first element being an object and the second element a method name
  476. * of the object.
  477. *
  478. * An event handler must be defined with the following signature,
  479. * <pre>
  480. * function handlerName($event) {}
  481. * </pre>
  482. * where $event includes parameters associated with the event.
  483. *
  484. * This is a convenient method of attaching a handler to an event.
  485. * It is equivalent to the following code:
  486. * <pre>
  487. * $component->getEventHandlers($eventName)->add($eventHandler);
  488. * </pre>
  489. *
  490. * Using {@link getEventHandlers}, one can also specify the excution order
  491. * of multiple handlers attaching to the same event. For example:
  492. * <pre>
  493. * $component->getEventHandlers($eventName)->insertAt(0,$eventHandler);
  494. * </pre>
  495. * makes the handler to be invoked first.
  496. *
  497. * @param string $name the event name
  498. * @param callback $handler the event handler
  499. * @throws CException if the event is not defined
  500. * @see detachEventHandler
  501. */
  502. public function attachEventHandler($name,$handler)
  503. {
  504. $this->getEventHandlers($name)->add($handler);
  505. }
  506. /**
  507. * Detaches an existing event handler.
  508. * This method is the opposite of {@link attachEventHandler}.
  509. * @param string $name event name
  510. * @param callback $handler the event handler to be removed
  511. * @return boolean if the detachment process is successful
  512. * @see attachEventHandler
  513. */
  514. public function detachEventHandler($name,$handler)
  515. {
  516. if($this->hasEventHandler($name))
  517. return $this->getEventHandlers($name)->remove($handler)!==false;
  518. else
  519. return false;
  520. }
  521. /**
  522. * Raises an event.
  523. * This method represents the happening of an event. It invokes
  524. * all attached handlers for the event.
  525. * @param string $name the event name
  526. * @param CEvent $event the event parameter
  527. * @throws CException if the event is undefined or an event handler is invalid.
  528. */
  529. public function raiseEvent($name,$event)
  530. {
  531. $name=strtolower($name);
  532. if(isset($this->_e[$name]))
  533. {
  534. foreach($this->_e[$name] as $handler)
  535. {
  536. if(is_string($handler))
  537. call_user_func($handler,$event);
  538. else if(is_callable($handler,true))
  539. {
  540. if(is_array($handler))
  541. {
  542. // an array: 0 - object, 1 - method name
  543. list($object,$method)=$handler;
  544. if(is_string($object)) // static method call
  545. call_user_func($handler,$event);
  546. else if(method_exists($object,$method))
  547. $object->$method($event);
  548. else
  549. throw new CException(Yii::t('yii','Event "{class}.{event}" is attached with an invalid handler "{handler}".',
  550. array('{class}'=>get_class($this), '{event}'=>$name, '{handler}'=>$handler[1])));
  551. }
  552. else // PHP 5.3: anonymous function
  553. call_user_func($handler,$event);
  554. }
  555. else
  556. throw new CException(Yii::t('yii','Event "{class}.{event}" is attached with an invalid handler "{handler}".',
  557. array('{class}'=>get_class($this), '{event}'=>$name, '{handler}'=>gettype($handler))));
  558. // stop further handling if param.handled is set true
  559. if(($event instanceof CEvent) && $event->handled)
  560. return;
  561. }
  562. }
  563. else if(YII_DEBUG && !$this->hasEvent($name))
  564. throw new CException(Yii::t('yii','Event "{class}.{event}" is not defined.',
  565. array('{class}'=>get_class($this), '{event}'=>$name)));
  566. }
  567. /**
  568. * Evaluates a PHP expression or callback under the context of this component.
  569. *
  570. * Valid PHP callback can be class method name in the form of
  571. * array(ClassName/Object, MethodName), or anonymous function (only available in PHP 5.3.0 or above).
  572. *
  573. * If a PHP callback is used, the corresponding function/method signature should be
  574. * <pre>
  575. * function foo($param1, $param2, ..., $component) { ... }
  576. * </pre>
  577. * where the array elements in the second parameter to this method will be passed
  578. * to the callback as $param1, $param2, ...; and the last parameter will be the component itself.
  579. *
  580. * If a PHP expression is used, the second parameter will be "extracted" into PHP variables
  581. * that can be directly accessed in the expression. See {@link http://us.php.net/manual/en/function.extract.php PHP extract}
  582. * for more details. In the expression, the component object can be accessed using $this.
  583. *
  584. * @param mixed $_expression_ a PHP expression or PHP callback to be evaluated.
  585. * @param array $_data_ additional parameters to be passed to the above expression/callback.
  586. * @return mixed the expression result
  587. * @since 1.1.0
  588. */
  589. public function evaluateExpression($_expression_,$_data_=array())
  590. {
  591. if(is_string($_expression_))
  592. {
  593. extract($_data_);
  594. return eval('return '.$_expression_.';');
  595. }
  596. else
  597. {
  598. $_data_[]=$this;
  599. return call_user_func_array($_expression_, $_data_);
  600. }
  601. }
  602. }
  603. /**
  604. * CEvent is the base class for all event classes.
  605. *
  606. * It encapsulates the parameters associated with an event.
  607. * The {@link sender} property describes who raises the event.
  608. * And the {@link handled} property indicates if the event is handled.
  609. * If an event handler sets {@link handled} to true, those handlers
  610. * that are not invoked yet will not be invoked anymore.
  611. *
  612. * @author Qiang Xue <qiang.xue@gmail.com>
  613. * @version $Id: CComponent.php 3204 2011-05-05 21:36:32Z alexander.makarow $
  614. * @package system.base
  615. * @since 1.0
  616. */
  617. class CEvent extends CComponent
  618. {
  619. /**
  620. * @var object the sender of this event
  621. */
  622. public $sender;
  623. /**
  624. * @var boolean whether the event is handled. Defaults to false.
  625. * When a handler sets this true, the rest of the uninvoked event handlers will not be invoked anymore.
  626. */
  627. public $handled=false;
  628. /**
  629. * @var mixed additional event parameters.
  630. * @since 1.1.7
  631. */
  632. public $params;
  633. /**
  634. * Constructor.
  635. * @param mixed $sender sender of the event
  636. * @param mixed $params additional parameters for the event
  637. */
  638. public function __construct($sender=null,$params=null)
  639. {
  640. $this->sender=$sender;
  641. $this->params=$params;
  642. }
  643. }
  644. /**
  645. * CEnumerable is the base class for all enumerable types.
  646. *
  647. * To define an enumerable type, extend CEnumberable and define string constants.
  648. * Each constant represents an enumerable value.
  649. * The constant name must be the same as the constant value.
  650. * For example,
  651. * <pre>
  652. * class TextAlign extends CEnumerable
  653. * {
  654. * const Left='Left';
  655. * const Right='Right';
  656. * }
  657. * </pre>
  658. * Then, one can use the enumerable values such as TextAlign::Left and
  659. * TextAlign::Right.
  660. *
  661. * @author Qiang Xue <qiang.xue@gmail.com>
  662. * @version $Id: CComponent.php 3204 2011-05-05 21:36:32Z alexander.makarow $
  663. * @package system.base
  664. * @since 1.0
  665. */
  666. class CEnumerable
  667. {
  668. }