Detects Singleton anti-pattern in PHP code. Identifies global state via static instances, hidden dependencies, tight coupling, and testability issues.
From accnpx claudepluginhub dykyi-roman/awesome-claude-code --plugin accThis skill uses the workspace's default tool permissions.
Analyze PHP code for Singleton anti-pattern usage that introduces global state and tight coupling.
// 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]);
}
}
// 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
}
}
// 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
// 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];
}
}
// 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,
) {}
}
# 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"
| 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: 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));
// 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(),
);
}
}
### 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
Provides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.