CiviCRM Community Forums (archive)

*

News:

Have a question about CiviCRM?
Get it answered quickly at the new
CiviCRM Stack Exchange Q+A site

This forum was archived on 25 November 2017. Learn more.
How to get involved.
What to do if you think you've found a bug.



  • CiviCRM Community Forums (archive) »
  • Old sections (read-only, deprecated) »
  • Developer Discussion »
  • 5.0 Saloon »
  • Controllers with OOP Requests & Responses
Pages: [1]

Author Topic: Controllers with OOP Requests & Responses  (Read 1074 times)

totten

  • Administrator
  • Ask me questions
  • *****
  • Posts: 695
  • Karma: 64
Controllers with OOP Requests & Responses
March 12, 2014, 02:14:29 am
In vanilla, non-MVC PHP code, PHP represents inputs as global variables ($_GET/$_POST) and handles outputs with "echo", "print", "header", etc. (You might describe this as a global output mechanism --> there's a single active output stream at any given time, and "echo" always writes to it.) This approach -- treating inputs and outputs as global, special-purpose creatures -- is nice for the novice learning to write his first web app, but relying on globals is problematic for larger applications.

Many, many people have written on this point (eg http://c2.com/cgi/wiki?GlobalVariablesAreBad and http://programmers.stackexchange.com/questions/148108/why-is-global-state-so-evil as just two). I don't want to rehash the matter but can discuss examples from Civi 4.x if you like.

The good news is that Civi's architecture is already relatively good about handling output -- you won't find many "echo" or "header" statements mixed in with PHP controller logic, and this has been a functional necessity: Civi defers to the CMS to handle final outputting (so that the CMS can add its own decorations/adornments to the page), and global "echo" calls would produce undefined behavior across the various CMS's. This is "relatively good" because it applies to typical HTML web pages, but it's not "great" -- for web-services that require full control of the output, Civi controllers get an ugly dose of "echo" and "exit" sprinkled around. Civi 4.5 improves this a little (but not enough IMHO).

The bad news is that we still rely on globals for *inputs*. There's a little bit of window-dressing which obfuscates the matter, e,g:

Code: [Select]
$mailingID = CRM_Utils_Request::retrieve('mid', 'Integer', $this, FALSE, NULL);

but CRM_Utils_Request::retrieve() relies upon the global $_GET/$_POST/$_REQUEST. Preparing a test-case with example inputs (query-parameters, headers, cookies, and POSTed content) is not intuitive.

Fortunately, Symfony 2 has a re-usable component ("HTTP Foundation", http://symfony.com/doc/current/components/http_foundation/introduction.html ) which allows one to represent inputs & outputs without any global variables. As a proof-of-concept, this commit adds support for HTTP Foundation to Civi's page-controller:

https://github.com/totten/civicrm-core/commit/0fc3cacbd2714be493afda6a33eda93846deaca2

A few key things about it:
  • OUTPUTS: In 4.x, a page-controller is expected to return an HTML string with its response. With the patch, one may return an HTML string *or* a response object (which includes the full-headers and output document required in web-services).
  • INPUTS: For page-controllers backed by static functions (eg "CRM_Contact_Page_AJAX::getContactList"), a "Request" object is passed as the first parameter.
  • INPUTS: For page-controllers derived from CRM_Core_Page or CRM_Core_Controller, the "Request" object is injected as an attribute of the controller ($this->request).
  • GENERAL: Classes may be based on PHP 5.2 naming conventions (eg "CRM_Contact_Page_AJAX") or PHP 5.3 namespaces (eg "\Civi\Contact\Page\AJAX")

So, for example, one can write a basic web-service as:

Code: [Select]
<?php
namespace Civi\Contact\Page;
use 
Symfony\Component\HttpFoundation\Request;
use 
Symfony\Component\HttpFoundation\Response;

class 
AJAXHelpers {
  static function 
getContactList(Request $request) {
    
$contacts = ...;
    if (
preg_match(':application/xml:', $request->headers->get('Accept')) {
      return new 
Response(CRM_Utils_Array::xml($contacts), 200, array(
        
'Content-type' => 'application/xml',
      ));
    } else {
      return new 
Response(json_encode($contacts), 200, array(
        
'Content-type' => 'application/json',
      ));
    }
  }
}

To write a unit-test, one can simply call the controller function and pass in an example Request object:

Code: [Select]
<?php
function testGetContactList_DefaultOutput() {
  
$request = new Request();
  
$response = \Civi\Contact\Page\AJAXHelpers::getContactList($request);
  
$this->assertEquals('application/json', $response->headers->get('Content-type');
  
$this->assertTrue(isWellFormedJSON($response->getContent());
}
function 
testGetContactList_XMLOutput() {
  
$request = new Request();
  
$request->headers->set('Accept', 'application/xml');
  
$response = \Civi\Contact\Page\AJAXHelpers::getContactList($request);
  
$this->assertEquals('application/xml', $response->headers->get('Content-type');
  
$this->assertTrue(isWellFormedXML($response->getContent());
}

JoeMurray

  • Administrator
  • Ask me questions
  • *****
  • Posts: 578
  • Karma: 24
    • JMA Consulting
  • CiviCRM version: 4.4 and 4.5 (as of Nov 2014)
  • CMS version: Drupal, WordPress, Joomla
  • MySQL version: MySQL 5.5, 5.6, MariaDB 10.0 (as of Nov 2014)
Re: Controllers with OOP Requests & Responses
May 18, 2014, 07:26:08 am
Without feedback here, I'm just wondering if you could confirm that this great approach is moving ahead from proof of concept into a future release (5.0?) branch?
Co-author of Using CiviCRM https://www.packtpub.com/using-civicrm/book

totten

  • Administrator
  • Ask me questions
  • *****
  • Posts: 695
  • Karma: 64
Re: Controllers with OOP Requests & Responses
May 19, 2014, 11:15:27 am
Support for it is merged in the "doctrine" (5.0) branch. Can't promise that it will stay the same in the final release (because a lot could change before 5.0 is released), but it should stay as-is for the foreseeable future. If it did change, then I think there'd be more discussion here.

Pages: [1]
  • CiviCRM Community Forums (archive) »
  • Old sections (read-only, deprecated) »
  • Developer Discussion »
  • 5.0 Saloon »
  • Controllers with OOP Requests & Responses

This forum was archived on 2017-11-26.