Environment Management

By the end of this lesson you'll keep passwords and API keys out of your code entirely — storing them in a .env file, reading them at runtime, and running the same code safely on dev, staging, and production.

Learn Python, JavaScript, Java and more with free interactive lessons, real projects and AI-powered help. Beginner-friendly.

Part of the free Php course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

What You'll Learn in This Lesson

1️⃣ The Problem: Secrets in Code

A secret is any value you'd be unhappy to see on a billboard — a database password, an API key, a token. The cardinal sin is writing one straight into your source: $dbPass = "super_secret_123"; . The moment you commit that, it's in your git history forever — deleting the line later does nothing, because the old commit still has it. The fix is environment variables : values that live outside your code, on the machine, and that your program reads when it runs. In PHP the everyday way to manage them is a .env file — plain text, one KEY=value per line.

2️⃣ Loading a .env with phpdotenv

The standard tool is the vlucas/phpdotenv library (install it with composer require vlucas/phpdotenv ). You point it at the folder holding your .env , call load() , and every line becomes available through getenv() , $_ENV , and $_SERVER . The worked example below parses the same text by hand so you can watch what the library does for you — then reads the values back the real way.

In a real app you write only the three lines at the top ( createImmutable → load() ) and the library does the parsing. Immutable means a real server-level variable always wins over the .env file — exactly what you want in production.

3️⃣ Defaults and Types (the env() helper)

Two traps catch everyone. First, getenv("MISSING") returns the boolean false when a key isn't set — not an error, just a silent false that breaks things downstream. Second, every value from a .env is a string , so "false" is the text f-a-l-s-e, which is truthy in PHP. A tiny env() helper solves both: it supplies a default for missing keys and converts "true" / "false" into real booleans. (Frameworks like Laravel ship exactly this helper.)

4️⃣ Config Per Environment (dev / staging / prod)

The 12-Factor App methodology has one golden rule here: separate config from code . You ship one identical codebase to every machine, and only the environment differs. So your laptop, the staging server, and production all run the same files — but production reads a .env where APP_ENV=production , turning debug off and caching on . The litmus test: could you make this repo public right now without leaking a single password? If yes, your config is in the right place.

Now you try. The script below has already "loaded" the environment for you — fill in each ___ using the 👉 hint, then run it and check it against the Output panel.

One more — the part that keeps apps from crashing. Finish env() so a missing key falls back to the default you pass in.

5️⃣ Keeping Secrets Out of Git

The .env file holds real secrets, so it must never be committed. You enforce that with one line in your .gitignore . But your teammates still need to know which variables the app expects — so you commit a .env.example instead: the same keys, with blank or fake values. It's living documentation that leaks nothing. A new developer just copies it: cp .env.example .env , then fills in their own values.

📋 Quick Reference — Environment & Config

No code is filled in this time — just a brief and an outline. Write it yourself, run it on onecompiler.com/php or your own machine, then check your result against the expected output in the comments. This is exactly the read-config-safely loop every real app uses on startup.

Practice quiz

Why is it dangerous to hardcode a database password directly in your code?

  • PHP refuses to run files that contain passwords
  • Hardcoded strings are automatically logged to the screen
  • Once committed, the password lives in git history forever, and the same code can't run with different credentials per environment
  • It makes the file too large to deploy

Answer: Once committed, the password lives in git history forever, and the same code can't run with different credentials per environment. Committed secrets stay in history forever, and baking them in means you'd need a different copy of the code per environment.

What is the format of each line in a .env file?

  • KEY=value, one per line
  • <?php define(KEY, value); ?>
  • JSON objects
  • KEY: value with YAML indentation

Answer: KEY=value, one per line. A .env file is plain text — one KEY=value pair per line, no spaces around the =.

Which standard PHP library loads a .env file into $_ENV and getenv()?

  • symfony/dotenv-loader
  • php-env/reader
  • composer/dotenv
  • vlucas/phpdotenv

Answer: vlucas/phpdotenv. The lesson uses vlucas/phpdotenv: createImmutable() then load() fills $_ENV, getenv(), and $_SERVER.

What does getenv("MISSING") return when the key is not set?

  • null
  • The boolean false
  • An empty string
  • It throws an exception

Answer: The boolean false. getenv() returns the boolean false for an unset key — a silent false that breaks things downstream if not handled.

Why does APP_DEBUG=false in a .env still behave as if debug is on, unless you convert it?

  • Because every .env value is a string, and the non-empty string "false" is truthy in PHP
  • Because .env files are case-insensitive
  • Because false is a reserved word PHP ignores
  • Because phpdotenv inverts booleans

Answer: Because every .env value is a string, and the non-empty string "false" is truthy in PHP. All .env values are strings; the text "false" is truthy, so you must compare === "true" or map it with an env() helper.

What is the main purpose of a small env() helper function in this lesson?

  • To encrypt the .env file
  • To upload secrets to a server
  • To supply a default for missing keys and convert "true"/"false" strings into real booleans
  • To delete unused environment variables

Answer: To supply a default for missing keys and convert "true"/"false" strings into real booleans. The env() helper returns a default when a key is missing and converts common string values into real PHP types.

According to the 12-Factor App rule taught here, what should differ between dev, staging, and production?

  • The codebase — ship a separate build per environment
  • Only the environment/config — you ship one identical codebase everywhere
  • The PHP version on each machine
  • Nothing — all three must be identical including secrets

Answer: Only the environment/config — you ship one identical codebase everywhere. Separate config from code: ship one codebase and feed it a different .env on each machine.

What should you do with the real .env file regarding git?

  • Commit it so teammates have the secrets
  • Encrypt it and commit the encrypted version
  • Rename it to config.php and commit that
  • List it in .gitignore so it is never committed, and commit a .env.example instead

Answer: List it in .gitignore so it is never committed, and commit a .env.example instead. The real .env holds secrets and must be gitignored; commit a .env.example with the same keys but blank/fake values.

If you accidentally committed and pushed a .env with a live password, what is the most important fix?

  • Just delete the file in a new commit
  • Rotate the credential (generate a new password/key), because it is now in git history
  • Make the repository private
  • Add a comment saying it's a fake password

Answer: Rotate the credential (generate a new password/key), because it is now in git history. Removing the file later doesn't help — the secret is in history. Treat it as compromised and rotate it.

What is the purpose of committing a .env.example file?

  • It is the file the app actually reads at runtime
  • It stores the production password safely
  • It documents which keys the app needs, with blank or fake values, without leaking any real secret
  • It is required by Composer to install dependencies

Answer: It documents which keys the app needs, with blank or fake values, without leaking any real secret. .env.example is living documentation: same keys, no real values — a new developer copies it to .env and fills in their own.