Question:
Background
Some "not_very_good" hosting services like to disable certain functions through php.ini
without any alternative. In particular, the sometimes useful parse_ini_file
. In my project (lightweight skeleton api) bypassed this way:
if (function_exists('parse_ini_file')) {
self::$settings = parse_ini_file(PROPERTIES);
return;
}
$content = preg_grep("/^[\w .]+=.*/", explode(PHP_EOL, file_get_contents(PROPERTIES)));
foreach ($content as $row) {
$row = strstr($row . ';', ';', true);
$key = trim(strstr($row, '=', true), " \n\r");
$value = trim(strstr($row, '=', false), " \"=\n\r");
self::$settings[ $key ] = $value;
}
Question
How can I now screw PHPUnit
to test a piece of code with a handwritten parser?
It would be ideal to designate it at runtime, but somehow I could not find any information about it.
PS Actually, I am at the very beginning of learning PHPUnit
and, perhaps, there is a true way, which I simply do not know.
Answer:
Came in from the back door.
Moved the self-written parser into a separate private method
private static function loadSettings() {
self::$settings = function_exists('parse_ini_file')
? parse_ini_file(PROPERTIES)
: self::loadSettingsHard();
}
private static function loadSettingsHard() {
$settings = [];
$content = preg_grep("/^[\w .]+=.*/", explode("\n", file_get_contents(PROPERTIES)));
foreach ($content as $row) {
$row = strstr($row . ';', ';', true);
$key = trim(strstr($row, '=', true), " \n\r");
$settings[ $key ] = trim(strstr($row, '=', false), " \"=\n\r");
}
return $settings;
}
In the test method, I applied a dirty hack with Reflection
public function testGetConfigValueHard() {
$method = new ReflectionMethod(
Config::class, 'loadSettingsHard'
);
$method->setAccessible(true);
$method->invoke(Config::class);
$this->assertEquals(
'localhost',
Config::get('db_host')
);
}
All in all, this task allowed me to look at testing from an unexpected angle. To cover this functionality with tests, I was forced to reduce the cyclomatic complexity of my code (in fact, I applied the principle of single responsibility), which is one of the pillars of a good application architecture.
As for answering the question, you can remove a function at runtime using the runkit_function_remove function, which is included in the runkit
PECL
extension. For many reasons, this approach is undesirable.