This page describes broadly how PHP is used in our project:

For more specific explanations of the types of classes we use, make sure to check PHP Structure!

Main entry point

The main entry point for the web is /index.php. That means that /index.php gets called whenever a gets sent to the website.

Another entry point is xhr.php, which is used for routes starting with These are used for our legacy API (see Xhr). The same applies for xhrapp.php.

These three entry points are now just standard Symfony entry points. The relevant (custom) code has been moved to src/Entrypoint, where three respective controllers do the actual work. For the IndexController, a large part of the code has been moved to src/EventSubscriber/RenderControllerSetupSubscriber.php.

The fourth entry point is restApi.php, which will be used whenever a URL starting with is requested. This is the route to our modern API following REST principles (see REST-API). It is also a standard Symfony entry point.

Dependencies and Dependency Injection


(Almost) no code comes without dependencies on other PHP classes.

In order to use a class defined in another file, it needs to get imported at the beginning of your file using use, following a namespace.

The namespace, starting with Foodsharing gets interpreted by the composer autoloader. This is configured in /composer.json under the key "autoload", where Foodsharing gets mapped to /src. Other namespaces than Foodsharing link to external libraries, which have their source code somewhere inside the /vendor directory.

The composer autoloader loads files corresponding to conventions specified by the PSR-4 standard. That means that every subnamespace following the Foodsharing namespace gets interpreted as subdirectory under /src.

use is not necessary for using classes in the same namespace (roughly: same directory).

Loading a depencency via require is just used in files like index.php, which are not in the /src directory and therefore can't use autoloading. require basically executes the PHP file unter the given file name, so you can use the class declaration defined in that file. Don't use require if you can use autoloading.


A service class is a class whose main purpose is not representing an object structure, but providing functionality. Service classes are used to structure operations using object oriented design patterns.

What exactly a service is and what not, is not well defined (see Blog Post Services in Domain-Driven Design), but there are some characteristics that are typical for services

  • only one instance is created per requests and then shared by all classes
  • services depend on other services and the non-service objects (like DTOs) they operate on
  • non-service objects don't depend on services

As we don't have any entity classes, except for the DTO classes, nearly all of our classes are services.

Because service classes only need to be instantiated once, we don't use the new statement to create instances. Instead, we use the Dependency Injection pattern. This enables us to share service instances throughout the application.

Automatic service injection

Responsible for creating and injecting the instances is the Symfony Dependency Injection component.

If we want Symfony to inject a dependency into our service, all we need to do is mentioning the class in our constructor (__construct), e.g.

class ActivityXhr extends Control {
  private $mailboxGateway; /* attributes (member variables) */
  public function __construct(ActivityModel $model, MailboxGateway $mailboxGateway)
    { /* using arguments for setting attributes */ }
... }

Dependency injection (Symfony) then makes sure that every service we request is created and injected when our service is instanciated.

This works because we only need at most one object of every service class. This is because, as already mentioned, service classes are not about representing objects using instances, but about the functionality they provide.

Symfony config is in /config/services.yml. This configuration makes sure that services get created automatically. See the Symfony docs for further reference.


Here we collect the parts of the PHP language that we use in this project. As I write this I already got some experiences in other languages and therefore only include short explanations of surprising syntax.

  • Variables: all occurences of variables are starting with a Dollar ($) symbol:
$out = '<ul class="linklist baskets">';
  • = is an assignment
  • 'string' and "string" are strings. Variables inside double-quoted string literals get replaced by their values.
  • . is the concatenation operator for strings (in other languages +)
  • .= adds the string to the right to the variable on the left
  • $var->member refers to a member variable or method of an object (in other languages .)
  • ClassName::member refers to a (static) member variable or function of a class
  • ... is used for packing and unpacking arrays in argument lists (packing in function definition argument list, unpacking in function use argument list). Longer explanation on stackoverflow


  • Do not use global variables (those indicated by the keyword global, can be found in legacy code)
  • Indentation: we use one tabstop per indentation level
  • Variables in strings: we do not use variables in strings but concatenate:
$list = 'bla'
$list .= $list . 'some text' . $someVariable . 'end text'
  • When (re)writing php code add types in arguments and return types of functions. If you work with existing code it is not always trivial and is therefore not enforced. In general:
    • as a parameter, something should be either the type (e.g. string, int) OR null if okay to not be set. ?string $blub = null works as a parameter definition to allow that.
    • as a return type, something should always ever be one type, if possible. Throw an exception otherwise. Especially an empty array [] is fine to say that there is no data when otherwise data would have been returned as an array.