3 * Intermediate class parsing structure.
\r
5 * phpDocumentor :: automatic documentation generator
\r
7 * PHP versions 4 and 5
\r
9 * Copyright (c) 2001-2007 Gregory Beaver
\r
13 * This library is free software; you can redistribute it
\r
14 * and/or modify it under the terms of the GNU Lesser General
\r
15 * Public License as published by the Free Software Foundation;
\r
16 * either version 2.1 of the License, or (at your option) any
\r
19 * This library is distributed in the hope that it will be useful,
\r
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
22 * Lesser General Public License for more details.
\r
24 * You should have received a copy of the GNU Lesser General Public
\r
25 * License along with this library; if not, write to the Free Software
\r
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
\r
28 * @category ToolsAndUtilities
\r
29 * @package phpDocumentor
\r
30 * @author Greg Beaver <cellog@php.net>
\r
31 * @copyright 2001-2007 Gregory Beaver
\r
32 * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
\r
33 * @version CVS: $Id: Classes.inc,v 1.11 2007/10/10 01:18:25 ashnazg Exp $
\r
35 * @link http://www.phpdoc.org
\r
36 * @link http://pear.php.net/PhpDocumentor
\r
37 * @see parserDocBlock, parserInclude, parserPage, parserClass
\r
38 * @see parserDefine, parserFunction, parserMethod, parserVar
\r
40 * @todo CS cleanup - change package to PhpDocumentor
\r
43 * Intermediate class parsing structure.
\r
45 * The {@link phpDocumentor_IntermediateParser} class uses this class and its
\r
46 * cousin, {@link ProceduralPages} to organize all parsed source code elements.
\r
47 * Data is fed to each immediately after it is parsed, and at conversion time,
\r
48 * everything is organized.
\r
50 * The Classes class is responsible for all inheritance, including resolving
\r
51 * name conflicts between classes, determining which classes extend other
\r
52 * classes, and is responsible for all inheritance of documentation.
\r
54 * This structure parses classes, vars and methods by file, and then iterates
\r
55 * over the class tree to set up inheritance. The {@link Inherit()}
\r
56 * method is the meat of the class, and processes the class trees from root to
\r
57 * branch, ensuring that parsing order is unimportant.}}
\r
59 * @category ToolsAndUtilities
\r
60 * @package phpDocumentor
\r
61 * @author Greg Beaver <cellog@php.net>
\r
62 * @copyright 2001-2007 Gregory Beaver
\r
63 * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
\r
64 * @version Release: 1.4.1
\r
65 * @link http://www.phpdoc.org
\r
66 * @link http://pear.php.net/PhpDocumentor
\r
68 * @todo CS cleanup - change package to PhpDocumentor
\r
76 * file being parsed, used in every add function to match up elements with
\r
77 * the file that contains them
\r
79 * This variable is used during parsing to associate class elements added
\r
80 * to the data structures that contain them with the file they reside in
\r
81 * @see addClass(), addMethod(), addVar(), nextFile()
\r
86 * class being parsed, used to match up methods and vars with their parent
\r
89 * This variable is used during parsing to associate class elements added
\r
90 * to the data structures that contain them with the file they reside in
\r
91 * @see addMethod(), addVar()
\r
97 * Used when a definite match is made between a parent class and a child
\r
100 * This variable is used in post-parsing.
\r
105 * parentfile => array(
\r
106 * child => childfile
\r
112 var $definitechild;
\r
114 * array of parsed classes organized by the name of the file that contains
\r
119 * filename => array(
\r
120 * classname => {@link parserClass}
\r
125 var $classesbyfile = array();
\r
127 * array of file names organized by classes that are in the file.
\r
129 * This structure is designed to handle name conflicts. Two files can
\r
130 * contain classes with the same name, and this array will record both
\r
131 * filenames to help control linking and inheritance errors
\r
135 * classname => array(
\r
136 * name of file containing classname,
\r
137 * name of file 2 containing classname,
\r
143 var $classesbynamefile = array();
\r
145 * array of parsed methods organized by the file that contains them.
\r
149 * filename => array(
\r
150 * classname => array(
\r
151 * {@link parserMethod} 1,
\r
152 * {@link parserMethod} 2,
\r
159 var $methodsbyfile = array();
\r
161 * array of parsed vars organized by the file that contains them.
\r
165 * filename => array(
\r
166 * classname => array(
\r
167 * {@link parserVar} 1,
\r
168 * {@link parserVar} 2,
\r
175 var $varsbyfile = array();
\r
177 * array of parsed class constants organized by the file that contains them.
\r
181 * filename => array(
\r
182 * classname => array(
\r
183 * {@link parserConst} 1,
\r
184 * {@link parserConst} 2,
\r
191 var $constsbyfile = array();
\r
193 * keeps track of extend declarations by file, used to find inheritance
\r
197 * filename => array(
\r
198 * classname => parentclassname
\r
203 var $extendsbyfile = array();
\r
205 * Keeps track of child classes by file.
\r
206 * Since phpDocumentor can document collections of files that contain name
\r
207 * conflicts (PHP would give a fatal error), it
\r
208 * is impossible to assume a class that declares "extends foo" necessarily
\r
209 * extends the class foo in file X. It could be an
\r
210 * extended class of class foo in file Y. Because of this, phpDocumentor
\r
211 * relies on packaging to resolve the name conflict
\r
212 * This array keeps track of the packages of a child class
\r
216 * parentclassname => array(
\r
217 * filename => array(
\r
218 * childclassname => array(
\r
227 var $classchildrenbyfile = array();
\r
229 * Keeps track of class packages found in a file.
\r
230 * This is used in {@link getParentClass()} to determine the number of
\r
231 * packages in a file, in order to resolve inheritance issues
\r
234 * filename => array(
\r
242 var $classpackagebyfile = array();
\r
244 * a tree of class inheritance by name.
\r
248 * childname => parentname,
\r
249 * childname1 => parentname1,
\r
254 * @see Converter::generateSortedClassTreeFromClass()
\r
256 var $classparents = array();
\r
258 * Keeps track of package and subpackage for each class name, organized
\r
263 * classname => array(
\r
277 var $classpathpackages = array();
\r
279 * used to delete duplicates in the same package to avoid documentation errors
\r
281 * Specifically used in {@link Converter::checkKillClass()}
\r
283 var $killclass = array();
\r
285 * array of methods by package and class
\r
288 * array(packagename =>
\r
289 * array(classname =>
\r
290 * array(methodname1 => {@link parserMethod} class,
\r
291 * methodname2 => {@link parserMethod} class,...)
\r
298 var $methods = array();
\r
301 * array of class variables by package and class
\r
304 * array(packagename =>
\r
305 * array(classname =>
\r
306 * array(variablename1 => {@link parserVar} class,
\r
307 * variablename2 => {@link parserVar} class,...
\r
314 var $vars = array();
\r
317 * array of class variables by package and class
\r
320 * array(packagename =>
\r
321 * array(classname =>
\r
322 * array(constname1 => {@link parserConst} class,
\r
323 * constname2 => {@link parserConst} class,...
\r
330 var $consts = array();
\r
332 * Reverse class_packages_by_file, used to prevent duplicates
\r
333 * @var array Format: array(packagename => 1)
\r
335 var $revcpbf = array();
\r
337 * All classes with no parents (no extends clause) are tracked in this array
\r
338 * by the file that contains them.
\r
342 * classname => array(
\r
343 * name of file1 that contains root classname,
\r
344 * name of file2 that contains root classname,
\r
350 var $roots = array();
\r
352 * All classes with a parent that was not parsed are included in this array
\r
356 * classname => array(
\r
357 * name of file1 that contains root classname,
\r
358 * name of file2 that contains root classname,
\r
364 var $specialRoots = array();
\r
367 * array of all files that contain classes with the same name
\r
368 * @var array Format: (classname => array(path1, path2,...))
\r
370 var $potentialclassconflicts = array();
\r
373 * array of all inter-package name conflicts of classes
\r
375 * This array allows documentation of PHP namespace conflicts that would
\r
376 * occur should a user try to include these files in the same file
\r
377 * @var array Format: (classname => array(path1, path2,...))
\r
379 var $classconflicts = array();
\r
382 * While parsing, add a class to the list of parsed classes
\r
384 * sets up the {@link $classesbyfile, $classesbynamefile, $extendsbyfile},
\r
385 * {@link $classchildrenbyfile, $roots} arrays, and sets {@link $curclass}
\r
387 * @param parserClass &$element element is a {@link parserClass}
\r
390 * @uses addPackageToFile() marks the current class's package as being
\r
391 * present in a file
\r
393 function addClass(&$element)
\r
395 $this->curclass = $element->getName();
\r
396 $element->curfile = $this->curfile;
\r
397 if (isset($this->classesbyfile[$this->curfile][$this->curclass])) {
\r
398 addWarning(PDERROR_ELEMENT_IGNORED,
\r
399 'class', $this->curclass, $this->curfile);
\r
400 $this->curclass = false;
\r
405 [$this->curfile][$this->curclass]
\r
408 classesbynamefile[$this->curclass][]
\r
411 extendsbyfile[$this->curfile][$this->curclass]
\r
412 = $element->getExtends();
\r
414 classchildrenbyfile[$element->getExtends()]
\r
415 [$this->curfile][$this->curclass][]
\r
416 = $element->docblock->package;
\r
417 if ($element->docblock->getExplicitPackage())
\r
418 $this->addPackageToFile($element->docblock->package);
\r
419 if (!$element->getExtends()) {
\r
420 $this->roots[$this->curclass][] = $this->curfile;
\r
425 * While parsing, add a method to the list of parsed methods
\r
427 * sets up the {@link $methodsbyfile} array using {@link $curfile} and
\r
428 * {@link $curclass}
\r
430 * @param parserMethod &$element element is a {@link parserMethod}
\r
434 function addMethod(&$element)
\r
436 if (!$this->curclass) return;
\r
437 $this->methodsbyfile[$this->curfile][$this->curclass][] = $element;
\r
441 * While parsing, add a variable to the list of parsed variables
\r
443 * sets up the {@link $varsbyfile} array using {@link $curfile}
\r
444 * and {@link $curclass}
\r
446 * @param parserVar &$element element is a {@link parserVar}
\r
450 function addVar(&$element)
\r
452 if (!$this->curclass) return;
\r
453 $this->varsbyfile[$this->curfile][$this->curclass][] = $element;
\r
457 * While parsing, add a variable to the list of parsed variables
\r
459 * sets up the {@link $constsbyfile} array using {@link $curfile}
\r
460 * and {@link $curclass}
\r
462 * @param parserConst &$element element is a {@link parserConst}
\r
466 function addConst(&$element)
\r
468 if (!$this->curclass) return;
\r
469 $this->constsbyfile[$this->curfile][$this->curclass][] = $element;
\r
473 * Prepare to parse a new file
\r
475 * sets {@link $curfile} to $file and {@link $curclass}
\r
476 * to false (no class being parsed)
\r
478 * @param string $file file currently being parsed
\r
482 function nextFile($file)
\r
484 $this->curfile = $file;
\r
485 $this->curclass = false;
\r
489 * Mark a package as being used in a class
\r
493 * @param string $package package name
\r
497 function addPackageToFile($package)
\r
499 if (!isset($this->revcpbf[$this->curfile][$package]))
\r
500 $this->classpackagebyfile[$this->curfile][] = $package;
\r
501 $this->revcpbf[$this->curfile][$package] = 1;
\r
505 * Find the parent class of $class, and set up structures to note this fact
\r
507 * Modifies the {@link parserClass} element in {@link $classesbyfile} to use
\r
508 * the parent's package, and inherit methods/vars
\r
510 * @param string $class child class to find parent class
\r
511 * @param string $file file child class is located in
\r
514 * @uses $definitechild if a match is made between a parent class and parameter
\r
515 * $class in file $file, then definitechild is set here
\r
516 * @uses getParentClass() to find the parent class
\r
518 function setClassParent($class,$file)
\r
520 if (is_array($par = $this->getParentClass($class, $file))) {
\r
522 // phpDocumentor_out("$file class $class extends "
\r
523 // . $par[1] ." file ". $par[0] . "\n");
\r
525 $this->classesbyfile[$file][$class]->setParent($par[1], $par[0], $this);
\r
526 $this->definitechild[$par[1]][$par[0]][$class] = $file;
\r
528 $this->classesbyfile[$file][$class]->setParentNoClass($par);
\r
533 * Main processing engine for setting up class inheritance.
\r
535 * This function uses {@link $roots} to traverse the inheritance tree via
\r
536 * {@link processChild()} and returns the data structures
\r
537 * phpDocumentor_IntermediateParser needs to convert parsed data
\r
538 * to output using {@link phpDocumentor_IntermediateParser::Convert()}
\r
540 * @param phpDocumentor_IntermediateParser &$render the renderer object
\r
543 * @uses processChild() set up inheritance
\r
544 * @todo CS Cleanup - rename to "inherit" for CamelCaps naming standard
\r
546 function Inherit(&$render)
\r
548 phpDocumentor_out("\nProcessing Class Inheritance\n\n");
\r
550 phpDocumentor_out("\nProcessing Root Trees\n\n");
\r
552 foreach ($this->roots as $class => $files) {
\r
553 for ($i=0; $i<count($files); $i++) {
\r
554 $this->processChild($render, $class, $files[$i]);
\r
558 foreach ($this->classesbyfile as $i => $j) {
\r
559 foreach ($j as $k => $m) {
\r
561 if ($i == 'iConverter') {
\r
566 phpDocumentor_out("\nProcessing leftover classes "
\r
567 . "(classes that extend root classes not found in the same package)\n");
\r
569 foreach ($this->classesbyfile as $i => $j) {
\r
570 foreach ($j as $k => $m) {
\r
571 $this->processChild($render, $k, $i, true);
\r
574 phpDocumentor_out("done processing leftover classes\n");
\r
576 $this->setupClassConflicts();
\r
580 * Transfers actual conflicts from {@link $potentialClassconflicts} to
\r
581 * {@link $classconflicts}
\r
585 * @uses $potentialclassconflicts transfers values to {@link $classconflicts}
\r
587 function setupClassConflicts()
\r
589 foreach ($this->potentialclassconflicts as $class => $paths) {
\r
590 if (count($paths) - 1) { //conflict
\r
591 $package = array();
\r
592 foreach ($paths as $path) {
\r
593 // create a list of conflicting classes in each package
\r
594 if (isset($this->classpathpackages[$class][$path]))
\r
595 $package[$this->classpathpackages[$class][$path][0]][] = $path;
\r
597 foreach ($package as $pathpackages) {
\r
599 * if at least 2 functions exist in the same package,
\r
600 * delete all but the first one and add warnings
\r
602 if (count($pathpackages) - 1) {
\r
603 for ($i=1; $i < count($pathpackages); $i++) {
\r
604 if (isset($this->classesbyfile[$pathpackages[$i]])) {
\r
605 addWarning(PDERROR_ELEMENT_IGNORED,
\r
606 'class', $class, $pathpackages[$i]);
\r
607 $this->killClass($class, $pathpackages[$i]);
\r
608 $oth = array_flip($paths);
\r
609 unset($paths[$oth[$pathpackages[$i]]]);
\r
614 $this->classconflicts[$class] = $paths;
\r
620 * If a package contains two classes with the same name, this function finds
\r
623 * Returns the {@link $classconflicts} entry for class $class, minus its own path
\r
625 * @param mixed $class the class name to search for
\r
627 * @return mixed returns false if no conflicts,
\r
628 * or an array of paths containing conflicts
\r
630 function getConflicts($class)
\r
632 if (!isset($this->classconflicts[$class])) return false;
\r
634 foreach ($this->classconflicts[$class] as $conflict) {
\r
635 $a[$this->classesbyfile[$conflict][$class]->docblock->package]
\r
636 = $this->classesbyfile[$conflict][$class];
\r
642 * sets up {@link $killclass} for use by Converter::checkKillClass()
\r
644 * @param mixed $class the class
\r
645 * @param mixed $path the path
\r
650 function killClass($class,$path)
\r
652 $this->killclass[$class][$path] = true;
\r
656 * This function recursively climbs up the class tree, setting inherited
\r
657 * information like package and adds the elements to
\r
658 * {@link phpDocumentor_IntermediateParser}.
\r
660 * Using structures defined in {@link Classes},
\r
661 * the function first sets package information,
\r
662 * and then seeks out child classes.
\r
663 * It uses 3 tests to determine whether a class is a child class.
\r
665 * <li>child class is in the same file as the parent class
\r
666 * and extends parent class
\r
668 * <li>child class is in a different file and specifies
\r
669 * the parent's @package in its docblock
\r
671 * <li>child class is in a different file and is in a
\r
672 * different @package, with one possible parent class
\r
676 * @param phpDocumentor_IntermediateParser &$render the renderer object
\r
677 * @param string $class class to process
\r
678 * @param string $file name of file $class
\r
680 * @param boolean $furb flag used privately
\r
681 * to control informational
\r
682 * output while parsing
\r
683 * (used when processing
\r
684 * leftover classes in
\r
685 * {@link Inherit()}
\r
688 * @global string default package, usually "default"
\r
690 function processChild(&$render,$class,$file,$furb = false)
\r
692 global $phpDocumentor_DefaultPackageName;
\r
693 if (isset($this->classesbyfile[$file][$class]->processed))
\r
695 $this->potentialclassconflicts[$class][] = $file;
\r
697 phpDocumentor_out("Processing $class in file $file\n");
\r
699 $this->classesbyfile[$file][$class]->processed = true;
\r
701 $db = $this->classesbyfile[$file][$class];
\r
702 $render->addUses($db, $file);
\r
703 if (!$render->parsePrivate) {
\r
705 * if this class has an @access private,
\r
706 * and parse private is disabled, remove it
\r
708 if ($db->docblock->hasaccess) {
\r
709 $aaa = $db->docblock->getKeyword('access');
\r
710 if (is_object($aaa) && $aaa->getString() == 'private') {
\r
711 if (isset($this->varsbyfile[$file])
\r
712 && isset($this->varsbyfile[$file][$class])) {
\r
713 unset($this->varsbyfile[$file][$class]);
\r
715 if (isset($this->methodsbyfile[$file])
\r
716 && isset($this->methodsbyfile[$file][$class])) {
\r
717 unset($this->methodsbyfile[$file][$class]);
\r
719 if (isset($this->constsbyfile[$file])
\r
720 && isset($this->constsbyfile[$file][$class])) {
\r
721 unset($this->constsbyfile[$file][$class]);
\r
723 $this->classesbyfile[$file][$class]->ignore = true;
\r
724 // if this is a root class, remove it from the roots array
\r
725 if (isset($this->roots[$class])) {
\r
726 foreach ($this->roots[$class] as $i => $files) {
\r
727 // find the file key and unset
\r
728 if ($files == $file)
\r
729 unset($this->roots[$class][$i]);
\r
733 * if this is a child, remove it from the list
\r
734 * of child classes of its parent
\r
736 if ($db->getExtends())
\r
737 unset($this->classchildrenbyfile[$db->getExtends()][$file]);
\r
742 if ($render->packageoutput) {
\r
743 if (!in_array($db->docblock->package, $render->packageoutput)) {
\r
744 if (isset($this->varsbyfile[$file])
\r
745 && isset($this->varsbyfile[$file][$class])) {
\r
746 unset($this->varsbyfile[$file][$class]);
\r
748 if (isset($this->methodsbyfile[$file])
\r
749 && isset($this->methodsbyfile[$file][$class])) {
\r
750 unset($this->methodsbyfile[$file][$class]);
\r
752 if (isset($this->constsbyfile[$file])
\r
753 && isset($this->constsbyfile[$file][$class])) {
\r
754 unset($this->constsbyfile[$file][$class]);
\r
756 $this->classesbyfile[$file][$class]->ignore = true;
\r
757 if (isset($this->roots[$class])) {
\r
758 foreach ($this->roots[$class] as $i => $files) {
\r
759 if ($files == $file) unset($this->roots[$class][$i]);
\r
762 if ($db->getExtends())
\r
763 unset($this->classchildrenbyfile[$db->getExtends()][$file]);
\r
767 $this->setClassParent($class, $file);
\r
768 $db = $this->classesbyfile[$file][$class];
\r
769 if ($furb && !is_array($db->parent)) {
\r
770 // debug("furb adding $class $file to roots");
\r
771 $this->specialRoots[$db->parent][] = array($class, $file);
\r
774 if (!$db->docblock->getExplicitPackage()) {
\r
775 $a = $render->proceduralpages->pagepackages[$file];
\r
776 if ($a[0] != $phpDocumentor_DefaultPackageName) {
\r
777 // inherit page package
\r
778 $this->classesbyfile[$file][$class]->docblock->package = $a[0];
\r
781 if ($this->classesbyfile[$file][$class]->docblock->package
\r
782 == $render->proceduralpages->pagepackages[$file][0]) {
\r
783 if ($this->classesbyfile[$file][$class]->docblock->subpackage == '') {
\r
784 $this->classesbyfile[$file][$class]->docblock->subpackage
\r
785 = $render->proceduralpages->pagepackages[$file][1];
\r
788 $db = $this->classesbyfile[$file][$class];
\r
789 $render->addPackageParent($db);
\r
790 $render->addPageIfNecessary($file, $db);
\r
791 if ($access = $db->docblock->getKeyword('access')) {
\r
792 if (!is_string($access) && is_object($access))
\r
793 $access = $access->getString();
\r
794 if (!is_string($access))
\r
795 $access = 'public';
\r
796 if (($access == 'private') && (!$render->parsePrivate)) {
\r
797 if (isset($this->varsbyfile[$file])
\r
798 && isset($this->varsbyfile[$file][$class])) {
\r
799 foreach ($this->varsbyfile[$file][$class] as $i => $vr) {
\r
800 $vr->docblock->addKeyword('access', 'private');
\r
801 $this->varsbyfile[$file][$class][$i] = $vr;
\r
804 if (isset($this->methodsbyfile[$file])
\r
805 && isset($this->methodsbyfile[$file][$class])) {
\r
806 foreach ($this->methodsbyfile[$file][$class] as $i => $vr) {
\r
807 $vr->docblock->addKeyword('access', 'private');
\r
808 $this->methodsbyfile[$file][$class][$i] = $vr;
\r
811 if (isset($this->constsbyfile[$file])
\r
812 && isset($this->constsbyfile[$file][$class])) {
\r
813 foreach ($this->constsbyfile[$file][$class] as $i => $vr) {
\r
814 $vr->docblock->addKeyword('access', 'private');
\r
815 $this->constsbyfile[$file][$class][$i] = $vr;
\r
820 $this->classpathpackages[$class][$file]
\r
821 = array($db->docblock->package,$db->docblock->subpackage);
\r
822 if ($db->docblock->getExplicitPackage()) {
\r
823 $render->proceduralpages->
\r
824 addClassPackageToFile($file,
\r
825 $db->docblock->package, $db->docblock->subpackage);
\r
827 $render->addElementToPage($db, $file);
\r
828 if (isset($this->varsbyfile[$file])
\r
829 && isset($this->varsbyfile[$file][$class])) {
\r
830 foreach ($this->varsbyfile[$file][$class] as $i => $vr) {
\r
831 $vr->docblock->package = $db->docblock->package;
\r
832 $vr->docblock->subpackage = $db->docblock->subpackage;
\r
833 $render->addElementToPage($vr, $file);
\r
834 $render->addUses($vr, $file);
\r
835 $this->varsbyfile[$file][$class][$i] = $vr;
\r
836 $this->vars[$db->docblock->package][$class][$vr->getName()] = $vr;
\r
839 if (isset($this->methodsbyfile[$file])
\r
840 && isset($this->methodsbyfile[$file][$class])) {
\r
841 foreach ($this->methodsbyfile[$file][$class] as $i => $vr) {
\r
842 $vr->docblock->package = $db->docblock->package;
\r
843 $vr->docblock->subpackage = $db->docblock->subpackage;
\r
844 $render->addElementToPage($vr, $file);
\r
845 $render->addUses($vr, $file);
\r
846 $this->methodsbyfile[$file][$class][$i] = $vr;
\r
847 $this->methods[$db->docblock->package][$class][$vr->getName()] = $vr;
\r
850 if (isset($this->constsbyfile[$file])
\r
851 && isset($this->constsbyfile[$file][$class])) {
\r
852 foreach ($this->constsbyfile[$file][$class] as $i => $vr) {
\r
853 $vr->docblock->package = $db->docblock->package;
\r
854 $vr->docblock->subpackage = $db->docblock->subpackage;
\r
855 $render->addElementToPage($vr, $file);
\r
856 $render->addUses($vr, $file);
\r
857 $this->constsbyfile[$file][$class][$i] = $vr;
\r
858 $this->methods[$db->docblock->package][$class][$vr->getName()] = $vr;
\r
861 $this->classpackages[$class][]
\r
862 = array($db->docblock->package,$db->docblock->subpackage);
\r
863 if (is_array($db->parent))
\r
864 $this->classparents[$db->docblock->package][$class] = $db->parent[1];
\r
866 $this->classparents[$db->docblock->package][$class] = $db->getExtends();
\r
867 if (is_array($db->parent)) {
\r
868 $z = $this->getClass($db->parent[1], $db->parent[0]);
\r
870 $this->classchildren[$z->docblock->package][$db->parent[1]][] = $db;
\r
872 if (isset($this->classchildrenbyfile[$class])) {
\r
873 foreach ($this->classchildrenbyfile[$class] as $childfile => $other) {
\r
874 // test 1, inherits in same file (must be same package)
\r
875 if ($childfile == $file) {
\r
876 foreach ($other as $child => $packages) {
\r
877 // debug("parent $class same file $child");
\r
878 $this->processChild($render, $child, $childfile);
\r
879 $x = $this->getClass($child, $childfile);
\r
880 if ($x->docblock->package
\r
881 != $GLOBALS['phpDocumentor_DefaultPackageName']) {
\r
882 // child package need root for class trees
\r
883 if ($x->docblock->package != $db->docblock->package) {
\r
884 // debug("adding $child in $childfile 1");
\r
885 $this->roots[$child][] = $childfile;
\r
890 // test 2, different file, same package
\r
891 foreach ($other as $child => $packages) {
\r
892 for ($j=0; $j<count($packages); $j++) {
\r
893 if ($this->classesbyfile[$file][$class]->
\r
894 docblock->package == $packages[$j]) {
\r
895 $this->processChild($render, $child, $childfile);
\r
896 // debug("$childfile diff file $child, parent $class,
\r
897 // same package ".$packages[$j]);
\r
900 * test 3, different file, different package,
\r
901 * only 1 parent is possible
\r
903 if (isset($this->classesbynamefile[$child])) {
\r
904 // 1 possible parent
\r
905 if (count($this->classesbynamefile[$class])
\r
907 // debug("$childfile diff file $child,
\r
909 // 1 possible parent root $class");
\r
910 $this->processChild($render,
\r
911 $child, $childfile);
\r
912 $x = $this->getClass($child, $childfile);
\r
913 if ($x->docblock->package
\r
915 ['phpDocumentor_DefaultPackageName']) {
\r
916 // child package need root
\r
918 if ($x->docblock->package
\r
919 != $db->docblock->package) {
\r
920 // debug("adding roots
\r
921 // $child in $childfile 2");
\r
922 $this->roots[$child][] = $childfile;
\r
936 * Get the parserClass representation of a class from its name and file
\r
938 * @param string $class classname
\r
939 * @param string $file file classname is located in
\r
941 * @return parserClass
\r
943 function &getClass($class, $file)
\r
945 // debug("getClass called with class $class file $file");
\r
946 return $this->classesbyfile[$file][$class];
\r
950 * Used by {@link parserData::getClasses()}
\r
951 * to retrieve classes defined in file $path
\r
953 * retrieves the array entry from {@link $classesbyfile} for $path
\r
955 * @param string $path full path to filename
\r
957 * @return mixed returns false if no classes defined in the file,
\r
958 * otherwise returns an array of {@link parserClass}es
\r
960 function getClassesInPath($path)
\r
962 if (!isset($this->classesbyfile[$path])) return false;
\r
963 return $this->classesbyfile[$path];
\r
967 * called by {@link parserClass::hasMethods()}. Should not be directly called
\r
969 * @param string $file file classname is located in
\r
970 * @param string $class classname
\r
975 function hasMethods($file, $class)
\r
977 return isset($this->methodsbyfile[$file][$class]);
\r
981 * called by {@link parserClass::hasConsts()}.
\r
982 * Should not be directly called
\r
984 * @param string $file file classname is located in
\r
985 * @param string $class classname
\r
990 function hasConsts($file,$class)
\r
992 return isset($this->constsbyfile[$file][$class]);
\r
996 * called by {@link parserClass::hasVars()}. Should not be directly called
\r
998 * @param string $file file classname is located in
\r
999 * @param string $class classname
\r
1004 function hasVars($file, $class)
\r
1006 return isset($this->varsbyfile[$file][$class]);
\r
1010 * called by {@link parserClass::hasMethod()}. Should not be directly called
\r
1012 * @param string $class classname
\r
1013 * @param string $file file classname is located in
\r
1014 * @param string $name method name
\r
1019 function hasMethod($class, $file, $name)
\r
1021 if (!$this->hasMethods($file, $class)) return false;
\r
1022 for ($i=0; $i<count($this->methodsbyfile[$file][$class]); $i++) {
\r
1023 if ($this->methodsbyfile[$file][$class][$i]->getName() == $name)
\r
1030 * called by {@link parserClass::hasVar()}. Should not be directly called
\r
1032 * @param string $class classname
\r
1033 * @param string $file file classname is located in
\r
1034 * @param string $name var name
\r
1039 function hasVar($class, $file, $name)
\r
1041 if (!$this->hasVars($file, $class)) return false;
\r
1042 for ($i=0; $i<count($this->varsbyfile[$file][$class]); $i++) {
\r
1043 if ($this->varsbyfile[$file][$class][$i]->getName() == $name)
\r
1050 * called by {@link parserClass::hasConst()}. Should not be directly called
\r
1052 * @param string $class classname
\r
1053 * @param string $file file classname is located in
\r
1054 * @param string $name constant name
\r
1059 function hasConst($class, $file, $name)
\r
1061 if (!$this->hasConsts($file, $class)) return false;
\r
1062 for ($i=0; $i<count($this->constsbyfile[$file][$class]); $i++) {
\r
1063 if ($this->constsbyfile[$file][$class][$i]->getName() == $name)
\r
1070 * called by {@link parserClass::getMethods()}. Should not be directly called
\r
1072 * @param string $class classname
\r
1073 * @param string $file file classname is located in
\r
1078 function &getMethods($class, $file)
\r
1080 if (!isset($this->methodsbyfile[$file][$class])) {
\r
1084 return $this->methodsbyfile[$file][$class];
\r
1088 * called by {@link parserClass::getVars()}. Should not be directly called
\r
1090 * @param string $class classname
\r
1091 * @param string $file file classname is located in
\r
1096 function &getVars($class, $file)
\r
1098 if (!isset($this->varsbyfile[$file][$class])) {
\r
1102 return $this->varsbyfile[$file][$class];
\r
1106 * called by {@link parserClass::getConsts()}. Should not be directly called
\r
1108 * @param string $class classname
\r
1109 * @param string $file file classname is located in
\r
1114 function &getConsts($class, $file)
\r
1116 if (!isset($this->constsbyfile[$file][$class])) {
\r
1120 return $this->constsbyfile[$file][$class];
\r
1124 * called by {@link parserClass::getMethod()}. Should not be directly called
\r
1126 * @param string $class classname
\r
1127 * @param string $file file classname is located in
\r
1128 * @param string $name method name
\r
1133 function getMethod($class, $file, $name)
\r
1135 if (!$this->hasMethod($class, $file, $name)) return false;
\r
1136 for ($i=0; $i<count($this->methodsbyfile[$file][$class]); $i++) {
\r
1137 if ($this->methodsbyfile[$file][$class][$i]->getName() == $name)
\r
1138 return $this->methodsbyfile[$file][$class][$i];
\r
1143 * called by {@link parserClass::getVar()}. Should not be directly called
\r
1145 * @param string $class classname
\r
1146 * @param string $file file classname is located in
\r
1147 * @param string $name var name
\r
1152 function getVar($class, $file, $name)
\r
1154 if (!$this->hasVar($class, $file, $name)) return false;
\r
1155 for ($i=0; $i<count($this->varsbyfile[$file][$class]); $i++) {
\r
1156 if ($this->varsbyfile[$file][$class][$i]->getName() == $name)
\r
1157 return $this->varsbyfile[$file][$class][$i];
\r
1162 * called by {@link parserClass::getConst()}. Should not be directly called
\r
1164 * @param string $class classname
\r
1165 * @param string $file file classname is located in
\r
1166 * @param string $name const name
\r
1171 function getConst($class, $file, $name)
\r
1173 if (!$this->hasConst($class, $file, $name)) return false;
\r
1174 for ($i=0; $i<count($this->constsbyfile[$file][$class]); $i++) {
\r
1175 if ($this->constsbyfile[$file][$class][$i]->getName() == $name)
\r
1176 return $this->constsbyfile[$file][$class][$i];
\r
1181 * Search for a class in a package
\r
1183 * @param string $class classname
\r
1184 * @param string $package package classname is in
\r
1186 * @return mixed returns false if no class in $package,
\r
1187 * otherwise returns a {@link parserClass}
\r
1189 function &getClassByPackage($class, $package)
\r
1191 if (!isset($this->classesbynamefile[$class])) {
\r
1192 // removed, too many warnings, not very useful
\r
1193 // addWarning(PDERROR_CLASS_NOT_IN_PACKAGE,$class,$package);
\r
1198 for ($i=0; $i < count($this->classesbynamefile[$class]); $i++) {
\r
1200 $this->classesbyfile[$this->classesbynamefile[$class][$i]][$class];
\r
1201 $pkg = $cls->getPackage();
\r
1202 if ($pkg == $package)
\r
1205 // addWarning(PDERROR_CLASS_NOT_IN_PACKAGE,$class,$package);
\r
1212 * Find the parent class of a class in file $file
\r
1213 * uses 3 tests to find the parent classname:
\r
1215 * <li>only one class with the parent classname</li>
\r
1216 * <li>more than one class, but only one in the same file as the child</li>
\r
1217 * <li>only one parent class in the same package as the child</li>
\r
1220 * @param string $class classname
\r
1221 * @param string $file file classname is located in
\r
1223 * @return mixed false if no parent class,
\r
1224 * a string if no parent class found by that name,
\r
1225 * and an array(file parentclass is in, parentclassname)
\r
1227 function getParentClass($class,$file)
\r
1229 if (!isset($this->classesbyfile[$file][$class])) {
\r
1232 $element = $this->classesbyfile[$file][$class];
\r
1233 if (!($ex = $element->getExtends())) return false;
\r
1234 // first check to see if there is one and only one
\r
1235 // class with the parent class's name
\r
1236 if (isset($this->classesbynamefile[$ex])) {
\r
1237 if (count($this->classesbynamefile[$ex]) == 1) {
\r
1238 if ($this->classesbyfile
\r
1239 [$this->classesbynamefile[$ex][0]][$ex]->ignore) {
\r
1242 return array($this->classesbynamefile[$ex][0],$ex);
\r
1244 // next check to see if there is a parent class in the same file
\r
1245 if (isset($this->classesbyfile[$file][$ex])) {
\r
1246 if ($this->classesbyfile[$file][$ex]->ignore) {
\r
1249 return array($file,$ex);
\r
1251 // next check to see if there is only one package
\r
1252 // used in the file, try to resolve it that way
\r
1253 if (isset($this->classpackagebyfile[$file])) {
\r
1254 if (count($this->classpackagebyfile[$file]) == 1) {
\r
1255 for ($i=0;$i<count($this->classesbynamefile[$ex]);$i++) {
\r
1256 if ($this->classesbyfile
\r
1257 [$this->classesbynamefile[$ex][$i]][$ex]->getPackage()
\r
1258 == $this->classpackagebyfile[$file][0]) {
\r
1259 if ($this->classesbyfile
\r
1260 [$this->classesbynamefile[$ex][$i]][$ex]->ignore)
\r
1262 return array($this->classesbynamefile[$ex][$i],$ex);
\r
1268 addWarning(PDERROR_INHERITANCE_CONFLICT, $class, $file, $ex);
\r
1272 if (class_exists('ReflectionClass') && class_exists($ex)) {
\r
1273 $r = new ReflectionClass($ex);
\r
1274 if ($r->isInternal()) {
\r
1275 return $ex; // no warning
\r
1278 addWarning(PDERROR_PARENT_NOT_FOUND, $class, $ex);
\r
1284 * Get a list of all root classes indexed by package. Used to generate
\r
1285 * class trees by {@link Converter}
\r
1287 * @param boolean $all [since phpDocumentor 1.3.0RC6] determines whether to
\r
1288 * return class trees that extend non-parsed classes
\r
1290 * @return array array(package => array(rootclassname, rootclassname,...),...)
\r
1292 function getRoots($all = false)
\r
1295 $temproots = $this->roots;
\r
1297 foreach ($this->specialRoots as $package => $root) {
\r
1298 foreach ($root as $parent => $info) {
\r
1299 $temproots[$info[0]][] = $info[1];
\r
1303 foreach ($temproots as $class => $files) {
\r
1304 if (count($files)) {
\r
1305 foreach ($files as $i => $boofou) {
\r
1306 $x = $this->getClass($class, $files[$i]);
\r
1308 $roots[$x->getPackage()][] = $class;
\r
1312 foreach ($roots as $package => $root) {
\r
1313 usort($roots[$package], "strnatcasecmp");
\r
1316 $specialRoots = array();
\r
1317 foreach ($this->specialRoots as $parent => $classinfo) {
\r
1318 if (count($classinfo)) {
\r
1319 foreach ($classinfo as $i => $info) {
\r
1320 $x = $this->getClass($info[0], $info[1]);
\r
1322 $specialRoots[$x->getPackage()][$parent][] = $info[0];
\r
1326 foreach ($specialRoots as $package => $root) {
\r
1327 uksort($specialRoots[$package], "strnatcasecmp");
\r
1328 foreach ($specialRoots[$package] as $parent => $classes) {
\r
1329 usort($specialRoots[$package][$parent], 'strnatcasecmp');
\r
1332 return array('special' => $specialRoots, 'normal' => $roots);
\r
1338 * Get all classes confirmed in parsing
\r
1339 * to be descended class $parclass in file $file
\r
1341 * @param string $parclass name of parent class
\r
1342 * @param string $file file parent class is found in
\r
1344 * @return mixed either false if no children, or array of format
\r
1345 * array(childname => childfile,childname2 => childfile2,...)
\r
1346 * @see parserClass::getChildClassList()
\r
1347 * @uses $definitechild
\r
1349 function getDefiniteChildren($parclass, $file)
\r
1351 if (isset($this->definitechild[$parclass][$file]))
\r
1352 return $this->definitechild[$parclass][$file];
\r