router.class.php 124 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923
  1. <?php
  2. /**
  3. * 此文件包括ZenTaoPHP框架的三个类:baseRouter, config, lang。
  4. * The router, config and lang class file of ZenTaoPHP framework.
  5. *
  6. * @package framework
  7. *
  8. * The author disclaims copyright to this source code. In place of
  9. * a legal notice, here is a blessing:
  10. *
  11. * May you do good and not evil.
  12. * May you find forgiveness for yourself and forgive others.
  13. * May you share freely, never taking more than you give.
  14. */
  15. class baseRouter
  16. {
  17. /**
  18. * ZenTaoPHP的基础目录,一般是程序的根目录。
  19. * The base path of the ZenTaoPHP framework.
  20. *
  21. * @var string
  22. * @access public
  23. */
  24. public $basePath;
  25. /**
  26. * 框架的根目录。
  27. * The root directory of the framwork($this->basePath/framework)
  28. *
  29. * @var string
  30. * @access public
  31. */
  32. public $frameRoot;
  33. /**
  34. * 类库的根目录。{$this->basePath/lib}
  35. * The root directory of the library($this->basePath/lib).
  36. *
  37. * @var string
  38. * @access public
  39. */
  40. public $coreLibRoot;
  41. /**
  42. * 应用名称
  43. * The appName.
  44. *
  45. * @var string
  46. * @access public
  47. */
  48. public $appName = '';
  49. /**
  50. * 应用程序的根目录。
  51. * The root directory of the app.
  52. *
  53. * @var string
  54. * @access public
  55. */
  56. public $appRoot;
  57. /**
  58. * 临时文件的根目录。
  59. * The root directory of temp.
  60. *
  61. * @var string
  62. * @access public
  63. */
  64. public $tmpRoot;
  65. /**
  66. * 缓存的根目录。
  67. * The root directory of cache.
  68. *
  69. * @var string
  70. * @access public
  71. */
  72. public $cacheRoot;
  73. /**
  74. * WWW目录。
  75. * The root directory of www.
  76. *
  77. * @var string
  78. * @access public
  79. */
  80. public $wwwRoot;
  81. /**
  82. * 附件存放目录。
  83. * The root directory of data.
  84. *
  85. * @var string
  86. * @access public
  87. */
  88. public $dataRoot;
  89. /**
  90. * 日志文件的根目录。
  91. * The root directory of log.
  92. *
  93. * @var string
  94. * @access public
  95. */
  96. public $logRoot;
  97. /**
  98. * 配置文件的根目录。
  99. * The root directory of config.
  100. *
  101. * @var string
  102. * @access public
  103. */
  104. public $configRoot;
  105. /**
  106. * 模块的根目录。
  107. * The root directory of module.
  108. *
  109. * @var string
  110. * @access public
  111. */
  112. public $moduleRoot;
  113. /**
  114. * 主题的根目录。
  115. * The root directory of theme.
  116. *
  117. * @var string
  118. * @access public
  119. */
  120. public $themeRoot;
  121. /**
  122. * 用户使用的语言。
  123. * The lang of the client user.
  124. *
  125. * @var string
  126. * @access public
  127. */
  128. public $clientLang;
  129. /**
  130. * 请求的原始模块名。
  131. * The requestd module name parsed from a URL.
  132. *
  133. * @var string
  134. * @access public
  135. */
  136. public $rawModule;
  137. /**
  138. * 请求的原始方法名。
  139. * The requested method name parsed from a URL.
  140. *
  141. * @var string
  142. * @access public
  143. */
  144. public $rawMethod;
  145. /**
  146. * 当前页面所在的应用,用于左侧菜单栏判断。
  147. * The current app code(url: '#app=?'), highlight left menu.
  148. *
  149. * @var string
  150. * @access public
  151. */
  152. public $tab;
  153. /**
  154. * API版本号
  155. * The version of API.
  156. *
  157. * @var string
  158. * @access public
  159. */
  160. public $apiVersion = '';
  161. /**
  162. * 用户使用的主题。
  163. * The theme of the client user.
  164. *
  165. * @var string
  166. * @access public
  167. */
  168. public $clientTheme;
  169. /**
  170. * 客户端设备类型。
  171. * The device type of client.
  172. *
  173. * @var string
  174. * @access public
  175. */
  176. public $clientDevice;
  177. /**
  178. * 当前模块的control对象。
  179. * The control object of current module.
  180. *
  181. * @var object
  182. * @access public
  183. */
  184. public $control;
  185. /**
  186. * 模块名。
  187. * The module name
  188. *
  189. * @var string
  190. * @access public
  191. */
  192. public $moduleName;
  193. /**
  194. * 当前访问模块的control文件。
  195. * The control file of the module current visiting.
  196. *
  197. * @var string
  198. * @access public
  199. */
  200. public $controlFile;
  201. /**
  202. * 当前访问的方法名。
  203. * The name of the method current visiting.
  204. *
  205. * @var string
  206. * @access public
  207. */
  208. public $methodName;
  209. /**
  210. * 当前方法的扩展文件。
  211. * The action extension file of current method.
  212. *
  213. * @var string
  214. * @access public
  215. */
  216. public $extActionFile;
  217. /**
  218. * 访问的URI。
  219. * The URI.
  220. *
  221. * @var string
  222. * @access public
  223. */
  224. public $uri;
  225. /**
  226. * url地址传递的参数。
  227. * The params passed in through url.
  228. *
  229. * @var array
  230. * @access public
  231. */
  232. public $params;
  233. /**
  234. * 视图类型。
  235. * The view type.
  236. *
  237. * @var string
  238. * @access public
  239. */
  240. public $viewType = 'html';
  241. /**
  242. * 全局$config对象。
  243. * The global $config object.
  244. *
  245. * @var object
  246. * @access public
  247. */
  248. public $config;
  249. /**
  250. * 已加载的配置文件.
  251. * Configs loaded.
  252. *
  253. * @static
  254. * @var array
  255. * @access public
  256. */
  257. static $loadedConfigs = array();
  258. /**
  259. * 已加载的语言文件.
  260. * Languages loaded.
  261. *
  262. * @static
  263. * @var array
  264. * @access public
  265. */
  266. static $loadedLangs = array();
  267. /**
  268. * 已加载的目标,可能是 model,tao 或者 zen 的实例化对象。
  269. * Targets loaded, maybe instance of model, tao or zen.
  270. *
  271. * @static
  272. * @var array
  273. * @access public
  274. */
  275. static $loadedTargets = array();
  276. /**
  277. * 全局$lang对象。
  278. * The global $lang object.
  279. *
  280. * @var object
  281. * @access public
  282. */
  283. public $lang;
  284. /**
  285. * 全局缓存对象,用于操作缓存。
  286. * The global cache object, used to operate cache.
  287. *
  288. * @var object
  289. * @access public
  290. */
  291. public $cache = null;
  292. /**
  293. * 全局$dbh对象,数据库连接句柄。
  294. * The global $dbh object, the database connection handler.
  295. *
  296. * @var object
  297. * @access public
  298. */
  299. public $dbh;
  300. /**
  301. * $dao对象,用于访问或者更新数据库。
  302. * The $dao object, used to access or update database.
  303. *
  304. * @var dao
  305. * @access public
  306. */
  307. public $dao;
  308. /**
  309. * $mao对象,用于访问或者更新缓存。
  310. * The $mao object, used to access or update cache.
  311. *
  312. * @var mao
  313. * @access public
  314. */
  315. public $mao;
  316. /**
  317. * 从数据库的句柄。
  318. * The slave database handler.
  319. *
  320. * @var object
  321. * @access public
  322. */
  323. public $slaveDBH;
  324. /**
  325. * $post对象,用于访问$_POST变量。
  326. * The $post object, used to access the $_POST var.
  327. *
  328. * @var object
  329. * @access public
  330. */
  331. public $post;
  332. /**
  333. * $get对象,用于访问$_GET变量。
  334. * The $get object, used to access the $_GET var.
  335. *
  336. * @var object
  337. * @access public
  338. */
  339. public $get;
  340. /**
  341. * $session对象,用于访问$_SESSION变量。
  342. * The $session object, used to access the $_SESSION var.
  343. *
  344. * @var object
  345. * @access public
  346. */
  347. public $session;
  348. /**
  349. * $server对象,用于访问$_SERVER变量。
  350. * The $server object, used to access the $_SERVER var.
  351. *
  352. * @var object
  353. * @access public
  354. */
  355. public $server;
  356. /**
  357. * $cookie对象,用于访问$_COOKIE变量。
  358. * The $cookie object, used to access the $_COOKIE var.
  359. *
  360. * @var object
  361. * @access public
  362. */
  363. public $cookie;
  364. /**
  365. * 原始SESSIONID
  366. * SESSIONID
  367. *
  368. * @var int
  369. * @access public
  370. */
  371. public $sessionID;
  372. /**
  373. * 网站代号。
  374. * The code of current site.
  375. *
  376. * @var string
  377. * @access public
  378. */
  379. public $siteCode;
  380. /**
  381. * 请求开始时间。
  382. * The start time of the request.
  383. *
  384. * @var float
  385. */
  386. public $startTime;
  387. /**
  388. * zin 请求时发生的错误信息。
  389. * The errors occurred when zin request.
  390. *
  391. * @var array
  392. */
  393. public $zinErrors = array();
  394. /**
  395. * 是否将mysql的错误作为异常抛出。
  396. * Whether to throw mysql error as exception.
  397. *
  398. * @var bool
  399. */
  400. public $throwError = false;
  401. /**
  402. * 是否使用客户端缓存。
  403. * Whether to use client cache.
  404. *
  405. * @var bool
  406. */
  407. public $useClientCache = true;
  408. /**
  409. * 客户端缓存时间。
  410. * The client cache time.
  411. *
  412. * @var int
  413. */
  414. public $clientCacheTime = 0;
  415. /**
  416. * 缓存Model。
  417. * The cache model.
  418. *
  419. * @var object
  420. */
  421. public $cacheModel;
  422. /**
  423. * 是否需要对htmlspecialchars进行解码。
  424. * Whether need to decode htmlspecialchars.
  425. *
  426. * @var bool
  427. */
  428. public $needDecodeHtmlSpecialChars = null;
  429. /**
  430. * 构造方法, 设置路径,类,超级变量等。注意:
  431. * 1.应该使用createApp()方法实例化router类;
  432. * 2.如果$appRoot为空,框架会根据$appName计算应用路径。
  433. *
  434. * The construct function.
  435. * Prepare all the paths, classes, super objects and so on.
  436. * Notice:
  437. * 1. You should use the createApp() method to get an instance of the router.
  438. * 2. If the $appRoot is empty, the framework will compute the appRoot according the $appName
  439. *
  440. * @param string $appName the name of the app
  441. * @param string $appRoot the root path of the app
  442. * @param string $mode the mode of the app running|installing|upgrading
  443. * @access public
  444. * @return void
  445. */
  446. public function __construct($appName = 'demo', $appRoot = '', $mode = 'running')
  447. {
  448. if($mode != 'running') $this->{$mode} = true;
  449. $this->setPathFix();
  450. $this->setBasePath();
  451. $this->setFrameRoot();
  452. $this->setCoreLibRoot();
  453. $this->setAppRoot($appName, $appRoot);
  454. $this->setTmpRoot();
  455. $this->setCacheRoot();
  456. $this->setLogRoot();
  457. $this->setConfigRoot();
  458. $this->setModuleRoot();
  459. $this->setWwwRoot();
  460. $this->setThemeRoot();
  461. $this->setDataRoot();
  462. $this->loadMainConfig();
  463. $this->loadClass('front', $static = true);
  464. $this->loadClass('filter', $static = true);
  465. $this->loadClass('form', $static = true);
  466. $this->loadClass('dbh', $static = true);
  467. $this->loadClass('sqlite', $static = true);
  468. $this->loadClass('dao', $static = true);
  469. $this->loadClass('mobile', $static = true);
  470. $this->setCookieSecure();
  471. $this->setDebug();
  472. $this->setErrorHandler();
  473. $this->setTimezone();
  474. if($this->config->framework->autoConnectDB) $this->connectDB();
  475. $this->setupXhprof();
  476. $this->setEdition();
  477. $this->setClient();
  478. $this->loadCache();
  479. }
  480. /**
  481. * 设置客户端信息
  482. * Set info of client: session, lang, device, theme...
  483. *
  484. * @access public
  485. * @return void
  486. */
  487. public function setClient()
  488. {
  489. $this->startSession();
  490. if($this->config->framework->multiSite) $this->setSiteCode() && $this->loadExtraConfig();
  491. if($this->config->framework->multiLanguage) $this->setClientLang();
  492. $this->setVision();
  493. $needDetectDevice = zget($this->config->framework->detectDevice, $this->clientLang, false);
  494. $this->clientDevice = $needDetectDevice ? $this->setClientDevice() : 'desktop';
  495. if($this->config->framework->multiLanguage) $this->loadLang('common');
  496. if($this->config->framework->multiTheme) $this->setClientTheme();
  497. $this->setOpenApp();
  498. $this->setSuperVars();
  499. if(isset($_SERVER['HTTP_X_ZIN_CACHE_TIME'])) $this->clientCacheTime = (int) $_SERVER['HTTP_X_ZIN_CACHE_TIME'];
  500. }
  501. /**
  502. * 创建一个应用。
  503. * Create an application.
  504. *
  505. * @param string $appName 应用名称。 The name of the app.
  506. * @param string $appRoot 应用根路径。The root path of the app.
  507. * @param string $className 应用类名,如果对router类做了扩展,需要指定类名。When extends router class, you should pass in the child router class name.
  508. * @param string $mode 应用模式。 The mode of the app. running|installing|upgrading
  509. * @static
  510. * @access public
  511. * @return baseRouter
  512. */
  513. public static function createApp($appName = 'demo', $appRoot = '', $className = '', $mode = 'running')
  514. {
  515. if(empty($className)) $className = self::class;
  516. return new $className($appName, $appRoot, $mode);
  517. }
  518. /**
  519. * 设置请求开始时间。
  520. * The start time of the request.
  521. *
  522. * @param float $startTime
  523. * @access public
  524. * @return void
  525. */
  526. public function setStartTime($startTime)
  527. {
  528. $this->startTime = $startTime;
  529. }
  530. //-------------------- 路径相关方法(Path related methods)--------------------//
  531. /**
  532. * 设置应用名称。
  533. * Set app name.
  534. *
  535. * @param string $appName
  536. * @access public
  537. * @return void
  538. */
  539. public function setAppName($appName)
  540. {
  541. $this->appName = $appName;
  542. }
  543. /**
  544. * 设置目录分隔符。
  545. * Set the path directory separator.
  546. *
  547. * @access public
  548. * @return void
  549. */
  550. public function setPathFix()
  551. {
  552. define('DS', DIRECTORY_SEPARATOR);
  553. }
  554. /**
  555. * 设置基础目录。
  556. * Set the base path.
  557. *
  558. * @access public
  559. * @return void
  560. */
  561. public function setBasePath()
  562. {
  563. $this->basePath = realpath(dirname(__FILE__, 3)) . DS;
  564. }
  565. /**
  566. * 设置框架根目录。
  567. * Set the frame root.
  568. *
  569. * @access public
  570. * @return void
  571. */
  572. public function setFrameRoot()
  573. {
  574. $this->frameRoot = $this->basePath . 'framework' . DS;
  575. }
  576. /**
  577. * 设置类库的根目录。
  578. * Set the app lib root.
  579. *
  580. * @access public
  581. * @return void
  582. */
  583. public function setCoreLibRoot()
  584. {
  585. $this->coreLibRoot = $this->basePath . 'lib' . DS;
  586. }
  587. /**
  588. * 设置应用的根目录。
  589. * Set the app root.
  590. *
  591. * @param string $appName
  592. * @param string $appRoot
  593. * @access public
  594. * @return void
  595. */
  596. public function setAppRoot($appName = 'demo', $appRoot = '')
  597. {
  598. if(empty($appRoot)) $this->appRoot = $this->basePath . 'app' . DS . $appName . DS;
  599. if(!empty($appRoot)) $this->appRoot = realpath($appRoot) . DS;
  600. if(!is_dir($this->appRoot)) $this->triggerError("The app you call not found in {$this->appRoot}", __FILE__, __LINE__, true);
  601. }
  602. /**
  603. * 设置临时文件的根目录。
  604. * Set the tmp root.
  605. *
  606. * @access public
  607. * @return void
  608. */
  609. public function setTmpRoot()
  610. {
  611. $this->tmpRoot = $this->basePath . 'tmp' . DS;
  612. }
  613. /**
  614. * 设置缓存的根目录。
  615. * Set the cache root.
  616. *
  617. * @access public
  618. * @return void
  619. */
  620. public function setCacheRoot()
  621. {
  622. $this->cacheRoot = $this->tmpRoot . 'cache' . DS;
  623. }
  624. /**
  625. * 设置log的根目录。
  626. * Set the log root.
  627. *
  628. * @access public
  629. * @return void
  630. */
  631. public function setLogRoot()
  632. {
  633. $this->logRoot = $this->tmpRoot . 'log' . DS;
  634. }
  635. /**
  636. * 设置config配置文件的根目录。
  637. * Set the config root.
  638. *
  639. * @access public
  640. * @return void
  641. */
  642. public function setConfigRoot()
  643. {
  644. $this->configRoot = $this->basePath . 'config' . DS;
  645. }
  646. /**
  647. * 设置模块的根目录。
  648. * Set the module root.
  649. *
  650. * @access public
  651. * @return void
  652. */
  653. public function setModuleRoot()
  654. {
  655. $this->moduleRoot = $this->basePath . 'module' . DS;
  656. }
  657. /**
  658. * 设置www的根目录。
  659. * Set the www root.
  660. *
  661. * @access public
  662. * @return void
  663. */
  664. public function setWwwRoot()
  665. {
  666. $this->wwwRoot = rtrim(dirname((string) $_SERVER['SCRIPT_FILENAME']), DS) . DS;
  667. }
  668. /**
  669. * 设置主题根目录。
  670. * Set the theme root.
  671. *
  672. * @access public
  673. * @return void
  674. */
  675. public function setThemeRoot()
  676. {
  677. $this->themeRoot = $this->wwwRoot . 'theme' . DS;
  678. }
  679. /**
  680. * 设置data根目录。
  681. * Set the data root.
  682. *
  683. * @access public
  684. * @return void
  685. */
  686. public function setDataRoot()
  687. {
  688. $this->dataRoot = $this->wwwRoot . 'data' . DS;
  689. }
  690. /**
  691. * 设置超级变量。
  692. * Set the super vars.
  693. *
  694. * @access public
  695. * @return void
  696. */
  697. public function setSuperVars()
  698. {
  699. if(isset($_SERVER['REQUEST_URI']))
  700. {
  701. $uri = $_SERVER['REQUEST_URI'];
  702. if(strpos((string) $uri, '?') !== false)
  703. {
  704. $parsedURL = parse_url((string) $uri);
  705. if(isset($parsedURL['query']))
  706. {
  707. parse_str($parsedURL['query'], $parsedQuery);
  708. foreach($parsedQuery as $key => $value)
  709. {
  710. if(!isset($_GET[$key])) $_GET[$key] = $value;
  711. }
  712. }
  713. }
  714. }
  715. $this->post = new super('post');
  716. $this->get = new super('get');
  717. $this->server = new super('server');
  718. $this->cookie = new super('cookie');
  719. $this->session = new super('session', $this->tab);
  720. unset($_REQUEST);
  721. /* Change for CSRF. */
  722. if($this->config->framework->filterCSRF)
  723. {
  724. $httpType = (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == 'on') ? 'https' : 'http';
  725. if(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) and strtolower((string) $_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https') $httpType = 'https';
  726. if(isset($_SERVER['REQUEST_SCHEME']) and strtolower((string) $_SERVER['REQUEST_SCHEME']) == 'https') $httpType = 'https';
  727. $httpHost = zget($_SERVER, 'HTTP_HOST', '');
  728. $apiMode = $this->apiVersion || isset($_GET[$this->config->sessionVar]);
  729. if(!$apiMode && (empty($httpHost) or strncmp((string) $this->server->http_referer, "$httpType://$httpHost", strlen("$httpType://$httpHost")) !== 0)) $_FILES = $_POST = array();
  730. }
  731. $_FILES = validater::filterFiles();
  732. $_POST = validater::filterSuper($_POST);
  733. $_GET = validater::filterSuper($_GET);
  734. $_COOKIE = validater::filterSuper($_COOKIE);
  735. $_SERVER = validater::filterSuper($_SERVER);
  736. /* Filter common get and cookie vars. */
  737. if($this->config->framework->filterParam == 2)
  738. {
  739. global $filter;
  740. foreach($filter->default->get as $key => $rules)
  741. {
  742. if(isset($_GET[$key]) and !validater::checkByRule($_GET[$key], $rules)) unset($_GET[$key]);
  743. }
  744. foreach($filter->default->cookie as $key => $rules)
  745. {
  746. if(isset($_COOKIE[$key]) and !validater::checkByRule($_COOKIE[$key], $rules)) unset($_COOKIE[$key]);
  747. }
  748. }
  749. }
  750. /**
  751. * Set cookieSecure config.
  752. *
  753. * @access public
  754. * @return void
  755. */
  756. public function setCookieSecure()
  757. {
  758. $this->config->cookieSecure = false;
  759. if($this->config->framework->setCookieSecure and isHttps()) $this->config->cookieSecure = true;
  760. }
  761. /**
  762. * 设置Debug模式。
  763. * set Debug.
  764. *
  765. * @access public
  766. * @return void
  767. */
  768. public function setDebug()
  769. {
  770. if(!empty($this->config->debug)) error_reporting(E_ALL);
  771. }
  772. /**
  773. * 启用Xhprof。
  774. * Setup xhprof.
  775. *
  776. * @return void
  777. */
  778. protected function setupXhprof()
  779. {
  780. if(!empty($this->config->debug) && $this->config->debug >= 4 && extension_loaded('xhprof')) xhprof_enable();
  781. }
  782. /**
  783. * 输出Xhprof结果。
  784. * Output xhprof.
  785. *
  786. * @return bool
  787. */
  788. public function outputXhprof()
  789. {
  790. if(empty($this->config->debug) || $this->config->debug < 4 || !extension_loaded('xhprof')) return false;
  791. $log = xhprof_disable();
  792. $xhprofPath = $this->getWwwRoot() . 'xhprof';
  793. $libUtilsPath = $xhprofPath . DS . 'xhprof_lib' . DS . 'utils' . DS;
  794. $outputDir = ini_get('xhprof.output_dir');
  795. if(!is_dir($xhprofPath)) return false;
  796. if(!$outputDir) $outputDir = $xhprofPath . DS . 'xhprof_runs';
  797. if(!is_dir($outputDir)) mkdir($outputDir, 0777, true);
  798. include_once $libUtilsPath . 'xhprof_lib.php';
  799. include_once $libUtilsPath . 'xhprof_runs.php';
  800. $xhprofRuns = new \XHProfRuns_Default($outputDir);
  801. $type = "{$this->moduleName}_{$this->methodName}";
  802. $runID = $xhprofRuns->save_run($log, $type);
  803. if(!headers_sent()) helper::header('Xhprof-RunID', $runID);
  804. return true;
  805. }
  806. /**
  807. * 设置版本。
  808. * Set edition.
  809. *
  810. * @access public
  811. * @return void
  812. */
  813. public function setEdition()
  814. {
  815. if(isset($this->config->edition)) return $this->config->edition;
  816. $edition = substr((string) $this->config->version, 0, 3);
  817. if(in_array($edition, array('pro', 'biz', 'max'))) return $this->config->edition = $edition;
  818. $this->config->edition = 'open';
  819. }
  820. /**
  821. * 设置vision。
  822. * set Debug.
  823. *
  824. * @access public
  825. * @return void
  826. */
  827. public function setVision()
  828. {
  829. $account = isset($_SESSION['user']) ? $_SESSION['user']->account : '';
  830. if(empty($account) and isset($_POST['account'])) $account = $_POST['account'];
  831. if(empty($account) and isset($_GET['account'])) $account = $_GET['account'];
  832. if(empty($account) and isset($_COOKIE['za'])) $account = $_COOKIE['za'];
  833. $vision = '';
  834. if($this->config->installed and validater::checkAccount($account) and !$this->upgrading)
  835. {
  836. if(!empty($_COOKIE['vision']))
  837. {
  838. $vision = $_COOKIE['vision'];
  839. }
  840. else
  841. {
  842. $vision = $this->dao->select("value")->from(TABLE_CONFIG)->where('owner')->eq($account)->andWhere('`key`')->eq('vision')->limit(1)->fetch('value');
  843. }
  844. $user = $this->dao->select('*')->from(TABLE_USER)->where('account')->eq($account)->andWhere('deleted')->eq('0')->limit(1)->fetch();
  845. if(!empty($user->visions))
  846. {
  847. $userVisions = explode(',', $user->visions);
  848. if(!in_array($vision, $userVisions)) $vision = '';
  849. if(empty($vision)) [$vision] = $userVisions;
  850. }
  851. }
  852. [$defaultVision] = explode(',', trim((string) $this->config->visions, ','));
  853. if($defaultVision != 'lite' && $defaultVision != 'or') $defaultVision = 'rnd';
  854. if($vision and strpos((string) $this->config->visions, ",{$vision},") === false) $vision = $defaultVision;
  855. $vision = $vision ?: $defaultVision;
  856. if(empty($_COOKIE['vision'])) setcookie('vision', $vision, $this->config->cookieLife, $this->config->webRoot, '', false, false);
  857. $this->config->vision = $vision;
  858. }
  859. /**
  860. * Get installed version.
  861. *
  862. * @access public
  863. * @return string
  864. */
  865. public function getInstalledVersion()
  866. {
  867. if(!isset($this->config->installed) || !$this->config->installed) return false;
  868. $version = $this->dao->select('`value`')->from(TABLE_CONFIG)->where('`owner`')->eq('system')->andWhere('`key`')->eq('version')->andWhere('`module`')->eq('common')->andWhere('`section`')->eq('global')->limit(1)->fetch('value');
  869. if(!$version && ($this->config->inContainer || $this->config->inQuickon)) return false;
  870. if(!$version) $version = '0.3.beta'; // No version, set as 0.3.beta.
  871. if($version == '3.0.stable') $version = '3.0'; // convert 3.0.stable to 3.0.
  872. return $version;
  873. }
  874. /**
  875. * 设置错误处理句柄。
  876. * Set the error handler.
  877. *
  878. * @access public
  879. * @return void
  880. */
  881. public function setErrorHandler()
  882. {
  883. set_error_handler(\Closure::fromCallable([$this, 'saveError']));
  884. register_shutdown_function(array($this, 'shutdown'));
  885. }
  886. /**
  887. * 获取应用名称
  888. * Get app name
  889. *
  890. * @access public
  891. * @return string
  892. */
  893. public function getAppName()
  894. {
  895. return $this->appName;
  896. }
  897. /**
  898. * 获取$basePath,即基础路径。
  899. * Get the $basePath var.
  900. *
  901. * @access public
  902. * @return string
  903. */
  904. public function getBasePath()
  905. {
  906. return $this->basePath;
  907. }
  908. /**
  909. * 获取$frameRoot,即框架根目录。
  910. * Get the $frameRoot var.
  911. *
  912. * @access public
  913. * @return string
  914. */
  915. public function getFrameRoot()
  916. {
  917. return $this->frameRoot;
  918. }
  919. /**
  920. * 获取$appRoot变量,即应用的根目录。
  921. * Get the $appRoot var.
  922. *
  923. * @access public
  924. * @return string
  925. */
  926. public function getAppRoot()
  927. {
  928. return $this->appRoot;
  929. }
  930. /**
  931. * 获取$wwwRoot变量。
  932. * Get the $wwwRoot var
  933. *
  934. * @access public
  935. * @return string
  936. */
  937. public function getWwwRoot()
  938. {
  939. return $this->wwwRoot;
  940. }
  941. /**
  942. * 获取$coreLibRoot变量,即应用类库的根目录。
  943. * Get the $coreLibRoot var.
  944. *
  945. * @access public
  946. * @return string
  947. */
  948. public function getCoreLibRoot()
  949. {
  950. return $this->coreLibRoot;
  951. }
  952. /**
  953. * 获取$tmpRoot变量,即临时文件的根目录。
  954. * Get the $tmpRoot var.
  955. *
  956. * @access public
  957. * @return string
  958. */
  959. public function getTmpRoot()
  960. {
  961. return $this->tmpRoot;
  962. }
  963. /**
  964. * 获取$cacheRoot变量,即缓存文件的根目录。
  965. * Get the $cacheRoot var.
  966. *
  967. * @access public
  968. * @return string
  969. */
  970. public function getCacheRoot()
  971. {
  972. return $this->cacheRoot;
  973. }
  974. /**
  975. * 获取$logRoot变量,即日志文件的根目录。
  976. * Get the $logRoot var.
  977. *
  978. * @access public
  979. * @return string
  980. */
  981. public function getLogRoot()
  982. {
  983. return $this->logRoot;
  984. }
  985. /**
  986. * 获取$configRoot变量,即配置文件的根目录。
  987. * Get the $configRoot var.
  988. *
  989. * @access public
  990. * @return string
  991. */
  992. public function getConfigRoot()
  993. {
  994. return $this->configRoot;
  995. }
  996. /**
  997. * 获取$moduleRoot变量,即应用模块的根目录。
  998. * Get the $moduleRoot var.
  999. *
  1000. * @param string $appName
  1001. * @access public
  1002. * @return string
  1003. */
  1004. public function getModuleRoot($appName = '')
  1005. {
  1006. if($appName == '') return $this->moduleRoot;
  1007. return dirname($this->moduleRoot) . DS . $appName . DS;
  1008. }
  1009. /**
  1010. * 获取扩展根目录。
  1011. * Get the root of extension.
  1012. *
  1013. * @access public
  1014. * @return string
  1015. */
  1016. public function getExtensionRoot()
  1017. {
  1018. return $this->basePath . 'extension' . DS;
  1019. }
  1020. /**
  1021. * 获取$webRoot,即应用的路径。
  1022. * Get the $webRoot var.
  1023. *
  1024. * @access public
  1025. * @return string
  1026. */
  1027. public function getWebRoot()
  1028. {
  1029. return $this->config->webRoot;
  1030. }
  1031. /**
  1032. * 获取$themeRoot变量,即主题的根目录。
  1033. * Get the $themeRoot var.
  1034. *
  1035. * @access public
  1036. * @return string
  1037. */
  1038. public function getThemeRoot()
  1039. {
  1040. return $this->themeRoot;
  1041. }
  1042. /**
  1043. * 获取$dataRoot目录
  1044. * Get the $dataRoot var
  1045. *
  1046. * @access public
  1047. * @return string
  1048. */
  1049. public function getDataRoot()
  1050. {
  1051. return $this->dataRoot;
  1052. }
  1053. //------ 客户端环境有关的函数(Client environment related functions) ------//
  1054. /**
  1055. * 根据配置设置当前时区。
  1056. * Set the time zone according to the config.
  1057. *
  1058. * @access public
  1059. * @return void
  1060. */
  1061. public function setTimezone()
  1062. {
  1063. if(isset($this->config->timezone)) date_default_timezone_set($this->config->timezone);
  1064. }
  1065. /**
  1066. * 开启 session
  1067. * Start the session.
  1068. *
  1069. * @access public
  1070. * @return void
  1071. */
  1072. public function startSession()
  1073. {
  1074. if(defined('SESSION_STARTED')) return;
  1075. /* API session use tmp/apisession directory. */
  1076. $apiMode = $this->apiVersion || isset($_GET[$this->config->sessionVar]);
  1077. if(ini_get('session.save_handler') == 'files' || $apiMode)
  1078. {
  1079. $savePath = $this->getTmpRoot() . ($apiMode ? 'apisession' : 'session');
  1080. if(!is_dir($savePath)) mkdir($savePath, 0777, true);
  1081. if(is_writable($savePath))
  1082. {
  1083. session_save_path($savePath);
  1084. $ztSessionHandler = new ztSessionHandler();
  1085. session_set_save_handler($ztSessionHandler, true);
  1086. }
  1087. }
  1088. $sessionName = $this->config->sessionVar;
  1089. session_name($sessionName);
  1090. session_set_cookie_params(0, $this->config->webRoot, '', $this->config->cookieSecure, true);
  1091. if(!session_id()) session_start();
  1092. $this->sessionID = isset($ztSessionHandler) ? $ztSessionHandler->getSessionID() : session_id();
  1093. if(isset($_SERVER['HTTP_TOKEN'])) // If request header has token, use it as session for authentication.
  1094. {
  1095. helper::restartSession($_SERVER['HTTP_TOKEN']);
  1096. $this->sessionID = isset($ztSessionHandler) ? $ztSessionHandler->getSessionID() : session_id();
  1097. }
  1098. elseif(isset($_GET[$this->config->sessionVar]))
  1099. {
  1100. /* 为了避免安全漏洞,必须在从GET参数恢复会话之前记录sessionID,以便在index.php判断session_id() != $app->sessionID后重启会话。*/
  1101. /* To avoid security issue, we have to record sessionID before restart session from GET param, so that we can restart session in index.php when session_id() != $app->sessionID. */
  1102. $this->sessionID = $_GET[$this->config->sessionVar];
  1103. helper::restartSession($_GET[$this->config->sessionVar]);
  1104. }
  1105. define('SESSION_STARTED', true);
  1106. }
  1107. /**
  1108. * 从cookie中获取当前的group, 即URL锚链接'#app=?'。
  1109. * Get current group from cookie, original source is url '#tab=?'.
  1110. *
  1111. * @access public
  1112. * @return void
  1113. */
  1114. public function setOpenApp()
  1115. {
  1116. $module = $this->rawModule;
  1117. $this->tab = 'my';
  1118. if(isset($this->lang->navGroup)) $this->tab = zget($this->lang->navGroup, $module, 'my');
  1119. if(isset($_COOKIE['tab']) && $_COOKIE['tab'] && preg_match('/^\w+$/', $_COOKIE['tab'])) $this->tab = $_COOKIE['tab'];
  1120. if(isset($this->config->zin) && isset($_SERVER['HTTP_X_ZIN_APP'])) $this->tab = $_SERVER['HTTP_X_ZIN_APP'];
  1121. if(!isset($this->lang->mainNav->{$this->tab})) $this->tab = 'my';
  1122. }
  1123. /**
  1124. * 根据用户浏览器的语言设置和服务器配置,选择显示的语言。
  1125. * 优先级:$lang参数 > session > cookie > 浏览器 > 配置文件。
  1126. *
  1127. * Set the language.
  1128. * Using the order of method $lang param, session, cookie, browser and the default lang.
  1129. *
  1130. * @param string $lang zh-cn|zh-tw|zh-hk|en
  1131. * @access public
  1132. * @return void
  1133. */
  1134. public function setClientLang($lang = '')
  1135. {
  1136. if(isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) $this->clientLang = $this->parseHttpAcceptLang();
  1137. if(isset($_COOKIE['lang'])) $this->clientLang = $_COOKIE['lang'];
  1138. if(isset($_SESSION['lang'])) $this->clientLang = $_SESSION['lang'];
  1139. if(!empty($lang)) $this->clientLang = $lang;
  1140. if(!empty($this->clientLang))
  1141. {
  1142. $this->clientLang = strtolower($this->clientLang);
  1143. if(!isset($this->config->langs[$this->clientLang])) $this->clientLang = $this->config->default->lang;
  1144. }
  1145. else
  1146. {
  1147. $this->clientLang = $this->config->default->lang;
  1148. }
  1149. helper::setcookie('lang', (string) $this->clientLang, $this->config->cookieLife, (string) $this->config->webRoot, '', $this->config->cookieSecure, false);
  1150. if(!isset($_COOKIE['lang'])) $_COOKIE['lang'] = $this->clientLang;
  1151. return true;
  1152. }
  1153. /**
  1154. * 从HTTP_ACCEPT_LANGUAGE中剔除去支持的语言。
  1155. * Parse the lang str from HTTP_ACCEPT_LANGUAGE header.
  1156. *
  1157. * @access public
  1158. * @return string
  1159. */
  1160. public function parseHttpAcceptLang()
  1161. {
  1162. if(empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) return '';
  1163. $raw = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
  1164. $pos = strpos((string) $raw, ',');
  1165. $lang = $pos === false ? $raw : substr((string) $raw, 0, $pos);
  1166. /* Fix clientLang for ie >= 10. https://www.drupal.org/node/365615. */
  1167. if(stripos((string) $lang, 'hans')) $lang = 'zh-cn';
  1168. if(stripos((string) $lang, 'hant')) $lang = 'zh-tw';
  1169. return $lang;
  1170. }
  1171. /**
  1172. * 设置客户端使用的主题,判断逻辑与客户端的语言相同。
  1173. * 主题的css和图片文件应该存放在www/theme/$themeName路径。
  1174. *
  1175. * Set the theme the client user using. The logic is same as the clientLang.
  1176. * The css and images files of an theme should saved at www/theme/$themeName
  1177. *
  1178. * @param string $theme
  1179. * @access public
  1180. * @return void
  1181. */
  1182. public function setClientTheme($theme = '')
  1183. {
  1184. if(isset($this->config->client->theme)) $this->clientTheme = $this->config->client->theme;
  1185. if(isset($_COOKIE['theme'])) $this->clientTheme = $_COOKIE['theme'];
  1186. if(!empty($theme)) $this->clientTheme = $theme;
  1187. if(!empty($this->clientTheme))
  1188. {
  1189. $this->clientTheme = strtolower($this->clientTheme);
  1190. if(!isset($this->lang->themes[$this->clientTheme])) $this->clientTheme = $this->config->default->theme;
  1191. }
  1192. else
  1193. {
  1194. $this->clientTheme = $this->config->default->theme;
  1195. }
  1196. helper::setcookie('theme', (string) $this->clientTheme, $this->config->cookieLife, (string) $this->config->webRoot, '', $this->config->cookieSecure, false);
  1197. if(!isset($_COOKIE['theme'])) $_COOKIE['theme'] = $this->clientTheme;
  1198. return true;
  1199. }
  1200. /**
  1201. * 设置客户端的设备类型。
  1202. * Set client device.
  1203. *
  1204. * @access public
  1205. * @return void
  1206. */
  1207. public function setClientDevice()
  1208. {
  1209. $this->clientDevice = 'desktop';
  1210. $cookieDevice = zget($_COOKIE, 'device', 'desktop');
  1211. if($cookieDevice == 'mobile') $this->clientDevice = 'mobile';
  1212. if($cookieDevice == 'desktop') $this->clientDevice = 'desktop';
  1213. if(empty($this->cookie->device) || strpos('mobile,desktop', (string) $this->cookie->device) === false)
  1214. {
  1215. $mobile = new mobile();
  1216. $this->clientDevice = ($mobile->isMobile() and !$mobile->isTablet()) ? 'mobile' : 'desktop';
  1217. }
  1218. helper::setcookie('device', $this->clientDevice, $this->config->cookieLife, (string) $this->config->webRoot, '', $this->config->cookieSecure, true);
  1219. if(!isset($_COOKIE['device'])) $_COOKIE['device'] = $this->clientDevice;
  1220. return $this->clientDevice;
  1221. }
  1222. /**
  1223. * 设置站点代号,可以针对不同的站点来加载不同的扩展。
  1224. * Set the code of current site, thus can load different extension of different site.
  1225. *
  1226. * @access public
  1227. * @return void
  1228. */
  1229. public function setSiteCode()
  1230. {
  1231. return $this->siteCode = helper::parseSiteCode($this->server->http_host);
  1232. }
  1233. /**
  1234. * 获取$clientLang变量,即客户端的语言。
  1235. * Get the $clientLang var.
  1236. *
  1237. * @access public
  1238. * @return string
  1239. */
  1240. public function getClientLang()
  1241. {
  1242. return $this->clientLang;
  1243. }
  1244. /**
  1245. * 获取$clientTheme变量。
  1246. * Get the $clientTheme var.
  1247. *
  1248. * @access public
  1249. * @return string
  1250. */
  1251. public function getClientTheme()
  1252. {
  1253. return $this->config->webRoot . 'theme/' . $this->clientTheme . '/';
  1254. }
  1255. /**
  1256. * 获得客户端的终端设备。
  1257. * Get the client device.
  1258. *
  1259. * @access public
  1260. * @return void
  1261. */
  1262. public function getClientDevice()
  1263. {
  1264. return $this->clientDevice;
  1265. }
  1266. //-------------------- 请求相关的方法(Request related methods) --------------------//
  1267. /**
  1268. * 解析本次请求的入口方法,根据请求的类型(PATH_INFO GET),调用相应的方法。
  1269. * The entrance of parsing request. According to the requestType, call related methods.
  1270. *
  1271. * @access public
  1272. * @return void
  1273. */
  1274. public function parseRequest()
  1275. {
  1276. if(strncmp($_SERVER['REQUEST_URI'], '/data/upload', strlen('/data/upload')) === 0 && !is_file($this->wwwRoot . $_SERVER['REQUEST_URI']))
  1277. {
  1278. helper::setStatus(404);
  1279. helper::end();
  1280. }
  1281. if($this->config->requestType == 'PATH_INFO' or $this->config->requestType == 'PATH_INFO2')
  1282. {
  1283. $this->parsePathInfo();
  1284. $this->setRouteByPathInfo();
  1285. }
  1286. elseif($this->config->requestType == 'GET')
  1287. {
  1288. $this->parseGET();
  1289. $this->setRouteByGET();
  1290. }
  1291. else
  1292. {
  1293. $this->triggerError("The request type {$this->config->requestType} not supported", __FILE__, __LINE__, true);
  1294. }
  1295. }
  1296. /**
  1297. * PATH_INFO方式解析,获取$uri和$viewType。
  1298. * Parse PATH_INFO, get the $uri and $viewType.
  1299. *
  1300. * @access public
  1301. * @return void
  1302. */
  1303. public function parsePathInfo()
  1304. {
  1305. $pathInfo = $this->getPathInfo();
  1306. if(!empty($pathInfo))
  1307. {
  1308. $dotPos = strrpos($pathInfo, '.');
  1309. if($dotPos)
  1310. {
  1311. $this->uri = substr($pathInfo, 0, $dotPos);
  1312. $this->viewType = substr($pathInfo, $dotPos + 1);
  1313. if(strpos((string) $this->config->views, ',' . $this->viewType . ',') === false)
  1314. {
  1315. $this->viewType = $this->config->default->view;
  1316. }
  1317. }
  1318. else
  1319. {
  1320. $this->uri = $pathInfo;
  1321. $this->viewType = $this->config->default->view;
  1322. }
  1323. }
  1324. else
  1325. {
  1326. $this->viewType = $this->config->default->view;
  1327. }
  1328. }
  1329. /**
  1330. * 从$_SERVER或者$_ENV全局变量根据pathinfo变量名获取$PATH_INFO值。
  1331. * PATH_INFO的变量名几乎都是'PATH_INFO',但也有可能是ORIG_PATH_INFO。
  1332. *
  1333. * Get $PATH_INFO from $_SERVER or $_ENV by the pathinfo var name.
  1334. * Mostly, the var name of PATH_INFO is PATH_INFO, but may be ORIG_PATH_INFO.
  1335. *
  1336. * @access public
  1337. * @return string the PATH_INFO
  1338. */
  1339. public function getPathInfo()
  1340. {
  1341. if(isset($_SERVER['PATH_INFO']))
  1342. {
  1343. $value = $_SERVER['PATH_INFO'];
  1344. }
  1345. elseif(isset($_SERVER['ORIG_PATH_INFO']))
  1346. {
  1347. $value = $_SERVER['ORIG_PATH_INFO'];
  1348. }
  1349. elseif(isset($this->uri))
  1350. {
  1351. $value = $this->uri;
  1352. $subpath = str_replace($_SERVER['DOCUMENT_ROOT'], '', dirname((string) $_SERVER['SCRIPT_FILENAME']));
  1353. if($subpath != '/') $subpath = '/' . $subpath;
  1354. if($subpath != '' and $subpath != '/' and strncmp($value, $subpath, strlen($subpath)) === 0) $value = substr($value, strlen($subpath));
  1355. }
  1356. else
  1357. {
  1358. $value = getenv('PATH_INFO');
  1359. if(empty($value)) $value = getenv('ORIG_PATH_INFO');
  1360. }
  1361. if(strpos((string) $value, (string) $_SERVER['SCRIPT_NAME']) !== false) $value = str_replace($_SERVER['SCRIPT_NAME'], '', (string) $value);
  1362. if(strpos((string) $value, '?') === false) return trim((string) $value, '/');
  1363. $value = parse_url((string) $value);
  1364. $pathInfo = trim((string) zget($value, 'path', ''), '/');
  1365. if(trim($pathInfo, '/') == trim((string) $this->config->webRoot, '/')) $pathInfo = '';
  1366. return $pathInfo;
  1367. }
  1368. /**
  1369. * GET请求方式解析,获取$uri和$viewType。
  1370. * Parse GET, get $uri and $viewType.
  1371. *
  1372. * @access public
  1373. * @return void
  1374. */
  1375. public function parseGET()
  1376. {
  1377. if(isset($_GET[$this->config->viewVar]))
  1378. {
  1379. $this->viewType = $_GET[$this->config->viewVar];
  1380. if(strpos((string) $this->config->views, ',' . $this->viewType . ',') === false) $this->viewType = $this->config->default->view;
  1381. }
  1382. else
  1383. {
  1384. $this->viewType = $this->config->default->view;
  1385. }
  1386. $this->uri = $_SERVER['REQUEST_URI'];
  1387. }
  1388. /**
  1389. * 获取$URL。
  1390. * Get the $URL.
  1391. *
  1392. * @param bool $full The URI contains the webRoot if $full is true else only the URI will be return.
  1393. * @access public
  1394. * @return string
  1395. */
  1396. public function getURI($full = false)
  1397. {
  1398. if($full and $this->config->requestType == 'PATH_INFO')
  1399. {
  1400. if($this->uri) return $this->config->webRoot . $this->uri . '.' . $this->viewType;
  1401. return $this->config->webRoot;
  1402. }
  1403. return $this->uri;
  1404. }
  1405. /**
  1406. * 获取$viewType变量。
  1407. * Get the $viewType var.
  1408. *
  1409. * @access public
  1410. * @return string
  1411. */
  1412. public function getViewType()
  1413. {
  1414. return $this->viewType;
  1415. }
  1416. //-------------------- 模块及扩展设置(Module and extension) --------------------//
  1417. /**
  1418. * 加载common模块。
  1419. *
  1420. * common模块比较特别,它会执行几乎每次请求都需要执行的操作,例如:
  1421. * 打开session,检查权限等等。
  1422. * 加载完$lang, $config, $dbh后,需要在入口文件(www/index.php)中手动调用该方法。
  1423. *
  1424. * Load the common module
  1425. *
  1426. * The common module is a special module, which can be used to do some common things. For example:
  1427. * start session, check privilege and so on.
  1428. * This method should called manually in the router file(www/index.php) after the $lang, $config, $dbh loaded.
  1429. *
  1430. * @access public
  1431. * @return commonModel|extcommonModel|object|bool the common model object or false if not exits.
  1432. */
  1433. public function loadCommon()
  1434. {
  1435. $this->setModuleName('common');
  1436. $commonModelFile = $this->setModelFile('common');
  1437. if(!file_exists($commonModelFile)) return false;
  1438. helper::import($commonModelFile);
  1439. if($this->config->framework->extensionLevel == 0 and class_exists('commonModel'))
  1440. {
  1441. $common = new commonModel();
  1442. }
  1443. elseif($this->config->framework->extensionLevel > 0 and class_exists('extCommonModel'))
  1444. {
  1445. $common = new extCommonModel();
  1446. }
  1447. elseif(class_exists('commonModel'))
  1448. {
  1449. $common = new commonModel();
  1450. }
  1451. else
  1452. {
  1453. return false;
  1454. }
  1455. $this->loadLang('company');
  1456. $common->setUserConfig();
  1457. $this->setDebug();
  1458. return $common;
  1459. }
  1460. /**
  1461. * 设置要被调用的模块名。
  1462. * Set the name of the module to be called.
  1463. *
  1464. * @param string $moduleName the module name
  1465. * @access public
  1466. * @return void
  1467. */
  1468. public function setModuleName($moduleName = '')
  1469. {
  1470. if($this->checkModuleName($moduleName)) $this->moduleName = strtolower($moduleName);
  1471. }
  1472. /**
  1473. * 设置要被调用的控制器文件。
  1474. * Set the control file of the module to be called.
  1475. *
  1476. * @param bool $exitIfNone 没有找到该控制器文件的情况:如果该参数为true,则终止程序;如果为false,则打印错误日志
  1477. * The control file was not found: if the parameter is true, the program is terminated;
  1478. * if false, the error log is printed.
  1479. * @access public
  1480. * @return bool
  1481. */
  1482. public function setControlFile($exitIfNone = true)
  1483. {
  1484. $this->controlFile = $this->getModulePath() . 'control.php';
  1485. if(file_exists($this->controlFile)) return true;
  1486. if(!$this->tryResetVision()) $this->triggerError("the control file $this->controlFile not found.", __FILE__, __LINE__, $exitIfNone);
  1487. }
  1488. /**
  1489. * 设置要被调用的方法名。
  1490. * Set the name of the method calling.
  1491. *
  1492. * @param string $methodName
  1493. * @access public
  1494. * @return void
  1495. */
  1496. public function setMethodName($methodName = '')
  1497. {
  1498. if($this->checkMethodName($methodName)) $this->methodName = strtolower($methodName);
  1499. }
  1500. /**
  1501. * 引入 control 文件。
  1502. * Import control file.
  1503. *
  1504. * @access public
  1505. * @return string
  1506. */
  1507. public function importControlFile()
  1508. {
  1509. $moduleName = $this->moduleName;
  1510. /**
  1511. * 引入该模块的control文件。
  1512. * Include the control file of the module.
  1513. */
  1514. $isExt = $this->setActionExtFile();
  1515. if($isExt)
  1516. {
  1517. $controlFile = $this->controlFile;
  1518. spl_autoload_register(function($class) use ($moduleName, $controlFile)
  1519. {
  1520. if($class == $moduleName) helper::import($controlFile);
  1521. });
  1522. }
  1523. $file2Included = $isExt ? $this->extActionFile : $this->controlFile;
  1524. chdir(dirname($file2Included));
  1525. helper::import($file2Included);
  1526. return $file2Included;
  1527. }
  1528. /**
  1529. * 获取要被调用的方法的参数和类型。
  1530. * Get the default valve and param type of the method calling.
  1531. *
  1532. * @access public
  1533. * @return array
  1534. */
  1535. public function getDefaultParams()
  1536. {
  1537. $appName = $this->appName;
  1538. $moduleName = $this->moduleName;
  1539. $methodName = $this->methodName;
  1540. $file2Included = $this->importControlFile();
  1541. $className = class_exists("my$moduleName") ? "my$moduleName" : $moduleName;
  1542. if(!class_exists($className)) $this->triggerError("the control $className not found", __FILE__, __LINE__, true);
  1543. /**
  1544. * 创建control类的实例。
  1545. * Create an instance of the control.
  1546. */
  1547. $module = new $className();
  1548. if(!method_exists($module, $methodName)) $this->triggerError("the module $moduleName has no $methodName method", __FILE__, __LINE__, true);
  1549. $this->control = $module;
  1550. /* Check file is encode by ioncube. */
  1551. $isEncrypted = false;
  1552. if(preg_match('/extension\\' . DS . '(custom|xuan|biz|max|ipd|or)\\' . DS . '/', $file2Included))
  1553. {
  1554. $fp = fopen($file2Included, 'r');
  1555. $line1 = fgets($fp);
  1556. fclose($fp);
  1557. if(strpos($line1, '<?php //') === 0) $isEncrypted = true;
  1558. }
  1559. /* include default value for module. */
  1560. $defaultValueFiles = glob($this->getTmpRoot() . "defaultvalue/*.php");
  1561. if($defaultValueFiles) foreach($defaultValueFiles as $file) include $file;
  1562. /* include default type for module. */
  1563. $defaultTypeFiles = glob($this->getTmpRoot() . "defaulttype/*.php");
  1564. if($defaultTypeFiles) foreach($defaultTypeFiles as $typeFile) include $typeFile;
  1565. /**
  1566. * 使用反射机制获取函数参数的默认值。
  1567. * Get the default settings of the method to be called using the reflecting.
  1568. */
  1569. $defaultParams = array();
  1570. $methodReflect = new reflectionMethod($className, $methodName);
  1571. foreach($methodReflect->getParameters() as $param)
  1572. {
  1573. $name = $param->getName();
  1574. $default = '_NOT_SET';
  1575. if(isset($paramDefaultValue[$appName][$className][$methodName][$name]))
  1576. {
  1577. $default = $paramDefaultValue[$appName][$className][$methodName][$name];
  1578. }
  1579. elseif(isset($paramDefaultValue[$className][$methodName][$name]))
  1580. {
  1581. $default = $paramDefaultValue[$className][$methodName][$name];
  1582. }
  1583. elseif(!$isEncrypted && $param->isDefaultValueAvailable())
  1584. {
  1585. $default = $param->getDefaultValue();
  1586. }
  1587. $type = 'string';
  1588. if(isset($paramDefaultType[$appName][$className][$methodName][$name]))
  1589. {
  1590. $type = $paramDefaultType[$appName][$className][$methodName][$name];
  1591. }
  1592. elseif(isset($paramDefaultType[$className][$methodName][$name]))
  1593. {
  1594. $type = $paramDefaultType[$className][$methodName][$name];
  1595. }
  1596. elseif(!$isEncrypted && method_exists($param, 'hasType') && $param->hasType())
  1597. {
  1598. $paramType = $param->getType();
  1599. if(method_exists($paramType, 'getName')) $type = $paramType->getName();
  1600. }
  1601. $type = strpos($type, '|') ? 'string' : $type;
  1602. $defaultParams[$name] = array('default' => $default, 'type' => $type);
  1603. }
  1604. return $defaultParams;
  1605. }
  1606. /**
  1607. * 设置要被调用方法的参数。
  1608. * Set the params of method calling.
  1609. *
  1610. * @access public
  1611. * @return void
  1612. */
  1613. public function setParams()
  1614. {
  1615. $defaultParams = $this->getDefaultParams();
  1616. /**
  1617. * 根据PATH_INFO或者GET方式设置请求的参数。
  1618. * Set params according PATH_INFO or GET.
  1619. */
  1620. if($this->config->requestType != 'GET')
  1621. {
  1622. $this->setParamsByPathInfo($defaultParams);
  1623. }
  1624. else
  1625. {
  1626. $this->setParamsByGET($defaultParams);
  1627. }
  1628. if ('cli' === PHP_SAPI)
  1629. {
  1630. if ($this->params)
  1631. {
  1632. $this->params = array_merge($defaultParams, $this->params);
  1633. }
  1634. else
  1635. {
  1636. $this->params = $defaultParams;
  1637. }
  1638. }
  1639. else
  1640. {
  1641. if($this->config->framework->filterParam == 2)
  1642. {
  1643. $_GET = validater::filterParam($_GET, 'get');
  1644. $_COOKIE = validater::filterParam($_COOKIE, 'cookie');
  1645. }
  1646. }
  1647. $this->rawParams = $this->params;
  1648. return true;
  1649. }
  1650. /**
  1651. * 获取一个模块的路径。
  1652. * Get the path of one module.
  1653. *
  1654. * @param string $appName the app name
  1655. * @param string $moduleName the module name
  1656. * @access public
  1657. * @return string the module path
  1658. */
  1659. public function getModulePath($appName = '', $moduleName = '')
  1660. {
  1661. if($moduleName == '') $moduleName = $this->moduleName;
  1662. $moduleName = strtolower($moduleName);
  1663. if($this->checkModuleName($moduleName))
  1664. {
  1665. $modulePath = $this->getExtensionRoot() . 'saas' . DS . $moduleName . DS;
  1666. if(is_dir($modulePath) and (file_exists($modulePath . 'control.php') or file_exists($modulePath . 'model.php'))) return $modulePath;
  1667. /* 1. 尝试在定制开发中寻找。 Finally, try to find the module in the custom dir. */
  1668. $modulePath = $this->getExtensionRoot() . 'custom' . DS . $moduleName . DS;
  1669. if(is_dir($modulePath) and (file_exists($modulePath . 'control.php') or file_exists($modulePath . 'model.php'))) return $modulePath;
  1670. /* 2. 如果设置过vision,尝试在vision中查找。 If vision is set, try to find the module in the vision. */
  1671. if($this->config->vision != 'rnd')
  1672. {
  1673. $modulePath = $this->getExtensionRoot() . $this->config->vision . DS . $moduleName . DS;
  1674. if(is_dir($modulePath) and (file_exists($modulePath . 'control.php') or file_exists($modulePath . 'model.php'))) return $modulePath;
  1675. }
  1676. /* 3. 尝试查找商业版本是否有此模块。 Try to find the module in other editon. */
  1677. if($this->config->edition != 'open')
  1678. {
  1679. $modulePath = $this->getExtensionRoot() . $this->config->edition . DS . $moduleName . DS;
  1680. if(is_dir($modulePath) and (file_exists($modulePath . 'control.php') or file_exists($modulePath . 'model.php'))) return $modulePath;
  1681. }
  1682. /* 4. 尝试查找喧喧是否有此模块。 Try to find the module in xuan. */
  1683. $modulePath = $this->getExtensionRoot() . 'xuan' . DS . $moduleName . DS;
  1684. if(is_dir($modulePath) and (file_exists($modulePath . 'control.php') or file_exists($modulePath . 'model.php'))) return $modulePath;
  1685. /* 5. 使用通用版本里的模块。 If module is in the open edition, use it. */
  1686. return $this->getModuleRoot($appName) . $moduleName . DS;
  1687. }
  1688. }
  1689. /**
  1690. * 获取一个模块的扩展路径。 Get extension path of one module.
  1691. *
  1692. * If the extensionLevel == 0, return empty array.
  1693. * If the extensionLevel == 1, return the common extension directory.
  1694. * If the extensionLevel == 2, return the common and site extension directories.
  1695. *
  1696. * @param string $appName the app name
  1697. * @param string $moduleName the module name
  1698. * @param string $ext the extension type, can be control|model|view|lang|config|zen|tao
  1699. * @access public
  1700. * @return array the extension path.
  1701. */
  1702. public function getModuleExtPath($moduleName, $ext)
  1703. {
  1704. $saasExtPath = $this->getExtensionRoot() . 'saas' . DS . $moduleName . DS . 'ext' . DS . $ext . DS;
  1705. /* 检查失败或者extensionLevel为0,直接返回空。If check failed or extensionLevel == 0, return empty array. */
  1706. if(!$this->checkModuleName($moduleName) or $this->config->framework->extensionLevel == 0) return array('saas' => $saasExtPath);
  1707. $paths = array();
  1708. /* When extensionLevel == 1. */
  1709. $paths['common'] = $this->config->edition != 'open' ? $this->getExtensionRoot() . $this->config->edition . DS . $moduleName . DS . 'ext' . DS . $ext . DS : '';
  1710. $paths['xuan'] = $this->getExtensionRoot() . 'xuan' . DS . $moduleName . DS . 'ext' . DS . $ext . DS;
  1711. $paths['vision'] = $this->config->vision == 'rnd' ? '' : $this->basePath . 'extension' . DS . $this->config->vision . DS . $moduleName . DS . 'ext' . DS . $ext . DS;
  1712. $paths['custom'] = $this->getExtensionRoot() . 'custom' . DS . $moduleName . DS . 'ext' . DS . $ext . DS;
  1713. if($this->config->framework->extensionLevel == 1)
  1714. {
  1715. $paths['saas'] = $saasExtPath;
  1716. return $paths;
  1717. }
  1718. /* When extensionLevel == 2. */
  1719. $paths['site'] = empty($this->siteCode) ? '' : $this->getExtensionRoot() . $this->config->edition . DS . $moduleName . DS . 'ext' . DS . '_' . $this->siteCode . DS . $ext . DS;
  1720. $paths['saas'] = $saasExtPath;
  1721. return $paths;
  1722. }
  1723. /**
  1724. * 检查模块中某一个变量必须为英文字母和数字组合。Check module a variable must be ascii.
  1725. *
  1726. * @param string $var
  1727. * @param bool $exit
  1728. * @access public
  1729. * @return bool
  1730. */
  1731. public function checkModuleName($var, $exit = true)
  1732. {
  1733. global $filter;
  1734. static $checkedModule = array();
  1735. if(!isset($checkedModule[$var]))
  1736. {
  1737. $rule = $filter->default->moduleName;
  1738. $result = validater::checkByRule($var, $rule);
  1739. $checkedModule[$var] = $result;
  1740. }
  1741. if($checkedModule[$var]) return true;
  1742. if(!$exit) return false;
  1743. if(!$var) return false;
  1744. $this->triggerError("'$var' illegal. ", __FILE__, __LINE__, true);
  1745. }
  1746. /**
  1747. * 检查方法中某一个变量必须为英文字母和数字组合。Check method a variable must be ascii.
  1748. *
  1749. * @param string $var
  1750. * @param bool $exit
  1751. * @access public
  1752. * @return bool
  1753. */
  1754. public function checkMethodName($var, $exit = true)
  1755. {
  1756. global $filter;
  1757. $rule = $filter->default->methodName;
  1758. if($this->config->framework->filterParam == 2 and isset($filter->{$this->moduleName}->methodName)) $rule = $filter->{$this->moduleName}->methodName;
  1759. if(validater::checkByRule($var, $rule)) return true;
  1760. if(!$exit) return false;
  1761. $this->triggerError("'$var' illegal. ", __FILE__, __LINE__, $exit = true);
  1762. }
  1763. /**
  1764. * 设置Action的扩展文件。 Set the action extension file.
  1765. *
  1766. * @access public
  1767. * @return bool
  1768. */
  1769. public function setActionExtFile()
  1770. {
  1771. $moduleExtPaths = $this->getModuleExtPath($this->moduleName, 'control');
  1772. /* 如果扩展目录为空,不包含任何扩展文件。If there's no ext paths return false.*/
  1773. if(empty($moduleExtPaths)) return false;
  1774. if(!empty($moduleExtPaths['saas']))
  1775. {
  1776. $this->extActionFile = $moduleExtPaths['saas'] . $this->methodName . '.php';
  1777. if(file_exists($this->extActionFile)) return true;
  1778. }
  1779. /* 1. 如果extensionLevel == 2,且扩展文件存在,返回该站点扩展文件。 If extensionLevel == 2 and site extensionFile exists, return it. */
  1780. if($this->config->framework->extensionLevel == 2 and !empty($moduleExtPaths['site']))
  1781. {
  1782. $this->extActionFile = $moduleExtPaths['site'] . $this->methodName . '.php';
  1783. if(file_exists($this->extActionFile)) return true;
  1784. }
  1785. /* 2. 尝试在定制开发目录寻找扩展文件。Then try to find the custom extension file. */
  1786. $this->extActionFile = $moduleExtPaths['custom'] . $this->methodName . '.php';
  1787. if(file_exists($this->extActionFile)) return true;
  1788. /* 3. 如果设置过vision,尝试在vision中查找扩展文件。If vision is set, try to find the vision extension file. */
  1789. if($moduleExtPaths['vision']) $this->extActionFile = $moduleExtPaths['vision'] . $this->methodName . '.php';
  1790. if(file_exists($this->extActionFile)) return true;
  1791. /* 4. 在喧喧目录中查找扩展文件。Then try to find the xuan extension file. */
  1792. if($moduleExtPaths['xuan']) $this->extActionFile = $moduleExtPaths['xuan'] . $this->methodName . '.php';
  1793. if(file_exists($this->extActionFile)) return true;
  1794. /* 5. 最后尝试寻找公共扩展文件。Finally, try to find the common extension file. */
  1795. $this->extActionFile = $moduleExtPaths['common'] . $this->methodName . '.php';
  1796. if(empty($moduleExtPaths['common'])) return false;
  1797. return file_exists($this->extActionFile);
  1798. }
  1799. /**
  1800. * Check for API extend.
  1801. *
  1802. * @access public
  1803. * @return bool
  1804. */
  1805. public function checkAPIFile()
  1806. {
  1807. $moduleExtPaths = $this->getModuleExtPath($this->moduleName, 'control');
  1808. /* 如果扩展目录为空,不包含任何扩展文件。If there's no ext paths return false.*/
  1809. if(empty($moduleExtPaths)) return false;
  1810. /* 如果extensionLevel == 2,且扩展文件存在,返回该站点扩展文件。If extensionLevel == 2 and site extensionFile exists, return it. */
  1811. if($this->config->framework->extensionLevel == 2 and !empty($moduleExtPaths['site']))
  1812. {
  1813. $locateFile = $moduleExtPaths['site'] . $this->methodName . '.302';
  1814. if(file_exists($locateFile)) $this->sendAPI($locateFile);
  1815. $requestFile = $moduleExtPaths['site'] . $this->methodName . '.api';
  1816. if(file_exists($requestFile)) $this->sendAPI($requestFile);
  1817. }
  1818. /* 然后再尝试寻找公共扩展文件。Then try to find the common extension file. */
  1819. $locateFile = $moduleExtPaths['common'] . $this->methodName . '.302';
  1820. if(file_exists($locateFile)) $this->sendAPI($locateFile);
  1821. $requestFile = $moduleExtPaths['common'] . $this->methodName . '.api';
  1822. if(file_exists($requestFile)) $this->sendAPI($requestFile);
  1823. return false;
  1824. }
  1825. /**
  1826. * 设置一个模块的model文件,如果存在model扩展,一起合并。
  1827. * Set the model file of one module. If there's an extension file, merge it with the main model file.
  1828. *
  1829. * @param string $moduleName 模块名,如果为空,使用当前模块。The module name, if empty, use current module's name.
  1830. * @param string $appName 应用名,如果为空,使用当前应用。The app name, if empty, use current app's name.
  1831. * @access public
  1832. * @return string the model file
  1833. */
  1834. public function setModelFile($moduleName, $appName = '')
  1835. {
  1836. return $this->setTargetFile($moduleName, $appName);
  1837. }
  1838. /**
  1839. * 设置一个模块的target文件,如果存在target扩展,一起合并。
  1840. * Set the target file of one module. If there's an extension file, merge it with the main target file.
  1841. *
  1842. * @param string $moduleName 模块名,如果为空,使用当前模块。The module name, if empty, use current module's name.
  1843. * @param string $appName 应用名,如果为空,使用当前应用。The app name, if empty, use current app's name.
  1844. * @param string $class 对象的类型,可选值 target、zen、tao,默认为 target。The type of the object, optional values target, zen, tao, the default is target.
  1845. * @access public
  1846. * @return string the target file
  1847. */
  1848. public function setTargetFile($moduleName, $appName = '', $class = 'model')
  1849. {
  1850. if($appName == '') $appName = $this->getAppName();
  1851. /* 设置主target文件。 Set the main target file. */
  1852. $mainTargetFile = $this->getModulePath($appName, $moduleName) . "{$class}.php";
  1853. if($this->config->framework->extensionLevel == 0) return $mainTargetFile;
  1854. /* 计算扩展的文件和hook文件。Compute the extension files and hook files. */
  1855. $hookFiles = array();
  1856. $extFiles = array();
  1857. $apiFiles = array();
  1858. $siteExtended = false;
  1859. $targetExtPaths = $this->getModuleExtPath($moduleName, $class);
  1860. foreach($targetExtPaths as $extType => $targetExtPath)
  1861. {
  1862. if(empty($targetExtPath)) continue;
  1863. $tmpHookFiles = helper::ls($targetExtPath . 'hook/', '.php');
  1864. $tmpExtFiles = helper::ls($targetExtPath, '.php');
  1865. $tmpAPIFiles = helper::ls($targetExtPath, '.api');
  1866. $hookFiles = array_merge($hookFiles, $tmpHookFiles);
  1867. $extFiles = array_merge($extFiles, $tmpExtFiles);
  1868. $apiFiles = array_merge($apiFiles, $tmpAPIFiles);
  1869. if($extType == 'site' and (!empty($tmpHookFiles) or !empty($tmpExtFiles) or !empty($tmpAPIFiles))) $siteExtended = true;
  1870. }
  1871. /* 如果没有扩展文件,返回主文件。 If no extension or hook files, return the main file directly. */
  1872. if(empty($extFiles) and empty($hookFiles) and empty($apiFiles)) return $mainTargetFile;
  1873. /* 计算合并之后的targetFile路径。Compute the merged target file path. */
  1874. $runMode = PHP_SAPI == 'cli' ? '_cli' : '';
  1875. $extTargetPrefix = $this->config->edition . $runMode . DS . $this->config->vision . DS;
  1876. if($siteExtended and !empty($this->siteCode)) $extTargetPrefix .= $this->siteCode[0] . DS . $this->siteCode;
  1877. $mergedTargetDir = $this->getTmpRoot() . $class . DS . $extTargetPrefix;
  1878. $mergedTargetFile = $mergedTargetDir . $moduleName . '.php';
  1879. if(!is_dir($mergedTargetDir))
  1880. {
  1881. $made = mkdir($mergedTargetDir, 0755, true);
  1882. if(!$made && !empty($this->config->debug)) helper::end("ERROR: Can NOT create required directory '{$mergedTargetDir}', please create it manually with correct permission.");
  1883. }
  1884. /* 判断生成的缓存文件是否需要更新。 Judge whether the merged target file needed update or not. */
  1885. if(!$this->needTargetFileUpdate($mergedTargetFile, $extFiles, $hookFiles, $apiFiles, $targetExtPaths, $mainTargetFile)) return $mergedTargetFile;
  1886. /* 合并扩展和hook文件。Merge the extension and hook files. */
  1887. $targetLines = $this->mergeTargetExtFiles($moduleName, $extFiles, $mergedTargetDir, $class);
  1888. $this->mergeTargetHookFiles($moduleName, $mainTargetFile, $targetLines, $hookFiles, $mergedTargetDir, $mergedTargetFile, $apiFiles, $class);
  1889. return $mergedTargetFile;
  1890. }
  1891. /**
  1892. * 检查合并之后的target文件是否需要更新。Check whether the merged target file need update or not.
  1893. *
  1894. * @param string $mergedTargetFile
  1895. * @param array $extFiles
  1896. * @param array $hookFiles
  1897. * @param array $apiFiles
  1898. * @param array $targetExtPaths
  1899. * @param string $mainTargetFile
  1900. * @access public
  1901. * @return bool
  1902. */
  1903. public function needTargetFileUpdate($mergedTargetFile, $extFiles, $hookFiles, $apiFiles, $targetExtPaths, $mainTargetFile)
  1904. {
  1905. $lastTime = file_exists($mergedTargetFile) ? filemtime($mergedTargetFile) : 0;
  1906. foreach($extFiles as $extFile) if(filemtime($extFile) > $lastTime) return true;
  1907. foreach($hookFiles as $hookFile) if(filemtime($hookFile) > $lastTime) return true;
  1908. foreach($apiFiles as $apiFile) if(filemtime($apiFile) > $lastTime) return true;
  1909. $targetExtPath = $targetExtPaths['common'];
  1910. $targetHookPath = $targetExtPaths['common'] . 'hook/';
  1911. if(is_dir($targetExtPath ) and filemtime($targetExtPath) > $lastTime) return true;
  1912. if(is_dir($targetHookPath) and filemtime($targetHookPath) > $lastTime) return true;
  1913. if(!empty($targetExtPaths['site']))
  1914. {
  1915. $targetExtPath = $targetExtPaths['site'];
  1916. $targetHookPath = $targetExtPaths['site'] . 'hook/';
  1917. if(is_dir($targetExtPath ) and filemtime($targetExtPath) > $lastTime) return true;
  1918. if(is_dir($targetHookPath) and filemtime($targetHookPath) > $lastTime) return true;
  1919. }
  1920. if(filemtime($mainTargetFile) > $lastTime) return true;
  1921. return false;
  1922. }
  1923. /**
  1924. * 将target的扩展文件合并在一起。Merge target ext files.
  1925. *
  1926. * @param string $moduleName
  1927. * @param array $extFiles
  1928. * @param string $mergedTargetDir
  1929. * @param string $class model | zen | tao
  1930. * @access public
  1931. * @return string
  1932. */
  1933. public function mergeTargetExtFiles($moduleName, $extFiles, $mergedTargetDir, $class = 'model')
  1934. {
  1935. /* 设置类名。Set the class names. */
  1936. $targetClass = $moduleName . ucfirst($class);
  1937. $tmpTargetClass = "tmpExt$targetClass";
  1938. /* 开始拼装代码。Prepare the codes. */
  1939. $targetLines = "<?php\n";
  1940. $targetLines .= "global \$app;\n";
  1941. $targetLines .= "helper::import(\$app->getModulePath('', '$moduleName') . " . "'$class.php');\n";
  1942. $targetLines .= "class $tmpTargetClass extends $targetClass \n{\n";
  1943. /* 将扩展文件的代码合并到代码中。Cycle all the extension files and merge them into target lines. */
  1944. $extTargets = array();
  1945. foreach($extFiles as $extFile) $extTargets[basename((string) $extFile)] = $extFile;
  1946. foreach($extTargets as $extTarget) $targetLines .= static::removePHPTAG($extTarget);
  1947. /* 做个标记,方便后面替换代码使用。Make a mark for replacing codes. */
  1948. $replaceMark = '//**//';
  1949. $targetLines .= "\n$replaceMark\n}";
  1950. /* 生成一个临时的target扩展文件,并加载,用于后续的hook文件加载使用。Create a tmp merged target file and import it for merge hook codes using. */
  1951. $tmpTargetFile = $mergedTargetDir . "tmp$moduleName.php";
  1952. if(file_put_contents($tmpTargetFile, $targetLines))
  1953. {
  1954. if(!class_exists($tmpTargetClass)) include $tmpTargetFile;
  1955. return $targetLines;
  1956. }
  1957. $this->triggerError("ERROR: $tmpTargetFile not writable.", __FILE__, __LINE__, true);
  1958. }
  1959. /**
  1960. * 合并target的hook脚本。Merge hook files for a target.
  1961. *
  1962. * @param string $moduleName
  1963. * @param string $mainTargetFile
  1964. * @param string $targetLines
  1965. * @param array $hookFiles
  1966. * @param string $mergedTargetDir
  1967. * @param string $mergedTargetFile
  1968. * @param array $apiFiles
  1969. * @param string $class model | zen | tao
  1970. * @access public
  1971. * @return void
  1972. */
  1973. public function mergeTargetHookFiles($moduleName, $mainTargetFile, $targetLines, $hookFiles, $mergedTargetDir, $mergedTargetFile, $apiFiles, $class = 'model')
  1974. {
  1975. /* 定义相关变量。Init vars. */
  1976. $targetClass = $moduleName . ucfirst($class);
  1977. $extTargetClass = 'ext' . $targetClass;
  1978. $tmpTargetClass = 'tmpExt' . $targetClass;
  1979. $tmpTargetFile = $mergedTargetDir . "tmp$moduleName.php";
  1980. $replaceMark = '//**//';
  1981. /* 读取hook文件。Get hook codes need to merge. */
  1982. $hookCodes = array();
  1983. foreach($apiFiles as $apiFile)
  1984. {
  1985. /* 通过文件名获得其对应的方法名。Get methods according it's filename. */
  1986. $fileName = baseName((string) $apiFile);
  1987. [$method] = explode('.', $fileName);
  1988. $url = static::extractAPIURL($apiFile);
  1989. if($url) $hookCodes[$method][] = "return helper::requestAPI('$url');";
  1990. }
  1991. foreach($hookFiles as $hookFile)
  1992. {
  1993. /* 通过文件名获得其对应的方法名。Get methods according it's filename. */
  1994. $fileName = baseName((string) $hookFile);
  1995. [$method] = explode('.', $fileName);
  1996. $hookCodes[$method][] = static::removePHPTAG($hookFile);
  1997. }
  1998. /* 合并Hook文件。Cycle the hook methods and merge hook codes. */
  1999. $hookedMethods = array_keys($hookCodes);
  2000. $mainTargetCodes = file($mainTargetFile);
  2001. $mergedTargetCodes = file($tmpTargetFile);
  2002. /* 如果已经合并,不需要再合并了。 If merged by other thread, return directly. */
  2003. if(!$mergedTargetCodes) return;
  2004. foreach($hookedMethods as $method)
  2005. {
  2006. /* 通过反射获得hook脚本对应的方法所在的文件和起止行数。Reflection the hooked method to get it's defined position. */
  2007. if(!method_exists($tmpTargetClass, $method)) continue;
  2008. $methodRelfection = new reflectionMethod($tmpTargetClass, $method);
  2009. $definedFile = $methodRelfection->getFileName();
  2010. $startLine = $methodRelfection->getStartLine();
  2011. $endLine = $methodRelfection->getEndLine();
  2012. /* 将Hook脚本和老的代码合并在一起,并替换原来的定义。Merge hook codes with old codes and replace back. */
  2013. $oldCodes = $definedFile == $tmpTargetFile ? $mergedTargetCodes : $mainTargetCodes;
  2014. $oldCodes = implode("", array_slice($oldCodes, $startLine - 1, $endLine - $startLine + 1));
  2015. $openBrace = strpos($oldCodes, '{');
  2016. $newCodes = substr($oldCodes, 0, $openBrace + 1) . "\n" . implode("\n", $hookCodes[$method]) . substr($oldCodes, $openBrace + 1);
  2017. if($definedFile == $tmpTargetFile) $targetLines = str_replace($oldCodes, $newCodes, $targetLines);
  2018. if($definedFile != $tmpTargetFile) $targetLines = str_replace($replaceMark, $newCodes . "\n$replaceMark", $targetLines);
  2019. }
  2020. /* 保存最终的Target文件。Save the last merged target file. */
  2021. $targetLines = str_replace($tmpTargetClass, $extTargetClass, $targetLines);
  2022. file_put_contents($mergedTargetFile, $targetLines);
  2023. unlink($tmpTargetFile);
  2024. }
  2025. /**
  2026. * Remove tags of PHP
  2027. *
  2028. * @param string $fileName
  2029. * @static
  2030. * @access public
  2031. * @return string
  2032. */
  2033. public static function removePHPTAG($fileName)
  2034. {
  2035. $code = trim(file_get_contents($fileName));
  2036. if(strncmp($code, '<?php', strlen('<?php')) === 0) $code = ltrim($code, '<?php');
  2037. $code = ltrim($code);
  2038. if(strncmp($code, 'declare(strict_types=1);', strlen('declare(strict_types=1);')) === 0) $code = ltrim($code, 'declare(strict_types=1);');
  2039. if(strrpos($code, '?' . '>') !== false) $code = rtrim($code, '?' . '>');
  2040. return trim($code);
  2041. }
  2042. /**
  2043. * Extract API url from api file.
  2044. *
  2045. * @param string $fileName
  2046. * @static
  2047. * @access public
  2048. * @return string
  2049. */
  2050. public static function extractAPIURL($fileName)
  2051. {
  2052. global $config;
  2053. $url = '';
  2054. $lines = file($fileName);
  2055. foreach($lines as $line)
  2056. {
  2057. $line = trim((string) $line);
  2058. if(empty($line)) continue;
  2059. if(preg_match('/^https?\:\/\//', $line))
  2060. {
  2061. $url = $line;
  2062. break;
  2063. }
  2064. }
  2065. if(empty($url)) return '';
  2066. return $url;
  2067. }
  2068. /**
  2069. * Send API.
  2070. *
  2071. * @param string $apiFile
  2072. * @access public
  2073. * @return void
  2074. */
  2075. public function sendAPI($apiFile)
  2076. {
  2077. $css = null;
  2078. $js = null;
  2079. $extension = substr($apiFile, strrpos($apiFile, '.') + 1);
  2080. if($extension != '302' and $extension != 'api') return false;
  2081. $lines = file($apiFile);
  2082. $url = '';
  2083. foreach($lines as $line)
  2084. {
  2085. $line = trim((string) $line);
  2086. if(empty($line)) continue;
  2087. if(preg_match('/^https?\:\/\//', $line))
  2088. {
  2089. $url = $line;
  2090. break;
  2091. }
  2092. }
  2093. if(empty($url)) return false;
  2094. $url .= (strpos($url, '?') !== false ? '&' : '?') . $this->config->sessionVar . '=' . session_id() . '&account=' . $_SESSION['user']->account;
  2095. if($extension == '302')
  2096. {
  2097. helper::header('location', $url);
  2098. helper::end();
  2099. }
  2100. if($extension == 'api')
  2101. {
  2102. $response = common::http($url);
  2103. $headFile = $this->moduleRoot . 'common/view/header.html.php';
  2104. $footFile = $this->moduleRoot . 'common/view/footer.html.php';
  2105. $obLevel = ob_get_level();
  2106. for($i = 0; $i < $obLevel; $i++) ob_end_clean();
  2107. $viewFiles = $this->control->setViewFile($this->moduleName, $this->methodName);
  2108. if($css) $this->control->view->pageCSS = $css;
  2109. if($js) $this->control->view->pageJS = $js;
  2110. $output = '';
  2111. $output .= $this->control->printViewFile($headFile);
  2112. $output .= $response;
  2113. if(isset($viewFiles['hookFiles'])) foreach($viewFiles['hookFiles'] as $hookFile) $output .= $this->control->printViewFile($hookFile);
  2114. $output .= $this->control->printViewFile($footFile);
  2115. helper::end($output);
  2116. }
  2117. }
  2118. //-------------------- 路由相关方法(Routing related methods) --------------------//
  2119. /**
  2120. * 设置路由(PATH_INFO 方式):
  2121. * 1.设置模块名;
  2122. * 2.设置方法名;
  2123. * 3.设置控制器文件。
  2124. *
  2125. * Set the route according to PATH_INFO.
  2126. * 1. set the module name.
  2127. * 2. set the method name.
  2128. * 3. set the control file.
  2129. *
  2130. * @access public
  2131. * @return void
  2132. */
  2133. public function setRouteByPathInfo()
  2134. {
  2135. if(!empty($this->uri))
  2136. {
  2137. /*
  2138. * 根据$requestFix分割符,分割网址。
  2139. * There's the request separator, split the URI by it.
  2140. **/
  2141. if(strpos($this->uri, (string) $this->config->requestFix) !== false)
  2142. {
  2143. $items = explode($this->config->requestFix, $this->uri);
  2144. $this->setModuleName($items[0]);
  2145. $this->setMethodName($items[1]);
  2146. }
  2147. /*
  2148. * 如果网址中没有分隔符,使用默认的方法。
  2149. * No request separator, use the default method name.
  2150. **/
  2151. else
  2152. {
  2153. $this->setModuleName($this->uri);
  2154. $this->setMethodName($this->config->default->method);
  2155. }
  2156. }
  2157. else
  2158. {
  2159. $this->setModuleName($this->config->default->module); // 使用默认模块 use the default module.
  2160. $this->setMethodName($this->config->default->method); // 使用默认方法 use the default method.
  2161. }
  2162. $this->setControlFile();
  2163. }
  2164. /**
  2165. * 设置路由(GET 方式):
  2166. * 1.设置模块名;
  2167. * 2.设置方法名;
  2168. * 3.设置控制器文件。
  2169. *
  2170. * Set the route according to GET.
  2171. * 1. set the module name.
  2172. * 2. set the method name.
  2173. * 3. set the control file.
  2174. *
  2175. * @access public
  2176. * @return void
  2177. */
  2178. public function setRouteByGET()
  2179. {
  2180. $moduleName = isset($_GET[$this->config->moduleVar]) ? strtolower((string) $_GET[$this->config->moduleVar]) : $this->config->default->module;
  2181. $methodName = isset($_GET[$this->config->methodVar]) ? strtolower((string) $_GET[$this->config->methodVar]) : $this->config->default->method;
  2182. $this->setModuleName($moduleName);
  2183. $this->setMethodName($methodName);
  2184. $this->setControlFile();
  2185. }
  2186. /**
  2187. * 加载一个模块:
  2188. * 1. 引入控制器文件或扩展的方法文件;
  2189. * 2. 创建control对象;
  2190. * 3. 解析url,得到请求的参数;
  2191. * 4. 使用call_user_function_array调用相应的方法。
  2192. *
  2193. * Load a module.
  2194. * 1. include the control file or the extension action file.
  2195. * 2. create the control object.
  2196. * 3. set the params passed in through url.
  2197. * 4. call the method by call_user_function_array
  2198. *
  2199. * @access public
  2200. * @return bool|object if the module object of die.
  2201. */
  2202. public function loadModule()
  2203. {
  2204. if(is_null($this->params) and !$this->setParams())
  2205. {
  2206. $this->outputXhprof();
  2207. return false;
  2208. }
  2209. /* 调用该方法 Call the method. */
  2210. $module = $this->control;
  2211. $method = $this->methodName ? $this->methodName : $this->config->default->method;
  2212. call_user_func_array(array($module, $method), $this->params);
  2213. $this->checkAPIFile();
  2214. $this->outputXhprof();
  2215. return $module;
  2216. }
  2217. /**
  2218. * 加载指定模块下的某种对象。
  2219. * Load the target object of one module.
  2220. *
  2221. * @param string $moduleName 模块名,如果为空,使用当前模块。The module name, if empty, use current module's name.
  2222. * @param string $appName 应用名,如果为空,使用当前应用。The app name, if empty, use current app's name.
  2223. * @param string $class 对象的类型,可选值 model、zen、tao,默认为 model。The type of the object, optional values model, zen, tao, the default is model.
  2224. * @access public
  2225. * @return object|bool 如果没有model文件,返回false,否则返回model对象。If no model file, return false, else return the model object.
  2226. */
  2227. public function loadTarget($moduleName = '', $appName = '', $class = 'model')
  2228. {
  2229. if(empty($moduleName)) $moduleName = $this->moduleName;
  2230. if(empty($appName)) $appName = $this->appName;
  2231. if(isset(self::$loadedTargets[$class][$appName][$moduleName])) return self::$loadedTargets[$class][$appName][$moduleName];
  2232. $targetFile = $this->setTargetFile($moduleName, $appName, $class);
  2233. /**
  2234. * 如果没有target文件,返回false。
  2235. * If no target file, return false.
  2236. */
  2237. if(!helper::import($targetFile)) return false;
  2238. /**
  2239. * 如果没有扩展文件,target类名是$moduleName + $class,如果有扩展,还需要增加ext前缀。
  2240. * If no extension file, target class name is $moduleName + $class, else with 'ext' as the prefix.
  2241. */
  2242. $targetClass = class_exists('ext' . $appName . $moduleName. $class) ? 'ext' . $appName . $moduleName . $class : $appName . $moduleName . $class;
  2243. if(!class_exists($targetClass))
  2244. {
  2245. $targetClass = class_exists('ext' . $moduleName. $class) ? 'ext' . $moduleName . $class : $moduleName . $class;
  2246. if(!class_exists($targetClass)) $this->triggerError(" The $class $targetClass not found", __FILE__, __LINE__, true);
  2247. }
  2248. /**
  2249. * 因为zen继承自control,tao继承自model,构造函数里会调用loadTarget方法,赋默认值值防止递归调用。
  2250. */
  2251. if($class == 'zen' || $class == 'tao') self::$loadedTargets[$class][$appName][$moduleName] = false;
  2252. /**
  2253. * 初始化target 对象并返回。
  2254. * Init the target object and return it.
  2255. */
  2256. $target = new $targetClass($appName);
  2257. self::$loadedTargets[$class][$appName][$moduleName] = $target;
  2258. return $target;
  2259. }
  2260. /**
  2261. * 设置请求的参数(PATH_INFO 方式)。
  2262. * Set the params by PATH_INFO.
  2263. *
  2264. * @param array $defaultParams the default settings of the params.
  2265. * @param string $type
  2266. * @access public
  2267. * @return void
  2268. */
  2269. public function setParamsByPathInfo($defaultParams = array(), $type = '')
  2270. {
  2271. $params = array();
  2272. if($type != 'fetch')
  2273. {
  2274. /* 分割URI。 Spit the URI. */
  2275. $items = explode($this->config->requestFix, (string)$this->uri);
  2276. $itemCount = count($items);
  2277. /**
  2278. * 前两项为模块名和方法名,参数从下标2开始。
  2279. * The first two item is moduleName and methodName. So the params should begin at 2.
  2280. **/
  2281. for($i = 2; $i < $itemCount; $i ++)
  2282. {
  2283. $key = key($defaultParams); // Get key from the $defaultParams.
  2284. if(empty($key)) continue;
  2285. $params[$key] = $items[$i];
  2286. next($defaultParams);
  2287. }
  2288. }
  2289. $this->params = $this->mergeParams($defaultParams, $params);
  2290. }
  2291. /**
  2292. * 设置请求的参数(GET 方式)。
  2293. * Set the params by GET.
  2294. *
  2295. * @param array $defaultParams the default settings of the params.
  2296. * @param string $type
  2297. * @access public
  2298. * @return void
  2299. */
  2300. public function setParamsByGET($defaultParams, $type = '')
  2301. {
  2302. $params = array();
  2303. if($type != 'fetch')
  2304. {
  2305. /* Unset moduleVar, methodVar, viewVar and session 变量, 剩下的作为参数。 */
  2306. /* Unset the moduleVar, methodVar, viewVar and session var, all the left are the params. */
  2307. unset($_GET[$this->config->moduleVar]);
  2308. unset($_GET[$this->config->methodVar]);
  2309. unset($_GET[$this->config->viewVar]);
  2310. unset($_GET[$this->config->sessionVar]);
  2311. $params = $_GET;
  2312. }
  2313. /* Fix bug #3267. Param 'words' is not validated when searching. */
  2314. if($this->rawModule == 'search' and $this->rawMethod == 'index') unset($params['words']);
  2315. $this->params = $this->mergeParams($defaultParams, $params);
  2316. }
  2317. /**
  2318. * 合并请求的参数和默认参数,这样就可以省略已经有默认值的参数了。
  2319. * Merge the params passed in and the default params. Thus, the params which have default values needn't pass value, just like a function.
  2320. *
  2321. * @param array $defaultParams the default params defined by the method.
  2322. * @param array $passedParams the params passed in through url.
  2323. * @access public
  2324. * @return array the merged params.
  2325. */
  2326. public function mergeParams($defaultParams, $passedParams)
  2327. {
  2328. global $filter;
  2329. /* Remove these three params. */
  2330. unset($passedParams['onlybody']);
  2331. unset($passedParams['tid']);
  2332. unset($passedParams['zin']);
  2333. unset($passedParams['HTTP_X_REQUESTED_WITH']);
  2334. /* Check params from URL. */
  2335. $nameRule = $filter->{$this->moduleName}->{$this->methodName}->paramName ?? $filter->default->paramName;
  2336. foreach($passedParams as $param => $value)
  2337. {
  2338. if(!validater::checkByRule($param, $nameRule)) helper::end('Bad Request!');
  2339. $valueRule = $filter->default->paramValue;
  2340. if(isset($filter->{$this->moduleName}->{$this->methodName}->paramValue[$param]))
  2341. {
  2342. $valueRule = $filter->{$this->moduleName}->{$this->methodName}->paramValue[$param];
  2343. }
  2344. if($value and !validater::checkByRule($value, $valueRule)) helper::end('Bad Request!');
  2345. }
  2346. $passedParams = array_values($passedParams);
  2347. $i = 0;
  2348. foreach($defaultParams as $key => $defaultItem)
  2349. {
  2350. if(isset($passedParams[$i]))
  2351. {
  2352. $defaultParams[$key] = helper::convertType(strip_tags((string) $passedParams[$i]), $defaultItem['type']);
  2353. }
  2354. else
  2355. {
  2356. if($defaultItem['default'] === '_NOT_SET') $this->triggerError("The param '$key' should pass value. ", __FILE__, __LINE__, true);
  2357. $defaultParams[$key] = helper::convertType(strip_tags((string) $defaultItem['default']), $defaultItem['type']);
  2358. }
  2359. $i ++;
  2360. }
  2361. return $defaultParams;
  2362. }
  2363. /**
  2364. * 获取$moduleName变量。
  2365. * Get the $moduleName var.
  2366. *
  2367. * @access public
  2368. * @return string
  2369. */
  2370. public function getModuleName()
  2371. {
  2372. return $this->moduleName;
  2373. }
  2374. /**
  2375. * 获取$controlFile变量。
  2376. * Get the $controlFile var.
  2377. *
  2378. * @access public
  2379. * @return string
  2380. */
  2381. public function getControlFile()
  2382. {
  2383. return $this->controlFile;
  2384. }
  2385. /**
  2386. * 获取$methodName变量。
  2387. * Get the $methodName var.
  2388. *
  2389. * @access public
  2390. * @return string
  2391. */
  2392. public function getMethodName()
  2393. {
  2394. return $this->methodName;
  2395. }
  2396. /**
  2397. * 获取$param变量。
  2398. * Get the $param var.
  2399. *
  2400. * @access public
  2401. * @return array
  2402. */
  2403. public function getParams()
  2404. {
  2405. return $this->params;
  2406. }
  2407. //-------------------- 常用的工具方法(Tool methods) ------------------//
  2408. /**
  2409. * 从类库中加载一个类文件。
  2410. *
  2411. * Load a class file.
  2412. *
  2413. * @param string $className the class name
  2414. * @param bool $static statis class or not
  2415. * @access public
  2416. * @return object|bool the instance of the class or just true.
  2417. */
  2418. public function loadClass($className, $static = false)
  2419. {
  2420. $className = strtolower($className);
  2421. /* 搜索$coreLibRoot(Search in $coreLibRoot) */
  2422. $classFile = $this->coreLibRoot . $className . DS . $className . '.class.php';
  2423. if(!helper::import($classFile)) $this->triggerError("class file $classFile not found", __FILE__, __LINE__, true);
  2424. /* 如果是静态调用,则返回(If static, return) */
  2425. if($static) return true;
  2426. /* 实例化该类(Instance it) */
  2427. global ${$className};
  2428. if(!class_exists($className)) $this->triggerError("the class $className not found in $classFile", __FILE__, __LINE__, true);
  2429. if(!is_object(${$className})) ${$className} = new $className();
  2430. return ${$className};
  2431. }
  2432. /**
  2433. * 加载整个应用公共的配置文件。
  2434. * Load the common config files for the app.
  2435. *
  2436. * @access public
  2437. * @return void
  2438. */
  2439. public function loadMainConfig()
  2440. {
  2441. /* 初始化$config对象。Init the $config object. */
  2442. global $config, $filter;
  2443. if(!is_object($config)) $config = new config();
  2444. /* 加载主配置文件。 Load the main config file. */
  2445. $mainConfigFile = $this->configRoot . 'config.php';
  2446. if(!file_exists($mainConfigFile)) $this->triggerError("The main config file $mainConfigFile not found", __FILE__, __LINE__, true);
  2447. include $mainConfigFile;
  2448. $this->config = $config;
  2449. }
  2450. /**
  2451. * 当multiSite功能打开的时候,加载额外的配置文件。
  2452. * When multiSite feature enabled, load extra config file.
  2453. *
  2454. * @access public
  2455. * @return void
  2456. */
  2457. public function loadExtraConfig()
  2458. {
  2459. global $config;
  2460. $multiConfigFile = $this->configRoot . 'multi.php';
  2461. if(file_exists($multiConfigFile)) include $multiConfigFile;
  2462. $siteConfigFile = $this->configRoot . "sites/{$this->siteCode}.php";
  2463. if(file_exists($siteConfigFile)) include $siteConfigFile;
  2464. }
  2465. /**
  2466. * 加载模块的config文件,返回全局$config对象。
  2467. * 如果该模块是common,加载$configRoot的配置文件,其他模块则加载其模块的配置文件。
  2468. *
  2469. * Load config and return it as the global config object.
  2470. * If the module is common, search in $configRoot, else in $modulePath.
  2471. *
  2472. * @param string $moduleName module name
  2473. * @param string $appName app name
  2474. * @access public
  2475. * @return void
  2476. */
  2477. public function loadModuleConfig($moduleName, $appName = '')
  2478. {
  2479. if(isset(self::$loadedConfigs[$moduleName])) return false;
  2480. self::$loadedConfigs[$moduleName] = $moduleName;
  2481. global $config;
  2482. if($config and (!isset($config->$moduleName) or !is_object($config->$moduleName))) $config->$moduleName = new stdclass();
  2483. /* 将主配置文件和扩展配置文件合并在一起。Put the main config file and extension config files together. */
  2484. $configFiles = $this->getMainAndExtFiles($moduleName, $appName, 'config');
  2485. if(empty($configFiles)) return false;
  2486. /* 加载每一个配置文件。Load every config file. */
  2487. foreach($configFiles as $configFile)
  2488. {
  2489. if(isset(self::$loadedConfigs[$configFile])) continue;
  2490. if(is_string($configFile) && file_exists($configFile)) include $configFile;
  2491. self::$loadedConfigs[$configFile] = $configFile;
  2492. }
  2493. /* 加载数据库中与本模块相关的配置项。Merge from the db configs. */
  2494. if($moduleName != 'common')
  2495. {
  2496. if(isset($config->systemDB->$moduleName)) $this->mergeConfig($config->systemDB->$moduleName, $moduleName);
  2497. if(isset($config->personalDB->$moduleName)) $this->mergeConfig($config->personalDB->$moduleName, $moduleName);
  2498. }
  2499. }
  2500. /**
  2501. * Merge db config.
  2502. *
  2503. * @param array $dbConfig
  2504. * @param string $moduleName
  2505. * @access public
  2506. * @return void
  2507. */
  2508. public function mergeConfig($dbConfig, $moduleName = 'common')
  2509. {
  2510. global $config;
  2511. /* 如果没有设置本模块配置,则首先进行初始化。Init the $config->$moduleName if not set.*/
  2512. if($moduleName != 'common' and !isset($config->$moduleName)) $config->$moduleName = new stdclass();
  2513. $config2Merge = $config;
  2514. if($moduleName != 'common') $config2Merge = $config->$moduleName;
  2515. foreach($dbConfig as $item)
  2516. {
  2517. if($item->section)
  2518. {
  2519. if(!isset($config2Merge->{$item->section})) $config2Merge->{$item->section} = new stdclass();
  2520. if(is_object($config2Merge->{$item->section}))
  2521. {
  2522. $config2Merge->{$item->section}->{$item->key} = $item->value;
  2523. }
  2524. }
  2525. else
  2526. {
  2527. $config2Merge->{$item->key} = $item->value;
  2528. }
  2529. }
  2530. }
  2531. /**
  2532. * 向客户端输出配置参数,客户端可以根据这些参数实现和调整请求的逻辑。
  2533. * Export the config params to the client, thus the client can adjust it's logic according the config.
  2534. *
  2535. * @access public
  2536. * @return string
  2537. */
  2538. public function exportConfig()
  2539. {
  2540. $view = new stdclass();
  2541. $view->version = $this->config->version;
  2542. $view->requestType = $this->config->requestType;
  2543. $view->requestFix = $this->config->requestFix;
  2544. $view->moduleVar = $this->config->moduleVar;
  2545. $view->methodVar = $this->config->methodVar;
  2546. $view->viewVar = $this->config->viewVar;
  2547. $view->sessionVar = $this->config->sessionVar;
  2548. $view->systemMode = $this->config->systemMode;
  2549. $view->sprintConcept = zget($this->config->custom, 'sprintConcept', '0');
  2550. $view->URAndSR = zget($this->config->custom, 'URAndSR', '0');
  2551. $view->maxUploadSize = strtoupper(ini_get('upload_max_filesize'));
  2552. $this->session->set('random', mt_rand(0, 10000));
  2553. $view->sessionName = session_name();
  2554. $view->sessionID = session_id();
  2555. $view->random = $this->session->random;
  2556. $view->expiredTime = ini_get('session.gc_maxlifetime');
  2557. $view->serverTime = time();
  2558. return json_encode($view);
  2559. }
  2560. /**
  2561. * 获取主语言文件。
  2562. * Get main lang file.
  2563. *
  2564. * @param string $moduleName the module name
  2565. * @param string $appName the app name
  2566. * @access public
  2567. * @return string.
  2568. */
  2569. private function getMainLangFile($moduleName, $appName = '')
  2570. {
  2571. $path = $moduleName . DS . 'lang' . DS . $this->clientLang . '.php';
  2572. $modulePath = $this->getExtensionRoot() . 'saas' . DS;
  2573. if(file_exists($modulePath . $path)) return $modulePath . $path;
  2574. /* 1. 最后尝试在定制开发中寻找。 Finally, try to find the module in the custom dir. */
  2575. $modulePath = $this->getExtensionRoot() . 'custom' . DS;
  2576. if(file_exists($modulePath . $path)) return $modulePath . $path;
  2577. /* 2. 如果设置过vision,尝试在vision中查找。 If vision is set, try to find the module in the vision. */
  2578. if($this->config->vision != 'rnd')
  2579. {
  2580. $modulePath = $this->getExtensionRoot() . $this->config->vision . DS;
  2581. if(file_exists($modulePath . $path)) return $modulePath . $path;
  2582. }
  2583. /* 3. 尝试查找商业版本是否有此模块。 Try to find the module in other editon. */
  2584. if($this->config->edition != 'open')
  2585. {
  2586. $modulePath = $this->getExtensionRoot() . $this->config->edition . DS;
  2587. if(file_exists($modulePath . $path)) return $modulePath . $path;
  2588. }
  2589. /* 4. 尝试查找喧喧是否有此模块。 Try to find the module in other editon. */
  2590. $modulePath = $this->getExtensionRoot() . 'xuan' . DS;
  2591. if(file_exists($modulePath . $path)) return $modulePath . $path;
  2592. /* 5. 如果通用版本里有此模块,优先使用。 If module is in the open edition, use it. */
  2593. $modulePath = $this->getModuleRoot($appName);
  2594. if(file_exists($modulePath . $path)) return $modulePath . $path;
  2595. return '';
  2596. }
  2597. /**
  2598. * 加载语言文件,返回全局$lang对象。
  2599. * Load lang and return it as the global lang object.
  2600. *
  2601. * @param string $moduleName the module name
  2602. * @param string $appName the app name
  2603. * @access public
  2604. * @return object the lang object
  2605. */
  2606. public function loadLang($moduleName, $appName = '')
  2607. {
  2608. /* 加载语言文件。Load lang files. */
  2609. global $lang;
  2610. if(!is_object($lang)) $lang = new language();
  2611. if(!isset($lang->$moduleName)) $lang->$moduleName = new stdclass();
  2612. if(isset(self::$loadedLangs[$moduleName])) return $lang;
  2613. self::$loadedLangs[$moduleName] = $moduleName;
  2614. /* 计算最终要加载的语言文件。 Get the lang files to be loaded. */
  2615. $langFilesToLoad = $this->getMainAndExtFiles($moduleName, $appName, 'lang');
  2616. if(empty($langFilesToLoad)) return $lang;
  2617. foreach($langFilesToLoad as $langFile)
  2618. {
  2619. if(isset(self::$loadedLangs[$langFile])) continue;
  2620. include $langFile;
  2621. self::$loadedLangs[$langFile] = $langFile;
  2622. }
  2623. $this->lang = $lang;
  2624. return $lang;
  2625. }
  2626. /**
  2627. * 连接数据库。
  2628. * Connect to database.
  2629. *
  2630. * @access public
  2631. * @return void
  2632. */
  2633. public function connectDB()
  2634. {
  2635. global $dbh, $slaveDBH;
  2636. if(!isset($this->config->installed) or !$this->config->installed) return;
  2637. /* Set master db. */
  2638. if(isset($this->config->db->host)) $this->dbh = $dbh = $this->connectByPDO($this->config->db, 'MASTER');
  2639. /* Set slave db. */
  2640. if(!empty($this->config->slaveDBList))
  2641. {
  2642. $biIndex = 0;
  2643. $slaveList = array();
  2644. foreach($this->config->slaveDBList as $index => $db)
  2645. {
  2646. if(isset($db->type) && $db->type == 'bi')
  2647. {
  2648. $biIndex = $index;
  2649. }
  2650. else
  2651. {
  2652. $slaveList[] = $index;
  2653. }
  2654. }
  2655. $slaveIndex = empty($slaveList) ? $biIndex : $slaveList[array_rand($slaveList)];
  2656. $this->config->biDB = $this->initSlaveDB($biIndex);
  2657. $this->slaveDBH = $slaveDBH = $this->connectByPDO($this->initSlaveDB($slaveIndex), 'SLAVE');
  2658. }
  2659. $this->loadDAO();
  2660. }
  2661. /**
  2662. * 加载DAO。
  2663. * Load DAO.
  2664. *
  2665. * @access public
  2666. * @return void
  2667. */
  2668. public function loadDAO()
  2669. {
  2670. global $dao;
  2671. $driver = $this->config->db->driver;
  2672. $classFile = $this->coreLibRoot . 'dao' . DS . $driver . '.class.php';
  2673. include_once($classFile);
  2674. $dao = new $driver($this);
  2675. $this->dao = $dao;
  2676. $this->loadClass('mao', true);
  2677. $this->mao = new mao($this);
  2678. }
  2679. /**
  2680. * 加载缓存类,初始化全局缓存对象。
  2681. * Load the cache class and init the global cache object.
  2682. *
  2683. * @access public
  2684. * @return void
  2685. */
  2686. private function loadCache()
  2687. {
  2688. if(!$this->checkInstalled()) return false;
  2689. if(!empty($this->installing) || !empty($this->upgrading)) return false;
  2690. $cacheConfig = $this->dao->select('`key`, value')->from(TABLE_CONFIG)->where('owner')->eq('system')->andWhere('module')->eq('common')->andWhere('section')->eq('cache')->fetchPairs();
  2691. foreach($cacheConfig as $key => $value) $this->config->cache->$key = $value;
  2692. if(!$this->config->cache->enable) return;
  2693. if($this->config->cache->driver == 'redis')
  2694. {
  2695. $redisConfig = $this->dao->select('`key`, value')->from(TABLE_CONFIG)->where('owner')->eq('system')->andWhere('module')->eq('common')->andWhere('section')->eq('redis')->fetchPairs();
  2696. foreach($redisConfig as $key => $value) $this->config->redis->$key = $value;
  2697. }
  2698. $tables = $this->dao->showTables();
  2699. foreach($tables as $table)
  2700. {
  2701. $table = current($table);
  2702. if($table == '`' . TABLE_AUTOCACHE . '`')
  2703. {
  2704. $autoCache = $this->dao->select('code, fields')->from(TABLE_AUTOCACHE)->fetchPairs();
  2705. foreach($autoCache as $code => $fields)
  2706. {
  2707. $table = "`{$this->config->db->prefix}{$code}`";
  2708. $name = 'CACHE_' . strtoupper($code) . '_AUTO';
  2709. $this->config->cache->res[$table][] =['name' => $name, 'fields' => explode(',', $fields)];
  2710. }
  2711. }
  2712. }
  2713. $this->loadClass('cache', true);
  2714. $this->cache = new Zentao\Cache\cache($this);
  2715. /* 为 dao 和 mao 设置访问缓存的对象。 Set the cache object for dao and mao. */
  2716. $this->dao->cache = $this->cache;
  2717. $this->mao->cache = $this->cache;
  2718. }
  2719. /**
  2720. * Init config of slave db.
  2721. *
  2722. * @param int $slaveIndex
  2723. * @access private
  2724. * @return object
  2725. */
  2726. private function initSlaveDB($slaveIndex = 0)
  2727. {
  2728. global $config;
  2729. $slaveDB = $config->slaveDBList[$slaveIndex];
  2730. $slaveDB->persistant = $config->db->persistent;
  2731. $slaveDB->driver = $config->db->driver;
  2732. $slaveDB->encoding = $config->db->encoding;
  2733. $slaveDB->strictMode = $config->db->strictMode;
  2734. $slaveDB->prefix = $config->db->prefix;
  2735. return $slaveDB;
  2736. }
  2737. /**
  2738. * 使用PDO连接数据库。
  2739. * Connect database by PDO.
  2740. *
  2741. * @param object $params the database params.
  2742. * @param string $flag the database flag.
  2743. * @access public
  2744. * @return object|bool
  2745. */
  2746. public function connectByPDO($params, $flag = 'MASTER')
  2747. {
  2748. if(!isset($params->driver)) static::triggerError('no pdo driver defined, it should be mysql or sqlite', __FILE__, __LINE__, true);
  2749. if(!isset($params->user)) return false;
  2750. try
  2751. {
  2752. $dbh = new dbh($params, true, $flag);
  2753. /*
  2754. * 如果系统是Linux,开启仿真预处理和缓冲查询。
  2755. * If run on linux, set emulatePrepare and bufferQuery to true.
  2756. **/
  2757. if(!isset($params->emulatePrepare) and PHP_OS == 'Linux') $params->emulatePrepare = true;
  2758. if(!isset($params->bufferQuery) and PHP_OS == 'Linux') $params->bufferQuery = true;
  2759. if(!$this->apiVersion) $params->emulatePrepare = false;
  2760. $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
  2761. $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  2762. $dbh->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_TO_STRING);
  2763. if(isset($params->emulatePrepare)) $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, $params->emulatePrepare);
  2764. if(isset($params->bufferQuery)) $dbh->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, $params->bufferQuery);
  2765. return $dbh;
  2766. }
  2767. catch(PDOException $exception)
  2768. {
  2769. $message = $exception->getMessage();
  2770. if(empty($message))
  2771. {
  2772. /* Try to repair table. */
  2773. helper::header('location', "{$this->config->webRoot}checktable.php");
  2774. helper::end();
  2775. }
  2776. static::triggerError($message, __FILE__, __LINE__, true);
  2777. }
  2778. }
  2779. /**
  2780. * 连接SQLite数据库。
  2781. * Connect SQLite database.
  2782. *
  2783. * @param object $params
  2784. * @access public
  2785. * @return object
  2786. */
  2787. public function connectSqlite($params = null)
  2788. {
  2789. if(empty($params))
  2790. {
  2791. $params = new stdclass();
  2792. $params->file = $this->getTmpRoot() . 'sqlite.db';
  2793. }
  2794. return new sqlite($params);
  2795. }
  2796. /**
  2797. * Query from database, use master or slave db.
  2798. *
  2799. * @param string $query
  2800. * @access public
  2801. * @return mixed
  2802. */
  2803. public function dbQuery($query)
  2804. {
  2805. if(!$this->dbh) return false;
  2806. if($this->slaveDBH && strtolower(substr($query, 0, 6)) == 'select') return $this->slaveDBH->query($query);
  2807. return $this->dbh->query($query);
  2808. }
  2809. //-------------------- 错误处理方法(Error methods) ------------------//
  2810. /**
  2811. * 程序停止时执行的函数。
  2812. * The shutdown handler.
  2813. *
  2814. * @access public
  2815. * @return void
  2816. */
  2817. public function shutdown()
  2818. {
  2819. /* 如果开启了缓存则关闭缓存连接,主要用于 Redis 等缓存服务。Close the cache connection if it's open. */
  2820. if(!empty($this->cache)) $this->cache->close();
  2821. /* 如果debug模式开启,保存sql语句(If debug on, save sql queries) */
  2822. if(!empty($this->config->debug)) $this->saveSQL();
  2823. /*
  2824. * 发现错误,保存到日志中。
  2825. * If any error occurs, save it.
  2826. * */
  2827. if(!function_exists('error_get_last')) return;
  2828. $error = error_get_last();
  2829. if($error)
  2830. {
  2831. $this->saveError($error['type'], $error['message'], $error['file'], $error['line']);
  2832. if(!empty($this->config->debug) && (!is_dir($this->logRoot) || !is_writable($this->logRoot))) helper::end('ERROR: ' . $error['message']);
  2833. }
  2834. }
  2835. /**
  2836. * 触发一个错误。
  2837. * Trigger an error.
  2838. *
  2839. * @param string $message 错误信息 error message
  2840. * @param string $file 所在文件 the file error occurs
  2841. * @param int $line 错误行 the line error occurs
  2842. * @param bool $exit 是否停止程序 exit the program or not
  2843. * @access public
  2844. * @return void
  2845. */
  2846. public function triggerError($message, $file, $line, $exit = false)
  2847. {
  2848. $function = null;
  2849. /* 设置错误信息(Set the error info) */
  2850. $message = htmlSpecialString($message);
  2851. if(preg_match('/[^\x00-\x80]/', $message)) $message = helper::convertEncoding($message, 'gbk');
  2852. /* Only show error when debug is open. */
  2853. if(!$this->config->debug) die;
  2854. $log = (new Exception())->getTraceAsString(); /* Print a backtrace like debug_print_backtrace(). */
  2855. $log = str_replace($this->basePath, '', $log); /* Remove the base path from the backtrace. */
  2856. $log .= "ERROR: $message in $file on line $line";
  2857. if(isset($_SERVER['SCRIPT_URI'])) $log .= ", request: $_SERVER[SCRIPT_URI]";
  2858. $trace = debug_backtrace();
  2859. extract($trace[0]);
  2860. extract($trace[1]);
  2861. $log .= ", last called by $file on line $line through function $function.\n";
  2862. /* Change absolute path to relative path. */
  2863. $log = str_replace($this->basePath, '', $log);
  2864. /* 触发错误(Trigger the error) */
  2865. $exit ? helper::end($log) : trigger_error($log, E_USER_WARNING);
  2866. }
  2867. /**
  2868. * 保存错误信息。
  2869. * Save error info.
  2870. *
  2871. * @param int $level
  2872. * @param string $message
  2873. * @param string $file
  2874. * @param int $line
  2875. * @access public
  2876. * @return void
  2877. */
  2878. public function saveError($level, $message, $file, $line)
  2879. {
  2880. if(empty($this->config->debug)) return true;
  2881. if(!is_dir($this->logRoot)) return true;
  2882. if(!is_writable($this->logRoot)) return true;
  2883. /*
  2884. * 删除设定时间之前的日志。
  2885. * Delete the log before the set time.
  2886. **/
  2887. if(mt_rand(0, 10) == 1)
  2888. {
  2889. $logDays = $this->config->framework->logDays ?? 14;
  2890. $dayTime = time() - $logDays * 24 * 3600;
  2891. foreach(glob($this->getLogRoot() . '*') as $logFile)
  2892. {
  2893. if(filemtime($logFile) <= $dayTime) unlink($logFile);
  2894. }
  2895. }
  2896. /*
  2897. * 忽略该错误:Redefining already defined constructor。
  2898. * Skip the error: Redefining already defined constructor.
  2899. **/
  2900. if(strpos($message, 'Redefining') !== false) return true;
  2901. /*
  2902. * 设置错误信息。
  2903. * Set the error info.
  2904. **/
  2905. if(preg_match('/[^\x00-\x80]/', $message)) $message = helper::convertEncoding($message, 'gbk');
  2906. $errorLog = "\n" . date('H:i:s') . " $message in <strong>$file</strong> on line <strong>$line</strong> ";
  2907. $uri = $this->getURI();
  2908. $errorLog .= "when visiting <strong>" . (empty($uri) ? '' : htmlspecialchars($uri)) . "</strong>\n";
  2909. /*
  2910. * 为了安全起见,对公网环境隐藏脚本路径。
  2911. * If the ip is pulic, hidden the full path of scripts.
  2912. */
  2913. $remoteIP = helper::getRemoteIp(true);
  2914. if(!defined('IN_SHELL') and !($remoteIP == '127.0.0.1' or filter_var($remoteIP, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE) === false))
  2915. {
  2916. $errorLog = str_replace($this->getBasePath(), '', $errorLog);
  2917. }
  2918. /* 保存到日志文件(Save to log file) */
  2919. $runMode = PHP_SAPI == 'cli' ? '_cli' : '';
  2920. $errorFile = $this->logRoot . "php$runMode." . date('Ymd') . '.log.php';
  2921. if(!is_file($errorFile)) file_put_contents($errorFile, "<?php\n die();\n?" . ">\n");
  2922. $fh = fopen($errorFile, 'a');
  2923. if($fh) fwrite($fh, strip_tags(htmlspecialchars_decode($errorLog))) and fclose($fh);
  2924. /*
  2925. * 如果debug > 1,直接在页面显示非严重错误。
  2926. * If the debug > 1, show non-serious errors on page directly.
  2927. **/
  2928. if(!empty($this->config->debug) && $this->config->debug > 1)
  2929. {
  2930. /* Send non-serious errors to page in zin mode. */
  2931. $isZinRequest = isset($_SERVER['HTTP_X_ZIN_OPTIONS']);
  2932. $isNonSeriousError = $level !== E_ERROR && $level !== E_PARSE && $level !== E_CORE_ERROR && $level !== E_COMPILE_ERROR;
  2933. if($isZinRequest && $isNonSeriousError)
  2934. {
  2935. /* Generate error stack trace info. */
  2936. $e = new Exception();
  2937. $trace = $e->getTraceAsString();
  2938. $this->zinErrors[] = array('file' => $file, 'line' => $line, 'message' => $message, 'level' => $level, 'trace' => $trace);
  2939. return;
  2940. }
  2941. /* Show non-serious errors to classic page. */
  2942. if($level == E_NOTICE or $level == E_WARNING or $level == 8192)
  2943. {
  2944. $cmd = "vim +$line $file";
  2945. $size = strlen($cmd);
  2946. echo "<pre class='alert alert-danger'>$message: ";
  2947. echo "<input type='text' value='$cmd' size='$size' style='border:none; background:none;' onclick='this.select();' /></pre>";
  2948. }
  2949. }
  2950. /*
  2951. * 如果是严重错误,由shutdown触发,会停止程序。
  2952. * If error level is serious, die by shutdown function.
  2953. * */
  2954. if(in_array($level, array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR)))
  2955. {
  2956. if(empty($this->config->debug)) return true;
  2957. if(PHP_SAPI == 'cli')
  2958. {
  2959. echo $errorLog;
  2960. }
  2961. else
  2962. {
  2963. $htmlError = "<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8' /></head>";
  2964. $htmlError .= "<body>" . nl2br($errorLog) . "</body></html>";
  2965. echo $htmlError;
  2966. }
  2967. }
  2968. return true;
  2969. }
  2970. /**
  2971. * 保存sql语句。
  2972. * Save the sql.
  2973. *
  2974. * @access public
  2975. * @return void
  2976. */
  2977. public function saveSQL()
  2978. {
  2979. if(!$this->config->debug) return true;
  2980. if(!class_exists('dao')) return;
  2981. $runMode = PHP_SAPI == 'cli' ? '_cli' : '';
  2982. $sqlLog = $this->getLogRoot() . "sql$runMode." . date('Ymd') . '.log.php';
  2983. if(!is_file($sqlLog)) file_put_contents($sqlLog, "<?php\n die();\n?" . ">\n");
  2984. if(!is_writable($sqlLog)) return false;
  2985. $fh = fopen($sqlLog, 'a');
  2986. fwrite($fh, date('Ymd H:i:s') . ": " . $this->getURI() . "\n");
  2987. foreach(dbh::$queries as $key => $query)
  2988. {
  2989. if(!empty(dbh::$flags)) fwrite($fh, ' #Flag: ' . (dbh::$flags[$key] ?? '') . "\n");
  2990. if(!empty(dbh::$durations)) fwrite($fh, ' #Query_time: ' . (dbh::$durations[$key] ?? '') . "\n");
  2991. if(!empty(dbh::$traces)) fwrite($fh, ' #Trigger_code: ' . (dbh::$traces[$key] ?? '') . "\n");
  2992. fwrite($fh, " $query\n");
  2993. }
  2994. fwrite($fh, "\n");
  2995. fclose($fh);
  2996. }
  2997. /**
  2998. * Check app if it is run in a container.
  2999. *
  3000. * @access public
  3001. * @return bool
  3002. */
  3003. public function isContainer()
  3004. {
  3005. return strtolower((string)getenv('IS_CONTAINER')) == 'true';
  3006. }
  3007. /**
  3008. * Check app if it can be upgraded automatically.
  3009. *
  3010. * @access public
  3011. * @return bool
  3012. */
  3013. public function canAutoUpgrade()
  3014. {
  3015. return strtolower((string)getenv('ZT_AUTO_UPGRADE')) == 'true';
  3016. }
  3017. /**
  3018. * Check app if it need check safe file.
  3019. *
  3020. * @access public
  3021. * @return bool
  3022. */
  3023. public function hasValidSafeFile()
  3024. {
  3025. return strtolower((string)getenv('ZT_CHECK_SAFE_FILE')) == 'true';
  3026. }
  3027. /**
  3028. * Get main file and ext files.
  3029. *
  3030. * @param string $moduleName
  3031. * @param string $appName
  3032. * @param string $type lang|config|control|model
  3033. * @access public
  3034. * @return array
  3035. */
  3036. public function getMainAndExtFiles($moduleName, $appName = '', $type = 'lang')
  3037. {
  3038. $clientLang = null;
  3039. /* 初始化变量。Init vars. */
  3040. $modulePath = $this->getModulePath($appName, $moduleName);
  3041. $extFiles = array();
  3042. $filesToLoad = array();
  3043. /* 判断主文件是否存在。Whether the main file exists or not. */
  3044. if($type == 'lang')
  3045. {
  3046. $mainFile = $this->getMainLangFile($moduleName, $appName);
  3047. }
  3048. else
  3049. {
  3050. $mainFile = $modulePath . $type . '.php';
  3051. }
  3052. if($mainFile) $filesToLoad[] = $mainFile;
  3053. if($type == 'config')
  3054. {
  3055. $filesToLoad[] = helper::ls($modulePath . DS . 'config', '.php');
  3056. }
  3057. /* 获取扩展文件。If extensionLevel > 0, get extension files. */
  3058. if($this->config->framework->extensionLevel > 0)
  3059. {
  3060. $commonExtFiles = array();
  3061. $siteExtFiles = array();
  3062. $extPath = $this->getModuleExtPath($moduleName, $type);
  3063. if($this->config->framework->extensionLevel >= 1)
  3064. {
  3065. $clientLang = $type == 'lang' ? $this->clientLang : '';
  3066. if(!empty($extPath['common'])) $commonExtFiles = helper::ls($extPath['common'] . $clientLang, '.php');
  3067. if(!empty($extPath['xuan'])) $commonExtFiles = array_merge($commonExtFiles, helper::ls($extPath['xuan'] . $clientLang, '.php'));
  3068. if(!empty($extPath['vision'])) $commonExtFiles = array_merge($commonExtFiles, helper::ls($extPath['vision'] . $clientLang, '.php'));
  3069. if(!empty($extPath['custom'])) $commonExtFiles = array_merge($commonExtFiles, helper::ls($extPath['custom'] . $clientLang, '.php'));
  3070. if(!empty($extPath['saas'])) $commonExtFiles = array_merge($commonExtFiles, helper::ls($extPath['saas'] . $clientLang, '.php'));
  3071. }
  3072. if($this->config->framework->extensionLevel == 2 and !empty($extPath['site'])) $siteExtFiles = helper::ls($extPath['site'] . $clientLang, '.php');
  3073. $extFiles = array_merge($commonExtFiles, $siteExtFiles);
  3074. }
  3075. /* 计算最终要加载的文件。 Get the files to be loaded. */
  3076. $filesToLoad = array_merge($filesToLoad, $extFiles);
  3077. if(empty($filesToLoad)) return false;
  3078. return $filesToLoad;
  3079. }
  3080. /**
  3081. * 检查是否已安装禅道,主要用于DevOps平台版。
  3082. * Check zentao is installed.
  3083. *
  3084. * @access public
  3085. * @return bool
  3086. */
  3087. public function checkInstalled()
  3088. {
  3089. $installed = true;
  3090. if(!isset($this->config->installed) || !$this->config->installed) $installed = false;
  3091. if(($this->config->inContainer || $this->config->inQuickon) && $installed && !$this->getInstalledVersion()) $installed = false;
  3092. if(!isset($_SESSION['installing']) && !$installed && !empty($this->installing)) $_SESSION['installing'] = true;
  3093. return $installed;
  3094. }
  3095. /**
  3096. * 检查是否需要升级。如果需要升级并且开启了缓存,清空缓存以避免直接执行 SQL 语句导致数据不一致。
  3097. * Check if need upgrade. If need upgrade and cache is open, clear the cache to avoid data inconsistency caused by direct execution of SQL statements.
  3098. *
  3099. * @access public
  3100. * @return bool
  3101. */
  3102. public function checkNeedUpgrade()
  3103. {
  3104. $installedVersion = $this->getInstalledVersion();
  3105. if($installedVersion == $this->config->version) return false;
  3106. if(!empty($this->cache)) $this->cache->clear();
  3107. return true;
  3108. }
  3109. }
  3110. class config
  3111. {
  3112. /**
  3113. * 设置成员变量,成员可以是'db.user'类似的格式。
  3114. * Set the value of a member. the member can be the format like db.user.
  3115. *
  3116. * <code>
  3117. * <?php
  3118. * $config->set('db.user', 'wwccss');
  3119. * ?>
  3120. * </code>
  3121. * @param string $key the key of the member
  3122. * @param mixed $value the value
  3123. * @access public
  3124. * @return void
  3125. */
  3126. public function set($key, $value)
  3127. {
  3128. helper::setMember('config', $key, $value);
  3129. }
  3130. }
  3131. /**
  3132. * lang类。
  3133. * The lang class.
  3134. *
  3135. * @package framework
  3136. */
  3137. class language
  3138. {
  3139. /**
  3140. * 设置成员变量,成员可以是'db.user'类似的格式。
  3141. * Set the value of a member. the member can be the foramt like db.user.
  3142. *
  3143. * <code>
  3144. * <?php
  3145. * $lang->set('version', '1.0);
  3146. * ?>
  3147. * </code>
  3148. * @param string $key 成员的键名,可以是father.child的形式。
  3149. * the key of the member, can be father.child
  3150. * @param mixed $value the value
  3151. * @access public
  3152. * @return void
  3153. */
  3154. public function set($key, $value)
  3155. {
  3156. helper::setMember('lang', $key, $value);
  3157. }
  3158. /**
  3159. * 显示一个成员的值。
  3160. * Show a member.
  3161. *
  3162. * @param object $obj the object
  3163. * @param string $key the key
  3164. * @access public
  3165. * @return void
  3166. */
  3167. public function show($obj, $key)
  3168. {
  3169. $obj = (array)$obj;
  3170. echo $obj[$key] ?? '';
  3171. }
  3172. }
  3173. /**
  3174. * 超级对象类,转化超级全局变量。
  3175. * The super object class.
  3176. *
  3177. * @package framework
  3178. */
  3179. class super
  3180. {
  3181. /**
  3182. * 构造函数,设置超级变量名。
  3183. * Construct, set the var scope.
  3184. *
  3185. * @param string $scope the scope, can be server, post, get, cookie, session, global
  3186. * @access public
  3187. * @return void
  3188. * @param string $tab
  3189. */
  3190. public function __construct($scope, $tab = '')
  3191. {
  3192. $this->scope = $scope;
  3193. $this->tab = $tab;
  3194. }
  3195. /**
  3196. * 设置超级变量的成员值。
  3197. * Set one member value.
  3198. *
  3199. * @param string $key the key
  3200. * @param mixed $value the value
  3201. * @param string $tab
  3202. * @access public
  3203. * @return void
  3204. */
  3205. public function set($key, $value, $tab = '')
  3206. {
  3207. if($this->scope == 'post')
  3208. {
  3209. $_POST[$key] = $value;
  3210. }
  3211. elseif($this->scope == 'get')
  3212. {
  3213. $_GET[$key] = $value;
  3214. }
  3215. elseif($this->scope == 'server')
  3216. {
  3217. $_SERVER[$key] = $value;
  3218. }
  3219. elseif($this->scope == 'cookie')
  3220. {
  3221. $_COOKIE[$key] = $value;
  3222. }
  3223. elseif($this->scope == 'session')
  3224. {
  3225. if($tab) $_SESSION["app-$tab"][$key] = $value;
  3226. $_SESSION[$key] = $value;
  3227. }
  3228. elseif($this->scope == 'env')
  3229. {
  3230. $_ENV[$key] = $value;
  3231. }
  3232. elseif($this->scope == 'global')
  3233. {
  3234. $GLOBALS[$key] = $value;
  3235. }
  3236. }
  3237. /**
  3238. * 超级变量的魔术方法,比如用$post->key访问$_POST['key']。
  3239. * The magic get method.
  3240. *
  3241. * @param string $key the key
  3242. * @access public
  3243. * @return mixed|bool return the value of the key or false.
  3244. */
  3245. public function __get($key)
  3246. {
  3247. if($this->scope == 'post')
  3248. {
  3249. if(isset($_POST[$key])) return $_POST[$key];
  3250. return false;
  3251. }
  3252. elseif($this->scope == 'get')
  3253. {
  3254. if(isset($_GET[$key])) return $_GET[$key];
  3255. return false;
  3256. }
  3257. elseif($this->scope == 'server')
  3258. {
  3259. if($key == 'ajax') return isset($_SERVER['HTTP_X_REQUESTED_WITH']) ? true : false;
  3260. if(isset($_SERVER[$key])) return $_SERVER[$key];
  3261. $key = strtoupper($key);
  3262. if(isset($_SERVER[$key])) return $_SERVER[$key];
  3263. return false;
  3264. }
  3265. elseif($this->scope == 'cookie')
  3266. {
  3267. if(isset($_COOKIE[$key])) return $_COOKIE[$key];
  3268. return false;
  3269. }
  3270. elseif($this->scope == 'session')
  3271. {
  3272. $tab = $this->tab;
  3273. if($tab and isset($_SESSION["app-$tab"][$key])) return $_SESSION["app-$tab"][$key];
  3274. if(isset($_SESSION[$key])) return $_SESSION[$key];
  3275. return false;
  3276. }
  3277. elseif($this->scope == 'env')
  3278. {
  3279. if(isset($_ENV[$key])) return $_ENV[$key];
  3280. return false;
  3281. }
  3282. elseif($this->scope == 'global')
  3283. {
  3284. if(isset($GLOBALS[$key])) return $GLOBALS[$key];
  3285. return false;
  3286. }
  3287. else
  3288. {
  3289. return false;
  3290. }
  3291. }
  3292. /**
  3293. * 打印变量的详细结构。
  3294. * Print the structure.
  3295. *
  3296. * @access public
  3297. * @return void
  3298. */
  3299. public function a()
  3300. {
  3301. if($this->scope == 'post') a($_POST);
  3302. if($this->scope == 'get') a($_GET);
  3303. if($this->scope == 'server') a($_SERVER);
  3304. if($this->scope == 'cookie') a($_COOKIE);
  3305. if($this->scope == 'session') a($_SESSION);
  3306. if($this->scope == 'env') a($_ENV);
  3307. if($this->scope == 'global') a($GLOBALS);
  3308. }
  3309. }
  3310. class EndResponseException extends \Exception
  3311. {
  3312. /**
  3313. * 响应内容
  3314. *
  3315. * @var string
  3316. */
  3317. private $content;
  3318. /**
  3319. * @param string $content
  3320. *
  3321. * @return self
  3322. */
  3323. public static function create($content = '')
  3324. {
  3325. $exception = new self($content);
  3326. $exception->content = $content;
  3327. return $exception;
  3328. }
  3329. /**
  3330. * Get 响应内容
  3331. *
  3332. * @return string
  3333. */
  3334. public function getContent()
  3335. {
  3336. return $this->content;
  3337. }
  3338. }
  3339. /**
  3340. * ZenTao session handler.
  3341. *
  3342. * @package framework
  3343. */
  3344. class ztSessionHandler implements SessionHandlerInterface
  3345. {
  3346. public $sessSavePath;
  3347. public $sessionFile;
  3348. public $sessionID;
  3349. public $sessionName;
  3350. /**
  3351. * Construct.
  3352. *
  3353. * @access public
  3354. * @return void
  3355. */
  3356. public function __construct()
  3357. {
  3358. register_shutdown_function('session_write_close');
  3359. }
  3360. /**
  3361. * Get sessionID.
  3362. *
  3363. * @access public
  3364. * @return string
  3365. */
  3366. public function getSessionID()
  3367. {
  3368. return $this->sessionID;
  3369. }
  3370. /**
  3371. * Get session file.
  3372. *
  3373. * @param string $id
  3374. * @access public
  3375. * @return string
  3376. */
  3377. public function getSessionFile($id)
  3378. {
  3379. if(!empty($this->sessionFile)) return $this->sessionFile;
  3380. if(!preg_match('/^\w+$/', $id)) return false;
  3381. $fileName = "sess_{$id}";
  3382. $this->sessionFile = $this->sessSavePath . DS . $fileName;
  3383. $this->sessionID = $id;
  3384. return $this->sessionFile;
  3385. }
  3386. /**
  3387. * Creates a new session, or reinitialize existing session.
  3388. *
  3389. * @param string $savePath
  3390. * @param string $sessionName
  3391. * @access public
  3392. * @return bool
  3393. */
  3394. #[\ReturnTypeWillChange]
  3395. public function open($savePath, $sessionName)
  3396. {
  3397. $this->sessSavePath = $savePath;
  3398. $this->sessionName = $sessionName;
  3399. return true;
  3400. }
  3401. /**
  3402. * Closes the current session.
  3403. *
  3404. * @access public
  3405. * @return bool
  3406. */
  3407. #[\ReturnTypeWillChange]
  3408. public function close()
  3409. {
  3410. return true;
  3411. }
  3412. /**
  3413. * Reads the session data from the session file, and returns the results.
  3414. *
  3415. * @param string $id
  3416. * @access public
  3417. * @return string|false
  3418. */
  3419. #[\ReturnTypeWillChange]
  3420. public function read($id)
  3421. {
  3422. $sessFile = $this->getSessionFile($id);
  3423. if(!$sessFile) return false;
  3424. if(file_exists($sessFile)) return file_get_contents($sessFile);
  3425. return '';
  3426. }
  3427. /**
  3428. * Writes the session data to the session file.
  3429. *
  3430. * @param string $id
  3431. * @param string $sessData
  3432. * @access public
  3433. * @return bool
  3434. */
  3435. #[\ReturnTypeWillChange]
  3436. public function write($id, $sessData)
  3437. {
  3438. $sessFile = $this->getSessionFile($id);
  3439. if(!$sessFile) return true;
  3440. if(!is_file($sessFile) && !touch($sessFile)) return true;
  3441. if(!is_writable($sessFile)) return true;
  3442. if(md5_file($sessFile) == md5($sessData)) return true;
  3443. if(file_put_contents($sessFile, $sessData, LOCK_EX)) return true;
  3444. return true;
  3445. }
  3446. /**
  3447. * Destroys a session.
  3448. *
  3449. * @param string $id
  3450. * @access public
  3451. * @return bool
  3452. */
  3453. #[\ReturnTypeWillChange]
  3454. public function destroy($id)
  3455. {
  3456. $sessFile = $this->getSessionFile($id);
  3457. if(file_exists($sessFile)) unlink($sessFile);
  3458. return true;
  3459. }
  3460. /**
  3461. * Cleans up expired sessions.
  3462. *
  3463. * @param int $maxlifeTime
  3464. * @access public
  3465. * @return int|false
  3466. */
  3467. #[\ReturnTypeWillChange]
  3468. public function gc($maxlifeTime)
  3469. {
  3470. /* API session never expires. */
  3471. global $config;
  3472. if((defined('RUN_MODE') && RUN_MODE == 'api') || isset($_GET[$config->sessionVar])) return 0;
  3473. $time = time();
  3474. $count = 0;
  3475. foreach(glob("{$this->sessSavePath}/sess_*") as $fileName)
  3476. {
  3477. if(filemtime($fileName) + $maxlifeTime < $time && unlink($fileName)) $count++;
  3478. }
  3479. return $count;
  3480. }
  3481. }