Start Here

Windows Setup

On Windows, PHP can run directly in PowerShell, through a local development stack, inside WSL, or inside containers. The important skill is proving which runtime your project is using.

Windows PHP setup problems usually come from PATH order, multiple PHP installs, missing DLL extensions, wrong php.ini, or mixing Windows PHP with WSL PHP without realising they are separate environments.

Common Windows PHP Setups

Typical options include:

  • official PHP ZIP builds
  • XAMPP, Laragon, WampServer, or similar stacks
  • Herd or other modern local development apps
  • WSL with Linux PHP
  • Docker Desktop containers

Any of these can work. The professional habit is recording which one the project expects and then proving the active PHP runtime.

Find The Active PHP Binary

In PowerShell:

where php
php -v

Example output:

C:\php\php.exe
PHP 8.3.12 (cli)

If where php prints several paths, Windows will use the first one found on PATH. That first result is the one php runs from the terminal.

If php -v fails, PHP is either not installed, not on PATH, or the terminal session has not picked up the updated PATH yet.

PATH On Windows

PATH is the list of directories Windows searches when you type php.

You can inspect it in PowerShell:

$env:Path -split ';'

After changing PATH through Windows settings or an installer, close and reopen PowerShell. Existing terminals may not see the update.

Avoid leaving old PHP installs earlier on PATH. That is how a project accidentally runs C:\xampp\php\php.exe when you expected C:\php\php.exe.

php.ini And Extensions

Check the loaded configuration:

php --ini

Check extensions:

php -m
php -m | Select-String mbstring

Important Windows-specific detail: many extensions are DLL files loaded from extension_dir.

Check the configured extension directory:

php -i | Select-String "extension_dir"

If extension_dir points at the wrong folder, PHP may not load extensions even when the .dll files exist somewhere else.

Inside PHP:

PHP example
<?php

declare(strict_types=1);

echo 'Loaded ini: ' . (php_ini_loaded_file() ?: 'none') . PHP_EOL;
echo 'extension_dir: ' . ini_get('extension_dir') . PHP_EOL;

foreach (['mbstring', 'pdo', 'openssl'] as $extension) {
    echo $extension . ': ';
    echo extension_loaded($extension) ? 'loaded' : 'missing';
    echo PHP_EOL;
}

// Prints:
// Loaded ini: C:\php\php.ini
// extension_dir: ext
// mbstring: loaded
// pdo: loaded
// openssl: loaded

Composer On Windows

Composer uses a PHP runtime too. Check both:

php -v
composer --version
composer check-platform-reqs

If Composer reports missing extensions, do not immediately edit application code. First confirm:

  • which php.exe is active
  • which php.ini is loaded
  • whether the extension is enabled
  • whether extension_dir points at the correct DLL folder

Windows PHP And WSL Are Separate

Windows PHP and WSL PHP are different runtimes.

PowerShell:

where php
php -v

WSL shell:

which php
php -v

Those commands can show different PHP versions, extensions, configuration files, and filesystem paths.

If a project runs inside WSL, install and verify PHP inside WSL. If it runs in PowerShell or a Windows stack, verify Windows PHP. Mixing the two is a common source of confusion.

Run A First Script

Create hello.php:

PHP example
<?php

declare(strict_types=1);

echo 'Hello from PHP ' . PHP_VERSION . PHP_EOL;

// Prints:
// Hello from PHP 8.3.12

Run it:

php hello.php
php -l hello.php

Run A Local Web Request

Create public/index.php:

PHP example
<?php

declare(strict_types=1);

header('Content-Type: text/plain');

echo 'Hello over HTTP' . PHP_EOL;
echo 'SAPI: ' . PHP_SAPI . PHP_EOL;

// Browser output:
// Hello over HTTP
// SAPI: cli-server

Start the built-in server:

php -S 127.0.0.1:8000 -t public

Open:

http://127.0.0.1:8000/

This proves Windows CLI PHP can serve a local request. It does not prove IIS, Apache, Nginx, WSL, or Docker are configured.

Choose The Correct Official Build

The official Windows downloads provide several builds. Choose based on architecture and execution model, not by selecting the first ZIP filename.

Architecture

Official guidance recommends x64 for almost all current Windows installations. Confirm the operating-system architecture before downloading. An x86 build is for a deliberate 32-bit requirement, not a generic compatibility choice.

A PHP process and its extension DLLs must use compatible architecture. An x64 PHP executable cannot load an x86 extension DLL.

Thread Safety

Windows downloads distinguish Non Thread Safe (NTS) and Thread Safe (TS) builds.

  • NTS is intended for single-threaded execution such as CLI and FastCGI, including typical IIS FastCGI use.
  • TS supports multithreaded SAPIs and is used when PHP is loaded as a module into a compatible web server such as Apache.

The built-in development server can be run from the CLI build selected for local learning. Do not switch to TS merely because the code will eventually answer web requests; the web-server integration determines the requirement.

Inspect the current build:

php -i | Select-String "Thread Safety"
php -i | Select-String "Architecture"

Runtime Libraries

Current official Windows builds are compiled with Microsoft Visual Studio toolchains and require the supported Microsoft Visual C++ Redistributable. If php.exe fails before it can print a PHP error, verify the runtime requirement documented beside the exact download.

Do not download random DLL files from third-party DLL sites. Install the signed Microsoft redistributable or the package required by the trusted PHP distribution.

Manual ZIP Installation

A manual official ZIP installation is portable in the sense that its files can live in a chosen directory, but it still requires deliberate configuration.

A controlled outline is:

  1. Download the correct supported ZIP from the official PHP Windows downloads page.
  2. Verify that the branch, architecture, and TS/NTS choice match the intended use.
  3. Extract it to a stable directory such as C:\tools\php\8.5, not a temporary download folder.
  4. Create php.ini from the supplied development or production template according to the environment.
  5. Review extension and timezone settings rather than enabling every commented extension.
  6. Add the selected PHP directory to PATH if host-wide CLI access is intended.
  7. Open a new PowerShell session and verify path, version, configuration, and extensions.

The development and production INI templates are starting points. A local development runtime should display or log useful errors appropriately, while production should not expose diagnostic details to users. Do not copy a production server's secrets or paths into a workstation INI.

Keep version directories separate when multiple branches are required. Replacing files inside one shared C:\php directory makes rollback and extension matching harder to reason about.

Use PowerShell Command Discovery Precisely

In PowerShell, where can be an alias for Where-Object, depending on how it is invoked. where.exe php calls the Windows executable search tool explicitly. Get-Command provides PowerShell-native evidence:

Get-Command php -All
where.exe php
(Get-Command php).Source
php -v

The first applicable command in the current session wins. PowerShell can also resolve aliases and functions, so Get-Command php -All is useful when a command is not simply an executable on PATH.

Inspect machine and user path entries carefully. Editing the graphical environment-variable settings does not update processes that are already running. Reopen PowerShell, Windows Terminal tabs, IDEs, and other launchers that inherited the old environment.

Avoid putting several stack and manual PHP directories on global PATH. A project-specific wrapper, WSL shell, container command, or development app can be clearer than continually reordering machine-wide entries.

Configure php.ini Deliberately

The Windows ZIP contains template INI files, but PHP reports Loaded Configuration File: (none) until a configuration file is selected or found. Use:

php --ini

The output shows the loaded file and scanned configuration locations. Do not assume that editing php.ini-development changes PHP; it is a template unless copied or selected as the active configuration.

For a manual installation, extension directives often refer to DLLs under an ext directory. Prefer an unambiguous extension directory associated with the selected installation. After changes, rerun php --ini, php -m, and a focused extension check.

When PHP prints a startup warning before script output, read the complete warning. It can identify a missing module, dependency, procedure, architecture mismatch, or incorrect path. Suppressing startup errors hides the installation defect.

Match Extension DLLs To The Runtime

A Windows extension module must match several properties:

  • PHP major and minor module API;
  • x64 or x86 architecture;
  • TS or NTS build mode;
  • compiler/runtime compatibility;
  • dependent native libraries.

An extension file existing under ext does not prove it can load. Use an extension build provided for the exact runtime or install it through the development stack or package owner.

Check build facts with php -i and extension state with:

php -m
php --ri mbstring

If an extension used by a stack works in its web server but not in PowerShell, compare the executable and INI path. Enabling it in the stack's INI will not configure a separate official ZIP runtime.

Decide Between Native Windows, WSL, And Containers

Choose one primary project environment and document it.

Native Windows PHP integrates directly with PowerShell and Windows files. WSL provides a Linux userland with its own package manager, PHP executable, paths, permissions, services, and shell. Containers provide image-defined runtimes accessed through container commands.

Avoid crossing boundaries casually:

  • a Windows Composer executable can invoke Windows PHP against files stored in WSL;
  • a WSL Composer installation uses Linux PHP and Linux paths;
  • Docker Desktop can mount either filesystem with different performance and permission characteristics;
  • editor extensions may run on the Windows side while the project terminal runs inside WSL.

For a WSL-first project, keep the repository and tools in the Linux environment when the team's documentation recommends it. Verify with which php and php -v inside WSL. Do not troubleshoot its missing extension by editing a Windows php.ini.

Keep Development Stacks Coherent

XAMPP, Laragon, WampServer, Herd, and similar tools bundle or manage several components. Operate their PHP, web server, extensions, and version switching through the same tool unless its documentation says otherwise.

Record the stack version, selected PHP branch, CLI integration, document root, and service controls. A stack can be convenient, but it should not become an unidentified source of executables earlier on global PATH.

Before uninstalling or upgrading a stack, preserve project databases and configuration that the tool owns. Source code should live in version control rather than relying on the stack directory as its only copy.

Windows-Specific Failure Patterns

Symptom First boundary to inspect
php.exe will not start Architecture and Visual C++ runtime requirement
Wrong PHP branch runs Get-Command php -All, where.exe php, and PATH
No INI file is loaded ZIP extraction and active php.ini selection
Startup warning names a DLL Extension path, build match, and native dependencies
PowerShell works but stack website fails Stack-owned PHP, INI, and web service
WSL command differs from PowerShell Expected separate operating environments
PATH change appears ignored Restart the terminal or application inheriting the environment

Do not disable antivirus, turn off Windows security, or run every development command as Administrator as a first response. Use signed trusted packages, inspect any quarantine event specifically, and correct file permissions or ownership without granting the application unnecessary authority.

Windows Setup Checklist

For a project setup note, record:

  1. Whether the project uses Windows PHP, WSL, Docker, or a dev stack.
  2. What where php returns.
  3. What php -v returns.
  4. What php --ini returns.
  5. What extension_dir is set to.
  6. Which extensions the project requires.
  7. Whether composer check-platform-reqs passes.
  8. How to run the local server.

This gives the next developer a way to prove their environment instead of guessing.

What You Should Be Able To Do

After this lesson, you should be able to find the active PHP binary on Windows, inspect PATH, check php.ini, verify extension_dir, check loaded extensions, understand the Windows/WSL boundary, and run a basic PHP script and local server.

For junior PHP work, this matters because Windows can have several PHP runtimes installed at once. The useful skill is identifying the one your project is actually using.

Practice

Practice: Produce A Windows PHP Setup Record

Requirements

For the selected environment, record:

  • installation owner and runtime boundary;
  • executable source, PHP version, architecture, and thread-safety mode;
  • loaded INI files, extension_dir, and modules;
  • Composer platform result;
  • CLI syntax/execution smoke test;
  • local HTTP smoke test and document root.

If native official ZIP PHP is used, record why the selected x64/x86 and TS/NTS build is appropriate and confirm the required Visual C++ runtime. Explain which checks must instead run inside WSL or Docker when those environments own the project.

Show solution
Get-Command php -All
(Get-Command php).Source
php -v
php -i | Select-String "Architecture|Thread Safety"
php --ini
php -i | Select-String "extension_dir"
php -m
composer check-platform-reqs
php -l hello.php
php hello.php

Record the installation owner, such as official x64 NTS ZIP under C:\tools\php\8.5. NTS is appropriate for CLI and FastCGI; TS is required for relevant multithreaded module use. The official download notes identify the Microsoft Visual C++ Redistributable required by that build.

Start a local development request with:

php -S 127.0.0.1:8000 -t public

Record that it proves only the selected CLI PHP can serve the local public entry point.

For a WSL-first project, run Linux equivalents inside WSL and identify the distribution. For Docker, run checks inside the documented service. For a development stack, record its selected branch, CLI integration, web service, and configuration. Native PowerShell output is irrelevant when another environment owns application execution.

Task: Resolve A Windows PHP Command Conflict

Requirements

  • Use PowerShell-native command discovery and where.exe to list candidates.
  • Inspect ordered PATH entries and aliases or functions.
  • Identify which running applications still have an old environment snapshot.
  • Correct activation without deleting the XAMPP installation.
  • Verify executable, version, INI, and Composer platform requirements afterward.
  • Explain why the first command candidate matters and why running as Administrator is not a path fix.
Show solution
Get-Command php -All
where.exe php
$env:Path -split ';'
php -v
php --ini

Get-Command also reveals aliases or functions, while where.exe searches executable paths. The first applicable command is what php invokes.

Place the intended C:\tools\php\8.5 directory before the XAMPP PHP directory for the environment where this project runs, or use a project-specific launcher. Preserve XAMPP because another project may use its bundled PHP and services.

Close and reopen PowerShell, Windows Terminal tabs, and the IDE after changing persistent environment settings. Existing processes retain the environment inherited when they started.

Verify:

(Get-Command php).Source
php -v
php --ini
composer check-platform-reqs

The selected source must be the intended executable, with matching version and configuration. Administrator privileges do not change command search order; elevation would only grant unnecessary authority and could create project files with inappropriate ownership or permissions.

Task: Diagnose A Windows Extension DLL Failure

Native Windows PHP starts with a warning that php_intl.dll cannot be loaded. The file exists under an ext directory, but extension_loaded('intl') returns false.

Requirements

  • Record the selected php.exe, version, architecture, thread-safety mode, and loaded INI file.
  • Inspect the resolved extension_dir, startup warnings, and loaded modules.
  • Check whether the DLL matches the PHP branch, x64/x86 architecture, and TS/NTS build.
  • Account for dependent native libraries and the required Visual C++ Redistributable.
  • Create a PHP diagnostic for mbstring, intl, pdo, and available PDO drivers.
  • Explain why copying a DLL from another PHP installation or downloading one from an arbitrary DLL site is unsafe.
  • Define the verification required after repairing the owning installation.
Show solution
(Get-Command php).Source
php -v
php -i | Select-String "Architecture|Thread Safety"
php --ini
php -i | Select-String "extension_dir"
php -m
php --ri intl

Read the complete startup warning. Confirm that the active INI enables intl, its extension_dir resolves to the expected installation's ext directory, and the DLL belongs to the same PHP branch, architecture, and TS/NTS build. Verify the Microsoft Visual C++ Redistributable and any native dependencies required by the trusted PHP package.

A focused script is:

PHP example
<?php

declare(strict_types=1);

echo 'PHP binary: ', PHP_BINARY, PHP_EOL;
echo 'Loaded ini: ', php_ini_loaded_file() ?: 'none', PHP_EOL;
echo 'extension_dir: ', ini_get('extension_dir'), PHP_EOL;

foreach (['mbstring', 'intl', 'pdo'] as $extension) {
    echo $extension, ': ', extension_loaded($extension) ? 'loaded' : 'missing', PHP_EOL;
}

$drivers = extension_loaded('pdo') ? PDO::getAvailableDrivers() : [];
echo 'PDO drivers: ', $drivers === [] ? 'none' : implode(', ', $drivers), PHP_EOL;

Repair the extension through the same ZIP distribution, stack, package, or development tool that owns php.exe. Do not copy a DLL from another runtime: module API, architecture, thread safety, compiler runtime, and dependencies must match. Arbitrary DLL download sites also remove provenance and signature confidence.

After repair, open a new shell if configuration changed and rerun php -v, php --ini, php -m, php --ri intl, the diagnostic script, Composer's platform check, and relevant project tests. The file merely existing is not success; the intended runtime must load and use it without startup warnings.