Skip to content

Commit eb112d2

Browse files
MarjovanLierclaude
andauthored
(Changed) Performance optimisations, bug fixes, and Docker-based development setup (#45)
* refactor: Change class to final and update static method calls to self (Changed) Make StringManipulation final and update static to self references Make `StringManipulation` class final and replace `static` with `self`. Converted the `StringManipulation` class to `final` to prevent inheritance and replaced all occurrences of `static` with `self` for improved clarity and static binding. Additionally, updated development dependencies in `composer.json` to their latest versions for enhanced tooling support. * refactor: improve string processing readability - Simplify control flow in string replacement methods - Reduce code complexity for better maintainability * perf: optimize string replacement logic for efficiency - Improve performance of string manipulation operations - Reduce unnecessary iterations in replacement algorithms * perf: cache accent replacement mappings for better performance - Implement caching mechanism for accent transformations - Reduce repeated computations in accent normalization * test: add comprehensive tests for string replacement optimizations - Add test coverage for edge cases in string replacement - Validate performance improvements with test cases * refactor: remove unnecessary optimizations in strReplace method - Simplify string replacement implementation - Remove premature optimization attempts * docs(project): Improve documentation accuracy and consistency - Standardise spelling to South African English throughout codebase - Change "optimize/optimization" to "optimise/optimisation" - Change "normalize" to "normalise" - Change "standardize/capitalizing" to "standardise/capitalising" - Fix README.md documentation issues - Remove duplicate UTF-8 to ANSI conversion section - Correct method name from validateDate() to isValidDate() - Remove references to non-existent validateTime() method - Fix incorrect output descriptions for utf8Ansi examples - Fix code quality issues - Remove duplicate PHPDoc for removeAccents() method - Fix duplicate 'ë' character in REMOVE_ACCENTS_FROM array - Standardise parameter naming: $valor to $value in utf8Ansi() - Clarify UnicodeMappings trait documentation - Update to accurately describe Unicode escape sequence decoding - Correct misconception about ANSI conversion functionality These changes improve code maintainability and ensure documentation accurately reflects the actual implementation. All tests pass. Signed-off-by: Marjo Wenzel van Lier <marjo.vanlier@gmail.com> * style: Fix code style issues - Add missing periods to inline comments - Fix array alignment in ACCENTS_REPLACEMENT - Fix spacing in method signature - Add parentheses around null coalescing operation - Change dynamic method calls to static in tests These changes address Codacy and PHPStan warnings. Signed-off-by: Marjo Wenzel van Lier <marjo.vanlier@gmail.com> * style: Fix binary operator spacing Laravel Pint detected incorrect spacing around the equals sign in the method signature default parameter. Signed-off-by: Marjo Wenzel van Lier <marjo.vanlier@gmail.com> * refactor: Remove unreachable null check The null check after nameFix() was unreachable since nameFix() only returns null when its input is null, and we already check for null input at the beginning of searchWords(). This improves code coverage by removing dead code. Signed-off-by: Marjo Wenzel van Lier <marjo.vanlier@gmail.com> * fix: Add type assertion for PHPStan PHPStan cannot infer that nameFix() only returns null when its input is null. Added an assertion to help the static analyzer understand the code flow better. Signed-off-by: Marjo Wenzel van Lier <marjo.vanlier@gmail.com> * refactor: Replace assert with type cast for PHPStan Using assert() in production code is not recommended as assertions can be disabled. Instead, we use a type cast to string which is safe because we've already verified the input is not null. Signed-off-by: Marjo Wenzel van Lier <marjo.vanlier@gmail.com> * fix: Remove unnecessary SensitiveParameter import and fix cast spacing - Remove duplicate SensitiveParameter import that conflicts with PHP 8's built-in attribute - Use global \SensitiveParameter attribute instead of importing it - Fix cast spacing to comply with PER coding standards 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: MarjovanLier <marjovanlier@gmail.com> * feat(ci): Add pre-commit hooks with containerised testing - Add Dockerfile for PHP 8.3 with all required extensions including xdebug - Create docker-compose.yml with individual test services - Set up pre-commit framework with comprehensive checks - Add custom commit-msg hook for conventional commits validation - Create test-runner.sh for easy test execution - Create setup-hooks.sh for automated setup - Add .dockerignore to optimise Docker builds - Document setup process in CONTRIBUTING.md - Update .gitignore with Docker and pre-commit entries All tests now run in consistent Docker containers to avoid environment-specific issues. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: MarjovanLier <marjovanlier@gmail.com> * fix(ci): Fix test runner script and remove docker-compose version warning - Fix missing print_info function in test-runner.sh - Remove obsolete version attribute from docker-compose.yml - Fix composer validation test handling in test runner - Add missing newlines at end of files (linter fixes) All required tests now pass successfully in containers. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: MarjovanLier <marjovanlier@gmail.com> * fix: resolve Phan compatibility issues with typed constants - Remove typed array constants to support Phan polyfill parser - Update Rector config to skip type-adding rules - Fix import path for RemoveUselessVarTagRector - Maintain type safety through PHPDoc annotations - Set Phan processes to 1 to avoid pcntl requirement All critical static analysis tools now passing (Phan, PHPStan, Rector) * fix: suppress MissingClassConstType errors in Psalm This allows both Phan and Psalm to pass - Phan requires untyped constants while Psalm normally requires typed constants. By suppressing this specific error type, we maintain compatibility with both tools. * feat: add all composer tests to pre-commit hooks via Docker - Add phpmd, phan, rector, and infection to pre-commit hooks - All tests now run in Docker containers for consistency - Infection mutation testing runs on pre-push stage - Add helper script run-tests-docker.sh for running all tests - Fix deprecated stage names (commit -> pre-commit, push -> pre-push) - Enable xdebug in Docker for mutation testing support All pre-commit hooks now passing with 98% mutation coverage * style: add periods to inline comments in test files - Add periods to all inline comments for consistency - Follows coding style guidelines for comment punctuation - Addresses CodeRabbit review feedback * feat(hooks): Implement commitlint for conventional commit validation - Add commitlint configuration with strict rules - Update commit-msg hook to use commitlint via npx - Add Docker support for commitlint when npm is not available - Update Dockerfile to include Node.js and npm - Add package.json for npm dependencies - Create commitlint-docker.sh wrapper script - Update setup script to install npm dependencies This ensures all commit messages follow the Conventional Commits specification with proper validation and helpful error messages. Signed-off-by: Marjo Wenzel van Lier <marjo.vanlier@gmail.com> * fix(config): Correct Phan configuration and pre-commit setup - Revert Phan directory_list to include full vendor directory - Change minimum_severity back to CRITICAL for focused analysis - Increase Phan processes from 1 to 4 for better performance - Remove deprecated stages declaration from PHPUnit pre-commit hook These changes fix the Phan configuration to properly analyze vendor dependencies while excluding them from analysis, and remove the deprecated stage warning from pre-commit. Signed-off-by: Marjo Wenzel van Lier <marjo.vanlier@gmail.com> * perf: optimize string manipulation methods and fix validation bugs - Optimize strReplace for single character replacements (69% faster) - Optimize searchWords by combining operations (15% faster) - Fix isValidDate to properly validate month/day combinations - Add performance benchmarking suite - Fix PHPStan type issues and improve null safety - Add edge case handling for empty strings The optimizations significantly improve performance for common use cases while maintaining full backward compatibility. All tests pass. Signed-off-by: Marjo Wenzel van Lier <marjo.vanlier@gmail.com> * feat: restore typed class constants with Docker AST support - Restored PHP 8.3+ typed class constants in AccentNormalization and UnicodeMappings traits - Fixed Dockerfile to properly install AST extension separately from xdebug - Updated composer.json to remove --allow-polyfill-parser flag for Phan - Removed AddTypeToConstRector skip in rector.php configuration - Removed MissingClassConstType suppression in psalm.xml - Reduced Phan processes from 4 to 1 to avoid memory issues - Fixed Psalm strict type issues in benchmark file with explicit casting - Applied code style fixes with Pint and Rector - Added RemoveConcatAutocastRector to skip list to avoid Psalm conflicts - Updated .gitignore to exclude temporary and Qodana files All static analysis tools now support typed constants when running in Docker containers with the AST extension properly installed. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: correct PHPStan and Psalm type annotations in IsValidTimePartTest - Updated data provider return type annotations to match exact array shape - Fixed type mismatch between data providers and test method parameters - Ensures both PHPStan and Psalm static analysis pass without errors * docs: update CLAUDE.md with Docker-first testing approach - Consolidated Docker testing commands into main Build & Testing section - Emphasized Docker as the recommended testing environment - Removed duplicate Docker testing commands section - Added all available Docker test services with proper naming - Clarified that Docker provides PHP 8.3 with AST extension * chore: update development dependencies - laravel/pint: >=1.21.1 -> >=1.22.1 - phpstan/phpstan: >=2.1.8 -> >=2.1.17 - phpstan/phpstan-strict-rules: >=2.0.3 -> >=2.0.4 - psalm/plugin-phpunit: >=0.19.2 -> >=0.19.3 - rector/rector: >=2.0.10 -> >=2.0.16 All tests passing with updated dependencies * feat: configure Codacy to use PER 2.0 coding standard - Added .php-cs-fixer.php with @PER-CS2.0 rule set - Configured Codacy to use PHP CS Fixer with PER 2.0 standard - Disabled phpcodesniffer in Codacy to avoid style conflicts - Simplified pint.json to use only the 'per' preset - Added .php-cs-fixer.cache to .gitignore This ensures both Pint and Codacy use the same PER 2.0 coding standard * fix: improve Codacy configuration to enforce PER 2.0 standard - Explicitly disabled all PHP style checkers except php-cs-fixer - Added .codacyignore to exclude non-source files - Excluded tests/Benchmark from analysis as it has different requirements - Disabled PHPMD to avoid style conflicts This should resolve Codacy issues by ensuring only php-cs-fixer with PER 2.0 standard is used for style checking * feat: add PHP_CodeSniffer configuration for Codacy - Added phpcs.xml with PER coding standard - Enabled PHP_CodeSniffer in Codacy configuration - Excluded specific rules that conflict with project standards - Kept both PHP_CodeSniffer and php-cs-fixer enabled This provides Codacy with explicit configuration files for both supported tools to enforce PER 2.0 coding standard --------- Signed-off-by: Marjo Wenzel van Lier <marjo.vanlier@gmail.com> Signed-off-by: MarjovanLier <marjovanlier@gmail.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent d24e598 commit eb112d2

41 files changed

Lines changed: 1888 additions & 43195 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.codacy.yaml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
# Codacy configuration to align with PER 2.0 coding standard
3+
engines:
4+
# Try to use PHP_CodeSniffer with our custom config
5+
phpcodesniffer:
6+
enabled: true
7+
config: phpcs.xml
8+
# Also try php-cs-fixer if supported
9+
php-cs-fixer:
10+
enabled: true
11+
config: .php-cs-fixer.php
12+
# Disable other style checkers
13+
phpmd:
14+
enabled: false
15+
# Keep duplication detection
16+
duplication:
17+
enabled: true
18+
config:
19+
languages:
20+
- php
21+
22+
# Exclude non-source files from analysis
23+
exclude_paths:
24+
- 'vendor/**'
25+
- '.github/**'
26+
- 'docker/**'
27+
- 'node_modules/**'
28+
- 'tests/Benchmark/**'
29+
- '*.md'
30+
- '*.sh'
31+
- '*.yml'
32+
- '*.yaml'
33+
- 'Dockerfile'
34+
- '.php-cs-fixer.cache'
35+
- 'composer.lock'
36+
- 'package-lock.json'

.codacyignore

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Codacy ignore file
2+
# Exclude test benchmarks as they have different style requirements
3+
tests/Benchmark/
4+
5+
# Exclude vendor and dependencies
6+
vendor/
7+
node_modules/
8+
9+
# Exclude build and CI files
10+
.github/
11+
docker/
12+
Dockerfile
13+
docker-compose.yml
14+
15+
# Exclude documentation
16+
*.md
17+
README.md
18+
CONTRIBUTING.md
19+
CLAUDE.md
20+
IMPROVEMENTS.md
21+
22+
# Exclude configuration files
23+
*.yml
24+
*.yaml
25+
.gitignore
26+
.dockerignore
27+
composer.lock
28+
package-lock.json
29+
30+
# Exclude shell scripts
31+
*.sh
32+
33+
# Exclude cache files
34+
.php-cs-fixer.cache
35+
.psalm-cache

.dockerignore

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
.git
2+
.github
3+
.idea
4+
vendor
5+
.phpunit.cache
6+
.phpunit.result.cache
7+
.php-cs-fixer.cache
8+
reports
9+
.qodo
10+
*.log
11+
.DS_Store
12+
docker-compose.yml
13+
Dockerfile
14+
.dockerignore
15+
.gitignore
16+
README.md
17+
CLAUDE.md

.githooks/commit-msg

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#!/usr/bin/env bash
2+
3+
# Conventional Commits validation hook using commitlint
4+
# This hook validates that commit messages follow the Conventional Commits specification
5+
6+
# Colors for output
7+
RED='\033[0;31m'
8+
YELLOW='\033[1;33m'
9+
GREEN='\033[0;32m'
10+
NC='\033[0m' # No Color
11+
12+
# Check if it's a merge commit
13+
merge_regex='^Merge '
14+
commit_message=$(cat "$1")
15+
16+
if echo "$commit_message" | grep -qE "$merge_regex"; then
17+
exit 0
18+
fi
19+
20+
# Determine which commitlint runner to use
21+
if command -v npx &> /dev/null && [ -d "node_modules" ]; then
22+
# Use local npx
23+
echo -e "${GREEN}Running commitlint (local)...${NC}"
24+
25+
if ! npx --no -- commitlint --edit "$1"; then
26+
echo ""
27+
echo -e "${RED}ERROR: Commit message does not pass commitlint validation!${NC}"
28+
echo ""
29+
echo "Please ensure your commit message follows the Conventional Commits format."
30+
echo "See commitlint.config.js for the specific rules."
31+
exit 1
32+
fi
33+
elif [ -f "./commitlint-docker.sh" ] && command -v docker &> /dev/null && docker info &> /dev/null; then
34+
# Use Docker wrapper
35+
echo -e "${GREEN}Running commitlint (Docker)...${NC}"
36+
37+
if ! ./commitlint-docker.sh "$1"; then
38+
echo ""
39+
echo -e "${RED}ERROR: Commit message does not pass commitlint validation!${NC}"
40+
echo ""
41+
echo "Please ensure your commit message follows the Conventional Commits format."
42+
echo "See commitlint.config.js for the specific rules."
43+
exit 1
44+
fi
45+
else
46+
# Fallback to basic regex validation
47+
echo -e "${YELLOW}WARNING: commitlint not available. Using basic validation.${NC}"
48+
49+
commit_regex='^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test|security)(\([a-z0-9\-]+\))?(!)?: .{1,100}$'
50+
first_line=$(echo "$commit_message" | head -n1)
51+
52+
if ! echo "$first_line" | grep -qE "$commit_regex"; then
53+
echo -e "${RED}ERROR: Commit message does not follow Conventional Commits format!${NC}"
54+
echo ""
55+
echo "Valid types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert, security"
56+
echo "Format: <type>(<scope>): <subject>"
57+
echo ""
58+
echo "Your message: $first_line"
59+
exit 1
60+
fi
61+
fi
62+
63+
# Additional checks for signed-off-by (required by this project)
64+
if ! grep -q "Signed-off-by: " "$1"; then
65+
echo -e "${YELLOW}Adding 'Signed-off-by' line...${NC}"
66+
67+
# Get git user info
68+
name=$(git config user.name)
69+
email=$(git config user.email)
70+
71+
if [ -n "$name" ] && [ -n "$email" ]; then
72+
echo "" >> "$1"
73+
echo "Signed-off-by: $name <$email>" >> "$1"
74+
else
75+
echo -e "${RED}ERROR: Cannot add Signed-off-by - git user.name or user.email not configured${NC}"
76+
exit 1
77+
fi
78+
fi
79+
80+
exit 0

.gitignore

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,26 @@ tests/temp
66
.phpunit.result.cache
77
.php-cs-fixer.cache
88
reports
9+
10+
.qodo
11+
12+
# Qodana
13+
qodana.yaml
14+
qodana.sarif.json
15+
.qodana/
16+
17+
# Temporary files
18+
commit_messages.txt
19+
*.tmp
20+
21+
# Docker
22+
.docker/
23+
docker-compose.override.yml
24+
25+
# Pre-commit
26+
.pre-commit/
27+
28+
# Node modules
29+
node_modules/
30+
package-lock.json
31+
.php-cs-fixer.cache

.gitmessage

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Conventional Commit Format:
2+
# <type>(<scope>): <subject>
3+
#
4+
# <body>
5+
#
6+
# <footer>
7+
#
8+
# Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
9+
# Scope: Optional, e.g., parser, compiler, etc.
10+
# Subject: Brief description (imperative mood, no period)
11+
# Body: Detailed explanation (optional)
12+
# Footer: Breaking changes, issue references (optional)
13+
#
14+
# Examples:
15+
# feat(api): add user authentication endpoint
16+
# fix(parser): resolve memory leak in tokenizer
17+
# docs: update installation instructions
18+
#
19+
# Breaking change example:
20+
# feat(api)!: change authentication to use JWT
21+
#
22+
# BREAKING CHANGE: API now requires JWT tokens instead of API keys

.phan/config.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@
5757
// Automatically inferred from composer.json requirement for "php" of "^8.3"
5858
'target_php_version' => '8.3',
5959

60+
// Minimum PHP version to check compatibility against
61+
'minimum_target_php_version' => '8.3',
62+
6063
// If enabled, missing properties will be created when
6164
// they are first seen. If false, we'll report an
6265
// error message if there is an attempt to write
@@ -306,7 +309,7 @@
306309

307310
// The number of processes to fork off during the analysis
308311
// phase.
309-
'processes' => 4,
312+
'processes' => 1,
310313

311314
// List of case-insensitive file extensions supported by Phan.
312315
// (e.g. `['php', 'html', 'htm']`)
@@ -356,6 +359,7 @@
356359
// your application should be included in this list.
357360
'directory_list' => [
358361
'src',
362+
'tests',
359363
'vendor',
360364
],
361365

.php-cs-fixer.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
$finder = PhpCsFixer\Finder::create()
6+
->in(__DIR__)
7+
->exclude('vendor')
8+
->exclude('.github')
9+
->exclude('docker')
10+
->name('*.php')
11+
->notName('*.blade.php')
12+
->ignoreDotFiles(true)
13+
->ignoreVCS(true);
14+
15+
return (new PhpCsFixer\Config())
16+
->setFinder($finder)
17+
->setRules([
18+
'@PER-CS2.0' => true, // PER 2.0 coding standard
19+
// Additional project-specific rules to align with our standards
20+
'concat_space' => ['spacing' => 'one'],
21+
'array_indentation' => true,
22+
'binary_operator_spaces' => [
23+
'operators' => [
24+
'=>' => 'align_single_space_minimal',
25+
],
26+
],
27+
'phpdoc_line_span' => [
28+
'const' => 'single',
29+
'property' => 'single',
30+
'method' => 'multi',
31+
],
32+
'no_superfluous_phpdoc_tags' => [
33+
'allow_mixed' => true,
34+
'allow_unused_params' => false,
35+
],
36+
])
37+
->setRiskyAllowed(true)
38+
->setUsingCache(true);

.pr_agent.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,4 @@ push_commands = [
5555
""",
5656
"/review auto_approve --pr_reviewer.num_code_suggestions=0 --pr_reviewer.inline_code_comments=true"
5757
]
58-
handle_push_trigger = true
58+
handle_push_trigger = true

.pre-commit-config.yaml

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# See https://pre-commit.com for more information
2+
repos:
3+
# Local hooks that run in Docker containers
4+
- repo: local
5+
hooks:
6+
- id: composer-validate
7+
name: Validate composer.json
8+
entry: docker-compose run --rm test-code-style composer validate --strict
9+
language: system
10+
files: composer\.(json|lock)$
11+
pass_filenames: false
12+
13+
- id: php-lint
14+
name: PHP Syntax Check
15+
entry: docker-compose run --rm test-lint
16+
language: system
17+
files: \.php$
18+
pass_filenames: false
19+
20+
- id: php-code-style
21+
name: PHP Code Style (Laravel Pint)
22+
entry: docker-compose run --rm test-code-style
23+
language: system
24+
files: \.php$
25+
pass_filenames: false
26+
27+
- id: phpstan
28+
name: PHPStan Static Analysis
29+
entry: docker-compose run --rm test-phpstan
30+
language: system
31+
files: \.php$
32+
pass_filenames: false
33+
34+
- id: psalm
35+
name: Psalm Static Analysis
36+
entry: docker-compose run --rm test-psalm
37+
language: system
38+
files: \.php$
39+
pass_filenames: false
40+
41+
- id: phpunit
42+
name: PHPUnit Tests
43+
entry: docker-compose run --rm test-phpunit
44+
language: system
45+
files: \.(php|xml)$
46+
pass_filenames: false
47+
48+
- id: security-check
49+
name: Security Vulnerabilities Check
50+
entry: docker-compose run --rm test-security
51+
language: system
52+
files: composer\.lock$
53+
pass_filenames: false
54+
55+
- id: phpmd
56+
name: PHP Mess Detector
57+
entry: docker-compose run --rm test-phpmd
58+
language: system
59+
files: \.php$
60+
pass_filenames: false
61+
62+
- id: phan
63+
name: Phan Static Analysis
64+
entry: docker-compose run --rm test-phan
65+
language: system
66+
files: \.php$
67+
pass_filenames: false
68+
69+
- id: rector
70+
name: Rector Code Quality
71+
entry: docker-compose run --rm test-rector
72+
language: system
73+
files: \.php$
74+
pass_filenames: false
75+
76+
- id: infection
77+
name: Infection Mutation Testing
78+
entry: docker-compose run --rm test-infection
79+
language: system
80+
files: \.php$
81+
pass_filenames: false
82+
stages: [pre-push]
83+
84+
# Standard pre-commit hooks
85+
- repo: https://github.com/pre-commit/pre-commit-hooks
86+
rev: v4.5.0
87+
hooks:
88+
- id: trailing-whitespace
89+
exclude: ^vendor/
90+
- id: end-of-file-fixer
91+
exclude: ^vendor/
92+
- id: check-yaml
93+
- id: check-added-large-files
94+
args: ['--maxkb=1000']
95+
- id: check-json
96+
- id: check-xml
97+
- id: mixed-line-ending
98+
args: ['--fix=lf']

0 commit comments

Comments
 (0)