1. Go to this page and download the library: Download geekset/octane-doctor library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
geekset / octane-doctor example snippets
// app/Services/UserCache.php
class UserCache
{
protected static array $cache = [];
}
// app/Services/ReportService.php
class ReportService
{
public function __construct(protected Request $request) {}
}
return [
'fail_on' => 'high',
'output' => 'table',
'paths' => [
app_path(),
config_path(),
],
'rules' => [
// Built in rules registered by default.
],
'custom_rules' => [
// Add your own rule classes here.
],
'ignore' => [
// Finding fingerprints or rule ids to suppress.
],
'baseline' => storage_path('app/octane-doctor-baseline.json'),
];
'ignore' => [
// Turn off an entire rule for this project.
'suspicious-singleton-name',
// Suppress one specific finding by its fingerprint (copy it from the JSON output).
'a1b2c3d4e5f60789',
],
namespace App\Octane\Rules;
use App\Tenancy\TenantContext;
use OctaneDoctor\Enums\Category;
use OctaneDoctor\Enums\RiskClass;
use OctaneDoctor\Enums\Severity;
use OctaneDoctor\Finding;
use OctaneDoctor\Rules\Rule;
use OctaneDoctor\Rules\RuleExplanation;
use OctaneDoctor\Scanning\ScanContext;
class ForbidTenantContextSingleton implements Rule
{
public function id(): string
{
return 'app-no-tenant-context-singleton';
}
public function title(): string
{
return 'TenantContext must not be a singleton';
}
public function severity(): Severity
{
return Severity::High;
}
public function category(): Category
{
return Category::SingletonSafety;
}
public function riskClass(): RiskClass
{
return RiskClass::DataLeak;
}
public function explanation(): RuleExplanation
{
return new RuleExplanation(
whyItMatters: 'TenantContext holds the current tenant for one request only. A singleton binding keeps the first request\'s tenant in place for every later request the worker handles.',
remediation: 'Bind it with scoped() so each request gets its own instance.',
examples: [
'$this->app->singleton(TenantContext::class); // flagged',
'$this->app->scoped(TenantContext::class); // safe',
],
);
}
public function run(ScanContext $context): iterable
{
$binding = $context->app->getBindings()[TenantContext::class] ?? null;
if ($binding === null || ($binding['shared'] ?? false) !== true) {
return;
}
yield new Finding(
ruleId: $this->id(),
title: $this->title(),
severity: $this->severity(),
category: $this->category(),
riskClass: $this->riskClass(),
summary: TenantContext::class.' is bound as a singleton.',
whyItMatters: $this->explanation()->whyItMatters,
remediation: $this->explanation()->remediation,
symbol: TenantContext::class,
);
}
}
bash
# Fail only on high severity findings (default for fresh installs).
php artisan octane-doctor:scan --fail-on=high
# Fail on anything medium and above.
php artisan octane-doctor:scan --fail-on=medium
# Run as an informational report; always exits 0.
php artisan octane-doctor:scan --fail-on=never