acc-check-singleton-antipattern
1
总安装量
1
周安装量
#53800
全站排名
安装命令
npx skills add https://github.com/dykyi-roman/awesome-claude-code --skill acc-check-singleton-antipattern
Agent 安装分布
opencode
1
claude-code
1
Skill 文档
Singleton Anti-Pattern Detection
Analyze PHP code for Singleton anti-pattern usage that introduces global state and tight coupling.
Detection Patterns
1. Classic Singleton Implementation
// ANTIPATTERN: Static instance with private constructor
final class DatabaseConnection
{
private static ?self $instance = null;
private function __construct(
private readonly PDO $pdo,
) {}
public static function getInstance(): self
{
if (self::$instance === null) {
self::$instance = new self(new PDO('mysql:host=localhost'));
}
return self::$instance;
}
}
// Usage creates hidden dependency
class UserRepository
{
public function find(int $id): User
{
$db = DatabaseConnection::getInstance(); // Hidden dependency!
return $db->query("SELECT * FROM users WHERE id = ?", [$id]);
}
}
2. Registry / Service Locator (Singleton Variant)
// ANTIPATTERN: Global registry acting as singleton container
final class Registry
{
private static array $services = [];
public static function set(string $key, mixed $value): void
{
self::$services[$key] = $value;
}
public static function get(string $key): mixed
{
return self::$services[$key] ?? throw new RuntimeException("Not found: $key");
}
}
// Usage hides ALL dependencies
class OrderService
{
public function create(array $data): Order
{
$repo = Registry::get('orderRepository'); // Hidden dependency
$mailer = Registry::get('mailer'); // Hidden dependency
}
}
3. Static State Accumulation
// ANTIPATTERN: Mutable static state
class EventBus
{
private static array $listeners = [];
public static function subscribe(string $event, callable $listener): void
{
self::$listeners[$event][] = $listener;
}
public static function dispatch(string $event, mixed $data): void
{
foreach (self::$listeners[$event] ?? [] as $listener) {
$listener($data);
}
}
}
// Problem: State leaks between tests, no way to reset
4. Late Static Binding Singleton
// ANTIPATTERN: Singleton via late static binding
abstract class BaseSingleton
{
protected static array $instances = [];
public static function getInstance(): static
{
$class = static::class;
if (!isset(static::$instances[$class])) {
static::$instances[$class] = new static();
}
return static::$instances[$class];
}
}
5. Framework Anti-Patterns
// ANTIPATTERN: Laravel Facade misuse (global state access)
class OrderController
{
public function store(): Response
{
Cache::put('key', 'value'); // Static singleton access
Log::info('Order created'); // Static singleton access
Event::dispatch(new Created()); // Static singleton access
// All dependencies hidden â not in constructor
}
}
// CORRECT: Inject dependencies
final readonly class OrderController
{
public function __construct(
private CacheInterface $cache,
private LoggerInterface $logger,
private EventDispatcherInterface $events,
) {}
}
Grep Patterns
# Classic singleton
Grep: "static.*\$instance|getInstance\(\)|private function __construct" --glob "**/*.php"
# Static service access
Grep: "static function get|static function getInstance|static::getInstance" --glob "**/*.php"
# Global state via static arrays
Grep: "private static array|protected static array" --glob "**/*.php"
# Registry / Service Locator
Grep: "Registry::get|ServiceLocator::get|Container::get" --glob "**/*.php"
# Mutable static properties
Grep: "static \$[a-z]+ =" --glob "**/Domain/**/*.php"
Severity Classification
| Pattern | Severity |
|---|---|
| Singleton in Domain layer | ð´ Critical |
| Service Locator pattern | ð´ Critical |
| Static mutable state | ð Major |
| Framework facade in Domain | ð Major |
| Static helper/utility | ð¡ Minor |
Correct Alternatives
Use Dependency Injection
// CORRECT: DI container manages lifecycle
final readonly class UserRepository
{
public function __construct(
private PDO $connection, // Injected, not global
) {}
public function find(UserId $id): User
{
// Use injected dependency
$stmt = $this->connection->prepare('SELECT * FROM users WHERE id = ?');
$stmt->execute([$id->value()]);
return $this->hydrate($stmt->fetch());
}
}
// Container configuration (single instance if needed)
$container->singleton(PDO::class, fn () => new PDO($dsn));
Use Factory for Complex Creation
// CORRECT: Factory instead of Singleton
final readonly class ConnectionFactory
{
public function __construct(
private DatabaseConfig $config,
) {}
public function create(): PDO
{
return new PDO(
$this->config->dsn(),
$this->config->username(),
$this->config->password(),
);
}
}
Output Format
### Singleton Anti-Pattern: [Description]
**Severity:** ð´/ð /ð¡
**Location:** `file.php:line`
**Issue:**
[Description of the singleton/global state problem]
**Impact:**
- Hidden dependency â not visible in constructor
- Tight coupling â cannot substitute in tests
- Global state â shared mutable state between tests/requests
**Code:**
```php
// Current singleton usage
Fix:
// Refactored with dependency injection