Skip to content

Commit b984913

Browse files
committed
Adding \Fuko\Source\Code
1 parent 89f5f5c commit b984913

3 files changed

Lines changed: 263 additions & 2 deletions

File tree

README.md

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,43 @@
33
**Fuko\\Source** is a small PHP library that helps you to extracts chunks of
44
source code identified by code references, e.g. filename + line.
55

6-
## Basic Use
6+
It is really simple to use. Imagine you want to extract a piece of code, and
7+
you know what source code line exactly you want to reference: for example
8+
`/var/www/html/index.php` at line 17. You must first create a new `\Fuko\Source\Code`
9+
object, and then call the `getLinesAt()` method:
710

8-
...
11+
```php
12+
include __DIR__ . '/vendor/autoload.php';
13+
14+
$source = new \Fuko\Source\Code('/var/www/html/index.php');
15+
print_r($source->getLinesAt(17));
16+
/*
17+
Array
18+
(
19+
[14] => Illuminate\Support\ClassLoader::register();
20+
[15] => Illuminate\Support\ClassLoader::addDirectories(array(CLASS_DIR,CONTROLLERS,CONTROLLERS.'Middleware/', MODELS, CONTROLLERS.'Admin/'));
21+
[16] =>
22+
[17] => $FileLoader = new FileLoader(new Filesystem, RESOURCES.'lang');
23+
[18] => $translator = new Translator($FileLoader, 'en');
24+
[19] => $Container = new Container();
25+
[20] => $validation = new Factory($translator, $Container);
26+
)
27+
*/
28+
29+
```
30+
31+
By default there are 7 lines of code (LOCs) returned, but you can specify a
32+
different number in the range of 1 to 20 (as defined in `\Fuko\Source\Code::LOC_MAX`):
33+
```php
34+
include __DIR__ . '/vendor/autoload.php';
35+
36+
$source = new \Fuko\Source\Code('/var/www/html/index.php');
37+
print_r($source->getLinesAt(17, 1));
38+
/*
39+
Array
40+
(
41+
[17] => $FileLoader = new FileLoader(new Filesystem, RESOURCES.'lang');
42+
)
43+
*/
44+
45+
```

src/Code.php

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
<?php /**
2+
* @category Fuko
3+
* @package Fuko\Source
4+
*
5+
* @author Kaloyan Tsvetkov (KT) <kaloyan@kaloyan.info>
6+
* @link https://github.com/fuko-php/source/
7+
* @license https://opensource.org/licenses/MIT
8+
*/
9+
10+
namespace Fuko\Source;
11+
12+
use InvalidArgumentException;
13+
use RuntimeException;
14+
15+
use function ceil;
16+
use function file_exists;
17+
use function fclose;
18+
use function fgets;
19+
use function fopen;
20+
21+
/**
22+
* Source Code Reader
23+
*
24+
* Use this class to extract source code lines using a code reference (filename + line)
25+
*
26+
* @package Fuko\Source
27+
*/
28+
class Code
29+
{
30+
/**
31+
* @var integer default LOCs (lines of code) returned
32+
* @see \Fuko\Source\Code::getLinesAt()
33+
*/
34+
const LOC_DEFAULT = 7;
35+
36+
/**
37+
* @var integer max numer of LOCs (lines of code) returned
38+
* @see \Fuko\Source\Code::getLinesAt()
39+
*/
40+
const LOC_MAX = 20;
41+
42+
/**
43+
* @var string source filename
44+
*/
45+
protected $sourceFilename = '';
46+
47+
/**
48+
* Source Code Constructor
49+
*
50+
* @param string $filename
51+
*/
52+
function __construct(string $filename)
53+
{
54+
$this->sourceFilename = $filename;
55+
56+
}
57+
58+
/**
59+
* Get Source Code Lines
60+
*
61+
* @param integer $line target line of the reference
62+
* @param integer $locs how many LOCs (lines of code) to return
63+
* @return array
64+
* @throws InvalidArgumentException
65+
* @throws RuntimeException
66+
*/
67+
function getLinesAt(int $line, int $locs = null) : array
68+
{
69+
if (!file_exists($this->sourceFilename))
70+
{
71+
throw new RuntimeException(
72+
"Source code file not found: {$this->sourceFilename}",
73+
20004
74+
);
75+
}
76+
77+
if ($line < 1)
78+
{
79+
throw new InvalidArgumentException(
80+
"The \$line argument must be a positive integer, instead {$line} given",
81+
20005
82+
);
83+
}
84+
85+
if (null !== $locs)
86+
{
87+
if ($locs < 1)
88+
{
89+
throw new InvalidArgumentException(
90+
"The \$locs argument must be a positive integer, instead {$locs} given",
91+
20006
92+
);
93+
}
94+
}
95+
96+
// by default show 7 lines, but do not go beyond 20
97+
//
98+
$locs = $locs ?? static::LOC_DEFAULT;
99+
if (static::LOC_MAX < $locs)
100+
{
101+
$locs = static::LOC_MAX;
102+
}
103+
104+
$before = ceil(($locs-1)/2);
105+
$after = $locs -1 - $before;
106+
107+
$from = ($from = $line - $before) > 1 ? $from : 1;
108+
$to = $line + $after;
109+
110+
$lines = array();
111+
$fp = fopen($this->sourceFilename, 'r');
112+
113+
$atLine = 0;
114+
while (false !== ($lineCode = fgets($fp)))
115+
{
116+
if (++$atLine < $from)
117+
{
118+
continue;
119+
}
120+
if ($atLine > $to)
121+
{
122+
break;
123+
}
124+
125+
$lines[ $atLine ] = $lineCode;
126+
}
127+
fclose($fp);
128+
129+
return $lines;
130+
}
131+
}

tests/CodeTest.php

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
3+
namespace Fuko\Source\Tests;
4+
5+
use Fuko\Source\Code;
6+
use PHPUnit\Framework\TestCase;
7+
8+
use function file;
9+
10+
class CodeTest extends TestCase
11+
{
12+
/**
13+
* @covers Fuko\Source\Code::getLinesAt()
14+
* @expectedException RuntimeException
15+
*/
16+
function testMissingFile()
17+
{
18+
$source = new Code('/i/am/not/here');
19+
$source->getLinesAt(123);
20+
}
21+
22+
/**
23+
* @covers Fuko\Source\Code::getLinesAt()
24+
* @expectedException InvalidArgumentException
25+
*/
26+
function testNegativeSourceLine()
27+
{
28+
$source = new Code(__FILE__);
29+
$source->getLinesAt(-2);
30+
}
31+
32+
/**
33+
* @covers Fuko\Source\Code::getLinesAt()
34+
* @expectedException InvalidArgumentException
35+
*/
36+
function testZeroSourceLine()
37+
{
38+
$source = new Code(__FILE__);
39+
$source->getLinesAt(0);
40+
}
41+
42+
/**
43+
* @covers Fuko\Source\Code::getLinesAt()
44+
* @expectedException InvalidArgumentException
45+
*/
46+
function testNegativeLOCs()
47+
{
48+
$source = new Code(__FILE__);
49+
$source->getLinesAt(2, -2);
50+
}
51+
52+
/**
53+
* @covers Fuko\Source\Code::getLinesAt()
54+
* @expectedException InvalidArgumentException
55+
*/
56+
function testZeroLOCs()
57+
{
58+
$source = new Code(__FILE__);
59+
$source->getLinesAt(1, 0);
60+
}
61+
62+
/**
63+
* @covers Fuko\Source\Code::getLinesAt()
64+
*/
65+
function testGetLinesAt()
66+
{
67+
$source = new Code($composer = __DIR__ . '/../composer.json');
68+
$lines = file($composer);
69+
70+
$this->assertEquals(
71+
$source->getLinesAt(1, 1),
72+
[ 1 => $lines[0] ]
73+
);
74+
75+
$this->assertEquals(
76+
$source->getLinesAt(4), [
77+
1 => $lines[0],
78+
2 => $lines[1],
79+
3 => $lines[2],
80+
4 => $lines[3],
81+
5 => $lines[4],
82+
6 => $lines[5],
83+
7 => $lines[6],
84+
]);
85+
86+
$this->assertEquals(
87+
$source->getLinesAt(4, 3), [
88+
3 => $lines[2],
89+
4 => $lines[3],
90+
5 => $lines[4],
91+
]);
92+
}
93+
}

0 commit comments

Comments
 (0)