* @copyright 2002-2006 Gregory Beaver * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @version CVS: $Id: ParserElements.inc 248547 2007-12-19 02:16:49Z ashnazg $ * @link http://www.phpdoc.org * @link http://pear.php.net/PhpDocumentor * @see Parser, WordParser * @since 1.1 */ /** * all elements except {@link parserPackagePage} descend from this abstract class * @abstract * @package phpDocumentor * @subpackage ParserElements * @author Greg Beaver * @since 1.0rc1 * @version $Id: ParserElements.inc 248547 2007-12-19 02:16:49Z ashnazg $ */ class parserElement extends parserBase { /** * @var mixed either false or a {@link parserDocBlock} */ var $docblock = false; /** * name of this element, or include type if element is a {@link parserInclude} */ var $name; /** * @var mixed either false or an array of paths to files with conflicts */ var $conflicts = false; /** * location of this element (filename) * @var string */ var $file = ''; /** * full path location of this element (filename) * @var string */ var $path = ''; /** * line number on file where this element stops * @since 1.2 * @var false|integer */ var $endlinenumber = 0; /** * Line number in the source on which this element appears * @since 1.2 * @var false|integer */ var $linenumber = false; /** * @param parserDocBlock */ function setDocBlock($docblock) { $this->docblock = $docblock; } /** * @param string */ function setName($name) { $this->name = trim($name); } /** * Set starting line number * @param integer */ function setLineNumber($number) { $this->linenumber = $number; } /** * Sets the ending line number of elements * @param integer */ function setEndLineNumber($l) { $this->endlinenumber = $l; } /** * @return integer */ function getLineNumber() { return $this->linenumber; } /** * @return integer */ function getEndLineNumber() { return $this->endlinenumber; } /** @return string package containing this element */ function getPackage() { if ($this->docblock) { return $this->docblock->package; } else return $GLOBALS['phpDocumentor_DefaultPackageName']; } /** @param string */ function setFile($file) { $this->file = $file; } /** @param string */ function setPath($file) { // look for special windows case if(SMART_PATH_DELIMITER === '\\') $this->path = strtr($file,'/','\\'); else $this->path = $file; } /** * @return string */ function getName() { if (!isset($this->name)) return false; return $this->name; } /** * @return string */ function getFile() { if (!isset($this->file)) return false; return $this->file; } /** * @return string */ function getPath() { if (!isset($this->path)) return false; return $this->path; } } /** * @package phpDocumentor * @subpackage ParserElements * @author Greg Beaver * @since 1.0rc1 * @version $Id: ParserElements.inc 248547 2007-12-19 02:16:49Z ashnazg $ */ class parserInclude extends parserElement { /** * Type is used by many functions to skip the hassle of if phpDocumentor_get_class($blah) == 'parserBlah' * @var string always 'include' */ var $type = 'include'; } /** * @package phpDocumentor * @subpackage ParserElements * @author Greg Beaver * @since 1.1 * @version $Id: ParserElements.inc 248547 2007-12-19 02:16:49Z ashnazg $ */ class parserGlobal extends parserElement { /** * Type is used by many functions to skip the hassle of if phpDocumentor_get_class($blah) == 'parserBlah' * @var string always 'global' */ var $type = 'global'; /** * Name of the global's data type * @var string */ var $datatype = 'mixed'; /** * quick way to link to this element * @return mixed converter-specific link to this global variable * @param Converter * @param string text to display for the link or false for default text */ function getLink(&$c, $text = false, $returnobj = false) { if ($returnobj) { return $c->getLink('global ' . $this->name, $this->docblock->package); } return $c->getGlobalLink($this->name, $this->docblock->package, $this->path, $text); } /** * Returns all global variables in other packages that have the same name as this global variable * @return mixed false or an array Format: (package => {@link parserGlobal} of conflicting global variable) * @param Converter */ function getConflicts(&$c) { $a = $c->proceduralpages->getGlobalConflicts($this->name); unset($a[$this->docblock->package]); return $a; } /** * Sets the name of the global variable's type * @param string */ function setDataType($type) { $this->datatype = $type; } /** * Retrieve converter-specific representation of the data type * * If the data type is a documented class name, then this function will * return a Converter-specific link to that class's documentation, so users * can click/browse to the documentation directly from the global variable * declaration * @return string * @param Converter */ function getDataType(&$converter) { $converted_datatype = $this->datatype; if (strpos($this->datatype,'|')) { $my_types = ''; $types = explode('|',$this->datatype); foreach($types as $returntype) { $a = $converter->getLink($returntype); if (is_object($a) && phpDocumentor_get_class($a) == 'classlink') { if (!empty($my_types)) $my_types .= '|'; $my_types .= $converter->returnSee($a,$converter->type_adjust($returntype)); } else { if (!empty($my_types)) $my_types .= '|'; $my_types .= $converter->type_adjust($returntype); } } $converted_datatype = $my_types; } else { $a = $converter->getLink($this->datatype); if (is_object($a) && phpDocumentor_get_class($a) == 'classlink') { $converted_datatype = $converter->returnSee($a,$converter->type_adjust($this->datatype)); } else { $converted_dataype = $converter->type_adjust($this->datatype); } } return $converted_datatype; } } /** * @package phpDocumentor * @subpackage ParserElements * @author Greg Beaver * @since 1.0rc1 * @version $Id: ParserElements.inc 248547 2007-12-19 02:16:49Z ashnazg $ */ class parserFunction extends parserElement { /** * Type is used by many functions to skip the hassle of if phpDocumentor_get_class($blah) == 'parserBlah' * @var string always 'function' */ var $type = 'function'; /** * parameters parsed from function definition. * * param name may be null, in which case, updateParams() must be called from the Converter * @var array Format: array(param name => default value parsed from function definition) * @see updateParams() */ var $params = false; /** * Function returns a reference to an element, instead of a value * * set to true if function is declared as: * * function &func(... * * @var boolean */ var $returnsreference = false; /** * global declarations parsed from function definition * * @var array Format: array(globalname1, globalname2,....) */ var $globals = false; /** * static variable declarations parsed from function definition * @var array Format: array(array('name' => staticvar1,'val' => '' or default val of staticvar1),...) */ var $statics = false; var $source = ''; /** * @param string * @param string default value parsed from function definition * @param boolean indicates whether this parameter has a default value * @param null|string class type hint */ function addParam($name, $value, $has_default = true, $typehint = null) { $this->params[$name] = array($value, $has_default); if (isset($typehint)) { $this->params[$name][2] = $typehint; } } /** * Set the source code. Always array in PHP 4.3.0+ * @param string|array */ function addSource($source) { $this->source = $source; } /** * Determine whether the source code has been requested via {@}source} * @return boolean */ function hasSource() { if (is_array($this->source)) return true; return strlen($this->source); } /** * @return string|array source code ready for highlighting */ function getSource() { return $this->source; } /** * quick way to link to this element * @return mixed converter-specific link to this function * @param Converter * @param string text to display for the link or false for default text */ function getLink($c, $text = false, $returnobj = false) { if ($returnobj) { return $c->getLink('function ' . $this->name, $this->docblock->package); } return $c->getFunctionLink($this->name, $this->docblock->package, $this->path, $text); } /** * Returns all functions in other packages that have the same name as this function * @return mixed false or an array Format: (package => {@link parserFunction} of conflicting functions) * @param Converter */ function getConflicts(&$c) { $a = $c->proceduralpages->getFuncConflicts($this->name); unset($a[$this->docblock->package]); return $a; } /** * Add all "global $var, $var2" declarations to this function * @param array $globals Format: array(globalname1, globalname2,....) */ function addGlobals($globals) { $this->globals = $globals; } /** * Add all "static $var, $var2 = 6" declarations to this function * @param array Format: array(varname1, varname2,...) * @param array Format: array(default val of var 1, default val of var 2,...) if var 1 has no default, array(default val of var 2,...) */ function addStatics($static,$vals) { if (count($static)) { $this->statics = array(); for($i=0;$istatics[] = array('name' => $static[$i],'val' => $a); } } } } /** * @return string default value of param $name * @param string */ function getParam ($name) { if (!isset($this->params[$name])) return false; $test = $this->params[$name]; if ($test[1]) { return $this->params[$name]; } else { return false; } } /** * @return array format: array(array(paramname, default value),...) */ function listParams () { if (isset($this->params)) { $ret = array(); if ($this->params) foreach($this->params as $key => $val) { if ($val[1]) { $arr = array($key,$val[0]); if (isset($val[2])) { $arr[2] = $val[2]; } $ret[$key] = $arr; } else { $arr = array($key,false); if (isset($val[2])) { $arr[2] = $val[2]; } $ret[$key] = $arr; } } return $ret; } else { return array(); } } /** * @return array format: array(array(index, globalname),...) */ function listGlobals () { if (isset($this->globals)) { $ret = array(); if ($this->globals) foreach($this->globals as $key => $val) { $ret[] = array($key,$val); } return $ret; } else { return array(); } } /** * @return array format: array(array(static var name, static var default value),...) */ function listStatics () { if (isset($this->statics)) { $ret = array(); if ($this->statics) foreach($this->statics as $key => $val) { $ret[] = array($val['name'],$val['val']); } return $ret; } else { return array(); } } /** * sets {@link $returnsreference} to true */ function setReturnsReference() { $this->returnsreference = true; } /** * @return boolean returns value of {@link $returnsreference} */ function getReturnsReference() { return $this->returnsreference; } /** * Get a human-friendly description of the function call * * takes declaration like: * * /** @returns string ... {rest of docblock} * function &func($param1, $param2 = 6, * $param3 = array('20',9 => "heroo")) * {...} * * and returns: * string &func( $param1, [$param2 = 6], [$param3 = array('20',9 => "heroo")] ) * @return string stylized function declaration */ function getFunctionCall() { $a = ''; if ($this->getReturnsReference()) $a = '&'; $function_call = $a.$this->getName() . " ( "; $tmp = 0; foreach($this->listParams() as $param) { if ($tmp == 0) { $tmp = 1; } else { $function_call .= ", "; } if ($param[1] !== false) { $function_call .= "[$param[0] = $param[1]]"; } else { $function_call .= $param[0]; } $update_params[] = $param[0]; } $function_call .= " )"; return $function_call; } /** * Like getFunctionCall(), but has no English or pre-determined formatting. * * Much more flexible. * @return array Format: * * array('name' => function name, * 'returnsref' => boolean if declared as "function &name()" * 'params' => array('type' => data type of parameter, * 'description' => from @param tag, * 'name' => variable name, * 'default' => default value if any)) * * @see getFunctionCall() */ function getIntricateFunctionCall($converter,$paramtags) { $a = array(); if ($this->getReturnsReference()) $a['returnsref'] = true; $a['name'] = $converter->type_adjust($this->getName()); $c = $this->listParams(); foreach($c as $param) { $b = array(); $b['type'] = 'mixed'; if (isset($paramtags[$param[0]])) { $b['type'] = $paramtags[$param[0]]['datatype']; $b['description'] = $paramtags[$param[0]]['data']; unset($paramtags[$param[0]]); } elseif(isset($paramtags[substr($param[0],1)])) { $b['type'] = $paramtags[substr($param[0],1)]['datatype']; $b['description'] = $paramtags[substr($param[0],1)]['data']; unset($paramtags[substr($param[0],1)]); } if (isset($param[2])) { $link = $converter->getLink('object ' . $param[2]); if ($link) { $link = $converter->returnSee($link, $param[2], true); $b['type'] = $link; } else { $b['type'] = $param[2]; } } $b['name'] = $param[0]; $b['default'] = $converter->postProcess($param[1]); $b['hasdefault'] = ($param[1] !== false); $a['params'][] = $b; } // @param tags that don't correspond to actual parameters (like extra function values) if (count($paramtags)) { foreach($paramtags as $param) { $b = array(); $b['type'] = $param['datatype']; $b['description'] = $param['data']; $b['name'] = $param['var']; $b['default'] = ''; $b['hasdefault'] = false; $a['params'][] = $b; } } return $a; } } /** * @package phpDocumentor * @subpackage ParserElements * @author Greg Beaver * @since 1.0rc1 * @version $Id: ParserElements.inc 248547 2007-12-19 02:16:49Z ashnazg $ */ class parserClass extends parserElement { /** * Type is used by many functions to skip the hassle of if phpDocumentor_get_class($blah) == 'parserBlah' * @var string always 'class' */ var $type = 'class'; /** @var string * @see parserPage::$sourceLocation */ var $sourceLocation = ''; /** * @var mixed false or contents of extends clause in class declaration */ var $extends = false; /** * @var array a list of interfaces this class implements */ var $_implements = array(); /** * @var array a list of interfaces this class implements * @access private */ var $_modifiers = false; /** * @var boolean determines whether a class is considered to be an interface * @access private */ var $_isInterface = false; /** * Format: array(file, parent) where parent class is found or false if no parent * @var mixed */ var $parent = false; /** * Used to determine whether a class should be ignored or not. Helps maintain integrity of parsing * @var boolean * @see Classes::getParentClass() */ var $ignore = false; /** * @var string same as {@link parserElement::$path} */ var $curfile = false; /** * @var tutorialLink|false either a link to the tutorial associated with this class, or false */ var $tutorial = false; /** * Get the PHP5+ modifiers for this class * (abstract/final/static/private/protected/public) * @return array|false */ function getModifiers() { return $this->_modifiers; } /** * Set the PHP5+ modifiers for this class * (abstract/final/static/private/protected/public) * @param string $m */ function setModifiers($m) { $this->_modifiers = $m; } /** * @param parserTutorial * @param Converter */ function addTutorial($t,&$c) { $this->tutorial = new tutorialLink; $this->tutorial->addLink('',$t->path,$t->name,$t->package,$t->subpackage,$t->getTitle($c)); } /** * Get the associated tutorial for this class, if any * @tutorial tutorials.pkg * @return parserTutorial */ function getTutorial() { return $this->tutorial; } /** * Returns all classes in other packages that have the same name as this class * @return mixed false or an array Format: (package => {@link parserClass} of conflicting classes) * @param Converter */ function getConflicts(&$c) { $a = $c->classes->getConflicts($this->name); unset($a[$this->docblock->package]); return $a; } /** * quick way to link to this element * @return mixed converter-specific link to this class * @param Converter * @param string text to display for the link or false for default text */ function getLink($c, $text = false, $returnobj = false) { if ($returnobj) { return $c->getLink('object ' . $this->name, $this->docblock->package); } return $c->getClassLink($this->name, $this->docblock->package, $this->curfile, $text); } /** * @param string parent class name * @param string parent class file * @param Classes {@link Classes} object currently calling setParent * @see Classes::setClassParent() */ function setParent($p,$f, &$c) { $this->parent = array($f, $p); $p = $c->getClass($p, $f); // inherit package if no @package tag is in the docblock, fixes 591396 if (!$this->docblock->getExplicitPackage()) { $this->docblock->package = $p->docblock->package; } if ($this->docblock->package == $p->docblock->package) { if ($this->docblock->subpackage == '') $this->docblock->subpackage = $p->docblock->subpackage; } $author = $p->docblock->getKeyword('author'); $version = $p->docblock->getKeyword('version'); $copyright = $p->docblock->getKeyword('copyright'); // inherit tags if (!$this->docblock->getKeyword('author')) { if ($author && !is_array($author)) $author = array($author); if ($author) $this->docblock->tags['author'] = $author; } if (!$this->docblock->getKeyword('version')) { if ($version && !is_array($version)) $version = array($version); if ($version) $this->docblock->tags['version'] = $version; } if (!$this->docblock->getKeyword('copyright')) { if ($copyright && !is_array($copyright)) $copyright = array($copyright); if ($copyright) $this->docblock->tags['copyright'] = $copyright; } if (!$this->docblock->sdesc) { $this->docblock->setShortDesc($p->docblock); $this->docblock->setDesc($p->docblock); } else { if ($this->docblock->hasInheritDoc()) { $this->docblock->replaceInheritDoc($p->docblock); } } } /** * @param string $par parent class name (used by {@link Classes::setClassParent()} if parent class not found */ function setParentNoClass($par) { $this->parent = $par; } /** * Use this method to set the type of class to be an interface */ function setInterface() { $this->_isInterface = true; } /** * @return boolean true if this is an interface class */ function isInterface() { return $this->_isInterface; } /** * Use this method to set access modifiers for a class * @param array */ function setAccessModifiers($modifiers) { $this->_modifiers = $modifiers; } /** * retrieve object that represents the parent class * @param Converter this function will not work before the Conversion stage of parsing * @return mixed returns the {@link parserClass} representation of the parent class, or false if no parent class */ function &getParent(&$c) { $a = false; if (!$this->parent) return $a; if (is_array($this->parent)) { return $c->classes->getClass($this->parent[1],$this->parent[0]); } else return $this->parent; } /** * @param Converter this function will not work before the Conversion stage of parsing * @return array returns a simple array of method objects */ function getMethods(&$c) { return $c->classes->getMethods($this->name,$this->curfile); } /** * @return mixed {@link parserMethod} or false if not found * @param Converter this function will not work before the Conversion stage of parsing * @param string method name in this class * @param boolean determines whether to search inherited methods as well */ function getMethod(&$c, $name, $inherited = false) { $ret = $c->classes->getMethod($this->name, $this->curfile, $name); if ($ret) return $ret; if ($inherited) { $x = $this; while ($x->parent && is_array($x->parent)) { $par = $x->getParent($c); $x = $par; if ($meth = $x->getMethod($c, $name)) return $meth; } } return false; } /** * @return mixed {@link parserVar} or false if not found * @param Converter this function will not work before the Conversion stage of parsing * @param string var name in this class */ function getVar(&$c, $name) { return $c->classes->getVar($this->name,$this->curfile,$name); } /** * @param Converter this function will not work before the Conversion stage of parsing * @return array returns a simple array of method name strings */ function getMethodNames(&$c) { if (!$c->classes->hasMethods($this->curfile, $this->name)) return array(); $arr = array(); $arr1 = $this->getMethods($c); for($i=0; $i < count($arr1); $i++) { $arr[] = $arr1[$i]->name; } return $arr; } /** * @param Converter this function will not work before the Conversion stage of parsing * @param string method name * @param boolean determines whether to search inherited methods as well * @return boolean whether this class has a method of name $name */ function hasMethod(&$c, $name, $inherited = false) { $ret = $c->classes->hasMethod($this->name, $this->curfile, $name); if ($ret) return $ret; if ($inherited) { $x = $this; while ($x->parent && is_array($x->parent)) { $par = $x->getParent($c); $x = $par; if ($x->hasMethod($c, $name)) return true; } } return false; } /** * @param Converter this function will not work before the Conversion stage of parsing * @param string var name * @return boolean whether this class has a var of name $name */ function hasVar(&$c,$name) { return $c->classes->hasVar($this->name, $this->curfile, $name); } /** * @param Converter this function will not work before the Conversion stage of parsing * @param string class constant name * @return boolean whether this class has a constant of name $name */ function hasConst(&$c,$name) { return $c->classes->hasConst($this->name, $this->curfile, $name); } /** * @param Converter this function will not work before the Conversion stage of parsing * @return array returns a simple array of var objects */ function getVars(&$c) { return $c->classes->getVars($this->name,$this->curfile); } /** * @param Converter this function will not work before the Conversion stage of parsing * @return array returns a simple array of const objects */ function getConsts(&$c) { return $c->classes->getConsts($this->name,$this->curfile); } /** * @param Converter this function will not work before the Conversion stage of parsing * @return array returns a simple array of var name strings */ function getVarNames(&$c) { if (!$c->classes->hasVars($this->curfile, $this->name)) return array(); $arr = array(); $arr1 = $this->getVars($c); for($i=0; $i < count($arr1); $i++) { $arr[] = $arr1[$i]->name; } return $arr; } /** * @param Converter this function will not work before the Conversion stage of parsing * @return array returns a simple array of const name strings */ function getConstNames(&$c) { if (!$c->classes->hasConsts($this->curfile, $this->name)) return array(); $arr = array(); $arr1 = $this->getConsts($c); for($i=0; $i < count($arr1); $i++) { $arr[] = $arr1[$i]->name; } return $arr; } /** * @param Converter this function will not work before the Conversion stage of parsing * @param boolean determines whether overriden methods should be included in the list of inherited methods * @return array returns an array of methods by parent classname array(name => array(method1,method2..),name2 => array(method1....)) */ function getInheritedMethods(&$c,$override = false) { $x = $oldx = $this; $methods = array(); $arr = array(); while ($x->parent && is_array($x->parent)) { $methods = array_merge($methods,$x->getMethodNames($c)); $par = $x->getParent($c); $parmethodnames = $par->getMethodNames($c); $parmethods = $par->getMethods($c); for($i=0; $idocblock && $parmethods[$i]->docblock->hasaccess && !$c->parseprivate && $parmethods[$i]->docblock->tags['access'][0]->value == 'private') { continue; } $methods[] = $parmethodnames[$i]; $arr[$par->getName()]['methods'][] = $parmethods[$i]; $arr[$par->getName()]['file'] = $par->curfile; } } else { // fix for bug 587733 if ($parmethods[$i]->docblock && $parmethods[$i]->docblock->hasaccess && !$c->parseprivate && $parmethods[$i]->docblock->tags['access'][0]->value == 'private') { continue; } $arr[$par->getName()]['methods'][] = $parmethods[$i]; $arr[$par->getName()]['file'] = $par->curfile; } } $oldx = $x; $x = &$par; } if (is_a($oldx, 'parserClass') && is_a($oldx->getExtends(true), 'ReflectionClass')) { $extends = $oldx->getExtends(true); foreach ($extends->getMethods() as $method) { $var = new parserMethod($oldx->getExtends()); if ($method->returnsReference()) { $var->setReturnsReference(); } $doc = new parserDocBlock; foreach ($method->getParameters() as $param) { $value = $param->isDefaultValueAvailable() ? var_export($param->getDefaultValue(), true) : null; if ($param->isPassedByReference()) { $var->addParam('&$' . $param->getName(), $value, $param->isOptional(), $param->getClass()); } else { $var->addParam('$' . $param->getName(), $value, $param->isOptional(), $param->getClass()); } } $var->setName($method->getName()); $doc->addPackage('package', $oldx->getPackage()); $var->setDocBlock($doc); $par = $method->getDeclaringClass(); $var->setLineNumber($par->getStartLine()); $modifiers = array(); if ($method->isPrivate()) { $modifiers[] = 'private'; } if ($method->isAbstract()) { $modifiers[] = 'abstract'; } if ($method->isFinal()) { $modifiers[] = 'final'; } if ($method->isProtected()) { $modifiers[] = 'protected'; } if ($method->isPublic()) { $modifiers[] = 'public'; } if ($method->isStatic()) { $modifiers[] = 'static'; } if ($method->isConstructor()) { $var->setConstructor(); } $var->setModifiers($modifiers); $arr[$oldx->getExtends()]['methods'][] = $var; $arr[$oldx->getExtends()]['file'] = '(internal)'; } } return $arr; } /** * @param Converter this function will not work before the Conversion stage of parsing * @param boolean determines whether overriden vars should be included in the list of inherited vars * @return array returns an array of vars by parent classname array(name => array(var1,var1..),name2 => array(var1....)) */ function getInheritedVars(&$c,$override = true, $vars = false) { $x = $oldx = $this; $vars = array(); $arr = array(); while ($x->parent && is_array($x->parent)) { $vars = array_merge($vars,$x->getVarNames($c)); $par = $x->getParent($c); $parvarnames = $par->getVarNames($c); $parvars = $par->getVars($c); for($i=0; $idocblock && $parvars[$i]->docblock->hasaccess && !$c->parseprivate && $parvars[$i]->docblock->tags['access'][0]->value == 'private') { continue; } $vars[] = $parvarnames[$i]; $arr[$par->getName()]['vars'][] = $parvars[$i]; $arr[$par->getName()]['file'] = $par->curfile; } } else { // fix for bug 587733 if ($parvars[$i]->docblock && $parvars[$i]->docblock->hasaccess && !$c->parseprivate && $parvars[$i]->docblock->tags['access'][0]->value == 'private') { continue; } $arr[$par->getName()]['vars'][] = $parvars[$i]; $arr[$par->getName()]['file'] = $par->curfile; } } $oldx = $x; $x = &$par; } if (is_a($oldx, 'parserClass') && is_a($oldx->getExtends(true), 'ReflectionClass')) { $extends = $oldx->getExtends(true); foreach ($extends->getProperties() as $property) { $var = new parserVar($oldx->getExtends()); $doc = new parserDocBlock; $var->setName('$' . $property->getName()); $doc->addPackage('package', $oldx->getPackage()); $par = $property->getDeclaringClass(); $var->setLineNumber($par->getStartLine()); $modifiers = array(); if ($property->isPrivate()) { $modifiers[] = 'private'; $doc->addAccess('private'); } if ($property->isProtected()) { $modifiers[] = 'protected'; $doc->addAccess('protected'); } if ($property->isPublic()) { $modifiers[] = 'public'; $doc->addAccess('public'); } $var->setDocBlock($doc); $var->setModifiers($modifiers); $arr[$oldx->getExtends()]['vars'][] = $var; $arr[$oldx->getExtends()]['file'] = '(internal)'; } } return $arr; } /** * @param Converter this function will not work before the Conversion stage of parsing * @param boolean determines whether overriden vars should be included in the list of inherited vars * @return array returns an array of consts by parent classname array(name => array(const1,const2..),name2 => array(const1....)) */ function getInheritedConsts(&$c,$override = false, $consts = false) { $x = $oldx = $this; $consts = array(); $arr = array(); while ($x->parent && is_array($x->parent)) { $consts = array_merge($consts,$x->getConstNames($c)); $par = $x->getParent($c); $parvarnames = $par->getConstNames($c); $parvars = $par->getConsts($c); for($i=0; $idocblock && $parvars[$i]->docblock->hasaccess && !$c->parseprivate && $parvars[$i]->docblock->tags['access'][0]->value == 'private') { continue; } $consts[] = $parvarnames[$i]; $arr[$par->getName()]['consts'][] = $parvars[$i]; $arr[$par->getName()]['file'] = $par->curfile; } } else { // fix for bug 587733 if ($parvars[$i]->docblock && $parvars[$i]->docblock->hasaccess && !$c->parseprivate && $parvars[$i]->docblock->tags['access'][0]->value == 'private') { continue; } $arr[$par->getName()]['consts'][] = $parvars[$i]; $arr[$par->getName()]['file'] = $par->curfile; } } $oldx = $x; $x = &$par; } if (is_a($oldx, 'parserClass') && is_a($oldx->getExtends(true), 'ReflectionClass')) { $extends = $oldx->getExtends(true); if (!$extends->getConstants()) { return $arr; } foreach ($extends->getConstants() as $property => $value) { $var = new parserConst($oldx->getExtends()); $doc = new parserDocBlock; $var->setName($property); $var->setValue(var_export($value, true)); $doc->addPackage('package', $oldx->getPackage()); $var->setLineNumber($extends->getStartLine()); $var->setDocBlock($doc); $arr[$oldx->getExtends()]['consts'][] = $var; $arr[$oldx->getExtends()]['file'] = '(internal)'; } } return $arr; } /** * @param Converter this function will not work before the Conversion stage of parsing * @return array Format: array(parentclassname => parserClass/false if no parent, parentclassname2 => ...) */ function getParentClassTree(&$c) { $result = array(); $result[$this->name] = $arr = $this->getParent($c); if (is_string($arr)) $result[$arr] = false; while ($arr && is_object($arr)) { $result[$arr->name] = $arr->getParent($c); $arr = $arr->getParent($c); if (is_string($arr)) $result[$arr] = false; } return $result; } /** * returns a list of all child classes of this class * @param Converter this function will not work before the Conversion stage of parsing * @return array Format: array(parserClass child1,parserClass child2,...) */ function getChildClassList(&$c) { $list = array(); $kids = $c->classes->getDefiniteChildren($this->name,$this->curfile); if ($kids) { foreach($kids as $chile => $file) { $list[] = $c->classes->getClass($chile,$file); } } return $list; } /** * @param string * @see $sourceLocation */ function setSourceLocation($sl) { $this->sourceLocation = $sl; } /** * @param Converter * @param boolean * @return string * @see $sourceLocation */ function getSourceLocation($c,$pearize = false) { global $_phpDocumentor_options; if (!isset($this->sourceLocation)) { $sl = false; } else { $sl = $this->sourceLocation; if ($pearize) { if (strpos($sl,'pear/')) { $sl = substr($sl,strpos($sl,'pear/') + 5); } } } return $sl; } /** * @param string * @see $extends */ function setExtends($extends) { $this->extends = $extends; if (!class_exists('ReflectionClass') || !class_exists($extends)) { return; } // this may throw an exception. Hopefully it won't if the class exists $parent = new ReflectionClass($extends); if (!$parent->isInternal()) { return; } $this->extends = $parent; } /** * @param string */ function addImplements($implements) { $this->_implements[] = $implements; } /** * @return array */ function getImplements() { return $this->_implements; } /** * @return boolean * @see $extends */ function getExtends($raw = false) { if (!isset($this->extends)) return false; if (!$raw) { if (is_a($this->extends, 'ReflectionClass')) { return $this->extends->getName(); } } return $this->extends; } } /** * @package phpDocumentor * @subpackage ParserElements * @author Greg Beaver * @since 1.0rc1 * @version $Id: ParserElements.inc 248547 2007-12-19 02:16:49Z ashnazg $ */ class parserVar extends parserElement { /** * Type is used by many functions to skip the hassle of if phpDocumentor_get_class($blah) == 'parserBlah' * @var string always 'var' */ var $type = 'var'; /** @var string class that contains this var */ var $class = ''; /** @var array */ var $_modifiers; /** * @param string */ function parserVar($class) { $this->class = $class; } /** * Retrieve the class name * @return string Class name that this var belongs to */ function getClass() { return $this->class; } /** * Return a list of access modifiers (static/private/etc.) * @return array */ function getModifiers() { return $this->_modifiers; } /** * Return name of the class that contains this method * @return string */ function setModifiers($m) { $this->_modifiers = $m; } /** * quick way to link to this element * @return mixed converter-specific link to this var * @param Converter $c * @param string $text text to display for the link or false for default text */ function getLink($c, $text = false, $returnobj = false) { if ($returnobj) { return $c->getLink($this->class . '::' . $this->name, $this->docblock->package); } return $c->getVarLink($this->name, $this->class, $this->docblock->package, false, $text); } /** * @param Converter * @return mixed {@link parserVar} representing var this var overrides from the parent class, or false if none */ function getOverrides(&$c) { $class = $c->classes->getClass($this->class,$this->path); $par = $class->getParent($c); if (!is_object($par)) { if (is_a($class->getExtends(true), 'ReflectionClass')) { $pare = $class->getExtends(true); if (method_exists($pare, 'hasProperty') && $pare->hasProperty(substr($this->name, 1))) { $par = $pare; $property = $par->getProperty(substr($this->name, 1)); $ret = new parserVar($par->getName()); $doc = new parserDocBlock; $ret->setName('$' . $property->getName()); $doc->addPackage('package', $class->getPackage()); $ret->setLineNumber($par->getStartLine()); $modifiers = array(); if ($property->isPrivate()) { if ($c->parseprivate) { return false; } $modifiers[] = 'private'; $doc->addAccess('private'); } if ($property->isProtected()) { $modifiers[] = 'protected'; $doc->addAccess('protected'); } if ($property->isPublic()) { $modifiers[] = 'public'; $doc->addAccess('public'); } $ret->setDocBlock($doc); $ret->setModifiers($modifiers); return $ret; } } } while (is_object($par)) { if ($par->hasVar($c,$this->name)) { $var = $par->getVar($c,$this->name); if (!($var->docblock && $var->docblock->hasaccess && !$c->parseprivate && $var->docblock->tags['access'][0]->value == 'private')) { return $var; } } $par = $par->getParent($c); } return false; } /** * @param Converter * @return array an array of parserVars from ALL child classes that override this var */ function getOverridingVars(&$c) { $class = $c->classes->getClass($this->class,$this->path); return $this->getOverridingVarsForClass($c, $class); } /** * @param Converter * @param parserClass * @return array an array of parserVars from ALL child classes that override this var in the given class */ function getOverridingVarsForClass(&$c, &$class) { $vars = array(); if (!$class) return $meths; $kids = $class->getChildClassList($c); for($i=0; $ihasVar($c, $this->name)) { $var = $kids[$i]->getVar($c,$this->name); if (!($var->docblock && $var->docblock->hasaccess && !$c->parseprivate && $var->docblock->tags['access'][0]->value == 'private')) $vars[] = $var; } $vars = array_merge($vars, $this->getOverridingVarsForClass($c, $kids[$i])); } return $vars; } } /** * @package phpDocumentor * @subpackage ParserElements * @author Greg Beaver * @since 1.2.4 */ class parserConst extends parserElement { /** * Type is used by many functions to skip the hassle of if phpDocumentor_get_class($blah) == 'parserBlah' * @var string always 'const' */ var $type = 'const'; /** @var string class that contains this var */ var $class = ''; /** * @param string */ function parserConst($class) { $this->class = $class; } /** * Retrieve the class name * @return string Class name that this var belongs to */ function getClass() { return $this->class; } /** * quick way to link to this element * @return mixed converter-specific link to this var * @param Converter $c * @param string $text text to display for the link or false for default text */ function getLink($c, $text = false, $returnobj = false) { if ($returnobj) { return $c->getLink($this->class . '::'. $this->name, $this->docblock->package); } return $c->getConstLink($this->name, $this->class, $this->docblock->package, false, $text); } } /** * @package phpDocumentor * @subpackage ParserElements * @author Greg Beaver * @since 1.0rc1 * @version $Id: ParserElements.inc 248547 2007-12-19 02:16:49Z ashnazg $ */ class parserMethod extends parserFunction { /** * Type is used by many functions to skip the hassle of if phpDocumentor_get_class($blah) == 'parserBlah' * @var string always 'method' */ var $type = 'method'; /** @var boolean whether this method is a constructor */ var $isConstructor = false; /** @var boolean whether this method is a destructor by PEAR standards */ var $isDestructor = false; /** @var string class that contains this method */ var $class = ''; var $_modifiers = array(); /** * @param string */ function parserMethod($class) { $this->class = $class; } /** * @param string * @param string default value parsed from function definition * @param boolean indicates whether this parameter has a default value * @param null|string class type hint */ function addParam($name, $value, $has_default = true, $typehint = null) { $this->params[$name] = array($value, $has_default); if (isset($typehint)) { $this->params[$name][2] = $typehint; } } /** * adds "constructor " to start of function call if {@link $isConstructor} is true * @return string * @see parent::getFunctionCall() */ function getFunctionCall() { $a = parserFunction::getFunctionCall(); if ($this->isConstructor) $a = "constructor $a"; return $a; } function getIntricateFunctionCall($converter,$paramtags) { $a = parserFunction::getIntricateFunctionCall($converter,$paramtags); if ($this->isConstructor) $a['constructor'] = true; if ($this->isDestructor) $a['destructor'] = true; return $a; } /** * Return name of the class that contains this method * @return string */ function getClass() { return $this->class; } /** * Return name of the class that contains this method * @return string */ function getModifiers() { return $this->_modifiers; } /** * Return name of the class that contains this method * @return string */ function setModifiers($m) { $this->_modifiers = $m; } /** * @param Converter * @return mixed {@link parserMethod} representing method this method * overrides from the parent class, or false if none */ function getOverrides(&$c) { $class = $c->classes->getClass($this->class,$this->path); $par = $class->getParent($c); if (!is_object($par)) { if (is_a($class->getExtends(true), 'ReflectionClass')) { $pare = $class->getExtends(true); if (method_exists($pare, 'hasMethod') && $pare->hasMethod($this->name)) { $par = $pare; $method = $par->getMethod($this->name); $var = new parserMethod($par->getName()); if ($method->returnsReference()) { $var->setReturnsReference(); } $doc = new parserDocBlock; foreach ($method->getParameters() as $param) { $value = ($param->isOptional() && !$method->isInternal()) ? var_export($param->getDefaultValue(), true) : null; if ($param->isPassedByReference()) { $var->addParam('&$' . $param->getName(), $value, $param->isOptional(), $param->getClass()); } else { $var->addParam('$' . $param->getName(), $value, $param->isOptional(), $param->getClass()); } } $var->setName($method->getName()); $doc->addPackage('package', $this->getPackage()); $par = $method->getDeclaringClass(); $var->setLineNumber($par->getStartLine()); $modifiers = array(); if ($method->isPrivate()) { $modifiers[] = 'private'; $doc->addAccess('private'); } $blank = new parserStringWithInlineTags; if ($method->isAbstract()) { $modifiers[] = 'abstract'; $doc->addKeyword('abstract', $blank); } if ($method->isFinal()) { $modifiers[] = 'final'; $doc->addKeyword('final', $blank); } if ($method->isProtected()) { $modifiers[] = 'protected'; $doc->addAccess('protected'); } if ($method->isPublic()) { $modifiers[] = 'public'; $doc->addAccess('public'); } if ($method->isStatic()) { $modifiers[] = 'static'; $doc->addKeyword('static', $blank); } if ($method->isConstructor()) { $var->setConstructor(); } $var->setDocBlock($doc); $var->setModifiers($modifiers); return $var; } } } while (is_object($par)) { if ($par->hasMethod($c,$this->name)) { $meth = $par->getMethod($c,$this->name); if (!($meth->docblock && $meth->docblock->hasaccess && !$c->parseprivate && $meth->docblock->tags['access'][0]->value == 'private')) { return $meth; } } $par = $par->getParent($c); } return false; } /** * @param Converter * @return mixed {@link parserMethod} representing method this method implements * from an interface, or false if none */ function getImplements(&$c) { $class = $c->classes->getClass($this->class,$this->path); $implements = $class->getImplements(); if (!count($implements)) { return false; } $ret = array(); $haveAlready = array(); foreach ($implements as $interface) { $interface_link = $c->getLink('object ' . $interface); if (is_a($interface_link, 'classlink')) { $par = $c->classes->getClass($interface_link->name, $interface_link->path); if (is_object($par)) { if ($par->hasMethod($c, $this->name, true)) { $meth = $par->getMethod($c, $this->name); if (!$meth) { $meth = $par->getMethod($c, $this->name, true); } if (!($meth->docblock && $meth->docblock->hasaccess && !$c->parseprivate && $meth->docblock->tags['access'][0]->value == 'private')) { if (isset($haveAlready[$meth->getClass()])) { // this ensures extended interfaces don't cause // 2 links to the same method if ($haveAlready[$meth->getClass()] == $this->name) { continue; } } $ret[] = $meth; $haveAlready = array($meth->getClass() => $this->name); } } } continue; } if (class_exists('ReflectionClass')) { if (interface_exists($interface)) { $info = new ReflectionClass($interface); if (method_exists($info, 'hasMethod') && $info->hasMethod($this->name)) { $par = $info; $method = $par->getMethod($this->name); $var = new parserMethod($par->getName()); if ($method->returnsReference()) { $var->setReturnsReference(); } $doc = new parserDocBlock; foreach ($method->getParameters() as $param) { $value = $param->isOptional() ? var_export($param->getDefaultValue(), true) : null; if ($param->isPassedByReference()) { $var->addParam('&$' . $param->getName(), $value, $param->isOptional(), $param->getClass()); } else { $var->addParam('$' . $param->getName(), $value, $param->isOptional(), $param->getClass()); } } $var->setName($method->getName()); $doc->addPackage('package', $this->getPackage()); $par = $method->getDeclaringClass(); $var->setLineNumber($par->getStartLine()); $modifiers = array(); if ($method->isPrivate()) { $modifiers[] = 'private'; $doc->addAccess('private'); } $blank = new parserStringWithInlineTags; if ($method->isAbstract()) { $modifiers[] = 'abstract'; $doc->addKeyword('abstract', $blank); } if ($method->isFinal()) { $modifiers[] = 'final'; $doc->addKeyword('final', $blank); } if ($method->isProtected()) { $modifiers[] = 'protected'; $doc->addAccess('protected'); } if ($method->isPublic()) { $modifiers[] = 'public'; $doc->addAccess('public'); } if ($method->isStatic()) { $modifiers[] = 'static'; $doc->addKeyword('static', $blank); } if ($method->isConstructor()) { $var->setConstructor(); } $var->setDocBlock($doc); $var->setModifiers($modifiers); $ret[] = $var; continue; } } } } return $ret; } /** * quick way to link to this element * @return mixed converter-specific link to this method * @param Converter $c * @param string $text text to display for the link or false for default text */ function getLink($c, $text = false, $returnobj = false) { if ($returnobj) { return $c->getLink($this->class . '::' . $this->name . '()', $this->docblock->package); } return $c->getMethodLink($this->name, $this->class, $this->docblock->package, false, $text); } /** * Use this method to tell the parser that this method is the class constructor */ function setConstructor() { $this->isConstructor = true; } /** * Use this method to tell the parser that this method is the class constructor */ function setDestructor() { $this->isDestructor = true; } /** * @param Converter * @return array an array of parserMethods from child classes that override this method */ function getOverridingMethods(&$c) { $class = $c->classes->getClass($this->class,$this->path); return $this->getOverridingMethodsForClass($c, $class); } /** * @param Converter * @param parserClass * @return array an array of parserMethods from ALL child classes that override this method in the given class */ function getOverridingMethodsForClass(&$c, &$class) { $meths = array(); if (!$class) return $meths; $kids = $class->getChildClassList($c); for($i=0; $ihasMethod($c, $this->name)) { $meth = $kids[$i]->getMethod($c,$this->name); if (!($meth->docblock && $meth->docblock->hasaccess && !$c->parseprivate && $meth->docblock->tags['access'][0]->value == 'private')) $meths[] = $meth; } $meths = array_merge($meths, $this->getOverridingMethodsForClass($c, $kids[$i])); } return $meths; } } /** * @package phpDocumentor * @subpackage ParserElements * @author Greg Beaver * @since 1.0rc1 * @version $Id: ParserElements.inc 248547 2007-12-19 02:16:49Z ashnazg $ */ class parserDefine extends parserElement { /** * Type is used by many functions to skip the hassle of if phpDocumentor_get_class($blah) == 'parserBlah' * @var string always 'define' */ var $type = 'define'; /** * quick way to link to this element * @return mixed converter-specific link to this define * @param Converter $c * @param string $text text to display for the link or false for default text */ function getLink($c, $text = false, $returnobj = false) { if ($returnobj) { return $c->getLink('constant ' . $this->name, $this->docblock->package); } return $c->getDefineLink($this->name, $this->docblock->package, false, $text); } /** * Returns all defines in other packages that have the same name as this define * @return mixed false or an array Format: (package => {@link parserDefine} of conflicting defines) * @param Converter */ function getConflicts(&$c) { $a = $c->proceduralpages->getDefineConflicts($this->name); unset($a[$this->docblock->package]); return $a; } } /** * @package phpDocumentor * @subpackage ParserElements * @author Greg Beaver * @since 1.0rc1 * @version $Id: ParserElements.inc 248547 2007-12-19 02:16:49Z ashnazg $ */ class parserPackagePage extends parserStringWithInlineTags { /** * Type is used by many functions to skip the hassle of if phpDocumentor_get_class($blah) == 'parserBlah' * @var string always 'packagepage' */ var $type = 'packagepage'; /** @var string */ var $package = 'default'; /** * @param string */ function parserPackagePage($package) { $this->package = $package; } /** * @param Converter */ function Convert(&$c) { return parent::Convert($c,false); } } /** * @package phpDocumentor * @subpackage ParserElements * @since 1.2 */ class parserTutorial extends parserPackagePage { /** * Type is used by many functions to skip the hassle of if phpDocumentor_get_class($blah) == 'parserBlah' * @var string always 'tutorial' */ var $type = 'tutorial'; /** @var string */ var $package = 'default'; /** * Either cls, pkg, or proc * @var string */ var $tutorial_type; /** * The documentable element this tutorial is linked to * * Can be a parserData, parserClass, or nothing for package/subpackage docs */ var $linked_element; /** * path to the tutorial page * @var string */ var $path; /** * filename minus extension of this tutorial (used for @tutorial tag) * @var string */ var $name; /** @var boolean */ var $_xml = true; /** * output from tutorialname.ext.ini * * an array generated by {@link phpDocumentor_parse_ini_file()} containing * an index 'Linked Tutorials' with an array of tutorial names in the order * they should appear. This is used to generate a linked list of tutorials like * {@tutorial phpDocumentor/tags.pkg} * @var array */ var $ini = false; /** * link to the next tutorial in a document series, or false if none * @var tutorialLink */ var $next = false; /** * link to the previous tutorial in a document series, or false if none * @var tutorialLink */ var $prev = false; /** * link to the parent tutorial in a document series, or false if none * * This is used to generate an "Up" or "Home" link like the php manual. * The parent is defined as a tutorial that has a parenttutorialname.ext.ini * file and is not contained by any other tutorial's tutorialname.ext.ini * @var tutorialLink */ var $parent = false; /** * links to the child tutorials, or false if none * @var array */ var $children = false; /** * @param parserXMLDocBookTag top-level tag ( for 1.2.0) * @param information about the tutorial file. Format: * *
     * array('tutename' => tutorial name,
     *       'path' => relative path of tutorial to tutorials/ directory
     *       'ini' => contents of the tutorial .ini file, if any)
     * 
*/ function parserTutorial($data, $info) { $this->value = $data; $this->package = $info['package']; $this->subpackage = $info['subpackage']; $this->tutorial_type = $info['tutetype']; $this->name = $info['tutename']; $this->path = $info['path']; $this->ini = $info['ini']; } /** * Retrieve the title of the tutorial, or of any subsection * @param Converter * @param string which subsection to retrieve the title from, if any * @uses parserXMLDocBookTag::getSubSection() retrieve the subsection to * to get a title from */ function getTitle(&$c,$subsection = '') { if (!empty($subsection)) { $z = $this->value->getSubSection($c,$subsection); if (!$z) { addWarning(PDERROR_TUTORIAL_SUBSECTION_NOT_FOUND,$this->name,$subsection); return $subsection; } return $z->getTitle($c); } return $this->value->getTitle($c); } /** * @param Converter * @param boolean determines whether character data is postprocessed to be * Converter-friendly or not. */ function Convert(&$c, $postprocess = true) { return $this->value->Convert($c, $postprocess); } /** * @uses $parent creates a link to the documentation for the parent tutorial * @param parserTutorial * @param Converter */ function setParent($parent,&$c) { $this->parent = new tutorialLink; $this->parent->addLink('', $parent->path, $parent->name, $parent->package, $parent->subpackage, $parent->getTitle($c)); } /** * Determine if this parserTutorial object is a child of another * * WARNING: This method can enter an infinite loop when run on PHP v5.2.1... * see {@link http://bugs.php.net/bug.php?id=40608 PHP Bug #40608} * and {@link http://pear.php.net/bugs/bug.php?id=10289 PEAR Bug #10289} * @param array $parents array of parserTutorials that have child tutorials * @return boolean whether or not this tutorial is a child of the any of the parents */ function isChildOf($parents) { // avoid infinite loop PHP bug #40608 in PHP v5.2.1, see PEAR #10289 checkForBugCondition('5.2.1', '40608', '10289'); foreach($parents as $i => $parent) { if ($parent->path == $this->path) continue; if ($parent->ini && ($parent->package == $this->package) && ($parent->subpackage == $this->subpackage) && ($parent->tutorial_type == $this->tutorial_type)) { foreach($parent->ini['Linked Tutorials'] as $child) { if ($child . '.' . $this->tutorial_type == $this->name) return true; } } } } /** * Retrieve converter-specific link to the parent tutorial's documentation * @param Converter */ function getParent(&$c) { if (!$this->parent) return false; return $c->returnSee($this->parent); } /** * @uses $next creates a link to the documentation for the next tutorial * @param parserTutorial * @param Converter */ function setNext($next,&$c) { if (phpDocumentor_get_class($next) == 'tutoriallink') return $this->next = $next; $this->next = new tutorialLink; $this->next->addLink('', $next->path, $next->name, $next->package, $next->subpackage, $next->getTitle($c)); } /** * Retrieve converter-specific link to the next tutorial's documentation * @param Converter */ function getNext(&$c) { if (!$this->next) return false; return $c->returnSee($this->next); } /** * @uses $prev creates a link to the documentation for the previous tutorial * @param parserTutorial * @param Converter */ function setPrev($prev,&$c) { if (phpDocumentor_get_class($prev) == 'tutoriallink') return $this->prev = $prev; $this->prev = new tutorialLink; $this->prev->addLink('', $prev->path, $prev->name, $prev->package, $prev->subpackage, $prev->getTitle($c)); } /** * Retrieve converter-specific link to the previous tutorial's documentation * @param Converter */ function getPrev(&$c) { if (!$this->prev) return false; return $c->returnSee($this->prev); } /** * Get a link to this tutorial, or to any subsection of this tutorial * @param Converter * @param boolean if true, returns a {@link tutorialLink} instead of a string * @param string section name to link to * @return string|tutorialLink */ function getLink(&$c,$pure = false,$section = '') { $link = new tutorialLink; $link->addLink($section, $this->path, $this->name, $this->package, $this->subpackage, $this->getTitle($c), $this->category); if ($pure) return $link; return $c->returnSee($link); } } ?>