Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .github/workflows/on-pr-merged.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: After-Merge Chores
on:
pull_request:
types:
- closed
# branches:
# - FRAMEWORK_6_0
workflow_dispatch:

jobs:
PostMerge:
if: github.event.pull_request.merged == true
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.3
extensions: bcmath, ctype, curl, dom, gd, gettext, iconv, imagick, json, ldap, mbstring, mysql, opcache, openssl, pcntl, pdo, posix, redis, soap, sockets, sqlite, tokenizer, xmlwriter, xdebug
ini-values: post_max_size=512M, max_execution_time=360
coverage: xdebug
tools: php-cs-fixer, phpunit:${{ matrix.phpunit-versions }}, composer:v2
- name: Setup Github Token as composer credential
run: composer config -g github-oauth.github.com ${{ secrets.GITHUB_TOKEN }}
- name: Install dependencies and local tools
run: |
COMPOSER_ROOT_VERSION=dev-FRAMEWORK_6_0 composer config minimum-stability dev
COMPOSER_ROOT_VERSION=dev-FRAMEWORK_6_0 composer config prefer-stable true
COMPOSER_ROOT_VERSION=dev-FRAMEWORK_6_0 composer install --no-interaction --no-progress

43 changes: 43 additions & 0 deletions .github/workflows/on-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Pull Request Chores
on:
pull_request:
branches:
- FRAMEWORK_6_0
workflow_dispatch:

jobs:
CI:
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.3
extensions: bcmath, ctype, curl, dom, gd, gettext, iconv, imagick, json, ldap, mbstring, mysql, opcache, openssl, pcntl, pdo, posix, redis, soap, sockets, sqlite, tokenizer, xmlwriter, xdebug
ini-values: post_max_size=512M, max_execution_time=360
coverage: xdebug
tools: php-cs-fixer, phpunit:${{ matrix.phpunit-versions }}, composer:v2
- name: Setup Github Token as composer credential
run: composer config -g github-oauth.github.com ${{ secrets.GITHUB_TOKEN }}
- name: Install dependencies and local tools
run: |
COMPOSER_ROOT_VERSION=dev-FRAMEWORK_6_0 composer config minimum-stability dev
COMPOSER_ROOT_VERSION=dev-FRAMEWORK_6_0 composer config prefer-stable true
COMPOSER_ROOT_VERSION=dev-FRAMEWORK_6_0 composer install --no-interaction --no-progress

- name: Run PHPUnit
run: vendor/bin/phpunit --testdox

- name: Run php-cs-fixer
run: vendor/bin/php-cs-fixer check -vvv

- name: Run phpstan (mandatory level)
run: vendor/bin/phpstan --no-progress

- name: Run phpstan (level 9, allowed to fail)
run: vendor/bin/phpstan --no-progress --level=9
continue-on-error: true

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ tools/
.php-cs-fixer.cache
.phpunit.cache/
composer.lock
.phpunit.result.cache
4 changes: 2 additions & 2 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
<?php
$potentialDirs = ['/lib', '/src', '/test'];
$potentialDirs = ['/lib', '/src', '/test', '/tests'];

$finder = (new PhpCsFixer\Finder());

foreach ($potentialDirs as $dir) {
$full = __DIR__ . $dir;
if (is_dir($full)) {
$finder->in($full);
}
}

$finder->exclude(['fixtures']);

return (new PhpCsFixer\Config())
->setRules([
Expand Down
1 change: 0 additions & 1 deletion .phpunit.result.cache

This file was deleted.

38 changes: 25 additions & 13 deletions src/PhpConfigFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Stringable;
use RuntimeException;
use InvalidArgumentException;

use function file_exists;
use function file_get_contents;
use function file_put_contents;
Expand All @@ -17,7 +18,6 @@
use function trim;
use function ltrim;
use function get_defined_vars;
use function eval;
use function var_export;
use function in_array;

Expand Down Expand Up @@ -56,15 +56,15 @@
return $this->contentBetweenHeaderAndFooter;
}

public function readConfigFile()
public function readConfigFile(): self
{
// Read the config file and parse it into an array
if (!file_exists($this->configFilePath)) {
throw new \RuntimeException("Config file does not exist: {$this->configFilePath}");
if (!file_exists((string) $this->configFilePath)) {
throw new RuntimeException("Config file does not exist: {$this->configFilePath}");
}
$configContent = file_get_contents($this->configFilePath);
$configContent = file_get_contents((string) $this->configFilePath);
if ($configContent === false) {
throw new \RuntimeException("Failed to read config file: {$this->configFilePath}");
throw new RuntimeException("Failed to read config file: {$this->configFilePath}");
}
// Strip leading and trailing php tags
$configContent = trim($configContent);
Expand All @@ -77,31 +77,37 @@
}
$this->content = $configContent;
// Get everything before $header
$headerStartPos = strpos($configContent, $this->header);
$headerStartPos = strpos($configContent, (string) $this->header);
$headerEndPos = 0;
$this->contentBeforeHeader = '';
if ($headerStartPos === false) {
$headerStartPos = 0;
} else {
$headerEndPos = $headerStartPos + strlen($this->header);
$headerEndPos = $headerStartPos + strlen((string) $this->header);
$this->contentBeforeHeader = substr($configContent, 0, $headerStartPos);
}

// Get everything after $footer
$footerStartPos = strpos($configContent, $this->footer);
$footerStartPos = strpos($configContent, (string) $this->footer);
if ($footerStartPos === false) {
$this->contentAfterFooter = '';
$footerStartPos = strlen($configContent);
$footerEndPos = $footerStartPos;
} else {
$footerEndPos = $footerStartPos + strlen($this->footer);
$footerEndPos = $footerStartPos + strlen((string) $this->footer);
$this->contentAfterFooter = substr($configContent, $footerEndPos);
}
$this->contentBetweenHeaderAndFooter = substr($configContent, $headerEndPos, $footerStartPos - $headerEndPos);
// Parse the content into an array (assuming it's a PHP array)
return $this;
}


/**
* Parse the content between the header and footer into an array
* @param string $area The area to parse from. Can be 'contentBetweenHeaderAndFooter', 'contentBeforeHeader', 'contentAfterFooter', or 'content'.
* @return array<mixed> The parsed content as an array
* @throws InvalidArgumentException If the area is invalid
*/
public function parseContent(string $area = 'content'): array
{
// TODO: Ensure to prevent any output from eval
Expand All @@ -117,7 +123,12 @@
throw new \InvalidArgumentException("Invalid area to parse from: $area");
}

public function writeConfigFile(array $config): void
/**
* Write the config file with the given array
* @param array<mixed> $config The config array to write
* @return self
*/
public function writeConfigFile(array $config): self
{
// Convert the array back to a string
$configContent = "<?php\n" .
Expand All @@ -127,7 +138,7 @@
if (is_array($value)) {
$configContent .= '$' . $key . ' = ' . var_export($value, true) . ";\n";
} else {
$configContent .= '$' . $key . ' = \'' . $value . "';\n";

Check failure on line 141 in src/PhpConfigFile.php

View workflow job for this annotation

GitHub Actions / CI

Binary operation "." between non-falsy-string and mixed results in an error.
}
}
$configContent .= "\n" .
Expand All @@ -135,6 +146,7 @@
$this->footer . "\n" .
$this->contentAfterFooter;
// Write the content back to the file
file_put_contents($this->configFilePath, $configContent);
file_put_contents((string) $this->configFilePath, $configContent);
return $this;
}
}
20 changes: 10 additions & 10 deletions test/unit/PhpConfigFileTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
*/
class PhpConfigFileTest extends TestCase
{
public function testReadEmptyConfigFile()
public function testReadEmptyConfigFile(): void
{
$file = new PhpConfigFile(
configFilePath: __DIR__ . '/../fixtures/EmptyConfigFile.php',
Expand All @@ -23,7 +23,7 @@
$this->assertEquals('', $file->getContent());
}

public function testReadEmptyConfigFileWithComment()
public function testReadEmptyConfigFileWithComment(): void
{
$file = new PhpConfigFile(
configFilePath: __DIR__ . '/../fixtures/EmptyConfigFileWithComment.php',
Expand All @@ -32,7 +32,7 @@
$this->assertEquals('// To be done', $file->getContent());
}

public function testReadConfigFileDefaultsAndOverridesWorkAsExpected()
public function testReadConfigFileDefaultsAndOverridesWorkAsExpected(): void
{
$file = new PhpConfigFile(
configFilePath: __DIR__ . '/../fixtures/WithPreHeaderAndPostFooterContent.php',
Expand All @@ -49,7 +49,7 @@

}

public function testReadConfigFileIgnoringBeforeHeaderAndAfterFooter()
public function testReadConfigFileIgnoringBeforeHeaderAndAfterFooter(): void
{
$file = new PhpConfigFile(
configFilePath: __DIR__ . '/../fixtures/WithPreHeaderAndPostFooterContent.php',
Expand All @@ -64,32 +64,32 @@
$this->assertEquals('not you the other one', $betweenContent['you'], 'Variable you not found in content');
$this->assertEquals('set', $betweenContent['something'], 'Variable something overwritten by footer in content');
$this->assertArrayNotHasKey('me', $betweenContent, 'Variable me found in content but only exists before header and after footer');

}

public function testNestedModernArrayFormat()
public function testNestedModernArrayFormat(): void
{
$file = new PhpConfigFile(
configFilePath: __DIR__ . '/../fixtures/NestedModernArrayFormat.php',
);
$file->readConfigFile();
$allContent = $file->parseContent();
$this->assertNotEmpty($allContent['config']['key3']['subkey1']);

Check failure on line 77 in test/unit/PhpConfigFileTest.php

View workflow job for this annotation

GitHub Actions / CI

Cannot access offset 'subkey1' on mixed.

Check failure on line 77 in test/unit/PhpConfigFileTest.php

View workflow job for this annotation

GitHub Actions / CI

Cannot access offset 'key3' on mixed.
$this->assertEquals('subsubvalue1', $allContent['config']['key3']['subkey2']['subsubkey1']);

Check failure on line 78 in test/unit/PhpConfigFileTest.php

View workflow job for this annotation

GitHub Actions / CI

Cannot access offset 'subsubkey1' on mixed.

Check failure on line 78 in test/unit/PhpConfigFileTest.php

View workflow job for this annotation

GitHub Actions / CI

Cannot access offset 'subkey2' on mixed.

Check failure on line 78 in test/unit/PhpConfigFileTest.php

View workflow job for this annotation

GitHub Actions / CI

Cannot access offset 'key3' on mixed.
}

public function testClassicHordeFormat()
public function testClassicHordeFormat(): void
{
$file = new PhpConfigFile(
configFilePath: __DIR__ . '/../fixtures/ClassicHordeFormat.php',
);
$file->readConfigFile();
$allContent = $file->parseContent();
$this->assertNotEmpty($allContent['conf']['sql']['hostspec']);

Check failure on line 88 in test/unit/PhpConfigFileTest.php

View workflow job for this annotation

GitHub Actions / CI

Cannot access offset 'sql' on mixed.

Check failure on line 88 in test/unit/PhpConfigFileTest.php

View workflow job for this annotation

GitHub Actions / CI

Cannot access offset 'hostspec' on mixed.
$this->assertTrue($allContent['conf']['readwritesplit']);

Check failure on line 89 in test/unit/PhpConfigFileTest.php

View workflow job for this annotation

GitHub Actions / CI

Cannot access offset 'readwritesplit' on mixed.
}

public function testWriteEmptyFileWithHeaderAndFooter()
public function testWriteEmptyFileWithHeaderAndFooter(): void
{
$file = new PhpConfigFile(
configFilePath: 'deleteme',
Expand All @@ -98,15 +98,15 @@
);
$file->writeConfigFile([]);
$this->assertFileExists('deleteme');
$contentString = file_get_contents('deleteme');
$contentString = (string) file_get_contents('deleteme');
$this->assertStringContainsString('/* Begin */', $contentString, 'Header not found');
$this->assertStringContainsString('/* End */', $contentString, 'Footer not found');
$contentValues = $file->parseContent();
$this->assertEmpty($contentValues, 'Content not empty');
unlink('deleteme');
}

public function testWriteFailure()
public function testReadFailure(): void
{
$this->expectException(\RuntimeException::class);
$file = new PhpConfigFile(
Expand Down
Loading