SlideShare a Scribd company logo
1 of 29
Download to read offline
Practical ZF1 to ZF2 Migration:
Lessons from the Field
Clark Everetts,
Zend Technologies, Inc.

Sunshine PHP Uncon
What are we going to talk about?

Higher-Level
(ROI, Organizational, Planning, Goals, Constraints)

Getting from Here

Suggestions / Best Practices

to Here

Lower-Level
(Coding Suggestions, Tools, Resources, Best Practices)

2
Who am I?
•Clark Everetts
– Professional Services Consultant for Zend Technologies
– Onsite and Remote Consulting – Linux, IBM iSeries, Windows
●
Software Architecture Audits and Code Reviews
●

Performance Audits

●

Continuous Delivery Assessments

Online / Onsite Training in PHP, ZF, Studio, Server, etc.
– SmartStarts: Training and Onsite Guided Development
•Past Life: Independent Consultant (2002 - 2012)
– Web Sites and Web Applications
– Contract Instructor for Zend
•Past, Past Life: Contractor for US Army and NASA (1990 - 2002)
– Interceptor Ground Control Software; Green Screen Db Applications
–

–

3

Space Station: Real-time, Embedded Software; Science Platforms,
Life Support
Who are You?

• Your role: Manager, developer, stakeholder/decision-maker?
• Size: Small to large companies; small to large dev teams
• Have you migrated applications from ZF1 to ZF2?
• Migrating now? How far along in the process are you?
• What challenges did you face / are you facing?
• What tools did you use, or wish existed?
• What did you do before you started?
• Did you expect to accomplish anything besides “the
migration?”
4
What (I think) You Want to Know
•Available Tools? How much do they do?
•Low-hanging fruit; most difficulties?
•Cost/Benefit? Expected gains?
–

Short-term?

–

Long-term?

–

When not to migrate?

•How to plan a migration; what factors affect the planning?
•Developer skills / skill levels required?
•Budget, schedule is expected for a given project size?
•Other resources?
Where this talk comes from

• Zend's Collective Experience
–

6

Planning, Guiding, Implementing Migrations
The Prospect of Migration...

7
Decisions, decisions...

• Write new ZF2 application from scratch?
• Run ZF1 and ZF2 side-by-side?
–

Use ZF2 components in existing ZF1 application

–

Use ZF1 components in new ZF2 application

–

Some URLs (existing capabilities) ZF1, with other URLs
(entirely new features) implemented in ZF2

–

Convert models, controllers, utility functions, helpers,
etc.

• Which ZF2 components are easiest to use in a ZF1 app?

8
More Decisions!
•New URLs for new features of application: can do in ZF2, but will likely rely
upon existing ZF1 models, helpers, etc., especially if you took the effort to
make those components reusable in the first place
•Build and test ZF2 version and then cut over from old site to new one, or
iterate existing ZF1 production site into a new ZF2-based site?
•Select a minimum PHP version
– PHP 5.3.3 is minimum for ZF2; current are 5.5.4 and 5.4.20. PHP
5.3.27+ will receive only security fixes through much of 2014 (Zend
supports PHP 5.2 for Enterprise customers on particular product
versions; will continue to support PHP 5.3)
– Consider most recent PHP version you can; this implies parallel
migrations (PHP version as well as ZF2), Look at
http://php.net/ChangeLog-5.php for removal of /changes to legacy
features and new capabilities
–

Consider impact to other PHP apps on the server
No One Migration Strategy to Rule Them All
*

There is no single, one-size-fits all, “correct” migration path for all
applications and organizations.
Many factors, including project size, team size and experience with ZF1
and ZF2, structure and quality of the ZF1 codebase, all affect the path to
take.

* http://ligh7bulb.deviantart.com/

10
Remember the Planned Migration Layer?

•Remember the planned Migration Layer?
– Was to allow ZF1 code to run on the ZF2 “engine”
–

Work started during ZF2 betas; too much change between beta
releases to keep such a tool current

–

Emulating ZF1 behaviour in ZF2 would have taken unacceptable
amount of developer time away from ZF2 development

•Though many people desired it, a full Migration Layer isn't strictly necessary
– PHP 5.3 Namespaces
– Migration: tedious, but not as hard as you think
●
Depends on the code you're starting with!
●

Fat controllers w/ data access, business logic, HTML :-(
Some Tools Exist to Help
•ZF2 Documentation Page
http://framework.zend.com/manual/2.2/en/migration/overview.html
●

GitHub: EvanDotPro/zf-2-for-1
ZF2 Form View Helpers, could be extended to other features
Check out the forks, which have more features.
–

•

Not for the long-term; it is a deliberately temporary solution!

GitHib: prolic/HumusMvcSkeletonApplication
integrates ModuleManager and ServiceManager into ZF1 application
See http://www.sasaprolic.com/2012/10/when-migration-to-zendframework-2.html
ZF1 Compatibility Module - Overview

• Custom ZF2 Module that you write – simple, straightforward
• Use existing ZF1-based application resources with minimal
initial code rewrite:
– Database
–

Translation

–

Session

• That is, Any Zend_Application_Resource plugin
• Obtain and use these resources as ZF2 Services via
ServiceManager
• Only for Application-wide, “global” resources

13
The Prospect of Migration...

Starting to Feel Better?

14
Planning

• What you do depends on what you want
–

Delay a complete rewrite
●

–

Phased approach, reusing much of your ZF1 code

Refactor and clean up “bad” code

• Smaller Application, perhaps non mission-critical
–

15

Rewrite, use as a learning experience for a larger
migration effort
ZF1 Compatibility Module – Add Module to config

// config/application.config.php
'modules' => array(
'Application',
'Zf1', // <-- your ZF1 compatibility module
// you will add other modules to the list as you
progress
),
// remainder of config array

16
ZF1 Compatibility Module – Autoload ZF1 Classes
// module/Zf1/Module.php
namespace Zf1;
use ZendModuleManagerFeatureAutoloaderProviderInterface;
use ZendMvcMvcEvent;
class Module implements AutoloaderProviderInterface
{
public function getAutoloaderConfig()
{
$zf1path = getenv('ZF1_PATH'); // SetEnv in Apache vhost/.htaccess
if (!$zf1path) { throw new Exception('Define ZF1 library path'); }
/// more coming...

17
ZF1 Compatibility Module – Autoload ZF1 (cont.)
// module/Zf1/Module.php getAutoloaderConfig()
return array(
'ZendLoaderStandardAutoloader' => array (
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' .
str_replace('', '/', __NAMESPACE__)
),
'prefixes' => array('Zend' => $zf1path)
)
);

18
ZF1 Compat. Module – Resources become Services
// module/Zf1/config/module.config.php
'service_manager' => array (
'factories' => array(
'zf1-db' => 'Zf1ServiceFactoryDatabase',
'zf1-translate'=> 'Zf1ServiceFactoryTranslate',
// other ZF1-based resources...
),
'aliases' => array (
'translate' => 'zf1-translate',

)
)

19
ZF1 Compat. Module – Factories to Create Services

namespace Zf1ServiceFactory;
use ZendServiceManager;
class Database implements ServiceManagerFactoryInterface {

}

20

public function createService(
ServiceManagerServiceLocatorInterface $serviceLocator
)
{
$config = $serviceLocator->get('config');
$adapter = Zend_Db::factory(
$config['zf1-db']['adapter'],
$config['zf1-db']['params']
);
return $adapter;
}
ZF1 Compatibility Module – Register the Services
// module/Zf1/Module.php
public function onBootstrap(MvcEvent $e)
{
$services = $e->getApplication()->getServiceManager();
$dbAdapter = $services->get('zf1-db');
Zend_Db_Table_Abstract::setDefaultAdapter($dbAdapter);
// Perhaps your views need config. For now, in views, access config
// directly via registry. Later, use code that injects config into view
$config = $services->get('config');
Zend_Registry::set('config', Util::arrayToObject($config));
// In audits, we often find ZF1 models accessing the db via registry
// What you see here is not “best practice,” but a baby step toward
// injecting this dependency in a later phase of migration
$database = $services->get('zf1-db');
Zend_Registry::set('database', $database);
$services = $e->getApplication()->getServiceManager();
Zend_Registry::set('session', $services->get('auth')->getStorage()>read());
}

21
ZF1 Compatibility Module – Review

• Temporary, stop-gap measure for reusing ZF1 resources
• Avoids major up-front rewrite
• Obtain and use these resources as ZF2 Services via
ServiceManager:
–

Add it to the list of modules used by your ZF2 app

–

Set up autoloading to find ZF1 library

–

Identify Resources as Services to the ServiceManager

–

Write Factories to create the services

–

Register the Services

• Only for Application-wide, “global” resources
• Remember: This Module will GO AWAY in a later phase!
22
General Migration Practices - MWOP

• Do as much as practical to separate your business logic OUT of your

controllers. Controllers should be thin, and primarily gather arguments
to pass to the domain model, and then gather the response to pass on to
the view.

• Try to remove use of plugins such as "ActionStack" and "Forward"

whenever possible. The latter has an equivalent in ZF2 but operates
slightly differently; ActionStack is unnecessary, as you can directly call
on other controllers more easily in ZF2.

• Rewrite a module at a time; this will help you identify patterns for
migration, and give you confidence as you go forward.

• Make sure you identify any dependencies on other modules early, so you
can migrate cleanly. Avoids major up-front rewrite.

• This talk shows how you can map ZF1 "application resources" as services
in ZF2.

• A module to bootstrap ZF1 config, autoloading, and resources makes it
23

easier to re-use resources written for ZF1 inside a ZF2 app; the bulk of
the work of migration will then be in the MVC, particularly in controllers
and forms.
The Prospect of Migration...

How are you feeling?

24
Reusing ZF1 Configuration - Overview

• ZF1 Configuration – .ini, .xml, .json, .yaml
• ZF2 Configuration – arrays
• Reuse existing ZF1 configuration files in ZF2 application

25
Reusing ZF1 Configuration - .ini files
// module/SomeModule/Module.php
public function getConfig ()
{
$path = __DIR__ . '/config/config.ini';
$config = new Zend_Config_Ini($path, $section);
$oldConfig = $config->toArray();
if (defined('ZF1_COMPAT')) {
$oldConfig['zf1-db'] = $oldConfig['db']; }
// Fix 1: for the database options
$dbParams = array ('driver' => $oldConfig['db']['adapter']);
$dbParams = array_merge($dbParams, $oldConfig['db']['params']);
$oldConfig['db'] = $dbParams;

}

26

$phpConfig = include __DIR__ . '/config/module.config.php';
return array_merge($oldConfig, $phpConfig);
Reuse ZF1 Models - Autoloading
// module/SomeModule/Module.php
public function getAutoloaderConfig ()
{
return array(
'ZendLoaderClassMapAutoloader' => array(
__DIR__ . '/autoload_classmap.php'
),
'ZendLoaderStandardAutoloader' => array (
'namespaces' => array(
// if we're in a namespace deeper than one level we need to
// fix the  in the path
__NAMESPACE__ => __DIR__ . '/src/' . str_replace('', '/',
__NAMESPACE__)
),
'prefixes' => array(// This goes away in later phase
'Model' => __DIR__ .'/src/' . str_replace('', '/', __NAMESPACE__)
. '/Model',
)
)
);
}

27
Reuse ZF1 Models – Merge config with ZF2 config
// module/SomeModule/Module.php
public function getConfig ()
{
include __DIR__ . '/config/config.php';
Opportunity
$path = __DIR__ . '/config/config.ini';
$config = new Zend_Config_Ini($path, $section);
$oldConfig = $config->toArray();

for refactoring

if (defined('ZF1_COMPAT')) {
$oldConfig['zf1-db'] = $oldConfig['db'];
}
// database options from db section of config.ini
$dbParams = array('driver' => $oldConfig['db']['adapter']);
$dbParams = array_merge($dbParams, $oldConfig['db']['params']);
$oldConfig['db'] = $dbParams;

}

28

$phpConfig = include __DIR__ . '/config/module.config.php';
return array_merge($oldConfig, $phpConfig);
Thank You!
Please give constructive feedback at
https://joind.in/talk/view/10680
Email: clark.e@zend.com
https://github.com/clarkphp
Twitter: @clarkphp

More Related Content

What's hot

Intro To Puppet.Key
Intro To Puppet.KeyIntro To Puppet.Key
Intro To Puppet.KeyWork
 
BP-10 Keeping Your Sanity – Rapid Development & Deployment Tools
BP-10 Keeping Your Sanity – Rapid Development & Deployment ToolsBP-10 Keeping Your Sanity – Rapid Development & Deployment Tools
BP-10 Keeping Your Sanity – Rapid Development & Deployment ToolsAlfresco Software
 
SynapseIndia drupal presentation on drupal info
SynapseIndia drupal  presentation on drupal infoSynapseIndia drupal  presentation on drupal info
SynapseIndia drupal presentation on drupal infoSynapseindiappsdevelopment
 
Mixing Plone and Django for explosive results
Mixing Plone and Django for explosive resultsMixing Plone and Django for explosive results
Mixing Plone and Django for explosive resultsSimone Deponti
 
Joe Damato
Joe DamatoJoe Damato
Joe DamatoOntico
 
Testing Alfresco extensions
Testing Alfresco extensionsTesting Alfresco extensions
Testing Alfresco extensionsITD Systems
 
Migration tales from java ee 5 to 7
Migration tales from java ee 5 to 7Migration tales from java ee 5 to 7
Migration tales from java ee 5 to 7Roberto Cortez
 
Organizing Your PHP Projects (2010 ConFoo)
Organizing Your PHP Projects (2010 ConFoo)Organizing Your PHP Projects (2010 ConFoo)
Organizing Your PHP Projects (2010 ConFoo)Paul Jones
 
Zend con 2016 bdd with behat for beginners
Zend con 2016   bdd with behat for beginnersZend con 2016   bdd with behat for beginners
Zend con 2016 bdd with behat for beginnersAdam Englander
 
KYSUC - Keep Your Schema Under Control
KYSUC - Keep Your Schema Under ControlKYSUC - Keep Your Schema Under Control
KYSUC - Keep Your Schema Under ControlCoimbra JUG
 
Maven - Taming the Beast
Maven - Taming the BeastMaven - Taming the Beast
Maven - Taming the BeastRoberto Cortez
 
Automating Drupal Deployment
Automating Drupal DeploymentAutomating Drupal Deployment
Automating Drupal DeploymentGerald Villorente
 
External Master Data in Alfresco: Integrating and Keeping Metadata Consistent...
External Master Data in Alfresco: Integrating and Keeping Metadata Consistent...External Master Data in Alfresco: Integrating and Keeping Metadata Consistent...
External Master Data in Alfresco: Integrating and Keeping Metadata Consistent...ITD Systems
 
The Architect Way - JSCamp.asia 2012
The Architect Way - JSCamp.asia 2012The Architect Way - JSCamp.asia 2012
The Architect Way - JSCamp.asia 2012Jan Jongboom
 
Deploying to the Salesforce1 Platform
Deploying to the Salesforce1 PlatformDeploying to the Salesforce1 Platform
Deploying to the Salesforce1 PlatformKeir Bowden
 
Code Refactoring
Code RefactoringCode Refactoring
Code RefactoringYu-Chih Lin
 
Take your CFML Legacy Apps to Modernization
Take your CFML Legacy Apps to ModernizationTake your CFML Legacy Apps to Modernization
Take your CFML Legacy Apps to ModernizationOrtus Solutions, Corp
 
Go - A Key Language in Enterprise Application Development?
Go - A Key Language in Enterprise Application Development?Go - A Key Language in Enterprise Application Development?
Go - A Key Language in Enterprise Application Development?C4Media
 

What's hot (20)

Stackato v6
Stackato v6Stackato v6
Stackato v6
 
Intro To Puppet.Key
Intro To Puppet.KeyIntro To Puppet.Key
Intro To Puppet.Key
 
BP-10 Keeping Your Sanity – Rapid Development & Deployment Tools
BP-10 Keeping Your Sanity – Rapid Development & Deployment ToolsBP-10 Keeping Your Sanity – Rapid Development & Deployment Tools
BP-10 Keeping Your Sanity – Rapid Development & Deployment Tools
 
SynapseIndia drupal presentation on drupal info
SynapseIndia drupal  presentation on drupal infoSynapseIndia drupal  presentation on drupal info
SynapseIndia drupal presentation on drupal info
 
Mixing Plone and Django for explosive results
Mixing Plone and Django for explosive resultsMixing Plone and Django for explosive results
Mixing Plone and Django for explosive results
 
Joe Damato
Joe DamatoJoe Damato
Joe Damato
 
Testing Alfresco extensions
Testing Alfresco extensionsTesting Alfresco extensions
Testing Alfresco extensions
 
Migration tales from java ee 5 to 7
Migration tales from java ee 5 to 7Migration tales from java ee 5 to 7
Migration tales from java ee 5 to 7
 
Organizing Your PHP Projects (2010 ConFoo)
Organizing Your PHP Projects (2010 ConFoo)Organizing Your PHP Projects (2010 ConFoo)
Organizing Your PHP Projects (2010 ConFoo)
 
Zend con 2016 bdd with behat for beginners
Zend con 2016   bdd with behat for beginnersZend con 2016   bdd with behat for beginners
Zend con 2016 bdd with behat for beginners
 
KYSUC - Keep Your Schema Under Control
KYSUC - Keep Your Schema Under ControlKYSUC - Keep Your Schema Under Control
KYSUC - Keep Your Schema Under Control
 
Maven - Taming the Beast
Maven - Taming the BeastMaven - Taming the Beast
Maven - Taming the Beast
 
Developing better PHP projects
Developing better PHP projectsDeveloping better PHP projects
Developing better PHP projects
 
Automating Drupal Deployment
Automating Drupal DeploymentAutomating Drupal Deployment
Automating Drupal Deployment
 
External Master Data in Alfresco: Integrating and Keeping Metadata Consistent...
External Master Data in Alfresco: Integrating and Keeping Metadata Consistent...External Master Data in Alfresco: Integrating and Keeping Metadata Consistent...
External Master Data in Alfresco: Integrating and Keeping Metadata Consistent...
 
The Architect Way - JSCamp.asia 2012
The Architect Way - JSCamp.asia 2012The Architect Way - JSCamp.asia 2012
The Architect Way - JSCamp.asia 2012
 
Deploying to the Salesforce1 Platform
Deploying to the Salesforce1 PlatformDeploying to the Salesforce1 Platform
Deploying to the Salesforce1 Platform
 
Code Refactoring
Code RefactoringCode Refactoring
Code Refactoring
 
Take your CFML Legacy Apps to Modernization
Take your CFML Legacy Apps to ModernizationTake your CFML Legacy Apps to Modernization
Take your CFML Legacy Apps to Modernization
 
Go - A Key Language in Enterprise Application Development?
Go - A Key Language in Enterprise Application Development?Go - A Key Language in Enterprise Application Development?
Go - A Key Language in Enterprise Application Development?
 

Viewers also liked

Just Married: Zend Framework and Doctrine
Just Married: Zend Framework and DoctrineJust Married: Zend Framework and Doctrine
Just Married: Zend Framework and DoctrineBenjamin Eberlei
 
Présentation de DBAL en PHP (Nantes)
Présentation de DBAL en PHP (Nantes)Présentation de DBAL en PHP (Nantes)
Présentation de DBAL en PHP (Nantes)Mickael Perraud
 
Moving a high traffic ZF1 Enterprise Application to SF2 - Lessons learned
Moving a high traffic ZF1 Enterprise Application to SF2 - Lessons learnedMoving a high traffic ZF1 Enterprise Application to SF2 - Lessons learned
Moving a high traffic ZF1 Enterprise Application to SF2 - Lessons learnedBaldur Rensch
 
Présentation de DBAL en PHP
Présentation de DBAL en PHPPrésentation de DBAL en PHP
Présentation de DBAL en PHPMickael Perraud
 

Viewers also liked (6)

Just Married: Zend Framework and Doctrine
Just Married: Zend Framework and DoctrineJust Married: Zend Framework and Doctrine
Just Married: Zend Framework and Doctrine
 
Présentation de DBAL en PHP (Nantes)
Présentation de DBAL en PHP (Nantes)Présentation de DBAL en PHP (Nantes)
Présentation de DBAL en PHP (Nantes)
 
Zf2 ce-qui-va-changer
Zf2 ce-qui-va-changerZf2 ce-qui-va-changer
Zf2 ce-qui-va-changer
 
Moving a high traffic ZF1 Enterprise Application to SF2 - Lessons learned
Moving a high traffic ZF1 Enterprise Application to SF2 - Lessons learnedMoving a high traffic ZF1 Enterprise Application to SF2 - Lessons learned
Moving a high traffic ZF1 Enterprise Application to SF2 - Lessons learned
 
Educação física e inglês (partes do corpo)
Educação física e inglês (partes do corpo)Educação física e inglês (partes do corpo)
Educação física e inglês (partes do corpo)
 
Présentation de DBAL en PHP
Présentation de DBAL en PHPPrésentation de DBAL en PHP
Présentation de DBAL en PHP
 

Similar to Sunshine php practical-zf1-zf2-migration

ZF2 Modules: Events, Services, and of course, modularity
ZF2 Modules: Events, Services, and of course, modularityZF2 Modules: Events, Services, and of course, modularity
ZF2 Modules: Events, Services, and of course, modularityJohn Coggeshall
 
Building A FaaA Platform With Redis: Paulo Arruda
Building A FaaA Platform With Redis: Paulo ArrudaBuilding A FaaA Platform With Redis: Paulo Arruda
Building A FaaA Platform With Redis: Paulo ArrudaRedis Labs
 
faastRuby - Building a FaaS platform with Redis (RedisConf19)
faastRuby - Building a FaaS platform with Redis (RedisConf19)faastRuby - Building a FaaS platform with Redis (RedisConf19)
faastRuby - Building a FaaS platform with Redis (RedisConf19)Paulo Arruda
 
SD PHP Zend Framework
SD PHP Zend FrameworkSD PHP Zend Framework
SD PHP Zend Frameworkphilipjting
 
Expressive Microservice Framework Blastoff
Expressive Microservice Framework BlastoffExpressive Microservice Framework Blastoff
Expressive Microservice Framework BlastoffAdam Culp
 
z13: New Opportunities – if you dare!
z13: New Opportunities – if you dare!z13: New Opportunities – if you dare!
z13: New Opportunities – if you dare!Michael Erichsen
 
Hashicorp-Terraform-Deep-Dive-with-no-Fear-Victor-Turbinsky-Texuna.pdf
Hashicorp-Terraform-Deep-Dive-with-no-Fear-Victor-Turbinsky-Texuna.pdfHashicorp-Terraform-Deep-Dive-with-no-Fear-Victor-Turbinsky-Texuna.pdf
Hashicorp-Terraform-Deep-Dive-with-no-Fear-Victor-Turbinsky-Texuna.pdfssuser705051
 
Collaborative Terraform with Atlantis
Collaborative Terraform with AtlantisCollaborative Terraform with Atlantis
Collaborative Terraform with AtlantisFerenc Kovács
 
AWS Community Day 2022 David Kirk_Hybrid Local Development Environments with...
AWS Community Day 2022  David Kirk_Hybrid Local Development Environments with...AWS Community Day 2022  David Kirk_Hybrid Local Development Environments with...
AWS Community Day 2022 David Kirk_Hybrid Local Development Environments with...AWS Chicago
 
Strategies and Tips for Building Enterprise Drupal Applications - PNWDS 2013
Strategies and Tips for Building Enterprise Drupal Applications - PNWDS 2013Strategies and Tips for Building Enterprise Drupal Applications - PNWDS 2013
Strategies and Tips for Building Enterprise Drupal Applications - PNWDS 2013Mack Hardy
 
Spring Roo Flex Add-on
Spring Roo Flex Add-onSpring Roo Flex Add-on
Spring Roo Flex Add-onBill Ott
 
Zend Framework 2 Components
Zend Framework 2 ComponentsZend Framework 2 Components
Zend Framework 2 ComponentsShawn Stratton
 
Using Service Oriented Operation and Provisioning at Financial Times
Using Service Oriented Operation and Provisioning at Financial TimesUsing Service Oriented Operation and Provisioning at Financial Times
Using Service Oriented Operation and Provisioning at Financial TimesEmeka Mosanya
 

Similar to Sunshine php practical-zf1-zf2-migration (20)

Serverless design with Fn project
Serverless design with Fn projectServerless design with Fn project
Serverless design with Fn project
 
Frameworks choice
Frameworks choiceFrameworks choice
Frameworks choice
 
Gulp overview
Gulp overviewGulp overview
Gulp overview
 
ZF2 Modules: Events, Services, and of course, modularity
ZF2 Modules: Events, Services, and of course, modularityZF2 Modules: Events, Services, and of course, modularity
ZF2 Modules: Events, Services, and of course, modularity
 
Building A FaaA Platform With Redis: Paulo Arruda
Building A FaaA Platform With Redis: Paulo ArrudaBuilding A FaaA Platform With Redis: Paulo Arruda
Building A FaaA Platform With Redis: Paulo Arruda
 
faastRuby - Building a FaaS platform with Redis (RedisConf19)
faastRuby - Building a FaaS platform with Redis (RedisConf19)faastRuby - Building a FaaS platform with Redis (RedisConf19)
faastRuby - Building a FaaS platform with Redis (RedisConf19)
 
SD PHP Zend Framework
SD PHP Zend FrameworkSD PHP Zend Framework
SD PHP Zend Framework
 
Expressive Microservice Framework Blastoff
Expressive Microservice Framework BlastoffExpressive Microservice Framework Blastoff
Expressive Microservice Framework Blastoff
 
z13: New Opportunities – if you dare!
z13: New Opportunities – if you dare!z13: New Opportunities – if you dare!
z13: New Opportunities – if you dare!
 
Hashicorp-Terraform-Deep-Dive-with-no-Fear-Victor-Turbinsky-Texuna.pdf
Hashicorp-Terraform-Deep-Dive-with-no-Fear-Victor-Turbinsky-Texuna.pdfHashicorp-Terraform-Deep-Dive-with-no-Fear-Victor-Turbinsky-Texuna.pdf
Hashicorp-Terraform-Deep-Dive-with-no-Fear-Victor-Turbinsky-Texuna.pdf
 
Terraform-2.pdf
Terraform-2.pdfTerraform-2.pdf
Terraform-2.pdf
 
Collaborative Terraform with Atlantis
Collaborative Terraform with AtlantisCollaborative Terraform with Atlantis
Collaborative Terraform with Atlantis
 
Lamp Zend Security
Lamp Zend SecurityLamp Zend Security
Lamp Zend Security
 
AWS Community Day 2022 David Kirk_Hybrid Local Development Environments with...
AWS Community Day 2022  David Kirk_Hybrid Local Development Environments with...AWS Community Day 2022  David Kirk_Hybrid Local Development Environments with...
AWS Community Day 2022 David Kirk_Hybrid Local Development Environments with...
 
Zend
ZendZend
Zend
 
Strategies and Tips for Building Enterprise Drupal Applications - PNWDS 2013
Strategies and Tips for Building Enterprise Drupal Applications - PNWDS 2013Strategies and Tips for Building Enterprise Drupal Applications - PNWDS 2013
Strategies and Tips for Building Enterprise Drupal Applications - PNWDS 2013
 
Spring Roo Flex Add-on
Spring Roo Flex Add-onSpring Roo Flex Add-on
Spring Roo Flex Add-on
 
Zend Framework 2 Components
Zend Framework 2 ComponentsZend Framework 2 Components
Zend Framework 2 Components
 
Using Service Oriented Operation and Provisioning at Financial Times
Using Service Oriented Operation and Provisioning at Financial TimesUsing Service Oriented Operation and Provisioning at Financial Times
Using Service Oriented Operation and Provisioning at Financial Times
 
Puppetconf2012
Puppetconf2012Puppetconf2012
Puppetconf2012
 

Recently uploaded

Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Hiroshi SHIBATA
 
Decarbonising Buildings: Making a net-zero built environment a reality
Decarbonising Buildings: Making a net-zero built environment a realityDecarbonising Buildings: Making a net-zero built environment a reality
Decarbonising Buildings: Making a net-zero built environment a realityIES VE
 
Manual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance AuditManual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance AuditSkynet Technologies
 
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...Wes McKinney
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .Alan Dix
 
Connecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfConnecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfNeo4j
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.Curtis Poe
 
Generative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersGenerative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersRaghuram Pandurangan
 
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...Scott Andery
 
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesHow to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesThousandEyes
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsPixlogix Infotech
 
[Webinar] SpiraTest - Setting New Standards in Quality Assurance
[Webinar] SpiraTest - Setting New Standards in Quality Assurance[Webinar] SpiraTest - Setting New Standards in Quality Assurance
[Webinar] SpiraTest - Setting New Standards in Quality AssuranceInflectra
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersNicole Novielli
 
What is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfWhat is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfMounikaPolabathina
 
Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Farhan Tariq
 
Generative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfGenerative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfIngrid Airi González
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxLoriGlavin3
 
A Framework for Development in the AI Age
A Framework for Development in the AI AgeA Framework for Development in the AI Age
A Framework for Development in the AI AgeCprime
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteDianaGray10
 

Recently uploaded (20)

Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024
 
Decarbonising Buildings: Making a net-zero built environment a reality
Decarbonising Buildings: Making a net-zero built environment a realityDecarbonising Buildings: Making a net-zero built environment a reality
Decarbonising Buildings: Making a net-zero built environment a reality
 
Manual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance AuditManual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance Audit
 
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .
 
Connecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfConnecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdf
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.
 
Generative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersGenerative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information Developers
 
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...
 
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesHow to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and Cons
 
[Webinar] SpiraTest - Setting New Standards in Quality Assurance
[Webinar] SpiraTest - Setting New Standards in Quality Assurance[Webinar] SpiraTest - Setting New Standards in Quality Assurance
[Webinar] SpiraTest - Setting New Standards in Quality Assurance
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software Developers
 
What is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfWhat is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdf
 
Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...
 
Generative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfGenerative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdf
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
 
A Framework for Development in the AI Age
A Framework for Development in the AI AgeA Framework for Development in the AI Age
A Framework for Development in the AI Age
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test Suite
 

Sunshine php practical-zf1-zf2-migration

  • 1. Practical ZF1 to ZF2 Migration: Lessons from the Field Clark Everetts, Zend Technologies, Inc. Sunshine PHP Uncon
  • 2. What are we going to talk about? Higher-Level (ROI, Organizational, Planning, Goals, Constraints) Getting from Here Suggestions / Best Practices to Here Lower-Level (Coding Suggestions, Tools, Resources, Best Practices) 2
  • 3. Who am I? •Clark Everetts – Professional Services Consultant for Zend Technologies – Onsite and Remote Consulting – Linux, IBM iSeries, Windows ● Software Architecture Audits and Code Reviews ● Performance Audits ● Continuous Delivery Assessments Online / Onsite Training in PHP, ZF, Studio, Server, etc. – SmartStarts: Training and Onsite Guided Development •Past Life: Independent Consultant (2002 - 2012) – Web Sites and Web Applications – Contract Instructor for Zend •Past, Past Life: Contractor for US Army and NASA (1990 - 2002) – Interceptor Ground Control Software; Green Screen Db Applications – – 3 Space Station: Real-time, Embedded Software; Science Platforms, Life Support
  • 4. Who are You? • Your role: Manager, developer, stakeholder/decision-maker? • Size: Small to large companies; small to large dev teams • Have you migrated applications from ZF1 to ZF2? • Migrating now? How far along in the process are you? • What challenges did you face / are you facing? • What tools did you use, or wish existed? • What did you do before you started? • Did you expect to accomplish anything besides “the migration?” 4
  • 5. What (I think) You Want to Know •Available Tools? How much do they do? •Low-hanging fruit; most difficulties? •Cost/Benefit? Expected gains? – Short-term? – Long-term? – When not to migrate? •How to plan a migration; what factors affect the planning? •Developer skills / skill levels required? •Budget, schedule is expected for a given project size? •Other resources?
  • 6. Where this talk comes from • Zend's Collective Experience – 6 Planning, Guiding, Implementing Migrations
  • 7. The Prospect of Migration... 7
  • 8. Decisions, decisions... • Write new ZF2 application from scratch? • Run ZF1 and ZF2 side-by-side? – Use ZF2 components in existing ZF1 application – Use ZF1 components in new ZF2 application – Some URLs (existing capabilities) ZF1, with other URLs (entirely new features) implemented in ZF2 – Convert models, controllers, utility functions, helpers, etc. • Which ZF2 components are easiest to use in a ZF1 app? 8
  • 9. More Decisions! •New URLs for new features of application: can do in ZF2, but will likely rely upon existing ZF1 models, helpers, etc., especially if you took the effort to make those components reusable in the first place •Build and test ZF2 version and then cut over from old site to new one, or iterate existing ZF1 production site into a new ZF2-based site? •Select a minimum PHP version – PHP 5.3.3 is minimum for ZF2; current are 5.5.4 and 5.4.20. PHP 5.3.27+ will receive only security fixes through much of 2014 (Zend supports PHP 5.2 for Enterprise customers on particular product versions; will continue to support PHP 5.3) – Consider most recent PHP version you can; this implies parallel migrations (PHP version as well as ZF2), Look at http://php.net/ChangeLog-5.php for removal of /changes to legacy features and new capabilities – Consider impact to other PHP apps on the server
  • 10. No One Migration Strategy to Rule Them All * There is no single, one-size-fits all, “correct” migration path for all applications and organizations. Many factors, including project size, team size and experience with ZF1 and ZF2, structure and quality of the ZF1 codebase, all affect the path to take. * http://ligh7bulb.deviantart.com/ 10
  • 11. Remember the Planned Migration Layer? •Remember the planned Migration Layer? – Was to allow ZF1 code to run on the ZF2 “engine” – Work started during ZF2 betas; too much change between beta releases to keep such a tool current – Emulating ZF1 behaviour in ZF2 would have taken unacceptable amount of developer time away from ZF2 development •Though many people desired it, a full Migration Layer isn't strictly necessary – PHP 5.3 Namespaces – Migration: tedious, but not as hard as you think ● Depends on the code you're starting with! ● Fat controllers w/ data access, business logic, HTML :-(
  • 12. Some Tools Exist to Help •ZF2 Documentation Page http://framework.zend.com/manual/2.2/en/migration/overview.html ● GitHub: EvanDotPro/zf-2-for-1 ZF2 Form View Helpers, could be extended to other features Check out the forks, which have more features. – • Not for the long-term; it is a deliberately temporary solution! GitHib: prolic/HumusMvcSkeletonApplication integrates ModuleManager and ServiceManager into ZF1 application See http://www.sasaprolic.com/2012/10/when-migration-to-zendframework-2.html
  • 13. ZF1 Compatibility Module - Overview • Custom ZF2 Module that you write – simple, straightforward • Use existing ZF1-based application resources with minimal initial code rewrite: – Database – Translation – Session • That is, Any Zend_Application_Resource plugin • Obtain and use these resources as ZF2 Services via ServiceManager • Only for Application-wide, “global” resources 13
  • 14. The Prospect of Migration... Starting to Feel Better? 14
  • 15. Planning • What you do depends on what you want – Delay a complete rewrite ● – Phased approach, reusing much of your ZF1 code Refactor and clean up “bad” code • Smaller Application, perhaps non mission-critical – 15 Rewrite, use as a learning experience for a larger migration effort
  • 16. ZF1 Compatibility Module – Add Module to config // config/application.config.php 'modules' => array( 'Application', 'Zf1', // <-- your ZF1 compatibility module // you will add other modules to the list as you progress ), // remainder of config array 16
  • 17. ZF1 Compatibility Module – Autoload ZF1 Classes // module/Zf1/Module.php namespace Zf1; use ZendModuleManagerFeatureAutoloaderProviderInterface; use ZendMvcMvcEvent; class Module implements AutoloaderProviderInterface { public function getAutoloaderConfig() { $zf1path = getenv('ZF1_PATH'); // SetEnv in Apache vhost/.htaccess if (!$zf1path) { throw new Exception('Define ZF1 library path'); } /// more coming... 17
  • 18. ZF1 Compatibility Module – Autoload ZF1 (cont.) // module/Zf1/Module.php getAutoloaderConfig() return array( 'ZendLoaderStandardAutoloader' => array ( 'namespaces' => array( __NAMESPACE__ => __DIR__ . '/src/' . str_replace('', '/', __NAMESPACE__) ), 'prefixes' => array('Zend' => $zf1path) ) ); 18
  • 19. ZF1 Compat. Module – Resources become Services // module/Zf1/config/module.config.php 'service_manager' => array ( 'factories' => array( 'zf1-db' => 'Zf1ServiceFactoryDatabase', 'zf1-translate'=> 'Zf1ServiceFactoryTranslate', // other ZF1-based resources... ), 'aliases' => array ( 'translate' => 'zf1-translate', ) ) 19
  • 20. ZF1 Compat. Module – Factories to Create Services namespace Zf1ServiceFactory; use ZendServiceManager; class Database implements ServiceManagerFactoryInterface { } 20 public function createService( ServiceManagerServiceLocatorInterface $serviceLocator ) { $config = $serviceLocator->get('config'); $adapter = Zend_Db::factory( $config['zf1-db']['adapter'], $config['zf1-db']['params'] ); return $adapter; }
  • 21. ZF1 Compatibility Module – Register the Services // module/Zf1/Module.php public function onBootstrap(MvcEvent $e) { $services = $e->getApplication()->getServiceManager(); $dbAdapter = $services->get('zf1-db'); Zend_Db_Table_Abstract::setDefaultAdapter($dbAdapter); // Perhaps your views need config. For now, in views, access config // directly via registry. Later, use code that injects config into view $config = $services->get('config'); Zend_Registry::set('config', Util::arrayToObject($config)); // In audits, we often find ZF1 models accessing the db via registry // What you see here is not “best practice,” but a baby step toward // injecting this dependency in a later phase of migration $database = $services->get('zf1-db'); Zend_Registry::set('database', $database); $services = $e->getApplication()->getServiceManager(); Zend_Registry::set('session', $services->get('auth')->getStorage()>read()); } 21
  • 22. ZF1 Compatibility Module – Review • Temporary, stop-gap measure for reusing ZF1 resources • Avoids major up-front rewrite • Obtain and use these resources as ZF2 Services via ServiceManager: – Add it to the list of modules used by your ZF2 app – Set up autoloading to find ZF1 library – Identify Resources as Services to the ServiceManager – Write Factories to create the services – Register the Services • Only for Application-wide, “global” resources • Remember: This Module will GO AWAY in a later phase! 22
  • 23. General Migration Practices - MWOP • Do as much as practical to separate your business logic OUT of your controllers. Controllers should be thin, and primarily gather arguments to pass to the domain model, and then gather the response to pass on to the view. • Try to remove use of plugins such as "ActionStack" and "Forward" whenever possible. The latter has an equivalent in ZF2 but operates slightly differently; ActionStack is unnecessary, as you can directly call on other controllers more easily in ZF2. • Rewrite a module at a time; this will help you identify patterns for migration, and give you confidence as you go forward. • Make sure you identify any dependencies on other modules early, so you can migrate cleanly. Avoids major up-front rewrite. • This talk shows how you can map ZF1 "application resources" as services in ZF2. • A module to bootstrap ZF1 config, autoloading, and resources makes it 23 easier to re-use resources written for ZF1 inside a ZF2 app; the bulk of the work of migration will then be in the MVC, particularly in controllers and forms.
  • 24. The Prospect of Migration... How are you feeling? 24
  • 25. Reusing ZF1 Configuration - Overview • ZF1 Configuration – .ini, .xml, .json, .yaml • ZF2 Configuration – arrays • Reuse existing ZF1 configuration files in ZF2 application 25
  • 26. Reusing ZF1 Configuration - .ini files // module/SomeModule/Module.php public function getConfig () { $path = __DIR__ . '/config/config.ini'; $config = new Zend_Config_Ini($path, $section); $oldConfig = $config->toArray(); if (defined('ZF1_COMPAT')) { $oldConfig['zf1-db'] = $oldConfig['db']; } // Fix 1: for the database options $dbParams = array ('driver' => $oldConfig['db']['adapter']); $dbParams = array_merge($dbParams, $oldConfig['db']['params']); $oldConfig['db'] = $dbParams; } 26 $phpConfig = include __DIR__ . '/config/module.config.php'; return array_merge($oldConfig, $phpConfig);
  • 27. Reuse ZF1 Models - Autoloading // module/SomeModule/Module.php public function getAutoloaderConfig () { return array( 'ZendLoaderClassMapAutoloader' => array( __DIR__ . '/autoload_classmap.php' ), 'ZendLoaderStandardAutoloader' => array ( 'namespaces' => array( // if we're in a namespace deeper than one level we need to // fix the in the path __NAMESPACE__ => __DIR__ . '/src/' . str_replace('', '/', __NAMESPACE__) ), 'prefixes' => array(// This goes away in later phase 'Model' => __DIR__ .'/src/' . str_replace('', '/', __NAMESPACE__) . '/Model', ) ) ); } 27
  • 28. Reuse ZF1 Models – Merge config with ZF2 config // module/SomeModule/Module.php public function getConfig () { include __DIR__ . '/config/config.php'; Opportunity $path = __DIR__ . '/config/config.ini'; $config = new Zend_Config_Ini($path, $section); $oldConfig = $config->toArray(); for refactoring if (defined('ZF1_COMPAT')) { $oldConfig['zf1-db'] = $oldConfig['db']; } // database options from db section of config.ini $dbParams = array('driver' => $oldConfig['db']['adapter']); $dbParams = array_merge($dbParams, $oldConfig['db']['params']); $oldConfig['db'] = $dbParams; } 28 $phpConfig = include __DIR__ . '/config/module.config.php'; return array_merge($oldConfig, $phpConfig);
  • 29. Thank You! Please give constructive feedback at https://joind.in/talk/view/10680 Email: clark.e@zend.com https://github.com/clarkphp Twitter: @clarkphp