]> git.llucax.com Git - mecon/meconlib.git/blob - lib/MECON/Graph/external/jpgraph/src/utils/jpdocgen/jplintphp.php
Se mejora un poco los ALT para navegadores de texto.
[mecon/meconlib.git] / lib / MECON / Graph / external / jpgraph / src / utils / jpdocgen / jplintphp.php
1 <?php
2 //==============================================================================
3 // Name:        JPLINTPHP.PHP
4 // Description: Simple static analysis of a PHP file.
5 // Created:     01/12/03 
6 // Author:      johanp@aditus.nu
7 // Version:     $Id: jplintphp.php,v 1.14 2003/03/01 21:51:18 aditus Exp $
8 //
9 // License:     QPL 1.0
10 //
11 // Copyright (C) 2001,2002 Johan Persson
12 //
13 // Long Description:
14 // Parses a correct PHP file for classes and methods and does some rudimentary
15 // checks and warns for:
16 // 1) ... unused instance variables exists
17 // 2) ... possible forgotten $this-> qualifier for access to instance variables
18 // 
19 // Please note that the PHP file MUST be syntactically correct since
20 // the parsing is very simple and can't cope with recovery after syntax errors. 
21 //==============================================================================
22
23 set_time_limit(0);
24
25 //-------------------------------------------------------------------
26 // Some testcode to get the ereg expressions correct. Why does this
27 // always has to be a bloody pain... :-)
28 //------------------------------------------------------------------------
29 //$aLine = 'var $txt1 = array(), $txt2 = "" , $txt3 = "kalle" ;';
30 //$pattern='/^var\s+(\$\w+)?\s*=?(array\(\)|\d+\.\d+|\d+|"\w*")?(,\s*(\$\w+)?\s*=?(array\(\)|\d+\.\d+|\d+|"\w*")?)*/';
31 //$vdec = '(\$\w+)?\s*=?\s*(array\(\)|\d+\.\d+|\d+|"\w*")?';
32 //$vlist = "\s*$vdec\s*,?";
33 //$pattern = "/^var $vlist$vlist$vlist$vlist$vlist;/";
34 //echo "pattern=$pattern<p>";
35
36
37 if( false ) {
38 //    $aLine = 'function GanttVLine($aDate,$aTitle="",$aColor="black",$aWeight="_{33}*45/12",$aStyle="dashed")';
39     $aLine = 'function SetFrameBevel($aDepth=3,$aBorder=false,$aBorderColor=\'black@0.4\',$aColor1=\'white\',$aColor2=\'darkgray\',$aFlg=true)';
40
41     $quotchars = "[\w|£|#|$|%|&|\.|@\[\]|+|*|\/|-|\{|\}]+";
42     $argdef  = '\s*(\&?\$\w+)*=?(""|'."''".'|'."'".$quotchars."'".'|\d+|\d+\.\d+|\w+|"'.$quotchars.'"|array\(\d*,?\d*,?\d*,?\))?\s*,?';
43
44     $pattern = '/^function\s+(\w+)\s*\(\s*'.$argdef.$argdef.$argdef.$argdef.$argdef.$argdef.$argdef.$argdef.'\s*\)/i';
45     
46     $flg=preg_match($pattern,trim($aLine),$matches);
47     if( $flg )
48     {
49         
50         $numArgs = ceil((count($matches)-2)/2);
51         $fname = $matches[1];
52         $args=array();
53         $argsval=array();
54         for($i=0; $i<$numArgs; ++$i) {
55             $args[$i]=$matches[2+$i*2];
56             if( isset($matches[3+$i*2]) )
57                 $argsval[$i]=$matches[3+$i*2];
58         }
59         
60         echo "Number of args: ".ceil((count($matches)-1)/2)."<br>";
61         for($i=0;$i<count($matches); ++$i)
62             echo "<pre>$i:$matches[$i]</pre>";
63     }
64     else
65         echo "No match found.<br>";
66     exit();
67 }
68
69
70
71 // Base class for PHP class properties (Class, methods)
72 class Prop {
73     var $iName;
74     function Prop($aName) {
75         $this->iName = $aName;
76     }
77     function GetName() {
78         return $this->iName;
79     }
80
81
82 // Stores properties for a class definition, name, methods, file etc
83 class ClassProp extends Prop {
84     var $iParent;
85     var $iFileName;
86     var $iLineNbr;
87     var $iFuncs,$iFuncNbr=0;
88     var $iVars=array(),$iVarNbr=0,$iUsed=array();
89         
90     function ClassProp($aParent,$aName,$aLineNbr,$aFile) {
91         $this->iName = $aName;
92         $this->iParent = $aParent;
93         $this->iLineNbr = $aLineNbr;
94         $this->iFileName = $aFile;                
95         $this->iFuncs=array();
96         $this->iVars=array();
97         $this->iUsed=array();
98         $this->iFuncNbr = 0;
99     }
100
101     function AddVar($aVar,$aVal="") {
102         $this->iVars[$this->iVarNbr] = array($aVar,$aVal);
103         $this->iUsed[$this->iVarNbr] = false;
104         $this->iVarNbr++;
105     }
106         
107     function IaAllVarsUsed() {
108         for($i=0; count($this->iVars); ++$i) 
109             if( !$this->iUsed[$i] )
110                 return false;
111         return true;
112     }
113
114     function AddFunc($aFunc) {
115     
116     // Sanity check. Make sure that a function with this name isn't
117     // alrey defined in this class
118     $found = false;
119     for($i=0; $i<$this->iFuncNbr && !$found; ++$i) {
120         $found = ($aFunc->iName == $this->iFuncs[$i]->iName);
121     }
122     if( $found ) {
123         echo "<br><font color=red><b>Semantic error in PHP file:</font></b> Function <b>$aFunc->iName</b> is multiple defined in class <b>$this->iName</b>. Skipping.<br>\n";
124                 return;
125     }
126     
127         
128         $this->iFuncs[$this->iFuncNbr]=$aFunc;
129         $this->iFuncNbr++;
130     }
131
132     function GetFileName() {
133         return $this->iFileName;
134     }
135         
136     function FormatVar($aVar) {
137         return "<i>".$aVar."</i>";
138     }
139
140     function FormatClass($aClass,$aParent) {
141         $res = "<hr>CLASS <b>".$aClass."</b>";
142         if( $aParent != "" )
143             $res .= " INHERITS <b>".$aParent."</b>";            
144         return $res;
145     }
146         
147     // Some Java style ToString() methods
148     function ToString() {
149         $res = $this->FormatClass($this->iName,$this->iParent);
150         $res .= "(Defined in: ".$this->iFileName.":".$this->iLineNbr.")<br>" ;                          
151         $res .= "<br><b>VARS</b>";
152         for( $i=0; $i<count($this->iVars); ++$i) {
153             $res .= "<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;".$this->FormatVar($this->iVars[$i][0]);
154             if($this->iVars[$i][1] != "")
155                 $res .= " = ".$this->FormatVar($this->iVars[$i][1]);
156             if( !$this->iUsed[$i] )
157                 $res .= "<font color=\"red\"> ** NOT USED **</font>";
158         }
159         $res .= "<p><b>METHODS</b><br>";
160         for( $i=0; $i<count($this->iFuncs); ++$i) {
161             $res .= $this->iFuncs[$i]->ToString();
162         }
163         return $res;
164     }
165         
166 }
167
168 // Stores properties for a class method
169 class FuncProp extends Prop {
170     var $iNumArgs;
171     var $iArgs=array(),$iArgsVal=array(), $iArgsDes=array();
172     var $iClassName;
173     var $iLineNbr;
174     var $iFileName;
175     var $iShortComment;
176         
177     function FuncProp($aClassName,$aName,$aLineNbr,$aArgs,$aArgsVal,$aShortComment="",$aFileName="") {
178         $this->iName = $aName;
179         $this->iClassName = $aClassName;
180         $this->iNumArgs = count($aArgs);
181         $this->iArgs = $aArgs;
182         $this->iArgsVal = $aArgsVal;
183         $this->iLineNbr = $aLineNbr;
184         $this->iShortComment = $aShortComment;
185         $this->iFileName = $aFileName;
186     }
187
188 // Some Java style ToString() methods
189     function ToString() {
190         $res = $this->iClassName."::<b>".$this->iName."</b>";
191                                  
192         if( $this->iNumArgs > 0 ) {
193             $res .= "(";
194             for($i=0; $i<$this->iNumArgs; ++$i) {
195                 if($i!=0) $res .= ", ";
196                 $res .= "<i>".$this->iArgs[$i];
197                 if( isset($this->iArgsVal[$i]) && $this->iArgsVal[$i]!="" )
198                     $res .= " = ".$this->iArgsVal[$i];
199                 $res .= "</i>";
200             }
201             $res .= ")";
202         }
203         else
204             $res .= "()";
205         return $res."<br>";
206     }   
207 }
208
209 // The actual parser class. very simple. Read all the line
210 // for a given file and try to figure out if there is a function or
211 // class definiton on that line.
212 class Parser {
213     var $iClasses=null,$iClassCnt;
214     var $iCurrClass=null;
215     var $iGlobalFuncs=null;
216     var $iBraceCnt=0;
217     var $iInComment=0,$iHyphenMarks=0,$iQuoteMarks=0,$iInString=0;
218     var $iCurrFileName;
219     var $iFp=null, $iCurrFile="";
220     var $iPrevLine,$iNextLine;
221     var $iWarnings=null;
222     var $iCommentBreak=true,$iLastComment="";
223
224     function Parser($aFile) {
225         $this->iClasses=array();
226         $this->iWarnings = array();
227         $this->iClassCnt=0;
228         $this->iGlobalFuncs = array();
229         $this->iCurrFileName=$aFile;
230         $fp = @fopen($aFile,"r");
231         if( !$fp ) {
232             die("Parser: Can't open file $aFile");
233         }
234         $this->iFp = $fp;
235         $this->iCurrFile=$aFile;
236     }
237
238     function MapClass($aClass) {
239         echo $aClass->ToString().'<p>';
240     }
241         
242         function MapGlobalFunc($aFunc) {
243                 echo $aFunc->ToString().'<p>';
244         }
245         
246         
247     function DoMapClasses() {
248         for($i=0; $i<count($this->iClasses); ++$i) {
249             $this->MapClass($this->iClasses[$i]);
250         }
251     }
252
253     function DoMapGlobalFuncs() {
254         $n = count($this->iGlobalFuncs);
255         for( $i=0; $i<$n; ++$i ) {
256                 $this->MapGlobalFunc($this->iGlobalFuncs[$i]);
257         }
258     }
259         
260         function StartIndicator($aFilename) {
261                 echo "<h2>File: $aFilename </h2>\n";
262                 flush();
263         }
264         
265     function Start() {
266         // Read line by line to find each class and all methods
267         // defined within that class
268         $lnbr=1;
269         $this->iPrevLine = "";
270         $this->iNextLine = fgets($this->iFp,256);               
271         $this->StartIndicator($this->iCurrFileName);
272         while( !feof($this->iFp) ) {
273             $buffer = $this->iNextLine;
274             $this->iNextLine = fgets($this->iFp,256);
275             $this->ParseLine($buffer,$lnbr);
276             $this->iPrevLine = $buffer;
277             ++$lnbr;
278         }
279         $buffer = $this->iNextLine;
280         $this->iNextLine="";
281         $this->ParseLine($buffer,$lnbr);
282     }
283
284     function End() {
285         fclose($this->iFp);
286     }
287         
288     function GetWarnings() {
289         $res="";
290         for($i=0; $i<count($this->iWarnings); ++$i)
291             $res .= $this->iWarnings[$i]."<br>";
292         return $res;
293     }
294         
295     function GetUnusedClassVariables() {
296         $res = "";
297         for($i=0; $i<count($this->iClasses); ++$i) {
298             $var = "";
299             for($j=0; $j<count($this->iClasses[$i]->iVars); ++$j) {
300                 if( !$this->iClasses[$i]->iUsed[$j] ) {                         
301                     if( $var != "" ) $var .= ", ";
302                     $var .= "<i>".$this->iClasses[$i]->iVars[$j][0]."</i>";
303                 }
304             }
305             if( $var != "" )
306                 $res .= "<b>Warning:</b>Unused variables in Class ".$this->iClasses[$i]->GetName()." (".$var.")<br>";
307         }
308         return $res;
309     }
310
311     function CheckUsedVars($aLine,$aLineNbr) {
312         $n = count($this->iCurrClass->iVars);
313         if( $n==0 )     return;
314         $ret=false;
315         for( $i=0; $i<$n; ++$i) {
316             $pattern = "/this->".substr($this->iCurrClass->iVars[$i][0],1)."/";
317             $isVarUsed=preg_match($pattern,trim($aLine));
318
319             $pattern = "/[^>\w]".trim(substr($this->iCurrClass->iVars[$i][0],1))."[^\w]/";
320             $isVarUsedWithoutThis=preg_match($pattern,trim($aLine));
321
322             if( $isVarUsed ) {
323                 $ret=true;
324                 $this->iCurrClass->iUsed[$i]=true;
325             }
326             elseif( $isVarUsedWithoutThis ) {               
327                 $this->iWarnings[] = "<b>Warning:</b> Possible use of <b>".$this->iCurrClass->iVars[$i][0]."</b> (Class ".$this->iCurrClass->GetName().") in ".
328                      $this->iCurrFileName.":".$aLineNbr." without a '\$this' qualifier.";
329             }
330         }
331         return $ret;
332     }
333         
334     function ParseClassVars($aLine) {
335         // Instance variables in $matches[$i], $i=1,2,3,...
336         $vdec = '(\$\w+)?\s*=?\s*(array\(\)|\d+\.\d+|\d+|".*"'."|'.*'".')?';
337         $vlist = "\s*$vdec\s*,?";
338         $pattern = "/^var $vlist$vlist$vlist$vlist$vlist;/";
339
340
341         $isVar=preg_match($pattern,trim($aLine),$matches);
342         if( !$isVar ) return false;
343         $n = ceil((count($matches)-1)/2);
344         for($i=0; $i<ceil((count($matches)-1)/2); ++$i) {
345             if( !isset($matches[2+$i*2]) )
346                 $matches[2+$i*2]="";
347             if( trim($matches[1+$i*2]) == "" ) {
348                 echo "****DEBUG #$i: line=$aLine<br>m1=".$matches[$i*2]."m2=".$matches[1+$i*2]."m3=".$matches[2+$i*2]."<p>";
349             }
350             else
351                 $this->iCurrClass->AddVar($matches[1+$i*2],$matches[2+$i*2]);
352         }
353         return true;
354     }
355         
356     // Factory function for classes
357     function &NewClassProp($aParent,$aName,$aLineNbr,$aFileName) {
358         return new ClassProp($aParent,$aName,$aLineNbr,$aFileName);
359     }
360         
361     // Factory function for methods
362     function &NewFuncProp($aClassName,$aName,$aLineNbr,$aArgs,$aArgsVal,$aShortComment) {
363         return new FuncProp($aClassName,$aName,$aLineNbr,$aArgs,$aArgsVal,$aShortComment,$this->iCurrFileName);
364     }
365         
366         function LineIndicatorMinor($aLineNbr) {
367                 echo "..$aLineNbr..";
368                 flush();
369         }
370
371         function LineIndicatorMajor($aLineNbr) {
372                 echo "<br>\n";
373         }
374
375         // Maintain brace count ignoring braces within strings and comments.
376         function BraceCount($aLine) {
377                 $n = strlen($aLine);
378                 $done = false;
379                 $prevc='';
380                 for( $i=0; $i<$n && !$done; ++$i ) {
381                         $cc = substr($aLine,$i,2);
382                         $c = substr($cc,0,1);
383                         if( $prevc != '\\' && $c=='"' && !$this->iHyphenMarks )
384                                 $this->iQuoteMarks = $this->iQuoteMarks ? 0 : 1;
385                                 
386                         if( $prevc != '\\' && $c=="'" && !$this->iQuoteMarks )
387                                 $this->iHyphenMarks = $this->iHyphenMarks ? 0 : 1;
388                         
389                         $this->iInString = $this->iHyphenMarks || $this->iQuoteMarks ? 1 : 0;
390                                 
391                         if( ($cc == '//' || $cc == '#') && !$this->iInString ) 
392                                 $done=true;
393                         else {
394                                 if( $cc == '/*' && !$this->iInString ) $this->iInComment = true;
395                                 elseif( $cc == '*/' && !$this->iInString )  $this->iInComment = false;
396                                 elseif( $c == '{' && !$this->iInComment && !$this->iInString ) ++$this->iBraceCnt;
397                                 elseif( $c == '}' && !$this->iInComment && !$this->iInString ) --$this->iBraceCnt;
398                         }
399                         $prevc = $c;
400                 }
401                 //echo " $this->iBraceCnt ($this->iInComment, $this->iInString) : ".htmlentities($aLine)."<br>\n";
402         }       
403         
404     function ParseLine($aLine,$aLineNbr) {
405
406                 
407     if( $aLineNbr % 50 == 0 )   {
408         $this->LineIndicatorMinor($aLineNbr);
409         if( $aLineNbr % 500 == 0 ) 
410                 $this->LineIndicatorMajor($aLineNbr);
411     }
412     
413     
414     $aLine = trim($aLine);
415     if( $aLine=='' ) return;
416     
417         $pattern = '/^\s*\/\//';
418         if( !$this->iInString && preg_match($pattern,$aLine) ) {
419             if( $this->iCommentBreak ) {
420                 $this->iLastComment = trim($aLine);
421                 $this->iCommentBreak = false;
422             }
423             else
424                 $this->iLastComment .= $aLine;
425             return;             
426         }
427         else
428             $this->iCommentBreak = true;
429                         
430         if( $this->iBraceCnt < 0 )
431             die("Syntax error in PHP file. Unmatched braces on line $aLineNbr");
432
433         if( $this->iBraceCnt > 0 ) {
434             if( $this->ParseClassVars($aLine) ) {
435                 return;
436             }
437             $this->CheckUsedVars($aLine,$aLineNbr);
438         }
439                         
440         // Is this a class definition of the form
441         // class classname {extends parentclass} \{
442         $pattern="/^(class)\s+(\w+)\s*(extends\s+(\w+\s*))?/i";
443         //$isClass=preg_match($pattern,trim($aLine),$matches);
444         if( !$this->iInString && preg_match($pattern,$aLine,$matches) ) {
445             $name = $matches[2];
446             if( isset($matches[4]) )    // Inheritance?
447                 $parent = $matches[4];
448             else
449                 $parent = "";
450             $this->iClasses[$this->iClassCnt] = $this->NewClassProp($parent,$name,$aLineNbr,$this->iCurrFileName);
451             $this->iCurrClass = &$this->iClasses[$this->iClassCnt];
452             $this->iClassCnt++;
453         }
454         else {  
455             // Look for a function definition with arguments which may have default
456             // values. The pattern below works for up to 10 arguments
457             // $matches[1]=function name
458             // $matches[2+($i)*2]=argument $i name [i=0,1,2,...]
459             // $matches[3+($i)*2]=argument $i value
460             // Number of arguments=ceil((count($matches)-2)/2)
461             // Note: We must use ceil() since if the last argument has no initialization
462             // the last two entries wont exist and we will get a floating point
463             // number back.
464
465             $quotchars = "[\w|£|#|$|%|&|\.|@\[\]|+|*|\/|-|\{|\}]+";
466             $argdef  = '\s*(\&?\$\w+)*=?(""|'."''".'|'."'".$quotchars."'".'|-?\d+|-?\d+\.\d+|\w+|"'.$quotchars.'"|array\(-?\d*,?-?\d*,?-?\d*,?\))?\s*,?';
467
468             $pattern = '/^function\s+(\w+)\s*\(\s*'.$argdef.$argdef.$argdef.$argdef.$argdef.$argdef.$argdef.$argdef.$argdef.$argdef.'\s*\)/i';
469
470             //$isFunction=preg_match($pattern,trim($aLine),$matches);
471             if( !$this->iInString && preg_match($pattern,$aLine,$matches) ) {
472                         $numArgs = ceil((count($matches)-2)/2);
473                         $fname = $matches[1];
474                         $args=array();
475                         $argsval=array();
476                         for($i=0; $i<$numArgs; ++$i) {
477                             $args[$i]=$matches[2+$i*2];
478                         if( isset($matches[3+$i*2]) )
479                                 $argsval[$i]=$matches[3+$i*2];
480                         }
481                         if( isset($this->iCurrClass) && $this->iCurrClass!=null && $this->iBraceCnt==1 )
482                         $this->iCurrClass->AddFunc($this->NewFuncProp($this->iCurrClass->GetName(),$fname,$aLineNbr,$args,$argsval,$this->iLastComment));
483                         elseif( $this->iBraceCnt==0 ) {
484                                 // Add a global function
485                                 //$aClassName,$aName,$aLineNbr,$aArgs,$aArgsVal,$aShortComment,$aFileName=""
486                                 $this->iGlobalFuncs[] = $this->NewFuncProp('',$fname,$aLineNbr,$args,$argsval,$this->iPrevLine);
487                         }
488                         else
489                             die("Syntax error in PHP file. Function definition within function. (".$this->iBraceCnt.")");
490
491                 // Clear comment once we used it
492             $this->iLastComment = "" ; 
493             }
494         }
495         $this->BraceCount($aLine);
496     }   
497 }
498
499
500 // Class Driver
501 // Parse a file and get all the functions and classed defined in that
502 // file. The methods and classes are stored in the properties
503 // iClasses and iFuncs and are each instances of ClassProp and FuncProp respectively
504 // To use this class just inherit the class and implement
505 // your own overloaded version of PostProcessing() (currently it just prints out the
506 // found methods)
507 class LintDriver {
508     var $iParser,$aFileName;
509         
510     function Driver($aFile) {
511         $this->iParser = $this->NewParser($aFile);
512     }
513         
514     function NewParser($aFile) {
515         return new Parser($aFile);
516     }
517                 
518     function Run() {
519         $this->iParser->Start();
520         $this->iParser->End();
521         $this->PostProcessing();
522     }
523         
524     function PostProcessing() {
525         $this->iParser->DoMapClasses();
526         $this->iParser->DoMapGlobalFuncs();             
527     }
528 }
529
530 // EOF
531 ?>