Very hacky but helps me get through my job of fixing up old undocumented php scripts.
Update: BTW, the more I think about this code, the more I don't like it. Any critiques would be most welcome.
#!/usr/bin/perl -w use strict; use warnings; use Carp; use File::Basename; use Data::Dumper; use Cwd; use constant DEBUG => 0; # Core Vars my %uml_data; # Hash to hold the UML data # INC ARRAY # >> SET THIS TO YOUR PHP INC ARRAY. << my $inc = './;/www/lib/pear-lib'; $::VERSION = '1.0.3a'; # Regular Expresions my $require_once_RE = '^\s*require_once.*"(.+)"'; my $class_RE = '^\s*class\s+'; my $class_name_RE = $class_RE . '(\w+)'; my $extends_RE = 'extends\s+(\w+)'; my $parent_class_RE = $class_name_RE . '\s+' . $extends_RE; my $var_RE = '^\s*var\s+(\$\w+)\s*'; my $var_defined_RE = $var_RE . '(.*);'; my $function_RE = '^\s*function\s+'; my $function_name_RE = $function_RE . '(&?\w+)\s*'; my $function_params_RE = $function_name_RE . '\((.*)\)'; # MAIN my $the_file = $ARGV[0] or die("USAGE: perl phpuml.pl filename [inc_pa +th;more_inc_path]\n"); if ($ARGV[1]) { $inc .= ";" . $ARGV[1]; } process($the_file); print report(); print Dumper(%uml_data) if DEBUG; sub process { my $file = shift; my @tried; # variable to hold typeglob for filehandle to be processed my $fh; # Are we in testing mode? if ($file eq "TEST") { # If so, use the test file at the end of this file. $fh = *main::DATA; } elsif ($file) { unless(open(PHP_FILE, "< $file")) { my @inc_array = split /;/, $inc; INC: foreach my $folder (@inc_array) { my $f = $folder . $file; open(PHP_FILE, "< $f") || next INC; last INC; } } $fh = *PHP_FILE; } else { die("Must pass file for processing"); } ################################### ## LETS GET READY TO RUMBLE!!!!! ## ################################### # Declare temp vars to hold values passed to the data hash my $class_name = ""; my $parent = ""; my @functions; my @fields; my $holding; # Flag used when function parameters extend over a single line. my $in_multi_line_parse = 0; # Flag used when there is more than one class in a file. my $i_have_seen_a_class = 0; FILE_LOOP: while(<$fh>) { if (/$require_once_RE/i) { my $fn = $1; push (@tried, $fn); } elsif (/$class_name_RE/i) { if ($i_have_seen_a_class) { unless ($uml_data{$class_name}) { $uml_data{$class_name} = { FILENAME => basename($file), INHERITS => $parent, FIELDS => [@fields], METHODS => [@functions] }; } # clear the parameters. @fields = (); @functions = (); } else { $i_have_seen_a_class = 1; } $class_name = $1; if (/$extends_RE/i) { $parent = $1; } } elsif (/$var_RE/i) { my $var = $1; if (/$var_defined_RE/i) { my $val = $2; $val =~ s/\s//g; $var = $var . $val; } push @fields, $var; } elsif (/$function_RE/i) { if ($class_name) { if (/$function_params_RE/i) { # All the parameters on one line my $func = $1 . "(" . $2 . ")"; $func =~ s/\s//g; push @functions, $func; } else { $in_multi_line_parse = 1; chomp; $holding = $_; } } } elsif ($in_multi_line_parse) { # We are in a subsequent line of a function # So we are passing what we know with what we # will know. s/\s//g; # Strip off white space chomp; if (/\)/) { # Reset the multiline flag. $in_multi_line_parse = 0; $holding .= $_; $holding =~ /$function_params_RE/i; my $func = $1 . "(" . $2 . ")"; $func =~ s/\s//g; push @functions, $func; } else { $holding .= $_; } } } # Send class hash to _data; unless ($uml_data{$class_name}) { $uml_data{$class_name} = { FILENAME => basename($file), INHERITS => $parent, FIELDS => [@fields], METHODS => [@functions] }; } foreach my $rec_file (@tried) { process($rec_file); } return; #print "\n*****--->>>>>> " . $self->{_data}->{foo}->{fields}[0] . +"\n"; } # process sub report { my $txt = "/****************************************************** +\n *\n"; while ((my $key, my $value) = each (%uml_data)) { $txt .= class_report($key); } $txt .= " ******************************************************/\ +n "; return $txt; } sub class_report { my $class_name = shift; # Class name my $txt = " *****************************************************\ +n"; $txt .= " * $class_name"; if ($uml_data{$class_name}{INHERITS}) { $txt .= " inherits " . $uml_data{$class_name}{INHERITS}; } $txt .= "\n"; $txt .= " *****************************************************\ +n"; # Class vars foreach my $var (@{$uml_data{$class_name}{FIELDS}}) { my $marker = "+"; if ($var =~ /^\$_/) { $marker = "-"; } $txt .= " * $marker $var\n"; } $txt .= " *****************************************************\ +n"; # Class methods foreach my $method (@{$uml_data{$class_name}{METHODS}}) { my $marker = "+"; if ($method =~ /^_/) { $marker = "-"; } #print Dumper($method); $txt .= " * $marker $method\n"; } $txt .= " ****************************************************\n + *\n"; return $txt; } # class_report if (DEBUG) { # process("D:/Php/pear/HTML/Table.php"); print "####################################################\n"; # print ">>>>>>>> " . $uml_data{Alert}{FIELDS}[1]; } # Module Test Data # # This is an OO php file with three classes, one extending the other # # It includes some challenges to the generator such as method calls # across lines. __DATA__ <?php /** * Foobar test class * Copyright (C) 2002 Christopher Raymond Baker * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-13 +07, USA. * * Christopher Raymond Baker [cbaker@cbaker.org] * http://cbaker.org */ /* ******************** *\ * REQUIRED LIBRARIES \* ******************** */ // PEAR Libraries require_once "PEAR.php"; require_once "HTML/Table.php"; define("DEFAULT_CSS", "3px solid red"); class Foo { var $flub; function Foo() { die("There is no there there."); } function rose($rose = "rose") { print "A "; while (1) { print "$rose is a "; } } // end func rose function _private($foo, $bar = "") { print "Hi Mom!"; } // end func _private } Class Box extends PEAR { /** * The width for the box */ var $_width; /** * The height for the box */ var $_height; /** * Field for holding the state of the class. */ var $state; function Box($w, $h = 0) { $this->state = true; if (is_int($w)) { $this->_width = $w; } else { $this->state = new PEAR_Error("Not a valid width dimension +.", NOT_AN_INTEGER_ERROR); } if (is_int($h)) { $this->_width = $h; } else { $this->state = new PEAR_Error("Not a valid height dimensio +n.", NOT_AN_INTEGER_ERROR); } } // end constructor function &factory($w, $h = 0) { $b = new Box($w, $h); if (Test::isGood($b->state)) return $b; return $b->state; } // end func factory } // end class Box class Alert extends Box { /** * Box object dimensions for the component. */ var $_dim; var $_message; var $_caption; /** * Field for holding the state of the class. */ var $state; function Alert($m, $class = "", $i = "") { $this->_dim =& Box::factory(400); if(is_string($m)) { $this->_message = $m; } elseif (is_array($m)) { } } // end constructor function setDimensions($r, $h = 0) { if (get_class($r) == "box") { $_dim = $r; } } // end func setDimensions } // end class Alert
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: PHP UML Diagram Generator
by rjray (Chaplain) on Mar 01, 2002 at 00:16 UTC | |
by ignatz (Vicar) on Mar 01, 2002 at 00:32 UTC | |
by rjray (Chaplain) on Mar 01, 2002 at 22:26 UTC | |
by ignatz (Vicar) on Mar 04, 2002 at 16:11 UTC |