[PHP-DEV] Better development streamlining of the built-in server

Greetings, I have recently started using the great feature of the built-in server for local development of an API that I have created from scratch, and a frontend that can interface with the API. Having a ready server makes development so much easier for a wider community of developers, without requiring to have a local Apache or Nginx. I even recently discovered the great feature that was introduced in PHP 7.4 PHP_CLI_SERVER_WORKERS, which I sorely needed because my API makes a couple of requests internally to various paths of the API, and nested requests require more than one process / worker, which wasn’t a problem for the Nginx / Apache production server but would have been a problem on localhost using the built-in server if it weren’t for this environment variable.

Seeing I have a number of people interested in contributing to the project, they have been asking me how they can develop on localhost without the trouble of setting up a whole WAMP environment. Having dabbled a bit in Docker and Ruby and Node and React, I have seen how streamlined the local development process is, because if you have any dotenv files they will be automagically read and loaded into the server environment. So you can have an environment file for local developement .env.development and another environment file for a production instance .env.production, and the application logic can detect whether it’s running on a development server or on a production server.

One of the nice features of React and frameworks that build off of React (such as Create React App or the more recent NextJS) is that they automatically create an environment variable of process.env.NODE_ENV with a value of development when you are running on localhost. I think it would be a nice feature of PHP’s built-in server to do something similar, automatically provide a $_ENV[APP_ENV] variable with a value of development so that the application logic knows whether it’s running on a localhost development environment rather than on a production server.

Ruby doesn’t have this functionality built-in, you need to require the dotenv-rails ruby gem, but it really does make the development process so much more user friendly. Instead, Docker and React provide this feature automatically.

In my use case, I have an API backend that I can spin up with php -S localhost:8000. Then when I spin up the frontend (which is also written in PHP), it knows nothing about where to find the API backend. On the production server, I had the host hardcoded into the frontend application. But if I want to be able to dynamically switch between localhost and production, I need to be able to instruct the frontend of where to find the API backend. This can easily be achieved through dotenv files, and even better if we have an automatic environment variable of $_ENV[APP_ENV]=development. I see that there is a composer package (GitHub - vlucas/phpdotenv: Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.) that can achieve loading dotenv files, but other than loading dotenv files by means of a composer package it would be nice if we could at least have an automatic environment variable of $_ENV[APP_ENV]=development created by PHP’s built-in server. Seeing that the built-in server will pretty much always be used for easy localhost development, I don’t see any reason why a development environment variable couldn’t or shouldn’t be created by the built-in server? It would make PHP localhost development that much more user friendly in my opinion…

Here for example is the Create React App documentation page about environment variables Adding Custom Environment Variables | Create React App :

By default you will have NODE_ENV defined for you, and any other environment variables starting with REACT_APP_.
There is a built-in environment variable called NODE_ENV. You can read it from process.env.NODE_ENV. When you run npm start, it is always equal to ‘development’, when you run npm test it is always equal to ‘test’, and when you run npm run build to make a production bundle, it is always equal to ‘production’. You cannot override NODE_ENV manually. This prevents developers from accidentally deploying a slow development build to production.

Similarly NextJS Configuring: Environment Variables | Next.js :

Next.js has built-in support for loading environment variables from .env* files into process.env.

And process.env.NODE_ENV is automatically set to development when running next dev; to test when running next test; and to production when running next build or next start.

I believe having similar functionality with PHP’s built-in server can make it more user / developer friendly to folks who are used to working with Docker or Node or Ruby.

John R.D.

On 19.11.2024 at 03:33, John D'Orazio wrote:

[…] I even recently discovered the great
feature that was introduced in PHP 7.4 `PHP_CLI_SERVER_WORKERS`, which I sorely
needed because my API makes a couple of requests internally to various paths of
the API, and nested requests require more than one process / worker, which
wasn't a problem for the Nginx / Apache production server but would have been a
problem on localhost using the built-in server if it weren't for this
environment variable.

Seeing I have a number of people interested in contributing to the project, they
have been asking me how they can develop on localhost without the trouble of
setting up a whole WAMP environment. […]

I don't quite understand how WAMP fits into this. WAMP is Windows only,
and on Windows `PHP_CLI_SERVER_WORKERS` is not supported.

In my use case, I have an API backend that I can spin up with `php -S
localhost:8000`. Then when I spin up the frontend (which is also written in
PHP), it knows nothing about where to find the API backend. On the production
server, I had the host hardcoded into the frontend application. But if I want to
be able to dynamically switch between localhost and production, I need to be
able to instruct the frontend of where to find the API backend. This can easily
be achieved through dotenv files, and even better if we have an automatic
environment variable of `$_ENV[APP_ENV]=development`. I see that there is a
composer package (GitHub - vlucas/phpdotenv: Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.) that can achieve loading
dotenv files, but other than loading dotenv files by means of a composer package
it would be nice if we could at least have an automatic environment variable of
`$_ENV[APP_ENV]=development` created by PHP's built-in server. Seeing that the
built-in server will pretty much always be used for easy localhost development,
I don't see any reason why a `development` environment variable couldn't or
shouldn't be created by the built-in server? It would make PHP localhost
development that much more user friendly in my opinion...

Besides that setting an environment globally is not necessarily
possible, this looks somewhat backwards to me, and is in my opinon way
too special – why would only the built-in development server advertize
itself as a development environment?

Christoph

I think it would be a nice feature of PHP’s built-in server to do something similar, automatically provide a $_ENV[APP_ENV] variable with a value of development so that the application logic knows whether it’s running on a localhost development environment rather than on a production server.

Why couldn’t you just do

if (php_sapi_name() === 'cli-server') {
$_ENV['APP_ENV'] = 'development';
}

in your entry point script?

···

Best regards,
Bruce Weirdan mailto:weirdan@gmail.com

Why couldn’t you just do

···

In fact this is pretty much how I currently handle the situation. Of course it can be handled in the entry point script.

My point is, would the built-in server ever be used for anything other than local development? If not, then it would be a simple convenience to developers to have an automatic environment variable of “development” when launching the built-in server. Instead of manually creating this environment variable, it would just be there. Making for a more familiar development environment in comparison to Node environments.

On Nov 19, 2024 1:33 PM, Bruce Weirdan weirdan@gmail.com wrote:

I think it would be a nice feature of PHP’s built-in server to do something similar, automatically provide a $_ENV[APP_ENV] variable with a value of development so that the application logic knows whether it’s running on a localhost development environment rather than on a production server.

Why couldn’t you just do

if (php_sapi_name() === 'cli-server') {
$_ENV['APP_ENV'] = 'development';
}

in your entry point script?

Best regards,
Bruce Weirdan mailto:weirdan@gmail.com

Sorry for the typo, I meant to write ‘LAMP’. I have always setup a Linux Apache MySQL PHP environment for local development. But I find that not everyone is so keen on such an involved local setup. Which is why I find the built-in server such a great convenience. It greatly simplifies the development process. Do my two cents are simply, why not take it one more step and make an environment similar / familiar to other development environments?

···

On Nov 19, 2024 1:12 PM, “Christoph M. Becker” cmbecker69@gmx.de wrote:

On 19.11.2024 at 03:33, John D’Orazio wrote:

[…] I even recently discovered the great
feature that was introduced in PHP 7.4 PHP_CLI_SERVER_WORKERS, which I sorely
needed because my API makes a couple of requests internally to various paths of
the API, and nested requests require more than one process / worker, which
wasn’t a problem for the Nginx / Apache production server but would have been a
problem on localhost using the built-in server if it weren’t for this
environment variable.

Seeing I have a number of people interested in contributing to the project, they
have been asking me how they can develop on localhost without the trouble of
setting up a whole WAMP environment. […]

I don’t quite understand how WAMP fits into this. WAMP is Windows only,
and on Windows PHP_CLI_SERVER_WORKERS is not supported.

In my use case, I have an API backend that I can spin up with php -S localhost:8000. Then when I spin up the frontend (which is also written in
PHP), it knows nothing about where to find the API backend. On the production
server, I had the host hardcoded into the frontend application. But if I want to
be able to dynamically switch between localhost and production, I need to be
able to instruct the frontend of where to find the API backend. This can easily
be achieved through dotenv files, and even better if we have an automatic
environment variable of $_ENV[APP_ENV]=development. I see that there is a
composer package (GitHub - vlucas/phpdotenv: Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.) that can achieve loading
dotenv files, but other than loading dotenv files by means of a composer package
it would be nice if we could at least have an automatic environment variable of
$_ENV[APP_ENV]=development created by PHP’s built-in server. Seeing that the
built-in server will pretty much always be used for easy localhost development,
I don’t see any reason why a development environment variable couldn’t or
shouldn’t be created by the built-in server? It would make PHP localhost
development that much more user friendly in my opinion…

Besides that setting an environment globally is not necessarily
possible, this looks somewhat backwards to me, and is in my opinon way
too special – why would only the built-in development server advertize
itself as a development environment?

Christoph

On 19/11/2024 12:52, John D'Orazio wrote:

My point is, would the built-in server ever be used for anything other than local development? If not, then it would be a simple convenience to developers to have an automatic environment variable of "development" when launching the built-in server. Instead of manually creating this environment variable, it would just be there.

As a matter of best practice, an application should not need to know which environment it is running in, let alone be designed specifically to respond to the PHP built-in server.

Cheers,
Bilge

I don’t understand why an application should not know which environment it is running in? You should be able to instruct a frontend where to look for a backend, depending on the environment it is running in. What best practice are you referring to?
And may I ask why the PHP built-in server was created in the first place, if we already have Apache and Nginx server environments? Is it not a convenient for development environments?

···

On Nov 19, 2024 1:59 PM, Bilge bilge@scriptfusion.com wrote:

On 19/11/2024 12:52, John D’Orazio wrote:

My point is, would the built-in server ever be used for anything other than local development? If not, then it would be a simple convenience to developers to have an automatic environment variable of “development” when launching the built-in server. Instead of manually creating this environment variable, it would just be there.

As a matter of best practice, an application should not need to know which environment it is running in, let alone be designed specifically to respond to the PHP built-in server.

Cheers,
Bilge

On 19/11/2024 13:26, John D'Orazio wrote:

I don't understand why an application should not know which environment it is running in?

I don't know if this list is the place to be discussing this, but here goes:

Typically you are injecting the environment name to have the application switch to a different configuration to load a whole bunch of different settings for that environment. Instead, you should be injecting all those different settings directly. The reason is because otherwise you are tightly coupling your application to a fixed number of known environments, limiting deployment options. Instead, the application should be environment agnostic, thereby enabling whomsoever deploys your application to configure as many different environment types as they wish.

This best practice also avoids the temptation to code different logic paths based on the environment name (very bad).

Don't worry, this is a common mistake, and even popular frameworks like Laravel and Symfony promote this anti-pattern to some degree.

Cheers,
Bilge

On Tue, Nov 19, 2024, 6:38 a.m. Bilge <bilge@scriptfusion.com> wrote:

On 19/11/2024 13:26, John D’Orazio wrote:

I don’t understand why an application should not know which
environment it is running in?

I don’t know if this list is the place to be discussing this, but here goes:

Typically you are injecting the environment name to have the application
switch to a different configuration to load a whole bunch of different
settings for that environment. Instead, you should be injecting all
those different settings directly. The reason is because otherwise you
are tightly coupling your application to a fixed number of known
environments, limiting deployment options. Instead, the application
should be environment agnostic, thereby enabling whomsoever deploys your
application to configure as many different environment types as they wish.

This best practice also avoids the temptation to code different logic
paths based on the environment name (very bad).

Don’t worry, this is a common mistake, and even popular frameworks like
Laravel and Symfony promote this anti-pattern to some degree.

Cheers,
Bilge

So if symfony and laravel do this, whose best practices are we going by exactly, yours?

Switching configuration is one of many reasons an app needs to know what environment it’s running on. What if I want more verbose logging at dev time? Or want to bypass expensive api calls with mock data? Or want to make sure something is never called in production?

Cheers,
Hammed

On 19/11/2024 13:48, Hammed Ajao wrote:

So if symfony and laravel do this, whose best practices are we going by exactly, yours?

One common citation for this is "The Twelve-Factor App" [The Twelve-Factor App], a methodology/manifesto published in 2011 by some engineers from Heroku as a way to build a particular sort of cloud-native service-based application. Coincidentally, an open-source project to update and expand on it was officially announced last week: https://12factor.net/blog/open\-source\-announcement

The idea is that "in a twelve-factor app":

- configuration comes directly from the environment; for instance, provided by the orchestration layer in a container-based deployment
- it is not written to a file, but passed directly to the application as environment variables
- it does not indicate a named environment, but configures the application directly

When people struggled with development environments which didn't have a suitable place for these variables to come from, the ".env file" was invented - a way to emulate the environment variables that a "real" deployment would receive.

This then took on a life of its own, and eventually ".env" became just another format for configuration files: instead of "development.ini" and "production.ini", you have ".env.development" and ".env.production". We came full circle, and the only thing which actually comes from the environment is a master switch to choose the file to load.

Personally, I don't think there's anything wrong with the "master switch" approach for a lot of applications. Not everybody works for a company with hundreds of developers, and a dozen kubernetes clusters running A/B tests on continuously deployed code. Sometimes you really do know you have 2, or 3, or 4, environments to configure.

However, I don't think the master switch is something that should be hard-coded in a server. Maybe you know that if it's running under "php -S", it's "dev mode", and it it's running under Apache, it's "live mode"; if so, you can look at PHP_SAPI / php_sapi_name(). But maybe you do run Apache on a dev server; or you have separate debug and QA modes.

--
Rowan Tommins
[IMSoP]

On 19/11/2024 23:14, Rowan Tommins [IMSoP] wrote:

On 19/11/2024 13:48, Hammed Ajao wrote:

So if symfony and laravel do this, whose best practices are we going by exactly, yours?

One common citation for this is "The Twelve-Factor App" [The Twelve-Factor App], a methodology/manifesto published in 2011 by some engineers from Heroku as a way to build a particular sort of cloud-native service-based application. Coincidentally, an open-source project to update and expand on it was officially announced last week: https://12factor.net/blog/open\-source\-announcement

The idea is that "in a twelve-factor app":

- configuration comes directly from the environment; for instance, provided by the orchestration layer in a container-based deployment
- it is not written to a file, but passed directly to the application as environment variables
- it does not indicate a named environment, but configures the application directly

When people struggled with development environments which didn't have a suitable place for these variables to come from, the ".env file" was invented - a way to emulate the environment variables that a "real" deployment would receive.

This then took on a life of its own, and eventually ".env" became just another format for configuration files: instead of "development.ini" and "production.ini", you have ".env.development" and ".env.production". We came full circle, and the only thing which actually comes from the environment is a master switch to choose the file to load.

Personally, I don't think there's anything wrong with the "master switch" approach for a lot of applications. Not everybody works for a company with hundreds of developers, and a dozen kubernetes clusters running A/B tests on continuously deployed code. Sometimes you really do know you have 2, or 3, or 4, environments to configure.

However, I don't think the master switch is something that should be hard-coded in a server. Maybe you know that if it's running under "php -S", it's "dev mode", and it it's running under Apache, it's "live mode"; if so, you can look at PHP_SAPI / php_sapi_name(). But maybe you do run Apache on a dev server; or you have separate debug and QA modes.

You wrote the perfect post summarising a brief history of configuration injection and brought it back to the main point that the master switch does not belong in the web server, and I hadn't heard about the 12factor open source announcement so I learned something new, too! Bravo. Thanks for posting this and agree with everything stated herein.

Cheers,
Bilge