Repository: jakobwesthoff/phuml Branch: master Commit: f8cb44ebdbd8 Files: 37 Total size: 67.8 KB Directory structure: gitextract_3wa82eqh/ ├── LICENSE ├── README.rst └── src/ ├── app/ │ └── phuml ├── autoload/ │ ├── base_autoload.php │ ├── php_autoload.php │ ├── phuml_autoload.php │ ├── processor_autoload.php │ └── structure_autoload.php ├── classes/ │ ├── base.php │ ├── generator/ │ │ └── tokenparser.php │ ├── php/ │ │ ├── attribute.php │ │ ├── class.php │ │ ├── function.php │ │ ├── functionParameter.php │ │ └── interface.php │ ├── phuml.php │ └── processor/ │ ├── dot.php │ ├── graphviz/ │ │ ├── options.php │ │ └── style/ │ │ └── default.php │ ├── graphviz.php │ ├── neato.php │ ├── options.php │ └── statistics.php ├── config/ │ └── config.php ├── exceptions/ │ ├── base/ │ │ └── property.php │ ├── generator/ │ │ └── notFound.php │ ├── phuml/ │ │ ├── invalidProcessor.php │ │ └── invalidProcessorChain.php │ └── processor/ │ ├── externalExecution.php │ ├── graphviz/ │ │ └── style/ │ │ └── notFound.php │ ├── notFound.php │ └── option.php ├── interfaces/ │ ├── generator.php │ ├── processor/ │ │ ├── externalCommand.php │ │ └── graphviz/ │ │ └── style.php │ └── processor.php └── scripts/ └── checkAll.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: LICENSE ================================================ Copyright (c) 2007-2014, Jakob Westhoff All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the {organization} nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: README.rst ================================================ .. note:: **WARNING**: This project isn't actively maintained anymore, mainly due to the lack of time. I migrated it to github by request. Hopefully somebody might find this useful as a base to work on again. Eventhough it should still be compatible with current PHP versions. It does not support *newer* features like namespace and such. Of course Pull-Requests are always welcome. If somebody wants to take over maintership completely I would be happy to discuss that. ===== phUML ===== What is this all about? ======================= phUML is fully automatic UML_ class diagramm generator written PHP_. It is capable of parsing any PHP5_ object oriented source code and create an appropriate image representation of the oo structure based on the UML_ specification. .. _UML: http://en.wikipedia.org/wiki/Unified_Modeling_Language .. _PHP: http://php.net .. _PHP5: http://www.php.net/downloads.php#v5 What does it look like? ======================= .. image:: https://raw.githubusercontent.com/jakobwesthoff/phuml/master/images/phuml_example_thumbnail.jpg :alt: Class diagram of the phUML generator :align: center :target: https://raw.githubusercontent.com/jakobwesthoff/phuml/master/images/phuml_example.png The image shown here is the class diagramm which phUML created when run on its own codebase. This image is hardly readable, because it has been resized to fit in the layout of this page. You can take a look at the complete image by clicking here_ .. _here: https://raw.githubusercontent.com/jakobwesthoff/phuml/master/images/phuml_example.png Can I use this for my own projects? =================================== phUML should be compatible with any object oriented code written in PHP5_. At the moment it unfortunatly does not support any PHP4_ code. .. _PHP4: http://php.net phUML has quite a informative help interface, which can be accessed by calling it with the -h option. :: $ phuml -h The phUML generator works with so called processors, which may be used in a chain to create a lot of different output formats. Every available processor can be listed by calling phUML with the -l option. :: $ phuml -l The most important processor used to create images of any kind is the graphviz processor. As its name indicates it outputs information in the so called dot language used by graphviz_. To sucessfully handle this output format and create the desired images you will need the graphviz_ toolkit installed on your system. You may then call the neato or dot executables, which are part of graphviz_, to process the created file manually or you may phUML do this for you by using the dot or neato processor. .. _graphviz: http://www.graphviz.org You should just play around with the phUML commandline tool to get a better understanding of what the processors do and how they work. To give you a short example of how a complete phUML call could look like, this is the one used generate the example you can see above. :: $ phuml -r ./ -dot -createAssociations false -neato example.png ================================================ FILE: src/app/phuml ================================================ #!/usr/bin/env php ) HEADER; } function showUsage() { echo << [PROCESSOR OPTIONS] ... Commands: -h Display this help text -l List all available processors Options: -r Scan given directorie recursively Example: phuml -r ./ -graphviz -createAssociations false -neato out.png This example will scan the current directory recursively for php files. Send them to the "dot" processor which will process them with the option "createAssociations" set to false. After that it will be send to the neato processor and saved to the file out.png USAGE; } function showProcessorList() { echo "The following processors are available:\n\n"; foreach( plProcessor::getProcessors() as $processor ) { echo "* Processor: ", $processor, "\n"; echo " - Options:", "\n"; $class = 'pl' . $processor . 'Processor'; $p = new $class(); $o = $p->options; if ( count( $o->getOptions() ) === 0 ) { echo " This processor does not have any options.", "\n\n"; continue; } foreach ( $o->getOptions() as $option ) { echo " ", $option, ' (', getOptionTypeRepresentation( $o->getOptionType( $option ) ), '):', "\n"; // Get the description as an word wrapped array to add the padding $description = explode( "\n", wordwrap( $o->getOptionDescription( $option ), 73, "\n", true ) ); foreach( $description as $descriptionLine ) { echo " ", $descriptionLine, "\n"; } echo "\n"; } } } function getOptionTypeRepresentation( $type ) { switch ( $type ) { case plProcessorOptions::BOOL: return 'boolean'; break; case plProcessorOptions::STRING: return 'string'; break; case plProcessorOptions::DECIMAL: return 'decimal'; break; default: return 'unknown type'; } } function updateArguments( $count ) { global $argc, $argv; $argv = array_slice( $argv, $count ); $argc = count( $argv ); } function cmdActions() { global $argc, $argv; if ( strtolower( $argv[0] ) === '-h' ) { showUsage(); exit( 1 ); } else if ( strtolower( $argv[0] ) === '-l' ) { showProcessorList(); exit( 0 ); } } function cmdOptions() { global $argc, $argv; global $recursive; if ( strtolower( $argv[0] ) === '-r' ) { $recursive = true; updateArguments( 1 ); } } function cmdDirOrFile() { global $argc, $argv; global $phuml, $recursive; if ( is_dir( $argv[0] ) ) { $phuml->addDirectory( $argv[0], 'php', $recursive ); updateArguments( 1 ); } else { showUsage(); exit( 1 ); } } function cmdProcessor() { global $argc, $argv; global $phuml; if ( substr( $argv[0], 0, 1 ) !== '-' ) { showUsage(); exit( 1 ); } // We do not need to check for correct options here, because the option // will throw an exception if there is something wrong $processor = plProcessor::factory( substr( $argv[0], 1 ) ); updateArguments( 1 ); while( $argc > 1 && !in_array( ucfirst( $option = substr( $argv[0], 1 ) ), plProcessor::getProcessors() ) ) { $processor->options->$option = $argv[1]; updateArguments( 2 ); } $phuml->addProcessor( $processor ); } function cmdOutFile() { global $argc, $argv; global $outfile; $outfile = $argv[0]; updateArguments( 1 ); } showHeader(); // The absolute minimum of parameters is 2 if ( $argc < 2 ) { showUsage(); exit( 1 ); } $recursive = false; $outfile = 'out'; $phuml = new plPhuml(); $phuml->generator = plStructureGenerator::factory( 'tokenparser' ); // Remove the application name updateArguments( 1 ); try { cmdActions(); cmdOptions(); cmdDirOrFile(); while( $argc > 1 ) { cmdProcessor(); } cmdOutFile(); echo '[|] Running... (This may take some time)', "\n"; $phuml->generate( $outfile ); } catch ( Exception $e ) { echo 'A fatal error occured during the process:', "\n"; echo $e->getMessage(), "\n"; } ?> ================================================ FILE: src/autoload/base_autoload.php ================================================ 'exceptions/base/property.php', ); ?> ================================================ FILE: src/autoload/php_autoload.php ================================================ 'classes/php/class.php', 'plPhpFunction' => 'classes/php/function.php', 'plPhpInterface' => 'classes/php/interface.php', 'plPhpAttribute' => 'classes/php/attribute.php', 'plPhpFunctionParameter' => 'classes/php/functionParameter.php', ); ?> ================================================ FILE: src/autoload/phuml_autoload.php ================================================ 'classes/phuml.php', 'plPhumlInvalidProcessorException' => 'exceptions/phuml/invalidProcessor.php', 'plPhumlInvalidProcessorChainException' => 'exceptions/phuml/invalidProcessorChain.php', ); ?> ================================================ FILE: src/autoload/processor_autoload.php ================================================ 'interfaces/processor.php', 'plProcessorOptions' => 'classes/processor/options.php', 'plProcessorOptionException' => 'exceptions/processor/option.php', 'plGraphvizProcessor' => 'classes/processor/graphviz.php', 'plGraphvizProcessorOptions' => 'classes/processor/graphviz/options.php', 'plProcessorNotFoundException' => 'exceptions/processor/notFound.php', 'plGraphvizProcessorStyle' => 'interfaces/processor/graphviz/style.php', 'plGraphvizProcessorDefaultStyle' => 'classes/processor/graphviz/style/default.php', 'plGraphvizProcessorStyleNotFoundException' => 'exceptions/processor/graphviz/style/notFound.php', 'plExternalCommandProcessor' => 'interfaces/processor/externalCommand.php', 'plNeatoProcessor' => 'classes/processor/neato.php', 'plDotProcessor' => 'classes/processor/dot.php', 'plStatisticsProcessor' => 'classes/processor/statistics.php', 'plProcessorExternalExecutionException' => 'exceptions/processor/externalExecution.php', ); ?> ================================================ FILE: src/autoload/structure_autoload.php ================================================ 'interfaces/generator.php', 'plStructureTokenparserGenerator' => 'classes/generator/tokenparser.php', 'plStructureGeneratorNotFoundException' => 'exceptions/generator/notFound.php', ); ?> ================================================ FILE: src/classes/base.php ================================================ ================================================ FILE: src/classes/generator/tokenparser.php ================================================ initGlobalAttributes(); $this->initParserAttributes(); } private function initGlobalAttributes() { $this->classes = array(); $this->interfaces = array(); } private function initParserAttributes() { $this->parserStruct = array( 'class' => null, 'interface' => null, 'function' => null, 'attributes' => array(), 'functions' => array(), 'typehint' => null, 'params' => array(), 'implements' => array(), 'extends' => null, 'modifier' => 'public', 'docblock' => null, ); $this->lastToken = array(); } public function createStructure( array $files ) { $this->initGlobalAttributes(); foreach( $files as $file ) { $this->initParserAttributes(); $tokens = token_get_all( file_get_contents( $file ) ); // Loop through all tokens foreach( $tokens as $token ) { // Split into Simple and complex token if ( is_array( $token ) !== true ) { switch( $token ) { case ',': $this->comma(); break; case '(': $this->opening_bracket(); break; case ')': $this->closing_bracket(); break; case '=': $this->equal_sign(); break; default: // Ignore everything else $this->lastToken = null; } } else if ( is_array( $token ) === true ) { switch ( $token[0] ) { case T_WHITESPACE: $this->t_whitespace( $token ); break; case T_FUNCTION: $this->t_function( $token ); break; case T_VAR: $this->t_var( $token ); break; case T_VARIABLE: $this->t_variable( $token ); break; case T_ARRAY: $this->t_array( $token ); break; case T_CONSTANT_ENCAPSED_STRING: $this->t_constant_encapsed_string( $token ); break; case T_LNUMBER: $this->t_lnumber( $token ); break; case T_DNUMBER: $this->t_dnumber( $token ); break; case T_PAAMAYIM_NEKUDOTAYIM: $this->t_paamayim_neukudotayim( $token ); break; case T_STRING: $this->t_string( $token ); break; case T_INTERFACE: $this->t_interface( $token ); break; case T_CLASS: $this->t_class( $token ); break; case T_IMPLEMENTS: $this->t_implements( $token ); break; case T_EXTENDS: $this->t_extends( $token ); break; case T_PUBLIC: $this->t_public( $token ); break; case T_PROTECTED: $this->t_protected( $token ); break; case T_PRIVATE: $this->t_private( $token ); break; case T_DOC_COMMENT: $this->t_doc_comment( $token ); break; default: // Ignore everything else $this->lastToken = null; // And reset the docblock $this->parserStruct['docblock'] = null; } } } // One file is completely scanned here // Store interface or class in the parser arrays $this->storeClassOrInterface(); } // Fix the class and interface connections $this->fixObjectConnections(); // Return the class and interface structure return array_merge( $this->classes, $this->interfaces ); } private function comma() { // Reset typehints on each comma $this->parserStruct['typehint'] = null; } private function opening_bracket() { // Ignore opening brackets } private function closing_bracket() { switch ( $this->lastToken ) { case T_FUNCTION: // The function declaration has been closed // Add the current function $this->parserStruct['functions'][] = array( $this->parserStruct['function'], $this->parserStruct['modifier'], $this->parserStruct['params'], $this->parserStruct['docblock'] ); // Reset the last token $this->lastToken = null; //Reset the modifier state $this->parserStruct['modifier'] = 'public'; // Reset the params array $this->parserStruct['params'] = array(); $this->parserStruct['typehint'] = null; // Reset the function name $this->parserStruct['function'] = null; // Reset the docblock $this->parserStruct['docblock'] = null; break; default: $this->lastToken = null; } } private function equal_sign() { switch ( $this->lastToken ) { case T_FUNCTION: // just ignore the equal sign break; default: $this->lastToken = null; } } private function t_whitespace( $token ) { // Ignore whitespaces } private function t_function( $token ) { switch( $this->lastToken ) { case null: case T_PUBLIC: case T_PROTECTED: case T_PRIVATE: $this->lastToken = $token[0]; break; default: $this->lastToken = null; } } private function t_var( $token ) { switch ( $this->lastToken ) { case T_FUNCTION: // just ignore the T_VAR break; default: $this->lastToken = null; } } private function t_variable( $token ) { switch( $this->lastToken ) { case T_PUBLIC: case T_PROTECTED: case T_PRIVATE: // A new class attribute $this->parserStruct['attributes'][] = array( $token[1], $this->parserStruct['modifier'], $this->parserStruct['docblock'], ); $this->lastToken = null; $this->parserStruct['modifier'] = 'public'; $this->parserStruct['docblock'] = null; break; case T_FUNCTION: // A new function parameter $this->parserStruct['params'][] = array( $this->parserStruct['typehint'], $token[1] ); break; } } private function t_array( $token ) { switch ( $this->lastToken ) { case T_FUNCTION: // just ignore the T_ARRAY break; default: $this->lastToken = null; } } private function t_constant_encapsed_string( $token ) { switch ( $this->lastToken ) { case T_FUNCTION: // just ignore the T_CONSTANT_ENCAPSED_STRING break; default: $this->lastToken = null; } } private function t_lnumber( $token ) { switch ( $this->lastToken ) { case T_FUNCTION: // just ignore the T_LNUMBER break; default: $this->lastToken = null; } } private function t_dnumber( $token ) { switch ( $this->lastToken ) { case T_FUNCTION: // just ignore the T_DNUMBER break; default: $this->lastToken = null; } } private function t_paamayim_neukudotayim( $token ) { switch ( $this->lastToken ) { case T_FUNCTION: // just ignore the T_PAAMAYIM_NEKUDOTAYIM break; default: $this->lastToken = null; } } private function t_string( $token ) { switch( $this->lastToken ) { case T_IMPLEMENTS: // Add interface to implements array $this->parserStruct['implements'][] = $token[1]; // We do not reset the last token here, because // there might be multiple interfaces break; case T_EXTENDS: // Set the superclass $this->parserStruct['extends'] = $token[1]; // Reset the last token $this->lastToken = null; break; case T_FUNCTION: // Add the current function only if there is no function name already // Because if we know the function name already this is a type hint if ( $this->parserStruct['function'] === null ) { // Function name $this->parserStruct['function'] = $token[1]; } else { // Type hint $this->parserStruct['typehint'] = $token[1]; } break; case T_CLASS: // Set the class name $this->parserStruct['class'] = $token[1]; // Reset the last token $this->lastToken = null; break; case T_INTERFACE: // Set the interface name $this->parserStruct['interface'] = $token[1]; // Reset the last Token $this->lastToken = null; break; default: $this->lastToken = null; } } private function t_interface( $token ) { switch( $this->lastToken ) { case null: // New initial interface token // Store the class or interface definition if there is any in the // parser arrays ( There might be more than one class/interface per // file ) $this->storeClassOrInterface(); // Remember the last token $this->lastToken = $token[0]; break; default: $this->lastToken = null; } } private function t_class( $token ) { switch( $this->lastToken ) { case null: // New initial interface token // Store the class or interface definition if there is any in the // parser arrays ( There might be more than one class/interface per // file ) $this->storeClassOrInterface(); // Remember the last token $this->lastToken = $token[0]; break; default: $this->lastToken = null; } } private function t_implements( $token ) { switch ( $this->lastToken ) { case null: $this->lastToken = $token[0]; break; default: $this->lastToken = null; } } private function t_extends( $token ) { switch ( $this->lastToken ) { case null: $this->lastToken = $token[0]; break; default: $this->lastToken = null; } } private function t_public( $token ) { switch ( $this->lastToken ) { case null: $this->lastToken = $token[0]; $this->parserStruct['modifier'] = $token[1]; break; default: $this->lastToken = null; } } private function t_protected( $token ) { switch ( $this->lastToken ) { case null: $this->lastToken = $token[0]; $this->parserStruct['modifier'] = $token[1]; break; default: $this->lastToken = null; } } private function t_private( $token ) { switch ( $this->lastToken ) { case null: $this->lastToken = $token[0]; $this->parserStruct['modifier'] = $token[1]; break; default: $this->lastToken = null; } } private function t_doc_comment( $token ) { switch ( $this->lastToken ) { case null: $this->parserStruct['docblock'] = $token[1]; break; default: $this->lastToken = null; $this->parserStruct['docblock'] = null; } } private function storeClassOrInterface() { // First we need to check if we should store interface data found so far if ( $this->parserStruct['interface'] !== null ) { // Init data storage $functions = array(); // Create the data objects foreach( $this->parserStruct['functions'] as $function ) { // Create the needed parameter objects $params = array(); foreach( $function[2] as $param) { $params[] = new plPhpFunctionParameter( $param[1], $param[0] ); } $functions[] = new plPhpFunction( $function[0], $function[1], $params ); } $interface = new plPhpInterface( $this->parserStruct['interface'], $functions, $this->parserStruct['extends'] ); // Store in the global interface array $this->interfaces[$this->parserStruct['interface']] = $interface; } // If there is no interface, we maybe need to store a class else if ( $this->parserStruct['class'] !== null ) { // Init data storage $functions = array(); $attributes = array(); // Create the data objects foreach( $this->parserStruct['functions'] as $function ) { // Create the needed parameter objects $params = array(); foreach( $function[2] as $param) { $params[] = new plPhpFunctionParameter( $param[1], $param[0] ); } $functions[] = new plPhpFunction( $function[0], $function[1], $params ); } foreach ( $this->parserStruct['attributes'] as $attribute ) { $type = null; // If there is a docblock try to isolate the attribute type if ( $attribute[2] !== null ) { // Regular expression that extracts types in array annotations $regexp = '/^[\s*]*@var\s+array\(\s*(\w+\s*=>\s*)?(\w+)\s*\).*$/m'; if ( preg_match( $regexp, $attribute[2], $matches ) ) { $type = $matches[2]; } else if ( $return = preg_match( '/^[\s*]*@var\s+(\S+).*$/m', $attribute[2], $matches ) ) { $type = trim( $matches[1] ); } } $attributes[] = new plPhpAttribute( $attribute[0], $attribute[1], $type ); } $class = new plPhpClass( $this->parserStruct['class'], $attributes, $functions, $this->parserStruct['implements'], $this->parserStruct['extends'] ); $this->classes[$this->parserStruct['class']] = $class; } $this->initParserAttributes(); } private function fixObjectConnections() { foreach( $this->classes as $class ) { $implements = array(); foreach( $class->implements as $key => $impl ) { $implements[$key] = array_key_exists( $impl, $this->interfaces ) ? $this->interfaces[$impl] : $this->interfaces[$impl] = new plPhpInterface( $impl ); } $class->implements = $implements; if ( $class->extends === null ) { continue; } $class->extends = array_key_exists( $class->extends, $this->classes ) ? $this->classes[$class->extends] : ( $this->classes[$class->extends] = new plPhpClass( $class->extends ) ); } foreach( $this->interfaces as $interface ) { if ( $interface->extends === null ) { continue; } $interface->extends = array_key_exists( $interface->extends, $this->interfaces ) ? $this->interfaces[$interface->extends] : ( $this->interfaces[$interface->extends] = new plPhpInterface( $interface->extends ) ); } } } ?> ================================================ FILE: src/classes/php/attribute.php ================================================ properties = array( 'name' => $name, 'modifier' => $modifier, 'type' => $type, ); } public function __get( $key ) { if ( !array_key_exists( $key, $this->properties ) ) { throw new plBasePropertyException( $key, plBasePropertyException::READ ); } return $this->properties[$key]; } public function __set( $key, $val ) { if ( !array_key_exists( $key, $this->properties ) ) { throw new plBasePropertyException( $key, plBasePropertyException::WRITE ); } $this->properties[$key] = $val; } } ?> ================================================ FILE: src/classes/php/class.php ================================================ properties = array( 'name' => $name, 'attributes' => $attributes, 'functions' => $functions, 'implements' => $implements, 'extends' => $extends, ); } public function __get( $key ) { if ( !array_key_exists( $key, $this->properties ) ) { throw new plBasePropertyException( $key, plBasePropertyException::READ ); } return $this->properties[$key]; } public function __set( $key, $val ) { if ( !array_key_exists( $key, $this->properties ) ) { throw new plBasePropertyException( $key, plBasePropertyException::WRITE ); } $this->properties[$key] = $val; } } ?> ================================================ FILE: src/classes/php/function.php ================================================ properties = array( 'name' => $name, 'modifier' => $modifier, 'params' => $params, ); } public function __get( $key ) { if ( !array_key_exists( $key, $this->properties ) ) { throw new plBasePropertyException( $key, plBasePropertyException::READ ); } return $this->properties[$key]; } public function __set( $key, $val ) { if ( !array_key_exists( $key, $this->properties ) ) { throw new plBasePropertyException( $key, plBasePropertyException::WRITE ); } $this->properties[$key] = $val; } } ?> ================================================ FILE: src/classes/php/functionParameter.php ================================================ properties = array( 'name' => $name, 'type' => $type, ); } public function __get( $key ) { if ( !array_key_exists( $key, $this->properties ) ) { throw new plBasePropertyException( $key, plBasePropertyException::READ ); } return $this->properties[$key]; } public function __set( $key, $val ) { if ( !array_key_exists( $key, $this->properties ) ) { throw new plBasePropertyException( $key, plBasePropertyException::WRITE ); } $this->properties[$key] = $val; } } ?> ================================================ FILE: src/classes/php/interface.php ================================================ properties = array( 'name' => $name, 'functions' => $functions, 'extends' => $extends, ); } public function __get( $key ) { if ( !array_key_exists( $key, $this->properties ) ) { throw new plBasePropertyException( $key, plBasePropertyException::READ ); } return $this->properties[$key]; } public function __set( $key, $val ) { if ( !array_key_exists( $key, $this->properties ) ) { throw new plBasePropertyException( $key, plBasePropertyException::WRITE ); } $this->properties[$key] = $val; } } ?> ================================================ FILE: src/classes/phuml.php ================================================ properties = array( 'generator' => plStructureGenerator::factory( 'tokenparser' ), ); $this->files = array(); } public function addFile( $file ) { $this->files[] = $file; } public function addDirectory( $directory, $extension = 'php', $recursive = true ) { if ( $recursive === false ) { $iterator = new DirectoryIterator( $directory ); } else { $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $directory ) ); } foreach( $iterator as $entry ) { if ( $entry->isDir() === true ) { continue; } if ( $sub = strtolower( substr( $entry->getFilename(), -1 * strlen( $extension ) ) ) !== strtolower( $extension ) ) { continue; } $this->files[] = $entry->getPathname(); } } public function addProcessor( $processor ) { if ( count( $this->processors ) === 0 ) { // First processor must support application/phuml-structure if ( !in_array( 'application/phuml-structure', $processor->getInputTypes() ) ) { throw new plPhumlInvalidProcessorChainException( 'application/phuml-structure', $processor->getInputTypes() ); } } else { $this->checkProcessorCompatibility( end( $this->processors ), $processor ); } $this->processors[] = $processor; } private function checkProcessorCompatibility( $first, $second ) { if ( !( $first instanceof plProcessor ) || !( $second instanceof plProcessor ) ) { throw new plPhumlInvalidProcessorException(); } if ( !in_array( $first->getOutputType(), $second->getInputTypes() ) ) { throw new plPhumlInvalidProcessorChainException( $first->getOutputType(), $second->getInputTypes() ); } } public function generate( $outfile ) { echo "[|] Parsing class structure", "\n"; $structure = $this->generator->createStructure( $this->files ); $temporary = array( $structure, 'application/phuml-structure' ); foreach( $this->processors as $processor ) { preg_match( '@^pl([A-Z][a-z]*)Processor$@', get_class( $processor ), $matches ); echo "[|] Running '" . $matches[1] . "' processor", "\n"; $temporary = array( $processor->process( $temporary[0], $temporary[1] ), $processor->getOutputType(), ); } echo "[|] Writing generated data to disk", "\n"; end( $this->processors )->writeToDisk( $temporary[0], $outfile ); } public function __get( $key ) { if ( !array_key_exists( $key, $this->properties ) ) { throw new plBasePropertyException( $key, plBasePropertyException::READ ); } return $this->properties[$key]; } public function __set( $key, $val ) { if ( !array_key_exists( $key, $this->properties ) ) { throw new plBasePropertyException( $key, plBasePropertyException::WRITE ); } $this->properties[$key] = $val; } } ?> ================================================ FILE: src/classes/processor/dot.php ================================================ options = new plProcessorOptions(); } public function getInputTypes() { return array( 'text/dot', ); } public function getOutputType() { return 'image/png'; } public function execute( $infile, $outfile, $type ) { exec( 'dot -Tpng -o ' . escapeshellarg( $outfile ) . ' ' . escapeshellarg( $infile ), $output, $return ); if ( $return !== 0 ) { throw new plProcessorExternalExecutionException( $output ); } } } ?> ================================================ FILE: src/classes/processor/graphviz/options.php ================================================ properties = array( 'style' => array( 'data' => plGraphvizProcessorStyle::factory( 'default' ), 'type' => self::STRING, 'description' => 'Style to use for the dot creation' ), 'createAssociations' => array( 'data' => true, 'type' => self::BOOL, 'description' => 'Create connections between classes that include each other. (This information can only be extracted if it is present in docblock comments)' ), ); } public function __set( $key, $val ) { switch( $key ) { case 'style': $this->properties[$key]['data'] = plGraphvizProcessorStyle::factory( (string)$val ); break; case 'createAssociations': $this->properties[$key]['data'] = ( $val === '0' || $val === 'false' ) ? false : true; break; default: throw new plProcessorOptionException( $key, plProcessorOptionException::WRITE ); } } } ?> ================================================ FILE: src/classes/processor/graphviz/style/default.php ================================================ ================================================ FILE: src/classes/processor/graphviz.php ================================================ options = new plGraphvizProcessorOptions(); $this->structure = null; $this->output = null; } public function getInputTypes() { return array( 'application/phuml-structure' ); } public function getOutputType() { return 'text/dot'; } public function process( $input, $type ) { $this->structure = $input; $this->output = 'digraph "' . sha1( mt_rand() ) . '" {' . "\n"; $this->output .= 'splines = true;' . "\n"; $this->output .= 'overlap = false;' . "\n"; $this->output .= 'mindist = 0.6;' . "\n"; foreach( $this->structure as $object ) { if ( $object instanceof plPhpClass ) { $this->output .= $this->getClassDefinition( $object ); } else if ( $object instanceof plPhpInterface ) { $this->output .= $this->getInterfaceDefinition( $object ); } } $this->output .= "}"; return $this->output; } private function getClassDefinition( $o ) { $def = ''; // First we need to create the needed data arrays $name = $o->name; $attributes = array(); $associations = array(); foreach( $o->attributes as $attribute ) { $attributes[] = $this->getModifierRepresentation( $attribute->modifier ) . $attribute->name; // Association creation is optional if ( $this->options->createAssociations === false ) { continue; } // Create associations if the attribute type is set if ( $attribute->type !== null && array_key_exists( $attribute->type, $this->structure ) && !array_key_exists( strtolower( $attribute->type ), $associations ) ) { $def .= $this->createNodeRelation( $this->getUniqueId( $this->structure[$attribute->type] ), $this->getUniqueId( $o ), array( 'dir' => 'back', 'arrowtail' => 'none', 'style' => 'dashed', ) ); $associations[strtolower( $attribute->type )] = true; } } $functions = array(); foreach( $o->functions as $function ) { $functions[] = $this->getModifierRepresentation( $function->modifier ) . $function->name . $this->getParamRepresentation( $function->params ); // Association creation is optional if ( $this->options->createAssociations === false ) { continue; } // Create association if the function is the constructor and takes // other classes as parameters if ( strtolower( $function->name ) === '__construct' ) { foreach( $function->params as $param ) { if ( $param->type !== null && array_key_exists( $param->type, $this->structure ) && !array_key_exists( strtolower( $param->type ), $associations ) ) { $def .= $this->createNodeRelation( $this->getUniqueId( $this->structure[$param->type] ), $this->getUniqueId( $o ), array( 'dir' => 'back', 'arrowtail' => 'none', 'style' => 'dashed', ) ); $associations[strtolower( $param->type )] = true; } } } } // Create the node $def .= $this->createNode( $this->getUniqueId( $o ), array( 'label' => $this->createClassLabel( $name, $attributes, $functions ), 'shape' => 'plaintext', ) ); // Create class inheritance relation if ( $o->extends !== null ) { // Check if we need an "external" class node if ( in_array( $o->extends, $this->structure ) !== true ) { $def .= $this->getClassDefinition( $o->extends ); } $def .= $this->createNodeRelation( $this->getUniqueId( $o->extends ), $this->getUniqueId( $o ), array( 'dir' => 'back', 'arrowtail' => 'empty', 'style' => 'solid' ) ); } // Create class implements relation foreach( $o->implements as $interface ) { // Check if we need an "external" interface node if ( in_array( $interface, $this->structure ) !== true ) { $def .= $this->getInterfaceDefinition( $interface ); } $def .= $this->createNodeRelation( $this->getUniqueId( $interface ), $this->getUniqueId( $o ), array( 'dir' => 'back', 'arrowtail' => 'normal', 'style' => 'dashed', ) ); } return $def; } private function getInterfaceDefinition( $o ) { $def = ''; // First we need to create the needed data arrays $name = $o->name; $functions = array(); foreach( $o->functions as $function ) { $functions[] = $this->getModifierRepresentation( $function->modifier ) . $function->name . $this->getParamRepresentation( $function->params ); } // Create the node $def .= $this->createNode( $this->getUniqueId( $o ), array( 'label' => $this->createInterfaceLabel( $name, array(), $functions ), 'shape' => 'plaintext', ) ); // Create interface inheritance relation if ( $o->extends !== null ) { // Check if we need an "external" interface node if ( in_array( $o->extends, $this->structure ) !== true ) { $def .= $this->getInterfaceDefinition( $o->extends ); } $def .= $this->createNodeRelation( $this->getUniqueId( $o->extends ), $this->getUniqueId( $o ), array( 'dir' => 'back', 'arrowtail' => 'empty', 'style' => 'solid' ) ); } return $def; } private function getModifierRepresentation( $modifier ) { return ( $modifier === 'public' ) ? ( '+' ) : ( ( $modifier === 'protected' ) ? ( '#' ) : ( '-' ) ); } private function getParamRepresentation( $params ) { if ( count( $params ) === 0 ) { return '()'; } $representation = '( '; for( $i = 0; $itype !== null ) { $representation .= $params[$i]->type . ' '; } $representation .= $params[$i]->name; if ( $i < count( $params ) - 1 ) { $representation .= ', '; } } $representation .= ' )'; return $representation; } private function getUniqueId( $object ) { return '"' . spl_object_hash( $object ) . '"'; } private function createNode( $name, $options ) { $node = $name . " ["; foreach( $options as $key => $value ) { $node .= $key . '=' . $value . ' '; } $node .= "]\n"; return $node; } private function createNodeRelation( $node1, $node2, $options ) { $relation = $node1 . ' -> ' . $node2 . ' ['; foreach( $options as $key => $value ) { $relation .= $key . '=' . $value . ' '; } $relation .= "]\n"; return $relation; } private function createInterfaceLabel( $name, $attributes, $functions ) { // Start the table $label = '<'; // The title $label .= ''; // The attributes block $label .= ''; // The function block $label .= ''; // End the table $label .= '
' . $name . '
'; if ( count( $attributes ) === 0 ) { $label .= ' '; } foreach( $attributes as $attribute ) { $label .= '' . $attribute . '
'; } $label .= '
'; if ( count( $functions ) === 0 ) { $label .= ' '; } foreach( $functions as $function ) { $label .= '' . $function . '
'; } $label .= '
>'; return $label; } private function createClassLabel( $name, $attributes, $functions ) { // Start the table $label = '<'; // The title $label .= ''; // The attributes block $label .= ''; // The function block $label .= ''; // End the table $label .= '
' . $name . '
'; if ( count( $attributes ) === 0 ) { $label .= ' '; } foreach( $attributes as $attribute ) { $label .= '' . $attribute . '
'; } $label .= '
'; if ( count( $functions ) === 0 ) { $label .= ' '; } foreach( $functions as $function ) { $label .= '' . $function . '
'; } $label .= '
>'; return $label; } } ?> ================================================ FILE: src/classes/processor/neato.php ================================================ options = new plProcessorOptions(); } public function getInputTypes() { return array( 'text/dot', ); } public function getOutputType() { return 'image/png'; } public function execute( $infile, $outfile, $type ) { exec( 'neato -Tpng -o ' . escapeshellarg( $outfile ) . ' ' . escapeshellarg( $infile ), $output, $return ); if ( $return !== 0 ) { throw new plProcessorExternalExecutionException( $output ); } } } ?> ================================================ FILE: src/classes/processor/options.php ================================================ properties ) ) { throw new plProcessorOptionException( $key, plProcessorOptionException::READ ); } return $this->properties[$key]['data']; } public function __set( $key, $val ) { if ( !array_key_exists( $key, $this->properties ) ) { throw new plProcessorOptionException( $key, plProcessorOptionException::WRITE ); } $this->properties[$key]['data'] = $val; } public function getOptions() { $options = array(); foreach( $this->properties as $key => $property ) { $options[] = $key; } return $options; } public function getOptionDescription( $option ) { if ( !array_key_exists( $option, $this->properties ) ) { throw new plProcessorOptionException( $option, plProcessorOptionException::UNKNOWN ); } return $this->properties[$option]['description']; } public function getOptionType( $option ) { if ( !array_key_exists( $option, $this->properties ) ) { throw new plProcessorOptionException( $option, plProcessorOptionException::UNKNOWN ); } return $this->properties[$option]['type']; } } ?> ================================================ FILE: src/classes/processor/statistics.php ================================================ options = new plProcessorOptions(); $this->information = array(); } public function getInputTypes() { return array( 'application/phuml-structure' ); } public function getOutputType() { return 'text/plain'; } public function process( $input, $type ) { // Initialize the values $this->information['interfaceCount'] = 0; $this->information['classCount'] = 0; $this->information['publicFunctionCount'] = 0; $this->information['publicAttributeCount'] = 0; $this->information['publicTypedAttributes'] = 0; $this->information['protectedFunctionCount'] = 0; $this->information['protectedAttributeCount'] = 0; $this->information['protectedTypedAttributes'] = 0; $this->information['privateFunctionCount'] = 0; $this->information['privateAttributeCount'] = 0; $this->information['privateTypedAttributes'] = 0; // Loop through the classes and interfaces foreach ( $input as $definition ) { if ( $definition instanceof plPhpInterface ) { $this->information['interfaceCount']++; } if ( $definition instanceof plPhpClass ) { $this->information['classCount']++; foreach( $definition->attributes as $attribute ) { switch ( $attribute->modifier ) { case 'public': $this->information['publicAttributeCount']++; if ( $attribute->type !== null ) { $this->information['publicTypedAttributes']++; } break; case 'protected': $this->information['protectedAttributeCount']++; if ( $attribute->type !== null ) { $this->information['protectedTypedAttributes']++; } break; case 'private': $this->information['privateAttributeCount']++; if ( $attribute->type !== null ) { $this->information['privateTypedAttributes']++; } break; } } } foreach( $definition->functions as $function ) { switch ( $function->modifier ) { case 'public': $this->information['publicFunctionCount']++; break; case 'protected': $this->information['protectedFunctionCount']++; break; case 'private': $this->information['privateFunctionCount']++; break; } } } $this->information['functionCount'] = $this->information['publicFunctionCount'] + $this->information['protectedFunctionCount'] + $this->information['privateFunctionCount']; $this->information['attributeCount'] = $this->information['publicAttributeCount'] + $this->information['protectedAttributeCount'] + $this->information['privateAttributeCount']; $this->information['typedAttributeCount'] = $this->information['publicTypedAttributes'] + $this->information['protectedTypedAttributes'] + $this->information['privateTypedAttributes']; $this->information['attributesPerClass'] = round( $this->information['attributeCount'] / $this->information['classCount'], 2 ); $this->information['functionsPerClass'] = round( $this->information['functionCount'] / $this->information['classCount'], 2 ); // Generate the needed text output return <<information['classCount']} Interfaces: {$this->information['interfaceCount']} Attributes: {$this->information['attributeCount']} ({$this->information['typedAttributeCount']} are typed) * private: {$this->information['privateAttributeCount']} * protected: {$this->information['protectedAttributeCount']} * public: {$this->information['publicAttributeCount']} Functions: {$this->information['functionCount']} * private: {$this->information['privateFunctionCount']} * protected: {$this->information['protectedFunctionCount']} * public: {$this->information['publicFunctionCount']} Average statistics ------------------ Attributes per class: {$this->information['attributesPerClass']} Functions per class: {$this->information['functionsPerClass']} END; } } ?> ================================================ FILE: src/config/config.php ================================================ ================================================ FILE: src/exceptions/base/property.php ================================================ ================================================ FILE: src/exceptions/generator/notFound.php ================================================ ================================================ FILE: src/exceptions/phuml/invalidProcessor.php ================================================ ================================================ FILE: src/exceptions/phuml/invalidProcessorChain.php ================================================ ================================================ FILE: src/exceptions/processor/externalExecution.php ================================================ ================================================ FILE: src/exceptions/processor/graphviz/style/notFound.php ================================================ ================================================ FILE: src/exceptions/processor/notFound.php ================================================ ================================================ FILE: src/exceptions/processor/option.php ================================================ ================================================ FILE: src/interfaces/generator.php ================================================ ================================================ FILE: src/interfaces/processor/externalCommand.php ================================================ execute( $infile, $outfile, $type ); $outdata = file_get_contents( $outfile ); unlink( $infile ); unlink( $outfile ); return $outdata; } } ?> ================================================ FILE: src/interfaces/processor/graphviz/style.php ================================================ ================================================ FILE: src/scripts/checkAll.sh ================================================ #!/bin/sh find ./ -iname "*.php" -exec php -l {} \;