Subversion-Projekte lars-tiefland.prado

Revision

Blame | Letzte Änderung | Log anzeigen | RSS feed

<?php
/**
 * WsdlGenerator file.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the BSD License.
 *
 * Copyright(c) 2005 by Marcus Nyeholt. All rights reserved.
 *
 * To contact the author write to {@link mailto:tanus@users.sourceforge.net Marcus Nyeholt}
 * This file is part of the PRADO framework from {@link http://www.xisc.com}
 *
 * @author Marcus Nyeholt               <tanus@users.sourceforge.net>
 * @version $Id: WsdlGenerator.php 2393 2008-03-07 17:12:19Z xue $
 * @package System.Web.Services.SOAP
 */

require_once(dirname(__FILE__).'/Wsdl.php');
require_once(dirname(__FILE__).'/WsdlMessage.php');
require_once(dirname(__FILE__).'/WsdlOperation.php');

/**
 * Generator for the wsdl.
 * Special thanks to Cristian Losada for implementing the Complex Types section of the WSDL.
 * @author              Marcus Nyeholt          <tanus@users.sourceforge.net>
 * @author              Cristian Losada         <cristian@teaxul.com>
 * @version     $Revision$
 */
class WsdlGenerator
{
        /**
         * The instance.
         * var          WsdlGenerator
         */
        private static $instance;

        /**
         * The name of this service (the classname)
         * @var         string
         */
        private $serviceName = '';

        /**
         * The complex types to use in the wsdl
         * @var         Array
         */
        private $types = array();

        /**
         * The operations available in this wsdl
         * @var         ArrayObject
         */
        private $operations;

        /**
         * The wsdl object.
         * @var         object
         */
        private $wsdlDocument;

        /**
         * The actual wsdl string
         * @var         string
         */
        private $wsdl = '';

        /**
         * The singleton instance for the generator
         */
        public static function getInstance()
        {
                if (is_null(self::$instance)) {
                        self::$instance = new WsdlGenerator();
                }
                return self::$instance;
        }

        /**
         * Get the Wsdl generated
         * @return      string          The Wsdl for this wsdl
         */
        public function getWsdl()
        {
                return $this->wsdl;
        }

        /**
         * Generates WSDL for a passed in class, and saves it in the current object. The
         * WSDL can then be retrieved by calling
         * @param       string          $className              The name of the class to generate for
         * @param       string          $serviceUri             The URI of the service that handles this WSDL
         * @param string $encoding character encoding.
         * @return      void
         */
        public function generateWsdl($className, $serviceUri='',$encoding='')
        {
                $this->wsdlDocument = new Wsdl($className, $serviceUri, $encoding);

                $classReflect = new ReflectionClass($className);
                $methods = $classReflect->getMethods();

                foreach ($methods as $method) {
                        // Only process public methods
                        if ($method->isPublic()) {
                                $this->processMethod($method);
                        }
                }

                foreach($this->types as $type => $elements) {
                        $this->wsdlDocument->addComplexType($type, $elements);
                }

                $this->wsdl = $this->wsdlDocument->getWsdl();
        }

        /**
         * Static method that generates and outputs the generated wsdl
         * @param               string          $className              The name of the class to export
         * @param               string          $serviceUri             The URI of the service that handles this WSDL
         * @param string $encoding character encoding.
         */
        public static function generate($className, $serviceUri='', $encoding='')
        {
                $generator = WsdlGenerator::getInstance();
                $generator->generateWsdl($className, $serviceUri,$encoding);
                //header('Content-type: text/xml');
                return $generator->getWsdl();
                //exit();

        }

        /**
         * Process a method found in the passed in class.
         * @param               ReflectionMethod                $method         The method to process
         */
        protected function processMethod(ReflectionMethod $method)
        {
                $comment = $method->getDocComment();
                if (strpos($comment, '@soapmethod') === false) {
                        return;
                }
                $comment = preg_replace("/(^[\\s]*\\/\\*\\*)
                                 |(^[\\s]\\*\\/)
                                 |(^[\\s]*\\*?\\s)
                                 |(^[\\s]*)
                                 |(^[\\t]*)/ixm", "", $comment);

            $comment = str_replace("\r", "", $comment);
            $comment = preg_replace("/([\\t])+/", "\t", $comment);
            $commentLines = explode("\n", $comment);

                $methodDoc = '';
                $params = array();
                $return = array();
                $gotDesc = false;
                $gotParams = false;

                foreach ($commentLines as $line) {
                        if ($line == '') continue;
                        if ($line{0} == '@') {
                                $gotDesc = true;
                                if (preg_match('/^@param\s+([\w\[\]()]+)\s+\$([\w()]+)\s*(.*)/i', $line, $match)) {
                                        $param = array();
                                        $param['type'] = $this->convertType($match[1]);
                                        $param['name'] = $match[2];
                                        $param['desc'] = $match[3];
                                        $params[] = $param;
                                }
                                else if (preg_match('/^@return\s+([\w\[\]()]+)\s*(.*)/i', $line, $match)) {
                                        $gotParams = true;
                                        $return['type'] = $this->convertType($match[1]);
                                        $return['desc'] = $match[2];
                                        $return['name'] = 'return';
                                }
                        }
                        else {
                                if (!$gotDesc) {
                                        $methodDoc .= trim($line);
                                }
                                else if (!$gotParams) {
                                        $params[count($params)-1]['desc'] .= trim($line);
                                }
                                else {
                                        if ($line == '*/') continue;
                                        $return['desc'] .= trim($line);
                                }
                        }
                }

                $methodName = $method->getName();
                $operation = new WsdlOperation($methodName, $methodDoc);

                $operation->setInputMessage(new WsdlMessage($methodName.'Request', $params));
                $operation->setOutputMessage(new WsdlMessage($methodName.'Response', array($return)));

                $this->wsdlDocument->addOperation($operation);

        }

        /**
         * Converts from a PHP type into a WSDL type. This is borrowed from
         * Cerebral Cortex (let me know and I'll remove asap).
         *
         * TODO: date and dateTime
         * @param               string          $type           The php type to convert
         * @return              string                                  The XSD type.
         */
        private function convertType($type)
        {
                 switch ($type) {
             case 'string':
             case 'str':
                 return 'xsd:string';
                 break;
             case 'int':
             case 'integer':
                 return 'xsd:int';
                 break;
             case 'float':
             case 'double':
                 return 'xsd:float';
                 break;
             case 'boolean':
             case 'bool':
                 return 'xsd:boolean';
                 break;
             case 'date':
                 return 'xsd:date';
                 break;
             case 'time':
                 return 'xsd:time';
                 break;
             case 'dateTime':
                 return 'xsd:dateTime';
                 break;
             case 'array':
                 return 'soap-enc:Array';
                 break;
             case 'object':
                 return 'xsd:struct';
                 break;
             case 'mixed':
                 return 'xsd:anyType';
                 break;
             case 'void':
                 return '';
             default:
                 if(strpos($type, '[]'))  // if it is an array
                 {
                        $className = substr($type, 0, strlen($type) - 2);
                        $type = $className . 'Array';
                        $this->types[$type] = '';
                        $this->convertType($className);
                 }
                 else
                 {
                         if(!isset($this->types[$type]))
                                $this->extractClassProperties($type);
                 }
                 return 'tns:' . $type;
         }
        }

        /**
         * Extract the type and the name of all properties of the $className class and saves it in the $types array
         * This method extract properties from PHPDoc formatted comments for variables. Unfortunately the reflectionproperty
         * class doesn't have a getDocComment method to extract comments about it, so we have to extract the information
         * about the variables manually. Thanks heaps to Cristian Losada for implementing this.
         * @param string $className The name of the class
         */
        private function extractClassProperties($className)
        {
                /**
                 * modified by Qiang Xue, Jan. 2, 2007
                 * Using Reflection's DocComment to obtain property definitions
                 * DocComment is available since PHP 5.1
                 */
                $reflection = new ReflectionClass($className);
                $properties = $reflection->getProperties();
                foreach($properties as $property)
                {
                        $comment = $property->getDocComment();
                        if(strpos($comment, '@soapproperty') !== false)
                        {
                                if (preg_match('/@var\s+(\w+(\[\])?)\s+\$(\w+)/mi', $comment, $match)) {
                                        $param = array();
                                        $param['type'] = $this->convertType($match[1]);
                                        $param['name'] = $match[3];
                                        $this->types[$className][] = $param;
                                }
                        }
                }
        }
}
?>