1: <?php
2:
3:
4:
5: ini_set('soap.wsdl_cache_enabled', false);
6:
7: class QSoapParameter extends QBaseClass {
8: protected $strName;
9: protected $strType;
10: protected $blnArray;
11: protected $blnReference;
12:
13: public function __construct($strName, $strType, $blnArray, $blnReference) {
14: $this->strName = $strName;
15: $this->strType = $strType;
16: $this->blnArray = $blnArray;
17: $this->blnReference = $blnReference;
18: }
19:
20: public function GetWsdlMessagePart(&$strComplexTypesArray) {
21: if ($this->blnArray) {
22: try {
23: $strType = QType::SoapType($this->strType);
24: $strArrayTypeName = QSoapService::GetArrayTypeName($strType);
25: $strToReturn = sprintf('<part name="%s" type="xsd1:%s"/>', $this->strName, $strArrayTypeName);
26:
27: QSoapService::AlterComplexTypesArrayForArrayType($strArrayTypeName, $strType, $strComplexTypesArray);
28: return $strToReturn;
29: } catch (QInvalidCastException $objExc) {}
30:
31: $strArrayTypeName = QSoapService::GetArrayTypeName($this->strType);
32: QSoapService::AlterComplexTypesArrayForArrayType($strArrayTypeName, 'xsd1:' . $this->strType, $strComplexTypesArray);
33: $strToReturn = sprintf('<part name="%s" type="xsd1:%s"/>', $this->strName, $strArrayTypeName);
34: } else {
35: try {
36: return sprintf('<part name="%s" type="xsd:%s"/>', $this->strName, QType::SoapType($this->strType));
37: } catch (QInvalidCastException $objExc) {}
38:
39: $strToReturn = sprintf('<part name="%s" type="xsd1:%s"/>', $this->strName, $this->strType);
40: }
41:
42: $objReflection = new ReflectionMethod($this->strType, 'AlterSoapComplexTypeArray');
43: $objReflection->invokeArgs(null, array(&$strComplexTypesArray));
44: return $strToReturn;
45: }
46:
47: public function __get($strName) {
48: try {
49: switch ($strName) {
50: case 'Name': return $this->strName;
51: case 'Type': return $this->strType;
52: case 'Array': return $this->blnArray;
53: case 'Reference': return $this->blnReference;
54: default: return parent::__get($strName);
55: }
56: } catch (QCallerException $objExc) {
57: $objExc->IncrementOffset();
58: throw $objExc;
59: }
60: }
61:
62: public function __set($strName, $mixValue) {
63: try {
64: switch ($strName) {
65: case 'Name': return ($this->strName = QType::Cast($mixValue, QType::String));
66: case 'Type': return ($this->strType= QType::Cast($mixValue, QType::String));
67: case 'Array': return ($this->blnArray = QType::Cast($mixValue, QType::Boolean));
68: case 'Reference': return ($this->blnReference = QType::Cast($mixValue, QType::Boolean));
69: default: return parent::__set($strName, $mixValue);
70: }
71: } catch (QCallerException $objExc) {
72: $objExc->IncrementOffset();
73: throw $objExc;
74: }
75: }
76:
77: public function Display() {
78: return sprintf('%s%s %s$%s',
79: $this->strType,
80: ($this->blnArray) ? '[]' : '',
81: ($this->blnReference) ? '&' : '',
82: $this->strName);
83: }
84:
85: public function DisplayReturn() {
86: return sprintf('%s%s',
87: $this->strType,
88: ($this->blnArray) ? '[]' : '');
89: }
90:
91: public function IsObject() {
92: switch ($this->strType) {
93: case QType::String:
94: case QType::Integer:
95: case QType::Float:
96: case QType::Boolean:
97: case QType::DateTime:
98: return false;
99: }
100:
101: return true;
102: }
103:
104: public function GetPhpFromSoapCode($strArgumentName) {
105:
106: if ($this->IsObject()) {
107: if ($this->blnArray)
108:
109: return sprintf('%s = %s::GetArrayFromSoapArray(%s); ', $strArgumentName, $this->strType, $strArgumentName);
110: else
111:
112: return sprintf('%s = %s::GetObjectFromSoapObject(%s); ', $strArgumentName, $this->strType, $strArgumentName);
113:
114:
115:
116: } else if ($this->strType == QType::DateTime) {
117: if ($this->blnArray)
118: return sprintf('$dttArray = array(); foreach(%s as $strDate) { ' .
119: 'array_push($dttArray, new QDateTime($strDate)); } %s = $dttArray; ',
120: $strArgumentName, $strArgumentName);
121: else
122: return sprintf('%s = new QDateTime(%s); ', $strArgumentName, $strArgumentName);
123:
124:
125: } else
126: return null;
127: }
128:
129: public function GetSoapFromPhpCode($strArgumentName) {
130:
131: if ($this->IsObject()) {
132: if ($this->blnArray)
133: return sprintf('%s::GetSoapArrayFromArray(%s)', $this->strType, $strArgumentName);
134: else
135: return sprintf('%s::GetSoapObjectFromObject(%s, true)', $this->strType, $strArgumentName);
136:
137:
138: } else if ($this->strType == QType::DateTime) {
139: if ($this->blnArray)
140: return sprintf('QDateTime::GetSoapDateTimeArray(%s)', $strArgumentName);
141: else
142: return sprintf('%s->qFormat(QDateTime::FormatSoap)', $strArgumentName);
143:
144:
145: } else
146: return $strArgumentName;
147: }
148: }
149:
150: class QSoapMethod extends QBaseClass {
151: protected $strName;
152: protected $objParameters = array();
153: protected $objReturnParameter;
154:
155: public function __construct($strName) {
156: $this->strName = $strName;
157: }
158:
159: public function AddParameter(QSoapParameter $objParameter) {
160: $this->objParameters[$objParameter->Name] = $objParameter;
161: }
162:
163: public function GetParameter($strName) {
164: if (array_key_exists($strName, $this->objParameters))
165: return $this->objParameters[$strName];
166: else
167: return null;
168: }
169:
170: public function GetAllParameters() {
171: return $this->objParameters;
172: }
173:
174: public function GetWsdlPortTypeOperation() {
175: return sprintf('<operation name="%s">' .
176: '<input message="tns:%sRequest"/>' .
177: '<output message="tns:%sResponse"/>' .
178: '</operation>',
179: $this->strName,
180: $this->strName,
181: $this->strName);
182: }
183:
184: public function GetWsdlBindingOperation($strNamespace) {
185: $strSoapBody = sprintf('<soap:body use="encoded" namespace="%s/%s" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>',
186: $strNamespace, $this->strName);
187: return sprintf('<operation name="%s"><soap:operation soapAction="%s/%s"/>' .
188: '<input>%s</input>' .
189: '<output>%s</output></operation>',
190: $this->strName,
191: $strNamespace,
192: $this->strName,
193: $strSoapBody,
194: $strSoapBody);
195: }
196:
197: public function GetWsdlMethodMessages(&$strComplexTypesArray) {
198: $strRequest = '';
199: foreach ($this->objParameters as $objParameter)
200: $strRequest .= $objParameter->GetWsdlMessagePart($strComplexTypesArray);
201:
202: $strResponse = '';
203: if ($this->objReturnParameter)
204: $strResponse = $this->objReturnParameter->GetWsdlMessagePart($strComplexTypesArray);
205: foreach ($this->objParameters as $objParameter)
206: if ($objParameter->Reference)
207: $strResponse .= $objParameter->GetWsdlMessagePart($strComplexTypesArray);
208:
209: $strToReturn = sprintf('<message name="%sRequest">%s</message>', $this->strName, $strRequest);
210: $strToReturn .= sprintf('<message name="%sResponse">%s</message>', $this->strName, $strResponse);
211: return $strToReturn;
212: }
213:
214: public function __get($strName) {
215: try {
216: switch ($strName) {
217: case 'Name': return $this->strName;
218: case 'ReturnParameter': return $this->objReturnParameter;
219: default: return parent::__get($strName);
220: }
221: } catch (QCallerException $objExc) {
222: $objExc->IncrementOffset();
223: throw $objExc;
224: }
225: }
226:
227: public function __set($strName, $mixValue) {
228: try {
229: switch ($strName) {
230: case 'Name': return ($this->strName = QType::Cast($mixValue, QType::String));
231: case 'ReturnParameter': return ($this->objReturnParameter = QType::Cast($mixValue, 'QSoapParameter'));
232: default: return parent::__set($strName, $mixValue);
233: }
234: } catch (QCallerException $objExc) {
235: $objExc->IncrementOffset();
236: throw $objExc;
237: }
238: }
239:
240: public function Display() {
241: $strParameters = '';
242: foreach ($this->objParameters as $objParameter)
243: $strParameters .= ', ' . $objParameter->Display();
244: if ($strParameters)
245: $strParameters = substr($strParameters, 2);
246: $strToReturn = sprintf('public %s %s(%s)', ($this->objReturnParameter) ? $this->objReturnParameter->DisplayReturn() : 'void', $this->strName, $strParameters);
247: return $strToReturn;
248: }
249: }
250:
251: class QSoapService extends QBaseClass {
252: public static $DefaultNamespace = 'http://qcubed.defaultnamespace.com';
253:
254: protected $objMethodArray = array();
255: protected $strNamespace;
256: protected $strClassName;
257: protected $objSoapServer;
258:
259: public static function GetArrayTypeName($strType) {
260: return sprintf('ArrayOf%s', ucfirst($strType));
261: }
262:
263: public static function AlterComplexTypesArrayForArrayType($strArrayTypeName, $strType, &$strComplexTypesArray) {
264: if (!array_key_exists($strArrayTypeName, $strComplexTypesArray))
265: $strComplexTypesArray[$strArrayTypeName] = sprintf(
266: '<complexType name="%s"><complexContent><restriction base="soapenc:Array">' .
267: '<attribute ref="soapenc:arrayType" wsdl:arrayType="%s[]"/>' .
268: '</restriction></complexContent></complexType>',
269: $strArrayTypeName,
270: $strType);
271: }
272:
273: public static function Run($strClassName, $strNamespace = null) {
274: QApplication::$EncodingType = 'UTF-8';
275:
276: $objWsdlCache = new QCache('soap', QApplication::$ScriptName, 'wsdl', QApplication::$ScriptFilename);
277: $objDiscoCache = new QCache('soap', QApplication::$ScriptName, 'disco', QApplication::$ScriptFilename);
278: $objClassWrapperCache = new QCache('soap', QApplication::$ScriptName, 'class.php', QApplication::$ScriptFilename);
279:
280:
281: $strDisco = $objDiscoCache->GetData();
282:
283: if (($strDisco === false) || (!$strNamespace))
284: $objReflection = new ReflectionClass($strClassName);
285:
286:
287: if (!$strNamespace) {
288: $objReflectionProperties = $objReflection->getStaticProperties();
289: $strNamespace = $objReflectionProperties['DefaultNamespace'];
290: }
291: $strNamespace = trim($strNamespace);
292: if (QString::LastCharacter($strNamespace) == '/')
293: $strNamespace = substr($strNamespace, 0, strlen($strNamespace) - 1);
294:
295:
296: if ($strDisco === false) {
297:
298: $objService = new $strClassName($strClassName, $strNamespace);
299:
300:
301: try {
302: $objService->SetupSoapMethods($objReflection);
303: } catch (QCallerException $objExc) {
304: $objExc->IncrementOffset();
305: throw $objExc;
306: }
307:
308:
309: $objWsdlCache->SaveData($objService->GetWsdl());
310: $objDiscoCache->SaveData($objService->GetDisco());
311: $objClassWrapperCache->SaveData($objService->GetClassWrapper());
312: }
313:
314:
315: if ($_SERVER['REQUEST_METHOD'] == 'GET') {
316: if (array_key_exists('QUERY_STRING', $_SERVER))
317: switch (strtolower($_SERVER['QUERY_STRING'])) {
318: case 'disco':
319: QApplication::$ContentType = 'text/xml';
320: _p('<?xml version="1.0" encoding="' . QApplication::$EncodingType . '"?>', false);
321: _p($objDiscoCache->GetData(), false);
322: return;
323: case 'wsdl':
324: QApplication::$ContentType = 'text/xml';
325: _p('<?xml version="1.0" encoding="' . QApplication::$EncodingType . '"?>', false);
326: _p($objWsdlCache->GetData(), false);
327: return;
328: }
329:
330: printf('<link rel="alternate" type="text/xml" href="%s?disco"/><a href="%s?disco">Web Service Discovery File</a> | <a href="%s?wsdl">Web Service Description File (WSDL)</a>',
331: QApplication::$ScriptName, QApplication::$ScriptName, QApplication::$ScriptName);
332: return;
333: }
334:
335:
336: $objService = new $strClassName($strClassName, $strNamespace);
337:
338:
339: $strRequest = file_get_contents("php://input");
340:
341:
342: require($objClassWrapperCache->GetFilePath());
343:
344:
345: $objService->objSoapServer = new SoapServer($objWsdlCache->GetFilePath());
346: $objService->objSoapServer->setClass($strClassName . 'Wrapper', $strClassName, $strNamespace);
347: $objService->objSoapServer->handle($strRequest);
348: }
349:
350: public function GetClassWrapper() {
351: $strNewClass = sprintf('<?php class %sWrapper extends %s { ', $this->strClassName, $this->strClassName);
352:
353: foreach ($this->objMethodArray as $objMethod) {
354: $strParameterArray = array();
355: foreach ($objMethod->GetAllParameters() as $objParameter) {
356: $strParameterDefinition = sprintf('%s %s$%s',
357: ($objParameter->Type == QType::DateTime) ? $objParameter->Type : '',
358: ($objParameter->Reference) ? '&' : '',
359: $objParameter->Name
360: );
361: $strParameterArray[] = trim($strParameterDefinition);
362: }
363:
364: $strNewClass .= sprintf('public function %s(%s) { $objArgs = func_get_args(); ', $objMethod->Name, implode(', ', $strParameterArray));
365:
366:
367: $strInputParameterArray = array();
368: $strOutputParameterArray = array();
369:
370: if ($objMethod->ReturnParameter)
371: array_push($strOutputParameterArray, $objMethod->ReturnParameter->GetSoapFromPhpCode('$objToReturn'));
372:
373: $intIndex = 0;
374: foreach ($objMethod->GetAllParameters() as $objParameter) {
375: $strArgumentName = sprintf('$objArgs[%s]', $intIndex);
376:
377:
378: array_push($strInputParameterArray, $strArgumentName);
379:
380: $strNewClass .= $objParameter->GetPhpFromSoapCode($strArgumentName);
381: if ($objParameter->Reference)
382: array_push($strOutputParameterArray, $objParameter->GetSoapFromPhpCode($strArgumentName));
383:
384: $intIndex++;
385: }
386:
387:
388: if ($objMethod->ReturnParameter)
389: $strNewClass .= '$objToReturn = ';
390: $strNewClass .= sprintf('parent::%s(%s); ', $objMethod->Name, implode(', ', $strInputParameterArray));
391:
392:
393: if (count($strOutputParameterArray) == 1)
394: $strNewClass .= sprintf('return %s;} ', $strOutputParameterArray[0]);
395: else if (count($strOutputParameterArray) > 1)
396: $strNewClass .= sprintf('return array(%s);} ', implode(', ', $strOutputParameterArray));
397: else
398: $strNewClass .= '} ';
399: }
400:
401: $strNewClass .= '} ?>';
402:
403:
404: $strNewClass = str_replace('{', "{\r\n", $strNewClass);
405: $strNewClass = str_replace('}', "}\r\n", $strNewClass);
406: $strNewClass = str_replace(';', ";\r\n", $strNewClass);
407:
408: return $strNewClass;
409: }
410:
411: public function GetLocation() {
412: return sprintf('http%s://%s%s',
413: (array_key_exists('HTTPS', $_SERVER) && $_SERVER['HTTPS']) ? 's' : '',
414: $_SERVER['HTTP_HOST'],
415: QApplication::$ScriptName);
416: }
417:
418: public function GetWsdlTypes($strComplexTypesArray) {
419: $strToReturn = sprintf('<types><schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="%s" ' .
420: 'xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">',
421: $this->strNamespace);
422:
423: foreach ($strComplexTypesArray as $strComplexType)
424: $strToReturn .= $strComplexType;
425:
426: $strToReturn .= '</schema></types>';
427: return $strToReturn;
428: }
429:
430: protected function GetWsdlService() {
431: $strToReturn = sprintf('<service name="%s"><documentation/>', $this->strClassName);
432: $strToReturn .= '<port name="Port" binding="tns:Binding">';
433: $strToReturn .= sprintf('<soap:address location="%s"/></port></service>', $this->GetLocation());
434: return $strToReturn;
435: }
436:
437: protected function GetWsdlBinding() {
438: $strToReturn = '<binding name="Binding" type="tns:PortType"><soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc"/>';
439:
440: foreach ($this->objMethodArray as $objMethod)
441: $strToReturn .= $objMethod->GetWsdlBindingOperation($this->strNamespace);
442:
443: $strToReturn .= '</binding>';
444: return $strToReturn;
445: }
446:
447: protected function GetWsdlPortType() {
448: $strToReturn = '<portType name="PortType">';
449:
450: foreach ($this->objMethodArray as $objMethod)
451: $strToReturn .= $objMethod->GetWsdlPortTypeOperation();
452:
453: $strToReturn .= '</portType>';
454: return $strToReturn;
455: }
456:
457: protected function GetDisco() {
458: $strUrl = $this->GetLocation();
459: $strToReturn = '<discovery xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' .
460: 'xmlns="http://schemas.xmlsoap.org/disco/">';
461: $strToReturn .= sprintf('<contractRef ref="%s?wsdl" docRef="%s" xmlns="http://schemas.xmlsoap.org/disco/scl/" />', $strUrl, $strUrl);
462: $strToReturn .= sprintf('<soap address="%s" xmlns:q1="%s" binding="q1:%sSoap" xmlns="http://schemas.xmlsoap.org/disco/soap/" />',
463: $strUrl, $this->strNamespace, $this->strClassName);
464: $strToReturn .= '</discovery>';
465: return $strToReturn;
466: }
467:
468: protected function GetWsdl() {
469: $strToReturn = sprintf('<definitions name="%s" ' .
470: 'targetNamespace="%s" ' .
471: 'xmlns="http://schemas.xmlsoap.org/wsdl/" ' .
472: 'xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" ' .
473: 'xmlns:tns="%s" ' .
474: 'xmlns:xsd="http://www.w3.org/2001/XMLSchema" ' .
475: 'xmlns:xsd1="%s">',
476: $this->strClassName, $this->strNamespace, $this->strNamespace, $this->strNamespace);
477:
478:
479: $strToReturn .= $this->GetWsdlService();
480:
481:
482: $strToReturn .= $this->GetWsdlBinding();
483:
484:
485: $strToReturn .= $this->GetWsdlPortType();
486:
487:
488: $strComplexTypesArray = array();
489: foreach ($this->objMethodArray as $objMethod)
490: $strToReturn .= $objMethod->GetWsdlMethodMessages($strComplexTypesArray);
491:
492:
493: $strToReturn .= $this->GetWsdlTypes($strComplexTypesArray);
494:
495:
496: $strToReturn .= '</definitions>';
497:
498: return $strToReturn;
499: }
500:
501: protected function SetupSoapMethods(ReflectionClass $objReflection) {
502: $objReflectionMethods = $objReflection->getMethods();
503: if ($objReflectionMethods) foreach ($objReflectionMethods as $objReflectionMethod) {
504: if ($objReflectionMethod->isPublic() && !$objReflectionMethod->isAbstract() &&
505: !$objReflectionMethod->isStatic() && !$objReflectionMethod->isConstructor() &&
506: !$objReflectionMethod->isDestructor() &&
507: ($objReflectionMethod->getDeclaringClass()->getName() != 'QBaseClass')) {
508: $objMethod = new QSoapMethod($objReflectionMethod->getName());
509:
510: $strComments = $objReflectionMethod->getDocComment();
511: if ($strComments) {
512:
513: $strTypeArray = array();
514: $blnArrayArray = array();
515: $strCommentArray = explode("\n", $strComments);
516: foreach ($strCommentArray as $strCommentLine) {
517: $strCommentLine = trim($strCommentLine);
518: $strMatches = array();
519:
520: preg_match_all ("/[\s]*\*[\s]*@param[\s]+([a-zA-Z0-9_]+)(\[\])?[\s]+[&$]*([a-zA-Z0-9_]+)/", $strCommentLine, $strMatches);
521: if ((count($strMatches) == 4) &&
522: (count($strMatches[0]) == 1) &&
523: (count($strMatches[1]) == 1) &&
524: (count($strMatches[2]) == 1) &&
525: (count($strMatches[3]) == 1)) {
526: $strType = $strMatches[1][0];
527: $strArray = $strMatches[2][0];
528: $strName = $strMatches[3][0];
529:
530: $strTypeArray[$strName] = $strType;
531: if ($strArray == '[]')
532: $blnArrayArray[$strName] = true;
533: else
534: $blnArrayArray[$strName] = false;
535: } else {
536: $strMatches = array();
537: preg_match_all ("/[\s]*\*[\s]*@return[\s]+([a-zA-Z0-9_]+)(\[\])?/", $strCommentLine, $strMatches);
538:
539: if ((count($strMatches) == 3) &&
540: (count($strMatches[0]) == 1) &&
541: (count($strMatches[1]) == 1) &&
542: (count($strMatches[2]) == 1)) {
543: $strType = $strMatches[1][0];
544: $strArray = $strMatches[2][0];
545:
546: if ($strArray == '[]')
547: $blnArray = true;
548: else
549: $blnArray = false;
550:
551: try {
552: $strType = QType::TypeFromDoc($strType);
553: } catch (QCallerException $objExc) {
554: $objExc->IncrementOffset();
555: throw $objExc;
556: }
557:
558: if ($strType != 'void')
559: $objMethod->ReturnParameter = new QSoapParameter($objMethod->Name . 'Result', $strType, $blnArray, false);
560: }
561: }
562: }
563:
564: $objParameters = $objReflectionMethod->getParameters();
565: if ($objParameters) foreach ($objParameters as $objParameter) {
566: $blnArray = false;
567: $strName = $objParameter->getName();
568:
569: if (array_key_exists($strName, $strTypeArray)) {
570: try {
571: $strType = QType::TypeFromDoc($strTypeArray[$strName]);
572: } catch (QCallerException $objExc) {
573: $objExc->IncrementOffset();
574: throw $objExc;
575: }
576: } else {
577: throw new QCallerException('Unable to determine Parameter Type for Method from PHPDoc Comment: ' . $objReflectionMethod->getName() . '(' . $strName . ')');
578: }
579:
580: if (array_key_exists($strName, $blnArrayArray))
581: $blnArray = $blnArrayArray[$strName];
582:
583: $objMethod->AddParameter(new QSoapParameter($strName, $strType, $blnArray, $objParameter->isPassedByReference()));
584: }
585:
586: array_push($this->objMethodArray, $objMethod);
587: }
588: }
589: }
590: }
591:
592: public function __construct($strClassName, $strNamespace) {
593: $this->strClassName = $strClassName;
594: $this->strNamespace = $strNamespace;
595: }
596:
597: public function __get($strName) {
598: switch ($strName) {
599: case 'ClassName': return $this->strClassName;
600: case 'Namespace': return $this->strNamespace;
601: default:
602: try {
603: return parent::__get($strName);
604: } catch (QCallerException $objExc) {
605: $objExc->IncrementOffset();
606: throw $objExc;
607: }
608: }
609: }
610: }