<?php
/**moDO(Classes)(Parsers)(parse_php)
@(Description)
Parses php code and generates an array with the code of the classes and stand-alone functions found.
@(Description){note warn}
Curly brackets outside the code structures can break the parser!
@(Syntax){quote}
object `parse_php` ( string ~$path~ )
@(Parameters)
@(Parameters)($path)
Path to the php file to be parsed.
@(Return)
Returns an object that contains a variable `parsed` that contains the resulting array.
@(Examples)
@(Examples)(1){info}
`Example 1:` Basic usage example.
@(Examples)(2){code php}
$parser = new parse_php(__FILE__);
print_r($parser->parsed);
@(Changelog){list}
(1.0) ~Initial release.~
*/
/**
* Parses php code and generates an array with the code of the classes and stand-alone functions found.
* Note: Curly brackets outside the code structures can break the parser!
* @syntax new parse_php($path);
* @path string containing path to file to be parsed
*/
class parse_php {
public $parsed = false;
/**
* Validates the path parameter and starts the parsing.
* Once parsing done it sets the result in the $parsed variable.
* @path string representing valid absolute or relative path to a file.
*/
function __construct($path) {
if(is_file($path)) {
$this->parsed = $this->load($path);
}
}
/**
* This loads prepares the contents for parsing.
* It normalizes the line endings, builds lines array and looks up the structures.
* @path string representing valid absolute or relative path to a file.
*/
private function load($path) {
$file = file_get_contents($path);
$string = str_replace(Array("\r\n", "\r", "\n"), Array("\n", "\n", "\r\n"), $file);
$array = explode("\r\n", $string);
preg_match_all('/((abstract[ ])?(function|class|interface)[ ]+'
.'[a-z_\x7f-\xff][a-z0-9_\x7f-\xff]+[ ]*(\((.+)?\)[ ]*)?)'
.'([ ]*(extends|implements)[ ]*[a-z_\x7f-\xff]'
.'[a-z0-9_\x7f-\xff]+[ ]?)?[ ]*(\r|\n|\r\n)*[ ]*(\{)/i'
, $string
, $matches);
$filtered = Array();
foreach($matches[0] AS $match) {
list($first, $rest) = explode("\r\n", $match, 2);
$filtered[] = $first;
}
return $this->parse($array, $filtered);
}
/**
* The parser that loops the content lines and builds the result array.
* It accounts for nesting and skipps all functions that belong to a class.
* @lines array with the lines of the code file.
* @matches array containing the classes and possible stand-alone functions to be looked up.
*/
private function parse($lines, $matches) {
$key = false;
$track = false;
$nesting = 0;
$structures = Array();
foreach($lines AS $line) {
if($key === false)
$key = $this->array_value_in_string($line, $matches);
if($key !== false) {
if($nesting > 0)
$track = true;
$nesting = $nesting + substr_count($line, ' {');
$nesting = $nesting - substr_count($line, ' }');
$structures[$key][] = $line;
if($track && $nesting == 0) {
$track = false;
$key = false;
}
}
}
return array_values($structures);
}
/**
* Checks if any of the (array)needles are found in the (string)haystack.
* @syntax $this->array_value_in_string($string, $array);
* @haystack string containing the haystack subject of the search.
* @needles array containing the needles to be searched for.
*/
private function array_value_in_string($haystack, $needles) {
foreach($needles AS $key => $value) {
if(stristr($haystack, $value))
return $key;
}
return false;
}
}
/**
* Example execute self
*/
header('Content-type: text/plain');
$parser = new parse_php('test.php');
print_r($parser->parsed);