Current File : //opt/RZphp72/includes/CodeGen/PECL/Element/Class.php
<?php
/**
 * Class describing a PHP class within a PECL extension 
 *
 * PHP versions 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
 * the PHP License and are unable to obtain it through the web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   Tools and Utilities
 * @package    CodeGen
 * @author     Hartmut Holzgraefe <hartmut@php.net>
 * @copyright  2005-2008 Hartmut Holzgraefe
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: Class.php,v 1.31 2006/10/09 21:27:04 hholzgra Exp $
 * @link       http://pear.php.net/package/CodeGen
 */

/**
 * includes
 */
require_once "CodeGen/PECL/Element.php";
require_once "CodeGen/PECL/Element/Property.php";
require_once "CodeGen/PECL/Element/ClassConstant.php";
require_once "CodeGen/PECL/Element/Method.php";
require_once "CodeGen/PECL/Element/ObjectInterface.php";

/**
 * Class describing a PHP class within a PECL extension 
 *
 * @category   Tools and Utilities
 * @package    CodeGen
 * @author     Hartmut Holzgraefe <hartmut@php.net>
 * @copyright  2005-2008 Hartmut Holzgraefe
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: @package_version@
 * @link       http://pear.php.net/package/CodeGen
 */

class CodeGen_PECL_Element_Class
    extends CodeGen_PECL_Element 
    implements CodeGen_PECL_Element_ObjectInterface
{
    /**
     * The class name
     *
     * @var     string
     */
    protected $name  = "unknown";

    /**
     * class name setter
     *
     * @param string Classname
     */
    function setName($name) 
    {
        if (!self::isName($name)) {
            return PEAR::raiseError("'$name' is not a valid class name");
        }
            
        $this->name = $name;
            
        return true;
    }

    /**
     * class name getter
     *
     * @return string Classname
     */
    function getName()
    {
        return $this->name;
    }



    /**
     * A short description
     *
     * @var     string
     */
    protected $summary = "";

    /**
     * Description summary setter
     *
     * @param string Description summary
     */
    function setSummary($text)
    {
        $this->summary = $text;
        return true;
    }




    /**
     * A long description
     *
     * @var     string
     */
    protected $description  = "";

    /**
     * Class description setter
     *
     * @param string Class description
     */
    function setDescription($text)
    {
        $this->description = $text;
        return true;
    }


        
    /**
     * Documentation
     *
     * TODO: isn't this in Element base class already?
     *
     * @var   string
     */
    protected $documentation = "";

    /**
     * Class documentation setter
     *
     * @param string Class documentation
     */
    function setDocumentation($text) 
    {
        $this->documentation = $text;
    }


        
    /**
     * Extents which class?
     *
     * @var   string
     */
    protected $extends = "";

    /**
     * Set parent class that this class inherits from
     *
     * @param string parent class name
     */
    function setExtends($parent) 
    {
        if (!self::isName($parent)) {
            return PEAR::raiseError("'$parent' is not a valid parent class name");
        }           

        $this->extends = $parent;
    }
        

    /**
     * Implemented Interfaces
     *
     * @var   array
     */
    protected $implements = array();
        
    /**
     * Add an interface that this class implements
     *
     * @param string interface name
     */
    function addInterface($interface) 
    {
        if (!self::isName($interface)) {
            return PEAR::raiseError("'$interface' is not a valid interface name");
        }           
        
        if (isset($this->implements[$interface])) {
            return PEAR::raiseError("interface '$interface' added twice");
        }

        $this->implements[$interface] = $interface;
    }


    /**
     * Properties
     *
     * @var   array
     */
    protected $properties = array();
        
    /**
     * Add a class property
     *
     * @param object a class property object
     */
    function addProperty(CodeGen_PECL_Element_Property $property) 
    {
        $name = $property->getName();

        if (isset($this->properties[$name])) {
            return PEAR::raiseError("property '$name' already exists");
        }

        $this->properties[$name] = $property;
    }

        
    /**
     * Constants
     *
     * @var   array
     */
    protected $constants = array();
        
    /**
     * Add a constant to a class
     *
     * @param object a class constant object
     */
    function addConstant(CodeGen_PECL_Element_ClassConstant $constant) 
    {
        $name = $constant->getName();

        if (isset($this->constants[$name])) {
            return PEAR::raiseError("constant '$name' already exists");
        }

        $this->constants[$name] = $constant;
    }

        
    /**
     * Member Functions
     *
     * @var   array
     */
    protected $methods = array();
        
    /**
     * Add a method definition to the class
     *
     * @param object class method object
     */
    function addMethod(CodeGen_PECL_Element_Method $method) 
    {
        $name = $method->getName();

        if (isset($this->functions[$name])) {
            return PEAR::raiseError("method '$name' already exists");
        }

        $this->methods[$name] = $method;

        return true;
    }



    /**
     * Is this an abstract class?
     *
     * @var   bool
     */
    protected $isAbstract = false;

    /**
     * Make class abstract
     */
    function isAbstract() 
    {
        $this->isAbstract = true;
    }



    /**
     * Is this class final?
     *
     * @var   bool
     */
    protected $isFinal = false;

    /**
     * Make class final
     */
    function isFinal()
    {
        $this->isFinal = true;
    }



    /**
     * Is this an interface?
     *
     * @var   bool
     */
    protected $isInterface = false;

    /**
     * Make class an interface 
     */
    function isInterface() 
    {
        // TODO: check for already added non-abstract stuff
            
        $this->isInterface = true;
    }


    /**
     * Class payload data type  
     *
     * @var string  C type name class payload data
     */
    protected $payloadType = "";

    /**
     * Payload type setter
     * 
     * @param string
     */
    function setPayloadType($type) 
    {
        // TODO check
        $this->payloadType = $type;
    }

    /**
     * Payload type getter
     *
     * @return string
     */
    function getPayloadType() 
    {
        return $this->payloadType;
    }


    /**
     * Allocate storage space for payload data? 
     *
     * @var bool
     */
    protected $payloadAlloc = true;

    /**
     * Payload alloc setter
     * 
     * @param string
     */
    function setPayloadAlloc($alloc) 
    {
        $this->payloadAlloc = (bool)$alloc;
    }

    /**
     * Payload init code snippet
     *
     * @param string
     */
    protected $payloadCtor = "";
        
    /** 
     * Payload init code setter
     *
     * @param string code snippet
     */
    function setPayloadCtor($code)
    {
        $this->payloadCtor = $code;
    }

    /**
     * Payload init code getter
     *
     * @return string code snippet
     */
    function getPayloadCtor($extension)
    {
        $code = "";

        if ($this->payloadAlloc) {
            $code.= "    payload->data = ({$this->payloadType} *)malloc(sizeof({$this->payloadType}));\n";
        }

        $code .= $extension->codegen->varblock($this->payloadCtor);

        return $code;
    }


    /**
     * Payload dtor code snippet
     *
     * @param string
     */
    protected $payloadDtor = "";
        
    /** 
     * Payload dtor code setter
     *
     * @param string code snippet
     */
    function setPayloadDtor($code)
    {
        $this->payloadDtor = $code;
    }

    /**
     * Payload dtor code getter
     *
     * @return string code snippet
     */
    function getPayloadDtor($extension)
    {
        $code = $extension->codegen->varblock($this->payloadDtor);

        if ($this->payloadAlloc) {
            $code.= "    free(payload->data);\n";
        }

        return $code;
    }
    /**
     * Create C header entry for clas
     *
     * @access public
     * @param  class Extension  extension the function is part of
     * @return string           C header code snippet
     */
    function hCode($extension) 
    {
        $code = "";

        if ($this->payloadType) {
            $upname = strtoupper($this->name);
            echo "
typedef struct _php_obj_{$this->name} {
    zend_object obj;
    {$this->payloadType} *data;
} php_obj_{$this->name}; 
";
        }

        foreach ($this->methods as $method) {
            $code.= $method->hCode($extension);
        }

        if ($code) {
            $code = $this->ifConditionStart() . $code . $this->ifConditionEnd();
        }

        return $code;
    }


    /**
     * Generate global scope code
     *
     * @access public
     * @return string
     */
    function globalCode($extension) 
    {
        $upname = strtoupper($this->name);

        ob_start();

            
        echo "/* {{{ Class {$this->name} */\n\n";

        echo $this->ifConditionStart();

        echo "static zend_class_entry * {$this->name}_ce_ptr = NULL;\n\n";

        echo "/* {{{ Methods */\n\n";
        foreach ($this->methods as $method) {
            echo $method->cCode($extension);
            echo "\n";
        }

        echo "static zend_function_entry {$this->name}_methods[] = {\n";

        foreach ($this->methods as $method) {
            echo "    ".$method->methodEntry()."\n";
        }

        echo "    { NULL, NULL, NULL }\n";
        echo "};\n\n";
        
        echo "/* }}} Methods */\n\n";

        if ($this->payloadType) {
            echo "
static zend_object_handlers {$this->name}_obj_handlers;

static void {$this->name}_obj_free(void *object TSRMLS_DC)
{
    php_obj_{$this->name} *payload = (php_obj_{$this->name} *)object;
    
    {$this->payloadType} *data = payload->data;
".$this->getPayloadDtor($extension)."
    efree(object);
}

static zend_object_value {$this->name}_obj_create(zend_class_entry *class_type TSRMLS_DC)
{
    php_obj_{$this->name} *payload;
    zval         *tmp;
    zend_object_value retval;

    payload = (php_obj_{$this->name} *)emalloc(sizeof(php_obj_{$this->name}));
    memset(payload, 0, sizeof(php_obj_{$this->name}));
    payload->obj.ce = class_type;
".$this->getPayloadCtor($extension)."
    retval.handle = zend_objects_store_put(payload, NULL, (zend_objects_free_object_storage_t) {$this->name}_obj_free, NULL TSRMLS_CC);
    retval.handlers = &{$this->name}_obj_handlers;
    
    return retval;
}

";
        }

        echo "static void class_init_{$this->name}(void)\n{\n";

        echo "    zend_class_entry ce;\n\n";

        echo "    INIT_CLASS_ENTRY(ce, \"{$this->name}\", {$this->name}_methods);\n";

        if ($this->payloadType) {
            echo "    ce.create_object = {$this->name}_obj_create;\n";
        }

        if ($this->extends) {
            echo "    {$this->name}_ce_ptr = zend_register_internal_class_ex(&ce, {$this->extends}_ce_ptr, NULL TSRMLS_CC);\n";
        } else {
            echo "    {$this->name}_ce_ptr = zend_register_internal_class(&ce);\n";
        }

        if ($this->payloadType) {
            echo "    memcpy(&{$this->name}_obj_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));\n";
            echo "    {$this->name}_obj_handlers.clone_obj = NULL;\n";
        }

        if ($this->isFinal) {
            echo "    {$this->name}_ce_ptr->ce_flags |= ZEND_ACC_FINAL_CLASS;\n";
        }

        if ($this->isAbstract) {
            echo "    {$this->name}_ce_ptr->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS;\n";
        }

        if (!empty($this->properties)) {
            echo "\n    /* {{{ Property registration */\n\n";
            foreach ($this->properties as $property) {
                echo $property->minitCode($this->name."_ce_ptr");
            }
            echo "    /* }}} Property registration */\n\n";
        }
            
        if (!empty($this->constants)) {
            echo "\n";
            echo CodeGen_PECL_Element_ClassConstant::minitHeader();
            foreach ($this->constants as $constant) {
                echo $constant->minitCode($this->name."_ce_ptr");
            }
            echo CodeGen_PECL_Element_ClassConstant::minitFooter();
        }
            
        if (count($this->implements)) {
            ob_start();

            echo "zend_class_entry **tmp;\n";
                
            $interfaces = array();
            foreach ($this->implements as $interface) {
                echo sprintf("if (SUCCESS == zend_hash_find(CG(class_table), \"%s\", %d, (void **)&tmp)) {\n", 
                             strtolower($interface), strlen($interface) + 1);
                echo "    zend_class_implements({$this->name}_ce_ptr TSRMLS_CC, 1, *tmp);\n";
                echo "} else {\n";
                echo "    php_error(E_WARNING, \"Couldn't find interface '$interface' while setting up class '{$this->name}', skipped\");\n";
                echo "}\n";
            }

            echo $extension->codegen->varblock(ob_get_clean());
        }
                

        echo "}\n\n";

        echo $this->ifConditionEnd();

        echo "/* }}} Class {$this->name} */\n\n";

        return ob_get_clean();
    }


    /**
     * MINIT code fragment
     * 
     * @access public
     * @return string
     */
    function minitCode($extension) 
    {
        return $this->ifConditionStart() 
            . "class_init_{$this->name}();\n"
            . $this->ifConditionEnd();
    }
        

    /**
     * DocBook documentation fragment
     *
     * @access public
     * @return string
     */
    function docEntry($base) 
    {
        $xml = "";
 
        return $xml;
    }

    /**
     * Write method test cases
     *
     * @param object Extension to write tests for
     */
    function writeTests(CodeGen_PECL_Extension $extension)
    {
        foreach ($this->methods as $method) {
            $method->writeTest($extension);
        }
    }

    /**
     * Return function alias entries for all methods
     *
     */
    function functionAliasEntries()
    {
        $code = "";
        
        foreach ($this->methods as $method) {
            $code.= $method->functionAliasEntry();
        }

        if ($code) {
            $code = $this->ifConditionStart() . $code . $this->ifConditionEnd();
        }
    
        return $code;
    }
        
}

?>