Guest User

Untitled

a guest
Aug 12th, 2012
2,425
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 18.70 KB | None | 0 0
  1. <?php
  2.  
  3. class WSDLDocumentException extends Exception{}
  4.  
  5. /**
  6.  * WSDL document generator.
  7.  *
  8.  * @author  Renan de Lima <[email protected]>
  9.  * @version 0.6
  10.  */
  11. class Wsdldocument extends DOMDocument
  12. {
  13.     /**#@+
  14.      * @var string
  15.      */
  16.     const BINDING = 'http://schemas.xmlsoap.org/soap/http';
  17.     const NS_SOAP_ENC = 'http://schemas.xmlsoap.org/soap/encoding/';
  18.     const NS_SOAP_ENV = 'http://schemas.xmlsoap.org/wsdl/soap/';
  19.     const NS_WSDL = 'http://schemas.xmlsoap.org/wsdl/';
  20.     const NS_XML = 'http://www.w3.org/2000/xmlns/';
  21.     const NS_XSD = 'http://www.w3.org/2001/XMLSchema';
  22.     /**#@-*/
  23.  
  24.     /**
  25.      * List of types already created or in creating proccess. It avoids
  26.      * recursion when creating complex types that reference themselves. See
  27.      * {@link WSDLDocument::createComplexType()} and
  28.      * {@link WSDLDocument::createArrayType()} operations for more details.
  29.      *
  30.      * @var string[]
  31.      */
  32.     protected $aCreatedTypes = array();
  33.  
  34.     /**
  35.      * @var DOMElement
  36.      */
  37.     protected $oBinding;
  38.  
  39.     /**
  40.      * @var ReflectionClass
  41.      */
  42.     protected $oClass;
  43.  
  44.     /**
  45.      * @var DOMElement
  46.      */
  47.     protected $oDefinitions;
  48.  
  49.     /**
  50.      * @var DOMElement
  51.      */
  52.     protected $oPortType;
  53.  
  54.     /**
  55.      * @var DOMElement
  56.      */
  57.     protected $oSchema;
  58.  
  59.     /**
  60.      * @var string
  61.      */
  62.     protected $sTns;
  63.  
  64.     /**
  65.      * @var string
  66.      */
  67.     protected $sUrl;
  68.  
  69.     /**
  70.      * Set webservice and url class and target name space.
  71.      *
  72.      * @param  string
  73.      * @param  string
  74.      * @param  string
  75.      * @return void
  76.      */
  77.     public function __construct($sClass, $sUrl = null, $sTns = null)
  78.     {
  79.         parent::__construct('1.0', 'utf-8');
  80.         // set class, url and target namespace
  81.         $this->oClass = new ReflectionClass($sClass);
  82.         $this->sUrl = empty($sUrl) == true ? $this->getDefaultUrl() : $sUrl;
  83.         $this->sTns = empty($sTns) == true ? $_SERVER['SERVER_NAME'] : $sTns;
  84.         // create document
  85.         $this->run();
  86.     }
  87.  
  88.     /**
  89.      * Create the WSDL definitions.
  90.      *
  91.      * @return void
  92.      */
  93.     protected function run()
  94.     {
  95.         // create root, schema type, port type and binding tags
  96.         $this->createMajorElements();
  97.         // add methods
  98.         foreach ($this->oClass->getMethods() as $oMethod) {
  99.             // check if method is allowed
  100.             if (
  101.                 $oMethod->isPublic() == true && // it must be public and...
  102.                 $oMethod->isStatic() == false && // non static
  103.                 $oMethod->isConstructor() == false && // non constructor
  104.                 substr($oMethod->name, 0, 2) != '__' // non magic methods
  105.             ) {
  106.                 // attach operation
  107.                 $this->createPortTypeOperation($oMethod);
  108.                 $this->createBindingOperation($oMethod);
  109.                 $this->createMessage($oMethod);
  110.             }
  111.         }
  112.         // append port type and binding
  113.         $this->oDefinitions->appendChild($this->oPortType);
  114.         $this->oDefinitions->appendChild($this->oBinding);
  115.         // service
  116.         $this->createService();
  117.     }
  118.  
  119.     /**
  120.      * Create array type once. It doesn't create a type, you have to call
  121.      * {@link WSDLDocument::createType()} to this. Returns the array type name.
  122.      *
  123.      * @param  string
  124.      * @return string
  125.      */
  126.     protected function createArrayType($sType)
  127.     {
  128.         // check if it was created
  129.         $sName = $sType . 'Array';
  130.         if (array_key_exists($sType, $this->aCreatedTypes) == true) {
  131.             return $sName;
  132.         }
  133.         // avoid recursion
  134.         $this->aCreatedTypes[$sType] = true;
  135.         // create tags
  136.         $oType = $this->createElementNS(self::NS_XSD, 'complexType');
  137.         $this->oSchema->appendChild($oType);
  138.         $oContent = $this->createElementNS(self::NS_XSD, 'complexContent');
  139.         $oType->appendChild($oContent);
  140.         $oRestriction = $this->createElementNS(self::NS_XSD, 'restriction');
  141.         $oContent->appendChild($oRestriction);
  142.         $oAttribute = $this->createElementNS(self::NS_XSD, 'attribute');
  143.         $oRestriction->appendChild($oAttribute);
  144.         // configure tags
  145.         $oType->setAttribute('name', $sName);
  146.         $oRestriction->setAttribute('base', 'soap-enc:Array');
  147.         $oAttribute->setAttribute('ref', 'soap-enc:arrayType');
  148.         // build name
  149.         $sNamespace = $this->getTypeNamespace($sType);
  150.         $sArrayType = $sNamespace . ':' . $sType . '[]';
  151.         $oAttribute->setAttributeNS(self::NS_WSDL, 'arrayType', $sArrayType);
  152.         return $sName;
  153.     }
  154.  
  155.     /**
  156.      * Create a binding operation tag.
  157.      *
  158.      * @param  ReflectionMethod
  159.      * @return void
  160.      */
  161.     protected function createBindingOperation(ReflectionMethod $oMethod)
  162.     {
  163.         $oOperation = $this->createElementNS(self::NS_WSDL, 'operation');
  164.         $oOperation->setAttribute('name', $oMethod->name);
  165.         $this->oBinding->appendChild($oOperation);
  166.         // binding operation soap
  167.         $oOperationSoap = $this->createElementNS(self::NS_SOAP_ENV, 'operation');
  168.         $sSeparator = parse_url($this->sUrl, PHP_URL_QUERY) == '' ? '?' : '&';
  169.         $sActionUrl = $this->sUrl . $sSeparator . 'method=' . $oMethod->name;
  170.         $oOperationSoap->setAttribute('soapAction', $sActionUrl);
  171.         $oOperationSoap->setAttribute('style', 'rpc');
  172.         $oOperation->appendChild($oOperationSoap);
  173.         // binding input and output
  174.         foreach (array('input', 'output') as $sTag) {
  175.             $oBindingTag = $this->createElementNS(self::NS_WSDL, $sTag);
  176.             $oOperation->appendChild($oBindingTag);
  177.             $oBody = $this->createElementNS(self::NS_SOAP_ENV, 'body');
  178.             $oBody->setAttribute('use', 'encoded');
  179.             $oBody->setAttribute('encodingStyle', self::NS_SOAP_ENC);
  180.             $oBindingTag->appendChild($oBody);
  181.         }
  182.     }
  183.  
  184.     /**
  185.      * Create a complex type once. It doesn't create a type, you have to call
  186.      * {@link WSDLDocument::createType()} to this.
  187.      *
  188.      * @return void
  189.      */
  190.     protected function createComplexType($sClass)
  191.     {
  192.         // check if it was created
  193.         if (array_key_exists($sClass, $this->aCreatedTypes) == true) {
  194.             return;
  195.         }
  196.         // avoid recursion
  197.         $this->aCreatedTypes[$sClass] = true;
  198.         // start type creation
  199.         $oComplex = $this->createElementNS(self::NS_XSD, 'complexType');
  200.         $this->oSchema->appendChild($oComplex);
  201.         $oComplex->setAttribute('name', $sClass);
  202.         $oAll = $this->createElementNS(self::NS_XSD, 'all');
  203.         $oComplex->appendChild($oAll);
  204.         // create attributes
  205.         $oReflection = new ReflectionClass($sClass);
  206.         $aProperty = $oReflection->getProperties(ReflectionProperty::IS_PUBLIC);
  207.         foreach ($oReflection->getProperties() as $oProperty) {
  208.             // check if property is allowed
  209.             if (
  210.                 $oProperty->isPublic() == true && // it must be public and...
  211.                 $oProperty->isStatic() == false // non static
  212.             ) {
  213.                 // create type for each element
  214.                 $sComment = $oProperty->getDocComment();
  215.                 $sType = reset($this->getTagComment($sComment, 'var'));
  216.                 $sPropertyTypeId = $this->createType($sType);
  217.                 // create element of property
  218.                 $oElement = $this->createElementNS(self::NS_XSD, 'element');
  219.                 $oAll->appendChild($oElement);
  220.                 $oElement->setAttribute('name', $oProperty->name);
  221.                 $oElement->setAttribute('type', $sPropertyTypeId);
  222.                 $oElement->setAttribute('minOccurs', 0);
  223.                 $oElement->setAttribute('maxOccurs', 1);
  224.             }
  225.         }
  226.     }
  227.  
  228.     /**
  229.      * Create a message with operation arguments and return.
  230.      *
  231.      * @param  ReflectionMethod
  232.      * @return void
  233.      */
  234.     protected function createMessage(ReflectionMethod $oMethod)
  235.     {
  236.         $sComment = $oMethod->getDocComment();
  237.         // message input
  238.         $oInput = $this->createElementNS(self::NS_WSDL, 'message');
  239.         $oInput->setAttribute('name', $oMethod->name . 'Request');
  240.         $this->oDefinitions->appendChild($oInput);
  241.         // input part
  242.         $aType = $this->getTagComment($sComment, 'param');
  243.         $aParameter = $oMethod->getParameters();
  244.         if (count($aType) != count($aParameter)) {
  245.             $sMethodName = $this->oClass->getName() . '::' . $oMethod->getName() . '()';
  246.             throw new WSDLDocumentException(
  247.                 'Declared and documented arguments do not match in ' . $sMethodName);
  248.         }
  249.         foreach ($aType as $iKey => $sType) {
  250.             $oPart = $this->createElementNS(self::NS_WSDL, 'part');
  251.             $oPart->setAttribute('name', $aParameter[$iKey]->name);
  252.             $oPart->setAttribute('type', $this->createType($sType));
  253.             $oInput->appendChild($oPart);
  254.         }
  255.         // message output
  256.         $oOutput = $this->createElementNS(self::NS_WSDL, 'message');
  257.         $oOutput->setAttribute('name', $oMethod->name . 'Response');
  258.         $this->oDefinitions->appendChild($oOutput);
  259.         // output part
  260.         $sType = (string) reset($this->getTagComment($sComment, 'return'));
  261.         if ($sType != 'void' && $sType != '') {
  262.             $oPart = $this->createElementNS(self::NS_WSDL, 'part');
  263.             $oPart->setAttribute('name', $oMethod->name . 'Return');
  264.             $oPart->setAttribute('type', $this->createType($sType));
  265.             $oOutput->appendChild($oPart);
  266.         }
  267.     }
  268.  
  269.     /**
  270.      * Create a port type operation tag.
  271.      *
  272.      * @param  ReflectionMethod
  273.      * @return void
  274.      */
  275.     protected function createPortTypeOperation(ReflectionMethod $oMethod)
  276.     {
  277.         $oOperation = $this->createElementNS(self::NS_WSDL, 'operation');
  278.         $oOperation->setAttribute('name', $oMethod->name);
  279.         $this->oPortType->appendChild($oOperation);
  280.         // documentation
  281.         $sDoc = $this->getDocComment($oMethod->getDocComment());
  282.         $oDoc = $this->createElementNS(self::NS_WSDL, 'documentation', $sDoc);
  283.         $oOperation->appendChild($oDoc);
  284.         // input
  285.         $oInput = $this->createElementNS(self::NS_WSDL, 'input');
  286.         $oInput->setAttribute('message', 'tns:' . $oMethod->name . 'Request');
  287.         $oOperation->appendChild($oInput);
  288.         // output
  289.         $oOutput = $this->createElementNS(self::NS_WSDL, 'output');
  290.         $oOutput->setAttribute('message', 'tns:' . $oMethod->name . 'Response');
  291.         $oOperation->appendChild($oOutput);
  292.     }
  293.  
  294.     /**
  295.      * Create service tag.
  296.      *
  297.      * @return void
  298.      */
  299.     protected function createService()
  300.     {
  301.         $oService = $this->createElementNS(self::NS_WSDL, 'service');
  302.         $oService->setAttribute('name', $this->oClass->name);
  303.         $this->oDefinitions->appendChild($oService);
  304.         // documentation
  305.         $sDoc = $this->getDocComment($this->oClass->getDocComment());
  306.         $oDoc = $this->createElementNS(self::NS_WSDL, 'documentation', $sDoc);
  307.         $oService->appendChild($oDoc);
  308.         // port
  309.         $oPort = $this->createElementNS(self::NS_WSDL, 'port');
  310.         $oPort->setAttribute('name', $this->oClass->name . 'Port');
  311.         $oPort->setAttribute('binding', 'tns:' . $this->oClass->name . 'Binding');
  312.         $oService->appendChild($oPort);
  313.         // address
  314.         $oAddress = $this->createElementNS(self::NS_SOAP_ENV, 'address');
  315.         $oAddress->setAttribute('location', $this->sUrl);
  316.         $oPort->appendChild($oAddress);
  317.     }
  318.  
  319.     /**
  320.      * Create a type in document. Receive the raw type name as it was on
  321.      * programmer's documentation. It returns wsdl name type.
  322.      *
  323.      * @param  string
  324.      * @return string
  325.      */
  326.     protected function createType($sType)
  327.     {
  328.         // check if is array
  329.         $sType = trim((string) $sType);
  330.         if ($sType == '') {
  331.             throw new WSDLDocumentException('Invalid type.');
  332.         }
  333.         // check if it's array and its depth
  334.         $iArrayDepth = 0;
  335.         while (substr($sType, -2) == '[]') {
  336.             $iArrayDepth++;
  337.             $sType = substr($sType, 0, -2);
  338.         }
  339.         // create complex type if necessary
  340.         $sType      = $this->getType($sType);
  341.         $sNamespace = $this->getTypeNamespace($sType);
  342.         if ($sNamespace == 'tns') {
  343.             $this->createComplexType($sType);
  344.         }
  345.         // create the arrays concerned depth
  346.         if ($iArrayDepth > 0) {
  347.             // force namespace to complex type, because it's an array
  348.             $sNamespace = 'tns';
  349.             for (; $iArrayDepth > 0; $iArrayDepth--) {
  350.                 $sType = $this->createArrayType($sType);
  351.             }
  352.         }
  353.         // wsdl type name
  354.         return $sNamespace . ':' . $sType;
  355.     }
  356.  
  357.     /**
  358.      * Generate default URL for SOAP requests.
  359.      *
  360.      * @return string
  361.      */
  362.     protected function getDefaultUrl()
  363.     {
  364.         // protocol
  365.         $sProtocol = $this->getServer('HTTPS') == 'on' ? 'https' : 'http';
  366.         // host and port
  367.         $sHost = $this->getServer('HTTP_HOST');
  368.         if ($sHost == '') {
  369.             $sHost = $this->getServer('SERVER_NAME');
  370.             $sPort = $this->getServer('SERVER_PORT');
  371.             if ($sPort != '') {
  372.                 $sHost .= ':' . $sPort;
  373.             }
  374.         }
  375.         // uri
  376.         $sUri = $this->getServer('HTTP_X_REWRITE_URL');
  377.         if ($sUri == '') {
  378.             $sUri = $this->getServer('REQUEST_URI');
  379.             if ($sUri == '') {
  380.                 $sUri = $this->getServer('ORIG_PATH_INFO');
  381.                 if ($sUri == '') {
  382.                     $sUri = $this->getServer('SCRIPT_NAME');
  383.                 }
  384.             }
  385.         }
  386.         if (($iLast = strpos($sUri, '?')) !== false) {
  387.             $sUri = substr($sUri, 0, $iLast);
  388.         }
  389.         // return everything gathered
  390.         return $sProtocol . '://' . $sHost . $sUri;
  391.     }
  392.  
  393.     /**
  394.      * @param  string
  395.      * @return string
  396.      */
  397.     protected function getServer($sKey)
  398.     {
  399.         return isset($_SERVER[$sKey]) ? $_SERVER[$sKey] : '';
  400.     }
  401.  
  402.     /**
  403.      * Fetch documentation in a comment.
  404.      *
  405.      * @param  string
  406.      * @return string
  407.      */
  408.     protected function getDocComment($sComment)
  409.     {
  410.         $sValue = '';
  411.         foreach (explode("\n",  $sComment) as $sLine) {
  412.             $sLine = trim($sLine, " *\t\r/");
  413.             if (strlen($sLine) > 0 && $sLine{0} == '@') {
  414.                 break;
  415.             }
  416.             $sValue .= ' ' . $sLine;
  417.         }
  418.         return trim($sValue);
  419.     }
  420.  
  421.     /**
  422.      * Fetch tag's values from a comment.
  423.      *
  424.      * @param  string
  425.      * @param  string
  426.      * @return string[]
  427.      */
  428.     protected function getTagComment($sComment, $sTagName)
  429.     {
  430.         $aValue = array();
  431.         foreach (explode("\n",  $sComment) as $sLine) {
  432.             $sPattern = "/^\*\s+@(.[^\s]+)\s+(.[^\s]+)/";
  433.             $sText = trim($sLine);
  434.             $aMatch = array();
  435.             preg_match($sPattern, $sText, $aMatch);
  436.             if (count($aMatch) > 2 && $aMatch[1] == $sTagName) {
  437.                 array_push($aValue, $aMatch[2]);
  438.             }
  439.         }
  440.         return $aValue;
  441.     }
  442.  
  443.     /**
  444.      * Create basics tags. Definitions (root), schema (contain types), port type
  445.      * and binding.
  446.      *
  447.      * @return void
  448.      */
  449.     protected function createMajorElements()
  450.     {
  451.         // >> definitions
  452.         $this->oDefinitions = $this->createElementNS(self::NS_WSDL, 'wsdl:definitions');
  453.         $this->oDefinitions->setAttributeNS(self::NS_XML, 'xmlns:soap-enc', self::NS_SOAP_ENC);
  454.         $this->oDefinitions->setAttributeNS(self::NS_XML, 'xmlns:soap-env', self::NS_SOAP_ENV);
  455.         $this->oDefinitions->setAttributeNS(self::NS_XML, 'xmlns:tns', $this->sTns);
  456.         $this->oDefinitions->setAttributeNS(self::NS_XML, 'xmlns:wsdl', self::NS_WSDL);
  457.         $this->oDefinitions->setAttributeNS(self::NS_XML, 'xmlns:xsd', self::NS_XSD);
  458.         $this->oDefinitions->setAttribute('targetNamespace', $this->sTns);
  459.         $this->appendChild($this->oDefinitions);
  460.         // >> definitions >> types
  461.         $oTypes = $this->createElementNS(self::NS_WSDL, 'types');
  462.         $this->oDefinitions->appendChild($oTypes);
  463.         // >> definitions >> types >> schema
  464.         $this->oSchema = $this->createElementNS(self::NS_XSD, 'schema');
  465.         $this->oSchema->setAttribute('targetNamespace', $this->sTns);
  466.         $oTypes->appendChild($this->oSchema);
  467.         // >> definitions >> types >>port type
  468.         $this->oPortType = $this->createElementNS(self::NS_WSDL, 'portType');
  469.         $this->oPortType->setAttribute('name', $this->oClass->name . 'PortType');
  470.         // >> definitions >> types >> binding
  471.         $this->oBinding = $this->createElementNS(self::NS_WSDL, 'binding');
  472.         $this->oBinding->setAttribute('name', $this->oClass->name . 'Binding');
  473.         $this->oBinding->setAttribute('type', 'tns:' . $this->oClass->name . 'PortType');
  474.         // >> definitions >> types >> binding >> soap binding
  475.         $oBindingSoap = $this->createElementNS(self::NS_SOAP_ENV, 'binding');
  476.         $oBindingSoap->setAttribute('style', 'rpc');
  477.         $oBindingSoap->setAttribute('transport', self::BINDING);
  478.         $this->oBinding->appendChild($oBindingSoap);
  479.     }
  480.  
  481.     /**
  482.      * Get type name.
  483.      *
  484.      * @param  string
  485.      * @return string
  486.      */
  487.     protected function getType($sType)
  488.     {
  489.         switch ($sType) {
  490.             case 'array':
  491.             case 'struct':
  492.                 return 'array';
  493.             case 'boolean':
  494.             case 'bool':
  495.                 return 'boolean';
  496.             case 'double':
  497.             case 'float':
  498.             case 'real':
  499.                 return 'float';
  500.             case 'integer':
  501.             case 'int':
  502.                 return 'int';
  503.             case 'string':
  504.             case 'str':
  505.                 return 'string';
  506.             default:
  507.                 return $sType;
  508.         }
  509.     }
  510.  
  511.     /**
  512.      * Get type namespace.
  513.      *
  514.      * @param  string
  515.      * @return string
  516.      */
  517.     protected function getTypeNamespace($sType)
  518.     {
  519.         switch ($sType) {
  520.             case 'array':
  521.             case 'struct':
  522.                 return 'soap-enc';
  523.             case 'boolean':
  524.             case 'bool':
  525.             case 'double':
  526.             case 'float':
  527.             case 'real':
  528.             case 'integer':
  529.             case 'int':
  530.             case 'string':
  531.             case 'str':
  532.                 return 'xsd';
  533.             // complex type, everything else is native type
  534.             default:
  535.                 return 'tns';
  536.         }
  537.     }
  538. }
Advertisement
Add Comment
Please, Sign In to add comment