diff --git a/README.md b/README.md index 7d8046d..5976301 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,43 @@ **Fuko\\Source** is a small PHP library that helps you to extracts chunks of source code identified by code references, e.g. filename + line. -## Basic Use +It is really simple to use. Imagine you want to extract a piece of code, and +you know what source code line exactly you want to reference: for example +`/var/www/html/index.php` at line 17. You must first create a new `\Fuko\Source\Code` +object, and then call the `getLinesAt()` method: -... +```php +include __DIR__ . '/vendor/autoload.php'; + +$source = new \Fuko\Source\Code('/var/www/html/index.php'); +print_r($source->getLinesAt(17)); +/* +Array +( + [14] => Illuminate\Support\ClassLoader::register(); + [15] => Illuminate\Support\ClassLoader::addDirectories(array(CLASS_DIR,CONTROLLERS,CONTROLLERS.'Middleware/', MODELS, CONTROLLERS.'Admin/')); + [16] => + [17] => $FileLoader = new FileLoader(new Filesystem, RESOURCES.'lang'); + [18] => $translator = new Translator($FileLoader, 'en'); + [19] => $Container = new Container(); + [20] => $validation = new Factory($translator, $Container); +) +*/ + +``` + +By default there are 7 lines of code (LOCs) returned, but you can specify a +different number in the range of 1 to 20 (as defined in `\Fuko\Source\Code::LOC_MAX`): +```php +include __DIR__ . '/vendor/autoload.php'; + +$source = new \Fuko\Source\Code('/var/www/html/index.php'); +print_r($source->getLinesAt(17, 1)); +/* +Array +( + [17] => $FileLoader = new FileLoader(new Filesystem, RESOURCES.'lang'); +) +*/ + +``` diff --git a/src/Code.php b/src/Code.php new file mode 100644 index 0000000..cdeaf6f --- /dev/null +++ b/src/Code.php @@ -0,0 +1,131 @@ + +* @link https://github.com/fuko-php/source/ +* @license https://opensource.org/licenses/MIT +*/ + +namespace Fuko\Source; + +use InvalidArgumentException; +use RuntimeException; + +use function ceil; +use function file_exists; +use function fclose; +use function fgets; +use function fopen; + +/** +* Source Code Reader +* +* Use this class to extract source code lines using a code reference (filename + line) +* +* @package Fuko\Source +*/ +class Code +{ + /** + * @var integer default LOCs (lines of code) returned + * @see \Fuko\Source\Code::getLinesAt() + */ + const LOC_DEFAULT = 7; + + /** + * @var integer max numer of LOCs (lines of code) returned + * @see \Fuko\Source\Code::getLinesAt() + */ + const LOC_MAX = 20; + + /** + * @var string source filename + */ + protected $sourceFilename = ''; + + /** + * Source Code Constructor + * + * @param string $filename + */ + function __construct(string $filename) + { + $this->sourceFilename = $filename; + + } + + /** + * Get Source Code Lines + * + * @param integer $line target line of the reference + * @param integer $locs how many LOCs (lines of code) to return + * @return array + * @throws InvalidArgumentException + * @throws RuntimeException + */ + function getLinesAt(int $line, int $locs = null) : array + { + if (!file_exists($this->sourceFilename)) + { + throw new RuntimeException( + "Source code file not found: {$this->sourceFilename}", + 20004 + ); + } + + if ($line < 1) + { + throw new InvalidArgumentException( + "The \$line argument must be a positive integer, instead {$line} given", + 20005 + ); + } + + if (null !== $locs) + { + if ($locs < 1) + { + throw new InvalidArgumentException( + "The \$locs argument must be a positive integer, instead {$locs} given", + 20006 + ); + } + } + + // by default show 7 lines, but do not go beyond 20 + // + $locs = $locs ?? static::LOC_DEFAULT; + if (static::LOC_MAX < $locs) + { + $locs = static::LOC_MAX; + } + + $before = ceil(($locs-1)/2); + $after = $locs -1 - $before; + + $from = ($from = $line - $before) > 1 ? $from : 1; + $to = $line + $after; + + $lines = array(); + $fp = fopen($this->sourceFilename, 'r'); + + $atLine = 0; + while (false !== ($lineCode = fgets($fp))) + { + if (++$atLine < $from) + { + continue; + } + if ($atLine > $to) + { + break; + } + + $lines[ $atLine ] = $lineCode; + } + fclose($fp); + + return $lines; + } +} diff --git a/tests/CodeTest.php b/tests/CodeTest.php new file mode 100644 index 0000000..fe86c86 --- /dev/null +++ b/tests/CodeTest.php @@ -0,0 +1,93 @@ +getLinesAt(123); + } + + /** + * @covers Fuko\Source\Code::getLinesAt() + * @expectedException InvalidArgumentException + */ + function testNegativeSourceLine() + { + $source = new Code(__FILE__); + $source->getLinesAt(-2); + } + + /** + * @covers Fuko\Source\Code::getLinesAt() + * @expectedException InvalidArgumentException + */ + function testZeroSourceLine() + { + $source = new Code(__FILE__); + $source->getLinesAt(0); + } + + /** + * @covers Fuko\Source\Code::getLinesAt() + * @expectedException InvalidArgumentException + */ + function testNegativeLOCs() + { + $source = new Code(__FILE__); + $source->getLinesAt(2, -2); + } + + /** + * @covers Fuko\Source\Code::getLinesAt() + * @expectedException InvalidArgumentException + */ + function testZeroLOCs() + { + $source = new Code(__FILE__); + $source->getLinesAt(1, 0); + } + + /** + * @covers Fuko\Source\Code::getLinesAt() + */ + function testGetLinesAt() + { + $source = new Code($composer = __DIR__ . '/../composer.json'); + $lines = file($composer); + + $this->assertEquals( + $source->getLinesAt(1, 1), + [ 1 => $lines[0] ] + ); + + $this->assertEquals( + $source->getLinesAt(4), [ + 1 => $lines[0], + 2 => $lines[1], + 3 => $lines[2], + 4 => $lines[3], + 5 => $lines[4], + 6 => $lines[5], + 7 => $lines[6], + ]); + + $this->assertEquals( + $source->getLinesAt(4, 3), [ + 3 => $lines[2], + 4 => $lines[3], + 5 => $lines[4], + ]); + } +}