diff --git a/.github/workflows/demo.yml b/.github/workflows/demo.yml
index 305e6b75..8c4b63d1 100644
--- a/.github/workflows/demo.yml
+++ b/.github/workflows/demo.yml
@@ -13,6 +13,8 @@
name: Demo
on:
+ release:
+ types: [ created ]
workflow_run:
workflows: [ "Publish Docker" ]
types:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index c4b5aea0..0cc6b61e 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -104,7 +104,11 @@ jobs:
tools: composer
extensions: ast
- - name: Build project
+ - name: Install project
+ run: make build --no-print-directory
+
+ ## To see the difference between the current and the lowest versions
+ - name: Downgrade dependencies
run: make update --no-print-directory
- name: 🧪 PHPUnit Tests
@@ -143,7 +147,11 @@ jobs:
tools: composer
extensions: ast
- - name: Build project
+ - name: Install project
+ run: make build --no-print-directory
+
+ ## To see the difference between the current and the latest versions
+ - name: Upgrade dependencies
run: make update --no-print-directory
- name: 🧪 PHPUnit Tests
@@ -277,6 +285,7 @@ jobs:
context: .
push: true
tags: jbzoo/csv-blueprint:master
+ platforms: linux/amd64,linux/arm64/v8,linux/386
verify-ga:
diff --git a/Makefile b/Makefile
index bd88cb44..49844ff9 100644
--- a/Makefile
+++ b/Makefile
@@ -29,7 +29,7 @@ INVALID_SCHEMA ?= --schema='./tests/schemas/demo_invalid.yml'
# Build/install ########################################################################################################
build: ##@Project Build project in development mode
- @composer install --optimize-autoloader
+ @composer install --optimize-autoloader --ansi
@rm -f `pwd`/ci-report-converter
@make build-version
@@ -56,7 +56,7 @@ build-version: ##@Project Save version info
update: ##@Project Update dependencies
@echo "Composer flags: $(JBZOO_COMPOSER_UPDATE_FLAGS)"
- @composer update $(JBZOO_COMPOSER_UPDATE_FLAGS)
+ @composer update $(JBZOO_COMPOSER_UPDATE_FLAGS) --ansi
# Demo #################################################################################################################
diff --git a/README.md b/README.md
index 20ae88d3..7a546e2e 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
[![GitHub Release](https://img.shields.io/github/v/release/jbzoo/csv-blueprint?label=Latest)](https://github.com/jbzoo/csv-blueprint/releases) [![Total Downloads](https://poser.pugx.org/jbzoo/csv-blueprint/downloads)](https://packagist.org/packages/jbzoo/csv-blueprint/stats) [![Docker Pulls](https://img.shields.io/docker/pulls/jbzoo/csv-blueprint.svg)](https://hub.docker.com/r/jbzoo/csv-blueprint/tags) [![Docker Image Size](https://img.shields.io/docker/image-size/jbzoo/csv-blueprint)](https://hub.docker.com/r/jbzoo/csv-blueprint/tags)
-[![Static Badge](https://img.shields.io/badge/Rules-292-green?label=Total%20number%20of%20rules&labelColor=darkgreen&color=gray)](schema-examples/full.yml) [![Static Badge](https://img.shields.io/badge/Rules-81-green?label=Cell%20rules&labelColor=blue&color=gray)](src/Rules/Cell) [![Static Badge](https://img.shields.io/badge/Rules-206-green?label=Aggregate%20rules&labelColor=blue&color=gray)](src/Rules/Aggregate) [![Static Badge](https://img.shields.io/badge/Rules-5-green?label=Extra%20checks&labelColor=blue&color=gray)](#extra-checks) [![Static Badge](https://img.shields.io/badge/Rules-142/54/8-green?label=Plan%20to%20add&labelColor=gray&color=gray)](tests/schemas/todo.yml)
+[![Static Badge](https://img.shields.io/badge/Rules-304-green?label=Total%20number%20of%20rules&labelColor=darkgreen&color=gray)](schema-examples/full.yml) [![Static Badge](https://img.shields.io/badge/Rules-93-green?label=Cell%20rules&labelColor=blue&color=gray)](src/Rules/Cell) [![Static Badge](https://img.shields.io/badge/Rules-206-green?label=Aggregate%20rules&labelColor=blue&color=gray)](src/Rules/Aggregate) [![Static Badge](https://img.shields.io/badge/Rules-5-green?label=Extra%20checks&labelColor=blue&color=gray)](#extra-checks) [![Static Badge](https://img.shields.io/badge/Rules-119/54/8-green?label=Plan%20to%20add&labelColor=gray&color=gray)](tests/schemas/todo.yml)
## Introduction
@@ -282,9 +282,28 @@ columns:
# See: https://en.wikipedia.org/wiki/ISO_639.
is_language_code: alpha-2 # Examples: "en", "eng"
+ # Filesystem (with IO!)
is_file_exists: true # Check if file exists on the filesystem (It's FS IO operation!).
is_dir_exists: true # Check if directory exists on the filesystem (It's FS IO operation!).
+ # Mathematical
+ is_fibonacci: true # Validates whether the input follows the Fibonacci integer sequence. Example: "8", "13".
+ is_prime_number: true # Validates a prime number. Example: "3", "5", "7", "11".
+ is_even: true # Check if the value is an even number. Example: "2", "4", "6".
+ is_odd: true # Check if the value is an odd number. Example: "1", "7", "11".
+ is_roman: true # Validates if the input is a Roman numeral. Example: "I", "IV", "XX".
+
+ # Identifications
+ phone: ALL # Validates if the input is a phone number. Specify the country code to validate the phone number for a specific country. Example: "ALL", "US", "BR".".
+
+ # Misc
+ is_version: true # Validates the string as version numbers using Semantic Versioning. Example: "1.2.3".
+ is_punct: true # Validates whether the input composed by only punctuation characters. Example: "!@#$%^&*()".
+ is_vowel: true # Validates whether the input contains only vowels. Example: "aei".
+ is_consonant: true # Validates if the input contains only consonants. Example: "bcd".
+ is_alnum: true # Validates whether the input is only alphanumeric. Example: "aBc123".
+ is_alpha: true # This is similar to `is_alnum`, but it does not allow numbers. Example: "aBc".
+
####################################################################################################################
# Data validation for the entire(!) column using different data aggregation methods.
# Depending on the file size and the chosen aggregation method - this can use a lot of RAM time.
diff --git a/composer.json b/composer.json
index f905c783..55d7ea67 100644
--- a/composer.json
+++ b/composer.json
@@ -27,20 +27,23 @@
"prefer-stable" : true,
"require" : {
- "php" : "^8.1",
- "ext-mbstring" : "*",
+ "php" : "^8.1",
+ "ext-mbstring" : "*",
- "league/csv" : "^9.15.0",
- "jbzoo/data" : "^7.1.1",
- "jbzoo/cli" : "^7.1.8",
- "jbzoo/utils" : "^7.2.0",
- "jbzoo/ci-report-converter" : "^7.2.1",
+ "league/csv" : "^9.15.0",
+ "jbzoo/data" : "^7.1.1",
+ "jbzoo/cli" : "^7.1.8",
+ "jbzoo/utils" : "^7.2.0",
+ "jbzoo/ci-report-converter" : "^7.2.1",
- "symfony/yaml" : ">=6.4.3",
- "symfony/filesystem" : ">=6.4.3",
- "symfony/finder" : ">=6.4.0",
- "markrogoyski/math-php" : "^2.9.0",
- "respect/validation" : "^2.3.5"
+ "symfony/yaml" : ">=6.4.3",
+ "symfony/filesystem" : ">=6.4.3",
+ "symfony/finder" : ">=6.4.0",
+ "markrogoyski/math-php" : "^2.9.0",
+ "respect/validation" : "^2.3.6",
+ "giggsey/libphonenumber-for-php-lite" : "^8.13.33",
+ "giggsey/locale" : "^2.5",
+ "symfony/polyfill-mbstring" : "^1.29.0"
},
"require-dev" : {
diff --git a/composer.lock b/composer.lock
index 8541c069..da9a52c9 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "4319f3136d58ff1f5669b4fba8e4bdb0",
+ "content-hash": "1adc3bef17fcdbac873f8ab7b4d6a5ff",
"packages": [
{
"name": "bluepsyduck/symfony-process-manager",
@@ -63,6 +63,142 @@
},
"time": "2021-12-03T21:30:28+00:00"
},
+ {
+ "name": "giggsey/libphonenumber-for-php-lite",
+ "version": "8.13.33",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/giggsey/libphonenumber-for-php-lite.git",
+ "reference": "9e2489ea908a9cc3826218674880ac7a1bfe95aa"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/giggsey/libphonenumber-for-php-lite/zipball/9e2489ea908a9cc3826218674880ac7a1bfe95aa",
+ "reference": "9e2489ea908a9cc3826218674880ac7a1bfe95aa",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.0",
+ "symfony/polyfill-mbstring": "^1.17"
+ },
+ "conflict": {
+ "giggsey/libphonenumber-for-php": "*"
+ },
+ "require-dev": {
+ "ext-dom": "*",
+ "friendsofphp/php-cs-fixer": "^3.12",
+ "infection/infection": "^0.26.16",
+ "pear/pear-core-minimal": "^1.10.11",
+ "pear/pear_exception": "^1.0.2",
+ "pear/versioncontrol_git": "^0.5",
+ "phing/phing": "^2.17.4",
+ "phpstan/extension-installer": "^1.2",
+ "phpstan/phpstan": "^1.8",
+ "phpstan/phpstan-phpunit": "^1.2",
+ "phpunit/phpunit": "^9.5.26",
+ "symfony/console": "^6.0"
+ },
+ "suggest": {
+ "giggsey/libphonenumber-for-php": "Use libphonenumber-for-php for geocoding, carriers, timezones and matching"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "8.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "libphonenumber\\": "src/"
+ },
+ "exclude-from-classmap": [
+ "/src/data/",
+ "/src/carrier/data/",
+ "/src/geocoding/data/",
+ "/src/timezone/data/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "Joshua Gigg",
+ "email": "giggsey@gmail.com",
+ "homepage": "https://giggsey.com/"
+ }
+ ],
+ "description": "A lite version of giggsey/libphonenumber-for-php, which is a PHP Port of Google's libphonenumber",
+ "homepage": "https://github.com/giggsey/libphonenumber-for-php-lite",
+ "keywords": [
+ "geocoding",
+ "geolocation",
+ "libphonenumber",
+ "mobile",
+ "phonenumber",
+ "validation"
+ ],
+ "support": {
+ "issues": "https://github.com/giggsey/libphonenumber-for-php-lite/issues",
+ "source": "https://github.com/giggsey/libphonenumber-for-php-lite"
+ },
+ "time": "2024-03-25T07:35:47+00:00"
+ },
+ {
+ "name": "giggsey/locale",
+ "version": "2.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/giggsey/Locale.git",
+ "reference": "e6d4540109a01dd2bc7334cdc842d6a6a67cf239"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/giggsey/Locale/zipball/e6d4540109a01dd2bc7334cdc842d6a6a67cf239",
+ "reference": "e6d4540109a01dd2bc7334cdc842d6a6a67cf239",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "require-dev": {
+ "ext-json": "*",
+ "pear/pear-core-minimal": "^1.9",
+ "pear/pear_exception": "^1.0",
+ "pear/versioncontrol_git": "^0.5",
+ "phing/phing": "^2.7",
+ "php-coveralls/php-coveralls": "^2.0",
+ "phpunit/phpunit": "^8.5|^9.5",
+ "symfony/console": "^5.0|^6.0",
+ "symfony/filesystem": "^5.0|^6.0",
+ "symfony/finder": "^5.0|^6.0",
+ "symfony/process": "^5.0|^6.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Giggsey\\Locale\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Joshua Gigg",
+ "email": "giggsey@gmail.com",
+ "homepage": "https://giggsey.com/"
+ }
+ ],
+ "description": "Locale functions required by libphonenumber-for-php",
+ "support": {
+ "issues": "https://github.com/giggsey/Locale/issues",
+ "source": "https://github.com/giggsey/Locale/tree/2.5"
+ },
+ "time": "2023-11-01T17:19:48+00:00"
+ },
{
"name": "jbzoo/ci-report-converter",
"version": "7.2.1",
@@ -5395,12 +5531,12 @@
"source": {
"type": "git",
"url": "https://github.com/Roave/SecurityAdvisories.git",
- "reference": "430219a8462bb5c66c1a0c118239e9c3ab5179db"
+ "reference": "a2c33d8cc3719997b91b628eec3570d725cebf7e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/430219a8462bb5c66c1a0c118239e9c3ab5179db",
- "reference": "430219a8462bb5c66c1a0c118239e9c3ab5179db",
+ "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/a2c33d8cc3719997b91b628eec3570d725cebf7e",
+ "reference": "a2c33d8cc3719997b91b628eec3570d725cebf7e",
"shasum": ""
},
"conflict": {
@@ -5977,7 +6113,7 @@
"thinkcmf/thinkcmf": "<=5.1.7",
"thorsten/phpmyfaq": "<3.2.2",
"tikiwiki/tiki-manager": "<=17.1",
- "tinymce/tinymce": "<5.10.9|>=6,<6.7.3",
+ "tinymce/tinymce": "<7",
"tinymighty/wiki-seo": "<1.2.2",
"titon/framework": "<9.9.99",
"tobiasbg/tablepress": "<=2.0.0.0-RC1",
@@ -6130,7 +6266,7 @@
"type": "tidelift"
}
],
- "time": "2024-03-26T20:04:27+00:00"
+ "time": "2024-03-26T22:04:37+00:00"
},
{
"name": "sabre/event",
diff --git a/psalm.xml b/psalm.xml
index 36c060aa..2fecab37 100644
--- a/psalm.xml
+++ b/psalm.xml
@@ -29,5 +29,10 @@
+
+
+
+
+
diff --git a/schema-examples/full.json b/schema-examples/full.json
index 2e74c900..85bb3043 100644
--- a/schema-examples/full.json
+++ b/schema-examples/full.json
@@ -111,8 +111,25 @@
"is_country_code" : "alpha-2",
"is_language_code" : "alpha-2",
+
"is_file_exists" : true,
- "is_dir_exists" : true
+ "is_dir_exists" : true,
+
+ "is_dir_exists" : true,
+ "is_fibonacci" : true,
+ "is_prime_number" : true,
+ "is_even" : true,
+ "is_odd" : true,
+ "is_roman" : true,
+
+ "phone" : "ALL",
+
+ "is_version" : true,
+ "is_punct" : true,
+ "is_vowel" : true,
+ "is_consonant" : true,
+ "is_alnum" : true,
+ "is_alpha" : true
},
"aggregate_rules" : {
"is_unique" : true,
diff --git a/schema-examples/full.php b/schema-examples/full.php
index b9965e34..207cdd1a 100644
--- a/schema-examples/full.php
+++ b/schema-examples/full.php
@@ -135,6 +135,21 @@
'is_file_exists' => true,
'is_dir_exists' => true,
+
+ 'is_fibonacci' => true,
+ 'is_prime_number' => true,
+ 'is_even' => true,
+ 'is_odd' => true,
+ 'is_roman' => true,
+
+ 'phone' => 'ALL',
+
+ 'is_version' => true,
+ 'is_punct' => true,
+ 'is_vowel' => true,
+ 'is_consonant' => true,
+ 'is_alnum' => true,
+ 'is_alpha' => true,
],
'aggregate_rules' => [
diff --git a/schema-examples/full.yml b/schema-examples/full.yml
index 5c98f9e3..ff98a445 100644
--- a/schema-examples/full.yml
+++ b/schema-examples/full.yml
@@ -196,9 +196,28 @@ columns:
# See: https://en.wikipedia.org/wiki/ISO_639.
is_language_code: alpha-2 # Examples: "en", "eng"
+ # Filesystem (with IO!)
is_file_exists: true # Check if file exists on the filesystem (It's FS IO operation!).
is_dir_exists: true # Check if directory exists on the filesystem (It's FS IO operation!).
+ # Mathematical
+ is_fibonacci: true # Validates whether the input follows the Fibonacci integer sequence. Example: "8", "13".
+ is_prime_number: true # Validates a prime number. Example: "3", "5", "7", "11".
+ is_even: true # Check if the value is an even number. Example: "2", "4", "6".
+ is_odd: true # Check if the value is an odd number. Example: "1", "7", "11".
+ is_roman: true # Validates if the input is a Roman numeral. Example: "I", "IV", "XX".
+
+ # Identifications
+ phone: ALL # Validates if the input is a phone number. Specify the country code to validate the phone number for a specific country. Example: "ALL", "US", "BR".".
+
+ # Misc
+ is_version: true # Validates the string as version numbers using Semantic Versioning. Example: "1.2.3".
+ is_punct: true # Validates whether the input composed by only punctuation characters. Example: "!@#$%^&*()".
+ is_vowel: true # Validates whether the input contains only vowels. Example: "aei".
+ is_consonant: true # Validates if the input contains only consonants. Example: "bcd".
+ is_alnum: true # Validates whether the input is only alphanumeric. Example: "aBc123".
+ is_alpha: true # This is similar to `is_alnum`, but it does not allow numbers. Example: "aBc".
+
####################################################################################################################
# Data validation for the entire(!) column using different data aggregation methods.
# Depending on the file size and the chosen aggregation method - this can use a lot of RAM time.
diff --git a/schema-examples/full_clean.yml b/schema-examples/full_clean.yml
index 042f1389..b16f71ec 100644
--- a/schema-examples/full_clean.yml
+++ b/schema-examples/full_clean.yml
@@ -145,6 +145,21 @@ columns:
is_file_exists: true
is_dir_exists: true
+ is_fibonacci: true
+ is_prime_number: true
+ is_even: true
+ is_odd: true
+ is_roman: true
+
+ phone: ALL
+
+ is_version: true
+ is_punct: true
+ is_vowel: true
+ is_consonant: true
+ is_alnum: true
+ is_alpha: true
+
aggregate_rules:
is_unique: true
is_sorted:
diff --git a/src/Rules/Cell/AbstractCellRule.php b/src/Rules/Cell/AbstractCellRule.php
index 4adf1b4c..ee9c539e 100644
--- a/src/Rules/Cell/AbstractCellRule.php
+++ b/src/Rules/Cell/AbstractCellRule.php
@@ -18,6 +18,9 @@
use JBZoo\CsvBlueprint\Rules\AbstarctRule;
+/**
+ * @SuppressWarnings(PHPMD.NumberOfChildren)
+ */
abstract class AbstractCellRule extends AbstarctRule
{
/**
diff --git a/src/Rules/Cell/IsAlnum.php b/src/Rules/Cell/IsAlnum.php
new file mode 100644
index 00000000..34caca39
--- /dev/null
+++ b/src/Rules/Cell/IsAlnum.php
@@ -0,0 +1,49 @@
+ [
+ 'true',
+ 'Validates whether the input is only alphanumeric. Example: "aBc123".',
+ ],
+ ],
+ ];
+ }
+
+ public function validateRule(string $cellValue): ?string
+ {
+ if ($cellValue === '') {
+ return null;
+ }
+
+ if (!Validator::alnum()->validate($cellValue)) {
+ return "The value \"{$cellValue}\" should contain only alphanumeric characters. " .
+ 'Example: "aBc123"';
+ }
+
+ return null;
+ }
+}
diff --git a/src/Rules/Cell/IsAlpha.php b/src/Rules/Cell/IsAlpha.php
new file mode 100644
index 00000000..e3c2fe98
--- /dev/null
+++ b/src/Rules/Cell/IsAlpha.php
@@ -0,0 +1,49 @@
+ [
+ 'true',
+ 'This is similar to `is_alnum`, but it does not allow numbers. Example: "aBc".',
+ ],
+ ],
+ ];
+ }
+
+ public function validateRule(string $cellValue): ?string
+ {
+ if ($cellValue === '') {
+ return null;
+ }
+
+ if (!Validator::alpha()->validate($cellValue)) {
+ return "The value \"{$cellValue}\" should contain only alphabetic characters. " .
+ 'Example: "aBc"';
+ }
+
+ return null;
+ }
+}
diff --git a/src/Rules/Cell/IsConsonant.php b/src/Rules/Cell/IsConsonant.php
new file mode 100644
index 00000000..2046434b
--- /dev/null
+++ b/src/Rules/Cell/IsConsonant.php
@@ -0,0 +1,48 @@
+ [
+ 'true',
+ 'Validates if the input contains only consonants. Example: "bcd".',
+ ],
+ ],
+ ];
+ }
+
+ public function validateRule(string $cellValue): ?string
+ {
+ if ($cellValue === '') {
+ return null;
+ }
+
+ if (!Validator::consonant()->validate($cellValue)) {
+ return "The value \"{$cellValue}\" should contain only consonants. Example: \"bcd\"";
+ }
+
+ return null;
+ }
+}
diff --git a/src/Rules/Cell/IsEven.php b/src/Rules/Cell/IsEven.php
new file mode 100644
index 00000000..cc0db528
--- /dev/null
+++ b/src/Rules/Cell/IsEven.php
@@ -0,0 +1,48 @@
+ [
+ 'true',
+ 'Check if the value is an even number. Example: "2", "4", "6".',
+ ],
+ ],
+ ];
+ }
+
+ public function validateRule(string $cellValue): ?string
+ {
+ if ($cellValue === '') {
+ return null;
+ }
+
+ if (!Validator::even()->validate($cellValue)) {
+ return "The value \"{$cellValue}\" should be an even number. Example: \"2\", \"4\", \"6\".";
+ }
+
+ return null;
+ }
+}
diff --git a/src/Rules/Cell/IsFibonacci.php b/src/Rules/Cell/IsFibonacci.php
new file mode 100644
index 00000000..b03573e3
--- /dev/null
+++ b/src/Rules/Cell/IsFibonacci.php
@@ -0,0 +1,48 @@
+ [
+ 'true',
+ 'Validates whether the input follows the Fibonacci integer sequence. Example: "8", "13".',
+ ],
+ ],
+ ];
+ }
+
+ public function validateRule(string $cellValue): ?string
+ {
+ if ($cellValue === '') {
+ return null;
+ }
+
+ if (!Validator::fibonacci()->validate($cellValue)) {
+ return "The value \"{$cellValue}\" should be a Fibonacci integer. Example: \"8\", \"13\"";
+ }
+
+ return null;
+ }
+}
diff --git a/src/Rules/Cell/IsOdd.php b/src/Rules/Cell/IsOdd.php
new file mode 100644
index 00000000..6a857ddd
--- /dev/null
+++ b/src/Rules/Cell/IsOdd.php
@@ -0,0 +1,48 @@
+ [
+ 'true',
+ 'Check if the value is an odd number. Example: "1", "7", "11".',
+ ],
+ ],
+ ];
+ }
+
+ public function validateRule(string $cellValue): ?string
+ {
+ if ($cellValue === '') {
+ return null;
+ }
+
+ if (!Validator::odd()->validate($cellValue)) {
+ return "The value \"{$cellValue}\" should be an odd number. Example: \"1\", \"7\", \"11\".";
+ }
+
+ return null;
+ }
+}
diff --git a/src/Rules/Cell/IsPrimeNumber.php b/src/Rules/Cell/IsPrimeNumber.php
new file mode 100644
index 00000000..97d52213
--- /dev/null
+++ b/src/Rules/Cell/IsPrimeNumber.php
@@ -0,0 +1,48 @@
+ [
+ 'true',
+ 'Validates a prime number. Example: "3", "5", "7", "11".',
+ ],
+ ],
+ ];
+ }
+
+ public function validateRule(string $cellValue): ?string
+ {
+ if ($cellValue === '') {
+ return null;
+ }
+
+ if (!Validator::primeNumber()->validate($cellValue)) {
+ return "The value \"{$cellValue}\" should be a prime number. Example: \"3\", \"5\", \"7\", \"11\"";
+ }
+
+ return null;
+ }
+}
diff --git a/src/Rules/Cell/IsPunct.php b/src/Rules/Cell/IsPunct.php
new file mode 100644
index 00000000..b4031072
--- /dev/null
+++ b/src/Rules/Cell/IsPunct.php
@@ -0,0 +1,48 @@
+ [
+ 'true',
+ 'Validates whether the input composed by only punctuation characters. Example: "!@#$%^&*()".',
+ ],
+ ],
+ ];
+ }
+
+ public function validateRule(string $cellValue): ?string
+ {
+ if ($cellValue === '') {
+ return null;
+ }
+
+ if (!Validator::punct()->validate($cellValue)) {
+ return "The value \"{$cellValue}\" should be composed by only punctuation characters.";
+ }
+
+ return null;
+ }
+}
diff --git a/src/Rules/Cell/IsRoman.php b/src/Rules/Cell/IsRoman.php
new file mode 100644
index 00000000..171b64a7
--- /dev/null
+++ b/src/Rules/Cell/IsRoman.php
@@ -0,0 +1,49 @@
+ [
+ 'true',
+ 'Validates if the input is a Roman numeral. Example: "I", "IV", "XX".',
+ ],
+ ],
+ ];
+ }
+
+ public function validateRule(string $cellValue): ?string
+ {
+ if ($cellValue === '') {
+ return null;
+ }
+
+ if (!Validator::roman()->validate($cellValue)) {
+ return "The value \"{$cellValue}\" should contain only Roman numeral. " .
+ 'Example: "I", "IV", "XX"';
+ }
+
+ return null;
+ }
+}
diff --git a/src/Rules/Cell/IsVersion.php b/src/Rules/Cell/IsVersion.php
new file mode 100644
index 00000000..1dd20cff
--- /dev/null
+++ b/src/Rules/Cell/IsVersion.php
@@ -0,0 +1,48 @@
+ [
+ 'true',
+ 'Validates the string as version numbers using Semantic Versioning. Example: "1.2.3".',
+ ],
+ ],
+ ];
+ }
+
+ public function validateRule(string $cellValue): ?string
+ {
+ if ($cellValue === '') {
+ return null;
+ }
+
+ if (!Validator::version()->validate($cellValue)) {
+ return "The value \"{$cellValue}\" should be a valid semantic version. Example: \"1.2.3\"";
+ }
+
+ return null;
+ }
+}
diff --git a/src/Rules/Cell/IsVowel.php b/src/Rules/Cell/IsVowel.php
new file mode 100644
index 00000000..f0a13cbf
--- /dev/null
+++ b/src/Rules/Cell/IsVowel.php
@@ -0,0 +1,48 @@
+ [
+ 'true',
+ 'Validates whether the input contains only vowels. Example: "aei".',
+ ],
+ ],
+ ];
+ }
+
+ public function validateRule(string $cellValue): ?string
+ {
+ if ($cellValue === '') {
+ return null;
+ }
+
+ if (!Validator::vowel()->validate($cellValue)) {
+ return "The value \"{$cellValue}\" should contain only vowels. Example: \"aei\"";
+ }
+
+ return null;
+ }
+}
diff --git a/src/Rules/Cell/Phone.php b/src/Rules/Cell/Phone.php
new file mode 100644
index 00000000..b39949a3
--- /dev/null
+++ b/src/Rules/Cell/Phone.php
@@ -0,0 +1,61 @@
+ [
+ 'ALL',
+ 'Validates if the input is a phone number. Specify the country code to validate the phone number ' .
+ 'for a specific country. Example: "ALL", "US", "BR".',
+ ],
+ ],
+ ];
+ }
+
+ public function validateRule(string $cellValue): ?string
+ {
+ if ($cellValue === '') {
+ return null;
+ }
+
+ $countryCode = $this->getOptionAsString();
+ if ($countryCode === '') {
+ return 'The country code is required. Example: "ALL", "US", "BR"';
+ }
+
+ if ($countryCode === 'ALL') {
+ $countryCode = null;
+ }
+
+ // @phpstan-ignore-next-line
+ if (!Validator::phone($countryCode)->validate($cellValue)) { // @phan-suppress-current-line PhanParamTooMany
+ return $countryCode === null
+ ? "The value \"{$cellValue}\" has invalid phone number format."
+ : "The value \"{$cellValue}\" has invalid phone number format for country \"{$countryCode}\".";
+ }
+
+ return null;
+ }
+}
diff --git a/tests/Rules/Cell/IsAlnumTest.php b/tests/Rules/Cell/IsAlnumTest.php
new file mode 100644
index 00000000..cc4f0d0b
--- /dev/null
+++ b/tests/Rules/Cell/IsAlnumTest.php
@@ -0,0 +1,71 @@
+create(true);
+ isSame(null, $rule->validate(''));
+
+ $valid = [
+ '',
+ 'b',
+ 'c',
+ 'D',
+ 'df',
+ 'Bcdf',
+ 'fdbC',
+ 'aBc123',
+ ];
+
+ foreach ($valid as $value) {
+ isSame('', $rule->test($value), $value);
+ }
+
+ $rule = $this->create(false);
+ isSame(null, $rule->validate(' 1'));
+ }
+
+ public function testNegative(): void
+ {
+ $rule = $this->create(true);
+
+ $invalid = [
+ ';',
+ '!@#$%^&*()',
+ 'aBc 123',
+ 'aBc-123',
+ ];
+
+ foreach ($invalid as $value) {
+ isSame(
+ "The value \"{$value}\" should contain only alphanumeric characters. Example: \"aBc123\"",
+ $rule->test($value),
+ $value,
+ );
+ }
+ }
+}
diff --git a/tests/Rules/Cell/IsAlphaTest.php b/tests/Rules/Cell/IsAlphaTest.php
new file mode 100644
index 00000000..c593fe33
--- /dev/null
+++ b/tests/Rules/Cell/IsAlphaTest.php
@@ -0,0 +1,72 @@
+create(true);
+ isSame(null, $rule->validate(''));
+
+ $valid = [
+ '',
+ 'b',
+ 'c',
+ 'D',
+ 'df',
+ 'Bcdf',
+ 'fdbC',
+ 'aBc',
+ ];
+
+ foreach ($valid as $value) {
+ isSame('', $rule->test($value), $value);
+ }
+
+ $rule = $this->create(false);
+ isSame(null, $rule->validate(' 1'));
+ }
+
+ public function testNegative(): void
+ {
+ $rule = $this->create(true);
+
+ $invalid = [
+ ';',
+ '!@#$%^&*()',
+ 'aBc123',
+ 'aBc 123',
+ 'aBc-123',
+ ];
+
+ foreach ($invalid as $value) {
+ isSame(
+ "The value \"{$value}\" should contain only alphabetic characters. Example: \"aBc\"",
+ $rule->test($value),
+ $value,
+ );
+ }
+ }
+}
diff --git a/tests/Rules/Cell/IsConsonantTest.php b/tests/Rules/Cell/IsConsonantTest.php
new file mode 100644
index 00000000..872edbd1
--- /dev/null
+++ b/tests/Rules/Cell/IsConsonantTest.php
@@ -0,0 +1,70 @@
+create(true);
+ isSame(null, $rule->validate(''));
+
+ $valid = [
+ '',
+ 'b',
+ 'c',
+ 'D',
+ 'df',
+ 'Bcdf',
+ 'fdbC',
+ ];
+
+ foreach ($valid as $value) {
+ isSame('', $rule->test($value), $value);
+ }
+
+ $rule = $this->create(false);
+ isSame(null, $rule->validate(' 1'));
+ }
+
+ public function testNegative(): void
+ {
+ $rule = $this->create(true);
+
+ $invalid = [
+ '16',
+ 'a',
+ 'Foo',
+ 'basic',
+ ];
+
+ foreach ($invalid as $value) {
+ isSame(
+ "The value \"{$value}\" should contain only consonants. Example: \"bcd\"",
+ $rule->test($value),
+ $value,
+ );
+ }
+ }
+}
diff --git a/tests/Rules/Cell/IsEvenTest.php b/tests/Rules/Cell/IsEvenTest.php
new file mode 100644
index 00000000..710be3e2
--- /dev/null
+++ b/tests/Rules/Cell/IsEvenTest.php
@@ -0,0 +1,68 @@
+create(true);
+ isSame(null, $rule->validate(''));
+
+ $valid = [
+ '',
+ '0',
+ '2',
+ '6',
+ '34',
+ '-34',
+ ];
+
+ foreach ($valid as $value) {
+ isSame('', $rule->test($value), $value);
+ }
+
+ $rule = $this->create(false);
+ isSame(null, $rule->validate(' 1'));
+ }
+
+ public function testNegative(): void
+ {
+ $rule = $this->create(true);
+
+ $invalid = [
+ '5',
+ '101',
+ '-101',
+ ];
+
+ foreach ($invalid as $value) {
+ isSame(
+ "The value \"{$value}\" should be an even number. Example: \"2\", \"4\", \"6\".",
+ $rule->test($value),
+ $value,
+ );
+ }
+ }
+}
diff --git a/tests/Rules/Cell/IsFibonacciTest.php b/tests/Rules/Cell/IsFibonacciTest.php
new file mode 100644
index 00000000..6fd2bac7
--- /dev/null
+++ b/tests/Rules/Cell/IsFibonacciTest.php
@@ -0,0 +1,67 @@
+create(true);
+ isSame(null, $rule->validate(''));
+
+ $valid = [
+ '',
+ '1',
+ '2',
+ '3',
+ '34',
+ ];
+
+ foreach ($valid as $value) {
+ isSame('', $rule->test($value), $value);
+ }
+
+ $rule = $this->create(false);
+ isSame(null, $rule->validate(' 1'));
+ }
+
+ public function testNegative(): void
+ {
+ $rule = $this->create(true);
+
+ $invalid = [
+ '100',
+ '101',
+ '6',
+ ];
+
+ foreach ($invalid as $value) {
+ isSame(
+ "The value \"{$value}\" should be a Fibonacci integer. Example: \"8\", \"13\"",
+ $rule->test($value),
+ $value,
+ );
+ }
+ }
+}
diff --git a/tests/Rules/Cell/IsOddTest.php b/tests/Rules/Cell/IsOddTest.php
new file mode 100644
index 00000000..6b1e9c31
--- /dev/null
+++ b/tests/Rules/Cell/IsOddTest.php
@@ -0,0 +1,68 @@
+create(true);
+ isSame(null, $rule->validate(''));
+
+ $valid = [
+ '',
+ '1',
+ '3',
+ '7',
+ '35',
+ '-35',
+ ];
+
+ foreach ($valid as $value) {
+ isSame('', $rule->test($value), $value);
+ }
+
+ $rule = $this->create(false);
+ isSame(null, $rule->validate(' 1'));
+ }
+
+ public function testNegative(): void
+ {
+ $rule = $this->create(true);
+
+ $invalid = [
+ '4',
+ '100',
+ '-100',
+ ];
+
+ foreach ($invalid as $value) {
+ isSame(
+ "The value \"{$value}\" should be an odd number. Example: \"1\", \"7\", \"11\".",
+ $rule->test($value),
+ $value,
+ );
+ }
+ }
+}
diff --git a/tests/Rules/Cell/IsPhoneTest.php b/tests/Rules/Cell/IsPhoneTest.php
new file mode 100644
index 00000000..d73ed290
--- /dev/null
+++ b/tests/Rules/Cell/IsPhoneTest.php
@@ -0,0 +1,78 @@
+create(true);
+ isSame(null, $rule->validate(''));
+
+ $valid = [
+ 'III',
+ 'IV',
+ 'VI',
+ 'XIX',
+ 'XLII',
+ 'LXII',
+ 'CXLIX',
+ 'CLIII',
+ 'MCCXXXIV',
+ 'MMXXIV',
+ 'MCMLXXV',
+ 'MMMMCMXCIX',
+ ];
+
+ foreach ($valid as $value) {
+ isSame('', $rule->test($value), $value);
+ }
+
+ $rule = $this->create(false);
+ isSame(null, $rule->validate(' 1'));
+ }
+
+ public function testNegative(): void
+ {
+ $rule = $this->create(true);
+
+ $invalid = [
+ ' ',
+ 'IIII',
+ 'IVVVX',
+ 'CCDC',
+ 'MXM',
+ 'XIIIIIIII',
+ 'MIMIMI',
+ ];
+
+ foreach ($invalid as $value) {
+ isSame(
+ "The value \"{$value}\" should contain only Roman numeral. Example: \"I\", \"IV\", \"XX\"",
+ $rule->test($value),
+ $value,
+ );
+ }
+ }
+}
diff --git a/tests/Rules/Cell/IsPrimeNumberTest.php b/tests/Rules/Cell/IsPrimeNumberTest.php
new file mode 100644
index 00000000..fbc7821f
--- /dev/null
+++ b/tests/Rules/Cell/IsPrimeNumberTest.php
@@ -0,0 +1,68 @@
+create(true);
+ isSame(null, $rule->validate(''));
+
+ $valid = [
+ '',
+ '2',
+ '3',
+ '7',
+ '11',
+ '101',
+ ];
+
+ foreach ($valid as $value) {
+ isSame('', $rule->test($value), $value);
+ }
+
+ $rule = $this->create(false);
+ isSame(null, $rule->validate(' 1'));
+ }
+
+ public function testNegative(): void
+ {
+ $rule = $this->create(true);
+
+ $invalid = [
+ '100',
+ '114',
+ '6',
+ ];
+
+ foreach ($invalid as $value) {
+ isSame(
+ "The value \"{$value}\" should be a prime number. Example: \"3\", \"5\", \"7\", \"11\"",
+ $rule->test($value),
+ $value,
+ );
+ }
+ }
+}
diff --git a/tests/Rules/Cell/IsPunctTest.php b/tests/Rules/Cell/IsPunctTest.php
new file mode 100644
index 00000000..615c3a97
--- /dev/null
+++ b/tests/Rules/Cell/IsPunctTest.php
@@ -0,0 +1,73 @@
+create(true);
+ isSame(null, $rule->validate(''));
+
+ $valid = [
+ '',
+ '.',
+ ',;:',
+ '-@#$*',
+ '()[]{}',
+ '!@#$%^&*(){}',
+ "[]?+=/\\-_|\"',><.",
+ ];
+
+ foreach ($valid as $value) {
+ isSame('', $rule->test($value), $value);
+ }
+
+ $rule = $this->create(false);
+ isSame(null, $rule->validate(' 1'));
+ }
+
+ public function testNegative(): void
+ {
+ $rule = $this->create(true);
+
+ $invalid = [
+ '16-50',
+ 'a',
+ ' ',
+ 'Foo',
+ '12.1',
+ '-12',
+ '( )_{}',
+ ];
+
+ foreach ($invalid as $value) {
+ isSame(
+ "The value \"{$value}\" should be composed by only punctuation characters.",
+ $rule->test($value),
+ $value,
+ );
+ }
+ }
+}
diff --git a/tests/Rules/Cell/IsRomanTest.php b/tests/Rules/Cell/IsRomanTest.php
new file mode 100644
index 00000000..62206f67
--- /dev/null
+++ b/tests/Rules/Cell/IsRomanTest.php
@@ -0,0 +1,79 @@
+create(true);
+ isSame(null, $rule->validate(''));
+
+ $valid = [
+ '',
+ 'III',
+ 'IV',
+ 'VI',
+ 'XIX',
+ 'XLII',
+ 'LXII',
+ 'CXLIX',
+ 'CLIII',
+ 'MCCXXXIV',
+ 'MMXXIV',
+ 'MCMLXXV',
+ 'MMMMCMXCIX',
+ ];
+
+ foreach ($valid as $value) {
+ isSame('', $rule->test($value), $value);
+ }
+
+ $rule = $this->create(false);
+ isSame(null, $rule->validate(' 1'));
+ }
+
+ public function testNegative(): void
+ {
+ $rule = $this->create(true);
+
+ $invalid = [
+ ' ',
+ 'IIII',
+ 'IVVVX',
+ 'CCDC',
+ 'MXM',
+ 'XIIIIIIII',
+ 'MIMIMI',
+ ];
+
+ foreach ($invalid as $value) {
+ isSame(
+ "The value \"{$value}\" should contain only Roman numeral. Example: \"I\", \"IV\", \"XX\"",
+ $rule->test($value),
+ $value,
+ );
+ }
+ }
+}
diff --git a/tests/Rules/Cell/IsVersionTest.php b/tests/Rules/Cell/IsVersionTest.php
new file mode 100644
index 00000000..9bae0bb3
--- /dev/null
+++ b/tests/Rules/Cell/IsVersionTest.php
@@ -0,0 +1,65 @@
+create(true);
+ isSame(null, $rule->validate(''));
+
+ $valid = [
+ '',
+ '1.0.0',
+ '1.0.0-alpha',
+ '1.0.0-alpha.1',
+ '1.0.0-0.3.7',
+ '1.0.0-x.7.z.92',
+ '1.3.7+build.2.b8f12d7',
+ '1.3.7-rc.1',
+ ];
+
+ foreach ($valid as $value) {
+ isSame('', $rule->test($value), $value);
+ }
+
+ $rule = $this->create(false);
+ isSame(null, $rule->validate(' 1'));
+ }
+
+ public function testNegative(): void
+ {
+ $rule = $this->create(true);
+
+ $invalid = ['1', '1.3.7++', '1.3.7--', \uniqid('', true), '1.2.3.4', '1.2.3.4-beta', 'beta'];
+ foreach ($invalid as $value) {
+ isSame(
+ "The value \"{$value}\" should be a valid semantic version. Example: \"1.2.3\"",
+ $rule->test($value),
+ $value,
+ );
+ }
+ }
+}
diff --git a/tests/Rules/Cell/IsVowelTest.php b/tests/Rules/Cell/IsVowelTest.php
new file mode 100644
index 00000000..a11e6387
--- /dev/null
+++ b/tests/Rules/Cell/IsVowelTest.php
@@ -0,0 +1,76 @@
+create(true);
+ isSame(null, $rule->validate(''));
+
+ $valid = [
+ '',
+ 'a',
+ 'e',
+ 'i',
+ 'o',
+ 'u',
+ 'aeiou',
+ 'uoiea',
+ ];
+
+ foreach ($valid as $value) {
+ isSame('', $rule->test($value), $value);
+ }
+
+ $rule = $this->create(false);
+ isSame(null, $rule->validate(' 1'));
+ }
+
+ public function testNegative(): void
+ {
+ $rule = $this->create(true);
+
+ $invalid = [
+ ' ',
+ "\n",
+ "\t",
+ "\r",
+ '16',
+ 'F',
+ 'g',
+ 'Foo',
+ 'basic',
+ ];
+
+ foreach ($invalid as $value) {
+ isSame(
+ "The value \"{$value}\" should contain only vowels. Example: \"aei\"",
+ $rule->test($value),
+ $value,
+ );
+ }
+ }
+}
diff --git a/tests/Rules/Cell/PhoneTest.php b/tests/Rules/Cell/PhoneTest.php
new file mode 100644
index 00000000..5985854e
--- /dev/null
+++ b/tests/Rules/Cell/PhoneTest.php
@@ -0,0 +1,104 @@
+create('ALL');
+ isSame(null, $rule->validate(''));
+
+ $valid = [
+ '',
+ '+1 650 253 00 00',
+ '+7 (999) 999-99-99',
+ '+7(999)999-99-99',
+ '+7(999)999-9999',
+ '+33(1)22 22 22 22',
+ '+1 650 253 00 00',
+ '+7 (999) 999-99-99',
+ '+7(999)999-99-99',
+ '+7(999)999-9999',
+ ];
+
+ foreach ($valid as $value) {
+ isSame('', $rule->test($value), $value);
+ }
+ }
+
+ public function testNegative(): void
+ {
+ $rule = $this->create('ALL');
+
+ $invalid = [
+ 'qwerty',
+ 'qa234qwerty',
+ ];
+
+ foreach ($invalid as $value) {
+ isSame(
+ "The value \"{$value}\" has invalid phone number format.",
+ $rule->test($value),
+ $value,
+ );
+ }
+ }
+
+ public function testByCountryCode(): void
+ {
+ $rule = $this->create('BR');
+ $valid = [
+ '+55 11 91111 1111',
+ '11 91111 1111',
+ '+5511911111111',
+ '11911111111',
+ '11 91111 1111',
+ ];
+
+ foreach ($valid as $value) {
+ isSame('', $rule->test($value), $value);
+ }
+
+ isSame(
+ 'The value "+7(999)999-9999" has invalid phone number format for country "BR".',
+ $rule->test('+7(999)999-9999'),
+ );
+ }
+
+ public function testInvalidCountryCode(): void
+ {
+ $rule = $this->create('QWERTY');
+ isSame(
+ '"phone" at line 1, column "prop". Unexpected error: Invalid country code QWERTY.',
+ (string)$rule->validate('+1 650 253 00 00'),
+ );
+
+ $rule = $this->create('');
+ isSame(
+ '"phone" at line 1, column "prop". The country code is required. Example: "ALL", "US", "BR".',
+ (string)$rule->validate('+1 650 253 00 00'),
+ );
+ }
+}
diff --git a/tests/schemas/todo.yml b/tests/schemas/todo.yml
index 74dd06a5..29ec3131 100644
--- a/tests/schemas/todo.yml
+++ b/tests/schemas/todo.yml
@@ -48,22 +48,12 @@ columns:
dateperiod: 1
dateinterval: 1
- # FS
- is_dir_name: true
- is_dir_name_full: true
- is_file_name: true
- is_file_name_full: true
- is_file_size: true
- is_file_extension: true
- is_mimetype: true
-
# Codes
subdivision_code: [ ] # https://github.com/Respect/Validation/blob/main/docs/rules/SubdivisionCode.md
# ids
is_credit_card: brands[] # https://github.com/Respect/Validation/blob/main/docs/rules/CreditCard.md
is_postal_code: country code # https://github.com/Respect/Validation/blob/main/docs/rules/PostalCode.md
- is_phone: true
is_bsn: true
is_cnh: true
is_cnpj: true
@@ -177,30 +167,15 @@ columns:
email_domain: example.com # Can be regex
email_user: user # Can be regex
- # Math
- is_fibonacci: true
- is_prime_number: true
- is_even: true
- is_odd: true
- is_roman: true
- is_perfect_square: true
-
# Strings
- is_alnum: true
is_hex: true
is_binary: true
- is_alpha: true
is_charset: true
is_hex_rgb_color: true
no_whitespace: true
- is_punct: true
- is_space: true
- is_regex: true
- is_version: true
- is_vowel: true
- is_consonant: true
- is_xdigit: true
+ # is_space: true
+ # is_xdigit: true
custom_func: callbak function