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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ vendor/
.phpunit.cache/
*.csv
.php-cs-fixer.cache
junit.xml
1 change: 1 addition & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ parameters:
excludePaths:
- tests/*
reportUnmatchedIgnoredErrors: true
treatPhpDocTypesAsCertain: false
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Disabling PHPDoc certainty – version and type-hint coverage checks.
The new treatPhpDocTypesAsCertain: false flag requires PHPStan ≥ 1.0.0 and will expose any methods or properties lacking native type declarations.

  1. Confirm phpstan/phpstan in your composer.json/lock is updated to a compatible version.
  2. Audit public APIs to ensure all parameters, return types, and properties have explicit native type hints.

Run:


🏁 Script executed:

#!/bin/bash
# Verify phpstan version constraint in composer files
grep -R '"phpstan/phpstan"' -n composer.json composer.lock

Length of output: 1326


PHPStan ≥1.0.0 requirement satisfied
Your composer.json pins "phpstan/phpstan": "^2.1", so treatPhpDocTypesAsCertain: false is fully supported.

Next, please audit your public API surface to ensure no type information is lost when PHPStan ignores PHPDoc types:

  • Review all public function signatures for missing parameter and return type declarations.
  • Check class properties to verify they declare native types.
🤖 Prompt for AI Agents
In phpstan.neon.dist at line 8, the setting treatPhpDocTypesAsCertain is set to
false, which requires PHPStan version 1.0.0 or higher and enforces native type
declarations. Confirm your composer.json and composer.lock files specify
phpstan/phpstan version 1.0.0 or above (which is already done). Next, audit all
public APIs in your codebase to ensure every public function has explicit native
parameter and return type declarations, and all class properties have native
type hints, removing reliance on PHPDoc types.

ignoreErrors:
-
identifier: missingType.iterableValue
Expand Down
44 changes: 31 additions & 13 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -1,27 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.5/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true"
cacheDirectory=".phpunit.cache"
executionOrder="depends,defects"
shortenArraysForExportThreshold="10"
requireCoverageMetadata="true"
beStrictAboutCoverageMetadata="true"
beStrictAboutOutputDuringTests="true"
displayDetailsOnPhpunitDeprecations="true"
failOnPhpunitDeprecation="true"
failOnRisky="true"
failOnWarning="true"
>
failOnRisky="true"
stopOnFailure="false"
executionOrder="random"
resolveDependencies="true">

<testsuites>
<testsuite name="default">
<testsuite name="All Tests">
<directory>tests</directory>
</testsuite>
</testsuites>

<source ignoreIndirectDeprecations="true" restrictNotices="true" restrictWarnings="true">
<coverage>
<include>
<directory>src</directory>
</include>
</source>
<exclude>
<directory>src/stubs</directory>
<file>src/bootstrap.php</file>
</exclude>
<report>
<html outputDirectory="coverage-html"/>
<text outputFile="coverage.txt"/>
<clover outputFile="coverage.xml"/>
</report>
</coverage>

<php>
<env name="APP_ENV" value="testing"/>
<ini name="memory_limit" value="512M"/>
<ini name="error_reporting" value="E_ALL"/>
<ini name="display_errors" value="1"/>
<ini name="display_startup_errors" value="1"/>
</php>

<logging>
<junit outputFile="junit.xml"/>
</logging>
</phpunit>
1 change: 1 addition & 0 deletions rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
])
->withSkip([
AddOverrideAttributeToOverriddenMethodsRector::class,
__DIR__.'/stubs',
])
->withPreparedSets(
deadCode: true,
Expand Down
90 changes: 90 additions & 0 deletions src/Configs/AbstractCsvConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,131 @@

use Phpcsv\CsvHelper\Contracts\CsvConfigInterface;

/**
* Abstract base class for CSV configuration implementations.
*
* Provides common properties and defines the abstract interface
* that concrete configuration classes must implement.
*/
abstract class AbstractCsvConfig implements CsvConfigInterface
{
/**
* The field delimiter character.
*/
protected string $delimiter = ',';

/**
* The field enclosure character.
*/
protected string $enclosure = '"';

/**
* The escape character.
*/
protected string $escape = '\\';

/**
* The file path.
*/
protected string $path = '';

/**
* The starting offset for reading.
*/
protected int $offset = 0;

/**
* Whether the CSV file has a header row.
*/
protected bool $hasHeader = true;

/**
* Gets the field delimiter character.
*
* @return string The delimiter character
*/
abstract public function getDelimiter(): string;

/**
* Sets the field delimiter character.
*
* @param string $delimiter The delimiter character to use
* @return CsvConfigInterface Returns the instance for method chaining
*/
abstract public function setDelimiter(string $delimiter): CsvConfigInterface;

/**
* Gets the field enclosure character.
*
* @return string The enclosure character
*/
abstract public function getEnclosure(): string;

/**
* Sets the field enclosure character.
*
* @param string $enclosure The enclosure character to use
* @return CsvConfigInterface Returns the instance for method chaining
*/
abstract public function setEnclosure(string $enclosure): CsvConfigInterface;

/**
* Gets the escape character.
*
* @return string The escape character
*/
abstract public function getEscape(): string;

/**
* Sets the escape character.
*
* @param string $escape The escape character to use
* @return CsvConfigInterface Returns the instance for method chaining
*/
abstract public function setEscape(string $escape): CsvConfigInterface;

/**
* Gets the file path.
*
* @return string The file path
*/
abstract public function getPath(): string;

/**
* Sets the file path.
*
* @param string $path The file path to use
* @return CsvConfigInterface Returns the instance for method chaining
*/
abstract public function setPath(string $path): CsvConfigInterface;

/**
* Gets the starting offset for reading.
*
* @return int The offset
*/
abstract public function getOffset(): int;

/**
* Sets the starting offset for reading.
*
* @param int $offset The offset to start reading from
* @return CsvConfigInterface Returns the instance for method chaining
*/
abstract public function setOffset(int $offset): CsvConfigInterface;

/**
* Checks if the CSV file has a header row.
*
* @return bool True if the file has headers, false otherwise
*/
abstract public function hasHeader(): bool;

/**
* Sets whether the CSV file has a header row.
*
* @param bool $hasHeader True if the file has headers, false otherwise
* @return CsvConfigInterface Returns the instance for method chaining
*/
abstract public function setHasHeader(bool $hasHeader): CsvConfigInterface;
}
86 changes: 86 additions & 0 deletions src/Configs/CsvConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,74 +4,160 @@

use Phpcsv\CsvHelper\Contracts\CsvConfigInterface;

/**
* Concrete implementation of CSV configuration.
*
* Provides a complete implementation of the CsvConfigInterface with
* default values and fluent interface for configuration management.
*/
class CsvConfig extends AbstractCsvConfig
{
/**
* Creates a new CSV configuration instance.
*
* @param string|null $path Optional file path to set initially
* @param bool $hasHeader Whether the CSV file has a header row (default: true)
*/
public function __construct(?string $path = null, bool $hasHeader = true)
{
if ($path !== null) {
$this->path = $path;
}
$this->hasHeader = $hasHeader;
}

/**
* Gets the field delimiter character.
*
* @return string The delimiter character
*/
public function getDelimiter(): string
{
return $this->delimiter;
}

/**
* Sets the field delimiter character.
*
* @param string $delimiter The delimiter character to use
* @return CsvConfigInterface Returns the instance for method chaining
*/
public function setDelimiter(string $delimiter): CsvConfigInterface
{
$this->delimiter = $delimiter;

return $this;
}

/**
* Gets the field enclosure character.
*
* @return string The enclosure character
*/
public function getEnclosure(): string
{
return $this->enclosure;
}

/**
* Sets the field enclosure character.
*
* @param string $enclosure The enclosure character to use
* @return CsvConfigInterface Returns the instance for method chaining
*/
public function setEnclosure(string $enclosure): CsvConfigInterface
{
$this->enclosure = $enclosure;

return $this;
}

/**
* Gets the escape character.
*
* @return string The escape character
*/
public function getEscape(): string
{
return $this->escape;
}

/**
* Sets the escape character.
*
* @param string $escape The escape character to use
* @return CsvConfigInterface Returns the instance for method chaining
*/
public function setEscape(string $escape): CsvConfigInterface
{
$this->escape = $escape;

return $this;
}

/**
* Gets the file path.
*
* @return string The file path
*/
public function getPath(): string
{
return $this->path;

}

/**
* Sets the file path.
*
* @param string $path The file path to use
* @return CsvConfigInterface Returns the instance for method chaining
*/
public function setPath(string $path): CsvConfigInterface
{
$this->path = $path;

return $this;
}

/**
* Gets the starting offset for reading.
*
* @return int The offset
*/
public function getOffset(): int
{
return $this->offset;
}

/**
* Sets the starting offset for reading.
*
* @param int $offset The offset to start reading from
* @return CsvConfigInterface Returns the instance for method chaining
*/
public function setOffset(int $offset): CsvConfigInterface
{
$this->offset = $offset;

return $this;
}

/**
* Checks if the CSV file has a header row.
*
* @return bool True if the file has headers, false otherwise
*/
public function hasHeader(): bool
{
return $this->hasHeader;
}

/**
* Sets whether the CSV file has a header row.
*
* @param bool $hasHeader True if the file has headers, false otherwise
* @return CsvConfigInterface Returns the instance for method chaining
*/
public function setHasHeader(bool $hasHeader): CsvConfigInterface
{
$this->hasHeader = $hasHeader;
Expand Down
Loading