forked from GNUsocial/gnu-social
		
	
		
			
	
	
		
			89 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
		
		
			
		
	
	
			89 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
|   | ### Entrypoint
 | ||
|  | 
 | ||
|  | GNU social's entrypoint is the `public/index.php` script, which gets | ||
|  | called by the webserver for all requests. This is handled by the | ||
|  | webserver itself, which translates a `GET /foo` to `GET | ||
|  | /index.php?p=foo`. This feature is called 'fancy URLs', as it was in V2. | ||
|  | 
 | ||
|  | The `index` script handles all the initialization of the Symfony | ||
|  | framework and social itself. It reads configuration from `.env` or any | ||
|  | `.env.*` file at the project root. The `index` script creates a | ||
|  | `Kernel` object, which is defined on the `src/Kernel.php` file. This | ||
|  | is the part where the code we control starts; the `Kernel` constructor | ||
|  | creates the needed constants, sets the timezone to UTC and the string | ||
|  | encoding to UTF8. The other functions in this class get called by the | ||
|  | Symfony framework at the appropriate times. We will come back to this | ||
|  | file. | ||
|  | 
 | ||
|  | ### Registering services
 | ||
|  | 
 | ||
|  | Next, the `src/Util/GNUsocial.php` class is instantiated by the | ||
|  | Symfony framework, on the `'onKernelRequest'` or `'onCommand'` events. The | ||
|  | former event, as described in the docs: | ||
|  | 
 | ||
|  | > This event is dispatched very early in Symfony, before the
 | ||
|  | > controller is determined. It's useful to add information to the
 | ||
|  | > Request or return a Response early to stop the handling of the
 | ||
|  | > request.
 | ||
|  | 
 | ||
|  | The latter, is launched on the `bin/console` script is used. | ||
|  | 
 | ||
|  | In both cases, these events call the `register` function, which | ||
|  | creates static references for the logging, event and translation | ||
|  | services. This is done so these services can be used via static | ||
|  | function calls, which is much less verbose and more accessible than | ||
|  | the way the framework recommends. This function also loads all the | ||
|  | Modules and Plugins, which like in V2, are components that aren't | ||
|  | directly connected to the core code, being used to implement internal | ||
|  | and optional functionality respectively, by handling events launched | ||
|  | by various parts of the code. | ||
|  | 
 | ||
|  | ### Database definitions
 | ||
|  | 
 | ||
|  | Going back to the `Kernel`, the `build` function gets called by the | ||
|  | Symfony framework and allows us to register a 'Compiler Pass'. | ||
|  | Specifically, we register the `SchemaDefPass` from the | ||
|  | `src/DependencyInjection/Compiler/SchemaDefPass.php` file, which adds | ||
|  | a new 'metadata driver' to Doctrine. The metadata driver is | ||
|  | responsible for loading database definitions. We keep the same method | ||
|  | as in V2, which was that each 'Entity' has a `schemaDef` static | ||
|  | function which returns an array describing the database. | ||
|  | 
 | ||
|  | This definition is handled by the `SchemaDefDriver` class from | ||
|  | `src/Util/SchemaDefDriver.php` file, which extends `StaticPHPDriver` | ||
|  | and replaces the methods `loadMetadata` with `schemaDef`. The function | ||
|  | `loadMetadataForClass` function is called by the Symfony framework for | ||
|  | each file in `src/Entity/`. It allows us to call the `schemaDef` | ||
|  | function and translate the array definition to Doctrine's internal | ||
|  | representation. | ||
|  | 
 | ||
|  | ### Routing
 | ||
|  | 
 | ||
|  | Next, we'll look at the `RouteLoader`, defined in | ||
|  | `src/Util/RoutLoader.php`, which loads all the files from | ||
|  | `src/Routes/*.php` and calls the static `load` method, which defines | ||
|  | routes with an interface similar to V2's `connect`, except it requires | ||
|  | an extra identifier as the first argument. This identifier is used, | ||
|  | for instance, to generate URLs for each route. Each route connects an | ||
|  | URL path to a Controller, with the possibility of taking arguments, | ||
|  | which are passed to the `__invoke` method of the respective | ||
|  | controller. The controllers are defined in `src/Controller/` and are | ||
|  | responsible for handling a request and return a Symfony `Response` | ||
|  | object (subject to change, in order to abstract HTML vs JSON output). | ||
|  | 
 | ||
|  | ### End to end
 | ||
|  | 
 | ||
|  | The next steps are handled by the Symfony framework which creates a | ||
|  | `Request` object from the HTTP request, and then a corresponding | ||
|  | `Response` is created by calling the `Kernel::handle` method, which | ||
|  | matches the appropriate route and thus calls it's controller. | ||
|  | 
 | ||
|  | ### Performance
 | ||
|  | 
 | ||
|  | All this happens on each request, which seems like a lot to handle, | ||
|  | and would be too slow. Fortunately, Symfony has a 'compiler' which | ||
|  | caches and optimizes the code paths. In production mode, this can be | ||
|  | done through a command, while in development mode, it's handled on | ||
|  | each request if the file changed, which has a performance impact, but | ||
|  | obviously makes development easier. |