Skip to content

Commit

Permalink
Refactored and validated alias inheritance in schemas
Browse files Browse the repository at this point in the history
The changes in various classes improve reliability and readability of the code. In Schema.php, a refactored constructor and stricter filename validation ensures correct file handling. Enhanced match and validation handling in ValidatorSchema and SchemaDataPrep improves alias processing. Furthermore, explicit casting in Utils.php's mergeConfigs function ensures reliable array handling.
  • Loading branch information
SmetDenis committed Apr 5, 2024
1 parent 9dccfa1 commit 894090f
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 75 deletions.
1 change: 0 additions & 1 deletion src/Schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ public function __construct(null|array|string $csvSchemaFilenameOrArray = null)
&& \file_exists($csvSchemaFilenameOrArray)
) {
$this->filename = $csvSchemaFilenameOrArray;
$data = new Data();
$fileExtension = \pathinfo($csvSchemaFilenameOrArray, \PATHINFO_EXTENSION);

if ($fileExtension === 'yml' || $fileExtension === 'yaml') {
Expand Down
61 changes: 26 additions & 35 deletions src/SchemaDataPrep.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,35 +105,12 @@ public static function validateAlias(string $alias): void
throw new \InvalidArgumentException('Empty alias');
}

if (\preg_match(self::getAliasRegex(), $alias) === 0) {
$regex = self::getAliasRegex();
if ($regex !== '' && \preg_match($regex, $alias) === 0) {
throw new \InvalidArgumentException("Invalid alias: \"{$alias}\"");
}
}

private function parseAliasParts(string $inherit): array
{
$alias = null;
$keyword = null;
$columnName = null;
$rules = null;

$parts = \explode('/', $inherit);
if (\count($parts) === 2) {
[$alias, $keyword] = $parts;
} elseif (\count($parts) === 3) {
[$alias, $keyword, $columnName] = $parts;
} elseif (\count($parts) === 4) {
[$alias, $keyword, $columnName, $rules] = $parts;
}

return [
'alias' => $alias,
'keyword' => $keyword,
'column' => $columnName,
'rules' => $rules,
];
}

/**
* @return Schema[]
*/
Expand All @@ -142,6 +119,8 @@ private function prepareAliases(AbstractData $data): array
$includes = [];

foreach ($data->getArray('includes') as $alias => $includedPathOrArray) {
$alias = (string)$alias;

self::validateAlias($alias);

if (\is_array($includedPathOrArray)) {
Expand Down Expand Up @@ -171,8 +150,8 @@ private function buildFilenamePattern(): string
{
$inherit = $this->data->findString('filename_pattern.inherit');

if (\str_ends_with($inherit, '/filename_pattern')) {
$inheritParts = $this->parseAliasParts($inherit);
if ($inherit !== '') {
$inheritParts = self::parseAliasParts($inherit);
$parent = $this->getParentSchema($inheritParts['alias']);
return $parent->getData()->get('filename_pattern');
}
Expand All @@ -185,13 +164,13 @@ private function buildByKey(string $key = 'structural_rules'): array
$inherit = $this->data->findString("{$key}.inherit");

$parentConfig = [];
if (\preg_match('/' . self::ALIAS_REGEX . '\/' . $key . '$/i', $inherit) === 1) {
$inheritParts = $this->parseAliasParts($inherit);
if ($inherit !== '') {
$inheritParts = self::parseAliasParts($inherit);
$parent = $this->getParentSchema($inheritParts['alias']);
$parentConfig = $parent->getData()->getArray($key);
}

$result = Utils::mergeConfigs(self::DEFAULTS[$key], $parentConfig, $this->data->getArray($key));
$result = Utils::mergeConfigs((array)self::DEFAULTS[$key], $parentConfig, $this->data->getArray($key));
unset($result['inherit']);

return $result;
Expand All @@ -206,8 +185,8 @@ private function buildColumns(): array
$columnInherit = $columnData->getString('inherit');

$parentConfig = [];
if (\preg_match('/' . self::ALIAS_REGEX . '\/columns\/[^\/]+$/i', $columnInherit) === 1) {
$inheritParts = $this->parseAliasParts($columnInherit);
if ($columnInherit !== '') {
$inheritParts = self::parseAliasParts($columnInherit);
$parent = $this->getParentSchema($inheritParts['alias']);
$parentColumn = $parent->getColumn($inheritParts['column']);
if ($parentColumn === null) {
Expand Down Expand Up @@ -254,8 +233,8 @@ private function buildRules(array $rules, string $typeOfRules): array
$inherit = $rules['inherit'] ?? '';

$parentConfig = [];
if (\preg_match('/' . self::ALIAS_REGEX . '\/columns\/[^\/]+\/' . $typeOfRules . '$/i', $inherit) === 1) {
$inheritParts = $this->parseAliasParts($inherit);
if ($inherit !== '') {
$inheritParts = self::parseAliasParts($inherit);
$parent = $this->getParentSchema($inheritParts['alias']);
$parentColumn = $parent->getColumn($inheritParts['column']);
if ($parentColumn === null) {
Expand All @@ -265,9 +244,21 @@ private function buildRules(array $rules, string $typeOfRules): array
$parentConfig = $parentColumn->getData()->getArray($typeOfRules);
}

$actualRules = Utils::mergeConfigs(self::DEFAULTS[$typeOfRules], $parentConfig, $rules);
$actualRules = Utils::mergeConfigs((array)self::DEFAULTS[$typeOfRules], $parentConfig, $rules);
unset($actualRules['inherit']);

return $actualRules;
}

private static function parseAliasParts(string $inherit): array
{
$parts = \explode('/', $inherit);
self::validateAlias($parts[0]);

if (\count($parts) === 1) {
return ['alias' => $parts[0]];
}

return ['alias' => $parts[0], 'column' => $parts[1]];
}
}
3 changes: 3 additions & 0 deletions src/Utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,9 @@ public static function fixArgv(array $originalArgs): array
return $newArgumens;
}

/**
* @param array<string, null|array|bool|string>|int[]|string[] ...$configs
*/
public static function mergeConfigs(array ...$configs): array
{
$merged = (array)\array_shift($configs); // Start with the first array
Expand Down
36 changes: 18 additions & 18 deletions tests/SchemaInheritTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public function testOverideFilenamePattern(): void
'parent' => ['filename_pattern' => '/.*/i'],
],
'filename_pattern' => [
'inherit' => 'parent/filename_pattern',
'inherit' => 'parent',
],
]);

Expand All @@ -141,7 +141,7 @@ public function testOverideCsvFull(): void
],
],
],
'csv' => ['inherit' => 'parent/csv'],
'csv' => ['inherit' => 'parent'],
]);

isSame([
Expand Down Expand Up @@ -170,7 +170,7 @@ public function testOverideCsvPartial(): void
],
],
'csv' => [
'inherit' => 'parent/csv',
'inherit' => 'parent',
'encoding' => 'utf-32',
],
]);
Expand Down Expand Up @@ -199,7 +199,7 @@ public function testOverideStructuralRulesFull(): void
],
],
'structural_rules' => [
'inherit' => 'parent/structural_rules',
'inherit' => 'parent',
],
]);

Expand All @@ -223,7 +223,7 @@ public function testOverideStructuralRulesPartial1(): void
],
],
'structural_rules' => [
'inherit' => 'parent/structural_rules',
'inherit' => 'parent',
'allow_extra_columns' => true,
],
]);
Expand All @@ -240,7 +240,7 @@ public function testOverideStructuralRulesPartial2(): void
$schema = new Schema([
'includes' => ['parent' => ['structural_rules' => []]],
'structural_rules' => [
'inherit' => 'parent/structural_rules',
'inherit' => 'parent',
'allow_extra_columns' => true,
],
]);
Expand Down Expand Up @@ -275,13 +275,13 @@ public function testOverideColumnFull(): void
$schema = new Schema([
'includes' => ['parent' => ['columns' => [$parentColum0, $parentColum1]]],
'columns' => [
['inherit' => 'parent/columns/0'],
['inherit' => 'parent/columns/1'],
['inherit' => 'parent/columns/0:'],
['inherit' => 'parent/columns/1:'],
['inherit' => 'parent/columns/Name'],
['inherit' => 'parent/columns/0:Name'],
['inherit' => 'parent/columns/1:Name'],
['inherit' => 'parent/0'],
['inherit' => 'parent/1'],
['inherit' => 'parent/0:'],
['inherit' => 'parent/1:'],
['inherit' => 'parent/Name'],
['inherit' => 'parent/0:Name'],
['inherit' => 'parent/1:Name'],
],
]);

Expand Down Expand Up @@ -315,7 +315,7 @@ public function testOverideColumnPartial(): void
'includes' => ['parent' => ['columns' => [$parentColum]]],
'columns' => [
[
'inherit' => 'parent/columns/Name',
'inherit' => 'parent/Name',
'name' => 'Child name',
'rules' => [
'is_int' => true,
Expand Down Expand Up @@ -366,7 +366,7 @@ public function testOverideColumnRulesFull(): void
'columns' => [
[
'name' => 'Child name',
'rules' => ['inherit' => 'parent/columns/0:/rules'],
'rules' => ['inherit' => 'parent/0:'],
],
],
]);
Expand Down Expand Up @@ -410,7 +410,7 @@ public function testOverideColumnRulesPartial(): void
[
'name' => 'Child name',
'rules' => [
'inherit' => 'parent/columns/0:/rules',
'inherit' => 'parent/0:',
'allow_values' => ['d', 'c'],
'length_max' => 100,
],
Expand Down Expand Up @@ -456,7 +456,7 @@ public function testOverideColumnAggregateRulesFull(): void
'columns' => [
[
'name' => 'Child name',
'aggregate_rules' => ['inherit' => 'parent/columns/0:/aggregate_rules'],
'aggregate_rules' => ['inherit' => 'parent/0:'],
],
],
]);
Expand Down Expand Up @@ -498,7 +498,7 @@ public function testOverideColumnAggregateRulesPartial(): void
[
'name' => 'Child name',
'aggregate_rules' => [
'inherit' => 'parent/columns/0:/aggregate_rules',
'inherit' => 'parent/0:',
'sum_max' => 4200,
'sum_min' => 1,
],
Expand Down
12 changes: 6 additions & 6 deletions tests/schemas/inherit/child-of-child.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@ filename_pattern: /child-of-child-\d.csv$/i


csv:
inherit: 'parent-1_0/csv'
delimiter: 'dd'
quote_char: 'qq'
enclosure: 'ee'
inherit: parent-1_0
delimiter: dd
quote_char: qq
enclosure: ee
encoding: utf-32
bom: false


structural_rules:
inherit: 'parent-1_0/structural_rules'
inherit: parent-1_0
allow_extra_columns: false

columns:
- inherit: 'parent-1_0/columns/Second Column'
- inherit: parent-1_0/Second Column
24 changes: 12 additions & 12 deletions tests/schemas/inherit/child.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,56 +21,56 @@ includes:


filename_pattern:
inherit: 'parent/filename_pattern'
inherit: parent


csv:
inherit: 'parent/csv'
inherit: parent
header: true


structural_rules:
inherit: 'parent/structural_rules'
inherit: parent
strict_column_order: true


columns:
# 0
- inherit: 'parent/columns/Name'
- inherit: parent/Name

# 1
- inherit: 'parent/columns/Name'
- inherit: parent/Name
name: Overridden name by column name

# 2
- inherit: 'parent/columns/0:'
- inherit: 'parent/0:'
name: Overridden name by column index

# 3
- inherit: 'parent/columns/0:Name'
- inherit: parent/0:Name
name: Overridden name by column index and column name

# 4
- inherit: 'parent/columns/0:Name'
- inherit: parent/0:Name
name: Overridden name by column index and column name + added rules
rules:
length_min: 1

# 5
- inherit: 'parent/columns/0:Name'
- inherit: parent/0:Name
name: Overridden name by column index and column name + added aggregate rules
aggregate_rules:
nth_num: [ 10, 0.05 ]

# 6
- name: Overridden only rules
rules:
inherit: 'parent/columns/0:Name/rules'
inherit: parent/0:Name

# 7
- name: Overridden only aggregation rules
aggregate_rules:
inherit: 'parent/columns/0:Name/aggregate_rules'
inherit: parent/0:Name

# 8
- inherit: 'parent/columns/Second Column'
- inherit: parent/Second Column
6 changes: 3 additions & 3 deletions tests/schemas/inherit/parent.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ filename_pattern: /parent-\d.csv$/i

csv:
header: false
delimiter: 'd'
quote_char: 'q'
enclosure: 'e'
delimiter: d
quote_char: q
enclosure: e
encoding: utf-16
bom: true

Expand Down

0 comments on commit 894090f

Please sign in to comment.