Back

When APP_ENV is qa

🧭

Is this package for you?

17 reasons, each a before and an after. Select the problems you have and see how many match.

Take the scorecard
πŸ—ΊοΈ

The class knows which environments it serves

A settings class defines one factory per environment. Which APP_ENV reaches which factory used to live in environment_map β€” a different file, and a different repository when the class ships inside a package. Reading the class showed you the method names and never the environments that reach them.

#[Environment] puts the answer beside the code.

Two files, and you must read both
// config/env-settings.php
'environment_map' => [
    'qa'  => 'staging',
    'uat' => 'staging',
],

// app/Settings/AuthSettings.php β€” says nothing
public static function staging(): static
One file, and it tells you
// app/Settings/AuthSettings.php
#[Environment('qa', 'uat')]
public static function qualityAssurance(): static

// no config entry needed at all

The second form also survives redistribution. Ship that class in a package and it resolves the same way in every application that installs it β€” no consumer has to edit their environment_map to make it work.

How a factory gets chosen

Four steps, first match wins.

1

#[Environment] on a method claims this APP_ENV wins

The class said so outright, so a map it cannot see does not get to override it.

2

environment_map translates APP_ENV to a method name

Unmapped names are tried as-is: APP_ENV=staging looks for staging().

3

fallback_environment

Configured default, development unless you change it.

4

development()

The last resort, always present β€” it is abstract on the base class.

Proof

One class, three marked factories, resolved nine times with env-settings:show --env=…. Every row is real output.

#[Environment('local', 'dev')]
public static function development()

#[Environment('production', 'prod')]
#[Environment('demo')]
public static function production()

#[Environment('qa', 'uat')]
public static function qualityAssurance()
APP_ENV Resolves to Why
local development() #[Environment('local', 'dev')]
dev development() #[Environment('local', 'dev')]
production production() #[Environment('production', 'prod')]
prod production() #[Environment('production', 'prod')]
demo production() #[Environment('demo')] β€” second attribute on the same method
qa qualityAssurance() #[Environment('qa', 'uat')] β€” the method name never matches
uat qualityAssurance() #[Environment('qa', 'uat')]
staging development() Nothing claims it, no staging() method β€” falls to fallback_environment
QA development() Matching is case-sensitive, so QA is not qa

🏷️ Names are free

qualityAssurance() serves qa. The method name no longer has to match the environment, so factories can be named for what they mean.

πŸ” Repeatable

Stack the attribute, or pass several names to one. production, prod and demo all reach the same factory above.

⚠️ Case-sensitive

APP_ENV=QA does not match 'qa' β€” it falls through to the fallback, silently. Matching is exact, because APP_ENV is.

πŸ₯Š

When the config disagrees

Set 'qa' => 'production' in environment_map while the class declares #[Environment('qa')] on another method, and the attribute wins β€” verified, not assumed. If two methods claim the same environment, the first declared wins.

🧬

It survives a local override

PHP does not inherit attributes onto an overridden method, so an override that redeclares a marked factory would lose its mapping and quietly resolve to development(). The resolver walks the class hierarchy to prevent exactly that. Only the mapping is inherited β€” the method still runs on your subclass, so your override's values are the ones used.

Read the attribute docs Enums Masking The gate In tests Overrides Is this for you? Back to the demo