SlideShare a Scribd company logo
API Design in PHP
          David Sklar
  Software Architect, Ning Inc.
      david@ninginc.com

    Zend Conference 2007
Ning Platform
Ning Platform
Ning

• PHP API provides interface to our platform
  REST APIs
• Live since August 2005 (with 5.0.4)
• Recent upgrade to 5.2.3
• In use in all 108,000+ networks on the
  platform
Ning

• Migration from XMLRPC to REST in 2005/6
• APIs used for content storage, user profile
  management, tagging, search, video
  transcoding, messaging, ...
• PHP (using APIs) runs in a hosted
  environment
API: XN_Content
<?php
$dinner = XN_Content::create('Meal');
$dinner->title = 'Salt Baked Combo';
$dinner->my->protein = 'seafood';
$dinner->my->ingredients = 
               array('shrimp','scallops','squid');
$dinner->save();
?>
PHP ➠ REST
POST /xn/atom/1.0/content
Content-Type: text/xml;charset=UTF-8

<entry xmlns="http://www.w3.org/2005/Atom"
       xmlns:xn="http://www.ning.com/atom/1.0"
       xmlns:my="http://afternoonsnack.ning.com/xn/atom/1.0">
 <xn:type>Meal</xn:type>
 <title type="text">Salt Baked Combo</title>
 <my:protein type='string'>seafood</my:protein>
 <my:ingredients type='string'>
   <xn:value>shrimp</xn:value><xn:value>scallops</xn:value>
   <xn:value>squid</xn:value>
 </my:ingredients>
</entry>
HTTP/1.1 200 OK
                      PHP ➠ REST
<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns="http://www.w3.org/2005/Atom"
      xmlns:xn="http://www.ning.com/atom/1.0">
 <xn:size>1</xn:size>
 <updated>2007-08-28T22:11:47.420Z</updated>
 <entry xmlns:my="http://afternoonsnack.ning.com/xn/atom/1.0">
  <id>http://afternoonsnack.ning.com/502068:Meal:122</id>
  <xn:type>Meal</xn:type>
  <xn:id>502068:Meal:122</xn:id>
  <title type="text">Salt Baked Combo</title>
  <published>2007-08-28T22:11:47.414Z</published>
  <updated>2007-08-28T22:11:47.414Z</updated>
  <link rel="alternate"
        href="http://afternoonsnack.ning.com/xn/detail/502068:Meal:122" />
  <my:protein type="string">seafood</my:protein>
  ...
 </entry>
</feed>
Design Priorities

• Promote predictability, modularity, stability
• Choose human performance over computer
  performance
• Make efficiency easy, make inefficiency hard/
  impossible
At the start...

• Write code before you write the API
• Use cases, Use cases, Use cases
• Names matter (but don’t discuss them
  forever)
Use the API before it
            exists
Sketch out what you want to do....
Use Cases First!


• What does the API need to do?
• (Not “what could it do?”)
Need-driven
        Development

• Adding is easy. Removing is hard.
• You have lots of freedom with arguments
• Accessors provide insulation
Arguments
Long parameter lists are toxic:
<?php

function save($data, $flavor = null, $scope = null,
              $commit = null, $cascade = null,
              $permissions = null) {
    if (is_null($flavor))      { $flavor = 'quick'; }
    if (is_null($scope))       { $scope = 'global'; }
    if (is_null($commit))      { $commit = true; }
    if (is_null($cascade))     { $cascade = false; }
    if (is_null($permissions)) { $permissions = 0755; }
    // ...
}
Bread Stuffing
Bread Stuffing
What does this do?
<?php

save($data, null, null, true, false);

?>
How about this?
<?php

save($data, array('scope' => 'local'));

?>
Ahh, much better:
<?php

function save($data, $paramsOrFlavor = null,
               $scope = null, $commit = null,
              $cascade = null, $permissions = null){
    if (is_array($paramsOrFlavor)) {
        // ...
    }
    else {
        // ...
    }
}
Fun with __get() and __set()
public function __get($name) {
  switch ($name) {
    case self::screenName:
      return $this->_screenName;
    case self::fullName:
      return $this->_fullName;
    case self::uploadEmailAddress:
      $this->_lazyLoad('uploadEmailAddress');
      return $this->_uploadEmailAddress;
    case 'description':
      // ...
}
Static ‘n’ Dynamic Analysis



• find + grep
• tokenizer
• hooks + logging
find + grep
find . -name *.php -exec grep -H '::load('

   • easy
   • fast
   • mostly correct: watch out for dynamic
     variable names, text collision, etc.
tokenizer


• php-specific knowledge, but...
• can be slower
• need to write custom rules and parsing
$tokens = array(T_INCLUDE => 0, T_INCLUDE_ONCE => 0,
                T_REQUIRE => 0, T_REQUIRE_ONCE => 0);

foreach (new PhpFilterIterator(new RecursiveIteratorIterator(
         new RecursiveDirectoryIterator($root))) as $f) {
  $muncher = new Tokenmunch(file_get_contents($f));
  foreach ($muncher as $token) {
    if (array_key_exists($token[0], $tokens)) {
      $tokens[$token[0]]++;
      $startOfLine = $muncher->scanBackFor(T_WHITESPACE,"/n/");
      $startOfBlock = $muncher->scanBackFor(T_OPEN_TAG);
      $previousComment = $muncher->scanBackFor(T_COMMENT,"/n$/");
      $startPosition = max($startOfLine, $startOfBlock, $previousComment) +1;
      $endOfLine = $muncher->scanForwardFor(T_STRING, '/^;$/');
      $slice =  $muncher->sliceAsString($startPosition,
                                       $endOfLine - $startPosition+1);
      print trim($slice) . "n";
    }
  }
}
Zendcon 2007 Api Design
Hooks + Logging


• need to instrument the API beforehand
• watch out for performance overhead
API for the API: XN_Event
class XN_Event {
    /**
     * Fire an event with optional arguments
     *
     * @param string $event
     * @param array $args optional arguments to pass to listeners
     */
    public static function fire($event, $args = null);
                                          
    /**
     * Listen for an event
     *
     * @param string $event
     * @param callback $callback Function to run when the event is fired
     * @param array $args optional arguments to pass to the callback
     * @return string
     */
    public static function listen($event, $callback, $args = null);
}
XN_Event in Use
XN_Content::save() calls:
XN_Event::fire('xn/content/save/before', array($this));
// do the save
XN_Event::fire('xn/content/save/after', array($this));



This has been very useful for cache expiration.
Late Static Binding Workaround
Class name registry for static inheritance:

 W_Cache::putClass('app','XG_App');

 // ... time passes ...

 $className = W_Cache::getClass($role);
 $retval = call_user_func_array(
              array($className, $method),
              $args
           );
Names Matter


• Namespacing / collisions
• Versioning
Namespacing

• At Ning, “XN” means “hands off”
 • class names
 • property names
 • xml namespace prefixes
Versioning

• YourClass and YourClass2...sigh.
• Using include_path and auto_prepend_file
• Version number in REST URLs:
 http://app.ning.com/xn/atom/1.0/content/...
Docblocks:Yay!
/** It’s easy to generate human(-ish)
  * readable documentation from
  * docblocks. (@see PHPDocumentor,
  * @see doxygen)
  *
  * And the documentation is close
  * to the code.
  */
public function debigulator() {
}
Docblocks: Boo!
/** What about examples and tutorials
  * and all of the other thing that
  * are not method or class
  * specific?
  *
  * Is this documentation up to date
  * with the @version of the code?
  */
public function rebigulator() {
}
Too Much Sugar
// "System" Attribute
$content->title = 'Duck with Pea Shoots';

// "Developer" Attribute
$content->my->meat = true;
Non-literal Names..!
$attrs = array('title','my->meat');

foreach ($attrs as $attr) {
    print "$attr is " . $content->$attr;
}




            ☹
Alternatives

OK $content->title    and $content->my_flavor;

OK $content->xn_title and $content->flavor;

NO $content['title']  and $content->flavor;
Testing
      &
Code Coverage
The extent of your
        test suite
          is the
strength of your contract
     with your users.
Tools are secondary.

Discipline is primary.
Tools

• SimpleTest
 •   http://www.lastcraft.com/simple_test.php

• PHPUnit
 •   http://phpunit.de/




                                         ginger monkey - popofatticus@flickr - CC attrib 2.0
                                                         gorilla: tancread@flickr - CC by-nc
Future: Classes As “Types”
✓   $content->my->number = 123;

✓   $content->my->string = 'ronald';

✗   $content->my->date = '2005-03-10T12:24:14Z';

?   $content->my->set('date',
                     '2005-03-10T12:24:14Z',
                     XN_Attribute::DATE);
Future: Classes As “Types”
Newer:
✓   $content->my->date = 
          new DateTime('2005-03-10T12:24:14Z');

Older:
✓   $content->my->date = 
          XN_Date('2005-03-10T12:24:14Z');
class XN_Date {
    public $_stamp;
    
    public function __construct($timeOrDateString) {
        if (ctype_digit($timeOrDateString)) {
            $this->_stamp = $timeOrDateString;
        } else {
            $failure = (version_compare(phpversion(),'5.1.0') >= 0) ? -1 : false;
            $stamp = strtotime($timeOrDateString);
            if ($stamp === $failure) {
                throw new Exception("$timeOrDateString is not a valid time/date");
            } else {
                $this->_stamp = $stamp;
            }
        }
    }
    
    public function __get($property) {
        if ($property == 'epoch') {
            return (integer) $this->_stamp;
        } else if ($property == 'ISO8601') {
            return $this->__toString();
        } else {
            return gmdate($property, $this->_stamp);
        }
    }
    
    public function __toString() {
        return gmstrftime('%Y-%m-%dT%H:%M:%SZ',$this->_stamp);
    }
}
function XND_Date($timeOrDateString) { return new XND_Date($timeOrDateString); }
Resources
•   Joshua Bloch: "How to Design a Good API and
    Why It Matters"

    •   http://lcsd05.cs.tamu.edu/slides/keynote.pdf

•   Zend Framework Documentation

    •   http://framework.zend.com/manual/manual/

•   eZ Components Documentation

    •   http://ez.no/doc/components/overview/

•   These slides: http://www.sklar.com/blog/
Come work at                         !

• Write the code that powers 100,000+ social
  networks
• Write the next version of our PHP API
• Work in Palo Alto (or not)
• http://jobs.ning.com - david@ninginc.com

More Related Content

PDF
Redis for your boss 2.0
PDF
Redis for your boss
PDF
Facebook的缓存系统
PDF
4069180 Caching Performance Lessons From Facebook
PDF
The IoC Hydra - Dutch PHP Conference 2016
PDF
Design Patterns avec PHP 5.3, Symfony et Pimple
PDF
Temporary Cache Assistance (Transients API): WordCamp Phoenix 2014
PDF
Scaling Symfony2 apps with RabbitMQ - Symfony UK Meetup
Redis for your boss 2.0
Redis for your boss
Facebook的缓存系统
4069180 Caching Performance Lessons From Facebook
The IoC Hydra - Dutch PHP Conference 2016
Design Patterns avec PHP 5.3, Symfony et Pimple
Temporary Cache Assistance (Transients API): WordCamp Phoenix 2014
Scaling Symfony2 apps with RabbitMQ - Symfony UK Meetup

What's hot (20)

PDF
PHP 5.3 and Lithium: the most rad php framework
PPT
Spring data iii
PDF
Silex meets SOAP & REST
PDF
Temporary Cache Assistance (Transients API): WordCamp Birmingham 2014
PDF
The History of PHPersistence
PDF
Database Design Patterns
PPT
Corephpcomponentpresentation 1211425966721657-8
PDF
Nubilus Perl
KEY
Perl Web Client
PDF
Forget about index.php and build you applications around HTTP!
PDF
Advanced php testing in action
PDF
Dependency Injection IPC 201
PDF
Advanced symfony Techniques
PDF
Command Bus To Awesome Town
PDF
Things I Believe Now That I'm Old
KEY
Designing Opeation Oriented Web Applications / YAPC::Asia Tokyo 2011
PDF
Introducing Assetic (NYPHP)
PDF
Symfony2 - WebExpo 2010
PDF
The Joy of Smartmatch
PDF
Doctrine MongoDB ODM (PDXPHP)
PHP 5.3 and Lithium: the most rad php framework
Spring data iii
Silex meets SOAP & REST
Temporary Cache Assistance (Transients API): WordCamp Birmingham 2014
The History of PHPersistence
Database Design Patterns
Corephpcomponentpresentation 1211425966721657-8
Nubilus Perl
Perl Web Client
Forget about index.php and build you applications around HTTP!
Advanced php testing in action
Dependency Injection IPC 201
Advanced symfony Techniques
Command Bus To Awesome Town
Things I Believe Now That I'm Old
Designing Opeation Oriented Web Applications / YAPC::Asia Tokyo 2011
Introducing Assetic (NYPHP)
Symfony2 - WebExpo 2010
The Joy of Smartmatch
Doctrine MongoDB ODM (PDXPHP)
Ad

Viewers also liked (8)

PDF
PRESENTACION DE PRUEBA
PPS
洗手間裡的晚宴
PPT
Zzz
PDF
คู่มือการใช้งาน Wmap 2013
DOC
PDF
Succession “Losers”: What Happens to Executives Passed Over for the CEO Job?
PRESENTACION DE PRUEBA
洗手間裡的晚宴
Zzz
คู่มือการใช้งาน Wmap 2013
Succession “Losers”: What Happens to Executives Passed Over for the CEO Job?
Ad

Similar to Zendcon 2007 Api Design (20)

PDF
PDF
PHP And Web Services: Perfect Partners
PDF
Working with web_services
KEY
Can't Miss Features of PHP 5.3 and 5.4
PDF
Workshop quality assurance for php projects - phpbelfast
PDF
Workshop quality assurance for php projects - ZendCon 2013
PDF
Living With Legacy Code
PDF
Making the most of 2.2
PPTX
Php meetup 20130912 reflection
PDF
PHP 5.3 Overview
PPTX
Introducing PHP Latest Updates
KEY
Workshop quality assurance for php projects tek12
PDF
Charla EHU Noviembre 2014 - Desarrollo Web
PDF
Quality Assurance for PHP projects - ZendCon 2012
PDF
Modern php
PDF
Services Drupalcamp Stockholm 2009
PDF
REST easy with API Platform
PPTX
Day02 a pi.
PDF
Advanced PHP Simplified - Sunshine PHP 2018
PDF
SPL Primer
PHP And Web Services: Perfect Partners
Working with web_services
Can't Miss Features of PHP 5.3 and 5.4
Workshop quality assurance for php projects - phpbelfast
Workshop quality assurance for php projects - ZendCon 2013
Living With Legacy Code
Making the most of 2.2
Php meetup 20130912 reflection
PHP 5.3 Overview
Introducing PHP Latest Updates
Workshop quality assurance for php projects tek12
Charla EHU Noviembre 2014 - Desarrollo Web
Quality Assurance for PHP projects - ZendCon 2012
Modern php
Services Drupalcamp Stockholm 2009
REST easy with API Platform
Day02 a pi.
Advanced PHP Simplified - Sunshine PHP 2018
SPL Primer

Recently uploaded (20)

PDF
Encapsulation theory and applications.pdf
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PDF
MIND Revenue Release Quarter 2 2025 Press Release
PDF
Building Integrated photovoltaic BIPV_UPV.pdf
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PPTX
Programs and apps: productivity, graphics, security and other tools
PDF
Advanced methodologies resolving dimensionality complications for autism neur...
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
PDF
Encapsulation_ Review paper, used for researhc scholars
PPT
Teaching material agriculture food technology
PDF
Review of recent advances in non-invasive hemoglobin estimation
PDF
Empathic Computing: Creating Shared Understanding
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PPTX
MYSQL Presentation for SQL database connectivity
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PPTX
Understanding_Digital_Forensics_Presentation.pptx
PPTX
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
Encapsulation theory and applications.pdf
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
MIND Revenue Release Quarter 2 2025 Press Release
Building Integrated photovoltaic BIPV_UPV.pdf
Diabetes mellitus diagnosis method based random forest with bat algorithm
Digital-Transformation-Roadmap-for-Companies.pptx
Programs and apps: productivity, graphics, security and other tools
Advanced methodologies resolving dimensionality complications for autism neur...
Per capita expenditure prediction using model stacking based on satellite ima...
Encapsulation_ Review paper, used for researhc scholars
Teaching material agriculture food technology
Review of recent advances in non-invasive hemoglobin estimation
Empathic Computing: Creating Shared Understanding
Dropbox Q2 2025 Financial Results & Investor Presentation
The Rise and Fall of 3GPP – Time for a Sabbatical?
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
MYSQL Presentation for SQL database connectivity
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
Understanding_Digital_Forensics_Presentation.pptx
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...

Zendcon 2007 Api Design