Download the PHP package inpsyde/wp-app-container without Composer

On this page you can find all versions of the php package inpsyde/wp-app-container. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package wp-app-container

WP App Container

DI Container and related tools to be used at website level.

PHP Quality Assurance


Table of Contents


What is and what is not

This is a package aimed to solve dependency injection container, service providers, and application "bootstrapping", at application, i.e. website, level.

The typical use case is when building a website for a client, for which we foresee to write several "packages": library, plugins, and theme(s), that will be then "glued" together using Composer.

Thanks to this package will be possible to have a centralized dependency resolution, and a quite standardized and consistent structure for the backend of those packages.

Technically speaking, right now, there's nothing that prevents the use at package level, however for several reasons, that is a no-goal of this package and no code will be added here to comply with that.

This package was not written to be "just a standard", i.e. provide just the abstraction leaving the implementations to consumers, but instead had been written to be a ready-to-use implementation.

However, an underlying support for PSR-11 allows for very flexible usage.

Concepts overview

App

This is the central class of the package. It is the place where "application bootstrapping" happen, where Service Providers are registered, and it is very likely the only object that need to be used from the website "package" (that one that "glues" other packages/plugins/themes via Composer).

Service provider

The package provides a single service provider interface (plus several abstract classes that partially implement it). The objects are used to "compose" the Container. Moreover, in this package implementation, service providers are (or better, could be) responsible to tell how to use the registered services. In WordPress world that very likely means to "add hooks".

Container

This is a "storage" that is capable of storing, and retrieve objects by an unique identifier. On retrieval (oftentimes just the first time they are retrieved), objects are "resolved", meaning that any other object that is required for the target object to be constructed, will first recursively resolved in the container, and then injected in the target object before it is returned. The container implementation shipped here is an extension of Pimple, with added PSR-11 support, with the capability to act as a "proxy" to several other PSR-11 containers. Which means that Service Providers can "compose" the dependency tree in the Container either by directly adding services factories to the underlying Pimple container or they can "append" to the main container a ready-made PSR-11 container.

Env config

As stated above, this package targets websites development, and something that is going to be required at website level is configuration. Working with WordPress configuration often means PHP constants, but when using Composer at website level, in combination with, for example, WP Starter it also also means environment variables. The package ships an SiteConfig interface with an EnvConfig implementation that does nothing in the regard of storing configuration, but offers a very flexible way to read configuration both from constants and env vars. Container::config() method returns and instance of SiteConfig.

Context

Service providers job is to both add services in the container and add the hooks that make use of them, however in WordPress it often happens that services are required under a specific "context". For example, a service provider responsible to register and enqueue assets for the front-end is not required in backoffice (dashboard), nor in AJAX or REST requests, and so on. Using the proper hooks to execute code is something that can be often addressed, but often not. E.g. distinguish a REST request is not very easy at an early hook, or there's no function or constants that tell us when we are on a login page and so on. Moreover, even storing objects factories in the Container for things we are sure are not going to be used is waste of memory we can avoid. The Context class of this package is a centralized service that provides info on the current request. Container::context() method returns and instance of Context.

Decisions

Because we wanted a ready-to-use package, we needed to pick a DI container implementation, and we went for Pimple, for the very reason that it is one of the simplest implementation out there.

However, as shown later, anyone who want to use a different PSR-11 container will be very able to do so.

In the "Concepts Overview" above, the last two concepts ("Env Config" and "Context") are not really something that are naturally coupled with the other three, however, the assumption that this package will be used for WordPress websites allow us to introduce this "coupling" without many risks (or sense of guilt): assuming we would ship these packages separately, when building websites (which, again, is the only goal of this package) we will very likely going to require those separate packages anyway, making this the perfect example for the Common-Reuse Principle: classes that tend to be reused together belong in the same package together.

Usage at website level

The "website" package, that will glue together all packages, needs to only interact with the App class, in a very simple way:

That's it. This code is assumed to be placed in MU plugin, but as better explained later, it is possible to do it outside any MU plugin or plugin, either wrapping the App::boot() call in a hook or not.

This one-liner will create the relevant objects and will fire actions that will enable other packages to register service providers.

Customizing site config

By creating an instance of App via App::new(), it will take care of creating an instance of EnvConfig that will later returned when calling Container::config(). EnvConfig is an object that allows to retrieve information regarding current environment (e.g. production, staging, development...) and also to get settings stored as PHP constants or environment variables.

Information regarding running environment are auto-discovered from env variables supported by WP Starter or from configurations defined in well-known hosting like Automattic VIP or WP Engine. There's a fallback in case no environment can be determined: if WP_DEBUG: is true, development environment is assumed, otherwise production.

In any case, a filter: "wp-app-environment" is available for customization of the determined environment.

Regarding PHP constants, EnvConfig is capable to search for constants defined in the root namespace, but also inside other namespaces. For the latter case, the class has to configured to let it know which "alternative" namespaces are supported.

That can be done by creating an instance of Container that uses a custom EnvConfig instance, and then pass it to App::new(). For example:

With the code in the above snippet, the created EnvConfig instance (that will be available via Container::config() method) can return settings in AcmeInc\Config or AcmeInc namespaces (besides root namespace).

For example, if some configuration file contains:

it will be possible to do:

Note that EnvConfig::get() accepts an optional second $default parameter to be returned in case no constant and no matching environment variable is set for given name:

Hosting provider

EnvConfig::hosting() returns the current Hosting provider. Currently we're automatically detecting following:

Custom hosting can be setup via a HOSTING env variable or constant.

To check in code which is the current solution, there's a EnvConfig::hostingIs() method that accepts an hosting name string and returns true when the given hosting matches the current hosting.

Locations

Access locations

EnvConfig::locations() returns an instance of Inpsyde\App\Location\Locations which allows to resolve following directories and URLs:

On VIP Go (HOSTING value will be EnvConfig::HOSTING_VIP), additional locations can be obtained:

In fact, Locations is an interface, and currently there are three implementation of it, one for "generic" hosting, one for VIP Go and one for WP Engine.

An example:

Adjust locations

In case the package is not capable of discovering paths and URLs automatically (e.g. because a very custom setup) they can be set by using a LOCATIONS constant that is an an array with two top-level elements, one for URLs and one for paths, each being a map in form of array with location name as keys and location URL / path as value:

For example:

As array key, besides Locations::VENDOR, Locations::ROOT, and Locations::CONTENT, it is also possible to use any other Locations constant, e.g. Locations::MU_PLUGINS or Locations::LANGUAGES and so on.

The config provided is merged with defaults that can be fine-tuned depending on hosting.

Custom locations

Besides the Locations constants, it is also possible to use custom keys, and retrieve them using the Locations::resolveDir() and Locations::resolveUrl() methods.

For example:

and then:

In the example above, calling $locations->resolveUrl('logs') will return null because no URL was set for the key 'logs' in the LOCATIONS constant.

Set locations via environment variables

In the examples above, both default and custom locations are customized using the LOCATIONS constant that, for obvious reasons, can only be set in PHP configuration files.

For websites that rely on environment variables to set configuration, the package provides a different approach.

Environment variables in the format WP_APP_{$location}_DIR and WP_APP_{$location}_URL can be used to set location directories and URLs.

For example, vendor path can be set via WP_APP_VENDOR_DIR and vendor URL via WP_APP_VENDOR_URL, just like root path can be set via WP_APP_ROOT_DIR and root URL via WP_APP_ROOT_URL.

This works also for custom paths.

For example, by setting environment variables like this:

it is then possible to retrieve them like this:

Please note that if both WP_APP_* env variable and value in LOCATIONS constant are set for the same location, the env variable takes precedence.

Usage at package level

At package level there are two ways to register services (will be shown later), but first providers need to be added to the App:

The hook App::ACTION_ADD_PROVIDERS can actually more than once (more on this soon), but for now is relevant that even if the hook is fired more than once, the App class will be clever enough to add the provider only once.

Contextual registration

As shown in the example above, App::addProvider(), besides the service provider itself, accepts a variadic number of "Context" constants, that tell the App the given provider should be only used in the listed contexts.

The full list of possible constants is:

Package-dependant registration

Besides App::ACTION_ADD_PROVIDERS there's another hook that packages can use to add service providers to the App. It is: App::ACTION_REGISTERED_PROVIDER.

This hook is fired right after any provider is registered. Using this hook it is possible to register providers only if a given package is registered, allowing to ship libraries / plugins that will likely do nothing if other library / plugin are not available.

The just-registered package ID is passed as first argument by the hook. By default the package ID is the FQCN of the provider class, but that can be easily changed, so to be dependant on a package it is necessary to know the ID it uses.

One think important to note is that App::ACTION_REGISTERED_PROVIDER hook is fired only if the target service provider register() method returns true. If e.g. the provider is a "booted only" provider (more on this below) the hook will not be fired.

In that case it is possible to use App::ACTION_ADDED_PROVIDER hook, which works similarly and it is fired in the moment the provider is added, so before registration is ever attempted.

Providers workflow

As already stated multiple times, the scope of the library is to provide a common ground for service registration and bootstrapping of all packages that compose a website.

This means that it is necessary to allow generic libraries, MU plugins, plugins, and themes, to register their services, which means that, in theory, application should "wait" for all of those packages to be available. However, at same time, it is very possible that some packages will need to run at an early stage in the WordPress loading workflow.

To satisfy both these requirements, the App class runs its "bootstrapping procedure" from one to three times, depending on when App::boot() is called for first time.

If App::boot() is called first time before plugins_loaded hook, it will automatically called again at plugins_loaded and again at init. For a total of 3 times.

If App::boot() is called first time after (or during) plugins_loaded , but before init it will automatically called again at init. For a total of 2 times.

If App::boot() is called first time during init it will not be called again, so will run once in total.

If App::boot() is called first time after init an exception will be thrown.

Each time App::boot() is called, the App::ACTION_ADD_PROVIDERS action is fired allowing packages to add service providers.

Added service providers register() method, that add services in the container, is normally immediately called, unless the just added service provider declares to support "delayed registration" (more on this soon).

Added service providers boot() method, that makes use of the registered services, is normally delayed until last time App::boot() is called (WP is at init hook), but service providers can declare to support "early booting" (more on this soon), in which case their boot() method is called after the register method, without waiting boot() to be called for last time at init.

In the case a service provider supports both delayed registration and early booting, its register() method will still be called before its boot() method, but after having called the register() method of all non-delayed providers that are going to be booted in the same boot() cycle.

Considering the case in which App::boot() is ran 3 times, (before plugins_loaded, on plugins_loaded, and on init) the order of events is the following:

To understand if a provider has support for delayed registration or for early booting, we have to look at two methods of the ServiceProvider interface, respectively registerLater() and bootEarly(), both returns a boolean.

The ServiceProvider interface has a total of 5 methods. Besides the two already mentioned there's also an id() method, and then the two most relevant: register() and boot().

The package ships several abstract classes that provides definitions for some of the methods. All of them as an id() method that by default returns the name of the class (more on this soon) and define different combination of registerLater() and bootEarly(). Some of theme also register empty boot() or register() for provider that needs to, respectively, only register services or only bootstrap them.

Available service provider abstract classes

By extending one of these classes, consumers can focus only on the methods that matter.

The case for delayed registration

If the reason behind "normal" VS "early" booted providers has been already mentioned (some providers needs to run early, but some other will not be available early) that's not the case for the "delayed registration" that providers can support.

To explain why this is a thing, let's do an example.

Let's assume a Acme Advanced Logger plugin ships a service provider that registers an Acme\Logger service.

Then, let's assume a separate plugin Acme Authentication ships a service provider that registers several other services that require Acme\Logger service.

The Acme Authentication service provider will need to make sure that the Acme\Logger service is available. One common strategy is to check the container for its availability, and in case of missing (e.g. Acme Advanced Logger plugin is deactivated), Acme Authentication registers an alternative logger that could replace the missing service.

For that check for availability to be effective, it must be done after Acme Advanced Logger service provider has been registered. By supporting delayed registration, Acme Authentication service provider will surely be registered after Acme Advanced Logger is eventually registered (assuming that is not delayed as well) and so on its register method can reliably check if Acme\Logger service is already available or not.

Service providers ID

ServiceProvider interface id() method returns an identifier used in several places.

For example, as shown in the "Package-dependant registration" section above, it is passed as argument to the App::ACTION_REGISTERED_PROVIDER to allow packages to depend on other packages.

The service provider ID can also be passed to the Container::hasProvider() method to know if the given provider has been registered.

All the abstract service provider classes shipped with the package use a trait which, in order:

So by extending one of the abstract classes and doing nothing else there's already an ID defined, which is the class name.

In case this is not fine for some reason, e.g. the same service provider class is used for several providers, it is possible to define the property, or just override the id() method.

Note: Provider IDs must be unique. Trying to add a provider with an ID that was already used will just skip the addition, doing nothing else.

Composing the container

ServiceProvider::register() is where providers add services to the Container, so that they will be available to be "consumed" in the ServiceProvider::boot() method.

ServiceProvider::register() signature is the following:

Receiving an instance of the Container service providers can add things to it in two ways:

Simple service provider example

The container shipped with the package is a PSR-11 container with basic features for adding services that use Pimple behind the scenes.

Besides the two PSR-11 methods, the container has the methods:

Service provider example using any PSR-11 container

In the following example I will use PHP-DI, but any PSR-11-compatible container will do.

Please refer to PHP-DI documentation to better understand the code, but again, any PSR-11 compatible Container can be "pushed" to the library Container.

Website-level providers

App::new() returns an instance of the App so that it is possible to add providers on the spot, without having to hook App::ACTION_ADD_PROVIDERS.

This allow to immediately add service providers shipped at website level.

Providers Package

Often times, when using this package, there's need of creating a "package" that is no more than a "collection" of providers. Not being a plugin or MU plugin, such package will need to be "loaded" manually, because WordPress will not load it, and using autoload for the purpose is not really doable, because using a "file" autoload strategy, the file would be loaded too early, before WP environment is loaded.

The suggested way to deal with this issue is to "load" the package from the same MU plugin that bootstrap the application. To ease this workflow, the package provides a ServiceProviders class, which resemble a collection of providers.

For example, let's assume we are creating a package to provide an authorization system to our application.

The reason why we will create a "library" and not a plugin is that there should be no way to "deactivate" it, being a core feature of the website, and also other plugins and libraries will require it as a dependency.

What we would do in the package is to create a package class, that will implement Inpsyde\App\Provider\Package: an interface with a single method: Package::providers().

With such class in place (and autoloadable), in the MU plugin that bootstrap the application we could do:

Advanced topics

Custom last boot hook

In several places in this README has been said that the last time App::boot() is called is init.

But reality is that is just the default, and even if this is fine in many cases, it is actually possible to use any hook that runs after plugins_loaded for the last "cycle", just keep in mind that:

In any case, the way to customize the "last step" hook is to call App::runLastBootAt() method:

Please note that App::runLastBootAt() must be called before App::boot() is called for first time, or an exception will be thrown.

Building custom container upfront

Sometimes might be desirable to use a pre-built container to be used for the App. This for example allows for easier usage of a different SiteConfig instance (of which EnvConfig is an implementation) or adding an arbitrary PSR-11 container before the container is passed to Service Providers.

This is possible by passing a creating an instance of App\Container, adding one (or more) PSR-11 container s to it (via the Container::addContainer method), then finally passing it to App\App::new. For example:

Resolve objects outside providers

App class has a static App::make() method that can be used to access objects from container outside any provider.

This can be used in plugins that just want to "quickly" access a service in the Container without writing a provider.

Because the method is static, it needs to refer to a booted instance of App. The one that will be used is the first App that is instantiated during a request.

Considering that the great majority of times there will be a single application, that is fine and convenient, because allows to resolve services in the container having no access to the container nor to the App instance.

If App::make() is called before any App has been created at all, an exception will be thrown.

In the case, for any reason, more instances of App are created, to resolve a service in a specific App instance it is necessary to have access to it and call resolve() method on it.

Assuming the code in the previous section, where we defined the app() function, we could do something like this to resolve a service:

Debug info

The App class collects information on the added providers and their status when WP_DEBUG is true.

App::debugInfo(), when debug is on, will return an array that could be something like this:

When debug is off, App::debugInfo() returns null.

To force enabling debug even if WP_DEBUG is false, it is possible to call App::enableDebug().

It is also possible to force debug to be disabled, even if WP_DEBUG is true, via App::disableDebug().

Installation

The best way to use this package is through Composer:

License

This repository is a free software, and is released under the terms of the GNU General Public License version 2 or (at your option) any later version. See LICENSE for complete license.

Contributing

All feedback / bug reports / pull requests are welcome.

Before sending a PR make sure that composer run qa will output no errors.

It will run, in turn:


All versions of wp-app-container with dependencies

PHP Build Version
Package Version
Requires php Version >=7.2.5 < 9
ext-json Version *
inpsyde/modularity Version ^1.4.0
inpsyde/wp-context Version ^1.3.1
psr/container Version ^2
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package inpsyde/wp-app-container contains the following files

Loading the files please wait ....