00001 <?php
00014 class CodeToDiagram
00015 {
00021 protected $arrFiles = array();
00022
00028 protected $booStart = false;
00029
00035 protected $strFileFrom = null;
00036
00042 protected $strFileName = "diagram.html";
00043
00050 protected $strOutputType = self::OUTPUT_TYPE_SCREEN;
00051
00058 protected $strPrinterType = self::PRINTER_TYPE_HTML;
00059
00065 protected $objPrinter;
00066
00072 protected $strCallerPath;
00073
00079 protected $strPublicPath;
00080
00087 protected $booExternalAccess = false;
00093 protected static $objInstance;
00094
00101 const RUN_IN_FILES = false;
00102
00111 const REMOVE_FILES = true;
00112
00117 const CODE_TO_DIAGRAM_CLASS_PREFIX = "CTD";
00118
00125 const OUTPUT_TYPE_STRING = "string";
00126
00134 const OUTPUT_TYPE_FILE = "file";
00135
00140 const OUTPUT_TYPE_SCREEN = "screen";
00141
00148 const PRINTER_TYPE_XML = "xml";
00149
00156 const PRINTER_TYPE_HTML = "html";
00157
00158 const MSG_NO_WRITE_PERMISSION = " The user of the system does not have permission to write the code to diagram files. Change the RUN_IN_FILES to false and save your files into a allowed folder.";
00159
00165 protected static $arrDefaultStereotypes = array( 'user' , 'system' , 'user' , 'entity' , 'controller' , 'boundary' , 'database');
00166
00173 public function __construct()
00174 {
00175 $this->loadDefaultsStereotypes();
00176 $this->setPrinterType( $this->getPrinterType() );
00177 }
00178
00182 protected function loadDefaultsStereotypes()
00183 {
00184 foreach( self::$arrDefaultStereotypes as $strDefaultStereotype )
00185 {
00186 $objStereotype = new UmlSequenceDiagramStereotype();
00187 $objStereotype->setName( $strDefaultStereotype )->setDefault( true );
00188 UmlSequenceDiagramStereotype::addStereotype( $objStereotype );
00189 }
00190
00191 }
00192
00199 public function getConfiguration()
00200 {
00201 return CodeInstrumentationReceiver::getInstance()->getConfiguration();
00202 }
00203
00210 public function setPrinter( UmlSequenceDiagramPrinterInterface $objPrinter )
00211 {
00212 $this->objPrinter = $objPrinter;
00213 return $this;
00214 }
00215
00221 public function getPrinter()
00222 {
00223 return $this->objPrinter;
00224 }
00225
00233 public function setPrinterConfiguration( UmlSequenceDiagramPrinterConfigurationInterface $objPrinterConfiguration )
00234 {
00235 if( get_class( $objPrinterConfiguration ) != get_class( $this->getPrinterConfiguration() ) )
00236 {
00237 throw new CodeToDiagramException( "Invalid Printer Configuration" );
00238 }
00239 $this->getPrinter()->setConfiguration( $objPrinterConfiguration );
00240 return $this;
00241 }
00242
00248 public function getPrinterConfiguration()
00249 {
00250 return $this->getPrinter()->getConfiguration();
00251 }
00252
00280 public function setOutputType( $strType )
00281 {
00282 switch( $strType )
00283 {
00284 case CodeToDiagram::OUTPUT_TYPE_SCREEN:
00285 case CodeToDiagram::OUTPUT_TYPE_STRING:
00286 case CodeToDiagram::OUTPUT_TYPE_FILE :
00287 {
00288 $this->strOutputType = $strType;
00289 break;
00290 }
00291 default:
00292 {
00293 throw new CodeToDiagramException( "Invalid output type. ('" . $strType . "')" );
00294 break;
00295 }
00296 }
00297 return $this;
00298 }
00299
00322 public function getOutputType()
00323 {
00324 return $this->strOutputType;
00325 }
00326
00350 public function setPrinterType( $strType )
00351 {
00352 switch( $strType )
00353 {
00354 case CodeToDiagram::PRINTER_TYPE_HTML:
00355 {
00356 $this->setPrinter( UmlSequenceDiagramPrinterToHtml::getInstance() );
00357 $this->strPrinterType = $strType;
00358 break;
00359 }
00360 case CodeToDiagram::PRINTER_TYPE_XML:
00361 {
00362 $this->setPrinter( UmlSequenceDiagramPrinterToXml::getInstance() );
00363 $this->strPrinterType = $strType;
00364 break;
00365 }
00366 default:
00367 {
00368 throw new CodeToDiagramException( "Invalid printer type. ('" . $strType . "')" );
00369 break;
00370 }
00371 }
00372 return $this;
00373 }
00374
00393 public function getPrinterType()
00394 {
00395 return $this->strPrinterType;
00396 }
00397
00406 public function setExternalAcess( $booExternalAccess )
00407 {
00408 $this->booExternalAccess = (boolean)$booExternalAccess;
00409 return $this;
00410 }
00411
00419 public function getExternalAcess()
00420 {
00421 return $this->booExternalAccess;
00422 }
00423
00439 public static function hasInstance()
00440 {
00441 return ( self::$objInstance !== null );
00442 }
00443
00454 public static function getInstance()
00455 {
00456 if( self::$objInstance === null )
00457 {
00458 self::$objInstance = new CodeToDiagram();
00459 }
00460 return self::$objInstance;
00461 }
00462
00479 public function setStarted( $booStarted )
00480 {
00481 $this->booStart = $booStarted;
00482 return $this;
00483 }
00484
00498 public function getStarted()
00499 {
00500 return $this->booStart;
00501 }
00502
00515 public function setFileFrom( $strFileFrom )
00516 {
00517 $this->strFileFrom = $strFileFrom;
00518 return $this;
00519 }
00520
00531 public function getFileFrom()
00532 {
00533 return $this->strFileFrom;
00534 }
00535
00546 public function setFileName( $strFileName )
00547 {
00548 $this->strFileName = $strFileName;
00549 return $this;
00550 }
00551
00561 public function getFileName()
00562 {
00563 return $this->strFileName;
00564 }
00565
00572 public function setCallerPath( $strCallerPath )
00573 {
00574 $this->strCallerPath = $strCallerPath;
00575 return $this;
00576 }
00577
00583 public function getCallerPath()
00584 {
00585 return $this->strCallerPath;
00586 }
00587
00594 public function setPublicPath( $strPublicPath )
00595 {
00596 $this->strPublicPath = $strPublicPath;
00597 return $this;
00598 }
00599
00605 public function getPublicPath()
00606 {
00607 return $this->strPublicPath;
00608 }
00609
00617 public function start()
00618 {
00619 ob_start();
00620 if( $this->getStarted() )
00621 {
00622 return $this;
00623 }
00624 else
00625 {
00626 CodeInstrumentationReceiver::getInstance()->restart();
00627 $this->CodeToDiagramRequireOnce($this->getFileFrom() , $this->getFileFrom() );
00628 exit();
00629
00630
00631 return $this;
00632 }
00633 }
00634
00642 public function restart()
00643 {
00644 CodeInstrumentationReceiver::getInstance()->restart();
00645 return $this;
00646 }
00647
00655 private function checkPermissionToWrite( $strFileName )
00656 {
00657 $strPath = CorujaFileManipulation::getPathOfFile( $strFileName );
00658
00659 if( $strPath == "" )
00660 {
00661 $strPath = $this->getPublicPath();
00662 }
00663
00664 if( file_exists( $strFileName ) )
00665 {
00666 if( !is_writable( $strFileName ) )
00667 {
00668 throw new CodeToDiagramException( 1 . self::MSG_NO_WRITE_PERMISSION );
00669 }
00670 return TRUE;
00671 }
00672
00673 if( is_dir( $strPath ) )
00674 {
00678 return true;
00679 }
00680
00681 if( !mkdir( $strPath , 0777, TRUE ) )
00682 {
00683 throw new CodeToDiagramException(3 . self::MSG_NO_WRITE_PERMISSION );
00684 }
00685 return TRUE;
00686 }
00687
00708 public function save()
00709 {
00710 $strReturn = "";
00711 $strDiagram = "";
00712
00713 if( $this->getStarted() )
00714 {
00715 $strContent = ob_get_contents();
00716 ob_end_clean();
00717 $objUmlSequenceDiagram = CodeInstrumentationReceiver::getInstance()->getUmlSequenceDiagram();
00718 $objUmlSequenceDiagram->setOutput( $strContent );
00719
00720 switch( $this->getPrinterType() )
00721 {
00722 case self::PRINTER_TYPE_HTML:
00723 {
00724 $objPrinter = UmlSequenceDiagramPrinterToHtml::getInstance();
00725 UmlSequenceDiagramPrinterToHtml::getInstance()->getConfiguration()->setPublicPath( $this->getPublicPath() );
00726 UmlSequenceDiagramPrinterToHtml::getInstance()->getConfiguration()->setCallerPath( $this->getCallerPath() );
00727 UmlSequenceDiagramPrinterToHtml::getInstance()->getConfiguration()->setExternalAccess( $this->getExternalAcess() );
00728 $strDiagram = UmlSequenceDiagramPrinterToHtml::getInstance()->perform( $objUmlSequenceDiagram );
00729 break;
00730 }
00731 case self::PRINTER_TYPE_XML:
00732 {
00733 $objPrinter = UmlSequenceDiagramPrinterToXml::getInstance();
00734 $strDiagram = UmlSequenceDiagramPrinterToXml::getInstance()->perform( $objUmlSequenceDiagram );
00735 break;
00736 }
00737 default:
00738 {
00739 throw new CodeToDiagramException( "Invalid printer type ({$this->getPrinterType()})" );
00740 break;
00741 }
00742 }
00743 switch( $this->getOutputType() )
00744 {
00745 case self::OUTPUT_TYPE_SCREEN:
00746 {
00747 $objPrinter->getHeader();
00748 print $strDiagram;
00749 break;
00750 }
00751 case self::OUTPUT_TYPE_STRING:
00752 {
00753 $strReturn = $strDiagram;
00754 break;
00755 }
00756 case self::OUTPUT_TYPE_FILE:
00757 {
00758 $this->checkPermissionToWrite( $this->getFileName() );
00759 file_put_contents( $this->getFileName() , $strDiagram );
00760 $strReturn = $strDiagram;
00761 break;
00762 }
00763 default:
00764 {
00765 throw new CodeToDiagramException( "Invalid output type ({$this->getOutputType()})" );
00766 break;
00767 }
00768 }
00769 CodeInstrumentationReceiver::getInstance()->restart();
00770 }
00771 return $strReturn;
00772 }
00773
00777 public function __destruct() {
00778
00779 $this->save();
00780 }
00781
00788 public static function init( $strFile )
00789 {
00790 if( self::getInstance()->getStarted() == false )
00791 {
00792 self::getInstance()->setFileFrom( $strFile );
00793 }
00794 return self::getInstance();
00795 }
00796
00805 protected function fixFileName( $strFileFrom, $strFile )
00806 {
00807 $strFileFrom = str_replace( '/', '\\', $strFileFrom );
00808 $strFile = str_replace( '/', '\\', $strFile );
00809 $strFilePath = substr( $strFileFrom , 0 , -(strlen(basename($strFileFrom ) ) ) );
00810 $strFile = $strFilePath . $strFile;
00811
00812 return $strFile;
00813 }
00814
00822 public function addFile( $strFileFrom, $strFile )
00823 {
00824 $strFile = $this->fixFileName( $strFileFrom, $strFile );
00825
00826 $this->arrFiles[] = $strFile;
00827
00828 return $this;
00829 }
00830
00838 protected function hasFile( $strFileFrom , $strFile )
00839 {
00840 return in_array( $strFile , $this->arrFiles );
00841 }
00842
00851 public function CodeToDiagramRequireOnce( $strFileFrom, $strFile )
00852 {
00853 $arrCodeToDiagramBackTrace = debug_backtrace();
00854
00855 if( !$this->hasFile( $strFileFrom , $strFile ) )
00856 {
00857 $this->CodeToDiagramRequire( $strFileFrom, $strFile );
00858 }
00859 return $this;
00860 }
00861
00870 public function CodeToDiagramIncludeOnce( $strFileFrom, $strFile )
00871 {
00872 if( !$this->hasFile( $strFileFrom , $strFile ) )
00873 {
00874 $this->CodeToDiagramIncludeOnce( $strFileFrom , $strFile );
00875 }
00876 return $this;
00877 }
00878
00887 public function CodeToDiagramRequire( $strFileFrom, $strFile )
00888 {
00889 $this->loadFile( $strFileFrom , $strFile );
00890 return $this;
00891 }
00892
00901 public function CodeToDiagramInclude( $strFileFrom, $strFile )
00902 {
00903 $this->loadFile( $strFileFrom , $strFile );
00904 return $this;
00905 }
00906
00915 public function CodeToDiagramExit( $strFileFrom = '', $strMessage = '')
00916 {
00917 print "Exit called into $strFileFrom ($strMessage ) ";
00918 }
00919
00928 protected function convertFileContent( $strContentFile , $strFile , $strFullFile )
00929 {
00930 if( self::getInstance()->getStarted() == false )
00931 {
00932 self::getInstance()->setStarted( true );
00933 $strContentFile = preg_replace('/require_once/', '//require_once', $strContentFile, 1);
00934 }
00935
00936 $strContentFile = str_replace(
00937 Array(
00938 'require_once(' ,
00939 'require(' ,
00940 'include(' ,
00941 'include_once(',
00942 'exit()',
00943 'exit(',
00944 '__FILE__',
00945 ),
00946 Array(
00947 'CodeToDiagram::getInstance()->CodeToDiagramRequireOnce("'. $strFile . '",' ,
00948 'CodeToDiagram::getInstance()->CodeToDiagramRequire("'. $strFile . '",' ,
00949 'CodeToDiagram::getInstance()->CodeToDiagramInclude("'. $strFile . '",' ,
00950 'CodeToDiagram::getInstance()->CodeToDiagramIncludeOnce("'. $strFile . '",',
00951 'CodeToDiagram::getInstance()->CodeToDiagramExit("'. $strFile . '")',
00952 'CodeToDiagram::getInstance()->CodeToDiagramExit("'. $strFile . '",',
00953 '"' . $this->getCallerPath() . $strFullFile . '"',
00954 ),
00955 $strContentFile
00956 );
00957
00958 return $strContentFile;
00959 }
00960
00968 public function codeInstrumentationLine( $strLine, $strTextSearch )
00969 {
00970 $arrResult = array();
00971
00972 if( substr( strtolower( trim( $strLine ) ) , 0 , strlen( $strTextSearch ) ) == $strTextSearch )
00973 {
00974 $strLine = trim( $strLine );
00975 $strBefore = substr( $strLine , 0 , strlen( $strTextSearch ) );
00976 $strAfter = substr( $strLine , strlen( $strTextSearch ) );
00977 $arrWords = explode( " " , $strAfter );
00978
00979 $strOldClassName = $arrWords[0];
00980 $strNewClassName = self::CODE_TO_DIAGRAM_CLASS_PREFIX . $strOldClassName;
00981
00982 $arrWords[0] = $strNewClassName;
00983 $strAfter = implode( " " , $arrWords );
00984 $strLine = $strBefore . $strAfter;
00985
00986 $arrResult[ "line" ] = $strLine;
00987 $arrResult[ "class_old" ] = $strOldClassName;
00988 $arrResult[ "class_new" ] = $strNewClassName;
00989
00990 }
00991 return $arrResult;
00992 }
00993
01002 protected function codeInstrumentationClass( $strTextSearch, array &$arrOldClasses , array &$arrNewClasses , array &$arrLines )
01003 {
01004 foreach( $arrLines as $intLine => $strLine )
01005 {
01006 $arrResult = $this->codeInstrumentationLine( $strLine , $strTextSearch );
01007
01008 if( sizeof( $arrResult ) !== 0 )
01009 {
01010 $arrOldClasses[] = $arrResult[ "class_old" ];
01011 $arrNewClasses[] = $arrResult[ "class_new" ];
01012 $arrLines[ $intLine ] = $arrResult[ "line" ];
01013 }
01014 }
01015 }
01016
01024 protected function preloadFile( $strFileName , $strContentFile )
01025 {
01026 if( self::RUN_IN_FILES )
01027 {
01028 $strFileName = $strFileName . "(0).phps";
01029 print $strFileName;
01030 $this->checkPermissionToWrite( $strFileName );
01031 file_put_contents( $strFileName , $strContentFile );
01032 require_once( $strFileName );
01033 if( self::REMOVE_FILES )
01034 {
01035 unlink( $strFileName );
01036 }
01037 }
01038 else
01039 {
01040 eval( '?' . '>' . $strContentFile );
01041 }
01042 return $this;
01043 }
01044
01051 protected function codeInstrumentationFile( $strFileName , $strContentFile )
01052 {
01053 $arrLines = explode( "\n" , $strContentFile );
01054 $arrOldClasses = array();
01055 $arrNewClasses = array();
01056 $arrOldInterface = array();
01057 $arrNewInterface = array();
01058
01059 $this->codeInstrumentationClass( "class " , $arrOldClasses , $arrNewClasses, $arrLines );
01060 $this->codeInstrumentationClass( "interface " , $arrOldInterface , $arrNewInterface, $arrLines );
01061
01062 $strContentFile = implode( "\n" , $arrLines );
01063
01064 $this->preloadFile( $strFileName , $strContentFile );
01065
01066 foreach( $arrNewClasses as $intKey => $strNewClassName )
01067 {
01068 $oReflectionCode = new CodeInstrumentationClass( $strNewClassName , $strContentFile );
01069 $oReflectionCode->setClassName( $arrOldClasses[ $intKey ] );
01070 $strNewCode = $oReflectionCode->getCode();
01071 if( self::RUN_IN_FILES )
01072 {
01073 $strFileName = trim( $strNewClassName ) . "(1).phps";
01074 $this->checkPermissionToWrite( $strFileName );
01075 file_put_contents( $strFileName , '<?' . 'php ' . $strNewCode );
01076 require_once( $strFileName );
01077 if( self::REMOVE_FILES )
01078 {
01079 unlink( $strFileName );
01080 }
01081 }
01082 else
01083 {
01084 eval( $strNewCode );
01085 }
01086 }
01087 foreach( $arrNewInterface as $intKey => $strNewInterfaceName )
01088 {
01089 $strOldInterface = $arrOldInterface[ $intKey ];
01091 $strNewCode = "interface $strOldInterface {} ";
01092 if( self::RUN_IN_FILES )
01093 {
01094 $strFileName = trim( $strNewInterfaceName ) . "(1).phps";
01095 $this->checkPermissionToWrite( $strFileName );
01096 file_put_contents( $strFileName , '<?' . 'php ' . $strNewCode );
01097 require_once( $strFileName );
01098 if( self::REMOVE_FILES )
01099 {
01100 unlink( $strFileName );
01101 }
01102 }
01103 else
01104 {
01105 eval( $strNewCode );
01106 }
01107 }
01108 }
01109
01117 protected function loadFile( $strFileFrom, $strFile )
01118 {
01119 if( basename( $strFile ) == 'CodeToDiagram.class.php' )
01120 {
01121 return $this;
01122 }
01123 $this->addFile( $strFileFrom, $strFile );
01124
01125 if( CorujaFileManipulation::isRelativePath( $strFile ) )
01126 {
01127 $strFullFile = $this->fixFileName( $strFileFrom, $strFile );
01128 }
01129 else
01130 {
01131 $strFullFile = $strFile;
01132 }
01133
01134 if( ! file_exists( $strFullFile ) )
01135 {
01136 throw new CodeToDiagramException( "Unable to find file: " . $strFullFile );
01137 }
01138
01139 $strContentFile = file_get_contents( $strFullFile );
01140 $strContentFile = $this->convertFileContent( $strContentFile , $strFile , $strFullFile );
01141 $this->codeInstrumentationFile( $strFile, $strContentFile );
01142
01143 return $this;
01144 }
01145
01151 public function setCallerPathByFile( $strCallerFile )
01152 {
01153 $this->setCallerPath( CorujaFileManipulation::getPathOfFile( $strCallerFile ) );
01154 }
01155
01163 public function addNote( $strNoteContent , $booAfter = true )
01164 {
01165 $objNote = new UmlSequenceDiagramNote();
01166 $objNote->setContent( $strNoteContent );
01167 $objMessage = CodeInstrumentationReceiver::getInstance()->getActualMessage();
01168 $objActor = CodeInstrumentationReceiver::getInstance()->getActualActor();
01169 $objNote->setActor( $objActor );
01170
01171 if( $booAfter )
01172 {
01173 $objMessage->addNoteAfter($objNote);
01174 }
01175 else
01176 {
01177 $objMessage->addNoteBefore( $objNote );
01178 }
01179 return $objNote;
01180 }
01181
01182 public function __call( $strMethod , $arrArguments )
01183 {
01184 throw new CodeToDiagramException( "unknow method " . $strMethod . " in " . get_class( $this ) );
01185 }
01186 }
01187
01188 ?>