own cloud internal
own cloud internals
Classloader
The classloader is provided by ownCloud and loads all your classes automatically. The only thing left to include by yourself are 3rdparty libraries. Those should be loaded in lib/AppInfo/Application.php.
New in version 9.1.
PSR-4 Autoloading
Since ownCloud 9.1 there is a PSR-4 autoloader in place. The namespace \OCA\MyApp is mapped to /apps/myapp/lib/. Afterwards normal PSR-4 rules apply, so a folder is a namespace section in the same casing and the class name matches the file name.
If your appid can not be turned into the namespace by uppercasing the first character, you can specify it in your appinfo/info.xml by providing a field called namespace. The required namespace is the one which comes after the top level namespace OCA\, e.g.: for OCA\MyBeautifulApp\Some\OtherClass the needed namespace would be MyBeautifulApp and would be added to the info.xml in the following way:
<?xml version="1.0"?>
<info>
<namespace>MyBeautifulApp</namespace>
<!-- other options here ... -->
</info>
3rd libs
- guzzle(HTTP client)
- symfony(Web Application framework and omponents)
- sabre(WebDAV server)
- doctrine(ORM)
- pimple(Dependency Injection Container)
Request lifecycle
Front controller
In the beginning, all requests are sent to ownCloud’s index.php which in turn executes lib/base.php. This file inspects the HTTP headers and abstracts away differences between different Web servers and initializes the basic classes. Afterwards the basic apps are being loaded in the following order:
- Authentication backends
- Filesystem
- Logging
The type of the app is determined by inspecting the app’s configuration file (appinfo/info.xml). Loading apps means that the main file (appinfo/app.php) of each installed app is being loaded and executed. That means that if you want to execute code before a specific app is being run, you can place code in your app’s main file.
Afterwards the following steps are performed:
- Try to authenticate the user
- Load and execute all the remaining apps’ Navigation and Pre-App configuration files
- Load and run all the routes in the apps’ appinfo/routes.php
- Execute the router
Router
The router parses the app’s routing files (appinfo/routes.php), inspects the request’s method and url, queries the controller from the Container and then passes control to the dispatcher. The dispatcher is responsible for running the hooks (called Middleware) before and after the controller, executing the controller method and rendering the output.
Middleware
A Middleware is a convenient way to execute common tasks such as custom authentication before or after a controller method is being run. You can execute code at the following locations:
- before the call of the controller method
- after the call of the controller method
- after an exception is thrown (also if it is thrown from a middleware, e.g. if an authentication fails)
- before the output is rendered
Container
The Container is the place where you define all of your classes and in particular all of your controllers. The container is responsible for assembling all of your objects (instantiating your classes) that should only have one single instance without relying on globals or singletons. If you want to know more about why you should use it and what the benefits are, read up on the topic in Container.
Controller
The controller contains the code that you actually want to run after a request has come in. Think of it like a callback that is executed if everything before went fine.
The controller returns a response which is then run through the middleware again (afterController and beforeOutput hooks are being run), HTTP headers are being set and the response’s render method is being called and printed.
Routing
Routes map an URL and a method to a controller method. Routes are defined inside appinfo/routes.php by passing a configuration array to the registerRoutes method. An example route would look like this:
<?php
namespace OCA\MyApp\AppInfo;
$application = new Application();
$application->registerRoutes($this, array(
'routes' => array(
array('name' => 'page#index', 'url' => '/', 'verb' => 'GET'),
)
));
The route array contains the following parts:
url: The url that is matched after /index.php/apps/myapp
name: The controller and the method to call; page#index is being mapped to PageController->index(), articles_api#drop_latest would be mapped to ArticlesApiController->dropLatest(). The controller that matches the page#index name would have to be registered in the following way inside appinfo/application.php:
<?php
namespace OCA\MyApp\AppInfo;
use \OCP\AppFramework\App;
use \OCA\MyApp\Controller\PageController;
class Application extends App {
public function __construct(array $urlParams=array()){
parent::__construct('myapp', $urlParams);
$container = $this->getContainer();
/**
* Controllers
*/
$container->registerService('PageController', function($c) {
return new PageController(
$c->query('AppName'),
$c->query('Request')
);
});
}
}
method (Optional, defaults to GET): The HTTP method that should be matched, (e.g. GET, POST, PUT, DELETE, HEAD, OPTIONS, PATCH)
requirements (Optional): lets you match and extract URLs that have slashes in them (see Matching suburls)
postfix (Optional): lets you define a route id postfix. Since each route name will be transformed to a route id (page#method -> myapp.page.method) and the route id can only exist once you can use the postfix option to alter the route id creation by adding a string to the route id e.g.: ‘name’ => ‘page#method’, ‘postfix’ => ‘test’ will yield the route id myapp.page.methodtest. This makes it possible to add more than one route/url for a controller method
defaults (Optional): If this setting is given, a default value will be assumed for each url parameter which is not present. The default values are passed in as a key => value par array
Container
The App Framework assembles the application by using a container based on the software pattern Dependency Injection. This makes the code easier to test and thus easier to maintain.
Using a container
Passing dependencies into the constructor rather than instantiating them in the constructor has the following drawback: Every line in the source code where new AuthorMapper is being used has to be changed, once a new constructor argument is being added to it.
The solution for this particular problem is to limit the new AuthorMapper to one file, the container. The container contains all the factories for creating these objects and is configured in lib/AppInfo/Application.php.
To add the app’s classes simply open the lib/AppInfo/Application.php and use the registerService method on the container object:
<?php
namespace OCA\MyApp\AppInfo;
use \OCP\AppFramework\App;
use \OCA\MyApp\Controller\AuthorController;
use \OCA\MyApp\Service\AuthorService;
use \OCA\MyApp\Db\AuthorMapper;
class Application extends App {
/**
* Define your dependencies in here
*/
public function __construct(array $urlParams=array()){
parent::__construct('myapp', $urlParams);
$container = $this->getContainer();
/**
* Controllers
*/
$container->registerService('AuthorController', function($c){
return new AuthorController(
$c->query('AppName'),
$c->query('Request'),
$c->query('AuthorService')
);
});
/**
* Services
*/
$container->registerService('AuthorService', function($c){
return new AuthorService(
$c->query('AuthorMapper')
);
});
/**
* Mappers
*/
$container->registerService('AuthorMapper', function($c){
return new AuthorMapper(
$c->query('ServerContainer')->getDb()
);
});
}
}
How the container works
The container works in the following way:
A request comes in and is matched against a route (for the AuthorController in this case)
The matched route queries AuthorController service from the container:
return new AuthorController(
$c->query('AppName'),
$c->query('Request'),
$c->query('AuthorService')
);
The AppName is queried and returned from the baseclass
The Request is queried and returned from the server container
AuthorService is queried:
$container->registerService('AuthorService', function($c){
return new AuthorService(
$c->query('AuthorMapper')
);
});
- AuthorMapper is queried:
$container->registerService('AuthorMappers', function($c){
return new AuthorService(
$c->query('ServerContainer')->getDb()
);
});
The database connection is returned from the server container
Now AuthorMapper has all of its dependencies and the object is returned
AuthorService gets the AuthorMapper and returns the object
AuthorController gets the AuthorService and finally the controller can be
new
ed and the object is returned
Controller
Controllers are used to connect routes with app logic. Think of it as callbacks that are executed once a request has come in. Controllers are defined inside the lib/Controller/ directory.
To create a controller, simply extend the Controller class and create a method that should be executed on a request:
<?php
namespace OCA\MyApp\Controller;
use OCP\AppFramework\Controller;
class AuthorController extends Controller {
public function index() {
}
}
Getting request parameters
Parameters can be passed in many ways:
- Extracted from the URL using curly braces like {key} inside the URL (see Routing)
- Appended to the URL as a GET request (e.g. ?something=true)
- application/x-www-form-urlencoded from a form or jQuery
- application/json from a POST, PATCH or PUT request
All those parameters can easily be accessed by adding them to the controller method:
<?php
namespace OCA\MyApp\Controller;
use OCP\AppFramework\Controller;
class PageController extends Controller {
/**
* @param int $id
*/
public function doSomething($id, $name='john', $job='author') {
// GET ?id=3&job=killer
// $id = 3
// $name = 'john'
// $job = 'killer'
}
}
Response
JSON
<?php
namespace OCA\MyApp\Controller;
use OCP\AppFramework\Controller;
class PageController extends Controller {
public function returnJSON() {
return array('test' => 'hi');
}
}
Templates
A template can be rendered by returning a TemplateResponse. A TemplateResponse takes the following parameters:
appName: tells the template engine in which app the template should be located
templateName: the name of the template inside the template/ folder without the .php extension
parameters: optional array parameters that are available in the template through $_, e.g.:
array('key' => 'something')
//can be accessed through:
$_['key']
- renderAs: defaults to user, tells ownCloud if it should include it in the web interface, or in case blank is passed solely render the template
<?php
namespace OCA\MyApp\Controller;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\TemplateResponse;
class PageController extends Controller {
public function index() {
$templateName = 'main'; // will use templates/main.php
$parameters = array('key' => 'hi');
return new TemplateResponse($this->appName, $templateName, $parameters);
}
}