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

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:

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:

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:

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:

<?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')
            );
        });
    }

}

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:

return new AuthorController(
  $c->query('AppName'),
  $c->query('Request'),
  $c->query('AuthorService')
);
$container->registerService('AuthorService', function($c){
  return new AuthorService(
    $c->query('AuthorMapper')
  );
});
$container->registerService('AuthorMappers', function($c){
  return new AuthorService(
    $c->query('ServerContainer')->getDb()
  );
});

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:

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:

array('key' => 'something')

//can be accessed through:

$_['key']
<?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);
    }

}