PageRenderTime 72ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 0ms

/servers/class.phpwsdl.servers.php

http://php-wsdl-creator.googlecode.com/
PHP | 1762 lines | 1193 code | 54 blank | 515 comment | 140 complexity | 3938d2aa575a8c539bf73749aab145ec MD5 | raw file
Possible License(s): GPL-3.0
  1. <?php
  2. /*
  3. PhpWsdl - Generate WSDL from PHP
  4. Copyright (C) 2011 Andreas Müller-Saala, wan24.de
  5. This program is free software; you can redistribute it and/or modify it under
  6. the terms of the GNU General Public License as published by the Free Software
  7. Foundation; either version 3 of the License, or (at your option) any later
  8. version.
  9. This program is distributed in the hope that it will be useful, but WITHOUT
  10. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  11. FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License along with
  13. this program; if not, see <http://www.gnu.org/licenses/>.
  14. */
  15. if(basename($_SERVER['SCRIPT_FILENAME'])==basename(__FILE__))
  16. exit;
  17. PhpWsdl::Debug('Servers extension loaded');
  18. PhpWsdl::RegisterHook('InterpretKeywordpw_restHook','servers','PhpWsdlServers::InterpretRest');
  19. PhpWsdl::RegisterHook('BeforeRunServerHook','servers','PhpWsdlServers::BeforeRunServerHook');
  20. PhpWsdl::RegisterHook('PrepareServerHook','servers','PhpWsdlServers::PrepareServerHook');
  21. PhpWsdl::RegisterHook('RunServerHook','servers','PhpWsdlServers::RunServerHook');
  22. PhpWsdl::RegisterHook('BeginCreatePhpHook','servers','PhpWsdlServers::BeginCreatePhpHook');
  23. PhpWsdl::RegisterHook('OutputPhpHook','servers','PhpWsdlServers::OutputPhpHook');
  24. PhpWsdl::RegisterHook('CreatePhpCallHook','servers','PhpWsdlServers::CreatePhpCallHook');
  25. PhpWsdl::RegisterHook('CreateMethodPhpHook','servers','PhpWsdlServers::CreateMethodPhpHook');
  26. PhpWsdl::RegisterHook('CreateHtmlGeneralHook','servers','PhpWsdlServers::CreateHtmlGeneralHook');
  27. PhpWsdl::RegisterHook('CreateMethodHtmlHook','servers','PhpWsdlServers::CreateMethodHtmlHook');
  28. PhpWsdl::RegisterHook('PdfAttachmentHook','servers','PhpWsdlServers::PdfAttachmentHook');
  29. PhpWsdl::RegisterHook('CreateInstanceHook','servers','PhpWsdlServers::ConstructorHook');
  30. /**
  31. * Some http protocol servers (JSON, http, REST and XML RPC)
  32. *
  33. * @author Andreas Müller-Saala, wan24.de
  34. */
  35. class PhpWsdlServers{
  36. /**
  37. * The GUID
  38. *
  39. * @var string
  40. */
  41. public $GUID;
  42. /**
  43. * Disable the client cache?
  44. *
  45. * @var boolean
  46. */
  47. public static $DisableClientCache=true;
  48. /**
  49. * Enable response compression?
  50. *
  51. * @var boolean
  52. */
  53. public static $EnableCompression=true;
  54. /**
  55. * Enable the JSON server
  56. *
  57. * @var boolean
  58. */
  59. public static $EnableJson=true;
  60. /**
  61. * Enable the http server
  62. *
  63. * @var boolean
  64. */
  65. public static $EnableHttp=true;
  66. /**
  67. * Enable the REST server
  68. *
  69. * @var boolean
  70. */
  71. public static $EnableRest=true;
  72. /**
  73. * Enable the RPC server
  74. *
  75. * @var boolean
  76. */
  77. public static $EnableRpc=true;
  78. /**
  79. * Enable parameter names in XML RPC requests?
  80. *
  81. * @var boolean
  82. */
  83. public static $EnableRpcNamedParameters=false;
  84. /**
  85. * Disable recoding arrays as objects in XML RPC mode?
  86. *
  87. * @var boolean
  88. */
  89. public static $NoRpcRecode=false;
  90. /**
  91. * Is this an RPC request?
  92. *
  93. * @var boolean|NULL
  94. */
  95. private static $IsRpcRequest=null;
  96. /**
  97. * The server object that will be used to run the server
  98. *
  99. * @var PhpWsdlServers
  100. */
  101. public static $UseServer=null;
  102. /**
  103. * Attach JavaScript to PDF?
  104. *
  105. * Note: JavaScript can be attached, but the user can't save JavaScript attachments with
  106. * Adobe Acrobat Reader per default. To open them from Windows the registry at (f.e.)
  107. * HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Adobe\Adobe Reader\[VERSION]\FeatureLockDown\cDefaultLaunchAttachmentPerms
  108. * has to be touched manually!
  109. *
  110. * @var boolean
  111. */
  112. public static $AttachJsInPdf=false;
  113. /**
  114. * The webservice handler class name
  115. *
  116. * @var string
  117. */
  118. public $ClassName=null;
  119. /**
  120. * The webservice handler object
  121. *
  122. * @var object
  123. */
  124. public $Object=null;
  125. /**
  126. * The PhpWsdl object
  127. *
  128. * @var PhpWsdl
  129. */
  130. public $Server=null;
  131. /**
  132. * The cached PHP http client proxy
  133. *
  134. * @var string
  135. */
  136. public $HttpPhp=null;
  137. /**
  138. * Set this to the PHP URI, if it's different from your SOAP endpoint + "?PHPHTTPCLIENT"
  139. *
  140. * @var string
  141. */
  142. public $HttpPhpUri=null;
  143. /**
  144. * Force sending HTTP PHP (has a higher priority than PhpWsdlServers->ForceNotOutputHttpPhp)
  145. *
  146. * @var boolean
  147. */
  148. public $ForceOutputHttpPhp=false;
  149. /**
  150. * Force NOT sending HTTP PHP (disable sending HTTP PHP)
  151. *
  152. * @var boolean
  153. */
  154. public $ForceNotOutputHttpPhp=false;
  155. /**
  156. * The cached PHP JSON client proxy
  157. *
  158. * @var string
  159. */
  160. public $JsonPhp=null;
  161. /**
  162. * Set this to the PHP URI, if it's different from your SOAP endpoint + "?PHPJSPNCLIENT"
  163. *
  164. * @var string
  165. */
  166. public $JsonPhpUri=null;
  167. /**
  168. * Force sending JSON PHP (has a higher priority than PhpWsdlServers->ForceNotOutputJsonPhp)
  169. *
  170. * @var boolean
  171. */
  172. public $ForceOutputJsonPhp=false;
  173. /**
  174. * Force NOT sending JSON PHP (disable sending JSON PHP)
  175. *
  176. * @var boolean
  177. */
  178. public $ForceNotOutputJsonPhp=false;
  179. /**
  180. * The cached PHP REST client proxy
  181. *
  182. * @var string
  183. */
  184. public $RestPhp=null;
  185. /**
  186. * Set this to the PHP URI, if it's different from your SOAP endpoint + "?PHPRESTCLIENT"
  187. *
  188. * @var string
  189. */
  190. public $RestPhpUri=null;
  191. /**
  192. * Force sending REST PHP (has a higher priority than PhpWsdlServers->ForceNotOutputRestPhp)
  193. *
  194. * @var boolean
  195. */
  196. public $ForceOutputRestPhp=false;
  197. /**
  198. * Force NOT sending REST PHP (disable sending REST PHP)
  199. *
  200. * @var boolean
  201. */
  202. public $ForceNotOutputRestPhp=false;
  203. /**
  204. * The cached PHP XML RPC client proxy
  205. *
  206. * @var string
  207. */
  208. public $RpcPhp=null;
  209. /**
  210. * Set this to the PHP URI, if it's different from your SOAP endpoint + "?PHPRPCCLIENT"
  211. *
  212. * @var string
  213. */
  214. public $RpcPhpUri=null;
  215. /**
  216. * Force sending XML RPC PHP (has a higher priority than PhpWsdlServers->ForceNotOutputRpcPhp)
  217. *
  218. * @var boolean
  219. */
  220. public $ForceOutputRpcPhp=false;
  221. /**
  222. * Force NOT sending XML RPC PHP (disable sending XML RPC PHP)
  223. *
  224. * @var boolean
  225. */
  226. public $ForceNotOutputRpcPhp=false;
  227. /**
  228. * The cached JavaScript JSON client proxy
  229. *
  230. * @var string
  231. */
  232. public $JsonJs=null;
  233. /**
  234. * Set this to the JavaScript URI, if it's different from your SOAP endpoint + "?JSJSONCLIENT"
  235. *
  236. * @var string
  237. */
  238. public $JsonJsUri=null;
  239. /**
  240. * Force sending JSON JS (has a higher priority than PhpWsdlServers->ForceNotOutputJsonJs)
  241. *
  242. * @var boolean
  243. */
  244. public $ForceOutputJsonJs=false;
  245. /**
  246. * Force NOT sending JSON JS (disable sending JSON JS)
  247. *
  248. * @var boolean
  249. */
  250. public $ForceNotOutputJsonJs=false;
  251. /**
  252. * The cached and compressed JavaScript JSON client proxy
  253. *
  254. * @var string
  255. */
  256. public $JsonJsMin=null;
  257. /**
  258. * Force the JSON JS compression (has a higher priority than PhpWsdlServers->ForceNotJsonJsMin)
  259. *
  260. * @var boolean
  261. */
  262. public $ForceJsonJsMin=false;
  263. /**
  264. * Force NOT JSON JS compression (disable compression)
  265. *
  266. * @var booleean
  267. */
  268. public $ForceNotJsonJsMin=false;
  269. /**
  270. * Constructor
  271. *
  272. * @param PhpWsdl $server The PhpWsdl object
  273. */
  274. public function PhpWsdlServers($server){
  275. $this->GUID=uniqid();
  276. PhpWsdl::Debug('New PhpWsdlServers "'.$this->GUID.'"');
  277. $this->Server=$server;
  278. self::$UseServer=$this;
  279. PhpWsdl::RegisterHook('ReadCacheHook','servers',Array($this,'ReadCacheHook'));
  280. PhpWsdl::RegisterHook('WriteCacheHook','servers',Array($this,'WriteCacheHook'));
  281. }
  282. /**
  283. * Determine if the request is a JSON request
  284. *
  285. * @return boolean JSON request?
  286. */
  287. public static function IsJsonRequest(){
  288. return self::$EnableJson&&function_exists('json_encode')&&!self::HasParam('call')&&(self::HasParam('json')||self::HasParam('JSON'));
  289. }
  290. /**
  291. * Determine if the request is a http request
  292. *
  293. * @return boolean http request?
  294. */
  295. public static function IsHttpRequest(){
  296. return self::$EnableHttp&&self::HasParam('call');
  297. }
  298. /**
  299. * Determine if the request is a REST request
  300. *
  301. * @return boolean REST request?
  302. */
  303. public static function IsRestRequest(){
  304. return self::$EnableRest&&isset($_SERVER['PATH_INFO']);
  305. }
  306. /**
  307. * Determine if the request is a RPC request
  308. *
  309. * @return boolean RPC request?
  310. */
  311. public static function IsRpcRequest(){
  312. if(!self::$EnableRpc||!is_null(self::$IsRpcRequest))
  313. return self::$EnableRpc&&self::$IsRpcRequest;
  314. if(function_exists('xmlrpc_server_create')){
  315. $in=file_get_contents('php://input');
  316. $xml=new DOMDocument();
  317. if($xml->loadXML($in)){
  318. $x=new DOMXPath($xml);
  319. $temp=$x->query('/*');
  320. self::$IsRpcRequest=$temp->length>0&&$temp->item(0)->nodeName=='methodCall';
  321. }else{
  322. self::$IsRpcRequest=false;
  323. }
  324. }else{
  325. self::$IsRpcRequest=false;
  326. }
  327. return self::$IsRpcRequest;
  328. }
  329. /**
  330. * Determine if the PhpWsdlServers extension can handle this request
  331. *
  332. * @return boolean Can handle?
  333. */
  334. public static function CanHandleRequest(){
  335. return
  336. self::IsHttpRequest()||
  337. self::IsJsonRequest()||
  338. self::IsRestRequest()||
  339. self::IsRpcRequest()||
  340. self::IsJsonPhpRequest()||
  341. self::IsJsonJsRequest()||
  342. self::IsHttpPhpRequest()||
  343. self::IsRestPhpRequest()||
  344. self::IsRpcPhpRequest();
  345. }
  346. /**
  347. * Determine if the PHP JSON client proxy is requested
  348. *
  349. * @return boolean Requested?
  350. */
  351. public static function IsJsonPhpRequest(){
  352. return self::$EnableJson&&(self::$UseServer->ForceOutputJsonPhp||((self::HasParam('phpjsonclient')||self::HasParam('PHPJSONCLIENT'))&&!self::$UseServer->ForceNotOutputJsonPhp));
  353. }
  354. /**
  355. * Determine if the JavaScript JSON client proxy is requested
  356. *
  357. * @return boolean Requested?
  358. */
  359. public static function IsJsonJsRequest(){
  360. return self::$EnableJson&&(self::$UseServer->ForceOutputJsonJs||((self::HasParam('jsjsonclient')||self::HasParam('JSJSONCLIENT'))&&!self::$UseServer->ForceNotOutputJsonJs));
  361. }
  362. /**
  363. * Determine if the PHP XML RPC client proxy is requested
  364. *
  365. * @return boolean Requested?
  366. */
  367. public static function IsRpcPhpRequest(){
  368. return self::$EnableRpc&&(self::$UseServer->ForceOutputRpcPhp||((self::HasParam('phprpcclient')||self::HasParam('PHPRPCCLIENT'))&&!self::$UseServer->ForceNotOutputRpcPhp));
  369. }
  370. /**
  371. * Determine if the PHP http client proxy is requested
  372. *
  373. * @return boolean Requested?
  374. */
  375. public static function IsHttpPhpRequest(){
  376. return self::$EnableHttp&&(self::$UseServer->ForceOutputHttpPhp||((self::HasParam('phphttpclient')||self::HasParam('PHPHTTPCLIENT'))&&!self::$UseServer->ForceNotOutputHttpPhp));
  377. }
  378. /**
  379. * Determine if the PHP REST client proxy is requested
  380. *
  381. * @return boolean Requested?
  382. */
  383. public static function IsRestPhpRequest(){
  384. return self::$EnableRest&&(self::$UseServer->ForceOutputRestPhp||((self::HasParam('phprestclient')||self::HasParam('PHPRESTCLIENT'))&&!self::$UseServer->ForceNotOutputRestPhp));
  385. }
  386. /**
  387. * Determine if we can handle the request
  388. *
  389. * @param array $data The event data
  390. * @return boolean Response
  391. */
  392. public static function BeforeRunServerHook($data){
  393. if(!self::CanHandleRequest())
  394. return true;
  395. PhpWsdl::Debug('PhpWsdlServers will handle the request');
  396. $server=$data['server'];
  397. $server->GetWsdlFromCache();
  398. $srv=self::$UseServer;
  399. $jsonPhp=self::IsJsonPhpRequest();
  400. $jsonJs=self::IsJsonJsRequest();
  401. $rpcPhp=self::IsRpcPhpRequest();
  402. $httpPhp=self::IsHttpPhpRequest();
  403. $restPhp=self::IsRestPhpRequest();
  404. if(!$jsonPhp&&!$jsonJs&&!$rpcPhp&&!$httpPhp&&!$restPhp)
  405. return false;
  406. if($jsonJs){
  407. PhpWsdl::Debug('Output JavaScript requested');
  408. $srv->OutputJs();
  409. }else{
  410. PhpWsdl::Debug('Output PHP requested');
  411. $php=null;
  412. if($jsonPhp){
  413. $php=$srv->JsonPhp;
  414. $ext='json';
  415. }else if($rpcPhp){
  416. $php=$srv->RpcPhp;
  417. $ext='rpc';
  418. }else if($httpPhp){
  419. $php=$srv->HttpPhp;
  420. $ext='http';
  421. }else{
  422. $php=$srv->RestPhp;
  423. $ext='rest';
  424. }
  425. $cache=is_null($php);
  426. $php=$server->OutputPhp(
  427. true,
  428. true,
  429. Array(
  430. 'serversext' => $ext
  431. )
  432. );
  433. if($cache){
  434. switch($ext){
  435. case 'json':
  436. $srv->JsonPhp=$php;
  437. break;
  438. case 'http':
  439. $srv->HttpPhp=$php;
  440. break;
  441. case 'rest':
  442. $srv->RestPhp=$php;
  443. break;
  444. case 'rpc':
  445. $srv->RpcPhp=$php;
  446. break;
  447. }
  448. $server->WriteWsdlToCache(null,null,null,true);
  449. }
  450. }
  451. PhpWsdl::Debug('Quit script execution');
  452. exit;
  453. }
  454. /**
  455. * Create a server object
  456. *
  457. * @param array $data The event data
  458. * @return boolean Response
  459. */
  460. public static function PrepareServerHook($data){
  461. if(!self::CanHandleRequest())
  462. return true;
  463. PhpWsdl::Debug('Prepare a JSON/http/REST/RPC server');
  464. $server=&$data['server'];
  465. $srv=self::$UseServer;
  466. $data['soapserver']=$srv;
  467. $useProxy=&$data['useproxy'];
  468. $class=&$data['class'];
  469. if($useProxy||!is_object($class)){
  470. $temp=($useProxy)?'PhpWsdlProxy':$class;
  471. if(!is_null($temp)){
  472. PhpWsdl::Debug('Setting server class '.$temp);
  473. $srv->ClassName=$temp;
  474. }else{
  475. PhpWsdl::Debug('No server class or object');
  476. }
  477. }else{
  478. PhpWsdl::Debug('Setting server object '.get_class($class));
  479. $srv->Object=$class;
  480. }
  481. return false;
  482. }
  483. /**
  484. * Run the server
  485. *
  486. * @param array $data The event data
  487. * @return boolean Response
  488. */
  489. public static function RunServerHook($data){
  490. if(!self::CanHandleRequest())
  491. return true;
  492. if(get_class($data['soapserver'])!='PhpWsdlServers'){
  493. PhpWsdl::Debug('Not a valid server object');
  494. return true;
  495. }
  496. PhpWsdl::Debug('Handle the request');
  497. if(self::IsJsonRequest()){
  498. $res=$data['soapserver']->HandleJsonRequest();
  499. }else if(self::IsHttpRequest()){
  500. return $data['soapserver']->HandleHttpRequest();
  501. }else if(self::IsRestRequest()){
  502. return $data['soapserver']->HandleRestRequest();
  503. }else if(self::IsRpcRequest()){
  504. return $data['soapserver']->HandleRpcRequest();
  505. }else{
  506. throw(new Exception('Could not handle request'));
  507. }
  508. if($data['andexit']){
  509. PhpWsdl::Debug('Stopping script execution');
  510. exit;
  511. }
  512. return $res;
  513. }
  514. /**
  515. * Interpret a REST declaration
  516. *
  517. * @param array $data The event data
  518. * @return boolean Response
  519. */
  520. public static function InterpretRest($data){
  521. if($data['method']=='')
  522. return true;
  523. PhpWsdl::Debug('Interpret REST "'.$data['keyword'][1].'" for method "'.$data['method'].'"');
  524. $info=explode(' ',$data['keyword'][1],3);
  525. $iLen=sizeof($info);
  526. if($iLen<2){
  527. PhpWsdl::Debug('Invalid REST definition');
  528. return true;
  529. }
  530. $method=$info[0];
  531. $path=$info[1];
  532. $docs=($iLen>2)?$info[2]:null;
  533. $settings=&$data['settings'];
  534. if(!isset($settings['settings']))
  535. $settings['settings']=Array();
  536. $settings=&$settings['settings'];
  537. if(!isset($settings['rest']))
  538. $settings['rest']=Array();
  539. $settings=&$settings['rest'];
  540. if(!isset($settings[$method]))
  541. $settings[$method]=Array();
  542. $settings=&$settings[$method];
  543. $settings[]=Array(
  544. 'path' => $path,
  545. 'docs' => $docs
  546. );
  547. return false;
  548. }
  549. /**
  550. * Create PHP properties
  551. *
  552. * @param array $data The event data
  553. * @return boolean Response
  554. */
  555. public static function BeginCreatePhpHook($data){
  556. if(!isset($data['options']['serversext']))
  557. return true;
  558. $res=&$data['res'];
  559. $res[]="\t/**";
  560. $res[]="\t * The endpoint URI";
  561. $res[]="\t *";
  562. $res[]="\t * @var string";
  563. $res[]="\t */";
  564. $res[]="\tpublic static \$_EndPoint='".$data['server']->EndPoint."';";
  565. return false;
  566. }
  567. /**
  568. * Change the class name
  569. *
  570. * @param array $data The event data
  571. * @return boolean Response
  572. */
  573. public static function OutputPhpHook($data){
  574. if(!isset($data['options']['serversext'])||isset($options['class']))
  575. return true;
  576. switch($data['options']['serversext']){
  577. case 'http':
  578. $ext='Http';
  579. break;
  580. case 'json':
  581. $ext='Json';
  582. break;
  583. case 'rest':
  584. $ext='Rest';
  585. break;
  586. case 'rpc':
  587. $ext='XmlRpc';
  588. break;
  589. default:
  590. return true;
  591. break;
  592. }
  593. $data['options']['class']=$data['server']->Name.$ext.'Client';
  594. return true;
  595. }
  596. /**
  597. * Create PHP code for a server request
  598. *
  599. * @param array $data The event data
  600. * @return boolean Response
  601. */
  602. public static function CreatePhpCallHook($data){
  603. if(!isset($data['options']['serversext']))
  604. return true;
  605. $res=&$data['res'];
  606. switch($data['options']['serversext']){
  607. case 'json':
  608. PhpWsdl::Debug('Create PhpWsdlServers JSON PHP client code');
  609. $res[]="\t\t".'$call=Array(';
  610. $res[]="\t\t\t".'"call"=>$method,';
  611. $res[]="\t\t\t".'"param"=>$param';
  612. $res[]="\t\t".');';
  613. $res[]="\t\t".'return json_decode(file_get_contents(self::$_EndPoint."?JSON=".urlencode(json_encode($call))));';
  614. break;
  615. case 'rpc':
  616. PhpWsdl::Debug('Create PhpWsdlServers XML RPC PHP client code');
  617. $res[]="\t\t".'self::$_Server=stream_context_create(Array(';
  618. $res[]="\t\t\t".'"http" => Array(';
  619. $res[]="\t\t\t\t".'"method" => "POST",';
  620. $res[]="\t\t\t\t".'"header" => "Content-Type: text/xml",';
  621. $res[]="\t\t\t\t".'"content" => xmlrpc_encode_request($method,$param)';
  622. $res[]="\t\t\t".')';
  623. $res[]="\t\t".'));';
  624. $res[]="\t\t".'$res=xmlrpc_decode(file_get_contents(self::$_EndPoint,false,self::$_Server));';
  625. $res[]="\t\t".'if(is_array($res)&&xmlrpc_is_fault($res))';
  626. $res[]="\t\t\t".'throw(new Exception($res["faultString"],$res["faultCode"]));';
  627. $res[]="\t\t".'return $res;';
  628. break;
  629. case 'http':
  630. PhpWsdl::Debug('Create PhpWsdlServers http PHP client code');
  631. $res[]="\t\t".'$param["call"]=$method;';
  632. $res[]="\t\t".'$temp=Array();';
  633. $res[]="\t\t".'$keys=array_keys($param);';
  634. $res[]="\t\t".'$i=-1;';
  635. $res[]="\t\t".'$len=sizeof($keys);';
  636. $res[]="\t\t".'while(++$i<$len)';
  637. $res[]="\t\t\t".'$temp[]=urlencode($keys[$i])."=".urlencode($param[$keys[$i]]);';
  638. $res[]="\t\t".'return file_get_contents(self::$_EndPoint."?".implode("&",$temp));';
  639. break;
  640. case 'rest':
  641. PhpWsdl::Debug('Create PhpWsdlServers REST PHP client code');
  642. $res[]="\t\t".'$keys=array_keys($param);';
  643. $res[]="\t\t".'$i=-1;';
  644. $res[]="\t\t".'$len=sizeof($keys);';
  645. $res[]="\t\t".'while(++$i<$len)';
  646. $res[]="\t\t\t".'$method=str_replace(" ".$keys[$i]."/",urlencode($param[$keys[$i]])."/",$method);';
  647. $res[]="\t\t".'return file_get_contents(self::$_EndPoint.$method);';
  648. break;
  649. default:
  650. return true;
  651. break;
  652. }
  653. return false;
  654. }
  655. /**
  656. * Create PHP code for a method call
  657. *
  658. * @param array $data The event data
  659. * @return boolean Response
  660. */
  661. public static function CreateMethodPhpHook($data){
  662. if(!isset($data['options']['serversext']))
  663. return true;
  664. $server=$data['server'];
  665. $m=$data['method'];
  666. $res=&$data['res'];
  667. switch($data['options']['serversext']){
  668. case 'rest':
  669. case 'http':
  670. PhpWsdl::Debug('Create PhpWsdlServers http/REST PHP method code');
  671. $basicType=true;
  672. if(!is_null($m->Return))
  673. $basicType=in_array($m->Return->Type,PhpWsdl::$BasicTypes);
  674. $pLen=sizeof($m->Param);
  675. if($data['options']['serversext']!='rest'){
  676. $name=$m->Name;
  677. }else{
  678. $temp=Array('/'.$m->Name);
  679. $i=-1;
  680. while(++$i<$pLen)
  681. $temp[]=' '.$m->Param[$i]->Name;
  682. $name=implode('/',$temp).'/';
  683. }
  684. $res[]="\t\treturn ".(($basicType)?"":"json_decode(")."self::_Call('".$name."',Array(";
  685. $i=-1;
  686. while(++$i<$pLen){
  687. $p=$m->Param[$i];
  688. $res[]="\t\t\t'".$p->Name."'=>".((in_array($p->Type,PhpWsdl::$BasicTypes))?"\$".$p->Name:"json_encode(\$".$p->Name.")");
  689. }
  690. $res[]="\t\t))".(($basicType)?"":")").";";
  691. break;
  692. case 'rpc':
  693. if(!self::$EnableRpcNamedParameters)
  694. return true;
  695. PhpWsdl::Debug('Create PhpWsdlServers XML RPC PHP method code for named parameters');
  696. $res[]="\t\treturn self::_Call('".$server->Name."',Array(";
  697. $i=-1;
  698. $len=sizeof($m->Param);
  699. while(++$i<$len)
  700. $res[]="\t\t\t".'"'.$m->Param[$i]->Name.'"=>$'.$m->Param[$i]->Name;
  701. $res[]="\t\t));";
  702. break;
  703. default:
  704. return true;
  705. break;
  706. }
  707. return false;
  708. }
  709. /**
  710. * Extend general HTML documentation
  711. *
  712. * @param array $data The event data
  713. * @return boolean Response
  714. */
  715. public static function CreateHtmlGeneralHook($data){
  716. if(!self::$EnableHttp&&!self::$EnableJson&&!self::$EnableRest&&!self::$EnableRpc)
  717. return true;
  718. PhpWsdl::Debug('Append HTML general information with PhpWsdlServers information');
  719. $server=$data['server'];
  720. $res=&$data['res'];
  721. $temp=Array('SOAP');
  722. if(self::$EnableJson){
  723. $temp[]='JSON';
  724. if(!$server->ForceNotOutputJsonPhp){
  725. $url=self::$UseServer->GetJsonPhpUri();
  726. $res[]='<p>PHP JSON client download URI: <span class="pre"><a href="'.$url.'">'.$url.'</a></span></p>';
  727. }
  728. if(!$server->ForceNotOutputJsonJs){
  729. $url=self::$UseServer->GetJsonJsUri();
  730. $res[]='<p>JavaScript JSON client download URI: <span class="pre"><a href="'.$url.'">'.$url.'</a></span></p>';
  731. if(self::IsJsPackerAvailable()){
  732. $url=self::$UseServer->GetJsonJsUri().'&min';
  733. $res[]='<p>Compressed JavaScript JSON client download URI: <span class="pre"><a href="'.$url.'">'.$url.'</a></span></p>';
  734. }
  735. }
  736. }
  737. if(self::$EnableRpc&&!$server->ForceNotOutputRpcPhp){
  738. $temp[]='XML RPC';
  739. $url=self::$UseServer->GetRpcPhpUri();
  740. $res[]='<p>PHP XML RPC client download URI: <span class="pre"><a href="'.$url.'">'.$url.'</a></span></p>';
  741. }
  742. if(self::$EnableHttp&&!$server->ForceNotOutputHttpPhp){
  743. $temp[]='http';
  744. $url=self::$UseServer->GetHttpPhpUri();
  745. $res[]='<p>PHP http client download URI: <span class="pre"><a href="'.$url.'">'.$url.'</a></span></p>';
  746. }
  747. if(self::$EnableRest&&!$server->ForceNotOutputRestPhp){
  748. $temp[]='REST';
  749. $url=self::$UseServer->GetRestPhpUri();
  750. $res[]='<p>PHP REST client download URI: <span class="pre"><a href="'.$url.'">'.$url.'</a></span></p>';
  751. }
  752. if(sizeof($temp)>1)
  753. $res[]='<p>The PhpWsdlServers extension allows PhpWsdl to serve these protocols: '.implode(', ',$temp).'</p>';
  754. return true;
  755. }
  756. /**
  757. * Extend method HTML documentation
  758. *
  759. * @param array $data The event data
  760. * @return boolean Response
  761. */
  762. public static function CreateMethodHtmlHook($data){
  763. if(!self::$EnableRest)
  764. return true;
  765. PhpWsdl::Debug('Append HTML method information with PhpWsdlServers information');
  766. $res=&$data['res'];
  767. $server=$data['server'];
  768. $m=$data['method'];
  769. if(!is_null($m->Settings))
  770. if(isset($m->Settings['rest'])){
  771. $temp=$m->Settings['rest'];
  772. $keys=array_keys($temp);
  773. $i=-1;
  774. $len=sizeof($keys);
  775. while(++$i<$len){
  776. $method=$keys[$i];
  777. $rest=$temp[$keys[$i]];
  778. $rTemp=Array();
  779. $j=-1;
  780. $rLen=sizeof($rest);
  781. while(++$j<$rLen){
  782. $url=$server->EndPoint.$rest[$j]['path'];
  783. $rTemp[]='<span class="bold">'.$method.'</span> REST URI: <span class="pre"><a href="'.$url.'">'.$url.'</a></span>';
  784. if(!is_null($rest[$j]['docs']))
  785. $rTemp[]='('.nl2br(htmlentities($rest[$j]['docs'])).')';
  786. }
  787. $res[]='<p>'.implode("<br>\n",$rTemp).'</p>';
  788. }
  789. }
  790. $url=$server->EndPoint.'/'.$m->Name.'/';
  791. $pLen=sizeof($m->Param);
  792. if($pLen>0){
  793. $i=-1;
  794. while(++$i<$pLen)
  795. $url.=':'.$m->Param[$i]->Name.'/';
  796. }
  797. $res[]='<p>Default <span class="bold">GET</span> REST URI: <span class="pre"><a href="'.$url.'">'.$url.'</a></span></p>';
  798. return true;
  799. }
  800. /**
  801. * Attach the PHP client sources
  802. *
  803. * @param array $data The event data
  804. * @return boolean Response
  805. */
  806. public static function PdfAttachmentHook($data){
  807. $temp=&$data['param'];
  808. $cnt=&$data['cnt'];
  809. $server=$data['server'];
  810. if(self::$EnableJson){
  811. if(!$server->ForceNotOutputJsonPhp){
  812. $cnt++;
  813. $temp['attachment_'.$cnt]=$server->Name.'.jsonclient.php:'.self::$UseServer->GetJsonPhpUri();
  814. }
  815. if(self::$AttachJsInPdf&&!$server->ForceNotOutputJsonJs){
  816. $cnt++;
  817. $temp['attachment_'.$cnt]=$server->Name.'.jsonclient.js:'.self::$UseServer->GetJsonJsUri();
  818. if(self::IsJsPackerAvailable()){
  819. $cnt++;
  820. $temp['attachment_'.$cnt]=$server->Name.'.jsonclient.min.js:'.self::$UseServer->GetJsonJsUri().'&min';
  821. }
  822. }
  823. }
  824. if(self::$EnableRpc&&!$server->ForceNotOutputRpcPhp){
  825. $cnt++;
  826. $temp['attachment_'.$cnt]=$server->Name.'.xmlrpcclient.php:'.self::$UseServer->GetRpcPhpUri();
  827. }
  828. if(self::$EnableHttp&&!$server->ForceNotOutputHttpPhp){
  829. $cnt++;
  830. $temp['attachment_'.$cnt]=$server->Name.'.httpclient.php:'.self::$UseServer->GetHttpPhpUri();
  831. }
  832. if(self::$EnableRest&&!$server->ForceNotOutputRestPhp){
  833. $cnt++;
  834. $temp['attachment_'.$cnt]=$server->Name.'.restclient.php:'.self::$UseServer->GetRestPhpUri();
  835. }
  836. return true;
  837. }
  838. /**
  839. * Constructor hook
  840. *
  841. * @param array $data The event data
  842. * @return boolean Response
  843. */
  844. public static function ConstructorHook($data){
  845. new PhpWsdlServers($data['server']);
  846. return true;
  847. }
  848. /**
  849. * Get the HTTP PHP download URI
  850. *
  851. * @return string The URI
  852. */
  853. public function GetHttpPhpUri(){
  854. return ((is_null($this->HttpPhpUri))?$this->Server->EndPoint:$this->HttpPhpUri).'?PHPHTTPCLIENT';
  855. }
  856. /**
  857. * Get the JSON PHP download URI
  858. *
  859. * @return string The URI
  860. */
  861. public function GetJsonPhpUri(){
  862. return ((is_null($this->JsonPhpUri))?$this->Server->EndPoint:$this->JsonPhpUri).'?PHPJSONCLIENT';
  863. }
  864. /**
  865. * Get the REST PHP download URI
  866. *
  867. * @return string The URI
  868. */
  869. public function GetRestPhpUri(){
  870. return ((is_null($this->RestPhpUri))?$this->Server->EndPoint:$this->RestPhpUri).'?PHPRESTCLIENT';
  871. }
  872. /**
  873. * Get the XML RPC PHP download URI
  874. *
  875. * @return string The URI
  876. */
  877. public function GetRpcPhpUri(){
  878. return ((is_null($this->RpcPhpUri))?$this->Server->EndPoint:$this->RpcPhpUri).'?PHPRPCCLIENT';
  879. }
  880. /**
  881. * Get the JSON JavaScript download URI
  882. *
  883. * @return string The URI
  884. */
  885. public function GetJsonJsUri(){
  886. return ((is_null($this->JsonJsUri))?$this->Server->EndPoint:$this->JsonJsUri).'?JSJSONCLIENT';
  887. }
  888. /**
  889. * Output the JavaScript JSON client source for this webservice
  890. *
  891. * @param boolean $withHeaders Send JavaScript headers? (default: TRUE)
  892. * @param boolean $echo Print source (default: TRUE)
  893. * @param boolean $cache Cache the result (default: TRUE);
  894. * @return string JavaScript source
  895. */
  896. public function OutputJs($withHeaders=true,$echo=true,$cache=true){
  897. PhpWsdl::Debug('Output JavaScript');
  898. if(sizeof($this->Server->Methods)<1)
  899. $this->Server->CreateWsdl();
  900. if($withHeaders)
  901. $this->JavaScriptHeaders();
  902. if(is_null($this->JsonJs)){
  903. $res=Array();
  904. $data=Array(
  905. 'server' => $this,
  906. 'res' => &$data,
  907. 'withheaders' => &$withHeaders,
  908. 'echo' => &$echo,
  909. 'cache' => &$cache
  910. );
  911. if(PhpWsdl::CallHook('ServersBeginOutputJsHook',$data)){
  912. $res[]='var '.$this->Server->Name.'JsonClient=function(){';
  913. $res[]=' this._EndPoint="'.$this->Server->EndPoint.'";';
  914. $res[]=' this._Call=function(method,param,cb,cbData){';
  915. $res[]=' var server=(window.XMLHttpRequest)?new XMLHttpRequest():new ActiveXObject("Microsoft.XMLHTTP");';
  916. $res[]=' server.open("POST",this._EndPoint,cb!=null);';
  917. $res[]=' server.setRequestHeader("Content-Type","application/x-www-form-urlencoded");';
  918. $res[]=' var req="json="+encodeURIComponent(JSON.stringify({call:method,param:(typeof(param)!="undefined")?param:[]}));';
  919. $res[]=' if(cb){';
  920. $res[]=' server.onreadystatechange=this._EndCall;';
  921. $res[]=' server.cb=cb;';
  922. $res[]=' server.cbData=(typeof(cbData)!="undefined")?cbData:null;';
  923. $res[]=' server.send(req);';
  924. $res[]=' return server;';
  925. $res[]=' }else{';
  926. $res[]=' server.send(req);';
  927. $res[]=' var res=JSON.parse(server.responseText);';
  928. $res[]=' delete(server);';
  929. $res[]=' return res;';
  930. $res[]=' }';
  931. $res[]=' };';
  932. $res[]=' this._EndCall=function(e){';
  933. $res[]=' var server=e.currentTarget;';
  934. $res[]=' if(server.readyState!=4)';
  935. $res[]=' return;';
  936. $res[]=' if(server.status!=200)';
  937. $res[]=' throw(new Exception("AJAX error "+server.status+": "+server.statusText));';
  938. $res[]=' if(server.cb)';
  939. $res[]=' server.cb(JSON.parse(server.responseText),server.cbData);';
  940. $res[]=' server.cb=null;';
  941. $res[]=' server.cbData=null;';
  942. $res[]=' delete(server);';
  943. $res[]=' };';
  944. }
  945. $i=-1;
  946. $len=sizeof($this->Server->Methods);
  947. while(++$i<$len){
  948. $m=$this->Server->Methods[$i];
  949. if(PhpWsdl::CallHook(
  950. 'ServersOutputMethodJsHook',
  951. array_merge(
  952. $data,
  953. Array(
  954. 'method' => &$m
  955. )
  956. )
  957. )
  958. ){
  959. $temp=Array();
  960. $j=-1;
  961. $pLen=sizeof($m->Param);
  962. while(++$j<$pLen)
  963. $temp[]=$m->Param[$j]->Name;
  964. $temp=implode(',',$temp);
  965. $res[]=' this.'.$m->Name.'=function('.$temp.(($temp!='')?',':'').'_cb,_cbData){';
  966. $res[]=' return this._Call("'.$m->Name.'",['.$temp.'],(typeof(_cb)!="undefined")?_cb:null,(typeof(_cbData)!="undefined")?_cbData:null);';
  967. $res[]=' };';
  968. }
  969. }
  970. $i=-1;
  971. $len=sizeof($this->Server->Types);
  972. while(++$i<$len){
  973. $t=$this->Server->Types[$i];
  974. if($t->IsArray)
  975. continue;
  976. if(PhpWsdl::CallHook(
  977. 'ServersOutputTypeJsHook',
  978. array_merge(
  979. $data,
  980. Array(
  981. 'type' => &$t
  982. )
  983. )
  984. )
  985. ){
  986. $eLen=sizeof($t->Elements);
  987. $j=-1;
  988. $class=get_class($t);
  989. switch($class){
  990. case 'PhpWsdlComplex':
  991. $temp=Array();
  992. if(!is_null($t->Inherit)){
  993. $it=$this->Server->GetType($t->Inherit);
  994. $j=-1;
  995. $tLen=sizeof($it->Elements);
  996. while(++$j<$tLen)
  997. $temp[]=$it->Elements[$j]->Name;
  998. }else{
  999. $tLen=0;
  1000. }
  1001. $j=-1;
  1002. while(++$j<$eLen)
  1003. $temp[]=$t->Elements[$j]->Name;
  1004. $tLen+=$eLen;
  1005. $res[]=' this.'.$t->Name.'=function('.implode(',',$temp).'){';
  1006. $j=-1;
  1007. while(++$j<$tLen)
  1008. $res[]=' this.'.$temp[$j].'=(typeof('.$temp[$j].')!="undefined")?'.$temp[$j].':null;';
  1009. $res[]=' };';
  1010. break;
  1011. case 'PhpWsdlEnum':
  1012. $res[]=' this.'.$t->Name.'={';
  1013. while(++$j<$eLen){
  1014. $temp=explode('=',$t->Elements[$j],2);
  1015. if(sizeof($temp)==1) $temp[]=$temp[0];
  1016. $res[]=' "'.$temp[0].'":"'.addslashes($temp[1]).'"'.(($j<$eLen-1)?',':'');
  1017. }
  1018. $res[]=' };';
  1019. break;
  1020. default:
  1021. PhpWsdl::Debug('WARNING: Could not create JavaScript code for unknown type "'.$class.'"!');
  1022. break;
  1023. }
  1024. }
  1025. }
  1026. PhpWsdl::CallHook('ServersEndOutputJsHook',$data);
  1027. $res[]='};';
  1028. $res=utf8_encode(implode("\n",$res));
  1029. $this->JsonJs=$res;
  1030. }
  1031. $min=$this->ForceJsonJsMin||(self::HasParam('min')&&self::IsJsPackerAvailable()&&!$this->ForceNotJsonJsMin);
  1032. if($min){
  1033. if(is_null($this->JsonJsMin))
  1034. $this->JsonJsMin=$this->PackJavaScript($this->JsonJs);
  1035. $res=$this->JsonJsMin;
  1036. }else if(!$min){
  1037. $res=$this->JsonJs;
  1038. }
  1039. if($cache)
  1040. $this->Server->WriteWsdlToCache(null,null,null,true);
  1041. if($echo)
  1042. echo $res;
  1043. return $res;
  1044. }
  1045. /**
  1046. * Get data from cache
  1047. *
  1048. * @param array $data The event data
  1049. * @return boolean Response
  1050. */
  1051. public function ReadCacheHook($data){
  1052. $d=&$data['data'];
  1053. $this->HttpPhp=$d['phphttp'];
  1054. $this->JsonPhp=$d['phpjson'];
  1055. $this->JsonJs=$d['jsjson'];
  1056. $this->JsonJsMin=$d['jsjsonmin'];
  1057. $this->RestPhp=$d['phprest'];
  1058. $this->RpcPhp=$d['phprpc'];
  1059. return true;
  1060. }
  1061. /**
  1062. * Write data to the cache
  1063. *
  1064. * @param array $data The event data
  1065. * @return boolean Response
  1066. */
  1067. public function WriteCacheHook($data){
  1068. $d=&$data['data'];
  1069. $d['phphttp']=$this->HttpPhp;
  1070. $d['phpjson']=$this->JsonPhp;
  1071. $d['jsjson']=$this->JsonJs;
  1072. $d['jsjsonmin']=$this->JsonJsMin;
  1073. $d['phprest']=$this->RestPhp;
  1074. $d['phprpc']=$this->RpcPhp;
  1075. return true;
  1076. }
  1077. /**
  1078. * Handle a REST request
  1079. *
  1080. * @return boolean Response
  1081. */
  1082. public function HandleRestRequest(){
  1083. // Find the requested method
  1084. $path=$_SERVER['PATH_INFO'];
  1085. $temp=self::GetParam('method');
  1086. $method=($temp=='')?$_SERVER['REQUEST_METHOD']:$temp;
  1087. PhpWsdl::Debug('REST call "'.$method.'" at "'.$path.'"');
  1088. $temp=$this->GetRestMethod($method,$path);
  1089. if(is_null($temp))
  1090. throw(new Exception('Method not found'));
  1091. list($method,$target)=$temp;
  1092. $req=Array(
  1093. 'call' => $method->Name,
  1094. 'param' => Array()
  1095. );
  1096. PhpWsdl::CallHook(
  1097. 'RestCallHook',
  1098. Array(
  1099. 'server' => $this,
  1100. 'req' => &$req,
  1101. 'path' => &$path,
  1102. 'method' => &$method,
  1103. 'target' => &$target
  1104. )
  1105. );
  1106. // Collect the parameters
  1107. if(PhpWsdl::CallHook(
  1108. 'RestParametersHook',
  1109. Array(
  1110. 'server' => $this,
  1111. 'req' => &$req,
  1112. 'path' => &$path,
  1113. 'method' => &$method,
  1114. 'target' => &$target
  1115. )
  1116. )
  1117. ){
  1118. $pLen=sizeof($method->Param);
  1119. if(is_null($target)){
  1120. // Unknown parameter handling
  1121. PhpWsdl::Debug('Undefined parameter handling');
  1122. $temp=explode('/',$path);
  1123. $tLen=sizeof($temp)-2;
  1124. // Collect parameters from the path info
  1125. $i=-1;
  1126. while(++$i<$pLen){
  1127. $p=$method->Param[$i];
  1128. if($i>$tLen)
  1129. break;
  1130. PhpWsdl::Debug('Found parameter "'.$p->Name.'"');
  1131. $req['param'][]=self::DecodeType($p->Type,urldecode($temp[$i+2]),true);
  1132. }
  1133. }else{
  1134. // Fixed parameter handling
  1135. PhpWsdl::Debug('Fixed parameter handling');
  1136. if(strpos($target,':')>-1){
  1137. PhpWsdl::Debug('Method with parameters');
  1138. list($mTemp,$pTemp)=explode(':',$target,2);
  1139. }else{
  1140. PhpWsdl::Debug('Method without parameters');
  1141. $mTemp=$target;
  1142. $pTemp=false;
  1143. }
  1144. if($pTemp!==false){
  1145. // Map input parameters to their names
  1146. $temp=explode('/',substr($path,strlen($mTemp)));
  1147. $tLen=sizeof($temp);
  1148. $iMap=explode('/',str_replace(':','',$pTemp));
  1149. $map=Array();
  1150. $i=-1;
  1151. $len=sizeof($iMap);
  1152. while(++$i<$len)
  1153. $map[$iMap[$i]]=$temp[$i];
  1154. // Sort parameters
  1155. $temp=Array();
  1156. $i=-1;
  1157. $lastValue=-1;
  1158. while(++$i<$pLen){
  1159. $p=$method->Param[$i];
  1160. $name=$p->Name;
  1161. if(isset($map[$name])){
  1162. $temp[]=self::DecodeType($p->Type,urldecode($map[$name]),true);
  1163. $lastValue=$i;
  1164. }else{
  1165. PhpWsdl::Debug('Parameter "'.$p->Name.'" not found');
  1166. $temp[]=null;
  1167. }
  1168. }
  1169. // Remove all parameters that are not present in the request
  1170. if($lastValue>-1&&$lastValue<$pLen){
  1171. $i=$lastValue;
  1172. $req['param']=array_slice($temp,0,$lastValue+1);
  1173. }else{
  1174. $req['param']=$temp;
  1175. }
  1176. }else{
  1177. $i=-1;
  1178. }
  1179. }
  1180. // Collect the last parameter from the request body
  1181. $in=file_get_contents('php://input');
  1182. if($in!=''&&$i<$pLen&&$pLen>0){
  1183. if(PhpWsdl::CallHook(
  1184. 'RestRequestHook',
  1185. Array(
  1186. 'server' => $this,
  1187. 'req' => &$req,
  1188. 'path' => &$path,
  1189. 'method' => &$method,
  1190. 'target' => &$target,
  1191. 'in' => &$in,
  1192. 'index' => $i
  1193. )
  1194. )
  1195. ){
  1196. PhpWsdl::Debug('Use request body as parameter #'.($i+1));
  1197. $req['param'][]=json_decode($temp);
  1198. }
  1199. }
  1200. }
  1201. if(PhpWsdl::$Debugging)
  1202. PhpWsdl::Debug('Parameters: '.print_r($req['param'],true));
  1203. // Execute the method and output the response
  1204. $temp=$this->HandleRequest($req,$method);
  1205. if(is_null($method->Return))
  1206. return false;// No return value -> no output
  1207. if(PhpWsdl::CallHook(
  1208. 'RestResponseHook',
  1209. Array(
  1210. 'server' => $this,
  1211. 'req' => &$req,
  1212. 'res' => &$temp,
  1213. 'path' => &$path,
  1214. 'method' => &$method,
  1215. 'target' => &$target
  1216. )
  1217. )
  1218. ){
  1219. if(self::$EnableCompression)
  1220. ob_start('ob_gzhandler');
  1221. $this->PlainTextHeaders();
  1222. echo self::EncodeType($method->Return->Type,$temp);
  1223. }
  1224. return false;
  1225. }
  1226. /**
  1227. * Handle a JSON request
  1228. *
  1229. * @return boolean Response
  1230. */
  1231. public function HandleJsonRequest(){
  1232. // Decode the JSON request
  1233. $json=(self::HasParam('json'))?'json':'JSON';
  1234. $json=preg_replace('/([^\\])\\n/','\\n',$json);
  1235. $req=json_decode(self::GetParam($json));
  1236. if(is_null($req)||!is_object($req))
  1237. throw(new Exception('Invalid JSON object'));
  1238. if(!isset($req->param))
  1239. $req->param=Array();
  1240. if(!isset($req->call))
  1241. throw(new Exception('Invalid JSON request'));
  1242. PhpWsdl::Debug('JSON call "'.$req->call.'"');
  1243. if(PhpWsdl::$Debugging)
  1244. PhpWsdl::Debug('Parameters: '.print_r($req->param,true));
  1245. $req=Array(
  1246. 'call' => $req->call,
  1247. 'param' => $req->param
  1248. );
  1249. // Find the requested method object
  1250. $method=$this->Server->GetMethod($req['call']);
  1251. if(is_null($method))
  1252. throw(new Exception('Method "'.$req['call'].'" not exists'));
  1253. // Execute the method and output the response
  1254. $temp=$this->HandleRequest($req,$method);
  1255. if(self::$EnableCompression)
  1256. ob_start('ob_gzhandler');
  1257. $this->PlainTextHeaders();
  1258. echo json_encode($temp);
  1259. return false;
  1260. }
  1261. /**
  1262. * Handle a http request
  1263. *
  1264. * @return boolean Response
  1265. */
  1266. public function HandleHttpRequest(){
  1267. // Initialize the method call
  1268. $req=Array(
  1269. 'call' => self::GetParam('call'),
  1270. 'param' => Array()
  1271. );
  1272. PhpWsdl::Debug('http call "'.$req['call'].'"');
  1273. // Find the requested method object and parameters
  1274. $method=$this->Server->GetMethod($req['call']);
  1275. if(is_null($method))
  1276. throw(new Exception('Method "'.$req['call'].'" not exists'));
  1277. if(PhpWsdl::CallHook(
  1278. 'HttpParametersHook',
  1279. Array(
  1280. 'server' => $this,
  1281. 'req' => &$req,
  1282. 'method' => &$method
  1283. )
  1284. )
  1285. ){
  1286. $i=-1;
  1287. $len=sizeof($method->Param);
  1288. while(++$i<$len){
  1289. $p=$method->Param[$i];
  1290. if(!self::HasParam($method->Param[$i]->Name)){
  1291. PhpWsdl::Debug('Collecting parameters stopped at missing "'.$method->Param[$i]->Name.'"');
  1292. break;
  1293. }
  1294. $temp=self::GetParam($method->Param[$i]->Name);
  1295. $req['param'][]=self::DecodeType($p->Type,$temp,true);
  1296. }
  1297. }
  1298. if(PhpWsdl::$Debugging)
  1299. PhpWsdl::Debug('Parameters: '.print_r($req['param'],true));
  1300. // Execute the method and output the response
  1301. $temp=$this->HandleRequest($req,$method);
  1302. if(is_null($method->Return))
  1303. return false;// No return value -> no output
  1304. if(PhpWsdl::CallHook(
  1305. 'HttpResponseHook',
  1306. Array(
  1307. 'server' => $this,
  1308. 'req' => &$req,
  1309. 'res' => &$temp,
  1310. 'method' => &$method
  1311. )
  1312. )
  1313. ){
  1314. if(self::$EnableCompression)
  1315. ob_start('ob_gzhandler');
  1316. $this->PlainTextHeaders();
  1317. echo self::EncodeType($method->Return->Type,$temp);
  1318. }
  1319. return false;
  1320. }
  1321. /**
  1322. * Handle a RPC request
  1323. *
  1324. * @return boolean Response
  1325. */
  1326. public function HandleRpcRequest(){
  1327. if(!self::IsRpcRequest())
  1328. return true;
  1329. PhpWsdl::Debug('Run XML RPC server');
  1330. $rpc=xmlrpc_server_create();
  1331. $i=-1;
  1332. $len=sizeof($this->Server->Methods);
  1333. while(++$i<$len)
  1334. xmlrpc_server_register_method($rpc,$this->Server->Methods[$i]->Name,Array($this,'RpcCallHandler'));
  1335. $temp=xmlrpc_server_call_method($rpc,file_get_contents('php://input'),null);
  1336. $this->XmlHeaders();
  1337. if(self::$EnableCompression)
  1338. ob_start('ob_gzhandler');
  1339. echo $temp;
  1340. return false;
  1341. }
  1342. /**
  1343. * (Internal) handle a RPC call
  1344. *
  1345. * @param string $method The method name
  1346. * @param array $param The parameters
  1347. * @return mixed The response
  1348. */
  1349. public function RpcCallHandler($method,$param){
  1350. PhpWsdl::Debug('XML RPC call "'.$method.'"');
  1351. $m=$this->Server->GetMethod($method);
  1352. if(is_null($m))
  1353. throw(new Exception('Method "'.$method.'" not exists'));
  1354. if(!self::$EnableRpcNamedParameters){
  1355. // Unnamed parameters
  1356. PhpWsdl::Debug('Unnamed parameters');
  1357. $req=Array(
  1358. 'call' => $m->Name,
  1359. 'param' => $param
  1360. );
  1361. }else{
  1362. // Named parameters
  1363. PhpWsdl::Debug('Named parameters');
  1364. $req=Array(
  1365. 'call' => $m->Name,
  1366. 'param' => Array()
  1367. );
  1368. $map=Array();
  1369. $i=-1;
  1370. $pLen=sizeof($param);
  1371. while(++$i<$pLen){
  1372. $p=$param[$i];
  1373. $keys=array_keys($p);
  1374. PhpWsdl::Debug('Found parameter "'.$keys[0].'"');
  1375. $map[$keys[0]]=$p[$keys[0]];
  1376. }
  1377. $lastValue=-1;
  1378. $i=-1;
  1379. $pLen=sizeof($m->Param);
  1380. while(++$i<$pLen){
  1381. $p=$m->Param[$i];
  1382. if(isset($map[$p->Name])){
  1383. $req['param'][]=$map[$p->Name];
  1384. $lastValue=$i;
  1385. }else{
  1386. PhpWsdl::Debug('Missing parameter "'.$p->Name.'"');
  1387. $req['param'][]=null;
  1388. }
  1389. }
  1390. if($lastValue<$pLen-1){
  1391. PhpWsdl::Debug('Slice parameters array from index #'.$lastValue);
  1392. $req['param']=array_slice($req['param'],0,$lastValue+1);
  1393. }
  1394. }
  1395. // Do recoding
  1396. if(!self::$NoRpcRecode){
  1397. PhpWsdl::Debug('Recoding parameters');
  1398. $i=-1;
  1399. $len=sizeof($req['param']);
  1400. while(++$i<$len){
  1401. if(is_null($req['param'][$i]))
  1402. continue;
  1403. $p=$m->Param[$i];
  1404. if(in_array($p->Type,PhpWsdl::$BasicTypes)){
  1405. PhpWsdl::Debug('"'.$p->Name.'" is a basic type');
  1406. continue;
  1407. }
  1408. $t=$this->Server->GetType($p->Type);
  1409. if($t->IsArray){
  1410. PhpWsdl::Debug('"'.$p->Name.'" is an array');
  1411. continue;
  1412. }
  1413. if(get_class($t)=='PhpWsdlEnum'&&in_array($p->Type,PhpWsdl::$BasicTypes)){
  1414. PhpWsdl::Debug('"'.$p->Name.'" is an basic type enumeration');
  1415. continue;
  1416. }
  1417. PhpWsdl::Debug('Recoding of "'.$p->Name.'" required');
  1418. $req['param'][$i]=$this->RecodeParameter($req['param'][$i],$t,$server);
  1419. }
  1420. }
  1421. if(PhpWsdl::$Debugging)
  1422. PhpWsdl::Debug('Parameters: '.print_r($req['param'],true));
  1423. return $this->HandleRequest($req,$m);
  1424. }
  1425. /**
  1426. * Recode an hash array into an object
  1427. *
  1428. * @param array $received The received hash array
  1429. * @param PhpWsdlObject $type The type
  1430. * @param PhpWsdl $server The PhpWsdl object
  1431. * @return object The object
  1432. */
  1433. private function RecodeParameter($received,$type,$server){
  1434. PhpWsdl::Debug('Recode "'.$type->Name.'"');
  1435. $obj=new stdClass();
  1436. $i=-1;
  1437. $len=sizeof($type->Elements);
  1438. while(++$i<$len){
  1439. $e=$type->Elements[$i];
  1440. $name=$e->Name;
  1441. if(!in_array($e->Type,PhpWsdl::$BasicTypes)){
  1442. $t=$server->GetType($e->Type);
  1443. if(is_null($t))
  1444. throw(new Exception('Could not recode "'.$e->Type.'"'));
  1445. if(get_class($t)=='PhpWsdlEnum'){
  1446. $basic=in_array($t->Type,PhpWsdl::$BasicTypes);
  1447. }else{
  1448. $basic=false;
  1449. $t=null;
  1450. }
  1451. }else{
  1452. $basic=true;
  1453. $t=null;
  1454. }
  1455. if(!$basic){
  1456. PhpWsdl::Debug('Recode element "'.$name.'" as object');
  1457. $obj[$name]=$this->RecodeParameter($received[$name],$t,$server);
  1458. }else{
  1459. PhpWsdl::Debug('Set element "'.$name.'"');
  1460. $obj[$name]=$received[$name];
  1461. }
  1462. }
  1463. return $obj;
  1464. }
  1465. /**
  1466. * Handle a request
  1467. *
  1468. * @param array $req The request data
  1469. * @param PhpWsdlMethod $method The method object
  1470. * @return mixed The response
  1471. */
  1472. private function HandleRequest($req,$method){
  1473. // Get the handler object
  1474. $res=null;
  1475. $obj=$this->Object;
  1476. if(is_null($obj))
  1477. if(!is_null($this->ClassName))
  1478. if(class_exists($this->ClassName))
  1479. eval("\$obj=new ".$this->ClassName."();");
  1480. PhpWsdl::Debug('Handler object: '.print_r($obj,true));
  1481. // Prepare the call
  1482. $call=($method->IsGlobal)
  1483. ?$method->Name
  1484. :Array(
  1485. $obj,
  1486. $method->Name
  1487. );
  1488. PhpWsdl::Debug('Call: '.print_r($call,true));
  1489. // Call the method
  1490. $pLen=sizeof($req['param']);
  1491. if($pLen<1){
  1492. return call_user_func($call);
  1493. }else if($pLen<2){
  1494. return call_user_func($call,$req['param'][0]);
  1495. }else{
  1496. return call_user_func_array($call,$req['param']);
  1497. }
  1498. }
  1499. /**
  1500. * Output plain text response headers
  1501. */
  1502. private function PlainTextHeaders(){
  1503. header('Content-Type: text/plain; encoding=UTF-8');
  1504. if(self::$DisableClientCache)
  1505. $this->NoCacheHeaders();
  1506. }
  1507. /**
  1508. * Output XML response headers
  1509. */
  1510. private function XmlHeaders(){
  1511. header('Content-Type: text/xml; encoding=UTF-8');
  1512. if(self::$DisableClientCache)
  1513. $this->NoCacheHeaders();
  1514. }
  1515. /**
  1516. * Output JavaScript response headers
  1517. */
  1518. private function JavaScriptHeaders(){
  1519. header('Content-Type: text/javascript; encoding=UTF-8');
  1520. header('Content-Disposition: attachment; filename='.$this->Server->Name.'JsonClient.js');
  1521. if(self::$DisableClientCache)
  1522. $this->NoCacheHeaders();
  1523. }
  1524. /**
  1525. * Output headers to disable the client cache
  1526. */
  1527. private function NoCacheHeaders(){
  1528. header('Expires: Tue, 03 Jul 2001 06:00:00 GMT');
  1529. header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
  1530. header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
  1531. header('Cache-Control: post-check=0, pre-check=0',false);
  1532. header('Pragma: no-cache');
  1533. }
  1534. /**
  1535. * Get a http parameter
  1536. *
  1537. * @param string $name The key
  1538. * @return string The value
  1539. */
  1540. private static function GetParam($name){
  1541. $name=addslashes($name);
  1542. if(isset($_GET[$name]))
  1543. return stripslashes($_GET[$name]);
  1544. if(isset($_POST[$name]))
  1545. return stripslashes($_POST[$name]);
  1546. return '';
  1547. }
  1548. /**
  1549. * Determine if we got a GET/POST parameter
  1550. *
  1551. * @param string $name
  1552. * @return boolean Parameter exists?
  1553. */
  1554. private static function HasParam($name){
  1555. return isset($_GET[$name])||isset($_POST[$name]);
  1556. }
  1557. /**
  1558. * Find the handler method for a REST request
  1559. *
  1560. * @param string $method The method (GET/POST/...)
  1561. * @param string $path The REST server path
  1562. * @return array The handler method and the path or NULL, if not found
  1563. */
  1564. private function GetRestMethod($method,$path){
  1565. if(substr($path,strlen($path)-1)!='/')
  1566. $path.='/';
  1567. // Find a REST method
  1568. PhpWsdl::Debug('Find REST method "'.$method.'" at "'.$path.'"');
  1569. $i=-1;
  1570. $mLen=sizeof($this->Server->Methods);
  1571. while(++$i<$mLen){
  1572. $m=$this->Server->Methods[$i];
  1573. if(is_null($m->Settings))
  1574. continue;
  1575. if(!isset($m->Settings['rest']))
  1576. continue;
  1577. if(!isset($m->Settings['rest'][$method]))
  1578. continue;
  1579. $rest=$m->Settings['rest'][$method];
  1580. $j=-1;
  1581. $rLen=sizeof($rest);
  1582. while(++$j<$rLen){
  1583. $call=$rest[$j]['path'];
  1584. $param=null;
  1585. if(strpos($call,':')>-1)
  1586. list($call,$param)=explode(':',$call,2);
  1587. if(substr($call,strlen($call)-1)!='/')
  1588. $call.='/';
  1589. if((!is_null($param)&&substr($path,0,strlen($call))==$call)||$path==$call){
  1590. PhpWsdl::Debug('Method found at index #'.$i.' with target "'.$rest[$j]['path'].'"');
  1591. return Array($m,$rest[$j]['path']);
  1592. }
  1593. }
  1594. }
  1595. // Find a method without REST declaration
  1596. $temp=explode('/',$path);
  1597. $res=$this->Server->GetMethod($temp[1]);
  1598. return (is_null($res))
  1599. ?null
  1600. :Array($res,null);
  1601. }
  1602. /**
  1603. * Compress a JavaScript
  1604. *
  1605. * @param string $js The uncompressed JavaScript
  1606. * @return string The compressed JavaScript
  1607. */
  1608. private function PackJavaScript($js){
  1609. if(!self::IsJsPackerAvailable())
  1610. return $js;
  1611. PhpWsdl::Debug('Compress a JavaScript');
  1612. if(PhpWsdl::HasHookHandler('ServersPackJsHook'))
  1613. return PhpWsdl::CallHook(
  1614. 'ServersPackJsHook',
  1615. Array(
  1616. 'server' => $this,
  1617. 'js' => &$js
  1618. )
  1619. );
  1620. $packer=new PhpWsdlJavaScriptPacker(utf8_decode($js),62,true,true);
  1621. return utf8_encode($packer->pack());
  1622. }
  1623. /**
  1624. * Determine if the JavaScript packer is available
  1625. *
  1626. * @return boolean Available?
  1627. */
  1628. public static function IsJsPackerAvailable(){
  1629. return PhpWsdl::HasHookHandler('ServersPackJsHook')||class_exists('PhpWsdlJavaScriptPacker');
  1630. }
  1631. /**
  1632. * Encode a value as parameter or return value
  1633. *
  1634. * @param string $type The type name
  1635. * @param mixed $value The value to be encoded
  1636. * @param boolean $parameter Is this a parameter value? (default: FALSE)
  1637. * @return mixed The encoded value
  1638. */
  1639. public static function EncodeType($type,$value,$parameter=false){
  1640. $res=null;
  1641. $data=Array(
  1642. 'type' => &$type,
  1643. 'value' => &$value,
  1644. 'parameter' => &$parameter,
  1645. 'res' => &$res
  1646. );
  1647. if(!PhpWsdl::CallHook(
  1648. 'EncodeTypeHook',
  1649. $data
  1650. )
  1651. )
  1652. return $res;
  1653. if(!PhpWsdl::CallHook(
  1654. 'EncodeType'.(($parameter)?'Parameter':'Return').'Hook',
  1655. $data
  1656. )
  1657. )
  1658. return $res;
  1659. if(in_array($type,PhpWsdl::$BasicTypes))
  1660. return $value;
  1661. $t=self::$UseServer->Server->GetType($type);
  1662. if(is_null($t))
  1663. throw(new Exception('Could not encode type "'.$type.'"'));
  1664. if(get_class($t)=='PhpWsdlEnum')
  1665. $type=$t->Type;
  1666. return (in_array($type,PhpWsdl::$BasicTypes))?$value:json_encode($value);
  1667. }
  1668. /**
  1669. * Decode a parameter or a return value
  1670. *
  1671. * @param string $type The type name
  1672. * @param mixed $value The value to be decoded
  1673. * @param boolean $parameter Is this a parameter value? (default: FALSE)
  1674. * @return mixed The decoded value
  1675. */
  1676. public static function DecodeType($type,$value,$parameter=false){
  1677. $res=null;
  1678. $data=Array(
  1679. 'type' => &$type,
  1680. 'value' => &$value,
  1681. 'parameter' => &$parameter,
  1682. 'res' => &$res
  1683. );
  1684. if(!PhpWsdl::CallHook(
  1685. 'DecodeTypeHook',
  1686. $data
  1687. )
  1688. )
  1689. return $res;
  1690. if(!PhpWsdl::CallHook(
  1691. 'DecodeType'.(($parameter)?'Parameter':'Return').'Hook',
  1692. $data
  1693. )
  1694. )
  1695. return $res;
  1696. if(in_array($type,PhpWsdl::$BasicTypes))
  1697. return $value;
  1698. $t=self::$UseServer->Server->GetType($type);
  1699. if(is_null($t))
  1700. throw(new Exception('Could not decode type "'.$type.'"'));
  1701. if(get_class($t)=='PhpWsdlEnum')
  1702. $type=$t->Type;
  1703. return (in_array($type,PhpWsdl::$BasicTypes))?$value:json_decode($value);
  1704. }
  1705. }