Thank you for your interest in contributing to WP Code Check! We welcome contributions from the WordPress community.
- Search existing issues to avoid duplicates
- Create a new issue with:
- Clear title describing the problem
- Steps to reproduce
- Expected vs actual behavior
- Your environment (OS, bash version, WordPress version)
- Sample code that triggers the issue (if applicable)
- Check existing feature requests to avoid duplicates
- Create a new issue with:
- Clear description of the feature
- Use case / problem it solves
- Example usage (if applicable)
- Sign the Contributor License Agreement (CLA) - See CLA Requirements below
- Fork the repository
- Create a feature branch (
git checkout -b feature/my-new-check) - Make your changes
- Add tests (see
dist/tests/fixtures/for examples) - Run the test suite (
dist/tests/run-fixture-tests.sh) - Update documentation (README.md, CHANGELOG.md)
- Commit with clear messages (see commit guidelines below)
- Push to your fork and submit a pull request
dist/patterns/. Inline rules in check-performance.sh are legacy and will be removed.
JSON Rule Structure:
Create a new JSON file in dist/patterns/ (or add to an existing category file):
{
"id": "unbounded-posts-per-page",
"version": "1.0.0",
"enabled": true,
"category": "performance",
"severity": "CRITICAL",
"title": "Unbounded posts_per_page",
"description": "Detects WordPress queries that disable pagination and can crash large sites.",
"rationale": "Unbounded queries can fetch all posts at once, leading to timeouts and memory exhaustion.",
"detection": {
"type": "simple",
"file_patterns": ["*.php"],
"grep": {
"include": [
"-E posts_per_page[[:space:]]*=>[[:space:]]*-1"
],
"override_include": "--include=*.php"
}
},
"remediation": {
"summary": "Add pagination or a reasonable LIMIT to queries to avoid loading all records at once.",
"examples": [
{
"bad": "WP_Query(['posts_per_page' => -1])",
"good": "WP_Query(['posts_per_page' => 50, 'paged' => $paged])",
"note": "Use paged queries and reasonable limits for production sites."
}
]
}
}Detection Types:
"simple"– Basic grep pattern matching (most common)"aggregated"– DRY/clone detection with grouping and thresholds"contextual"– Context-aware rules (requires custom validator)"scripted"– Complex rules requiring custom validator scripts
File Organization:
dist/patterns/core/*.json– Core performance/security rulesdist/patterns/dry/*.json– DRY/duplication detection rulesdist/patterns/headless/*.json– Headless WordPress patternsdist/patterns/js/*.json– JavaScript-specific patternsdist/patterns/nodejs/*.json– Node.js-specific patterns
Legacy Inline Format (DO NOT USE):
# ❌ DEPRECATED - Do not add new rules this way
run_check "ERROR" "CRITICAL" \
"Unbounded posts_per_page" "unbounded-posts-per-page" \
"-E posts_per_page[[:space:]]*=>[[:space:]]*-1"See PROJECT/2-WORKING/PATTERN-MIGRATION-TO-JSON.md for the migration plan.
Every new check must have test fixtures:
- Add antipattern to
dist/tests/fixtures/antipatterns.php - Add safe pattern to
dist/tests/fixtures/clean-code.php - Update expected counts in
dist/tests/run-fixture-tests.sh
- Bash: Follow Google Shell Style Guide
- Comments: Explain why, not what
- Functions: One responsibility per function
- Variables: Use descriptive names (
FINDING_COUNTnotfc)
The cache-primed hooks dictionary (WP_CACHE_PRIMED_HOOKS) is located in dist/bin/ai-triage.py. This dictionary maps WordPress hooks to object types and is critical for reducing false positives in N+1 detection.
- Identify New Hooks:
- Look for WordPress hooks that pre-cache metadata for objects (e.g.,
user,post,comment).
- Look for WordPress hooks that pre-cache metadata for objects (e.g.,
- Add to the Dictionary:
- Open
dist/bin/ai-triage.pyand add the new hook to theWP_CACHE_PRIMED_HOOKSdictionary.
- Open
- Test Your Changes:
- Run the test suite to ensure no regressions.
- Add new test cases if necessary.
- Submit a Pull Request:
- Include a clear description of the added hooks and their purpose.
WP_CACHE_PRIMED_HOOKS = {
'show_user_profile': 'user',
'edit_user_profile': 'user',
'add_meta_boxes': 'post',
# Add new hooks here
}cd dist/tests
./run-fixture-tests.shcd dist/bin
./check-performance.sh --paths ../tests/fixtures/antipatterns.phpExpected output:
- Errors: 6+ (depending on active checks)
- Warnings: 4+
Use the keyword "Run template [name] end to end" to execute a complete scan and AI triage workflow with minimal human intervention.
What this does:
- Loads the template configuration from
TEMPLATES/[name].txt - Executes the full performance scan (
check-performance.sh) - Generates JSON log with all findings
- Runs AI-assisted triage on the findings
- Converts JSON to HTML report with triage data embedded
- Opens the final report in your browser
Example:
# User request: "Run template gravityforms end to end"
# AI will execute:
./dist/bin/run gravityforms --format json
python3 dist/bin/ai-triage.py dist/logs/[latest].json
python3 dist/bin/json-to-html.py dist/logs/[latest].json dist/reports/[output].htmlBenefits:
- ✅ Complete workflow in one command
- ✅ AI triage automatically classifies findings
- ✅ HTML report includes triage classifications and confidence levels
- ✅ No manual JSON/HTML conversion needed
- ✅ Ideal for testing new checks or validating fixes
Template Requirements:
- Template file must exist in
TEMPLATES/[name].txt - Must contain
PROJECT_PATHpointing to a valid WordPress plugin/theme directory - Optional:
FORMAT=jsonto enable JSON output (required for triage)
Use conventional commits format:
<type>(<scope>): <subject>
<body>
<footer>
Types:
feat: New featurefix: Bug fixdocs: Documentation onlytest: Adding/updating testsrefactor: Code restructuring (no behavior change)perf: Performance improvementchore: Maintenance tasks
Examples:
feat(checks): add detection for unbounded get_users()
Detects get_users() calls without 'number' parameter which can
fetch ALL users and crash sites with large user bases.
Closes #42
fix(html): display full path instead of "." in report header
When scanning with --paths ., the HTML report now shows the
resolved absolute path instead of just a dot.
Do NOT commit:
- API keys, tokens, or credentials
- User-generated logs or reports
- Proprietary plugin/theme code
- Personal information
If you discover a security vulnerability, please email security@hypercart.com instead of opening a public issue.
By contributing, you agree that your contributions will be licensed under the Apache License 2.0, the same license as the project.
See LICENSE for full terms.
WP Code Check is dual-licensed under Apache 2.0 (open source) and commercial licenses. The CLA ensures:
- Legal clarity - You confirm you have the right to contribute
- Dual licensing - Your contributions can be included in both open source and commercial versions
- Patent protection - Explicit patent grants protect all users
- Community protection - Prevents legal issues that could harm the project
For Individual Contributors:
- Read the Individual Contributor License Agreement (CLA.md)
- On your first pull request, add a comment: "I have read and agree to the CLA"
- Alternatively, email a signed copy to
cla@hypercart.com
For Corporate Contributors:
If you're contributing on behalf of your employer:
- Your employer must sign the Corporate CLA (CLA-CORPORATE.md)
- You must also sign the Individual CLA
- Contact
cla@hypercart.comto submit the Corporate CLA
- ✅ You retain copyright to your contributions
- ✅ You can use your code elsewhere - No restrictions on your own use
- ✅ Open source stays open - Apache 2.0 version always available
- ✅ One-time process - Sign once, contribute forever
- ✅ Standard practice - Based on Apache Software Foundation's CLA
Questions about the CLA? Email cla@hypercart.com
Contributors will be recognized in:
- GitHub contributors page
- CHANGELOG.md (for significant contributions)
- Project README (for major features)
- Documentation: See README.md and dist/README.md
- Issues: GitHub Issues
- Email: support@hypercart.com
Thank you for helping make WordPress faster and more secure! 🚀