DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • Inheritance in PHP: A Simple Guide With Examples
  • The Impact of Asynchronous Work on Engineering Innovation
  • The Blue Elephant in the Room: Why PHP Should Not Be Ignored Now or Ever
  • Oracle: Migrate PDB to Another Database

Trending

  • Customer 360: Fraud Detection in Fintech With PySpark and ML
  • Understanding IEEE 802.11(Wi-Fi) Encryption and Authentication: Write Your Own Custom Packet Sniffer
  • AI-Driven Root Cause Analysis in SRE: Enhancing Incident Resolution
  • Mastering Advanced Aggregations in Spark SQL
  1. DZone
  2. Coding
  3. Languages
  4. Practical PHP Patterns: Remote Facade

Practical PHP Patterns: Remote Facade

By 
Giorgio Sironi user avatar
Giorgio Sironi
·
Aug. 10, 10 · Interview
Likes (0)
Comment
Save
Tweet
Share
3.6K Views

Join the DZone community and get the full member experience.

Join For Free

The Remote Facade pattern is a specialization of the Facade one which abstracts the interaction with a remote service (which is executed out of the PHP process, or out of the current machine).

PHP applications usually aren't fond of transparent remote invocation, since the remote application is often not usually written in PHP (think of Twitter or Google web services). A Remote Facade provides a unique point of access to an external service (usually a web service, since it is so easy to connect to it with PHP code), with a coarse-grained interface that would not leak as a Proxy object would in some chatty use cases.

Implementation

The Remote Faced may create and manage a bunch of domain objects, which you can use to manipulate the returned data and prepare message to send; only when the Remote Facade is called network communication happens.

Many small objects are the key to a resilient and easy to use object model. The Remote Facade hides the complexity of the communication, which involves mapping of objects to a data stream, and is built over this object model.

Other responsibilities can be applied to the Facade, given its key position on a port of the application.

For example security and access control can be applied by making checks in its methods. Also automatic transactional control can be implemented based on the semantics of the model: every method is a transaction, since you can never rely on the the calling order and consistency of client code.

There are also responsibilities that a Remote Facade should not have. The main one is domain logic: it is a more orthogonal approach to model business logic in an external object model and leave only the translation mechanism in the Remote Facade (that way you would be even able to swap different Facades over the same domain model).

Backends

PHP has networking under its skin, so there are multiple ways to interact with remote servers.

A common approach is using curl (it is implemented as an extension, but it is very common in PHP installations).

The PHP extension is based on libcurl, a C library which according to php.net currently supports http, https, ftp, gopher, telnet, dict, file, and ldap protocols. And also HTTPS certificates, HTTP POST, HTTP PUT, FTP uploading, HTTP form based upload, proxies, cookies, and user+password authentication.

Another standard networking mechanism is using sockets and streams (stream_socket_client() and the related famiily of functions). The definition of stream in PHP is that of a resource (which is a PHP variable type) which exhibits streamable behavior, meaning that it can be written to or read from in a linear fashion. From PHP 4.3, streams unify all these resources under a common interface to reuse various functions orthogonally to the wrappers which identify the various types of sources, like the filesystem, HTTP and FTP, but also compression streams and Phar archives. The stream extension, which includes the set of functions that handle streams (comprehending the remote ones, via sockets), is even more common that the curl one.

Advantages

There are many advantages to insulate your application from external resources using a Remote Facade.

For starters, you will keep network-related code in one place, so that it would be easy to change, cohesive and with no duplication in various parts of the codebase.

You also easily obtain insulation during testing: you can run integration tests on the Facade itself, and unit-level ones on the few classes that use it. Usually domain classes are managed by the Facade for transmission but do not depend on it, so they don't need a fake implementation to be tested, like Entities do not need fake repositories for testing in general.

Disadvantages

It's hard to find a single disadvantage in the usage of this foundational pattern.

The only problem I can think of is that remoting is not clearly expressed in the Api since all the networking code is hidden. Using the Facade carelessly could lead to very bad performance even with the precautions of a coarse interface which depends on domain objects, but it should be clear that if you're calling an external web site the speed of your scripts could be slower than when acting on in-memory objects...

Example

The sample implementation of a Remote Facade I'll present here is the Zend_Service_SlideShare component of Zend Framework: it hides interactions with slideshare.com under an object-oriented interface.

It also has a domain model which contains business logic, via its Zend_Service_SlideShare_SlideShow class. This is one of the many Zend_Service classes which are currently being ported to Zend Framework 2.

Here is the relevant code (the main Api methods plus a protected Factory Method and the constructor), taken from Zend Framework 1.x trunk.

<?php

/**
* The Zend_Service_SlideShare component is used to interface with the
* slideshare.net web server to retrieve slide shows hosted on the web site for
* display or other processing.
*
* @category Zend
* @package Zend_Service
* @subpackage SlideShare
* @throws Zend_Service_SlideShare_Exception
* @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zend_Service_SlideShare
{
/**
* The Constructor
*
* @param string $apikey The API key
* @param string $sharedSecret The shared secret
* @param string $username The username
* @param string $password The password
*/
public function __construct($apikey, $sharedSecret, $username = null, $password = null)
{
$this->setApiKey($apikey)
->setSharedSecret($sharedSecret)
->setUserName($username)
->setPassword($password);

$this->_httpclient = new Zend_Http_Client();
}

...

/**
* Uploads the specified Slide show the the server
*
* @param Zend_Service_SlideShare_SlideShow $ss The slide show object representing the slide show to upload
* @param boolean $make_src_public Determines if the the slide show's source file is public or not upon upload
* @return Zend_Service_SlideShare_SlideShow The passed Slide show object, with the new assigned ID provided
*/
public function uploadSlideShow(Zend_Service_SlideShare_SlideShow $ss, $make_src_public = true)
{

$timestamp = time();

$params = array('api_key' => $this->getApiKey(),
'ts' => $timestamp,
'hash' => sha1($this->getSharedSecret().$timestamp),
'username' => $this->getUserName(),
'password' => $this->getPassword(),
'slideshow_title' => $ss->getTitle());

$description = $ss->getDescription();
$tags = $ss->getTags();

$filename = $ss->getFilename();

if(!file_exists($filename) || !is_readable($filename)) {
require_once 'Zend/Service/SlideShare/Exception.php';
throw new Zend_Service_SlideShare_Exception("Specified Slideshow for upload not found or unreadable");
}

if(!empty($description)) {
$params['slideshow_description'] = $description;
} else {
$params['slideshow_description'] = "";
}

if(!empty($tags)) {
$tmp = array();
foreach($tags as $tag) {
$tmp[] = "\"$tag\"";
}
$params['slideshow_tags'] = implode(' ', $tmp);
} else {
$params['slideshow_tags'] = "";
}


$client = $this->getHttpClient();
$client->setUri(self::SERVICE_UPLOAD_URI);
$client->setParameterPost($params);
$client->setFileUpload($filename, "slideshow_srcfile");

require_once 'Zend/Http/Client/Exception.php';
try {
$response = $client->request('POST');
} catch(Zend_Http_Client_Exception $e) {
require_once 'Zend/Service/SlideShare/Exception.php';
throw new Zend_Service_SlideShare_Exception("Service Request Failed: {$e->getMessage()}", 0, $e);
}

$sxe = simplexml_load_string($response->getBody());

if($sxe->getName() == "SlideShareServiceError") {
$message = (string)$sxe->Message[0];
list($code, $error_str) = explode(':', $message);
require_once 'Zend/Service/SlideShare/Exception.php';
throw new Zend_Service_SlideShare_Exception(trim($error_str), $code);
}

if(!$sxe->getName() == "SlideShowUploaded") {
require_once 'Zend/Service/SlideShare/Exception.php';
throw new Zend_Service_SlideShare_Exception("Unknown XML Respons Received");
}

$ss->setId((int)(string)$sxe->SlideShowID);

return $ss;
}

/**
* Retrieves a slide show's information based on slide show ID
*
* @param int $ss_id The slide show ID
* @return Zend_Service_SlideShare_SlideShow the Slideshow object
*/
public function getSlideShow($ss_id)
{
$timestamp = time();

$params = array('api_key' => $this->getApiKey(),
'ts' => $timestamp,
'hash' => sha1($this->getSharedSecret().$timestamp),
'slideshow_id' => $ss_id);

$cache = $this->getCacheObject();

$cache_key = md5("__zendslideshare_cache_$ss_id");

if(!$retval = $cache->load($cache_key)) {
$client = $this->getHttpClient();

$client->setUri(self::SERVICE_GET_SHOW_URI);
$client->setParameterPost($params);

require_once 'Zend/Http/Client/Exception.php';
try {
$response = $client->request('POST');
} catch(Zend_Http_Client_Exception $e) {
require_once 'Zend/Service/SlideShare/Exception.php';
throw new Zend_Service_SlideShare_Exception("Service Request Failed: {$e->getMessage()}", 0, $e);
}

$sxe = simplexml_load_string($response->getBody());

if($sxe->getName() == "SlideShareServiceError") {
$message = (string)$sxe->Message[0];
list($code, $error_str) = explode(':', $message);
require_once 'Zend/Service/SlideShare/Exception.php';
throw new Zend_Service_SlideShare_Exception(trim($error_str), $code);
}

if(!$sxe->getName() == 'Slideshows') {
require_once 'Zend/Service/SlideShare/Exception.php';
throw new Zend_Service_SlideShare_Exception('Unknown XML Repsonse Received');
}

$retval = $this->_slideShowNodeToObject(clone $sxe->Slideshow[0]);

$cache->save($retval, $cache_key);
}

return $retval;
}

/**
* Converts a SimpleXMLElement object representing a response from the service
* into a Zend_Service_SlideShare_SlideShow object
*
* @param SimpleXMLElement $node The input XML from the slideshare.net service
* @return Zend_Service_SlideShare_SlideShow The resulting object
*/
protected function _slideShowNodeToObject(SimpleXMLElement $node)
{

if($node->getName() == 'Slideshow') {

$ss = new Zend_Service_SlideShare_SlideShow();

$ss->setId((string)$node->ID);
$ss->setDescription((string)$node->Description);
$ss->setEmbedCode((string)$node->EmbedCode);
$ss->setNumViews((string)$node->Views);
$ss->setPermaLink((string)$node->Permalink);
$ss->setStatus((string)$node->Status);
$ss->setStatusDescription((string)$node->StatusDescription);

foreach(explode(",", (string)$node->Tags) as $tag) {

if(!in_array($tag, $ss->getTags())) {
$ss->addTag($tag);
}
}

$ss->setThumbnailUrl((string)$node->Thumbnail);
$ss->setTitle((string)$node->Title);
$ss->setLocation((string)$node->Location);
$ss->setTranscript((string)$node->Transcript);

return $ss;

}

require_once 'Zend/Service/SlideShare/Exception.php';
throw new Zend_Service_SlideShare_Exception("Was not provided the expected XML Node for processing");
}
}
PHP remote

Opinions expressed by DZone contributors are their own.

Related

  • Inheritance in PHP: A Simple Guide With Examples
  • The Impact of Asynchronous Work on Engineering Innovation
  • The Blue Elephant in the Room: Why PHP Should Not Be Ignored Now or Ever
  • Oracle: Migrate PDB to Another Database

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!