1<?php 2/** 3 * 4 * This is a TEST sample, designed to by parsed by PHP_UML itself. 5 * 6 * Do not use it. 7 * It is probably an old version, already out of date. 8 * 9 * PHP version 5 10 * 11 * @category PHP 12 * @package PHP_UML::tests 13 * @author Baptiste Autin <ohlesbeauxjours@yahoo.fr> 14 * @license http://www.gnu.org/licenses/lgpl.html LGPL License 3 15 * @link http://pear.php.net/package/PHP_UML 16 */ 17 18 19/* 20 * PHP File scanner 21 */ 22abstract class PHP_UML_Scanner 23{ 24 /** 25 * Directories to ignore during scan 26 * @var array 27 */ 28 public $ignoredDirectories = array(); 29 30 /** 31 * Recursive search 32 * @var bool 33 */ 34 public $subDirectories = true; 35 36 /** 37 * Traverse recursively the directories for files to parse 38 * 39 * @param string $dir Path folder to look into 40 * @param int $level Level of recursion 41 * 42 * @return void 43 */ 44 protected function traverseDirectory($dir, $level = 1) 45 { 46 if (is_dir($dir)) { 47 $this->atFolderIn($level, $dir); 48 if ($dh = opendir($dir)) { 49 while (($file = readdir($dh)) !== false) { 50 if (array_search($file, $this->ignoredDirectories)===false) { 51 if (filetype($dir.$file) == 'dir') { 52 if ($this->subDirectories) { 53 $this->traverseDirectory( 54 $dir.$file.DIRECTORY_SEPARATOR, $level+1 55 ); 56 } 57 } 58 else { 59 $this->atFile($level, $dir.$file); 60 } 61 } 62 } 63 closedir($dh); 64 } 65 $this->atFolderOut($level, $dir); 66 } 67 } 68} 69 70 71/** 72 * The main class to instantiate 73 * 74 */ 75class PHP_UML extends PHP_UML_Scanner 76{ 77 const FILE = 1; 78 const DIR_OPEN = 2; 79 const DIR_CLOSE = 3; 80 81 /** 82 * Extensions of files to scan 83 * @var Array 84 */ 85 public $acceptedExtensions = array('php'); 86 87 /** 88 * Filenames are added to classes and interfaces 89 * @var bool 90 */ 91 public $tagFilename = true; 92 93 /** 94 * Each file generates an UML:Artifact (in the logicial view) 95 * @var bool 96 */ 97 public $pageAsArtifact = true; 98 99 /** 100 * A component view is created at root model, with the whole scanned file system 101 * inside (as components) 102 * @var bool 103 */ 104 public $componentView = true; 105 106 /** 107 * Docblocks are read (package, param and return). This includes class, 108 * function and file comments. 109 * @var bool 110 */ 111 public $docblocks = true; 112 113 /** 114 * Keep the PHP variable prefix $ 115 * @var bool 116 */ 117 public $dollar = true; 118 119 /** 120 * A reference to a PHP_UML_Metamodel_Superstructure object 121 * Either parseFile() or parserDirectory() set it, once their job is done. 122 * Or you can set it yourself with a predefined instance of superstructure. 123 * @var PHP_UML_Metamodel_Superstructure 124 */ 125 public $model; 126 127 /** 128 * XML Encoding (see the constructor) 129 * @var string 130 */ 131 private $_xmlEncoding; 132 133 /** 134 * The concatened XMI string 135 * @var string 136 */ 137 private $_xmi = ''; 138 139 /** 140 * A reference to a PHP_UML_PHP_Parser object 141 * @var PHP_UML_PHP_Parser 142 */ 143 private $_parser; 144 145 /** 146 * A reference to a PHP_UML_XMI_Factory object 147 * @var PHP_UML_XMI_Factory 148 */ 149 private $_factory; 150 151 /** 152 * Original directory path 153 * @var string 154 */ 155 private $_originalDir = ''; 156 157 /** 158 * Stack of parsed files and folders. Used for building the filesystem tree. 159 * @var array 160 */ 161 private $_visited = array(); 162 163 /** 164 * Start position of the scanned filepath 165 * @var int 166 */ 167 private $_basePathStart = 0; 168 169 170 public function __construct() 171 { 172 } 173 174 /** 175 * Parse a PHP file, and builds the resulting XMI. 176 * 177 * @param mixed $filename File(s) to parse. Can be a single file, 178 * or an array of files. 179 * @param string $model Name of the model placed at root (enclosing pkg) 180 * 181 * It is the "PHP global" namespace. 182 */ 183 public function parseFile($filename, $model = 'default') 184 { 185 $this->_parser = new PHP_UML_PHP_Parser($model, $this->docblocks, $this->dollar); 186 187 $this->_visited = array(); 188 189 if (!is_array($filename)) { 190 $filename = array($filename); 191 } 192 193 foreach ($filename as $filename_item) { 194 if (file_exists($filename_item)) { 195 $filename_item = realpath($filename_item); 196 $name = basename($filename_item); 197 $this->_originalDir = dirname($filename_item); 198 $this->_basePathStart = 1+strlen($this->_originalDir); 199 $path = $this->_originalDir.DIRECTORY_SEPARATOR; 200 $this->atFile(1, $path.$name); 201 } 202 else 203 throw new PHP_UML_Exception($filename_item.' : file not found.'); 204 } 205 $this->_parser->finalize(); 206 $this->model = &$this->_parser->model; 207 } 208 209 /** 210 * Parse a PHP file, and builds the resulting XMI. 211 * 212 * @param mixed $path Path(s) of the directories. Can be a single path, 213 * or an array of pathes. 214 * @param string $model Name of the model placed at root (enclosing pkg) 215 */ 216 public function parseDirectory($path, $model = 'default') 217 { 218 $this->_parser = new PHP_UML_PHP_Parser($model, $this->docblocks, $this->dollar); 219 220 $this->_visited = array(); 221 222 array_push($this->ignoredDirectories, '.'); 223 array_push($this->ignoredDirectories, '..'); 224 225 if (!is_array($path)) { 226 $path = array($path); 227 } 228 229 foreach ($path as $path_item) { 230 $this->_originalDir = realpath($path_item); 231 $this->_basePathStart = 1+strlen($this->_originalDir); 232 if ($this->_originalDir != '') { 233 $this->traverseDirectory($this->_originalDir.DIRECTORY_SEPARATOR); 234 } 235 else 236 throw new PHP_UML_Exception($path_item.' : unknown path.'); 237 } 238 $this->_parser->finalize(); 239 $this->model = &$this->_parser->model; 240 } 241 242 /** 243 * XMI Generator 244 * Generates XMI corresponding to the PHP model stored in $this->model. 245 * 246 * If you need to use this XMI Generator without any previous PHP parsing, 247 * simply set $this->model with a proper PHP_UML_Metamodel_Superstructure object 248 * 249 * @param float $version XMI Version For XMI 1.x, any value below 2. 250 * For XMI 2.1, any value above or equal to 2. 251 * @param string $xmlEncoding XML Encoding 252 */ 253 public function generateXMI($version = 2.1, $xmlEncoding = 'iso-8859-1') 254 { 255 $this->_xmlEncoding = $xmlEncoding; 256 $this->_xmi = '<?xml version="1.0" encoding="'.$this->_xmlEncoding.'"?>'; 257 $this->_factory = PHP_UML_XMI_Factory::factory($version); 258 259 if(empty($this->model)) { 260 throw new PHP_UML_Exception('No model given'); 261 } 262 263 $_root = &$this->model->packages->get(0); 264 $this->_xmi .= $this->_factory->getModelOpen($_root); 265 266 foreach ($this->model->datatypes->getIterator() as $type) 267 $this->_xmi .= $this->_factory->getDatatype($type); 268 269 $this->addPackage($_root, true); 270 271 if ($this->componentView) { 272 $this->_xmi .= $this->_factory->getComponentView($this->_visited); 273 } 274 $this->_xmi .= $this->_factory->getModelClose(); 275 } 276 277 /** 278 * Save the previously generated XMI to a file. 279 * 280 * @param string $output_file Filename 281 */ 282 public function saveXMI($output_file) 283 { 284 if ($ptr = fopen($output_file, 'w+')) { 285 fwrite($ptr, $this->XMI); 286 fclose($ptr); 287 } 288 else 289 throw new PHP_UML_Exception( 290 'File '.$output_file.' could not be created.' 291 ); 292 } 293 294 /** 295 * Save a UML Profile XMI-suited with PHP_UML. 296 * 297 * THIS IS EXPERIMENTAL. 298 * Only XMI and UML >= 2.x 299 * 300 * @param string $output_file Filename 301 */ 302 private function _saveXMIProfile($output_file) 303 { 304 if ($ptr = fopen($output_file, 'w+')) { 305 fwrite($ptr, '<?xml version="1.0" encoding="'.$this->_xmlEncoding.'"?>'. 306 $this->_factory->getProfile() 307 ); 308 fclose($ptr); 309 } 310 else 311 throw new PHP_UML_Exception( 312 'File '.$output_file.' could not be created.' 313 ); 314 } 315 316 /** 317 * Accessor to the XMI. 318 * 319 * @param string $name Must be "XMI" or "parsed" 320 * 321 * @return string The XMI code, or a PHP_UML_Parser_Result object 322 */ 323 public function __get($name) 324 { 325 switch($name) { 326 case 'XMI': 327 if (strtolower($this->_xmlEncoding)=='utf-8') 328 return utf8_encode($this->_xmi); 329 else 330 return $this->_xmi; 331 break; 332 case 'XMIProfile': 333 return $this->_factory->getProfile(); 334 break; 335 default: 336 return null; 337 } 338 } 339 340 /** 341 * Function executed each time a new file is traversed 342 * 343 * @param int $level Level of recursion in the sub-directories 344 * @param string $pathfile Current file path 345 */ 346 protected function atFile($level, $pathfile) 347 { 348 $path_parts = pathinfo($pathfile); 349 if (isset($path_parts['extension'])) { 350 $extension = $path_parts['extension']; 351 } 352 else { 353 $extension = ''; 354 } 355 if (in_array($extension, $this->acceptedExtensions)) { 356 $this->_parser->parse($pathfile); 357 } 358 $this->_visited[] = array( 359 self::FILE => substr($pathfile, $this->_basePathStart) 360 ); 361 } 362 363 /** 364 * Enters a new folder 365 * 366 * @param int $level Level of recursion 367 * @param string $dir Name of folder 368 */ 369 protected function atFolderIn($level, $dir) 370 { 371 $this->_visited[] = array(self::DIR_OPEN => $dir); 372 } 373 374 /** 375 * Exits a folder 376 * 377 * @param int $level Level of recursion 378 * @param string $dir Name of folder 379 */ 380 protected function atFolderOut($level, $dir) 381 { 382 $this->_visited[] = array(self::DIR_CLOSE => $dir); 383 } 384 385 /** 386 * Traverses all packages, and adds recursively the elements found 387 * to the "xmi" string property. 388 * 389 * @param PHP_UML_Metamodel_Package $package New package to traverse 390 * @param bool $stripTag Omit package XMI tag 391 */ 392 protected function addPackage(PHP_UML_Metamodel_Package $package, $stripTag = false) 393 { 394 if (!$stripTag) { 395 $this->_xmi .= $this->_factory->getPackageOpen($package); 396 } 397 398 foreach ($package->ownedType as &$elt) { 399 if (get_class($elt)=='PHP_UML_Metamodel_Interface') 400 $this->_xmi .= $this->_factory->getInterface($elt); 401 else 402 $this->_xmi .= $this->_factory->getClass($elt); 403 } 404 405 foreach ($package->nestedPackage as $idx) 406 $this->addPackage($this->model->packages->get($idx)); 407 408 /*if($this->pageAsArtifact) { 409 $files_list = self::getFilesInPackage($obj); 410 $this->_xmi .= $this->_factory->getArtifacts( 411 $files_list, $package 412 ); 413 }*/ 414 if (!$stripTag) { 415 $this->_xmi .= $this->_factory->getPackageClose(); 416 } 417 } 418 419 /** 420 * Filename part of a given path 421 * 422 * @param string $x Filename 423 * 424 * @return string 425 */ 426 private static function _getFilename($x) 427 { 428 $pathinfo = pathinfo($x); 429 return $pathinfo['filename']; 430 } 431 432 /** 433 * Basename part of a given path 434 * 435 * @param string $x Filename 436 * 437 * @return string 438 */ 439 private static function _getBasename($x) 440 { 441 $pathparts = pathinfo(realpath($x)); 442 return $pathparts['basename']; 443 } 444} 445 446 447/** 448 * Subclass for PHP_UML_Exception 449 * 450 */ 451class PHP_UML_Exception extends PEAR_Exception 452{ 453} 454 455/** 456 * Maintains of stack of warning messages. Worth to being checked, especially 457 * if multiple classes in your PHP files have the same name... 458 */ 459class PHP_UML_Warning 460{ 461 /** 462 * The $stack to read. 463 * @var array 464 */ 465 static public $stack; 466 467 /** 468 * Adds a message to the pile 469 * 470 * @param string $message The warning message to add 471 */ 472 static public function add($message) 473 { 474 self::$stack[] = $message; 475 } 476 477 /** 478 * Clears the pile 479 */ 480 static public function clear() 481 { 482 self::$stack = array(); 483 } 484} 485 486 487/** 488 * Abstract class to build UML elements through XMI code. 489 * Only basic UML concepts are available. 490 * To deal with the two different versions of XMI (1.4 and 2.1), you must use one of 491 * the two specialized versions : PHP_UML_XMI_Factory1, or PHP_UML_XMI_Factory2 492 * 493 */ 494abstract class PHP_UML_XMI_Factory 495{ 496 const EXPORTER_NAME = 'PEAR::PHP_UML'; 497 const PHP_FILE = 'PHP File'; 498 499 static public $stereotypes = array('File', self::PHP_FILE); 500 static public $extensions = array(''=>'File', 'php'=>self::PHP_FILE); 501 502 /** 503 * Retrieves the ID of a stereotype, given a filename 504 * 505 * @param string $filename The file name 506 * 507 * @return string The PHP_UML ID of the matching extension 508 */ 509 static protected function guessStereotype($filename = '') 510 { 511 $path_parts = pathinfo($filename); 512 $extension = isset($path_parts['extension']) ? $path_parts['extension'] : ''; 513 if (isset(self::$extensions[$extension])) 514 return self::generateID('stereotype', self::$extensions[$extension]); 515 else 516 return self::generateID('stereotype', self::$extensions['']); 517 } 518 519 static protected function generateID($type, $element) 520 { 521 return md5(self::EXPORTER_NAME.'#'.$type.'#'.$element); 522 } 523 524 protected static function getFilesInPackage(Array &$package) 525 { 526 $files_list = array(); 527 if (!empty($package)) { 528 foreach ($package as $c => &$value) { 529 if (!in_array($value['file'], $files_list)) 530 $files_list[] = $value['file']; 531 } 532 } 533 return $files_list; 534 } 535 536 /** 537 * Insert a component view of the scanned file system. 538 * Files are treated as components (all files are inserted) 539 * Folders are treated as subsystems in UML1, and as nested components in UML2. 540 * 541 * @param array &$obj Visited files 542 * 543 * @return string XMI 544 */ 545 public function getComponentView(Array &$obj) 546 { 547 $str = $this->getSubsystemOpen('Component View'); 548 foreach ($obj as $value) { 549 $keys = array_keys($value); 550 $type = $keys[0]; 551 $element = $value[$type]; 552 switch($type) { 553 case PHP_UML::FILE : 554 $str .= $this->getComponent( 555 basename($element), $element, self::guessStereotype($element) 556 ); 557 break; 558 case PHP_UML::DIR_OPEN : 559 $str .= $this->getSubsystemOpen(basename($element), $element); 560 break; 561 case PHP_UML::DIR_CLOSE : 562 $str .= $this->getSubsystemClose(); 563 break; 564 } 565 } 566 $str .= $this->getSubsystemClose(); 567 return $str; 568 } 569 570 /** 571 * Factory method 572 * 573 * @param int $version XMI version 574 * 575 * @return PHP_UML_XMI_Factory 576 */ 577 static function factory($version) 578 { 579 if ($version < 2) 580 return new PHP_UML_XMI_Factory1(); 581 else 582 return new PHP_UML_XMI_Factory2(); 583 } 584} 585 586/** 587 * Implementation class to create XMI in version 1 588 * 589 */ 590class PHP_UML_XMI_Factory1 extends PHP_UML_XMI_Factory 591{ 592 const XMI_VERSION = 1.2; 593 const UML_VERSION = 1.4; 594 595 const DEFAULT_CLASSIFIER_ATT = ' visibility="public" isAbstract="false" 596 isSpecification="false" isRoot="false" isLeaf="false" '; 597 598 /** 599 * Formates the XMI header 600 * 601 * @param string &$model Name of the model (root package) 602 */ 603 public function getModelOpen(PHP_UML_Metamodel_Package &$model) 604 { 605 $str = '<XMI xmi.version="'.self::XMI_VERSION.'" 606 xmlns:UML="http://www.omg.org/spec/UML/1.4"> 607 <XMI.header> 608 <XMI.documentation> 609 <XMI.exporter>'.self::EXPORTER_NAME.'</XMI.exporter> 610 </XMI.documentation> 611 <XMI.metamodel XMI.name="UML" XMI.version="'.self::XMI_VERSION.'" /> 612 </XMI.header> 613 <XMI.content> 614 <UML:Model name="'.$model->name.'" 615 xmi.id="'.parent::generateID('model', $model->name).'" '. 616 self::DEFAULT_CLASSIFIER_ATT.'> 617 <UML:Namespace.ownedElement>'; 618 619 foreach (self::$stereotypes as $item) 620 $str .= '<UML:Stereotype xmi.id="'.parent::generateID('stereotype', $item).'" 621 name="'.$item.'" '.self::DEFAULT_CLASSIFIER_ATT.' />'; 622 623 $str .= '<UML:Stereotype xmi.id="'.parent::generateId('stereotype', 'realize').'" 624 name="realize" '.self::DEFAULT_CLASSIFIER_ATT.'> 625 <UML:Stereotype.baseClass>Abstraction</UML:Stereotype.baseClass> 626 </UML:Stereotype>'; 627 return $str; 628 } 629 630 /** 631 * Formates the opening tag for a package 632 * 633 * @param PHP_UML_Package &$package Package 634 */ 635 public function getPackageOpen(PHP_UML_Metamodel_Package &$package) 636 { 637 return '<UML:Package xmi.id="'.parent::generateID('package', $package->name). 638 '" name="'.$package->name.'"><UML:Namespace.ownedElement>'; 639 } 640 641 /** 642 * Formates the closing tag of a package 643 * 644 */ 645 public function getPackageClose() 646 { 647 return '</UML:Namespace.ownedElement></UML:Package>'; 648 } 649 650 /** 651 * Formates the XMI declaration of the main PHP types (official and unofficial ones) 652 * 653 * @param PHP_UML_Type &$type Datatype 654 */ 655 public function getDatatype(PHP_UML_Metamodel_Type &$type) 656 { 657 return '<UML:DataType xmi.id="'.self::generateID('datatype', $type->name). 658 '" name="'.$type->name.'" visibility="public" isRoot="false" '. 659 ' isLeaf="false" isAbstract="false"/>'; 660 } 661 /*$str .= '<UML:TagDefinition xmi.id="'.parent::generateID('tag','src_path').'" 662 name="src_path" isSpecification="false" tagType="String"> 663 <UML:TagDefinition.multiplicity> 664 <UML:Multiplicity xmi.id="'.parent::generateID('tag','src_path_multi').'"> 665 <UML:Multiplicity.range> 666 <UML:MultiplicityRange xmi.id="'. 667 parent::generateID('tag','src_path_multi_range'). 668 '" lower="0" upper="1" /> 669 </UML:Multiplicity.range> 670 </UML:Multiplicity> 671 </UML:TagDefinition.multiplicity> 672 </UML:TagDefinition>';*/ 673 674 675 /** 676 * Formates the closing tag of an XMI:Model 677 */ 678 public function getModelClose() 679 { 680 return '</UML:Namespace.ownedElement></UML:Model></XMI.content></XMI>'; 681 } 682 683 684 public function getClass(PHP_UML_Metamodel_Class &$class) 685 { 686 $strRealization = ''; 687 $strGeneral = ''; 688 689 $cn = $class->name; 690 $nn = $class->package->name; 691 692 $str = '<UML:Class name="'.$cn.'" xmi.id="'. 693 parent::generateID('class', $nn.'#'.$cn).'" visibility="package" 694 isAbstract="'.($class->isAbstract?'true':'false').'">'; 695 696 $str .= self::_getGeneralizations($class, $strGeneral); 697 $strRealization .= self::_getRealizations($class); 698 699 $str .= '<UML:Classifier.feature>'; 700 701 foreach ($class->ownedAttribute as &$property) { 702 $str .= self::getProperty($property); 703 } 704 705 foreach ($class->ownedOperation as &$operation) 706 $str .= self::getOperation($operation); 707 708 $str .= '</UML:Classifier.feature>'; 709 $str .= '</UML:Class>'; 710 711 return $str.$strGeneral.$strRealization; 712 } 713 714 public function getInterface(PHP_UML_Metamodel_Interface &$interface) 715 { 716 $in = $interface->name; 717 $nn = $interface->package->name; 718 719 $strGeneral = ''; 720 721 $str = '<UML:Interface name="'.$in.'"'. 722 ' xmi.id="'.parent::generateID('class', $nn.'#'.$in).'"'. 723 ' visibility="package" isAbstract="true">'; 724 725 $str .= '<UML:Classifier.feature>'; 726 foreach ($interface->ownedOperation as &$operation) 727 $str .= self::getOperation($operation, $nn, $in); 728 729 $str .= '</UML:Classifier.feature>'; 730 $str .= self::_getGeneralizations($interface, $strGeneral); 731 732 $str .= '</UML:Interface>'; 733 return $str.$strGeneral; 734 } 735 736 static private function _getGeneralizations(PHP_UML_Metamodel_Type &$client, &$general) 737 { 738 $str = ''; 739 $set = $client->superClass; 740 $cn = $client->name; 741 $nn = $client->package->name; 742 743 foreach ($set as &$gclass) { 744 if (!empty($gclass)) { 745 $gcn = $gclass->name; 746 $gnn = $gclass->package->name; 747 $id = parent::generateID( 748 'generalization', $nn.'#'.$cn.'-'.$gnn.'#'.$gcn 749 ); 750 751 $str .= '<UML:GeneralizableElement.generalization> 752 <UML:Generalization xmi.idref="'.$id.'"/> 753 </UML:GeneralizableElement.generalization>'; 754 755 $general .= '<UML:Generalization xmi.id="'.$id.'"> 756 <UML:Generalization.child><UML:Class xmi.idref="'. 757 parent::generateID('class', $nn.'#'.$cn). 758 '" /></UML:Generalization.child> 759 <UML:Generalization.parent><UML:Class xmi.idref="'. 760 parent::generateID('class', $gnn.'#'.$gcn).'"/> 761 </UML:Generalization.parent></UML:Generalization>'; 762 } 763 } 764 return $str; 765 } 766 767 static private function _getRealizations(PHP_UML_Metamodel_Class &$client) 768 { 769 $str = ''; 770 $set = $client->implements; 771 $cn = $client->name; 772 $nn = $client->package->name; 773 774 foreach ($set as &$rclass) { 775 if (!empty($rclass)) { 776 $rcn = $rclass->name; 777 $rnn = $rclass->package->name; 778 $str .= '<UML:Abstraction '. 779 'xmi.id="'.parent::generateID( 780 'realize', $nn.'#'.$cn.'-'.$rnn.'#'.$rcn 781 ).'" isSpecification="false">'. 782 '<UML:ModelElement.stereotype><UML:Stereotype xmi.idref="'. 783 parent::generateID('stereotype', 'realize').'"/>'. 784 '</UML:ModelElement.stereotype>'. 785 '<UML:Dependency.client><UML:Class xmi.idref="'. 786 parent::generateID('class', $nn.'#'.$cn). 787 '"/></UML:Dependency.client>'. 788 '<UML:Dependency.supplier><UML:Interface xmi.idref="'. 789 parent::generateID('class', $rnn.'#'.$rcn).'"/>'. 790 '</UML:Dependency.supplier></UML:Abstraction>'; 791 } 792 } 793 return $str; 794 } 795 796 static public function getProperty(PHP_UML_Metamodel_Property &$property) 797 { 798 $pn = $property->name; 799 $cn = $property->class->name; 800 $nn = $property->class->package->name; 801 $id = parent::generateID('property', $nn.'#'.$cn.'#'.$pn); 802 803 $str = '<UML:Attribute name="'.$pn.'"'. 804 ' xmi.id="'.$id.'"'. 805 ' visibility="'.$property->visibility.'" '; 806 if (!$property->isInstantiable) 807 $str .= ' isStatic="true" ownerScope="classifier" '; 808 else 809 $str .= 'ownerScope="instance" '; 810 if ($property->isReadOnly) 811 $str .= 'changeability="frozen" isReadOnly="true" '; 812 813 $str .= '>'; 814 $str .= self::_getTypeAndDefProp($property, 815 parent::generateID('literal', $nn.'#'.$cn.'#'.$pn.'##dv') 816 ); 817 818 $str .= '</UML:Attribute>'; 819 return $str; 820 } 821 822 /** 823 * Special version of getTypeAndDefault for XMI 1.x 824 * Splits a parameter into its type, name and default value 825 * 826 * @param PHP_UML_TypedElement &$parameter Parameter to split 827 * @param int $id Id of tag Expression 828 */ 829 static private function _getTypeAndDefProp(PHP_UML_Metamodel_TypedElement &$parameter, $id) 830 { 831 $str = ''; 832 if (get_class($parameter->type)=='PHP_UML_Class') { 833 $cn = $parameter->type->name; 834 $nn = $parameter->type->package->name; 835 $str .= '<UML:StructuralFeature.type>'. 836 '<UML:DataType xmi.idref="'.self::generateID( 837 'class', $nn.'#'.$cn 838 ).'"/>'. 839 '</UML:StructuralFeature.type>'; 840 } 841 elseif (get_class($parameter->type)=='PHP_UML_Type') { 842 $cn = $parameter->type->name; 843 $str .= '<UML:StructuralFeature.type>'. 844 '<UML:DataType xmi.idref="'.self::generateID( 845 'datatype', $cn 846 ).'"/>'. 847 '</UML:StructuralFeature.type>'; 848 } 849 if ($parameter->default!='') 850 $str .= '<UML:Attribute.initialValue>'. 851 '<UML:Expression xmi.id="'.$id.'"'. 852 ' body="'.htmlentities($parameter->default, ENT_QUOTES, "UTF-8").'" />'. 853 '</UML:Attribute.initialValue>'; 854 return $str; 855 } 856 857 /* 858 private function _getMethods(Array &$obj, $package, $c) 859 { 860 $str = ''; 861 foreach ($obj as $m => &$v) { 862 $str .= ' 863 <UML:Operation name="'.$m.'" xmi.id="'. 864 parent::generateID('method', $package.'#'.$c.'#'.$m). 865 '" visibility="'.$v['visibility'].'" '; 866 if ($v['static']) 867 $str .= ' isStatic="true"'; 868 if ($v['abstract']) 869 $str .= ' isAbstract="true"'; 870 $str .= ' isQuery="false" concurrency="sequential">'. 871 '<UML:BehavioralFeature.parameter>'; 872 873 $str .= $this->prepareParameters($v, $package, $c, $m); 874 875 $str .= '</UML:BehavioralFeature.parameter></UML:Operation>'; 876 } 877 return $str; 878 }*/ 879 880 static public function getOperation(PHP_UML_Metamodel_Operation &$operation) 881 { 882 $on = $operation->name; 883 $cn = $operation->class->name; 884 $nn = $operation->class->package->name; 885 886 $str = '<UML:Operation name="'.$on.'" xmi.id="'. 887 parent::generateID('method', $nn.'#'.$cn.'#'.$on).'" 888 visibility="'.$operation->visibility.'" '; 889 if (!$operation->isInstantiable) 890 $str .= ' isStatic="true"'; 891 if ($operation->isAbstract) 892 $str .= ' isAbstract="true"'; 893 894 $str .= ' isQuery="false" concurrency="sequential">'. 895 '<UML:BehavioralFeature.parameter>'; 896 897 $i = 0; 898 foreach ($operation->ownedParameter as &$parameter) { 899 $str .= self::getParameter($parameter, $i++); 900 } 901 902 $str .= '</UML:BehavioralFeature.parameter></UML:Operation>'; 903 904 return $str; 905 } 906 907 /* 908 protected function getParameter($p, $c, $m, $name, $type, $kind, $default = '') 909 { 910 $temp = $p.'#'.$c.'#'.$m.'#'.$name; 911 $id = parent::generateID('parameter', $temp); 912 $str = '<UML:Parameter name="'.$name.'" xmi.id="'.$id.'" kind="'.$kind.'">'. 913 '<UML:Parameter.type>'. 914 '<UML:DataType xmi.idref="'.$type.'" />'. 915 '</UML:Parameter.type>'; 916 if ($default != '') { 917 $id = parent::generateID('expression', $temp.'#'.$default); 918 $str .= '<UML:Parameter.defaultValue>'. 919 '<UML:Expression xmi.id="'.$id.'" body="'.$default.'"/>'. 920 '</UML:Parameter.defaultValue>'; 921 } 922 $str .= '</UML:Parameter>'; 923 return $str; 924 }*/ 925 926 static public function getParameter(PHP_UML_Metamodel_Parameter &$parameter, $order = 0) 927 { 928 $pn = $parameter->name; 929 $on = $parameter->operation->name; 930 $cn = $parameter->operation->class->name; 931 $nn = $parameter->operation->class->package->name; 932 $id = parent::generateID('parameter', $nn.'#'.$cn.'#'.$on.'#'.$order); 933 934 $str = '<UML:Parameter name="'.$pn.'" xmi.id="'.$id.'" '. 935 'kind="'.$parameter->direction.'">'; 936 $str .= self::_getTypeAndDefault($parameter, 937 parent::generateID('literal', $nn.'#'.$cn.'#'.$on.'#'.$pn.'##dv') 938 ); 939 940 $str .= '</UML:Parameter>'; 941 942 return $str; 943 } 944 945 static private function _getTypeAndDefault(PHP_UML_Metamodel_TypedElement &$parameter, $id) 946 { 947 // Exception to MOF : a PHP class can have the name of a datatype 948 $str = ''; 949 if (get_class($parameter->type)=='PHP_UML_Metamodel_Class') { 950 $cn = $parameter->type->name; 951 $nn = $parameter->type->package->name; 952 $str .= '<UML:Parameter.type>'. 953 '<UML:DataType xmi.idref="'.self::generateID( 954 'class', $nn.'#'.$cn 955 ).'"/>'. 956 '</UML:Parameter.type>'; 957 } 958 elseif (get_class($parameter->type)=='PHP_UML_Metamodel_Type') { 959 $cn = $parameter->type->name; 960 $str .= '<UML:Parameter.type>'. 961 '<UML:DataType xmi.idref="'.self::generateID( 962 'datatype', $cn 963 ).'"/>'. 964 '</UML:Parameter.type>'; 965 } 966 if ($parameter->default!='') 967 $str .= '<UML:Parameter.defaultValue>'. 968 '<UML:Expression xmi.id="'.$id.'"'. 969 ' body="'.htmlentities($parameter->default, ENT_QUOTES, "UTF-8").'" />'. 970 '</UML:Parameter.defaultValue>'; 971 return $str; 972 } 973 974 /* 975 static private function _getTaggedValue($id, $value, $id_type) 976 { 977 return '<UML:ModelElement.taggedValue><UML:TaggedValue xmi.id="'.$id.'">'. 978 '<UML:TaggedValue.dataValue>'.$value.'</UML:TaggedValue.dataValue>'. 979 '<UML:TaggedValue.type><UML:TagDefinition xmi.idref="'.$id_type.'" />'. 980 '</UML:TaggedValue.type>'. 981 '</UML:TaggedValue></UML:ModelElement.taggedValue>'; 982 } 983 */ 984 985 /** 986 * Gets the XMI code of the artifacts in a given package 987 * 988 * @param array $files_list List of files to map to artifacts 989 * @param string $package Package to retrieve (for ID generation) 990 * 991 * @return string XMI Code 992 */ 993 public function getArtifacts(Array $files_list, $package) 994 { 995 $str = ''; 996 foreach ($files_list as $name) 997 $str .= '<UML:Artifact xmi.id="'. 998 parent::generateID('artifact', $package.'#'.$name).'" name="'.$name.'"> 999 <UML:ModelElement.stereotype> 1000 <UML:Stereotype xmi.idref="'.parent::generateID('stereotype', self::PHP_FILE).'"/> 1001 </UML:ModelElement.stereotype> 1002 </UML:Artifact>'; 1003 1004 return $str; 1005 } 1006 1007 /** 1008 * Formates the XMI code for a subsystem 1009 * 1010 * @param string $name Name of the subsystem 1011 * @param string $id Identifier (optional) 1012 * 1013 * @return string XMI Code 1014 */ 1015 public function getSubsystemOpen($name, $id = null) 1016 { 1017 $str = '<UML:Subsystem name="'.$name.'" xmi.id="'. 1018 (is_null($id) ? parent::generateID('subsystem', $name) : $id). 1019 '" isInstantiable="false"><UML:Namespace.ownedElement>'; 1020 return $str; 1021 } 1022 1023 /* 1024 * Formates the closing tag of a subsystem 1025 */ 1026 public function getSubsystemClose() 1027 { 1028 return '</UML:Namespace.ownedElement></UML:Subsystem>'; 1029 } 1030 1031 /** 1032 * Formates the XMI for a component 1033 * 1034 * @param string $name Name of the component. 1035 * @param string $id Identifier (optional) 1036 * @param string $stereotype Stereotype 1037 * 1038 * @return string XMI code 1039 */ 1040 public function getComponent($name, $id = null, $stereotype = '') 1041 { 1042 return '<UML:Component xmi.id="'. 1043 (is_null($id) ? parent::generateID('component', $name) : $id). 1044 '" name="'.$name.'" '. 1045 self::DEFAULT_CLASSIFIER_ATT.' stereotype="'.$stereotype.'">'. 1046 '</UML:Component>'; 1047 } 1048 1049 public function getProfile() 1050 { 1051 } 1052} 1053 1054 1055/** 1056 * Implementation class to create XMI in version 2. See version 1 for explanations. 1057 * 1058 * 1059 */ 1060class PHP_UML_XMI_Factory2 extends PHP_UML_XMI_Factory 1061{ 1062 const XMI_VERSION = '2.1'; 1063 const UML_VERSION = '2.1.2'; 1064 1065 const DEFAULT_CLASSIFIER_ATT = ' visibility="public" isAbstract="false" '; 1066 1067 /** 1068 * PHP_UML UML Profile (TODO) 1069 * @var string 1070 */ 1071 public $profile = ''; 1072 1073 public function getModelOpen(PHP_UML_Metamodel_Package &$model) 1074 { 1075 return '<xmi:XMI xmi:version="'.self::XMI_VERSION.'" 1076 xmlns:uml="http://schema.omg.org/spec/UML/'.self::UML_VERSION.'" 1077 xmlns:xmi="http://schema.omg.org/spec/XMI/'.self::XMI_VERSION.'"> 1078 <xmi:Documentation exporter="'.self::EXPORTER_NAME.'" 1079 exporterVersion="0.2" /> 1080 <uml:Model xmi:type="uml:Model" name="'.$model->name.'" 1081 xmi:id="'.parent::generateID('model', $model->name).'" '. 1082 self::DEFAULT_CLASSIFIER_ATT.'>'; 1083 } 1084 1085 public function getPackageOpen(PHP_UML_Metamodel_Package &$package) 1086 { 1087 return '<packagedElement xmi:type="uml:Package" xmi:id="'. 1088 parent::generateID('package', $package->name). 1089 '" name="'.$package->name.'">'; 1090 } 1091 1092 public function getPackageClose() 1093 { 1094 return '</packagedElement>'; 1095 } 1096 1097 public function getDatatype(PHP_UML_Metamodel_Type &$type) 1098 { 1099 return '<packagedElement xmi:type="uml:DataType"'. 1100 ' xmi:id="'.self::generateID('datatype', $type->name).'"'. 1101 ' name="'.$type->name.'" />'; 1102 } 1103 1104 public function getModelClose() 1105 { 1106 return '</uml:Model></xmi:XMI>'; 1107 } 1108 1109 public function getClass(PHP_UML_Metamodel_Class &$class) 1110 { 1111 $strRealization = ''; 1112 1113 $cn = $class->name; 1114 $nn = $class->package->name; 1115 $str = '<packagedElement xmi:type="uml:Class" name="'.$cn.'" xmi:id="'. 1116 parent::generateID('class', $nn.'#'.$cn).'" visibility="package" 1117 isAbstract="'.($class->isAbstract?'true':'false').'">'; 1118 1119 $str .= self::_getGeneralizations($class); 1120 1121 $strRealization .= self::_getRealizations($class); 1122 1123 foreach ($class->ownedAttribute as &$property) 1124 $str .= self::getProperty($property); 1125 1126 foreach ($class->ownedOperation as &$operation) 1127 $str .= self::getOperation($operation); 1128 1129 /* 1130 if ($tagFilename) 1131 $str .= $this->getComment( 1132 parent::generateID('comment', $nn.'#'.$cn), 'src_path', $class->file->name 1133 );*/ 1134 1135 $str .= '</packagedElement>'; 1136 1137 return $str.$strRealization; 1138 } 1139 1140 public function getInterface(PHP_UML_Metamodel_Interface &$interface) 1141 { 1142 $in = $interface->name; 1143 $nn = $interface->package->name; 1144 $str = '<packagedElement xmi:type="uml:Interface" name="'.$in.'"'. 1145 ' xmi:id="'.parent::generateID('class', $nn.'#'.$in).'"'. 1146 ' visibility="package" isAbstract="true">'; 1147 1148 foreach ($interface->ownedOperation as &$operation) 1149 $str .= self::getOperation($operation, $nn, $in); 1150 1151 $str .= self::_getGeneralizations($interface); 1152 1153 /* 1154 if ($tagFilename) 1155 $str .= $this->getComment( 1156 parent::generateID('comment', $nn.'#'.$in), 1157 'src_path', $interface->file->name 1158 );*/ 1159 1160 $str .= '</packagedElement>'; 1161 return $str; 1162 } 1163 1164 static private function _getRealizations(PHP_UML_Metamodel_Class &$client) 1165 { 1166 $str = ''; 1167 $set = $client->implements; 1168 $cn = $client->name; 1169 $nn = $client->package->name; 1170 1171 foreach ($set as &$rclass) { 1172 if (!empty($rclass)) { 1173 $rcn = $rclass->name; 1174 $rnn = $rclass->package->name; 1175 $str .= '<packagedElement xmi:type="uml:Realization" '. 1176 'xmi:id="'.parent::generateID('realize', $nn.'#'.$cn.'-'.$rnn.'#'.$rcn).'" '. 1177 'client="'.parent::generateID('class', $nn.'#'.$cn).'" '. 1178 'supplier="'.parent::generateID('class', $rnn.'#'.$rcn).'" '. 1179 'realizingClassifier="'.parent::generateID('class', $rnn.'#'.$rcn).'"/>'; 1180 } 1181 } 1182 return $str; 1183 } 1184 1185 static private function _getGeneralizations(PHP_UML_Metamodel_Type &$client) 1186 { 1187 $str = ''; 1188 $set = $client->superClass; 1189 $cn = $client->name; 1190 $nn = $client->package->name; 1191 1192 foreach ($set as &$gclass) { 1193 if (!empty($gclass)) { 1194 $gcn = $gclass->name; 1195 $gnn = $gclass->package->name; 1196 $str .= '<generalization xmi:type="uml:Generalization" '. 1197 'xmi:id="'.parent::generateID('generalization', $nn.'#'.$cn.'-'.$gnn.'#'.$gcn).'"'. 1198 ' general="'.parent::generateID('class', $gnn.'#'.$gcn).'"/> '; 1199 } 1200 } 1201 return $str; 1202 } 1203 1204 static public function getProperty(PHP_UML_Metamodel_Property &$property) 1205 { 1206 $pn = $property->name; 1207 $cn = $property->class->name; 1208 $nn = $property->class->package->name; 1209 $id = parent::generateID('property', $nn.'#'.$cn.'#'.$pn); 1210 1211 $str = '<ownedAttribute xmi:type="uml:Property"'. 1212 ' name="'.$pn.'"'. 1213 ' xmi:id="'.$id.'"'. 1214 ' visibility="'.$property->visibility.'" '; 1215 if (!$property->isInstantiable) 1216 $str .= ' isStatic="true"'; 1217 if ($property->isReadOnly) 1218 $str .= ' isReadOnly="true" '; 1219 1220 $str .= '>'; 1221 $str .= self::_getTypeAndDefault($property, 1222 parent::generateID('literal', $nn.'#'.$cn.'#'.$pn.'##dv') 1223 ); 1224 1225 $str .= '</ownedAttribute>'; 1226 return $str; 1227 } 1228 1229 static public function getOperation(PHP_UML_Metamodel_Operation &$operation) 1230 { 1231 $on = $operation->name; 1232 $cn = $operation->class->name; 1233 $nn = $operation->class->package->name; 1234 1235 $str = '<ownedOperation name="'.$on.'" xmi:id="'. 1236 parent::generateID('method', $nn.'#'.$cn.'#'.$on).'" 1237 visibility="'.$operation->visibility.'" '; 1238 if (!$operation->isInstantiable) 1239 $str .= ' isStatic="true"'; 1240 if ($operation->isAbstract) 1241 $str .= ' isAbstract="true"'; 1242 $str .= '>'; 1243 1244 $i = 0; 1245 foreach ($operation->ownedParameter as &$parameter) 1246 $str .= self::getParameter($parameter, $i++); 1247 1248 $str .= '</ownedOperation>'; 1249 1250 return $str; 1251 } 1252 1253 static public function getParameter(PHP_UML_Metamodel_Parameter &$parameter, $order = 0) 1254 { 1255 $pn = $parameter->name; 1256 $on = $parameter->operation->name; 1257 $cn = $parameter->operation->class->name; 1258 $nn = $parameter->operation->class->package->name; 1259 $id = parent::generateID('parameter', $nn.'#'.$cn.'#'.$on.'#'.$pn.$order); 1260 $str = '<ownedParameter name="'.$pn.'" xmi:id="'.$id.'" '. 1261 'direction="'.$parameter->direction.'">'; 1262 $str .= self::_getTypeAndDefault($parameter, 1263 parent::generateID('literal', $nn.'#'.$cn.'#'.$on.'#'.$pn.'##dv') 1264 ); 1265 $str .= '</ownedParameter>'; 1266 1267 return $str; 1268 } 1269 1270 static private function _getTypeAndDefault(PHP_UML_Metamodel_TypedElement &$parameter, $id) 1271 { 1272 // Exception to MOF : a PHP class can have the name of a datatype 1273 $str = ''; 1274 if (get_class($parameter->type)=='PHP_UML_Metamodel_Class') { 1275 $cn = $parameter->type->name; 1276 $nn = $parameter->type->package->name; 1277 $str .= '<type xmi:idref="'.self::generateID('class', $nn.'#'.$cn).'"/>'; 1278 } 1279 elseif (get_class($parameter->type)=='PHP_UML_Metamodel_Type') { 1280 $cn = $parameter->type->name; 1281 $str .= '<type xmi:idref="'.self::generateID('datatype', $cn).'"/>'; 1282 } 1283 if ($parameter->default!='') 1284 $str .= '<defaultValue xmi:type="uml:LiteralString" xmi:id="'.$id.'"'. 1285 ' value="'.htmlentities($parameter->default, ENT_QUOTES, "UTF-8").'" />'; 1286 //htmlentities($parameter->default, ENT_QUOTES, "UTF-8") 1287 return $str; 1288 } 1289 1290 public function getComment($id, $name, $body) 1291 { 1292 $body = ''; 1293 return '<ownedComment xmi:type="uml:Comment" 1294 xmi:id="'.$id.'" name="'.$name.'" body="'.$body.'"/>'; 1295 } 1296 1297 public function getArtifacts(Array &$obj = array(), $package = '') 1298 { 1299 $files_list = parent::getFilesInPackage($obj); 1300 $str = ''; 1301 foreach ($files_list as $name) 1302 $str .= '<packagedElement xmi:type="uml:Artifact"'. 1303 ' xmi:id="'.parent::generateID('artifact', $package.'#'.$name).'"'. 1304 ' name="'.$name.'"'. 1305 ' stereotype="'.parent::generateID('stereotype', self::PHP_FILE).'">'. 1306 '</packagedElement>'; 1307 return $str; 1308 } 1309 1310 public function getSubsystemOpen($name, $id = null) 1311 { 1312 return '<packagedElement xmi:type="uml:Component" xmi:id="'. 1313 (is_null($id) ? parent::generateID('subsystem', $name) : $id). 1314 '" name="'.$name.'" '.self::DEFAULT_CLASSIFIER_ATT.'>'; 1315 } 1316 1317 public function getSubsystemClose() 1318 { 1319 return '</packagedElement>'; 1320 } 1321 1322 public function getComponent($name, $id = null) 1323 { 1324 return '<packagedElement xmi:type="uml:Component" xmi:id="'. 1325 (is_null($id) ? parent::generateID('component', $name) : $id). 1326 '" name="'.$name.'" '.self::DEFAULT_CLASSIFIER_ATT.' />'; 1327 } 1328 1329 /** 1330 * Formates a Profile adapted to PHP_UML. 1331 * 1332 * TODO. Experimental. 1333 * 1334 * @return string 1335 */ 1336 public function getProfile() 1337 { 1338 $str = ' 1339 <uml:Profile xmi:version="'.self::XMI_VERSION.'" 1340 nsURI="http://PHP_UML" nsPrefix="PHP_UML" 1341 xmlns:uml="http://schema.omg.org/spec/UML/'.self::UML_VERSION.'/uml.xml" 1342 xmlns:xmi="http://schema.omg.org/spec/XMI/'.self::XMI_VERSION.'" 1343 xmi:id="'.parent::generateID('profile', 'PHP_UML').'" name="PHP_UML" 1344 metamodelReference="PHP_UML_Metamodel"> 1345 <packageImport xmi:id="PHP_UML_Metamodel"> 1346 <importedPackage href="http://schema.omg.org/spec/UML/'.self::UML_VERSION.'/uml.xml"/> 1347 </packageImport> 1348 <ownedMember xmi:type="uml:Stereotype" xmi:id="'. 1349 parent::generateID('stereotype', self::PHP_FILE).'" name="'.self::PHP_FILE.'" '. 1350 self::DEFAULT_CLASSIFIER_ATT.' /> 1351 </uml:Profile>'; 1352 return $str; 1353 } 1354 1355} 1356 1357/** 1358 * A combination of string iteration and regular expressions. 1359 * It stores all the elements if finds in MOF program elements : 1360 * $packages, $interfaces, $classes, $functions, $parameters 1361 * 1362 * Most navigabilities between associated elements are bidirectional 1363 * (the packages know their owned elements, and the classes know their 1364 * nesting package) 1365 * At first, relations use string references (the name of the element). 1366 * Once the parsing is completed, the method finalize() must be called, 1367 * so that the named references be replaced by PHP references (&$xxx). 1368 * 1369 * 1370 */ 1371class PHP_UML_PHP_Parser 1372{ 1373 /** 1374 * Regular expressions for a PHP variable 1375 */ 1376 const PREG_VARIABLE = '[a-z_\\x7f-\\xff][a-z0-9_\\x7f-\\xff]*'; 1377 const PREG_HEREDOC = '<<<([^<\n\r]*)[\n\r]'; 1378 const PREG_COMMENT = '\/\/[^\n]*\n|\/\*.*\*\/|#[^\n]*\n'; 1379 const PREG_PACKAGE = '\*[ \t]+@package[ \t]+([^\s]+)\s'; 1380 1381 /** 1382 * Reference to a PHP_UML_Metamodel_Superstructure 1383 * (where the parser stores all the program elements it finds) 1384 * 1385 * @var PHP_UML_Metamodel_Superstructure 1386 */ 1387 public $model; 1388 1389 private $_text = ''; 1390 private $_filename; 1391 private $_docblocks; 1392 private $_dollar; 1393 private $_cancel; 1394 1395 private $_packageSeqIdx; // current pkg index 1396 1397 /** 1398 * Constructor 1399 * 1400 * @param string $root Root package name 1401 * @param bool $docblocks True = docblocks are scanned 1402 * @param bool $dollar True = $ in variables is kept 1403 */ 1404 public function __construct($root, $docblocks = true, $dollar = true) 1405 { 1406 $this->_docblocks = $docblocks; 1407 $this->_dollar = $dollar; 1408 1409 $this->model = new PHP_UML_Metamodel_Superstructure(); 1410 $this->_packageSeqId = $this->_addPackage($root); 1411 } 1412 1413 /** 1414 * Parses a PHP file 1415 * 1416 * @param string $filename File to parse 1417 */ 1418 public function parse($filename) 1419 { 1420 if (file_exists($filename)) { 1421 $this->_text = file_get_contents($filename); 1422 $this->_filename = $filename; 1423 } 1424 else 1425 throw new PHP_UML_Exception('File '.$filename.' does not exist.'); 1426 1427 $f = new PHP_UML_Metamodel_File; 1428 $f->name = $filename; 1429 $this->model->files->add($f); 1430 1431 $set = array(); 1432 $lenText = strlen($this->_text); 1433 $modePHP = false; 1434 $modeQuotesD = false; // double quote 1435 $modeQuotesS = false; // single quote 1436 $modeHeredoc = false; 1437 $modeQuotes = $modeQuotesD || $modeQuotesS || $modeHeredoc; 1438 $heredoc = ''; 1439 $attributes = array(); // a collector for attributes (public, static..) 1440 $clasName = ''; 1441 $clasType = ''; 1442 $propName = ''; 1443 $funcName = ''; 1444 1445 $lastCsLevel = 0; // braces level at which current class is defined 1446 $lastFnLevel = 0; // braces level at which current function is defined 1447 $lastFnPos = 0; // character position of last visited function 1448 $lastPrPos = 0; // character position of last visited prop. default. value 1449 $lastDocblock = ''; 1450 1451 $modeClass = false; 1452 $modeFunction = false; 1453 $modeInterface = false; 1454 $modeProperty = false; 1455 $modeExpression = false; 1456 1457 $i = 0; 1458 $level = 0; // curly braces level 1459 $levelPar = 0; // parens level 1460 1461 $this->_packageSeqIdx = 0; 1462 if ($this->_docblocks) { 1463 // First, let's have a look at the file docblock : 1464 $package = $this->_getFilePackage(); 1465 if ($package!='') 1466 $this->_packageSeqIdx = $this->_addPackage($package, 0); 1467 } 1468 1469 while($i<$lenText) { 1470 $one = substr($this->_text, $i, 1); 1471 $two = substr($this->_text, $i, 2); 1472 $remaining = substr($this->_text, $i); 1473 1474 if ((!$modePHP)) { 1475 if ($two=='<?') { 1476 $modePHP = true; 1477 $i += 2; 1478 } 1479 else { 1480 $nxt = strpos($this->_text, '<?', $i); 1481 if ($nxt===false) 1482 $i = $lenText; 1483 else 1484 $i = $nxt; 1485 } 1486 } 1487 else { 1488 if ((!$modeQuotes) && $two=='?>') { 1489 $modePHP = false; 1490 $i += 2; 1491 } 1492 elseif ((!$modeQuotes) && $two=='/*') { 1493 $nxt = strpos($this->_text, '*/', $i+2); 1494 if ($nxt===false) 1495 $i = $lenText; 1496 else 1497 $i = ($nxt+2); 1498 } 1499 elseif ((!$modeQuotes) && ($two=='//' || $one=='#')) { 1500 $nxt = preg_match( 1501 '/(\n|\?>)/', $this->_text, $set, PREG_OFFSET_CAPTURE, $i 1502 ); 1503 if ($nxt==0) 1504 $i = $lenText; 1505 else 1506 $i = $set[1][1]; 1507 } 1508 elseif ($modeQuotes && $two=='\\\\') { 1509 $i += 2; 1510 } 1511 elseif ($modeQuotesD && $two=='\"') { 1512 $i += 2; 1513 } 1514 elseif ($modeQuotesS && $two=='\\\'') { 1515 $i += 2; 1516 } 1517 elseif ((!$modeQuotes) 1518 && preg_match('/^'.self::PREG_HEREDOC.'/s', $remaining, $set)>0 1519 ) { 1520 $heredoc = trim($set[1]); 1521 $modeHeredoc = true; 1522 $modeQuotes = $modeQuotesD || $modeQuotesS || $modeHeredoc; 1523 $i += strlen($set[0]); 1524 } 1525 elseif ($modeHeredoc 1526 && (preg_match('/^'.$heredoc.'/s', $remaining, $set)>0) 1527 ) { 1528 $heredoc = ''; 1529 $modeHeredoc = false; 1530 $modeQuotes = $modeQuotesD || $modeQuotesS || $modeHeredoc; 1531 $i += strlen($set[0]); 1532 } 1533 elseif ((!($modeQuotesS || $modeHeredoc)) && $this->_text[$i]=='"') { 1534 $modeQuotesD = (!$modeQuotesD); 1535 $modeQuotes = $modeQuotesD || $modeQuotesS || $modeHeredoc; 1536 $i++; 1537 } 1538 elseif ((!($modeQuotesD || $modeHeredoc)) && $this->_text[$i]=="'") { 1539 $modeQuotesS = (!$modeQuotesS); 1540 $modeQuotes = $modeQuotesD || $modeQuotesS || $modeHeredoc; 1541 $i++; 1542 } 1543 elseif ((!$modeQuotes)) { 1544 if ($one=='{') { 1545 if ($modeClass && $clasName!='') { 1546 $idxNs = $this->_getClassDocIdx($lastDocblock); 1547 if ($modeInterface) 1548 $this->_addInterface($clasName, $attributes, $idxNs); 1549 else 1550 $this->_addClass($clasName, $attributes, $idxNs); 1551 // Classes are not always defined at 1st level : 1552 $lastCsLevel = $level+1; 1553 $attributes = array(); 1554 $clasName = ''; 1555 $lastDocblock = ''; 1556 } 1557 $i++; 1558 $level++; 1559 1560 } 1561 elseif ($one=='}') { 1562 if ($modeClass && $level == $lastCsLevel) 1563 $modeClass = false; 1564 $level--; 1565 $i++; 1566 } 1567 elseif ($one=='(') { 1568 $levelPar++; 1569 $i++; 1570 } 1571 elseif ($one==')') { 1572 if ($modeFunction && $levelPar==1 && !$this->_cancel) { 1573 $this->_addOperation( 1574 $funcName, $attributes, $modeInterface 1575 ); 1576 $str = substr($this->_text, $lastFnPos, $i-$lastFnPos); 1577 if ($str!='') 1578 $this->_addParameters($str, $lastDocblock); 1579 $attributes = array(); 1580 $propName = ''; 1581 $modeFunction = false; 1582 $lastDocblock = ''; 1583 } 1584 $levelPar--; 1585 $i++; 1586 } 1587 elseif ($this->_findNamespace($remaining, $set)>0) { 1588 $this->_packageSeqIdx = $this->_addPackage($set[1], 0); 1589 $i += strlen($set[0]); 1590 } 1591 elseif ($this->_findAttr($remaining, $set)>0) { 1592 $attributes[] = strtolower($set[1]); 1593 $lastDocblock .= $this->_revDocblock( 1594 substr($this->_text, 0, $i) 1595 ); 1596 $i += strlen($set[0]); 1597 } 1598 elseif ($this->_findClass($remaining, $set)>0) { 1599 // Class / Interface : 1600 $modeClass = true; 1601 $clasName = trim($set[2]); 1602 $modeInterface = (strtolower($set[1])=='interface'); 1603 $lastDocblock .= $this->_revDocblock( 1604 substr($this->_text, 0, $i) 1605 ); 1606 $i += strlen($set[0]); 1607 } 1608 elseif ($modeClass && $clasName!='' 1609 && $this->_findClassRelation($remaining, $set)>0) { 1610 // Superclass : 1611 $attributes[$set[1]] = $set[2]; 1612 $i += strlen($set[0]); 1613 } 1614 elseif ($modeClass && $level==$lastCsLevel 1615 && $this->_findFunction($remaining, $set)>0 1616 && !$this->_cancel) { 1617 $lastDocblock .= $this->_revDocblock( 1618 substr($this->_text, 0, $i) 1619 ); 1620 $funcName = $set[1]; 1621 $modeFunction = true; 1622 $levelPar = 1; 1623 $i += strlen($set[0]); 1624 $lastFnPos = $i; 1625 $lastFnLevel = $level; 1626 } 1627 elseif ($modeClass && (!$modeFunction) && $level==$lastCsLevel 1628 && (!$modeExpression) 1629 && $this->_findProperty($remaining, $set)>0) { 1630 $i += strlen($set[0]); 1631 $lastPrPos = $i; 1632 $propName = $set[1]; 1633 $modeProperty = true; 1634 if (isset($set[2]) && $set[2]=='=') 1635 $modeExpression = true; 1636 } 1637 elseif ($modeProperty && $one==';' && !$this->_cancel) { 1638 $default = $this->_stripComments( 1639 trim(substr($this->_text, $lastPrPos, $i-$lastPrPos)) 1640 ); 1641 $this->_addProperty($propName, $attributes, $default); 1642 $modeProperty = false; 1643 $modeExpression = false; 1644 $attributes = array(); 1645 $lastDocblock = ''; 1646 $propName = ''; 1647 $i++; 1648 } 1649 else 1650 $i++; 1651 } 1652 else 1653 $i++; 1654 } 1655 } 1656 } 1657 1658 /** 1659 * Launches the resolution of the references for all stacks from root 1660 * 1661 * Every reference (a temporary string) is replaced by a PHP reference 1662 * to the corresponding type (that is, a class or a datatype) 1663 * To be run once the filesystem scan is complete. 1664 * 1665 */ 1666 public function finalize() 1667 { 1668 $oDef = &$this->model->packages->get(0); 1669 $this->_resolveReferences($oDef, $oDef); 1670 } 1671 1672 1673 /** 1674 * Adds a package to the "packages" stack 1675 * 1676 * @param string $name Name of the package 1677 * @param int $baseIdx Current nesting package index 1678 * 1679 * @return int Index of the newly created package (or of the existing one) 1680 */ 1681 private function _addPackage($name, $baseIdx = null) 1682 { 1683 $index = $this->model->packages->searchElement($name); 1684 if ($index===false) { 1685 $p = new PHP_UML_Metamodel_Package; 1686 $p->name = $name; 1687 $p->nestingPackage = $baseIdx; 1688 $p->nestedPackage = array(); 1689 $this->model->packages->add($p); 1690 $index = $this->model->packages->key(); 1691 if (!is_null($baseIdx)) { 1692 $parent = $this->model->packages->get($baseIdx); 1693 $parent->nestedPackage[] = $index; 1694 } 1695 } 1696 return $index; 1697 } 1698 1699 /** 1700 * Get the index of the corresponding @package class docblock 1701 * 1702 * @param string $classDocblock Preceding code (before the class declaration) 1703 * 1704 * @return int 1705 */ 1706 private function _getClassDocIdx($classDocblock) 1707 { 1708 // Where's the class package ? 1709 if ($this->_docblocks) { 1710 // Is there a @package in the class docblock ? 1711 $r = $this->_findPackageInDocblock($classDocblock, $set); 1712 if ($r) { 1713 return $this->_addPackage($set[1], $this->_packageSeqIdx); 1714 } 1715 } 1716 return $this->_packageSeqIdx; 1717 } 1718 1719 /** 1720 * Adds an interface to the "interfaces" stack 1721 * 1722 * @param string $name Interface name 1723 * @param array $attr Some interface attributes (superclasses) 1724 * @param int $classPkgIndex The index of the current nesting package 1725 * 1726 */ 1727 private function _addInterface($name, $attr, $classPkgIndex) 1728 { 1729 $c = new PHP_UML_Metamodel_Interface; 1730 1731 if (isset($attr['extends'])) { 1732 $c->superClass[] = trim($attr['extends']); 1733 } 1734 $c->name = $name; 1735 $c->isAbstract = in_array('abstract', $attr); 1736 $c->file = &$this->model->files->current(); 1737 $nestingPkg = $this->model->packages->get($classPkgIndex); 1738 $c->package = &$nestingPkg; 1739 if ($this->_searchIntoPackage($c->package, $c->name)===false) { 1740 $nestingPkg->ownedType[] = &$c; 1741 $c->implements = null; 1742 $this->model->interfaces->add($c); 1743 } 1744 else 1745 PHP_UML_Warning::add( 1746 'Interface '.$c->name.' already defined in '.$this->_filename 1747 ); 1748 } 1749 1750 /** 1751 * Adds a class to the "classes" stack ($this->model->classes) 1752 * 1753 * @param string $name Class name 1754 * @param array $attr Some class attributes (superclasses) 1755 * @param int $classPkgIndex The index of the current nesting package 1756 */ 1757 private function _addClass($name, $attr, $classPkgIndex) 1758 { 1759 $c = new PHP_UML_Metamodel_Class; 1760 1761 if (isset($attr['extends'])) { 1762 $c->superClass[] = trim($attr['extends']); 1763 } 1764 if (isset($attr['implements'])) { 1765 $imp = explode(',', $attr['implements']); 1766 foreach ($imp as $item) { 1767 $c->implements[] = trim($item); 1768 } 1769 } 1770 $c->name = $name; 1771 $c->isAbstract = in_array('abstract', $attr); 1772 $c->file = &$this->model->files->current(); 1773 $nestingPkg = $this->model->packages->get($classPkgIndex); 1774 $c->package = &$nestingPkg; 1775 if ($this->_searchIntoPackage($c->package, $c->name)===false) { 1776 $nestingPkg->ownedType[] = &$c; 1777 $this->model->classes->add($c); 1778 $this->_cancel = false; 1779 } 1780 else { 1781 PHP_UML_Warning::add( 1782 'Class '.$c->name.' already defined in '.$this->_filename 1783 ); 1784 $this->_cancel = true; 1785 } 1786 } 1787 1788 private function _addOperation($name, $attr, $modeInterface) 1789 { 1790 $f = new PHP_UML_Metamodel_Operation; 1791 1792 $f->name = trim(str_replace('&', '', $name)); 1793 $f->isInstantiable = !in_array('static', $attr); 1794 $f->isAbstract = in_array('abstract', $attr); 1795 $f->visibility = self::_getVisibility($attr); 1796 $this->model->functions->add($f); 1797 $obj = null; 1798 if ($modeInterface) 1799 $obj = &$this->model->interfaces->current(); 1800 else 1801 $obj = &$this->model->classes->current(); 1802 $obj->ownedOperation[] = &$f; 1803 $f->class = &$obj; 1804 } 1805 1806 private function _addProperty($name, $attr, $default) 1807 { 1808 $p = new PHP_UML_Metamodel_Property; 1809 1810 $p->name = $name; 1811 $p->isReadOnly = in_array('const', $attr); 1812 $p->isInstantiable = !(in_array('static', $attr) || $p->isReadOnly); 1813 $p->visibility = self::_getVisibility($attr); 1814 $p->default = self::_stripComments($default); 1815 $p->type = self::_guessType($p->default); 1816 1817 $class = &$this->model->classes->current(); 1818 $class->ownedAttribute[] = &$p; 1819 $p->class = &$class; 1820 } 1821 1822 private function _addParameters($set, $docblock) 1823 { 1824 $function = &$this->model->functions->current(); 1825 1826 $docblockParameter = array(); 1827 if ($this->_docblocks) { 1828 $set_comment = $this->_findParamInDocblock($docblock); 1829 $return = false; 1830 foreach ($set_comment as $k) { 1831 if (substr($k[1], 0, 6)=='return' && !$return) { 1832 $pr = new PHP_UML_Metamodel_Parameter; 1833 $pr->name = 'return'; 1834 $pr->direction = 'return'; 1835 $pr->type = $k[2]; 1836 $pr->operation = &$function; 1837 $function->ownedParameter[] = &$pr; 1838 $return = true; 1839 } 1840 elseif ($k[1]=='param') { 1841 $docblockParameter[self::_cleanVariable($k[3])] = $k[2]; 1842 } 1843 } 1844 } 1845 $arr = explode(',', $set); 1846 foreach ($arr as &$parameters) { 1847 $parametre = explode('=', $parameters); 1848 $parameterName = $this->_cleanParameter($parametre[0]); 1849 1850 // Any default value given ? 1851 if (count($parametre)>1) 1852 $default = $this->_cleanParameter($parametre[1]); 1853 else 1854 $default = ''; 1855 // Any @param in the method docblock ? 1856 $tmpParameterName = self::_cleanVariable($parameterName); 1857 if (isset($docblockParameter[$tmpParameterName])) 1858 $param = $docblockParameter[$tmpParameterName]; 1859 else 1860 $param = ''; 1861 // By ref or by value ? (inout/in) 1862 if (strpos($parameterName, '&')===false) 1863 $direction = 'in'; 1864 else 1865 $direction = 'inout'; 1866 1867 list($name, $type) = self::_splitNameType($parameterName, $default, $param); 1868 1869 $p = new PHP_UML_Metamodel_Parameter; 1870 $p->name = $name; 1871 $p->default = $default; 1872 $p->type = $type; 1873 $p->direction = $direction; 1874 $p->operation = &$function; 1875 $this->model->parameters->add($p); 1876 $function->ownedParameter[] = $p; 1877 } 1878 } 1879 1880 private function _findClass(&$text, &$set) 1881 { 1882 return preg_match( 1883 '/^\s+(class|interface)\s+('.self::PREG_VARIABLE.')/si', $text, $set 1884 ); 1885 } 1886 1887 private function _findClassRelation(&$text, &$set) 1888 { 1889 $r = preg_match( 1890 '/^\s+(implements)\s+(('.self::PREG_VARIABLE.'[ \t,]*)+)?/si', $text, $set 1891 ); 1892 if ($r==0) { 1893 $r = preg_match( 1894 '/^\s+(extends)\s+('.self::PREG_VARIABLE.')?/si', $text, $set 1895 ); 1896 if ($r>0) 1897 $set = array($set[0], 'extends', $set[2]); 1898 } 1899 else 1900 $set = array($set[0], 'implements', $set[2]); 1901 return $r; 1902 } 1903 1904 private function _findNamespace(&$text, &$set) 1905 { 1906 return preg_match('/^namespace\s+('. 1907 self::PREG_VARIABLE.')[ \t]*;/si', $text, $set 1908 ); 1909 } 1910 1911 private function _findPackageInDocblock($text, &$set) 1912 { 1913 return preg_match('/'.self::PREG_PACKAGE.'/si', $text, $set); 1914 } 1915 1916 /** 1917 * Looks into file docblock, till it finds the 1st @package 1918 * 1919 * @param string &$text Content of file 1920 * @param array &$set Preg result 1921 * 1922 * @return int 1923 */ 1924 private function _findFileDocblock(&$text, &$set) 1925 { 1926 return preg_match('/^\s*<\?(?:php)?\s+'. 1927 '(\/\/[^\n\r]*\s+|'. 1928 '\/\*[^\n\r]*?\*\/\s+)?'. 1929 '\/\*(.*?)\*\//si', $text, $set); 1930 } 1931 1932 private function _findProperty(&$text, &$set) 1933 { 1934 return preg_match('/^(\$?'.self::PREG_VARIABLE.')\s*(=)?/si', $text, $set); 1935 } 1936 1937 private function _findAttr(&$text, &$set) 1938 { 1939 return preg_match( 1940 '/^[\s](var|public|protected|private|const|static|abstract|final)/si', 1941 $text, $set 1942 ); 1943 } 1944 1945 private function _findFunction(&$text, &$set) 1946 { 1947 return preg_match( 1948 '/^function\s([&\s]*'.self::PREG_VARIABLE.')\s*\(/si', $text, $set 1949 ); 1950 } 1951 1952 /** 1953 * Looks for a docblock backwards 1954 * 1955 * @param string $text The text to search in (from the end) 1956 * 1957 * @return string 1958 */ 1959 private function _revDocblock($text) 1960 { 1961 $r = preg_match('/^\s*\/\*(.*?)\*\//s', strrev($text), $set); 1962 if ($r>0) 1963 return strrev($set[1]); 1964 else 1965 return ''; 1966 } 1967 1968 /** 1969 * Search for docblock tags 1970 * 1971 * @param string $text Text 1972 * 1973 * @return array 1974 */ 1975 private function _findParamInDocblock($text) 1976 { 1977 $r = preg_match_all( 1978 '/\*[ \t]*@([\w]+)[ \t]+([\w]+)[ \t]*([\w\$]*)\s/', $text, $set, 1979 PREG_SET_ORDER 1980 ); 1981 return $set; 1982 } 1983 1984 private function _getFilePackage() 1985 { 1986 $r = $this->_findFileDocblock($this->_text, $set); 1987 if ($r>0) { 1988 $r = $this->_findPackageInDocblock($set[1], $doc); 1989 if ($r>0) 1990 return $doc[1]; 1991 else { 1992 $r = $this->_findPackageInDocblock($set[2], $doc); 1993 if ($r>0) 1994 return $doc[1]; 1995 } 1996 } 1997 return ''; 1998 } 1999 2000 private function _cleanParameter($str) 2001 { 2002 if (!$this->_dollar) 2003 $str = str_replace('$', '', $str); 2004 return htmlspecialchars(preg_replace('/\s\s+/', ' ', trim($str))); 2005 } 2006 2007 2008 /** 2009 * Splits a parameter into its name and type 2010 * 2011 * @param string $parameter The parameter to analyse 2012 * @param string $default Default value 2013 * @param string $param Value of docblock "param" 2014 * 2015 * @return array 2016 */ 2017 private static function _splitNameType($parameter, $default = '', $param = '') 2018 { 2019 $exp_param_name = explode(' ', trim($parameter)); 2020 $nat = array(); 2021 if (count($exp_param_name)>1) { 2022 // Parameter like "MyType $myVariable" 2023 $nat[0] = trim($exp_param_name[1]); 2024 $nat[1] = trim($exp_param_name[0]); 2025 } 2026 else { 2027 // Parameter like "$myVariable" 2028 $nat[0] = $exp_param_name[0]; 2029 if ($param!='') { 2030 // if a @param was provided, let's use it : 2031 $nat[1] = $param; 2032 } 2033 else 2034 $nat[1] = self::_guessType($default); 2035 } 2036 return $nat; 2037 } 2038 2039 static private function _cleanVariable($str) 2040 { 2041 return str_replace('$', '', str_replace('&', '', $str)); 2042 } 2043 2044 static private function _stripComments($str) 2045 { 2046 $patt = array('/'.self::PREG_COMMENT.'/s'); 2047 $repl = array(''); 2048 while ($str!=preg_replace($patt, $repl, $str)) 2049 $str = preg_replace($patt, $repl, $str); 2050 return $str; 2051 } 2052 2053 static private function _getVisibility($arr) 2054 { 2055 if (in_array('private', $arr)) 2056 return 'private'; 2057 elseif (in_array('protected', $arr)) 2058 return 'protected'; 2059 else 2060 return 'public'; 2061 } 2062 2063 /** 2064 * Tries to guess the type of a given value 2065 * 2066 * @param string $value The type to check 2067 * 2068 * @return string The corresponding XMI DataType. 2069 */ 2070 static private function _guessType($value) 2071 { 2072 $value = trim(strtolower($value)); 2073 if (substr($value, 0, 6) == 'array(') 2074 $type = 'array'; 2075 elseif (!(strpos($value, "'")===false && strpos($value, '"')===false)) 2076 $type = 'string'; 2077 elseif ($value=='true' || $value=='false') 2078 $type = 'bool'; 2079 elseif ($value=='void') 2080 $type = 'void'; 2081 elseif (is_numeric($value)) { 2082 if (strpos($value, '.')===false) 2083 $type = 'int'; 2084 else 2085 $type = 'float'; 2086 } 2087 else 2088 $type = 'mixed'; 2089 return $type; 2090 } 2091 2092 /** 2093 * Recursively replaces the temporary string values by references in a package 2094 * 2095 * @param PHP_UML_Metamodel_Package &$ns Package to look into 2096 * @param PHP_UML_Metamodel_Package &$_oDef Root package 2097 */ 2098 private function _resolveReferences(PHP_UML_Metamodel_Package &$ns, PHP_UML_Metamodel_Package &$_oDef) 2099 { 2100 if (!is_null($ns->nestedPackage)) { 2101 foreach ($ns->nestedPackage as &$nsIdx) { 2102 $this->_resolveReferences($this->model->packages->get($nsIdx), $_oDef); 2103 } 2104 } 2105 if (!is_null($ns->ownedType)) 2106 foreach ($ns->ownedType as &$elt) { 2107 if (isset($elt->superClass) && !is_null($elt->superClass)) { 2108 foreach ($elt->superClass as &$className) { 2109 $this->_resolveType($ns, $className, $_oDef, $elt); 2110 } 2111 } 2112 if (isset($elt->implements) && !is_null($elt->implements)) { 2113 foreach ($elt->implements as &$className) { 2114 $this->_resolveType($ns, $className, $_oDef, $elt); 2115 } 2116 } 2117 foreach ($elt->ownedOperation as &$function) { 2118 foreach ($function->ownedParameter as &$parameter) { 2119 $this->_resolveType($ns, $parameter->type, $_oDef, $elt); 2120 } 2121 } 2122 if (isset($elt->ownedAttribute)) { 2123 foreach ($elt->ownedAttribute as &$attribute) { 2124 $this->_resolveType($ns, $attribute->type, $_oDef, $elt); 2125 } 2126 } 2127 } 2128 } 2129 2130 private function _searchIntoPackage(PHP_UML_Metamodel_Package &$ns, $value) 2131 { 2132 foreach ($ns->ownedType as $key => &$o) { 2133 if (strtolower($o->name)==strtolower($value)) { 2134 return $o; 2135 } 2136 } 2137 return false; 2138 } 2139 2140 private function _searchIntoDatatype($value) 2141 { 2142 foreach ($this->model->datatypes->getIterator() as $dt) { 2143 if (strtolower($dt->name)==strtolower($value)) { 2144 return $dt; 2145 } 2146 } 2147 return false; 2148 } 2149 2150 /** 2151 * Makes the type resolution for a given element in a given package 2152 * 2153 * @param PHP_UML_Metamodel_Package &$pkg The nesting package 2154 * @param string &$element The element to resolve, provided as a name 2155 * @param PHP_UML_Metamodel_Package &$_pkgDef The root package 2156 * @param PHP_UML_Metamodel_Type &$context The context (the nesting class/interface, which 2157 * is the only element to know the nesting file) 2158 */ 2159 private function _resolveType(PHP_UML_Metamodel_Package &$pkg, &$element, 2160 PHP_UML_Metamodel_Package &$_pkgDef, PHP_UML_Metamodel_Type &$context) 2161 { 2162 $_o = $this->_searchIntoPackage($pkg, $element); 2163 if (!($_o===false)) { 2164 $element = $_o; 2165 } 2166 else { 2167 $_o = $this->_searchIntoPackage($_pkgDef, $element); 2168 if (!($_o===false)) { 2169 $element = $_o; 2170 } 2171 else { 2172 $_o = $this->_searchIntoDatatype($element); 2173 if (!($_o===false)) { 2174 $element = $_o; 2175 } 2176 else { 2177 PHP_UML_Warning::add('Could not resolve '.$element. 2178 ' in '.$context->file->name); 2179 $element = null; 2180 } 2181 } 2182 } 2183 } 2184} 2185 2186?> 2187