php — Cursor rule: what it does, how to install
A Cursor rule for PHP style, security, API design, and testing. It targets PSR-12 and modern PHP 8.4+ projects.
.cursor/rules/php.mdcWhat php does
This rule guides Cursor while it edits PHP files. It sets PSR-12 as the style baseline, requires four-space indentation and Unix LF endings, and places declare(strict_types=1); in each PHP file. It also favors typed parameters and return values, constructor property promotion, match, ??, and native attributes.
The guidance covers Composer autoload settings, ORM query loading, caching, strict comparisons, and error handling. Security checks include input validation, context-aware output escaping, parameterized queries, password hashing with password_hash(), dependency audits, and CSRF tokens.
It also describes REST conventions, PHPUnit unit and integration tests, PHPStan or Psalm, and CI checks on every commit. The rule targets PHP 8.4+, so older projects need a compatibility review before adopting these recommendations.
What it helps with
- format PHP code according to PSR-12
- add strict typing and explicit types
- prevent SQL injection in database queries
- validate input and escape rendered output
- set up PHPUnit and static analysis checks
Example prompts for the agent
- Refactor this PHP file to PSR-12 and add strict typing.
- Review the
$_POSTand$_GEThandling for validation, query safety, and output escaping. - Create PHPUnit tests for this class and suggest PHPStan checks.
How to install php
Manually: put the files in place
.cursor/rules/php.mdcFrontmatter: description, globs, alwaysApply
Manually: put the files in place
.claude/rules/php.mdThe paths frontmatter field limits the files: move globs from the source into paths
AGENTS.md
Codex reads AGENTS.md: paste the rule text there
Manually: put the files in place
.github/instructions/php.instructions.mdThe applyTo field sets which files it applies to: move globs from the source into applyTo
Manually: put the files in place
.devin/rules/php.mdManually: put the files in place
.clinerules/php.mdStep by step, with commands for each platform: Cursor rules: .mdc files, four modes and what replaced .cursorrules
Questions about php
How do I install php in Cursor?
Place the rule file at .cursor/rules/php.mdc in the project.
What does the php rule do?
It gives Cursor guidance for PHP style, type declarations, security controls, API structure, exception handling, and tests.
Which PHP version does this rule target?
Its recommendations target PHP 8.4+ and use features such as union types, attributes, and match.
Source text
php.mdc · CC0-1.0
---
description: This guide provides definitive best practices for writing clean, secure, and maintainable PHP code, strictly adhering to PSR-12 and modern PHP 8.4+ features.
globs: **/*
---
# PHP Best Practices
This document outlines the definitive best practices for PHP development within our team. Adherence to these guidelines ensures consistency, maintainability, and security across all projects.
## Critical Guidelines:
### 1. Code Organization and Structure (PSR-12 & Strict Types)
Always adhere to PSR-12 for coding style. This includes indentation, brace placement, and line length. Crucially, enable strict typing in every PHP file.
* **Indentation**: Use 4 spaces, never tabs.
* **Line Endings**: Unix LF only.
* **Closing Tag**: Omit `?>` from files containing only PHP code.
* **File Header Order**:
1. `<?php`
2. File-level docblock (optional)
3. `declare(strict_types=1);`
4. `namespace` declaration
5. `use` statements (class, then function, then constant, grouped)
6. The rest of the code.
❌ BAD:
```php
<?php
namespace App\Service;
use App\Model\User;
declare(strict_types=1);
class UserService {
public function getUser($id) { // Missing return type, no strict types at top
return User::find($id);
}
}
```
✅ GOOD:
```php
<?php
declare(strict_types=1);
namespace App\Service;
use App\Model\User;
use App\Repository\UserRepository;
class UserService
{
public function __construct(private UserRepository $userRepository) {}
public function getUser(int $id): ?User
{
return $this->userRepository->find($id);
}
}
```
### 2. Common Patterns and Anti-patterns
* **Type Hinting**: Always use scalar and return type declarations. Leverage PHP 8+ union types, intersection types, and `mixed`/`static`/`never` where appropriate.
* **Constructor Property Promotion**: Use for concise dependency injection.
* **Match Expression**: Prefer `match` over `switch` for cleaner, more expressive comparisons.
* **Null Coalescing Operator (`??`)**: Use to simplify checks for `null` or unset variables.
* **Attributes**: Prefer native PHP attributes (`#[Attribute]`) over docblock annotations for metadata.
❌ BAD:
```php
class LegacyProcessor {
public function process($type, $data) {
switch ($type) { // Old switch, no types
case 'foo': return 'Foo ' . $data;
case 'bar': return 'Bar ' . $data;
default: return null;
}
}
}
```
✅ GOOD:
```php
<?php
declare(strict_types=1);
#[Attribute(Attribute::TARGET_CLASS)]
class MyServiceAttribute {}
#[MyServiceAttribute]
class ModernProcessor
{
public function process(string $type, string $data): ?string
{
return match ($type) {
'foo' => 'Foo ' . $data,
'bar' => 'Bar ' . $data,
default => null,
};
}
public function getSetting(array $config): string
{
// Use null coalescing for default values
return $config['setting'] ?? 'default_value';
}
}
```
### 3. Performance Considerations
* **Latest PHP Version**: Always use the current stable PHP version (8.4+).
* **Composer Autoloading**: Optimize Composer's autoloader for production (`composer dump-autoload --optimize --no-dev --classmap-authoritative`).
* **Avoid N+1 Queries**: Eager load relationships in ORMs.
* **Caching**: Implement application-level caching for frequently accessed data.
### 4. Common Pitfalls and Gotchas
* **Loose Comparisons**: Always use strict comparison operators (`===`, `!==`).
* **Error Suppression**: Never use the `@` operator. Handle errors gracefully with exceptions.
* **Modifying Arrays During Iteration**: Leads to unpredictable behavior. Iterate over a copy or use appropriate collection methods.
### 5. Security Best Practices
* **Input Validation & Sanitization**: Validate all external input. Use specific validation rules, not just `filter_var`.
* **Output Escaping**: Escape all output based on context (HTML, URL, JavaScript, CSS). Use templating engines that auto-escape or dedicated functions (e.g., `htmlspecialchars`).
* **Prepared Statements**: Always use parameterized queries (PDO or ORM) to prevent SQL injection.
* **Password Hashing**: Use `password_hash()` with `PASSWORD_ARGON2ID` (or `PASSWORD_BCRYPT` as fallback). Never store plain text passwords.
* **Dependency Updates**: Regularly update Composer dependencies and run `composer audit`.
* **CSRF Protection**: Implement CSRF tokens for state-changing requests.
❌ BAD:
```php
$username = $_POST['username'];
$password = $_POST['password'];
$sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'"; // SQL Injection!
$db->query($sql);
echo "Hello, " . $_GET['name']; // XSS vulnerability!
```
✅ GOOD:
```php
<?php
declare(strict_types=1);
// Input Validation
if (!isset($_POST['username']) || !is_string($_POST['username'])) {
throw new InvalidArgumentException('Invalid username.');
}
$username = trim($_POST['username']);
// Password Hashing
$hashedPassword = password_hash($_POST['password'], PASSWORD_ARGON2ID);
// Prepared Statements
$stmt = $db->prepare("SELECT * FROM users WHERE username = :username AND password = :password");
$stmt->execute([':username' => $username, ':password' => $hashedPassword]);
// Output Escaping
echo "Hello, " . htmlspecialchars($_GET['name'] ?? 'Guest', ENT_QUOTES, 'UTF-8');
```
### 6. Error Handling
* **Exceptions**: Use exceptions for all exceptional conditions. Create custom exception classes for domain-specific errors.
* **Centralized Handling**: Implement a global exception handler to catch unhandled exceptions and log them.
* **Logging**: Use a robust logging library (e.g., Monolog) for all errors, warnings, and important events.
### 7. API Design
* **RESTful Principles**: Design APIs to be resource-oriented, using standard HTTP methods (GET, POST, PUT, DELETE) and status codes.
* **Consistent Naming**: Use consistent pluralized resource names (e.g., `/users`, not `/user`).
* **Versioning**: Implement API versioning (e.g., `/v1/users`).
* **Payload Validation**: Strictly validate all incoming request payloads.
### 8. Testing Approaches
* **PHPUnit**: Write comprehensive unit and integration tests using PHPUnit.
* **Test Doubles**: Use mocks, stubs, and fakes to isolate units of code during testing.
* **Static Analysis**: Integrate PHPStan or Psalm into your CI/CD pipeline with a high strictness level.
* **Continuous Integration**: Automate tests and static analysis checks on every commit.
© sanjeed5 · CC0-1.0 · sanjeed5/awesome-cursor-rules-mdc