PageRenderTime 49ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/class/lang/aop/Aspect.php

https://github.com/JeCat/framework
PHP | 368 lines | 296 code | 34 blank | 38 comment | 20 complexity | 0bf9a9a9c0a1db42119090642485562d MD5 | raw file
  1. <?php
  2. ////////////////////////////////////////////////////////////////////////////////////////////////////////////
  3. // 这个文件是 JeCat PHP框架的一部分,该项目和此文件 均遵循 GNU 自由软件协议
  4. //
  5. // Copyleft 2008-2012 JeCat.cn(http://team.JeCat.cn)
  6. //
  7. //
  8. // JeCat PHP框架 的正式全名是:Jellicle Cat PHP Framework。
  9. // “Jellicle Cat”出自 Andrew Lloyd Webber的音乐剧《猫》(《Prologue:Jellicle Songs for Jellicle Cats》)。
  10. // JeCat 是一个开源项目,它像音乐剧中的猫一样自由,你可以毫无顾忌地使用JCAT PHP框架。JCAT 由中国团队开发维护。
  11. // 正在使用的这个版本是:0.7.1
  12. //
  13. //
  14. //
  15. // 相关的链接:
  16. // [主页] http://www.JeCat.cn
  17. // [源代码] https://github.com/JeCat/framework
  18. // [下载(http)] https://nodeload.github.com/JeCat/framework/zipball/master
  19. // [下载(git)] git clone git://github.com/JeCat/framework.git jecat
  20. // 不很相关:
  21. // [MP3] http://www.google.com/search?q=jellicle+songs+for+jellicle+cats+Andrew+Lloyd+Webber
  22. // [VCD/DVD] http://www.google.com/search?q=CAT+Andrew+Lloyd+Webber+video
  23. //
  24. ////////////////////////////////////////////////////////////////////////////////////////////////////////////
  25. /*-- Project Introduce --*/
  26. namespace org\jecat\framework\lang\aop ;
  27. use org\jecat\framework\lang\Type;
  28. use org\jecat\framework\bean\BeanFactory;
  29. use org\jecat\framework\bean\IBean;
  30. use org\jecat\framework\fs\FSO;
  31. use org\jecat\framework\io\InputStreamCache;
  32. use org\jecat\framework\lang\compile\CompilerFactory;
  33. use org\jecat\framework\lang\compile\object\ClassDefine;
  34. use org\jecat\framework\lang\compile\DocComment;
  35. use org\jecat\framework\pattern\composite\Container;
  36. use org\jecat\framework\pattern\composite\NamedObject;
  37. class Aspect extends NamedObject implements \Serializable, IBean
  38. {
  39. static public function createFromToken(ClassDefine $aClassToken,$sAspectFilepath=null)
  40. {
  41. $sAspectName = $aClassToken->fullName() ;
  42. $aTokenPool = $aClassToken->parent() ;
  43. $aAspect = new self($sAspectName) ;
  44. // 先定义 pointcut
  45. foreach($aTokenPool->functionIterator($sAspectName) as $aMethodToken)
  46. {
  47. if( !$aDocCommentToken=$aMethodToken->docToken() or !$aDocComment=$aDocCommentToken->docComment() )
  48. {
  49. continue ;
  50. }
  51. // pointcut
  52. if( $aDocComment->hasItem('pointcut') )
  53. {
  54. $aPointcut = Pointcut::createFromToken($aMethodToken) ;
  55. $aAspect->pointcuts()->add($aPointcut) ;
  56. }
  57. }
  58. // 然后定义 advice
  59. foreach($aTokenPool->functionIterator($sAspectName) as $aMethodToken)
  60. {
  61. if( !$aDocCommentToken=$aMethodToken->docToken() or !$aDocComment=$aDocCommentToken->docComment() )
  62. {
  63. continue ;
  64. }
  65. if( $aDocComment->hasItem('advice') )
  66. {
  67. $aAdvice = Advice::createFromToken($aMethodToken,$aAspect) ;
  68. $aAspect->addAdvice($aAdvice) ;
  69. }
  70. }
  71. $aAspect->sAspectName = $sAspectName ;
  72. if($sAspectFilepath)
  73. {
  74. $aAspect->setAspectFilepath(FSO::tidyPath($sAspectFilepath)) ;
  75. }
  76. return $aAspect ;
  77. }
  78. static public function createAspectsFromCode($sSource,$sAspectName=null)
  79. {
  80. eval($sSource) ;
  81. $aClassCompiler = CompilerFactory::singleton()->create() ;
  82. $aAspectTokens = $aClassCompiler->scan( new InputStreamCache('<?php '.$sSource.' ?>') ) ;
  83. $aClassCompiler->interpret($aAspectTokens) ;
  84. if( $sAspectName===null )
  85. {
  86. $arrAspects = array() ;
  87. foreach($aAspectTokens->classIterator() as $aClassToken)
  88. {
  89. $arrAspects[] = self::createFromToken($aClassToken) ;
  90. }
  91. return $arrAspects ;
  92. }
  93. else
  94. {
  95. if( $aClassToken = $aAspectTokens->findClass($sAspectName) )
  96. {
  97. return self::createFromToken($aClassToken) ;
  98. }
  99. else
  100. {
  101. return null ;
  102. }
  103. }
  104. }
  105. public function addAdvice(Advice $aAdvice)
  106. {
  107. if(!$this->advices()->has($aAdvice))
  108. {
  109. $this->advices()->add($aAdvice) ;
  110. }
  111. foreach($aAdvice->forPointcuts() as $sPointcutName)
  112. {
  113. if( $aPointcut=$this->pointcuts()->getByName($sPointcutName) )
  114. {
  115. $aPointcut->advices()->add($aAdvice) ;
  116. }
  117. }
  118. }
  119. /**
  120. * @return org\jecat\framework\pattern\composite\IContainer
  121. */
  122. public function pointcuts()
  123. {
  124. if( !$this->aPointcuts )
  125. {
  126. $this->aPointcuts = new Container('org\\jecat\\framework\\lang\\aop\\Pointcut') ;
  127. }
  128. return $this->aPointcuts ;
  129. }
  130. /**
  131. * @return org\jecat\framework\pattern\composite\IContainer
  132. */
  133. public function advices()
  134. {
  135. if( !$this->aAdvices )
  136. {
  137. $this->aAdvices = new Container('org\\jecat\\framework\\lang\\aop\\Advice') ;
  138. }
  139. return $this->aAdvices ;
  140. }
  141. public function aspectName()
  142. {
  143. return $this->sAspectName ;
  144. }
  145. public function aspectFilepath()
  146. {
  147. return $this->sAspectFilepath ;
  148. }
  149. public function setAspectFilepath($sFilepath)
  150. {
  151. $this->sAspectFilepath = $sFilepath ;
  152. $this->nAspectFilemtime = filemtime($sFilepath) ;
  153. }
  154. public function aspectFilemtime()
  155. {
  156. return $this->nAspectFilemtime ;
  157. }
  158. public function isValid()
  159. {
  160. if( $this->sAspectFilepath and $this->nAspectFilemtime )
  161. {
  162. if( !is_file($this->sAspectFilepath) or filemtime($this->sAspectFilepath)>$this->nAspectFilemtime )
  163. {
  164. return false ;
  165. }
  166. }
  167. foreach( $this->advices()->iterator() as $aAdvice )
  168. {
  169. if( !$aAdvice->isValid() )
  170. {
  171. return false ;
  172. }
  173. }
  174. return true ;
  175. }
  176. public function serialize ()
  177. {
  178. return serialize( array(
  179. 'aPointcuts' => & $this->aPointcuts ,
  180. 'aAdvices' => & $this->aAdvices ,
  181. 'sAspectName' => & $this->sAspectName ,
  182. 'sAspectFilepath' => & $this->sAspectFilepath ,
  183. 'nAspectFilemtime' => & $this->nAspectFilemtime ,
  184. ) ) ;
  185. }
  186. /**
  187. * @param serialized
  188. */
  189. public function unserialize ($serialized)
  190. {
  191. $arrData = unserialize($serialized) ;
  192. $this->aPointcuts =& $arrData['aPointcuts'] ;
  193. $this->aAdvices =& $arrData['aAdvices'] ;
  194. $this->sAspectName =& $arrData['sAspectName'] ;
  195. $this->sAspectFilepath =& $arrData['sAspectFilepath'] ;
  196. $this->nAspectFilemtime =& $arrData['nAspectFilemtime'] ;
  197. if($this->aPointcuts)
  198. {
  199. foreach($this->aPointcuts->iterator() as $aPointcut)
  200. {
  201. $aPointcut->setAspect($this) ;
  202. }
  203. }
  204. if($this->aAdvices)
  205. {
  206. foreach($this->aAdvices->iterator() as $aAdvice)
  207. {
  208. $aAdvice->setAspect($this) ;
  209. $this->addAdvice($aAdvice) ;
  210. }
  211. }
  212. }
  213. // IBean
  214. static public function createBean(array & $arrConfig,$sNamespace='*',$bBuildAtOnce,BeanFactory $aBeanFactory=null)
  215. {
  216. $aBean = new self() ;
  217. if($bBuildAtOnce)
  218. {
  219. $aBean->buildBean($arrConfig,$sNamespace) ;
  220. }
  221. return $aBean ;
  222. }
  223. public function buildBean(array & $arrConfig,$sNamespace='*',BeanFactory $aBeanFactory=null)
  224. {
  225. $aPointcut = new Pointcut('default_pointcut') ;
  226. $aPointcut->setAspect($this) ;
  227. $this->pointcuts()->add($aPointcut) ;
  228. $nAdviceIdx = 0 ;
  229. foreach($arrConfig as $key=>&$item)
  230. {
  231. if(!is_int($key))
  232. {
  233. continue ;
  234. }
  235. // jointpoint
  236. if( is_string($item) )
  237. {
  238. foreach( array('JointPointMethodDefine','JointPointCallFunction','JointPointNewObject') as $sJointpointClass )
  239. {
  240. if( $aJointPoint=call_user_func(array(__NAMESPACE__.'\\jointpoint\\'.$sJointpointClass,'createFromDeclare'),$item) )
  241. {
  242. $aPointcut->jointPoints()->add($aJointPoint) ;
  243. $aJointPoint->setPointcut($aPointcut) ;
  244. break ;
  245. }
  246. }
  247. }
  248. // advice
  249. else if( is_callable($item,true) )
  250. {
  251. // 源文
  252. $sSource = Type::reflectFunctionBody($item) ;
  253. $aRefFunc = is_array($item)? new \ReflectionMethod($item[0],$item[1]): new \ReflectionFunction($item) ;
  254. // 函数定义时声明的 access 和 static
  255. if( $aRefFunc instanceof \ReflectionMethod )
  256. {
  257. if( $aRefFunc->isPrivate() )
  258. {
  259. $sAccess = 'private' ;
  260. }
  261. else if( $aRefFunc->isProtected() )
  262. {
  263. $sAccess = 'protected' ;
  264. }
  265. else if( $aRefFunc->isPublic() )
  266. {
  267. $sAccess = 'public' ;
  268. }
  269. $bStatic = $aRefFunc->isStatic() ;
  270. $sAdviceName = $aRefFunc->getDeclaringClass()->getName().'::'.$aRefFunc->getName() ;
  271. }
  272. else
  273. {
  274. $sAdviceName = 'nameless_' . $nAdviceIdx++ ;
  275. $sAccess = 'private' ;
  276. $bStatic = false ;
  277. }
  278. // 函数定义 注释中的 @use 和 @advice
  279. $arrUseDeclare = array() ;
  280. if( $sComment=$aRefFunc->getDocComment() )
  281. {
  282. $aDocComment = new DocComment($sComment) ;
  283. // @use
  284. $arrUseDeclare = $aDocComment->items('use')?: array() ;
  285. // @advice
  286. $sPosition = $aDocComment->item('advice')?: Advice::after ;
  287. }
  288. else
  289. {
  290. $sPosition = Advice::after ;
  291. }
  292. $aAdvice = new Advice($sAdviceName,$sSource,$sPosition) ;
  293. $aAdvice->addPointcutName($aPointcut->name()) ;
  294. $aAdvice->setDefineFile($aRefFunc->getFileName()) ;
  295. $aAdvice->setAccess($sAccess) ;
  296. $aAdvice->setStatic($bStatic) ;
  297. foreach($arrUseDeclare as &$sUseDclare)
  298. {
  299. $aAdvice->addUseDeclare($sUseDclare) ;
  300. }
  301. $aPointcut->advices()->add($aAdvice) ;
  302. $this->advices()->add($aAdvice) ;
  303. $aAdvice->setAspect($this) ;
  304. }
  305. }
  306. $this->arrBeanConfig =& $arrConfig ;
  307. }
  308. public function beanConfig()
  309. {
  310. return $this->arrBeanConfig ;
  311. }
  312. private $aPointcuts ;
  313. private $aAdvices ;
  314. private $sAspectName ;
  315. private $sAspectFilepath ;
  316. private $nAspectFilemtime = 0 ;
  317. }