Perforce PHP Object and Record Models Geoff Nicol Software Developer Perforce PHP Object Model Overview
• Abstract perforce operations • Do a lot of the parsing and assembly to avoid string bashing elsewhere • Create a more PHP natural layer • Wrap multiple connection types • Provide a reliable interface • Creates a foundation for PHP Record Model MODELS MODELS MODELS MODELS SPEC OUTPUT SAMPLE
p4 changes -m1 ... change 318652 ... time 1305914360 ... user gnicol ... client gnicol ... status submitted ... changeType public ... path //depot/main/application/...... desc Sed ut perspiciatis und
p4 change -o 318652 ... Change 318652 ... Date 2011/05/20 10:59:20 ... Client gnicol ... User gnicol ... Status submitted ... Description Sed ut perspiciatis unde omnis iste natus error sit voluptatem.
Accusantium doloremque laudantium.
... Type public CONNECTION HANDLING
• Connection Abstract defines base capabilities • Concrete wrappers for CLI and Native Extension • Static factory method does type selection • Supports concept of setting ‘default’ connection
• Connected Abstract • Implements [get/set/has/clear]Connection • Provides static getDefaultConnection to allow per-model defaults CONNECTION SAMPLE
$connection = P4_Connection::factory( 'perforce:1666', 'user', 'client', null );
P4_Connection::setDefaultConnection($connection); SPEC SAMPLE
// Assume default connection setup as per previous example $job = new P4_Job(); $job->setDescription('test')->save();
// Or use a manual connection $job = new P4_Job( P4_Connection::factory('other:1666', 'joe') ); $job->setValue('Description', 'test2')->save();
// will contain a partially populated version $jobs = P4_Job::fetchAll( array(P4_Job::FETCH_BY_FILTER => 'test') );
foreach($jobs as $job) { echo $job->getId(); // Pulled from partial population echo ': ' . $job->getDescription(); // Will lazy load } P4_File
• Wraps numerous perforce commands (fstat, print, attribute, etc.)
• Allows users to limit files via a Query API we provide • Limit results based on path(s) • Specify sorting options • Paginate results: max rows, row offset
• Exposes our Filter API to further limit results • Programmatic interface to fstat -F
$file = new P4_File; $file->setFilespec('//depot/small.txt') ->open() ->setLocalContents('A small file.') ->setAttribute('foo', 'small') ->submit('Add a small file.');
$file->edit() ->setLocalContents('An edited small file.') ->submit('Edit a small file');
echo P4_File::fetch('//depot/small.txt') ->getDepotContents();
----- Output ------Edit a small file SUGAR
• Pass strings or objects when passing parameters like user, group, etc. • Spec word-lists are returned as associative arrays • Mutators accept hashes or strings as input • A fluent interface is supported wherever possible • Iterator provide convenience methods such as Invoke, Filter, Search, Sort SUGAR EXAMPLES
// Pass in object or string, where appropriate $group = new P4_Group; $user = P4_User::fetch('Bob'); $group->setId('test') ->addUser($user) ->addUser('joe') ->save();
// Pass in string or hash, where appropriate $triggers = P4_Triggers::fetch(); $values = $triggers->getTriggers(); $values[] = 'test-trigger form-in //... /path/script.sh'; $values[] = array( 'name' => 'test2', 'type' => 'form-out', 'path' => '//...', 'command' => '/path/otherScript.sh' ); $triggers->setTriggers($values)->save(); Perforce CMS Record Model Overview
• Storing data in perforce can be hard, this layer should make it easy. • Provide a natural CRUD interface more in line with Active Record • Provide features such as lazy loading, fluent interface, etc. • Has the option of extension • Supports several modes of operation • Static • Object • Extended Object SIMPLE STATIC API
• store(
$record = P4Cms_Record::store( array( 'firstName' => 'Don', 'lastName' => 'Draper’ ) );
print_r($record->getValues());
----- Output ------Array( [id] => 1 [firstName] => Don [lastName] => Draper ) STORAGE LOCATION
• Storage Path is provided by the Record Adapter • We support a default adapter concept • Storage Sub-Path is, optionally, provided by the Record Model • ID is based on Record ID OBJECT MODEL
• Can instantiate record class • Provides in-memory data objects • get/setId() • get/setValues() • save() • delete() USING THE OBJECT MODEL
$peg = new P4Cms_Record; $peg->setValue('firstName', 'Peggy') ->setValue('lastName', 'Olson') ->save();
print_r($peg->getValues());
----- Output ------Array( [id] => 2 [firstName] => Peggy [lastName] => Olson ) EXTENDING THE OBJECT MODEL
• Why extend? • Specify a storage sub-path • Define defaults, known fields, id-field, file-field • Define custom field accessors, mutators • Provide access to related records • e.g. $person->getFriends() OTHER FEATURES
• Lazy-load • fetch(
• IDs & Auto-increment • IDs can be strings e.g. ‘path/to/foo.bar’ • If no ID is given, store() makes one
• Query and Filter APIs extended from Object layer
• Batches • Put modified records in a pending change • Can be committed or reverted • Faster, atomic USING THE FILTER API
$names = array('Pete', 'Betty', 'Joan', 'Roger'); foreach ($names as $name) { Person::store(array('firstName' => $name)); }
$filter = new P4Cms_Record_Filter; $filter->add('firstName', array('Betty', 'Joan')); $ladies = Person::fetchAll( new P4Cms_Record_Query(array('filter' => $filter)) );
print_r($ladies->invoke('getValue', array('firstName'));
----- Output ------Array( [0] => Betty [1] => Joan ) USING BATCHES
$people = Person::fetchAll(); $adapter = P4Cms_Record::getDefaultAdapter();
$adapter->beginBatch('Bulk Update'); $people->invoke('setValue', array('fictional', true)); $people->invoke('save'); $adapter->commitBatch();
// use the P4 layer to verify batching occured $changes = P4_Change::fetchAll(array('maximum' => 1)); print count($changes->current()->getFiles());
----- Output ------6 HOW DOES IT WORK?
• Record ‘Adapter’ hold connection object, specifies storage path, manages batches • Record IDs map to filenames • Values map to attributes • Query API leans heavily on p4 fstat • Uses our Perforce PHP Object Model CONCLUSION
• Perforce PHP Object Model • Provides deep access to perforce functionality • Removes the need to bash strings and spec forms manually • Provides a more consistent and fully detailed view of objects
• Perforce PHP Record Model • Provides a more ActiveRecord style interface to Perforce • Can be used in conjunction with Object Model Questions ???