Diff.php #1

  • //
  • guest/
  • perforce_software/
  • chronicle/
  • main/
  • library/
  • P4Cms/
  • Diff.php
  • View
  • Commits
  • Open Download .zip Download (6 KB)
<?php
/**
 * Use Paul Butler's Simple Diff Algorithm
 * https://github.com/paulgb/simplediff
 */
defined('LIBRARY_PATH')
    or define('LIBRARY_PATH', dirname(__DIR__));
require_once LIBRARY_PATH . '/simplediff/simplediff.php';

/**
 * Object oriented interface to Paul Butler's simple diff.
 *
 * @copyright   2011 Perforce Software. All rights reserved.
 * @license     Please see LICENSE.txt in top-level folder of this distribution.
 * @version     <release>/<patch>
 */
class P4Cms_Diff
{
    /**
     * Compare two values using the most appropriate comparison
     * method for the value type. If the values are of differing
     * types, the left value will be cast to the type of the right.
     *
     * @param   mixed               $left       the left-hand input
     * @param   mixed               $right      the right-hand input
     * @param   P4Cms_Diff_Options  $options    options to augment comparison behavior.
     * @return  P4Cms_Diff_Result   list of differences.
     */
    public function compare($left, $right, P4Cms_Diff_Options $options = null)
    {
        // normalize options.
        $options = !is_null($options) ? $options : new P4Cms_Diff_Options;
        
        // cast unsupported types to string.
        $types = array('array', 'string');
        $left  = !in_array(gettype($left),  $types) ? (string) $left  : $left;
        $right = !in_array(gettype($right), $types) ? (string) $right : $right;

        // ensure left/right types are the same.
        settype($left, gettype($right));
        
        // use appropriate comparison function.
        switch (gettype($right))
        {
            case 'string':
                if ($options->isBinaryDiff()) {
                    return $this->compareBinaries($left, $right, $options);
                } else {
                    return $this->compareStrings($left, $right, $options);
                }
            case 'array':
                return $this->compareArrays($left, $right, $options);
        }
    }

    /**
     * Compare two arrays.
     *
     * @param   array               $left       the left-hand array
     * @param   array               $right      the right-hand array
     * @param   P4Cms_Diff_Options  $options    options to augment comparison behavior.
     * @return  P4Cms_Diff_Result   list of differences
     */
    public function compareArrays(array $left, array $right, P4Cms_Diff_Options $options = null)
    {
        // normalize options.
        $options = !is_null($options) ? $options : new P4Cms_Diff_Options;

        // run simplediff.
        $result = diff($left, $right);

        // simplediff seems to report empty difference blocks
        // (no deletion, no insertion), filter out these artifacts.
        $artifact = array('d' => array(), 'i' => array());
        $result   = array_filter(
            $result,
            function($diff) use ($artifact)
            {
                return $diff !== $artifact;
            }
        );
        
        return new P4Cms_Diff_Result($result, $options);
    }

    /**
     * Compare two strings. Splits on lines by default.
     *
     * @param   string              $left       the left-hand string
     * @param   string              $right      the right-hand string
     * @param   P4Cms_Diff_Options  $options    options to augment comparison behavior.
     * @return  P4Cms_Diff_Result   list of differences
     */
    public function compareStrings($left, $right, P4Cms_Diff_Options $options = null)
    {
        // normalize options.
        $options = !is_null($options) ? $options : new P4Cms_Diff_Options;

        // determine what pattern and flags to use to split input strings.
        $args  = is_array($options->getSplitArgs())
            ? $options->getSplitArgs()
            : array(P4Cms_Diff_Options::PATTERN_LINES, 0);

        $left  = preg_split($args[0], $left, -1, $args[1]);
        $right = preg_split($args[0], $right, -1, $args[1]);

        return $this->compareArrays($left, $right, $options);
    }

    /**
     * Compare two binary values. Unlike compare strings, the left/right
     * values are not exploded prior to calling compareArrays().
     *
     * @param   string              $left       the left-hand binary string
     * @param   string              $right      the right-hand binary string
     * @param   P4Cms_Diff_Options  $options    options to augment comparison behavior.
     * @return  P4Cms_Diff_Result   list of differences
     */
    public function compareBinaries($left, $right, P4Cms_Diff_Options $options = null)
    {
        return $this->compareArrays(
            strlen($left)  ? array($left)  : array(),
            strlen($right) ? array($right) : array(),
            $options
        );
    }

    /**
     * Compare two fielded models. Walks over every field in each model
     * and compares the values. Puts results in a diff result collection.
     *
     * @param   P4Cms_Model                     $left       the left hand model
     * @param   P4Cms_Model                     $right      the right-hand model
     * @param   P4Cms_Diff_OptionsCollection    $options    per-field options to augment compare.
     * @return  P4Cms_Diff_ResultCollection     collection with one diff result per field.
     */
    public function compareModels(
        P4Cms_Model $left,
        P4Cms_Model $right,
        P4Cms_Diff_OptionsCollection $options = null)
    {
        // normalize options to a collection.
        $options = !is_null($options) ? $options : new P4Cms_Diff_OptionsCollection;

        // get list of all unique fields across models.
        $fields = array_unique(array_merge($left->getFields(), $right->getFields()));

        // compare each field value.
        $results = array();
        foreach ($fields as $field) {

            // normalize field options.
            $fieldOptions = isset($options[$field]) 
                ? $options[$field]
                : new P4Cms_Diff_Options;

            // skip diffing undesired fields
            if ($fieldOptions->isSkipped()) {
                continue;
            }

            // do the compare.
            $results[$field] = $this->compare(
                $left->hasField($field)  ? $left->getValue($field)  : null,
                $right->hasField($field) ? $right->getValue($field) : null,
                $fieldOptions
            );
        }

        return new P4Cms_Diff_ResultCollection($results, $options);
    }
}
# Change User Description Committed
#1 16170 perforce_software Move Chronicle files to follow new path scheme for branching.
//guest/perforce_software/chronicle/library/P4Cms/Diff.php
#1 8972 Matt Attaway Initial add of the Chronicle source code