New plugin repo is cloned to /store/pluginrepos/REPONAME for analysis

This commit is contained in:
Andrew Manning 2016-05-01 22:29:51 -04:00
parent b1ae4d776c
commit c2d15e6c3b
126 changed files with 18177 additions and 15 deletions

View File

@ -1,5 +1,8 @@
<?php
namespace Zotlabs\Module;
use PHPGit\Git as Git;
/**
* @file mod/admin.php
* @brief Hubzilla's admin controller.
@ -36,6 +39,10 @@ class Admin extends \Zotlabs\Web\Controller {
$this->admin_page_channels_post($a);
break;
case 'plugins':
if (argc() > 2 && argv(2) === 'addrepo') {
$this->admin_page_plugins_post('addrepo');
break;
}
if (argc() > 2 &&
is_file("addon/" . argv(2) . "/" . argv(2) . ".php")){
@include_once("addon/" . argv(2) . "/" . argv(2) . ".php");
@ -1329,7 +1336,7 @@ class Admin extends \Zotlabs\Web\Controller {
$admin_plugins_add_repo_form= replace_macros(
get_markup_template('admin_plugins_addrepo.tpl'), array(
'$post' => 'admin/plugins',
'$post' => 'admin/plugins/addrepo',
'$desc' => t('Enter the public git repository URL of the plugin repo.'),
'$repoURL' => array('repoURL', t('Plugin repo git URL'), '', ''),
'$submit' => t('Download Plugin Repo')
@ -1638,6 +1645,55 @@ class Admin extends \Zotlabs\Web\Controller {
));
}
function admin_page_plugins_post($action) {
switch($action) {
case 'addrepo':
if(array_key_exists('repoURL',$_REQUEST)) {
require __DIR__ . '/../../library/PHPGit.autoload.php'; // Load PHPGit dependencies
info('Repo URL submitted: ' . $_REQUEST['repoURL']);
//$git = new Git();
$repoURL = $_REQUEST['repoURL'];
//logger('hubsites: new git object created: ' . json_encode($git));
$urlpath = parse_url($repoURL, PHP_URL_PATH);
$lastslash = strrpos($urlpath, '/') + 1;
$gitext = strrpos($urlpath, '.');
if ($gitext) {
$reponame = substr($urlpath, $lastslash, $gitext - $lastslash);
} else {
logger('invalid git repo URL');
notice('Invalid git repo URL');
break;
}
$storepath = realpath(__DIR__ . '/../../store/');
logger('storepath: ' . $storepath);
$repopath = $storepath . '/pluginrepos/' . $reponame;
if (!file_exists($repopath)) {
logger('epopath does not exist');
if (mkdir($repopath, 0770, true)) {
logger('repopath created');
$git = new Git();
logger('new git object created');
$cloned = $git->clone($repoURL, $repopath);
if (!$cloned) {
logger('git clone failed');
notice('Repo coule not be cloned. Filesystem path error.');
return null;
}
} else {
logger('repopath could not be created');
notice('Repo coule not be cloned. Filesystem path error.');
return null;
}
}
}
break;
default:
break;
}
}
function admin_page_profs_post(&$a) {
if(array_key_exists('basic',$_REQUEST)) {

19
Zotlabs/Storage/Git.php Normal file
View File

@ -0,0 +1,19 @@
<?php
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
namespace Zotlabs\Storage;
require __DIR__ . '/../../library/PHPGit.autoload.php'; // Load PHPGit dependencies
/**
* Description of Git
*
* @author andrew
*/
class Git {}

View File

@ -0,0 +1,7 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer' . '/autoload_real.php';
return ComposerAutoloaderInit929ad7f4e2f44798d12d2ab06db7b39b::getLoader();

View File

@ -0,0 +1,413 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0 class loader
*
* See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class ClassLoader
{
// PSR-4
private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
private $fallbackDirsPsr4 = array();
// PSR-0
private $prefixesPsr0 = array();
private $fallbackDirsPsr0 = array();
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoritative = false;
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
}
return array();
}
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-0 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
/**
* Unregisters this instance as an autoloader.
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
if ('\\' == $class[0]) {
$class = substr($class, 1);
}
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative) {
return false;
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if ($file === null && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if ($file === null) {
// Remember that this class does not exist.
return $this->classMap[$class] = false;
}
return $file;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
if (0 === strpos($class, $prefix)) {
foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
if (is_file($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}

View File

@ -0,0 +1,9 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

View File

@ -0,0 +1,11 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'PHPGit' => array($baseDir . '/src'),
'' => array($vendorDir . '/kzykhys/git/src'),
);

View File

@ -0,0 +1,11 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Symfony\\Component\\Process\\' => array($vendorDir . '/symfony/process'),
'Symfony\\Component\\OptionsResolver\\' => array($vendorDir . '/symfony/options-resolver'),
);

View File

@ -0,0 +1,50 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit929ad7f4e2f44798d12d2ab06db7b39b
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit929ad7f4e2f44798d12d2ab06db7b39b', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit929ad7f4e2f44798d12d2ab06db7b39b', 'loadClassLoader'));
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
$loader->register(true);
return $loader;
}
}
function composerRequire929ad7f4e2f44798d12d2ab06db7b39b($file)
{
require $file;
}

View File

@ -0,0 +1,157 @@
[
{
"name": "symfony/process",
"version": "v2.8.0",
"version_normalized": "2.8.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "1b988a88e3551102f3c2d9e1d47a18c3a78d6312"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/1b988a88e3551102f3c2d9e1d47a18c3a78d6312",
"reference": "1b988a88e3551102f3c2d9e1d47a18c3a78d6312",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
},
"time": "2015-11-30 12:35:10",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.8-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Symfony\\Component\\Process\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Process Component",
"homepage": "https://symfony.com"
},
{
"name": "symfony/options-resolver",
"version": "v2.8.0",
"version_normalized": "2.8.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/options-resolver.git",
"reference": "e7f62cf7d9e48238299cfa5d0556aee8cff1e075"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/e7f62cf7d9e48238299cfa5d0556aee8cff1e075",
"reference": "e7f62cf7d9e48238299cfa5d0556aee8cff1e075",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
},
"time": "2015-11-18 13:45:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.8-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Symfony\\Component\\OptionsResolver\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony OptionsResolver Component",
"homepage": "https://symfony.com",
"keywords": [
"config",
"configuration",
"options"
]
},
{
"name": "kzykhys/git",
"version": "dev-master",
"version_normalized": "9999999-dev",
"source": {
"type": "git",
"url": "https://github.com/kzykhys/PHPGit.git",
"reference": "8b3ee3147d58e0aee62610f0aebcd30eb076ad5d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/kzykhys/PHPGit/zipball/8b3ee3147d58e0aee62610f0aebcd30eb076ad5d",
"reference": "8b3ee3147d58e0aee62610f0aebcd30eb076ad5d",
"shasum": ""
},
"require": {
"php": ">=5.3.2",
"symfony/options-resolver": ">=2.3",
"symfony/process": ">=2.3"
},
"require-dev": {
"symfony/filesystem": ">=2.3"
},
"time": "2014-02-27 13:14:59",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"installation-source": "source",
"autoload": {
"psr-0": {
"": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Kazuyuki Hayashi",
"email": "hayashi@valnur.net"
}
],
"description": "A Git wrapper for PHP5.3+"
}
]

View File

@ -0,0 +1,20 @@
language: php
php:
- 5.5
- 5.4
- 5.3
before_script:
- composer require --no-update satooshi/php-coveralls:dev-master@dev sensiolabs/security-checker:dev-master
- composer update --dev --no-interaction
- git config --global user.name "John Doe"
- git config --global user.email "example@example.com"
script:
- mkdir -p build/logs
- phpunit -c phpunit.xml.dist --coverage-clover build/logs/clover.xml
- php vendor/bin/security-checker security:check composer.lock
after_script:
- php vendor/bin/coveralls

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- http://www.phpunit.de/manual/current/en/appendixes.configuration.html -->
<phpunit
backupGlobals = "false"
backupStaticAttributes = "false"
colors = "true"
convertErrorsToExceptions = "true"
convertNoticesToExceptions = "true"
convertWarningsToExceptions = "true"
processIsolation = "false"
stopOnFailure = "false"
syntaxCheck = "false"
bootstrap = "vendor/autoload.php" >
<testsuites>
<testsuite name="Project Test Suite">
<directory>test</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>src</directory>
</whitelist>
</filter>
</phpunit>

View File

@ -0,0 +1,123 @@
<?php
namespace PHPGit;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Process\ProcessBuilder;
/**
* Base class for git commands
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
abstract class Command
{
/**
* @var Git
*/
protected $git;
/**
* @param Git $git
*/
public function __construct(Git $git)
{
$this->git = $git;
}
/**
* Returns the combination of the default and the passed options
*
* @param array $options An array of options
*
* @return array
*/
public function resolve(array $options = array())
{
$resolver = new OptionsResolver();
$this->setDefaultOptions($resolver);
return $resolver->resolve($options);
}
/**
* Sets the default options
*
* @param OptionsResolverInterface $resolver The resolver for the options
*
* @codeCoverageIgnore
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
}
/**
* Split string by new line or null(\0)
*
* @param string $input The string to split
* @param bool $useNull True to split by new line, otherwise null
*
* @return array
*/
protected function split($input, $useNull = false)
{
if ($useNull) {
$pattern = '/\0/';
} else {
$pattern = '/\r?\n/';
}
return preg_split($pattern, rtrim($input), -1, PREG_SPLIT_NO_EMPTY);
}
/**
* Adds boolean options to command arguments
*
* @param ProcessBuilder $builder A ProcessBuilder object
* @param array $options An array of options
* @param array $optionNames The names of options to add
*/
protected function addFlags(ProcessBuilder $builder, array $options = array(), array $optionNames = null)
{
if ($optionNames) {
foreach ($optionNames as $name) {
if (isset($options[$name]) && is_bool($options[$name]) && $options[$name]) {
$builder->add('--' . $name);
}
}
} else {
foreach ($options as $name => $option) {
if ($option) {
$builder->add('--' . $name);
}
}
}
}
/**
* Adds options with values to command arguments
*
* @param ProcessBuilder $builder A ProcessBuilder object
* @param array $options An array of options
* @param array $optionNames The names of options to add
*/
protected function addValues(ProcessBuilder $builder, array $options = array(), array $optionNames = null)
{
if ($optionNames) {
foreach ($optionNames as $name) {
if (isset($options[$name]) && $options[$name]) {
$builder->add('--' . $name . '=' . $options[$name]);
}
}
} else {
foreach ($options as $name => $option) {
if ($option) {
$builder->add('--' . $name . '=' . $option);
}
}
}
}
}

View File

@ -0,0 +1,75 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Exception\GitException;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Add file contents to the index - `git add`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class AddCommand extends Command
{
/**
* Add file contents to the index
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->add('file.txt');
* $git->add('file.txt', ['force' => false, 'ignore-errors' => false);
* ```
*
* ##### Options
*
* - **force** (_boolean_) Allow adding otherwise ignored files
* - **ignore-errors** (_boolean_) Do not abort the operation
*
* @param string|array|\Traversable $file Files to add content from
* @param array $options [optional] An array of options {@see AddCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function __invoke($file, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('add');
$this->addFlags($builder, $options);
if (!is_array($file) && !($file instanceof \Traversable)) {
$file = array($file);
}
foreach ($file as $value) {
$builder->add($value);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*
* - **force** (_boolean_) Allow adding otherwise ignored files
* - **ignore-errors** (_boolean_) Do not abort the operation
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
//'dry-run' => false,
'force' => false,
'ignore-errors' => false,
//'ignore-missing' => false,
));
}
}

View File

@ -0,0 +1,96 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Exception\GitException;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Create an archive of files from a named tree - `git archive`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class ArchiveCommand extends Command
{
/**
* Create an archive of files from a named tree
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->archive('repo.zip', 'master', null, ['format' => 'zip']);
* ```
*
* ##### Options
*
* - **format** (_boolean_) Format of the resulting archive: tar or zip
* - **prefix** (_boolean_) Prepend prefix/ to each filename in the archive
*
* @param string $file The filename
* @param string $tree [optional] The tree or commit to produce an archive for
* @param string|array|\Traversable $path [optional] If one or more paths are specified, only these are included
* @param array $options [optional] An array of options {@see ArchiveCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function __invoke($file, $tree = null, $path = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('archive');
if ($options['format']) {
$builder->add('--format=' . $options['format']);
}
if ($options['prefix']) {
$builder->add('--prefix=' . $options['prefix']);
}
$builder->add('-o')->add($file);
if ($tree) {
$builder->add($tree);
}
if (!is_array($path) && !($path instanceof \Traversable)) {
$path = array($path);
}
foreach ($path as $value) {
$builder->add($value);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*
* - **format** (_boolean_) Format of the resulting archive: tar or zip
* - **prefix** (_boolean_) Prepend prefix/ to each filename in the archive
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'format' => null,
'prefix' => null
));
$resolver->setAllowedTypes(array(
'format' => array('null', 'string'),
'prefix' => array('null', 'string')
));
$resolver->setAllowedValues(array(
'format' => array('tar', 'zip')
));
}
}

View File

@ -0,0 +1,229 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Exception\GitException;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* List, create, or delete branches - `git branch`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class BranchCommand extends Command
{
/**
* Returns an array of both remote-tracking branches and local branches
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $branches = $git->branch();
* ```
*
* ##### Output Example
*
* ```
* [
* 'master' => ['current' => true, 'name' => 'master', 'hash' => 'bf231bb', 'title' => 'Initial Commit'],
* 'origin/master' => ['current' => false, 'name' => 'origin/master', 'alias' => 'remotes/origin/master']
* ]
* ```
*
* ##### Options
*
* - **all** (_boolean_) List both remote-tracking branches and local branches
* - **remotes** (_boolean_) List the remote-tracking branches
*
* @param array $options [optional] An array of options {@see BranchCommand::setDefaultOptions}
*
* @throws GitException
* @return array
*/
public function __invoke(array $options = array())
{
$options = $this->resolve($options);
$branches = array();
$builder = $this->getProcessBuilder()
->add('-v')->add('--abbrev=7');
if ($options['remotes']) {
$builder->add('--remotes');
}
if ($options['all']) {
$builder->add('--all');
}
$process = $builder->getProcess();
$this->git->run($process);
$lines = preg_split('/\r?\n/', rtrim($process->getOutput()), -1, PREG_SPLIT_NO_EMPTY);
foreach ($lines as $line) {
$branch = array();
preg_match('/(?<current>\*| ) (?<name>[^\s]+) +((?:->) (?<alias>[^\s]+)|(?<hash>[0-9a-z]{7}) (?<title>.*))/', $line, $matches);
$branch['current'] = ($matches['current'] == '*');
$branch['name'] = $matches['name'];
if (isset($matches['hash'])) {
$branch['hash'] = $matches['hash'];
$branch['title'] = $matches['title'];
} else {
$branch['alias'] = $matches['alias'];
}
$branches[$matches['name']] = $branch;
}
return $branches;
}
/**
* Creates a new branch head named **$branch** which points to the current HEAD, or **$startPoint** if given
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->branch->create('bugfix'); // from current HEAD
* $git->branch->create('patch-1', 'a092bf7s'); // from commit
* $git->branch->create('1.0.x-fix', 'v1.0.2'); // from tag
* ```
*
* ##### Options
*
* - **force** (_boolean_) Reset **$branch** to **$startPoint** if **$branch** exists already
*
* @param string $branch The name of the branch to create
* @param string $startPoint [optional] The new branch head will point to this commit.
* It may be given as a branch name, a commit-id, or a tag.
* If this option is omitted, the current HEAD will be used instead.
* @param array $options [optional] An array of options {@see BranchCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function create($branch, $startPoint = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->getProcessBuilder();
if ($options['force']) {
$builder->add('-f');
}
$builder->add($branch);
if ($startPoint) {
$builder->add($startPoint);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* Move/rename a branch and the corresponding reflog
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->branch->move('bugfix', '2.0');
* ```
*
* ##### Options
*
* - **force** (_boolean_) Move/rename a branch even if the new branch name already exists
*
* @param string $branch The name of an existing branch to rename
* @param string $newBranch The new name for an existing branch
* @param array $options [optional] An array of options {@see BranchCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function move($branch, $newBranch, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->getProcessBuilder();
if ($options['force']) {
$builder->add('-M');
} else {
$builder->add('-m');
}
$builder->add($branch)->add($newBranch);
$this->git->run($builder->getProcess());
return true;
}
/**
* Delete a branch
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->branch->delete('2.0');
* ```
*
* The branch must be fully merged in its upstream branch, or in HEAD if no upstream was set with --track or --set-upstream.
*
* ##### Options
*
* - **force** (_boolean_) Delete a branch irrespective of its merged status
*
* @param string $branch The name of the branch to delete
* @param array $options [optional] An array of options {@see BranchCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function delete($branch, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->getProcessBuilder();
if ($options['force']) {
$builder->add('-D');
} else {
$builder->add('-d');
}
$builder->add($branch);
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*
* - **force** (_boolean_) Reset <branchname> to <startpoint> if <branchname> exists already
* - **all** (_boolean_) List both remote-tracking branches and local branches
* - **remotes** (_boolean_) List or delete (if used with delete()) the remote-tracking branches
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'force' => false,
'all' => false,
'remotes' => false,
));
}
/**
* @return \Symfony\Component\Process\ProcessBuilder
*/
protected function getProcessBuilder()
{
return $this->git->getProcessBuilder()
->add('branch');
}
}

View File

@ -0,0 +1,91 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Exception\GitException;
/**
* Provide content or type and size information for repository objects - `git cat-file`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class CatCommand extends Command
{
/**
* Returns the contents of blob object
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $contents = $git->cat->blob('e69de29bb2d1d6434b8b29ae775ad8');
* ```
*
* @param string $object The name of the blob object to show
*
* @throws GitException
* @return string
*/
public function blob($object)
{
$process = $this->git->getProcessBuilder()
->add('cat-file')
->add('blob')
->add($object)
->getProcess();
return $this->git->run($process);
}
/**
* Returns the object type identified by **$object**
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $type = $git->cat->type('e69de29bb2d1d6434b8b29ae775ad8');
* ```
*
* @param string $object The name of the object to show
*
* @throws GitException
* @return string
*/
public function type($object)
{
$process = $this->git->getProcessBuilder()
->add('cat-file')
->add('-t')
->add($object)
->getProcess();
return trim($this->git->run($process));
}
/**
* Returns the object size identified by **$object**
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $type = $git->cat->size('e69de29bb2d1d6434b8b29ae775ad8');
* ```
*
* @param string $object The name of the object to show
*
* @throws GitException
* @return string
*/
public function size($object)
{
$process = $this->git->getProcessBuilder()
->add('cat-file')
->add('-s')
->add($object)
->getProcess();
return trim($this->git->run($process));
}
}

View File

@ -0,0 +1,145 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Exception\GitException;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Checkout a branch or paths to the working tree - `git checkout`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class CheckoutCommand extends Command
{
/**
* Switches branches by updating the index, working tree, and HEAD to reflect the specified branch or commit
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->checkout('develop');
* ```
*
* ##### Options
*
* - **force** (_boolean_) Proceed even if the index or the working tree differs from HEAD
* - **merge** (_boolean_) Merges local modification
*
* @param string $branch Branch to checkout
* @param array $options [optional] An array of options {@see CheckoutCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function __invoke($branch, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('checkout');
$this->addFlags($builder, $options, array('force', 'merge'));
$builder->add($branch);
$this->git->run($builder->getProcess());
return true;
}
/**
* Create a new branch and checkout
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->checkout->create('patch-1');
* $git->checkout->create('patch-2', 'develop');
* ```
*
* ##### Options
*
* - **force** (_boolean_) Proceed even if the index or the working tree differs from HEAD
*
* @param string $branch Branch to checkout
* @param string $startPoint The name of a commit at which to start the new branch
* @param array $options [optional] An array of options {@see CheckoutCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function create($branch, $startPoint = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('checkout')
->add('-b');
$this->addFlags($builder, $options, array('force', 'merge'));
$builder->add($branch);
if ($startPoint) {
$builder->add($startPoint);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* Create a new orphan branch, named <new_branch>, started from <start_point> and switch to it
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->checkout->orphan('gh-pages');
* ```
*
* ##### Options
*
* - **force** (_boolean_) Proceed even if the index or the working tree differs from HEAD
*
* @param string $branch Branch to checkout
* @param string $startPoint [optional] The name of a commit at which to start the new branch
* @param array $options [optional] An array of options {@see CheckoutCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function orphan($branch, $startPoint = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('checkout');
$this->addFlags($builder, $options, array('force', 'merge'));
$builder->add('--orphan')->add($branch);
if ($startPoint) {
$builder->add($startPoint);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*
* - **force** (_boolean_) Proceed even if the index or the working tree differs from HEAD
* - **merge** (_boolean_) Merges local modification
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'force' => false,
'merge' => false
));
}
}

View File

@ -0,0 +1,71 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Exception\GitException;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Clone a repository into a new directory - `git clone`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class CloneCommand extends Command
{
/**
* Clone a repository into a new directory
*
* ``` php
* $git = new PHPGit\Git();
* $git->clone('https://github.com/kzykhys/PHPGit.git', '/path/to/repo');
* ```
*
* ##### Options
*
* - **shared** (_boolean_) Starts out without any object of its own
* - **bare** (_boolean_) Make a bare GIT repository
*
* @param string $repository The repository to clone from
* @param string $path [optional] The name of a new directory to clone into
* @param array $options [optional] An array of options {@see CloneCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function __invoke($repository, $path = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('clone')
->add('--quiet');
$this->addFlags($builder, $options);
$builder->add($repository);
if ($path) {
$builder->add($path);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*
* - **shared** (_boolean_) Starts out without any object of its own
* - **bare** (_boolean_) Make a bare GIT repository
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'shared' => false,
'bare' => false
));
}
}

View File

@ -0,0 +1,87 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Exception\GitException;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Record changes to the repository - `git commit`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class CommitCommand extends Command
{
/**
* Record changes to the repository
*
* ``` php
* $git = new PHPGit\Git();
* $git->clone('https://github.com/kzykhys/PHPGit.git', '/path/to/repo');
* $git->setRepository('/path/to/repo');
* $git->add('README.md');
* $git->commit('Fixes README.md');
* ```
*
* ##### Options
*
* - **all** (_boolean_) Stage files that have been modified and deleted
* - **reuse-message** (_string_) Take an existing commit object, and reuse the log message and the authorship information (including the timestamp) when creating the commit
* - **squash** (_string_) Construct a commit message for use with rebase --autosquash
* - **author** (_string_) Override the commit author
* - **date** (_string_) Override the author date used in the commit
* - **cleanup** (_string_) Can be one of verbatim, whitespace, strip, and default
* - **amend** (_boolean_) Used to amend the tip of the current branch
*
* @param string $message Use the given <$msg> as the commit message
* @param array $options [optional] An array of options {@see CloneCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function __invoke($message, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('commit')
->add('-m')->add($message);
$this->addFlags($builder, $options, array('all', 'amend'));
$this->addValues($builder, $options, array('reuse-message', 'squash', 'author', 'date', 'cleanup'));
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*
* - **all** (_boolean_) Stage files that have been modified and deleted
* - **reuse-message** (_string_) Take an existing commit object, and reuse the log message and the authorship information (including the timestamp) when creating the commit
* - **squash** (_string_) Construct a commit message for use with rebase --autosquash
* - **author** (_string_) Override the commit author
* - **date** (_string_) Override the author date used in the commit
* - **cleanup** (_string_) Can be one of verbatim, whitespace, strip, and default
* - **amend** (_boolean_) Used to amend the tip of the current branch
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'all' => false,
'reuse-message' => null,
'squash' => null,
'author' => null,
'date' => null,
'cleanup' => null,
'amend' => false
));
$resolver->setAllowedValues(array(
'cleanup' => array(null, 'default', 'verbatim', 'whitespace', 'strip')
));
}
}

View File

@ -0,0 +1,132 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Exception\GitException;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Get and set repository or global options - `git config`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class ConfigCommand extends Command
{
/**
* Returns all variables set in config file
*
*
* ##### Options
*
* - **global** (_boolean_) Read or write configuration options for the current user
* - **system** (_boolean_) Read or write configuration options for all users on the current machine
*
* @param array $options [optional] An array of options {@see ConfigCommand::setDefaultOptions}
*
* @throws GitException
* @return array
*/
public function __invoke(array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('config')
->add('--list')
->add('--null');
$this->addFlags($builder, $options, array('global', 'system'));
$config = array();
$output = $this->git->run($builder->getProcess());
$lines = $this->split($output, true);
foreach ($lines as $line) {
list($name, $value) = explode("\n", $line, 2);
if (isset($config[$name])) {
$config[$name] .= "\n" . $value;
} else {
$config[$name] = $value;
}
}
return $config;
}
/**
* Set an option
*
* ##### Options
*
* - **global** (_boolean_) Read or write configuration options for the current user
* - **system** (_boolean_) Read or write configuration options for all users on the current machine
*
* @param string $name The name of the option
* @param string $value The value to set
* @param array $options [optional] An array of options {@see ConfigCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function set($name, $value, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('config');
$this->addFlags($builder, $options, array('global', 'system'));
$builder->add($name)->add($value);
$process = $builder->getProcess();
$this->git->run($process);
return true;
}
/**
* Adds a new line to the option without altering any existing values
*
* ##### Options
*
* - **global** (_boolean_) Read or write configuration options for the current user
* - **system** (_boolean_) Read or write configuration options for all users on the current machine
*
* @param string $name The name of the option
* @param string $value The value to add
* @param array $options [optional] An array of options {@see ConfigCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function add($name, $value, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('config');
$this->addFlags($builder, $options, array('global', 'system'));
$builder->add('--add')->add($name)->add($value);
$process = $builder->getProcess();
$this->git->run($process);
return true;
}
/**
* {@inheritdoc}
*
* - **global** (_boolean_) Read or write configuration options for the current user
* - **system** (_boolean_) Read or write configuration options for all users on the current machine
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'global' => false,
'system' => false,
));
}
}

View File

@ -0,0 +1,90 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Show the most recent tag that is reachable from a commit - `git describe`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class DescribeCommand extends Command
{
/**
* Returns the most recent tag that is reachable from a commit
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->tag->create('v1.0.0');
* $git->commit('Fixes #14');
* echo $git->describe('HEAD', ['tags' => true]);
* ```
*
* ##### Output Example
*
* ```
* v1.0.0-1-g7049efc
* ```
*
* ##### Options
*
* - **all** (_boolean_) Enables matching any known branch, remote-tracking branch, or lightweight tag
* - **tags** (_boolean_) Enables matching a lightweight (non-annotated) tag
* - **always** (_boolean_) Show uniquely abbreviated commit object as fallback
*
* @param string $committish [optional] Committish object names to describe.
* @param array $options [optional] An array of options {@see DescribeCommand::setDefaultOptions}
*
* @return string
*/
public function __invoke($committish = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('describe');
$this->addFlags($builder, $options, array());
if ($committish) {
$builder->add($committish);
}
return trim($this->git->run($builder->getProcess()));
}
/**
* Equivalent to $git->describe($committish, ['tags' => true]);
*
* @param string $committish [optional] Committish object names to describe.
* @param array $options [optional] An array of options {@see DescribeCommand::setDefaultOptions}
*
* @return string
*/
public function tags($committish = null, array $options = array())
{
$options['tags'] = true;
return $this->__invoke($committish, $options);
}
/**
* {@inheritdoc}
*
* - **all** (_boolean_) Enables matching any known branch, remote-tracking branch, or lightweight tag
* - **tags** (_boolean_) Enables matching a lightweight (non-annotated) tag
* - **always** (_boolean_) Show uniquely abbreviated commit object as fallback
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'all' => false,
'tags' => false,
'always' => false,
));
}
}

View File

@ -0,0 +1,112 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Exception\GitException;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Download objects and refs from another repository - `git fetch`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class FetchCommand extends Command
{
/**
* Fetches named heads or tags from one or more other repositories, along with the objects necessary to complete them
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'git://your/repo.git');
* $git->fetch('origin');
* ```
*
* ##### Options
*
* - **append** (_boolean_) Append ref names and object names of fetched refs to the existing contents of .git/FETCH_HEAD
* - **keep** (_boolean_) Keep downloaded pack
* - **prune** (_boolean_) After fetching, remove any remote-tracking branches which no longer exist on the remote
*
* @param string $repository The "remote" repository that is the source of a fetch or pull operation
* @param string $refspec The format of a <refspec> parameter is an optional plus +, followed by the source ref <src>,
* followed by a colon :, followed by the destination ref <dst>
* @param array $options [optional] An array of options {@see FetchCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function __invoke($repository, $refspec = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('fetch');
$this->addFlags($builder, $options);
$builder->add($repository);
if ($refspec) {
$builder->add($refspec);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* Fetch all remotes
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'git://your/repo.git');
* $git->remote->add('release', 'git://your/another_repo.git');
* $git->fetch->all();
* ```
*
* ##### Options
*
* - **append** (_boolean_) Append ref names and object names of fetched refs to the existing contents of .git/FETCH_HEAD
* - **keep** (_boolean_) Keep downloaded pack
* - **prune** (_boolean_) After fetching, remove any remote-tracking branches which no longer exist on the remote
*
* @param array $options [optional] An array of options {@see FetchCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function all(array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('fetch')
->add('--all');
$this->addFlags($builder, $options);
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*
* - **append** (_boolean_) Append ref names and object names of fetched refs to the existing contents of .git/FETCH_HEAD
* - **keep** (_boolean_) Keep downloaded pack
* - **prune** (_boolean_) After fetching, remove any remote-tracking branches which no longer exist on the remote
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'append' => false,
//'force' => false,
'keep' => false,
'prune' => false,
));
}
}

View File

@ -0,0 +1,65 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Exception\GitException;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Create an empty git repository or reinitialize an existing one - `git init`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class InitCommand extends Command
{
/**
* Create an empty git repository or reinitialize an existing one
*
* ``` php
* $git = new PHPGit\Git();
* $git->init('/path/to/repo1');
* $git->init('/path/to/repo2', array('shared' => true, 'bare' => true));
* ```
*
* ##### Options
*
* - **shared** (_boolean_) Specify that the git repository is to be shared amongst several users
* - **bare** (_boolean_) Create a bare repository
*
* @param string $path The directory to create an empty repository
* @param array $options [optional] An array of options {@see InitCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function __invoke($path, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('init');
$this->addFlags($builder, $options, array('shared', 'bare'));
$process = $builder->add($path)->getProcess();
$this->git->run($process);
return true;
}
/**
* {@inheritdoc}
*
* - **shared** (_boolean_) Specify that the git repository is to be shared amongst several users
* - **bare** (_boolean_) Create a bare repository
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'shared' => false,
'bare' => false
));
}
}

View File

@ -0,0 +1,105 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Exception\GitException;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Show commit logs - `git log`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class LogCommand extends Command
{
/**
* Returns the commit logs
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $logs = $git->log(array('limit' => 10));
* ```
*
* ##### Output Example
*
* ``` php
* [
* 0 => [
* 'hash' => '1a821f3f8483747fd045eb1f5a31c3cc3063b02b',
* 'name' => 'John Doe',
* 'email' => 'john@example.com',
* 'date' => 'Fri Jan 17 16:32:49 2014 +0900',
* 'title' => 'Initial Commit'
* ],
* 1 => [
* //...
* ]
* ]
* ```
*
* ##### Options
*
* - **limit** (_integer_) Limits the number of commits to show
* - **skip** (_integer_) Skip number commits before starting to show the commit output
*
* @param string $revRange [optional] Show only commits in the specified revision range
* @param string $path [optional] Show only commits that are enough to explain how the files that match the specified paths came to be
* @param array $options [optional] An array of options {@see LogCommand::setDefaultOptions}
*
* @throws GitException
* @return array
*/
public function __invoke($revRange = '', $path = null, array $options = array())
{
$commits = array();
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('log')
->add('-n')->add($options['limit'])
->add('--skip=' . $options['skip'])
->add('--format=%H||%aN||%aE||%aD||%s');
if ($revRange) {
$builder->add($revRange);
}
if ($path) {
$builder->add('--')->add($path);
}
$output = $this->git->run($builder->getProcess());
$lines = $this->split($output);
foreach ($lines as $line) {
list($hash, $name, $email, $date, $title) = preg_split('/\|\|/', $line, -1, PREG_SPLIT_NO_EMPTY);
$commits[] = array(
'hash' => $hash,
'name' => $name,
'email' => $email,
'date' => $date,
'title' => $title
);
}
return $commits;
}
/**
* {@inheritdoc}
*
* - **limit** (_integer_) Limits the number of commits to show
* - **skip** (_integer_) Skip number commits before starting to show the commit output
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'limit' => 10,
'skip' => 0
));
}
}

View File

@ -0,0 +1,110 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Exception\GitException;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Join two or more development histories together - `git merge`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class MergeCommand extends Command
{
/**
* Incorporates changes from the named commits into the current branch
*
* ```php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->merge('1.0');
* $git->merge('1.1', 'Merge message', ['strategy' => 'ours']);
* ```
*
* ##### Options
*
* - **no-ff** (_boolean_) Do not generate a merge commit if the merge resolved as a fast-forward, only update the branch pointer
* - **rerere-autoupdate** (_boolean_) Allow the rerere mechanism to update the index with the result of auto-conflict resolution if possible
* - **squash** (_boolean_) Allows you to create a single commit on top of the current branch whose effect is the same as merging another branch
* - **strategy** (_string_) Use the given merge strategy
* - **strategy-option** (_string_) Pass merge strategy specific option through to the merge strategy
*
* @param string|array|\Traversable $commit Commits to merge into our branch
* @param string $message [optional] Commit message to be used for the merge commit
* @param array $options [optional] An array of options {@see MergeCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function __invoke($commit, $message = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('merge');
$this->addFlags($builder, $options, array('no-ff', 'rerere-autoupdate', 'squash'));
if (!is_array($commit) && !($commit instanceof \Traversable)) {
$commit = array($commit);
}
foreach ($commit as $value) {
$builder->add($value);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* Abort the merge process and try to reconstruct the pre-merge state
*
* ```php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* try {
* $git->merge('dev');
* } catch (PHPGit\Exception\GitException $e) {
* $git->merge->abort();
* }
* ```
*
* @throws GitException
* @return bool
*/
public function abort()
{
$builder = $this->git->getProcessBuilder()
->add('merge')
->add('--abort');
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*
* - **no-ff** (_boolean_) Do not generate a merge commit if the merge resolved as a fast-forward, only update the branch pointer
* - **rerere-autoupdate** (_boolean_) Allow the rerere mechanism to update the index with the result of auto-conflict resolution if possible
* - **squash** (_boolean_) Allows you to create a single commit on top of the current branch whose effect is the same as merging another branch
* - **strategy** (_string_) Use the given merge strategy
* - **strategy-option** (_string_) Pass merge strategy specific option through to the merge strategy
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'no-ff' => false,
'rerere-autoupdate' => false,
'squash' => false,
'strategy' => null,
'strategy-option' => null
));
}
}

View File

@ -0,0 +1,70 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Move or rename a file, a directory, or a symlink - `git mv`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class MvCommand extends Command
{
/**
* Move or rename a file, a directory, or a symlink
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->mv('UPGRADE-1.0.md', 'UPGRADE-1.1.md');
* ```
*
* ##### Options
*
* - **force** (_boolean_) Force renaming or moving of a file even if the target exists
*
* @param string|array|\Iterator $source The files to move
* @param string $destination The destination
* @param array $options [optional] An array of options {@see MvCommand::setDefaultOptions}
*
* @return bool
*/
public function __invoke($source, $destination, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('mv');
$this->addFlags($builder, $options, array('force'));
if (!is_array($source) && !($source instanceof \Traversable)) {
$source = array($source);
}
foreach ($source as $value) {
$builder->add($value);
}
$builder->add($destination);
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*
* - **force** (_boolean_) Force renaming or moving of a file even if the target exists
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'force' => false
));
}
}

View File

@ -0,0 +1,59 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Fetch from and merge with another repository or a local branch - `git pull`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class PullCommand extends Command
{
/**
* Fetch from and merge with another repository or a local branch
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->pull('origin', 'master');
* ```
*
* @param string $repository The "remote" repository that is the source of a fetch or pull operation
* @param string $refspec The format of a <refspec> parameter is an optional plus +,
* followed by the source ref <src>, followed by a colon :, followed by the destination ref <dst>
* @param array $options [optional] An array of options {@see PullCommand::setDefaultOptions}
*
* @return bool
*/
public function __invoke($repository = null, $refspec = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('pull');
if ($repository) {
$builder->add($repository);
if ($refspec) {
$builder->add($refspec);
}
}
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
}
}

View File

@ -0,0 +1,65 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Update remote refs along with associated objects - `git push`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class PushCommand extends Command
{
/**
* Update remote refs along with associated objects
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->push('origin', 'master');
* ```
*
* @param string $repository The "remote" repository that is destination of a push operation
* @param string $refspec Specify what destination ref to update with what source object
* @param array $options [optional] An array of options {@see PushCommand::setDefaultOptions}
*
* @return bool
*/
public function __invoke($repository = null, $refspec = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('push');
$this->addFlags($builder, $options);
if ($repository) {
$builder->add($repository);
if ($refspec) {
$builder->add($refspec);
}
}
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'all' => false,
'mirror' => false,
'tags' => false,
'force' => false
));
}
}

View File

@ -0,0 +1,129 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Forward-port local commits to the updated upstream head - `git rebase`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class RebaseCommand extends Command
{
/**
* Forward-port local commits to the updated upstream head
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->fetch('origin');
* $git->rebase('origin/master');
* ```
*
* ##### Options
*
* - **onto** (_string_) Starting point at which to create the new commits
* - **no-verify** (_boolean_) Bypasses the pre-rebase hook
* - **force-rebase** (_boolean_) Force the rebase even if the current branch is a descendant of the commit you are rebasing onto
*
* @param string $upstream [optional] Upstream branch to compare against
* @param string $branch [optional] Working branch; defaults to HEAD
* @param array $options [optional] An array of options {@see RebaseCommand::setDefaultOptions}
*
* @return bool
*/
public function __invoke($upstream = null, $branch = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('rebase');
if ($options['onto']) {
$builder->add('--onto')->add($options['onto']);
}
if ($upstream) {
$builder->add($upstream);
}
if ($branch) {
$builder->add($branch);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* Restart the rebasing process after having resolved a merge conflict
*
* @return bool
*/
public function continues()
{
$builder = $this->git->getProcessBuilder()
->add('rebase')
->add('--continue');
$this->git->run($builder->getProcess());
return true;
}
/**
* Abort the rebase operation and reset HEAD to the original branch
*
* @return bool
*/
public function abort()
{
$builder = $this->git->getProcessBuilder()
->add('rebase')
->add('--abort');
$this->git->run($builder->getProcess());
return true;
}
/**
* Restart the rebasing process by skipping the current patch
*
* @return bool
*/
public function skip()
{
$builder = $this->git->getProcessBuilder()
->add('rebase')
->add('--skip');
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*
* - **onto** (_string_) Starting point at which to create the new commits
* - **no-verify** (_boolean_) Bypasses the pre-rebase hook
* - **force-rebase** (_boolean_) Force the rebase even if the current branch is a descendant of the commit you are rebasing onto
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'onto' => null,
'no-verify' => false,
'force-rebase' => false
));
$resolver->setAllowedTypes(array(
'onto' => array('null', 'string')
));
}
}

View File

@ -0,0 +1,98 @@
<?php
namespace PHPGit\Command\Remote;
use PHPGit\Command;
/**
* Changes the list of branches tracked by the named remote
*
* @author Kazuyuki Hayashi
*/
class SetBranchesCommand extends Command
{
/**
* Alias of set()
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
* $git->remote->branches('origin', array('master', 'develop'));
* ```
*
* @param string $name The remote name
* @param array $branches The names of the tracked branch
*
* @return bool
*/
public function __invoke($name, array $branches)
{
return $this->set($name, $branches);
}
/**
* Changes the list of branches tracked by the named remote
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
* $git->remote->branches->set('origin', array('master', 'develop'));
* ```
*
* @param string $name The remote name
* @param array $branches The names of the tracked branch
*
* @return bool
*/
public function set($name, array $branches)
{
$builder = $this->git->getProcessBuilder()
->add('remote')
->add('set-branches')
->add($name);
foreach ($branches as $branch) {
$builder->add($branch);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* Adds to the list of branches tracked by the named remote
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
* $git->remote->branches->add('origin', array('master', 'develop'));
* ```
*
* @param string $name The remote name
* @param array $branches The names of the tracked branch
*
* @return bool
*/
public function add($name, array $branches)
{
$builder = $this->git->getProcessBuilder()
->add('remote')
->add('set-branches')
->add($name)
->add('--add');
foreach ($branches as $branch) {
$builder->add($branch);
}
$this->git->run($builder->getProcess());
return true;
}
}

View File

@ -0,0 +1,120 @@
<?php
namespace PHPGit\Command\Remote;
use PHPGit\Command;
/**
* Sets or deletes the default branch (i.e. the target of the symbolic-ref refs/remotes/<name>/HEAD) for the named remote
*
* @author Kazuyuki Hayashi
*/
class SetHeadCommand extends Command
{
/**
* Alias of set()
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
* $git->remote->head('origin');
* ```
*
* @param string $name The remote name
* @param string $branch [optional] The symbolic-ref to set
*
* @return bool
*/
public function __invoke($name, $branch = null)
{
return $this->set($name, $branch);
}
/**
* Sets the default branch for the named remote
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
* $git->remote->head->set('origin');
* ```
*
* @param string $name The remote name
* @param string $branch [optional] The symbolic-ref to set
*
* @return bool
*/
public function set($name, $branch)
{
$builder = $this->git->getProcessBuilder()
->add('remote')
->add('set-head')
->add($name);
if ($branch) {
$builder->add($branch);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* Deletes the default branch for the named remote
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
* $git->remote->head->delete('origin');
* ```
*
* @param string $name The remote name
*
* @return bool
*/
public function delete($name)
{
$builder = $this->git->getProcessBuilder()
->add('remote')
->add('set-head')
->add($name)
->add('-d');
$this->git->run($builder->getProcess());
return true;
}
/**
* Determine the default branch by querying remote
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
* $git->remote->head->remote('origin');
* ```
*
* @param string $name The remote name
*
* @return bool
*/
public function remote($name)
{
$builder = $this->git->getProcessBuilder()
->add('remote')
->add('set-head')
->add($name)
->add('-a');
$this->git->run($builder->getProcess());
return true;
}
}

View File

@ -0,0 +1,175 @@
<?php
namespace PHPGit\Command\Remote;
use PHPGit\Command;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Changes URL remote points to
*
* @author Kazuyuki Hayashi
*/
class SetUrlCommand extends Command
{
/**
* Alias of set()
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
* $git->remote->url('origin', 'https://github.com/text/Text.git');
* ```
*
* ##### Options
*
* - **push** (_boolean_) Push URLs are manipulated instead of fetch URLs
*
* @param string $name The name of remote
* @param string $newUrl The new URL
* @param string $oldUrl [optional] The old URL
* @param array $options [optional] An array of options {@see SetUrlCommand::setDefaultOptions}
*
* @return bool
*/
public function __invoke($name, $newUrl, $oldUrl = null, array $options = array())
{
return $this->set($name, $newUrl, $oldUrl, $options);
}
/**
* Sets the URL remote to $newUrl
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
* $git->remote->url->set('origin', 'https://github.com/text/Text.git');
* ```
*
* ##### Options
*
* - **push** (_boolean_) Push URLs are manipulated instead of fetch URLs
*
* @param string $name The name of remote
* @param string $newUrl The new URL
* @param string $oldUrl [optional] The old URL
* @param array $options [optional] An array of options {@see SetUrlCommand::setDefaultOptions}
*
* @return bool
*/
public function set($name, $newUrl, $oldUrl = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('remote')
->add('set-url');
$this->addFlags($builder, $options);
$builder
->add($name)
->add($newUrl);
if ($oldUrl) {
$builder->add($oldUrl);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* Adds new URL to remote
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
* $git->remote->url->add('origin', 'https://github.com/text/Text.git');
* ```
*
* ##### Options
*
* - **push** (_boolean_) Push URLs are manipulated instead of fetch URLs
*
* @param string $name The name of remote
* @param string $newUrl The new URL
* @param array $options [optional] An array of options {@see SetUrlCommand::setDefaultOptions}
*
* @return bool
*/
public function add($name, $newUrl, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('remote')
->add('set-url')
->add('--add');
$this->addFlags($builder, $options);
$builder
->add($name)
->add($newUrl);
$this->git->run($builder->getProcess());
return true;
}
/**
* Deletes all URLs matching regex $url
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
* $git->remote->url->delete('origin', 'https://github.com');
* ```
*
* ##### Options
*
* - **push** (_boolean_) Push URLs are manipulated instead of fetch URLs
*
* @param string $name The remote name
* @param string $url The URL to delete
* @param array $options [optional] An array of options {@see SetUrlCommand::setDefaultOptions}
*
* @return bool
*/
public function delete($name, $url, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('remote')
->add('set-url')
->add('--delete');
$this->addFlags($builder, $options);
$builder
->add($name)
->add($url);
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*
* - **push** (_boolean_) Push URLs are manipulated instead of fetch URLs
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'push' => false
));
}
}

View File

@ -0,0 +1,278 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Git;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Manage set of tracked repositories - `git remote`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*
* @method head($name, $branch) Sets the default branch for the named remote
* @method branches($name, $branches) Changes the list of branches tracked by the named remote
* @method url($name, $newUrl, $oldUrl = null, $options = array()) Sets the URL remote to $newUrl
*/
class RemoteCommand extends Command
{
/** @var Remote\SetHeadCommand */
public $head;
/** @var Remote\SetBranchesCommand */
public $branches;
/** @var Remote\SetUrlCommand */
public $url;
/**
* @param Git $git
*/
public function __construct(Git $git)
{
parent::__construct($git);
$this->head = new Remote\SetHeadCommand($git);
$this->branches = new Remote\SetBranchesCommand($git);
$this->url = new Remote\SetUrlCommand($git);
}
/**
* Calls sub-commands
*
* @param string $name The name of a property
* @param array $arguments An array of arguments
*
* @throws \BadMethodCallException
* @return mixed
*/
public function __call($name, $arguments)
{
if (isset($this->{$name}) && is_callable($this->{$name})) {
return call_user_func_array($this->{$name}, $arguments);
}
throw new \BadMethodCallException(sprintf('Call to undefined method %s::%s()', __CLASS__, $name));
}
/**
* Returns an array of existing remotes
*
* ``` php
* $git = new PHPGit\Git();
* $git->clone('https://github.com/kzykhys/Text.git', '/path/to/repo');
* $git->setRepository('/path/to/repo');
* $remotes = $git->remote();
* ```
*
* ##### Output Example
*
* ``` php
* [
* 'origin' => [
* 'fetch' => 'https://github.com/kzykhys/Text.git',
* 'push' => 'https://github.com/kzykhys/Text.git'
* ]
* ]
* ```
*
* @return array
*/
public function __invoke()
{
$builder = $this->git->getProcessBuilder()
->add('remote')
->add('-v');
$remotes = array();
$output = $this->git->run($builder->getProcess());
$lines = $this->split($output);
foreach ($lines as $line) {
if (preg_match('/^(.*)\t(.*)\s\((.*)\)$/', $line, $matches)) {
if (!isset($remotes[$matches[1]])) {
$remotes[$matches[1]] = array();
}
$remotes[$matches[1]][$matches[3]] = $matches[2];
}
}
return $remotes;
}
/**
* Adds a remote named **$name** for the repository at **$url**
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
* $git->fetch('origin');
* ```
*
* ##### Options
*
* - **tags** (_boolean_) With this option, `git fetch <name>` imports every tag from the remote repository
* - **no-tags** (_boolean_) With this option, `git fetch <name>` does not import tags from the remote repository
*
* @param string $name The name of the remote
* @param string $url The url of the remote
* @param array $options [optional] An array of options {@see RemoteCommand::setDefaultOptions}
*
* @return bool
*/
public function add($name, $url, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('remote')
->add('add');
$this->addFlags($builder, $options, array('tags', 'no-tags'));
$builder->add($name)->add($url);
$this->git->run($builder->getProcess());
return true;
}
/**
* Rename the remote named **$name** to **$newName**
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
* $git->remote->rename('origin', 'upstream');
* ```
*
* @param string $name The remote name to rename
* @param string $newName The new remote name
*
* @return bool
*/
public function rename($name, $newName)
{
$builder = $this->git->getProcessBuilder()
->add('remote')
->add('rename')
->add($name)
->add($newName);
$this->git->run($builder->getProcess());
return true;
}
/**
* Remove the remote named **$name**
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
* $git->remote->rm('origin');
* ```
*
* @param string $name The remote name to remove
*
* @return bool
*/
public function rm($name)
{
$builder = $this->git->getProcessBuilder()
->add('remote')
->add('rm')
->add($name);
$this->git->run($builder->getProcess());
return true;
}
/**
* Gives some information about the remote **$name**
*
* ``` php
* $git = new PHPGit\Git();
* $git->clone('https://github.com/kzykhys/Text.git', '/path/to/repo');
* $git->setRepository('/path/to/repo');
* echo $git->remote->show('origin');
* ```
*
* ##### Output Example
*
* ```
* \* remote origin
* Fetch URL: https://github.com/kzykhys/Text.git
* Push URL: https://github.com/kzykhys/Text.git
* HEAD branch: master
* Remote branch:
* master tracked
* Local branch configured for 'git pull':
* master merges with remote master
* Local ref configured for 'git push':
* master pushes to master (up to date)
* ```
*
* @param string $name The remote name to show
*
* @return string
*/
public function show($name)
{
$builder = $this->git->getProcessBuilder()
->add('remote')
->add('show')
->add($name);
return $this->git->run($builder->getProcess());
}
/**
* Deletes all stale remote-tracking branches under **$name**
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->prune('origin');
* ```
*
* @param string $name The remote name
*
* @return bool
*/
public function prune($name = null)
{
$builder = $this->git->getProcessBuilder()
->add('remote')
->add('prune');
if ($name) {
$builder->add($name);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*
* - **tags** (_boolean_) With this option, `git fetch <name>` imports every tag from the remote repository
* - **no-tags** (_boolean_) With this option, `git fetch <name>` does not import tags from the remote repository
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'tags' => false,
'no-tags' => false
));
}
}

View File

@ -0,0 +1,199 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Exception\GitException;
/**
* Reset current HEAD to the specified state - `git reset`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class ResetCommand extends Command
{
/**
* Resets the index entries for all **$paths** to their state at **$commit**
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->reset();
* ```
*
* @param string|array|\Traversable $paths The paths to reset
* @param string $commit The commit
*
* @return bool
*/
public function __invoke($paths, $commit = null)
{
$builder = $this->git->getProcessBuilder()
->add('reset');
if ($commit) {
$builder->add($commit)->add('--');
}
if (!is_array($paths) && !($paths instanceof \Traversable)) {
$paths = array($paths);
}
foreach ($paths as $path) {
$builder->add($path);
}
try {
$this->git->run($builder->getProcess());
} catch (GitException $e) {
// Confirm exit code
}
return true;
}
/**
* Resets the current branch head to **$commit**
*
* Does not touch the index file nor the working tree at all (but resets the head to **$commit**,
* just like all modes do).
* This leaves all your changed files "Changes to be committed", as git status would put it.
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->reset->soft();
* ```
*
* @param string $commit The commit
*
* @return bool
*/
public function soft($commit = null)
{
return $this->mode('soft', $commit);
}
/**
* Resets the current branch head to **$commit**
*
* Resets the index but not the working tree (i.e., the changed files are preserved but not marked for commit)
* and reports what has not been updated. This is the default action.
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->reset->mixed();
* ```
*
* @param string $commit The commit
*
* @return bool
*/
public function mixed($commit = null)
{
return $this->mode('mixed', $commit);
}
/**
* Resets the current branch head to **$commit**
*
* Resets the index and working tree. Any changes to tracked files in the working tree since **$commit** are discarded
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->reset->hard();
* ```
*
* @param string $commit The commit
*
* @return bool
*/
public function hard($commit = null)
{
return $this->mode('hard', $commit);
}
/**
* Resets the current branch head to **$commit**
*
* Resets the index and updates the files in the working tree that are different between **$commit** and HEAD,
* but keeps those which are different between the index and working tree
* (i.e. which have changes which have not been added). If a file that is different between **$commit** and
* the index has unstaged changes, reset is aborted
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->reset->merge();
* ```
*
* @param string $commit The commit
*
* @return bool
*/
public function merge($commit = null)
{
return $this->mode('merge', $commit);
}
/**
* Resets the current branch head to **$commit**
*
* Resets index entries and updates files in the working tree that are different between **$commit** and HEAD.
* If a file that is different between **$commit** and HEAD has local changes, reset is aborted.
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->reset->keep();
* ```
*
* @param string $commit The commit
*
* @return bool
*/
public function keep($commit = null)
{
return $this->mode('keep', $commit);
}
/**
* Resets the current branch head to **$commit**
*
* Possibly updates the index (resetting it to the tree of **$commit**) and the working tree depending on **$mode**
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->reset->mode('hard');
* ```
*
* @param string $mode --<mode>
* @param string $commit The commit
*
* @throws \InvalidArgumentException
* @return bool
*/
public function mode($mode, $commit = null)
{
if (!in_array($mode, array('soft', 'mixed', 'hard', 'merge', 'keep'))) {
throw new \InvalidArgumentException('$mode must be one of the following: soft, mixed, hard, merge, keep');
}
$builder = $this->git->getProcessBuilder()
->add('reset')
->add('--' . $mode);
if ($commit) {
$builder->add($commit);
}
$this->git->run($builder->getProcess());
return true;
}
}

View File

@ -0,0 +1,97 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Remove files from the working tree and from the index - `git rm`
*
* @author Kazuyuki Hayashi
*/
class RmCommand extends Command
{
/**
* Remove files from the working tree and from the index
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->rm('CHANGELOG-1.0-1.1.txt', ['force' => true]);
* ```
*
* ##### Options
*
* - **force** (_boolean_) Override the up-to-date check
* - **cached** (_boolean_) Unstage and remove paths only from the index
* - **recursive** (_boolean_) Allow recursive removal when a leading directory name is given
*
* @param string|array|\Traversable $file Files to remove. Fileglobs (e.g. *.c) can be given to remove all matching files.
* @param array $options [optional] An array of options {@see RmCommand::setDefaultOptions}
*
* @return bool
*/
public function __invoke($file, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('rm');
$this->addFlags($builder, $options, array('force', 'cached'));
if ($options['recursive']) {
$builder->add('-r');
}
if (!is_array($file) && !($file instanceof \Traversable)) {
$file = array($file);
}
foreach ($file as $value) {
$builder->add($value);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* Equivalent to $git->rm($file, ['cached' => true]);
*
* ##### Options
*
* - **force** (_boolean_) Override the up-to-date check
* - **recursive** (_boolean_) Allow recursive removal when a leading directory name is given
*
* @param string|array|\Traversable $file Files to remove. Fileglobs (e.g. *.c) can be given to remove all matching files.
* @param array $options [optional] An array of options {@see RmCommand::setDefaultOptions}
*
* @return bool
*/
public function cached($file, array $options = array())
{
$options['cached'] = true;
return $this->__invoke($file, $options);
}
/**
* {@inheritdoc}
*
* - **force** (_boolean_) Override the up-to-date check
* - **cached** (_boolean_) Unstage and remove paths only from the index
* - **recursive** (_boolean_) Allow recursive removal when a leading directory name is given
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'force' => false,
'cached' => false,
'recursive' => false
));
}
}

View File

@ -0,0 +1,134 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
/**
* Summarize 'git log' output - `git shortlog`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class ShortlogCommand extends Command
{
/**
* Summarize 'git log' output
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $shortlog = $git->shortlog();
* ```
*
* ##### Output Example
*
* ``` php
* [
* 'John Doe <john@example.com>' => [
* 0 => ['commit' => '589de67', 'date' => new \DateTime('2014-02-10 12:56:15 +0300'), 'subject' => 'Update README'],
* 1 => ['commit' => '589de67', 'date' => new \DateTime('2014-02-15 12:56:15 +0300'), 'subject' => 'Update README'],
* ],
* //...
* ]
* ```
* @param string|array|\Traversable $commits [optional] Defaults to HEAD
*
* @return array
*/
public function __invoke($commits = 'HEAD')
{
$builder = $this->git->getProcessBuilder()
->add('shortlog')
->add('--numbered')
->add('--format=')
->add('-w256,2,2')
->add('-e');
if (!is_array($commits) && !($commits instanceof \Traversable)) {
$commits = array($commits);
}
foreach ($commits as $commit) {
$builder->add($commit);
}
$process = $builder->getProcess();
$process->setCommandLine(str_replace('--format=', '--format=%h|%ci|%s', $process->getCommandLine()));
$output = $this->git->run($process);
$lines = $this->split($output);
$result = array();
$author = null;
foreach ($lines as $line) {
if (substr($line, 0, 1) != ' ') {
if (preg_match('/([^<>]*? <[^<>]+>)/', $line, $matches)) {
$author = $matches[1];
$result[$author] = array();
}
continue;
}
list ($commit, $date, $subject) = explode('|', trim($line), 3);
$result[$author][] = array(
'commit' => $commit,
'date' => new \DateTime($date),
'subject' => $subject
);
}
return $result;
}
/**
* Suppress commit description and provide a commit count summary only
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $shortlog = $git->shortlog->summary();
* ```
*
* ##### Output Example
*
* ``` php
* [
* 'John Doe <john@example.com>' => 153,
* //...
* ]
* ```
*
* @param string $commits [optional] Defaults to HEAD
*
* @return array
*/
public function summary($commits = 'HEAD')
{
$builder = $this->git->getProcessBuilder()
->add('shortlog')
->add('--numbered')
->add('--summary')
->add('-e');
if (!is_array($commits) && !($commits instanceof \Traversable)) {
$commits = array($commits);
}
foreach ($commits as $commit) {
$builder->add($commit);
}
$output = $this->git->run($builder->getProcess());
$lines = $this->split($output);
$result = array();
foreach ($lines as $line) {
list ($commits, $author) = explode("\t", trim($line), 2);
$result[$author] = (int) $commits;
}
return $result;
}
}

View File

@ -0,0 +1,70 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Show various types of objects - `git show`
*
* @author Kazuyuki Hayashi
*/
class ShowCommand extends Command
{
/**
* Shows one or more objects (blobs, trees, tags and commits)
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* echo $git->show('3ddee587e209661c8265d5bfd0df999836f6dfa2');
* ```
*
* ##### Options
*
* - **format** (_string_) Pretty-print the contents of the commit logs in a given format, where <format> can be one of oneline, short, medium, full, fuller, email, raw and format:<string>
* - **abbrev-commit** (_boolean_) Instead of showing the full 40-byte hexadecimal commit object name, show only a partial prefix
*
* @param string $object The names of objects to show
* @param array $options [optional] An array of options {@see ShowCommand::setDefaultOptions}
*
* @return string
*/
public function __invoke($object, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('show');
$this->addFlags($builder, $options, array('abbrev-commit'));
if ($options['format']) {
$builder->add('--format=' . $options['format']);
}
$builder->add($object);
return $this->git->run($builder->getProcess());
}
/**
* {@inheritdoc}
*
* - **format** (_string_) Pretty-print the contents of the commit logs in a given format, where <format> can be one of oneline, short, medium, full, fuller, email, raw and format:<string>
* - **abbrev-commit** (_boolean_) Instead of showing the full 40-byte hexadecimal commit object name, show only a partial prefix
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'format' => null,
'abbrev-commit' => false
));
$resolver->setAllowedTypes(array(
'format' => array('null', 'string'),
));
}
}

View File

@ -0,0 +1,309 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
/**
* Stash the changes in a dirty working directory away - `git stash`
*
* @author Kazuyuki Hayashi
*/
class StashCommand extends Command
{
/**
* Save your local modifications to a new stash, and run git reset --hard to revert them
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->stash();
* ```
*
* @return bool
*/
public function __invoke()
{
$builder = $this->git->getProcessBuilder()
->add('stash');
$this->git->run($builder->getProcess());
return true;
}
/**
* Save your local modifications to a new stash, and run git reset --hard to revert them.
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->stash->save('My stash');
* ```
*
* @param string $message [optional] The description along with the stashed state
* @param array $options [optional] An array of options {@see StashCommand::setDefaultOptions}
*
* @return bool
*/
public function save($message = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('stash')
->add('save');
$builder->add($message);
$this->git->run($builder->getProcess());
return true;
}
/**
* Returns the stashes that you currently have
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $stashes = $git->stash->lists();
* ```
*
* ##### Output Example
*
* ``` php
* [
* 0 => ['branch' => 'master', 'message' => '0e2f473 Fixes README.md'],
* 1 => ['branch' => 'master', 'message' => 'ce1ddde Initial commit'],
* ]
* ```
*
* @param array $options [optional] An array of options {@see StashCommand::setDefaultOptions}
*
* @return array
*/
public function lists(array $options = array())
{
$builder = $this->git->getProcessBuilder()
->add('stash')
->add('list');
$output = $this->git->run($builder->getProcess());
$lines = $this->split($output);
$list = array();
foreach ($lines as $line) {
if (preg_match('/stash@{(\d+)}:.* [Oo]n (.*): (.*)/', $line, $matches)) {
$list[$matches[1]] = array(
'branch' => $matches[2],
'message' => $matches[3]
);
}
}
return $list;
}
/**
* Show the changes recorded in the stash as a diff between the stashed state and its original parent
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* echo $git->stash->show('stash@{0}');
* ```
*
* ##### Output Example
*
* ```
* REAMDE.md | 2 +-
* 1 files changed, 1 insertions(+), 1 deletions(-)
* ```
*
* @param string $stash The stash to show
*
* @return string
*/
public function show($stash = null)
{
$builder = $this->git->getProcessBuilder()
->add('stash')
->add('show');
if ($stash) {
$builder->add($stash);
}
return $this->git->run($builder->getProcess());
}
/**
* Remove a single stashed state from the stash list
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->stash->drop('stash@{0}');
* ```
*
* @param string $stash The stash to drop
*
* @return mixed
*/
public function drop($stash = null)
{
$builder = $this->git->getProcessBuilder()
->add('stash')
->add('drop');
if ($stash) {
$builder->add($stash);
}
return $this->git->run($builder->getProcess());
}
/**
* Remove a single stashed state from the stash list and apply it on top of the current working tree state
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->stash->pop('stash@{0}');
* ```
*
* @param string $stash The stash to pop
* @param array $options [optional] An array of options {@see StashCommand::setDefaultOptions}
*
* @return bool
*/
public function pop($stash = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('stash')
->add('pop');
$this->addFlags($builder, $options, array('index'));
if ($stash) {
$builder->add($stash);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* Like pop, but do not remove the state from the stash list
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->stash->apply('stash@{0}');
* ```
*
* @param string $stash The stash to apply
* @param array $options [optional] An array of options {@see StashCommand::setDefaultOptions}
*
* @return bool
*/
public function apply($stash = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('stash')
->add('apply');
$this->addFlags($builder, $options, array('index'));
if ($stash) {
$builder->add($stash);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* Creates and checks out a new branch named <branchname> starting from the commit at which the <stash> was originally created, applies the changes recorded in <stash> to the new working tree and index
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->stash->branch('hotfix', 'stash@{0}');
* ```
*
* @param string $name The name of the branch
* @param string $stash The stash
*
* @return bool
*/
public function branch($name, $stash = null)
{
$builder = $this->git->getProcessBuilder()
->add('stash')
->add('branch')
->add($name);
if ($stash) {
$builder->add($stash);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* Remove all the stashed states
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->stash->clear();
* ```
*
* @return bool
*/
public function clear()
{
$builder = $this->git->getProcessBuilder()
->add('stash')
->add('clear');
$this->git->run($builder->getProcess());
return true;
}
/**
* Create a stash (which is a regular commit object) and return its object name, without storing it anywhere in the ref namespace
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $commit = $git->stash->create();
* ```
*
* ##### Output Example
*
* ```
* 877316ea6f95c43b7ccc2c2a362eeedfa78b597d
* ```
*
* @return string
*/
public function create()
{
$builder = $this->git->getProcessBuilder()
->add('stash')
->add('create');
return $this->git->run($builder->getProcess());
}
}

View File

@ -0,0 +1,147 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Show the working tree status - `git status`
*
* = unmodified
* M = modified
* A = added
* D = deleted
* R = renamed
* C = copied
* U = updated but unmerged
*
* X Y Meaning
* -------------------------------------------------
* [MD] not updated
* M [ MD] updated in index
* A [ MD] added to index
* D [ M] deleted from index
* R [ MD] renamed in index
* C [ MD] copied in index
* [MARC] index and work tree matches
* [ MARC] M work tree changed since index
* [ MARC] D deleted in work tree
* -------------------------------------------------
* D D unmerged, both deleted
* A U unmerged, added by us
* U D unmerged, deleted by them
* U A unmerged, added by them
* D U unmerged, deleted by us
* A A unmerged, both added
* U U unmerged, both modified
* -------------------------------------------------
* ? ? untracked
* ! ! ignored
* -------------------------------------------------
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class StatusCommand extends Command
{
const UNMODIFIED = ' ';
const MODIFIED = 'M';
const ADDED = 'A';
const DELETED = 'D';
const RENAMED = 'R';
const COPIED = 'C';
const UPDATED_BUT_UNMERGED = 'U';
const UNTRACKED = '?';
const IGNORED = '!';
/**
* Returns the working tree status
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $status = $git->status();
* ```
*
* ##### Constants
*
* - StatusCommand::UNMODIFIED [=' '] unmodified
* - StatusCommand::MODIFIED [='M'] modified
* - StatusCommand::ADDED [='A'] added
* - StatusCommand::DELETED [='D'] deleted
* - StatusCommand::RENAMED [='R'] renamed
* - StatusCommand::COPIED [='C'] copied
* - StatusCommand::UPDATED_BUT_UNMERGED [='U'] updated but unmerged
* - StatusCommand::UNTRACKED [='?'] untracked
* - StatusCommand::IGNORED [='!'] ignored
*
* ##### Output Example
*
* ``` php
* [
* 'branch' => 'master',
* 'changes' => [
* ['file' => 'item1.txt', 'index' => 'A', 'work_tree' => 'M'],
* ['file' => 'item2.txt', 'index' => 'A', 'work_tree' => ' '],
* ['file' => 'item3.txt', 'index' => '?', 'work_tree' => '?'],
* ]
* ]
* ```
*
* ##### Options
*
* - **ignored** (_boolean_) Show ignored files as well
*
* @param array $options [optional] An array of options {@see StatusCommand::setDefaultOptions}
*
* @return mixed
*/
public function __invoke(array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('status')
->add('--porcelain')->add('-s')->add('-b')->add('--null');
$this->addFlags($builder, $options);
$process = $builder->getProcess();
$result = array('branch' => null, 'changes' => array());
$output = $this->git->run($process);
list($branch, $changes) = preg_split('/(\0|\n)/', $output, 2);
$lines = $this->split($changes, true);
if (substr($branch, -11) == '(no branch)') {
$result['branch'] = null;
} elseif (preg_match('/([^ ]*)\.\.\..*?\[.*?\]$/', $branch, $matches)) {
$result['branch'] = $matches[1];
} elseif (preg_match('/ ([^ ]*)$/', $branch, $matches)) {
$result['branch'] = $matches[1];
}
foreach ($lines as $line) {
$result['changes'][] = array(
'file' => substr($line, 3),
'index' => substr($line, 0, 1),
'work_tree' => substr($line, 1, 1)
);
}
return $result;
}
/**
* {@inheritdoc}
*
* - **ignored** (_boolean_) Show ignored files as well
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'ignored' => false
));
}
}

View File

@ -0,0 +1,156 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Exception\GitException;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Create, list, delete or verify a tag object signed with GPG - `git tag`
*
* @author Kazuyuki Hayashi
*/
class TagCommand extends Command
{
/**
* Returns an array of tags
*
* ``` php
* $git = new PHPGit\Git();
* $git->clone('https://github.com/kzykhys/PHPGit.git', '/path/to/repo');
* $git->setRepository('/path/to/repo');
* $tags = $git->tag();
* ```
*
* ##### Output Example
*
* ```
* ['v1.0.0', 'v1.0.1', 'v1.0.2']
* ```
*
* @throws GitException
* @return array
*/
public function __invoke()
{
$builder = $this->git->getProcessBuilder()
->add('tag');
$output = $this->git->run($builder->getProcess());
return $this->split($output);
}
/**
* Creates a tag object
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->tag->create('v1.0.0');
* ```
*
* ##### Options
*
* - **annotate** (_boolean_) Make an unsigned, annotated tag object
* - **sign** (_boolean_) Make a GPG-signed tag, using the default e-mail addresss key
* - **force** (_boolean_) Replace an existing tag with the given name (instead of failing)
*
* @param string $tag The name of the tag to create
* @param string $commit The SHA1 object name of the commit object
* @param array $options [optional] An array of options {@see TagCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function create($tag, $commit = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('tag')
->add($tag);
$this->addFlags($builder, $options, array('annotate', 'sign', 'force'));
if ($commit) {
$builder->add($commit);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* Delete existing tags with the given names
*
* @param string|array|\Traversable $tag The name of the tag to create
*
* @throws GitException
* @return bool
*/
public function delete($tag)
{
$builder = $this->git->getProcessBuilder()
->add('tag')
->add('-d');
if (!is_array($tag) && !($tag instanceof \Traversable)) {
$tag = array($tag);
}
foreach ($tag as $value) {
$builder->add($value);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* Verify the gpg signature of the given tag names
*
* @param string|array|\Traversable $tag The name of the tag to create
*
* @throws GitException
* @return bool
*/
public function verify($tag)
{
$builder = $this->git->getProcessBuilder()
->add('tag')
->add('-v');
if (!is_array($tag) && !($tag instanceof \Traversable)) {
$tag = array($tag);
}
foreach ($tag as $value) {
$builder->add($value);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*
* - **annotate** (_boolean_) Make an unsigned, annotated tag object
* - **sign** (_boolean_) Make a GPG-signed tag, using the default e-mail addresss key
* - **force** (_boolean_) Replace an existing tag with the given name (instead of failing)
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'annotate' => false,
'sign' => false,
'force' => false,
));
}
}

View File

@ -0,0 +1,70 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
/**
* List the contents of a tree object - `git ls-tree`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class TreeCommand extends Command
{
/**
* Returns the contents of a tree object
*
* ``` php
* $git = new PHPGit\Git();
* $git->clone('https://github.com/kzykhys/PHPGit.git', '/path/to/repo');
* $git->setRepository('/path/to/repo');
* $tree = $git->tree('master');
* ```
*
* ##### Output Example
*
* ``` php
* [
* ['mode' => '100644', 'type' => 'blob', 'hash' => '1f100ce9855b66111d34b9807e47a73a9e7359f3', 'file' => '.gitignore', 'sort' => '2:.gitignore'],
* ['mode' => '100644', 'type' => 'blob', 'hash' => 'e0bfe494537037451b09c32636c8c2c9795c05c0', 'file' => '.travis.yml', 'sort' => '2:.travis.yml'],
* ['mode' => '040000', 'type' => 'tree', 'hash' => '8d5438e79f77cd72de80c49a413f4edde1f3e291', 'file' => 'bin', 'sort' => '1:.bin'],
* ]
* ```
*
* @param string $branch The commit
* @param string $path The path
*
* @return array
*/
public function __invoke($branch = 'master', $path = '')
{
$objects = array();
$builder = $this->git->getProcessBuilder();
$process = $builder->add('ls-tree')->add($branch . ':' . $path)->getProcess();
$output = $this->git->run($process);
$lines = $this->split($output);
$types = array(
'submodule' => 0,
'tree' => 1,
'blob' => 2
);
foreach ($lines as $line) {
list($meta, $file) = explode("\t", $line);
list($mode, $type, $hash) = explode(" ", $meta);
$objects[] = array(
'sort' => sprintf('%d:%s', $types[$type], $file),
'mode' => $mode,
'type' => $type,
'hash' => $hash,
'file' => $file
);
}
return $objects;
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace PHPGit\Exception;
/**
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class GitException extends \Exception
{
/**
* @var string
*/
protected $commandLine;
/**
* Construct the exception. Note: The message is NOT binary safe.
*
* @param string $message [optional] The Exception message to throw.
* @param int $code [optional] The Exception code.
* @param string $commandLine [optional] Command-line
* @param \Exception $previous [optional] The previous exception used for the exception chaining. Since 5.3.0
*/
public function __construct($message = "", $code = 0, $commandLine = null, \Exception $previous = null)
{
parent::__construct($message, $code, $previous);
$this->commandLine = $commandLine;
}
/**
* @return null|string
*/
public function getCommandLine()
{
return $this->commandLine;
}
}

View File

@ -0,0 +1,308 @@
<?php
namespace PHPGit;
use PHPGit\Command;
use PHPGit\Exception\GitException;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\ProcessBuilder;
/**
* PHPGit - A Git wrapper for PHP5.3+
* ==================================
*
* [![Latest Unstable Version](https://poser.pugx.org/kzykhys/git/v/unstable.png)](https://packagist.org/packages/kzykhys/git)
* [![Build Status](https://travis-ci.org/kzykhys/PHPGit.png?branch=master)](https://travis-ci.org/kzykhys/PHPGit)
* [![Coverage Status](https://coveralls.io/repos/kzykhys/PHPGit/badge.png)](https://coveralls.io/r/kzykhys/PHPGit)
* [![SensioLabsInsight](https://insight.sensiolabs.com/projects/04f10b57-a113-47ad-8dda-9a6dacbb079f/mini.png)](https://insight.sensiolabs.com/projects/04f10b57-a113-47ad-8dda-9a6dacbb079f)
*
* Requirements
* ------------
*
* * PHP5.3
* * Git
*
* Installation
* ------------
*
* Update your composer.json and run `composer update`
*
* ``` json
* {
* "require": {
* "kzykhys/git": "dev-master"
* }
* }
* ```
*
* Basic Usage
* -----------
*
* ``` php
* <?php
*
* require __DIR__ . '/vendor/autoload.php';
*
* $git = new PHPGit\Git();
* $git->clone('https://github.com/kzykhys/PHPGit.git', '/path/to/repo');
* $git->setRepository('/path/to/repo');
* $git->remote->add('production', 'git://example.com/your/repo.git');
* $git->add('README.md');
* $git->commit('Adds README.md');
* $git->checkout('release');
* $git->merge('master');
* $git->push();
* $git->push('production', 'release');
* $git->tag->create('v1.0.1', 'release');
*
* foreach ($git->tree('release') as $object) {
* if ($object['type'] == 'blob') {
* echo $git->show($object['file']);
* }
* }
* ```
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
* @license MIT
*
* @method add($file, $options = array()) Add file contents to the index
* @method archive($file, $tree = null, $path = null, $options = array()) Create an archive of files from a named tree
* @method branch($options = array()) List both remote-tracking branches and local branches
* @method checkout($branch, $options = array()) Checkout a branch or paths to the working tree
* @method clone($repository, $path = null, $options = array()) Clone a repository into a new directory
* @method commit($message = '', $options = array()) Record changes to the repository
* @method config($options = array()) List all variables set in config file
* @method describe($committish = null, $options = array()) Returns the most recent tag that is reachable from a commit
* @method fetch($repository, $refspec = null, $options = array()) Fetches named heads or tags from one or more other repositories
* @method init($path, $options = array()) Create an empty git repository or reinitialize an existing one
* @method log($path = null, $options = array()) Returns the commit logs
* @method merge($commit, $message = null, $options = array()) Incorporates changes from the named commits into the current branch
* @method mv($source, $destination, $options = array()) Move or rename a file, a directory, or a symlink
* @method pull($repository = null, $refspec = null, $options = array()) Fetch from and merge with another repository or a local branch
* @method push($repository = null, $refspec = null, $options = array()) Update remote refs along with associated objects
* @method rebase($upstream = null, $branch = null, $options = array()) Forward-port local commits to the updated upstream head
* @method remote() Returns an array of existing remotes
* @method reset($commit = null, $paths = array()) Resets the index entries for all <paths> to their state at <commit>
* @method rm($file, $options = array()) Remove files from the working tree and from the index
* @method shortlog($commits = array()) Summarize 'git log' output
* @method show($object, $options = array()) Shows one or more objects (blobs, trees, tags and commits)
* @method stash() Save your local modifications to a new stash, and run git reset --hard to revert them
* @method status($options = array()) Show the working tree status
* @method tag() Returns an array of tags
* @method tree($branch = 'master', $path = '') List the contents of a tree object
*/
class Git
{
/** @var Command\AddCommand */
public $add;
/** @var Command\ArchiveCommand */
public $archive;
/** @var Command\BranchCommand */
public $branch;
/** @var Command\CatCommand */
public $cat;
/** @var Command\CheckoutCommand */
public $checkout;
/** @var Command\CloneCommand */
public $clone;
/** @var Command\CommitCommand */
public $commit;
/** @var Command\ConfigCommand */
public $config;
/** @var Command\DescribeCommand */
public $describe;
// Not implemented yet
public $diff;
/** @var Command\FetchCommand */
public $fetch;
/** @var Command\InitCommand */
public $init;
/** @var Command\LogCommand */
public $log;
/** @var Command\MergeCommand */
public $merge;
/** @var Command\MvCommand */
public $mv;
/** @var Command\PullCommand */
public $pull;
/** @var Command\PushCommand */
public $push;
/** @var Command\RebaseCommand */
public $rebase;
/** @var Command\RemoteCommand */
public $remote;
/** @var Command\ResetCommand */
public $reset;
/** @var Command\RmCommand */
public $rm;
/** @var Command\ShortlogCommand */
public $shortlog;
/** @var Command\ShowCommand */
public $show;
/** @var Command\StashCommand */
public $stash;
/** @var Command\StatusCommand */
public $status;
/** @var Command\TagCommand */
public $tag;
/** @var Command\TreeCommand */
public $tree;
/** @var string */
private $bin = 'git';
/** @var string */
private $directory = '.';
/**
* Initializes sub-commands
*/
public function __construct()
{
$this->add = new Command\AddCommand($this);
$this->archive = new Command\ArchiveCommand($this);
$this->branch = new Command\BranchCommand($this);
$this->cat = new Command\CatCommand($this);
$this->checkout = new Command\CheckoutCommand($this);
$this->clone = new Command\CloneCommand($this);
$this->commit = new Command\CommitCommand($this);
$this->config = new Command\ConfigCommand($this);
$this->describe = new Command\DescribeCommand($this);
$this->fetch = new Command\FetchCommand($this);
$this->init = new Command\InitCommand($this);
$this->log = new Command\LogCommand($this);
$this->merge = new Command\MergeCommand($this);
$this->mv = new Command\MvCommand($this);
$this->pull = new Command\PullCommand($this);
$this->push = new Command\PushCommand($this);
$this->rebase = new Command\RebaseCommand($this);
$this->remote = new Command\RemoteCommand($this);
$this->reset = new Command\ResetCommand($this);
$this->rm = new Command\RmCommand($this);
$this->shortlog = new Command\ShortlogCommand($this);
$this->show = new Command\ShowCommand($this);
$this->stash = new Command\StashCommand($this);
$this->status = new Command\StatusCommand($this);
$this->tag = new Command\TagCommand($this);
$this->tree = new Command\TreeCommand($this);
}
/**
* Calls sub-commands
*
* @param string $name The name of a property
* @param array $arguments An array of arguments
*
* @throws \BadMethodCallException
* @return mixed
*/
public function __call($name, $arguments)
{
if (isset($this->{$name}) && is_callable($this->{$name})) {
return call_user_func_array($this->{$name}, $arguments);
}
throw new \BadMethodCallException(sprintf('Call to undefined method PHPGit\Git::%s()', $name));
}
/**
* Sets the Git binary path
*
* @param string $bin
*
* @return Git
*/
public function setBin($bin)
{
$this->bin = $bin;
return $this;
}
/**
* Sets the Git repository path
*
* @var string $directory
*
* @return Git
*/
public function setRepository($directory)
{
$this->directory = $directory;
return $this;
}
/**
* Returns version number
*
* @return mixed
*/
public function getVersion()
{
$process = $this->getProcessBuilder()
->add('--version')
->getProcess();
return $this->run($process);
}
/**
* Returns an instance of ProcessBuilder
*
* @return ProcessBuilder
*/
public function getProcessBuilder()
{
return ProcessBuilder::create()
->setPrefix($this->bin)
->setWorkingDirectory($this->directory);
}
/**
* Executes a process
*
* @param Process $process The process to run
*
* @throws Exception\GitException
* @return mixed
*/
public function run(Process $process)
{
$process->run();
if (!$process->isSuccessful()) {
throw new GitException($process->getErrorOutput(), $process->getExitCode(), $process->getCommandLine());
}
return $process->getOutput();
}
}

View File

@ -0,0 +1,33 @@
<?php
use Symfony\Component\Filesystem\Filesystem;
/**
* @author Kazuyuki Hayashi <hayashi@siance.co.jp>
*/
abstract class BaseTestCase extends PHPUnit_Framework_TestCase
{
/**
* @var string
*/
protected $directory;
/**
* {@inheritdoc}
*/
public function setUp()
{
$this->directory = __DIR__.'/../../build/' . strtolower(get_class($this));
}
/**
* {@inheritdoc}
*/
public function tearDown()
{
$filesystem = new Filesystem();
$filesystem->remove($this->directory);
}
}

View File

@ -0,0 +1,42 @@
<?php
use PHPGit\Git;
use Symfony\Component\Filesystem\Filesystem;
require_once __DIR__ . '/../BaseTestCase.php';
/**
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class AddCommandTest extends BaseTestCase
{
public function testAdd()
{
$filesystem = new Filesystem();
$filesystem->mkdir($this->directory);
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/test.txt', 'foo');
$filesystem->dumpFile($this->directory . '/test.md', '**foo**');
$this->assertTrue($git->add('test.txt'));
$this->assertTrue($git->add(array('test.md'), array('force' => true)));
}
/**
* @expectedException \PHPGit\Exception\GitException
* @expectedExceptionCode 128
*/
public function testException()
{
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$git->add('foo');
}
}

View File

@ -0,0 +1,32 @@
<?php
use PHPGit\Git;
use Symfony\Component\Filesystem\Filesystem;
require_once __DIR__ . '/../BaseTestCase.php';
/**
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class ArchiveCommandTest extends BaseTestCase
{
public function testArchive()
{
$filesystem = new Filesystem();
$filesystem->mkdir($this->directory);
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/test.txt', 'hello');
$git->add('test.txt');
$git->commit('Initial commit');
$git->archive($this->directory . '/test.zip', 'master', null, array('format' => 'zip', 'prefix' => 'test/'));
$this->assertFileExists($this->directory . '/test.zip');
}
}

View File

@ -0,0 +1,106 @@
<?php
use PHPGit\Git;
use Symfony\Component\Filesystem\Filesystem;
require_once __DIR__ . '/../BaseTestCase.php';
class BranchCommandTest extends BaseTestCase
{
public function setUp()
{
parent::setUp();
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/test.txt', '');
$git->add('test.txt');
$git->commit('Initial commit');
}
public function testBranch()
{
$git = new Git();
$git->setRepository($this->directory);
$branches = $git->branch();
$this->assertCount(1, $branches);
$this->assertEquals('master', $branches['master']['name']);
$this->assertTrue($branches['master']['current']);
$this->assertEquals('Initial commit', $branches['master']['title']);
}
public function testAllBranch()
{
$git = new Git();
$git->clone('file://' . realpath($this->directory), $this->directory.'2');
$git->setRepository($this->directory.'2');
$branches = $git->branch(array('remotes' => true));
$this->assertArrayHasKey('origin/master', $branches);
$branches = $git->branch(array('all' => true));
$this->assertArrayHasKey('master', $branches);
$this->assertArrayHasKey('remotes/origin/master', $branches);
$filesystem = new Filesystem();
$filesystem->remove($this->directory.'2');
}
public function testBranchCreate()
{
$git = new Git();
$git->setRepository($this->directory);
$git->branch->create('1.0');
$branches = $git->branch();
$this->assertCount(2, $branches);
$git->branch->create('1.0-fix', '1.0', array('force' => true));
$branches = $git->branch();
$this->assertCount(3, $branches);
$this->assertArrayHasKey('1.0', $branches);
$this->assertArrayHasKey('1.0-fix', $branches);
}
public function testBranchMove()
{
$git = new Git();
$git->setRepository($this->directory);
$git->branch->create('1.0');
$git->branch->move('1.0', '1.0.x');
$branches = $git->branch();
$this->assertCount(2, $branches);
$this->assertArrayHasKey('1.0.x', $branches);
$git->branch->move('1.0.x', '2.x', array('force' => true));
$branches = $git->branch();
$this->assertCount(2, $branches);
$this->assertArrayHasKey('2.x', $branches);
}
public function testBranchDelete()
{
$git = new Git();
$git->setRepository($this->directory);
$git->branch->create('1.0');
$git->branch->create('2.0');
$branches = $git->branch();
$this->assertCount(3, $branches);
$git->branch->delete('1.0');
$branches = $git->branch();
$this->assertCount(2, $branches);
$git->branch->delete('2.0', array('force' => true));
$branches = $git->branch();
$this->assertCount(1, $branches);
}
}

View File

@ -0,0 +1,65 @@
<?php
use PHPGit\Git;
use Symfony\Component\Filesystem\Filesystem;
require_once __DIR__ . '/../BaseTestCase.php';
class CatCommandTest extends BaseTestCase
{
public function testCatBlob()
{
$filesystem = new Filesystem();
$filesystem->mkdir($this->directory);
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/test.txt', 'foo');
$git->add('test.txt');
$git->commit('Initial commit');
$tree = $git->tree();
$this->assertEquals('foo', $git->cat->blob($tree[0]['hash']));
}
public function testCatType()
{
$filesystem = new Filesystem();
$filesystem->mkdir($this->directory);
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/test.txt', 'foo');
$git->add('test.txt');
$git->commit('Initial commit');
$tree = $git->tree();
$this->assertEquals('blob', $git->cat->type($tree[0]['hash']));
}
public function testCatSize()
{
$filesystem = new Filesystem();
$filesystem->mkdir($this->directory);
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/test.txt', 'foo');
$git->add('test.txt');
$git->commit('Initial commit');
$tree = $git->tree();
$this->assertEquals(3, $git->cat->size($tree[0]['hash']));
}
}

View File

@ -0,0 +1,65 @@
<?php
use PHPGit\Git;
use Symfony\Component\Filesystem\Filesystem;
require_once __DIR__ . '/../BaseTestCase.php';
class CheckoutCommandTest extends BaseTestCase
{
public function setUp()
{
parent::setUp();
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/test.txt', '');
$git->add('test.txt');
$git->commit('Initial commit');
}
public function testCheckout()
{
$git = new Git();
$git->setRepository($this->directory);
$git->branch->create('next');
$git->checkout('next');
$branches = $git->branch();
$this->assertArrayHasKey('next', $branches);
$this->assertTrue($branches['next']['current']);
}
public function testCheckoutCreate()
{
$git = new Git();
$git->setRepository($this->directory);
$git->checkout->create('next');
$branches = $git->branch();
$this->assertArrayHasKey('next', $branches);
$this->assertTrue($branches['next']['current']);
$git->checkout->create('develop', 'next');
$branches = $git->branch();
$this->assertArrayHasKey('develop', $branches);
$this->assertTrue($branches['develop']['current']);
}
public function testCheckoutOrphan()
{
$git = new Git();
$git->setRepository($this->directory);
$git->checkout->orphan('gh-pages', 'master', array('force' => true));
$status = $git->status();
$this->assertEquals('gh-pages', $status['branch']);
}
}

View File

@ -0,0 +1,28 @@
<?php
use PHPGit\Git;
use Symfony\Component\Filesystem\Filesystem;
require_once __DIR__ . '/../BaseTestCase.php';
class CloneCommandTest extends BaseTestCase
{
public function testClone()
{
$git = new Git();
$git->clone('https://github.com/kzykhys/Text.git', $this->directory);
$git->setRepository($this->directory);
$this->assertFileExists($this->directory . '/.git');
$filesystem = new Filesystem();
$filesystem->remove($this->directory);
$git->setRepository('.');
$git->clone('https://github.com/kzykhys/Text.git', $this->directory, array('shared' => true));
$this->assertFileExists($this->directory . '/.git');
}
}

View File

@ -0,0 +1,26 @@
<?php
use PHPGit\Git;
use Symfony\Component\Filesystem\Filesystem;
require_once __DIR__ . '/../BaseTestCase.php';
class CommitCommandTest extends BaseTestCase
{
public function testCommit()
{
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem = new Filesystem();
$filesystem->dumpFile($this->directory . '/test.txt', '');
$git->add('test.txt');
$git->commit('Initial commit');
$logs = $git->log('test.txt');
$this->assertCount(1, $logs);
}
}

View File

@ -0,0 +1,55 @@
<?php
use PHPGit\Git;
require_once __DIR__ . '/../BaseTestCase.php';
class ConfigCommandTest extends BaseTestCase
{
public function testConfigSetAndList()
{
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$before = $git->config();
$git->config->set('user.name', 'John Doe');
$config = $git->config();
$this->assertArrayHasKey('user.name', $config);
$expected = 'John Doe';
if (isset($before['user.name'])) {
$expected = $before['user.name'] . "\n" . $expected;
}
$this->assertEquals($expected, $config['user.name']);
}
public function testConfigAdd()
{
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$before = $git->config();
$git->config->set('user.name', 'John Doe');
$git->config->add('user.name', 'Foo');
$config = $git->config();
$this->assertArrayHasKey('user.name', $config);
$expected = "John Doe\nFoo";
if (isset($before['user.name'])) {
$expected = $before['user.name'] . "\n" . $expected;
}
$this->assertEquals($expected, $config['user.name']);
}
}

View File

@ -0,0 +1,36 @@
<?php
use PHPGit\Git;
use Symfony\Component\Filesystem\Filesystem;
require_once __DIR__ . '/../BaseTestCase.php';
class DescribeCommandTest extends BaseTestCase
{
public function testDescribeTags()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/README.md', 'hello');
$git->add('README.md');
$git->commit('Initial commit');
$git->tag->create('v1.0.0');
$version = $git->describe->tags('HEAD');
$this->assertEquals('v1.0.0', $version);
$filesystem->dumpFile($this->directory . '/README.md', 'hello2');
$git->add('README.md');
$git->commit('Fixes README');
$version = $git->describe->tags('HEAD');
$this->assertStringStartsWith('v1.0.0', $version);
$this->assertStringEndsNotWith('v1.0.0', $version);
}
}

View File

@ -0,0 +1,36 @@
<?php
use PHPGit\Git;
require_once __DIR__ . '/../BaseTestCase.php';
class FetchCommandTest extends BaseTestCase
{
public function testFetch()
{
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
$git->fetch('origin', '+refs/heads/*:refs/remotes/origin/*');
$tags = $git->tag();
$this->assertContains('v1.0.0', $tags);
}
public function testFetchAll()
{
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
$git->fetch->all();
$tags = $git->tag();
$this->assertContains('v1.0.0', $tags);
}
}

View File

@ -0,0 +1,101 @@
<?php
use PHPGit\Git;
use Symfony\Component\Filesystem\Filesystem;
require_once __DIR__ . '/../BaseTestCase.php';
class MergeCommandTest extends BaseTestCase
{
public function testMerge()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/test.txt', 'foo');
$git->add('test.txt');
$git->commit('master');
$git->checkout->create('develop');
$filesystem->dumpFile($this->directory . '/test.txt', 'bar');
$git->add('test.txt');
$git->commit('develop');
$git->checkout('master');
$this->assertEquals('foo', file_get_contents($this->directory . '/test.txt'));
$git->merge('develop');
$this->assertEquals('bar', file_get_contents($this->directory . '/test.txt'));
}
/**
* @expectedException \PHPGit\Exception\GitException
*/
public function testMergeFail()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
// branch:master
$filesystem->dumpFile($this->directory . '/test.txt', 'foo');
$git->add('test.txt');
$git->commit('master');
// branch:develop
$git->checkout->create('develop');
$filesystem->dumpFile($this->directory . '/test.txt', 'bar');
$git->add('test.txt');
$git->commit('develop');
// branch:master
$git->checkout('master');
$filesystem->dumpFile($this->directory . '/test.txt', 'baz');
$git->merge('develop');
}
public function testMergeAbort()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
// branch:master
$filesystem->dumpFile($this->directory . '/test.txt', 'foo');
$git->add('test.txt');
$git->commit('master');
// branch:develop
$git->checkout->create('develop');
$filesystem->dumpFile($this->directory . '/test.txt', 'bar');
$git->add('test.txt');
$git->commit('develop');
// branch:master
$git->checkout('master');
$filesystem->dumpFile($this->directory . '/test.txt', 'baz');
$git->add('test.txt');
$git->commit('master');
try {
$git->merge('develop');
$this->fail('$git->merge("develop") should fail');
} catch (Exception $e) {
}
$git->merge->abort();
$this->assertEquals('baz', file_get_contents($this->directory . '/test.txt'));
}
}

View File

@ -0,0 +1,26 @@
<?php
use PHPGit\Git;
use Symfony\Component\Filesystem\Filesystem;
require_once __DIR__ . '/../BaseTestCase.php';
class MvCommandTest extends BaseTestCase
{
public function testMv()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/test.txt', 'foo');
$git->add('test.txt');
$git->commit('Initial commit');
$git->mv('test.txt', 'test2.txt');
$this->assertFileExists($this->directory . '/test2.txt');
}
}

View File

@ -0,0 +1,22 @@
<?php
use PHPGit\Git;
require_once __DIR__ . '/../BaseTestCase.php';
class PullCommandTest extends BaseTestCase
{
public function testPull()
{
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
$git->pull('origin', 'master');
$this->assertFileExists($this->directory . '/README.md');
}
}

View File

@ -0,0 +1,34 @@
<?php
use PHPGit\Git;
use Symfony\Component\Filesystem\Filesystem;
require_once __DIR__ . '/../BaseTestCase.php';
class PushCommandTest extends BaseTestCase
{
public function testPush()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory, array('shared' => true, 'bare' => true));
$git->clone('file://' . realpath($this->directory), $this->directory.'2');
$git->setRepository($this->directory.'2');
$filesystem->dumpFile($this->directory.'2/test.txt', 'foobar');
$git->add('test.txt');
$git->commit('test');
$git->push('origin', 'master');
$git->clone('file://' . realpath($this->directory), $this->directory.'3');
$this->assertFileExists($this->directory.'3/test.txt');
$filesystem->remove($this->directory.'2');
$filesystem->remove($this->directory.'3');
}
}

View File

@ -0,0 +1,154 @@
<?php
use PHPGit\Git;
use Symfony\Component\Filesystem\Filesystem;
require_once __DIR__ . '/../BaseTestCase.php';
class RebaseCommandTest extends BaseTestCase
{
public function testRebase()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/test.txt', '123');
$git->add('test.txt');
$git->commit('initial commit');
$git->checkout->create('next');
$filesystem->dumpFile($this->directory . '/test2.txt', '123');
$git->add('test2.txt');
$git->commit('test');
$git->checkout('master');
$git->rebase('next', 'master');
$this->assertFileExists($this->directory. '/test2.txt');
}
public function testRebaseOnto()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/test.txt', '123');
$git->add('test.txt');
$git->commit('initial commit');
$git->checkout->create('next');
$filesystem->dumpFile($this->directory . '/test2.txt', '123');
$git->add('test2.txt');
$git->commit('test');
$git->checkout->create('topic', 'next');
$filesystem->dumpFile($this->directory . '/test3.txt', '123');
$git->add('test3.txt');
$git->commit('test');
$git->rebase('next', null, array('onto' => 'master'));
$this->assertFileNotExists($this->directory . '/test2.txt');
}
public function testRebaseContinue()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/test.txt', 'foo');
$git->add('test.txt');
$git->commit('initial commit');
$git->checkout->create('next');
$filesystem->dumpFile($this->directory . '/test.txt', 'bar');
$git->add('test.txt');
$git->commit('next commit');
$git->checkout('master');
$filesystem->dumpFile($this->directory . '/test.txt', 'baz');
$git->add('test.txt');
$git->commit('master commit');
try {
$git->rebase('next');
$this->fail('GitException should be thrown');
} catch (\PHPGit\Exception\GitException $e) {
}
$filesystem->dumpFile($this->directory . '/test.txt', 'foobar');
$git->add('test.txt');
$git->rebase->continues();
}
public function testRebaseAbort()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/test.txt', 'foo');
$git->add('test.txt');
$git->commit('initial commit');
$git->checkout->create('next');
$filesystem->dumpFile($this->directory . '/test.txt', 'bar');
$git->add('test.txt');
$git->commit('next commit');
$git->checkout('master');
$filesystem->dumpFile($this->directory . '/test.txt', 'baz');
$git->add('test.txt');
$git->commit('master commit');
try {
$git->rebase('next');
$this->fail('GitException should be thrown');
} catch (\PHPGit\Exception\GitException $e) {
}
$git->rebase->abort();
}
public function testRebaseSkip()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/test.txt', 'foo');
$git->add('test.txt');
$git->commit('initial commit');
$git->checkout->create('next');
$filesystem->dumpFile($this->directory . '/test.txt', 'bar');
$git->add('test.txt');
$git->commit('next commit');
$git->checkout('master');
$filesystem->dumpFile($this->directory . '/test.txt', 'baz');
$git->add('test.txt');
$git->commit('master commit');
try {
$git->rebase('next');
$this->fail('GitException should be thrown');
} catch (\PHPGit\Exception\GitException $e) {
}
$git->rebase->skip();
}
}

View File

@ -0,0 +1,28 @@
<?php
use PHPGit\Git;
require_once __DIR__ . '/../../BaseTestCase.php';
class SetBranchesCommandTest extends BaseTestCase
{
public function testSetBranches()
{
$git = new Git();
$git->clone('https://github.com/kzykhys/Text.git', $this->directory);
$git->setRepository($this->directory);
$git->remote->branches('origin', array('master'));
}
public function testSetBranchesAdd()
{
$git = new Git();
$git->clone('https://github.com/kzykhys/Text.git', $this->directory);
$git->setRepository($this->directory);
$git->remote->branches->add('origin', array('gh-pages'));
}
}

View File

@ -0,0 +1,56 @@
<?php
use PHPGit\Git;
require_once __DIR__ . '/../../BaseTestCase.php';
class SetHeadCommandTest extends BaseTestCase
{
public function testSetHead()
{
$git = new Git();
$git->clone('https://github.com/kzykhys/Text.git', $this->directory);
$git->setRepository($this->directory);
$before = $git->branch(array('all' => true));
$git->remote->head('origin', 'master');
$after = $git->branch(array('all' => true));
$this->assertEquals($before, $after);
}
public function testSetHeadDelete()
{
$git = new Git();
$git->clone('https://github.com/kzykhys/Text.git', $this->directory);
$git->setRepository($this->directory);
$before = $git->branch(array('all' => true));
$git->remote->head->delete('origin');
$after = $git->branch(array('all' => true));
$this->assertNotEquals($before, $after);
}
public function testSetHeadRemote()
{
$git = new Git();
$git->clone('https://github.com/kzykhys/Text.git', $this->directory);
$git->setRepository($this->directory);
$before = $git->branch(array('all' => true));
$git->remote->head->delete('origin');
$git->remote->head->remote('origin');
$after = $git->branch(array('all' => true));
$this->assertEquals($before, $after);
}
}

View File

@ -0,0 +1,46 @@
<?php
use PHPGit\Git;
require_once __DIR__ . '/../../BaseTestCase.php';
class SetUrlCommandTest extends BaseTestCase
{
public function testSetUrl()
{
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$git->remote->add('origin', 'http://example.com/test.git');
$git->remote->url('origin', 'https://github.com/kzykhys/Text.git', 'http://example.com/test.git');
$remotes = $git->remote();
$this->assertEquals('https://github.com/kzykhys/Text.git', $remotes['origin']['fetch']);
}
public function testSetUrlAdd()
{
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$git->remote->add('origin', 'http://example.com/test.git');
$git->remote->url->add('origin', 'https://github.com/kzykhys/Text.git');
}
public function testSetUrlDelete()
{
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$git->remote->add('origin', 'http://example.com/test.git');
$git->remote->url->add('origin', 'https://github.com/kzykhys/Text.git');
$git->remote->url->delete('origin', 'https://github.com');
$remotes = $git->remote();
$this->assertEquals('http://example.com/test.git', $remotes['origin']['fetch']);
}
}

View File

@ -0,0 +1,100 @@
<?php
use PHPGit\Git;
require_once __DIR__ . '/../BaseTestCase.php';
class RemoteCommandTest extends BaseTestCase
{
public function testRemote()
{
$git = new Git();
$git->clone('https://github.com/kzykhys/Text.git', $this->directory);
$git->setRepository($this->directory);
$remotes = $git->remote();
$this->assertEquals(array(
'origin' => array(
'fetch' => 'https://github.com/kzykhys/Text.git',
'push' => 'https://github.com/kzykhys/Text.git'
)
), $remotes);
}
public function testRemoteAdd()
{
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
$remotes = $git->remote();
$this->assertEquals(array(
'origin' => array(
'fetch' => 'https://github.com/kzykhys/Text.git',
'push' => 'https://github.com/kzykhys/Text.git'
)
), $remotes);
}
public function testRemoteRename()
{
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
$git->remote->rename('origin', 'upstream');
$remotes = $git->remote();
$this->assertEquals(array(
'upstream' => array(
'fetch' => 'https://github.com/kzykhys/Text.git',
'push' => 'https://github.com/kzykhys/Text.git'
)
), $remotes);
}
public function testRemoteRm()
{
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
$git->remote->rm('origin');
$remotes = $git->remote();
$this->assertEquals(array(), $remotes);
}
public function testRemoteShow()
{
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
$this->assertNotEmpty($git->remote->show('origin'));
}
public function testRemotePrune()
{
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
$git->remote->prune('origin');
}
/**
* @expectedException \BadMethodCallException
*/
public function testBadMethodCall()
{
$git = new Git();
$git->remote->foo();
}
}

View File

@ -0,0 +1,128 @@
<?php
use PHPGit\Git;
use Symfony\Component\Filesystem\Filesystem;
require_once __DIR__ . '/../BaseTestCase.php';
class ResetCommandTest extends BaseTestCase
{
public function testReset()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/README.md', 'foo');
$git->add('README.md');
$git->commit('Initial commit');
$filesystem->dumpFile($this->directory . '/README.md', 'hello');
$git->add('README.md');
$git->reset('README.md', 'HEAD');
}
public function testResetSoft()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/README.md', 'foo');
$git->add('README.md');
$git->commit('Initial commit');
$filesystem->dumpFile($this->directory . '/README.md', 'hello');
$git->reset->soft();
}
public function testResetMixed()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/README.md', 'foo');
$git->add('README.md');
$git->commit('Initial commit');
$filesystem->dumpFile($this->directory . '/README.md', 'hello');
$git->reset->mixed();
}
public function testResetHard()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/README.md', 'foo');
$git->add('README.md');
$git->commit('Initial commit');
$filesystem->dumpFile($this->directory . '/README.md', 'hello');
$git->reset->hard('HEAD');
}
public function testResetMerge()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/README.md', 'foo');
$git->add('README.md');
$git->commit('Initial commit');
$filesystem->dumpFile($this->directory . '/README.md', 'hello');
$git->reset->merge();
}
public function testResetKeep()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/README.md', 'foo');
$git->add('README.md');
$git->commit('Initial commit');
$filesystem->dumpFile($this->directory . '/README.md', 'hello');
$git->reset->keep();
}
/**
* @expectedException InvalidArgumentException
*/
public function testResetInvalidMode()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/README.md', 'foo');
$git->add('README.md');
$git->commit('Initial commit');
$filesystem->dumpFile($this->directory . '/README.md', 'hello');
$git->reset->mode('foo');
}
}

View File

@ -0,0 +1,51 @@
<?php
use PHPGit\Git;
use Symfony\Component\Filesystem\Filesystem;
require_once __DIR__ . '/../BaseTestCase.php';
class RmCommandTest extends BaseTestCase
{
public function testRm()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/README.md', 'foo');
$filesystem->dumpFile($this->directory . '/bin/test.php', 'foo');
$git->add(array('README.md', 'bin/test.php'));
$git->commit('Initial commit');
$git->rm('README.md');
$git->rm('bin', array('recursive' => true));
$this->assertFileNotExists($this->directory . '/README.md');
}
public function testRmCached()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/README.md', 'foo');
$git->add('README.md');
$git->commit('Initial commit');
$git->rm->cached('README.md');
$git->commit('Delete README.md');
$this->assertFileExists($this->directory . '/README.md');
$tree = $git->tree();
$this->assertEquals(array(), $tree);
}
}

View File

@ -0,0 +1,71 @@
<?php
use PHPGit\Git;
use Symfony\Component\Filesystem\Filesystem;
require_once __DIR__ . '/../BaseTestCase.php';
class ShortlogCommandTest extends BaseTestCase
{
public function testShortlog()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$git->config->set('user.name', 'Name One');
$git->config->set('user.email', 'one@example.com');
$filesystem->dumpFile($this->directory . '/test.txt', '');
$git->add('test.txt');
$git->commit('1');
$filesystem->dumpFile($this->directory . '/test2.txt', '');
$git->add('test2.txt');
$git->commit('2');
$git->config->set('user.name', 'Name Two');
$git->config->set('user.email', 'two@example.com');
$filesystem->dumpFile($this->directory . '/test3.txt', '');
$git->add('test3.txt');
$git->commit('3');
$shortlog = $git->shortlog();
$this->assertCount(2, $shortlog);
$this->assertCount(2, $shortlog['Name One <one@example.com>']);
$this->assertCount(1, $shortlog['Name Two <two@example.com>']);
$this->assertEquals('1', $shortlog['Name One <one@example.com>'][0]['subject']);
}
public function testShortlogSummary()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$git->config->set('user.name', 'Name One');
$git->config->set('user.email', 'one@example.com');
$filesystem->dumpFile($this->directory . '/test.txt', '');
$git->add('test.txt');
$git->commit('1');
$filesystem->dumpFile($this->directory . '/test2.txt', '');
$git->add('test2.txt');
$git->commit('2');
$git->config->set('user.name', 'Name Two');
$git->config->set('user.email', 'two@example.com');
$filesystem->dumpFile($this->directory . '/test3.txt', '');
$git->add('test3.txt');
$git->commit('3');
$summary = $git->shortlog->summary();
$this->assertEquals(array(
'Name One <one@example.com>' => 2,
'Name Two <two@example.com>' => 1
), $summary);
}
}

View File

@ -0,0 +1,26 @@
<?php
use PHPGit\Git;
use Symfony\Component\Filesystem\Filesystem;
require_once __DIR__ . '/../BaseTestCase.php';
class ShowCommandTest extends BaseTestCase
{
public function testShow()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/README.md', 'foobar');
$git->add('README.md');
$git->commit('Initial commit');
$git->show('master', array('format' => 'oneline'));
}
}

View File

@ -0,0 +1,204 @@
<?php
use PHPGit\Git;
use Symfony\Component\Filesystem\Filesystem;
require_once __DIR__ . '/../BaseTestCase.php';
class StashCommandTest extends BaseTestCase
{
public function testStash()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/README.md', 'hello');
$git->add('.');
$git->commit('Initial commit');
$filesystem->dumpFile($this->directory . '/README.md', 'hi!');
$git->stash();
$this->assertEquals('hello', file_get_contents($this->directory.'/README.md'));
}
public function testStashSave()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/README.md', 'hello');
$git->add('.');
$git->commit('Initial commit');
$filesystem->dumpFile($this->directory . '/README.md', 'hi!');
$git->stash->save('stash test');
$this->assertEquals('hello', file_get_contents($this->directory.'/README.md'));
}
public function testStashList()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/README.md', 'hello');
$git->add('.');
$git->commit('Initial commit');
$filesystem->dumpFile($this->directory . '/README.md', 'hi!');
$git->stash();
$stashes = $git->stash->lists();
$this->assertCount(1, $stashes);
$this->assertEquals('master', $stashes[0]['branch']);
$this->assertStringEndsWith('Initial commit', $stashes[0]['message']);
}
public function testStashShow()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/README.md', 'hello');
$git->add('.');
$git->commit('Initial commit');
$filesystem->dumpFile($this->directory . '/README.md', 'hi!');
$git->stash();
$git->stash->show('stash@{0}');
}
public function testStashDrop()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/README.md', 'hello');
$git->add('.');
$git->commit('Initial commit');
$filesystem->dumpFile($this->directory . '/README.md', 'hi!');
$git->stash();
$git->stash->drop();
$filesystem->dumpFile($this->directory . '/README.md', 'hi!');
$git->stash();
$git->stash->drop('stash@{0}');
$this->assertCount(0, $git->stash->lists());
}
public function testStashPop()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/README.md', 'hello');
$git->add('.');
$git->commit('Initial commit');
$filesystem->dumpFile($this->directory . '/README.md', 'hi!');
$git->stash->save('stash#1');
$filesystem->dumpFile($this->directory . '/README.md', 'bar');
$git->stash->save('stash#2');
$git->stash->pop('stash@{1}');
$this->assertEquals('hi!', file_get_contents($this->directory.'/README.md'));
$this->assertCount(1, $git->stash->lists());
}
public function testStashApply()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/README.md', 'hello');
$git->add('.');
$git->commit('Initial commit');
$filesystem->dumpFile($this->directory . '/README.md', 'hi!');
$git->stash->save('stash#1');
$filesystem->dumpFile($this->directory . '/README.md', 'bar');
$git->stash->save('stash#2');
$git->stash->apply('stash@{1}');
$this->assertEquals('hi!', file_get_contents($this->directory.'/README.md'));
$this->assertCount(2, $git->stash->lists());
}
public function testStashBranch()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/README.md', 'hello');
$git->add('.');
$git->commit('Initial commit');
$filesystem->dumpFile($this->directory . '/README.md', 'hi!');
$git->stash();
$git->stash->branch('dev', 'stash@{0}');
$status = $git->status();
$this->assertEquals('dev', $status['branch']);
$this->assertEquals('hi!', file_get_contents($this->directory.'/README.md'));
}
public function testStashClear()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/README.md', 'hello');
$git->add('.');
$git->commit('Initial commit');
$filesystem->dumpFile($this->directory . '/README.md', 'hi!');
$git->stash();
$git->stash->clear();
$this->assertCount(0, $git->stash->lists());
}
public function testStashCreate()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/README.md', 'hello');
$git->add('.');
$git->commit('Initial commit');
$filesystem->dumpFile($this->directory . '/README.md', 'hi!');
$object = $git->stash->create();
$this->assertNotEmpty($object);
}
}

View File

@ -0,0 +1,76 @@
<?php
use PHPGit\Command\StatusCommand;
use PHPGit\Git;
use Symfony\Component\Filesystem\Filesystem;
require_once __DIR__ . '/../BaseTestCase.php';
class StatusCommandTest extends BaseTestCase
{
public function testStatus()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/item1.txt', '1');
$filesystem->dumpFile($this->directory . '/item2.txt', '2');
$filesystem->dumpFile($this->directory . '/item3.txt', '3');
$git->add('item1.txt');
$git->add('item2.txt');
$filesystem->dumpFile($this->directory . '/item1.txt', '1-1');
$status = $git->status();
$this->assertEquals(array(
'branch' => 'master',
'changes' => array(
array('file' => 'item1.txt', 'index' => StatusCommand::ADDED, 'work_tree' => StatusCommand::MODIFIED),
array('file' => 'item2.txt', 'index' => StatusCommand::ADDED, 'work_tree' => StatusCommand::UNMODIFIED),
array('file' => 'item3.txt', 'index' => StatusCommand::UNTRACKED, 'work_tree' => StatusCommand::UNTRACKED),
)
), $status);
}
public function testDetachedHeadStatus()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/item1.txt', '1');
$git->add('item1.txt');
$git->commit('initial commit');
$logs = $git->log();
$hash = $logs[0]['hash'];
$git->checkout($hash);
$status = $git->status();
$this->assertEquals(null, $status['branch']);
}
public function testTrackingBranchStatus()
{
$filesystem = new Filesystem();
$git = new Git();
$git->clone('https://github.com/kzykhys/Text.git', $this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/test.txt', '1');
$git->add('test.txt');
$git->commit('test');
$status = $git->status();
$this->assertEquals('master', $status['branch']);
}
}

View File

@ -0,0 +1,58 @@
<?php
use PHPGit\Git;
use Symfony\Component\Filesystem\Filesystem;
require_once __DIR__ . '/../BaseTestCase.php';
class TagCommandTest extends BaseTestCase
{
public function testTagDelete()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/README.md', 'hello');
$git->add('.');
$git->commit('Initial commit');
$git->tag->create('v1.0.0');
$git->tag->delete('v1.0.0');
$this->assertCount(0, $git->tag());
}
/**
* @expectedException \PHPGit\Exception\GitException
*/
public function testTagVerify()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/README.md', 'hello');
$git->add('.');
$git->commit('Initial commit');
$git->tag->create('v1.0.0');
$git->tag->verify('v1.0.0');
}
public function testCreateTagFromCommit()
{
$filesystem = new Filesystem();
$git = new Git();
$git->init($this->directory);
$git->setRepository($this->directory);
$filesystem->dumpFile($this->directory . '/README.md', 'hello');
$git->add('.');
$git->commit('Initial commit');
$log = $git->log(null, null, array('limit' => 1));
$git->tag->create('v1.0.0', $log[0]['hash']);
$this->assertCount(1, $git->tag());
}
}

View File

@ -0,0 +1,23 @@
<?php
use PHPGit\Exception\GitException;
use PHPGit\Git;
class GitExceptionTest extends PHPUnit_Framework_TestCase
{
public function testException()
{
$git = new Git();
$git->setRepository(sys_get_temp_dir());
try {
$git->status();
$this->fail('Previous operation should fail');
} catch (GitException $e) {
$command = $e->getCommandLine();
$command = str_replace(array('"', "'"), '', $command);
$this->assertStringEndsWith('status --porcelain -s -b --null', $command);
}
}
}

View File

@ -0,0 +1,33 @@
<?php
use PHPGit\Git;
class GitTest extends PHPUnit_Framework_TestCase
{
public function testGetVersion()
{
$git = new Git();
$this->assertNotEmpty($git->getVersion());
}
/**
* @expectedException \PHPGit\Exception\GitException
*/
public function testInvalidGitBinary()
{
$git = new Git();
$git->setBin('/foo/bar');
$git->getVersion();
}
/**
* @expectedException \BadMethodCallException
*/
public function testBadMethodCall()
{
$git = new Git();
$git->foo();
}
}

View File

@ -0,0 +1,46 @@
CHANGELOG
=========
2.6.0
-----
* deprecated OptionsResolverInterface
* [BC BREAK] removed "array" type hint from OptionsResolverInterface methods
setRequired(), setAllowedValues(), addAllowedValues(), setAllowedTypes() and
addAllowedTypes()
* added OptionsResolver::setDefault()
* added OptionsResolver::hasDefault()
* added OptionsResolver::setNormalizer()
* added OptionsResolver::isRequired()
* added OptionsResolver::getRequiredOptions()
* added OptionsResolver::isMissing()
* added OptionsResolver::getMissingOptions()
* added OptionsResolver::setDefined()
* added OptionsResolver::isDefined()
* added OptionsResolver::getDefinedOptions()
* added OptionsResolver::remove()
* added OptionsResolver::clear()
* deprecated OptionsResolver::replaceDefaults()
* deprecated OptionsResolver::setOptional() in favor of setDefined()
* deprecated OptionsResolver::isKnown() in favor of isDefined()
* [BC BREAK] OptionsResolver::isRequired() returns true now if a required
option has a default value set
* [BC BREAK] merged Options into OptionsResolver and turned Options into an
interface
* deprecated Options::overload() (now in OptionsResolver)
* deprecated Options::set() (now in OptionsResolver)
* deprecated Options::get() (now in OptionsResolver)
* deprecated Options::has() (now in OptionsResolver)
* deprecated Options::replace() (now in OptionsResolver)
* [BC BREAK] Options::get() (now in OptionsResolver) can only be used within
lazy option/normalizer closures now
* [BC BREAK] removed Traversable interface from Options since using within
lazy option/normalizer closures resulted in exceptions
* [BC BREAK] removed Options::all() since using within lazy option/normalizer
closures resulted in exceptions
* [BC BREAK] OptionDefinitionException now extends LogicException instead of
RuntimeException
* [BC BREAK] normalizers are not executed anymore for unset options
* normalizers are executed after validating the options now
* [BC BREAK] an UndefinedOptionsException is now thrown instead of an
InvalidOptionsException when non-existing options are passed

View File

@ -0,0 +1,22 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\OptionsResolver\Exception;
/**
* Thrown when trying to read an option outside of or write it inside of
* {@link \Symfony\Component\OptionsResolver\Options::resolve()}.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class AccessException extends \LogicException implements ExceptionInterface
{
}

View File

@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\OptionsResolver\Exception;
/**
* Marker interface for all exceptions thrown by the OptionsResolver component.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
interface ExceptionInterface
{
}

View File

@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\OptionsResolver\Exception;
/**
* Thrown when an argument is invalid.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
{
}

View File

@ -0,0 +1,23 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\OptionsResolver\Exception;
/**
* Thrown when the value of an option does not match its validation rules.
*
* You should make sure a valid value is passed to the option.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class InvalidOptionsException extends InvalidArgumentException
{
}

View File

@ -0,0 +1,23 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\OptionsResolver\Exception;
/**
* Exception thrown when a required option is missing.
*
* Add the option to the passed options array.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class MissingOptionsException extends InvalidArgumentException
{
}

View File

@ -0,0 +1,26 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\OptionsResolver\Exception;
/**
* Thrown when trying to read an option that has no value set.
*
* When accessing optional options from within a lazy option or normalizer you should first
* check whether the optional option is set. You can do this with `isset($options['optional'])`.
* In contrast to the {@link UndefinedOptionsException}, this is a runtime exception that can
* occur when evaluating lazy options.
*
* @author Tobias Schultze <http://tobion.de>
*/
class NoSuchOptionException extends \OutOfBoundsException implements ExceptionInterface
{
}

View File

@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\OptionsResolver\Exception;
/**
* Thrown when two lazy options have a cyclic dependency.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class OptionDefinitionException extends \LogicException implements ExceptionInterface
{
}

View File

@ -0,0 +1,24 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\OptionsResolver\Exception;
/**
* Exception thrown when an undefined option is passed.
*
* You should remove the options in question from your code or define them
* beforehand.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class UndefinedOptionsException extends InvalidArgumentException
{
}

View File

@ -0,0 +1,19 @@
Copyright (c) 2004-2015 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,22 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\OptionsResolver;
/**
* Contains resolved option values.
*
* @author Bernhard Schussek <bschussek@gmail.com>
* @author Tobias Schultze <http://tobion.de>
*/
interface Options extends \ArrayAccess, \Countable
{
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,212 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\OptionsResolver;
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
use Symfony\Component\OptionsResolver\Exception\MissingOptionsException;
use Symfony\Component\OptionsResolver\Exception\OptionDefinitionException;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @deprecated since version 2.6, to be removed in 3.0. Use {@link OptionsResolver} instead.
*/
interface OptionsResolverInterface
{
/**
* Sets default option values.
*
* The options can either be values of any types or closures that
* evaluate the option value lazily. These closures must have one
* of the following signatures:
*
* <code>
* function (Options $options)
* function (Options $options, $value)
* </code>
*
* The second parameter passed to the closure is the previously
* set default value, in case you are overwriting an existing
* default value.
*
* The closures should return the lazily created option value.
*
* @param array $defaultValues A list of option names as keys and default
* values or closures as values.
*
* @return OptionsResolverInterface The resolver instance.
*/
public function setDefaults(array $defaultValues);
/**
* Replaces default option values.
*
* Old defaults are erased, which means that closures passed here cannot
* access the previous default value. This may be useful to improve
* performance if the previous default value is calculated by an expensive
* closure.
*
* @param array $defaultValues A list of option names as keys and default
* values or closures as values.
*
* @return OptionsResolverInterface The resolver instance.
*/
public function replaceDefaults(array $defaultValues);
/**
* Sets optional options.
*
* This method declares valid option names without setting default values for them.
* If these options are not passed to {@link resolve()} and no default has been set
* for them, they will be missing in the final options array. This can be helpful
* if you want to determine whether an option has been set or not because otherwise
* {@link resolve()} would trigger an exception for unknown options.
*
* @param array $optionNames A list of option names.
*
* @return OptionsResolverInterface The resolver instance.
*/
public function setOptional(array $optionNames);
/**
* Sets required options.
*
* If these options are not passed to {@link resolve()} and no default has been set for
* them, an exception will be thrown.
*
* @param array $optionNames A list of option names.
*
* @return OptionsResolverInterface The resolver instance.
*/
public function setRequired($optionNames);
/**
* Sets allowed values for a list of options.
*
* @param array $allowedValues A list of option names as keys and arrays
* with values acceptable for that option as
* values.
*
* @return OptionsResolverInterface The resolver instance.
*
* @throws InvalidOptionsException If an option has not been defined
* (see {@link isKnown()}) for which
* an allowed value is set.
*/
public function setAllowedValues($allowedValues);
/**
* Adds allowed values for a list of options.
*
* The values are merged with the allowed values defined previously.
*
* @param array $allowedValues A list of option names as keys and arrays
* with values acceptable for that option as
* values.
*
* @return OptionsResolverInterface The resolver instance.
*
* @throws InvalidOptionsException If an option has not been defined
* (see {@link isKnown()}) for which
* an allowed value is set.
*/
public function addAllowedValues($allowedValues);
/**
* Sets allowed types for a list of options.
*
* @param array $allowedTypes A list of option names as keys and type
* names passed as string or array as values.
*
* @return OptionsResolverInterface The resolver instance.
*
* @throws InvalidOptionsException If an option has not been defined for
* which an allowed type is set.
*/
public function setAllowedTypes($allowedTypes);
/**
* Adds allowed types for a list of options.
*
* The types are merged with the allowed types defined previously.
*
* @param array $allowedTypes A list of option names as keys and type
* names passed as string or array as values.
*
* @return OptionsResolverInterface The resolver instance.
*
* @throws InvalidOptionsException If an option has not been defined for
* which an allowed type is set.
*/
public function addAllowedTypes($allowedTypes);
/**
* Sets normalizers that are applied on resolved options.
*
* The normalizers should be closures with the following signature:
*
* <code>
* function (Options $options, $value)
* </code>
*
* The second parameter passed to the closure is the value of
* the option.
*
* The closure should return the normalized value.
*
* @param array $normalizers An array of closures.
*
* @return OptionsResolverInterface The resolver instance.
*/
public function setNormalizers(array $normalizers);
/**
* Returns whether an option is known.
*
* An option is known if it has been passed to either {@link setDefaults()},
* {@link setRequired()} or {@link setOptional()} before.
*
* @param string $option The name of the option.
*
* @return bool Whether the option is known.
*/
public function isKnown($option);
/**
* Returns whether an option is required.
*
* An option is required if it has been passed to {@link setRequired()},
* but not to {@link setDefaults()}. That is, the option has been declared
* as required and no default value has been set.
*
* @param string $option The name of the option.
*
* @return bool Whether the option is required.
*/
public function isRequired($option);
/**
* Returns the combination of the default and the passed options.
*
* @param array $options The custom option values.
*
* @return array A list of options and their values.
*
* @throws InvalidOptionsException If any of the passed options has not
* been defined or does not contain an
* allowed value.
* @throws MissingOptionsException If a required option is missing.
* @throws OptionDefinitionException If a cyclic dependency is detected
* between two lazy options.
*/
public function resolve(array $options = array());
}

View File

@ -0,0 +1,20 @@
OptionsResolver Component
=========================
This component processes and validates option arrays.
Documentation
-------------
The documentation for the component can be found [online] [1].
Resources
---------
You can run the unit tests with the following command:
$ cd path/to/Symfony/Component/OptionsResolver/
$ composer install
$ phpunit
[1]: https://symfony.com/doc/current/components/options_resolver.html

View File

@ -0,0 +1,733 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\OptionsResolver\Tests;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\OptionsResolver\Options;
/**
* @group legacy
*/
class LegacyOptionsResolverTest extends \PHPUnit_Framework_TestCase
{
/**
* @var OptionsResolver
*/
private $resolver;
protected function setUp()
{
$this->resolver = new OptionsResolver();
}
public function testResolve()
{
$this->resolver->setDefaults(array(
'one' => '1',
'two' => '2',
));
$options = array(
'two' => '20',
);
$this->assertEquals(array(
'one' => '1',
'two' => '20',
), $this->resolver->resolve($options));
}
public function testResolveNumericOptions()
{
$this->resolver->setDefaults(array(
'1' => '1',
'2' => '2',
));
$options = array(
'2' => '20',
);
$this->assertEquals(array(
'1' => '1',
'2' => '20',
), $this->resolver->resolve($options));
}
public function testResolveLazy()
{
$this->resolver->setDefaults(array(
'one' => '1',
'two' => function (Options $options) {
return '20';
},
));
$this->assertEquals(array(
'one' => '1',
'two' => '20',
), $this->resolver->resolve(array()));
}
public function testTypeAliasesForAllowedTypes()
{
$this->resolver->setDefaults(array(
'force' => false,
));
$this->resolver->setAllowedTypes(array(
'force' => 'boolean',
));
$this->resolver->resolve(array(
'force' => true,
));
}
public function testResolveLazyDependencyOnOptional()
{
$this->resolver->setDefaults(array(
'one' => '1',
'two' => function (Options $options) {
return $options['one'].'2';
},
));
$options = array(
'one' => '10',
);
$this->assertEquals(array(
'one' => '10',
'two' => '102',
), $this->resolver->resolve($options));
}
public function testResolveLazyDependencyOnMissingOptionalWithoutDefault()
{
$test = $this;
$this->resolver->setOptional(array(
'one',
));
$this->resolver->setDefaults(array(
'two' => function (Options $options) use ($test) {
/* @var \PHPUnit_Framework_TestCase $test */
$test->assertFalse(isset($options['one']));
return '2';
},
));
$options = array();
$this->assertEquals(array(
'two' => '2',
), $this->resolver->resolve($options));
}
public function testResolveLazyDependencyOnOptionalWithoutDefault()
{
$test = $this;
$this->resolver->setOptional(array(
'one',
));
$this->resolver->setDefaults(array(
'two' => function (Options $options) use ($test) {
/* @var \PHPUnit_Framework_TestCase $test */
$test->assertTrue(isset($options['one']));
return $options['one'].'2';
},
));
$options = array(
'one' => '10',
);
$this->assertEquals(array(
'one' => '10',
'two' => '102',
), $this->resolver->resolve($options));
}
public function testResolveLazyDependencyOnRequired()
{
$this->resolver->setRequired(array(
'one',
));
$this->resolver->setDefaults(array(
'two' => function (Options $options) {
return $options['one'].'2';
},
));
$options = array(
'one' => '10',
);
$this->assertEquals(array(
'one' => '10',
'two' => '102',
), $this->resolver->resolve($options));
}
public function testResolveLazyReplaceDefaults()
{
$test = $this;
$this->resolver->setDefaults(array(
'one' => function (Options $options) use ($test) {
/* @var \PHPUnit_Framework_TestCase $test */
$test->fail('Previous closure should not be executed');
},
));
$this->resolver->replaceDefaults(array(
'one' => function (Options $options, $previousValue) {
return '1';
},
));
$this->assertEquals(array(
'one' => '1',
), $this->resolver->resolve(array()));
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
* @expectedExceptionMessage The option "foo" does not exist. Defined options are: "one", "three", "two".
*/
public function testResolveFailsIfNonExistingOption()
{
$this->resolver->setDefaults(array(
'one' => '1',
));
$this->resolver->setRequired(array(
'two',
));
$this->resolver->setOptional(array(
'three',
));
$this->resolver->resolve(array(
'foo' => 'bar',
));
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException
*/
public function testResolveFailsIfMissingRequiredOption()
{
$this->resolver->setRequired(array(
'one',
));
$this->resolver->setDefaults(array(
'two' => '2',
));
$this->resolver->resolve(array(
'two' => '20',
));
}
public function testResolveSucceedsIfOptionValueAllowed()
{
$this->resolver->setDefaults(array(
'one' => '1',
));
$this->resolver->setAllowedValues(array(
'one' => array('1', 'one'),
));
$options = array(
'one' => 'one',
);
$this->assertEquals(array(
'one' => 'one',
), $this->resolver->resolve($options));
}
public function testResolveSucceedsIfOptionValueAllowed2()
{
$this->resolver->setDefaults(array(
'one' => '1',
'two' => '2',
));
$this->resolver->setAllowedValues(array(
'one' => '1',
'two' => '2',
));
$this->resolver->addAllowedValues(array(
'one' => 'one',
'two' => 'two',
));
$options = array(
'one' => '1',
'two' => 'two',
);
$this->assertEquals(array(
'one' => '1',
'two' => 'two',
), $this->resolver->resolve($options));
}
public function testResolveSucceedsIfOptionalWithAllowedValuesNotSet()
{
$this->resolver->setRequired(array(
'one',
));
$this->resolver->setOptional(array(
'two',
));
$this->resolver->setAllowedValues(array(
'one' => array('1', 'one'),
'two' => array('2', 'two'),
));
$options = array(
'one' => '1',
);
$this->assertEquals(array(
'one' => '1',
), $this->resolver->resolve($options));
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
*/
public function testResolveFailsIfOptionValueNotAllowed()
{
$this->resolver->setDefaults(array(
'one' => '1',
));
$this->resolver->setAllowedValues(array(
'one' => array('1', 'one'),
));
$this->resolver->resolve(array(
'one' => '2',
));
}
public function testResolveSucceedsIfOptionTypeAllowed()
{
$this->resolver->setDefaults(array(
'one' => '1',
));
$this->resolver->setAllowedTypes(array(
'one' => 'string',
));
$options = array(
'one' => 'one',
);
$this->assertEquals(array(
'one' => 'one',
), $this->resolver->resolve($options));
}
public function testResolveSucceedsIfOptionTypeAllowedPassArray()
{
$this->resolver->setDefaults(array(
'one' => '1',
));
$this->resolver->setAllowedTypes(array(
'one' => array('string', 'bool'),
));
$options = array(
'one' => true,
);
$this->assertEquals(array(
'one' => true,
), $this->resolver->resolve($options));
}
public function testResolveSucceedsIfOptionTypeAllowedPassObject()
{
$this->resolver->setDefaults(array(
'one' => '1',
));
$this->resolver->setAllowedTypes(array(
'one' => 'object',
));
$object = new \stdClass();
$options = array(
'one' => $object,
);
$this->assertEquals(array(
'one' => $object,
), $this->resolver->resolve($options));
}
public function testResolveSucceedsIfOptionTypeAllowedPassClass()
{
$this->resolver->setDefaults(array(
'one' => '1',
));
$this->resolver->setAllowedTypes(array(
'one' => '\stdClass',
));
$object = new \stdClass();
$options = array(
'one' => $object,
);
$this->assertEquals(array(
'one' => $object,
), $this->resolver->resolve($options));
}
public function testResolveSucceedsIfOptionTypeAllowedAddTypes()
{
$this->resolver->setDefaults(array(
'one' => '1',
'two' => '2',
));
$this->resolver->setAllowedTypes(array(
'one' => 'string',
'two' => 'bool',
));
$this->resolver->addAllowedTypes(array(
'one' => 'float',
'two' => 'integer',
));
$options = array(
'one' => 1.23,
'two' => false,
);
$this->assertEquals(array(
'one' => 1.23,
'two' => false,
), $this->resolver->resolve($options));
}
public function testResolveSucceedsIfOptionalWithTypeAndWithoutValue()
{
$this->resolver->setOptional(array(
'one',
'two',
));
$this->resolver->setAllowedTypes(array(
'one' => 'string',
'two' => 'int',
));
$options = array(
'two' => 1,
);
$this->assertEquals(array(
'two' => 1,
), $this->resolver->resolve($options));
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
*/
public function testResolveFailsIfOptionTypeNotAllowed()
{
$this->resolver->setDefaults(array(
'one' => '1',
));
$this->resolver->setAllowedTypes(array(
'one' => array('string', 'bool'),
));
$this->resolver->resolve(array(
'one' => 1.23,
));
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
*/
public function testResolveFailsIfOptionTypeNotAllowedMultipleOptions()
{
$this->resolver->setDefaults(array(
'one' => '1',
'two' => '2',
));
$this->resolver->setAllowedTypes(array(
'one' => 'string',
'two' => 'bool',
));
$this->resolver->resolve(array(
'one' => 'foo',
'two' => 1.23,
));
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
*/
public function testResolveFailsIfOptionTypeNotAllowedAddTypes()
{
$this->resolver->setDefaults(array(
'one' => '1',
));
$this->resolver->setAllowedTypes(array(
'one' => 'string',
));
$this->resolver->addAllowedTypes(array(
'one' => 'bool',
));
$this->resolver->resolve(array(
'one' => 1.23,
));
}
public function testFluidInterface()
{
$this->resolver->setDefaults(array('one' => '1'))
->replaceDefaults(array('one' => '2'))
->setAllowedValues(array('one' => array('1', '2')))
->addAllowedValues(array('one' => array('3')))
->setRequired(array('two'))
->setOptional(array('three'));
$options = array(
'two' => '2',
);
$this->assertEquals(array(
'one' => '2',
'two' => '2',
), $this->resolver->resolve($options));
}
public function testKnownIfDefaultWasSet()
{
$this->assertFalse($this->resolver->isKnown('foo'));
$this->resolver->setDefaults(array(
'foo' => 'bar',
));
$this->assertTrue($this->resolver->isKnown('foo'));
}
public function testKnownIfRequired()
{
$this->assertFalse($this->resolver->isKnown('foo'));
$this->resolver->setRequired(array(
'foo',
));
$this->assertTrue($this->resolver->isKnown('foo'));
}
public function testKnownIfOptional()
{
$this->assertFalse($this->resolver->isKnown('foo'));
$this->resolver->setOptional(array(
'foo',
));
$this->assertTrue($this->resolver->isKnown('foo'));
}
public function testRequiredIfRequired()
{
$this->assertFalse($this->resolver->isRequired('foo'));
$this->resolver->setRequired(array(
'foo',
));
$this->assertTrue($this->resolver->isRequired('foo'));
}
public function testNormalizersTransformFinalOptions()
{
$this->resolver->setDefaults(array(
'foo' => 'bar',
'bam' => 'baz',
));
$this->resolver->setNormalizers(array(
'foo' => function (Options $options, $value) {
return $options['bam'].'['.$value.']';
},
));
$expected = array(
'foo' => 'baz[bar]',
'bam' => 'baz',
);
$this->assertEquals($expected, $this->resolver->resolve(array()));
$expected = array(
'foo' => 'boo[custom]',
'bam' => 'boo',
);
$this->assertEquals($expected, $this->resolver->resolve(array(
'foo' => 'custom',
'bam' => 'boo',
)));
}
public function testResolveWithoutOptionSucceedsIfRequiredAndDefaultValue()
{
$this->resolver->setRequired(array(
'foo',
));
$this->resolver->setDefaults(array(
'foo' => 'bar',
));
$this->assertEquals(array(
'foo' => 'bar',
), $this->resolver->resolve(array()));
}
public function testResolveWithoutOptionSucceedsIfDefaultValueAndRequired()
{
$this->resolver->setDefaults(array(
'foo' => 'bar',
));
$this->resolver->setRequired(array(
'foo',
));
$this->assertEquals(array(
'foo' => 'bar',
), $this->resolver->resolve(array()));
}
public function testResolveSucceedsIfOptionRequiredAndValueAllowed()
{
$this->resolver->setRequired(array(
'one', 'two',
));
$this->resolver->setAllowedValues(array(
'two' => array('2'),
));
$options = array(
'one' => '1',
'two' => '2',
);
$this->assertEquals($options, $this->resolver->resolve($options));
}
public function testResolveSucceedsIfValueAllowedCallbackReturnsTrue()
{
$this->resolver->setRequired(array(
'test',
));
$this->resolver->setAllowedValues(array(
'test' => function ($value) {
return true;
},
));
$options = array(
'test' => true,
);
$this->assertEquals($options, $this->resolver->resolve($options));
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
*/
public function testResolveFailsIfValueAllowedCallbackReturnsFalse()
{
$this->resolver->setRequired(array(
'test',
));
$this->resolver->setAllowedValues(array(
'test' => function ($value) {
return false;
},
));
$options = array(
'test' => true,
);
$this->assertEquals($options, $this->resolver->resolve($options));
}
public function testClone()
{
$this->resolver->setDefaults(array('one' => '1'));
$clone = clone $this->resolver;
// Changes after cloning don't affect each other
$this->resolver->setDefaults(array('two' => '2'));
$clone->setDefaults(array('three' => '3'));
$this->assertEquals(array(
'one' => '1',
'two' => '2',
), $this->resolver->resolve());
$this->assertEquals(array(
'one' => '1',
'three' => '3',
), $clone->resolve());
}
public function testOverloadReturnsThis()
{
$this->assertSame($this->resolver, $this->resolver->overload('foo', 'bar'));
}
public function testOverloadCallsSet()
{
$this->resolver->overload('foo', 'bar');
$this->assertSame(array('foo' => 'bar'), $this->resolver->resolve());
}
}

View File

@ -0,0 +1,337 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\OptionsResolver\Tests;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* @group legacy
*/
class LegacyOptionsTest extends \PHPUnit_Framework_TestCase
{
/**
* @var OptionsResolver
*/
private $options;
protected function setUp()
{
$this->options = new OptionsResolver();
}
public function testSetLazyOption()
{
$test = $this;
$this->options->set('foo', function (Options $options) use ($test) {
return 'dynamic';
});
$this->assertEquals(array('foo' => 'dynamic'), $this->options->resolve());
}
public function testOverloadKeepsPreviousValue()
{
$test = $this;
// defined by superclass
$this->options->set('foo', 'bar');
// defined by subclass
$this->options->overload('foo', function (Options $options, $previousValue) use ($test) {
/* @var \PHPUnit_Framework_TestCase $test */
$test->assertEquals('bar', $previousValue);
return 'dynamic';
});
$this->assertEquals(array('foo' => 'dynamic'), $this->options->resolve());
}
public function testPreviousValueIsEvaluatedIfLazy()
{
$test = $this;
// defined by superclass
$this->options->set('foo', function (Options $options) {
return 'bar';
});
// defined by subclass
$this->options->overload('foo', function (Options $options, $previousValue) use ($test) {
/* @var \PHPUnit_Framework_TestCase $test */
$test->assertEquals('bar', $previousValue);
return 'dynamic';
});
$this->assertEquals(array('foo' => 'dynamic'), $this->options->resolve());
}
public function testPreviousValueIsNotEvaluatedIfNoSecondArgument()
{
$test = $this;
// defined by superclass
$this->options->set('foo', function (Options $options) use ($test) {
$test->fail('Should not be called');
});
// defined by subclass, no $previousValue argument defined!
$this->options->overload('foo', function (Options $options) use ($test) {
return 'dynamic';
});
$this->assertEquals(array('foo' => 'dynamic'), $this->options->resolve());
}
public function testLazyOptionCanAccessOtherOptions()
{
$test = $this;
$this->options->set('foo', 'bar');
$this->options->set('bam', function (Options $options) use ($test) {
/* @var \PHPUnit_Framework_TestCase $test */
$test->assertEquals('bar', $options->get('foo'));
return 'dynamic';
});
$this->assertEquals(array('foo' => 'bar', 'bam' => 'dynamic'), $this->options->resolve());
}
public function testLazyOptionCanAccessOtherLazyOptions()
{
$test = $this;
$this->options->set('foo', function (Options $options) {
return 'bar';
});
$this->options->set('bam', function (Options $options) use ($test) {
/* @var \PHPUnit_Framework_TestCase $test */
$test->assertEquals('bar', $options->get('foo'));
return 'dynamic';
});
$this->assertEquals(array('foo' => 'bar', 'bam' => 'dynamic'), $this->options->resolve());
}
public function testNormalizer()
{
$this->options->set('foo', 'bar');
$this->options->setNormalizer('foo', function () {
return 'normalized';
});
$this->assertEquals(array('foo' => 'normalized'), $this->options->resolve());
}
public function testNormalizerReceivesUnnormalizedValue()
{
$this->options->set('foo', 'bar');
$this->options->setNormalizer('foo', function (Options $options, $value) {
return 'normalized['.$value.']';
});
$this->assertEquals(array('foo' => 'normalized[bar]'), $this->options->resolve());
}
public function testNormalizerCanAccessOtherOptions()
{
$test = $this;
$this->options->set('foo', 'bar');
$this->options->set('bam', 'baz');
$this->options->setNormalizer('bam', function (Options $options) use ($test) {
/* @var \PHPUnit_Framework_TestCase $test */
$test->assertEquals('bar', $options->get('foo'));
return 'normalized';
});
$this->assertEquals(array('foo' => 'bar', 'bam' => 'normalized'), $this->options->resolve());
}
public function testNormalizerCanAccessOtherLazyOptions()
{
$test = $this;
$this->options->set('foo', function (Options $options) {
return 'bar';
});
$this->options->set('bam', 'baz');
$this->options->setNormalizer('bam', function (Options $options) use ($test) {
/* @var \PHPUnit_Framework_TestCase $test */
$test->assertEquals('bar', $options->get('foo'));
return 'normalized';
});
$this->assertEquals(array('foo' => 'bar', 'bam' => 'normalized'), $this->options->resolve());
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
*/
public function testFailForCyclicDependencies()
{
$this->options->set('foo', function (Options $options) {
$options->get('bam');
});
$this->options->set('bam', function (Options $options) {
$options->get('foo');
});
$this->options->resolve();
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
*/
public function testFailForCyclicDependenciesBetweenNormalizers()
{
$this->options->set('foo', 'bar');
$this->options->set('bam', 'baz');
$this->options->setNormalizer('foo', function (Options $options) {
$options->get('bam');
});
$this->options->setNormalizer('bam', function (Options $options) {
$options->get('foo');
});
$this->options->resolve();
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
*/
public function testFailForCyclicDependenciesBetweenNormalizerAndLazyOption()
{
$this->options->set('foo', function (Options $options) {
$options->get('bam');
});
$this->options->set('bam', 'baz');
$this->options->setNormalizer('bam', function (Options $options) {
$options->get('foo');
});
$this->options->resolve();
}
public function testReplaceClearsAndSets()
{
$this->options->set('one', '1');
$this->options->replace(array(
'two' => '2',
'three' => function (Options $options) {
return '2' === $options['two'] ? '3' : 'foo';
},
));
$this->assertEquals(array(
'two' => '2',
'three' => '3',
), $this->options->resolve());
}
public function testClearRemovesAllOptions()
{
$this->options->set('one', 1);
$this->options->set('two', 2);
$this->options->clear();
$this->assertEmpty($this->options->resolve());
}
public function testOverloadCannotBeEvaluatedLazilyWithoutExpectedClosureParams()
{
$this->options->set('foo', 'bar');
$this->options->overload('foo', function () {
return 'test';
});
$resolved = $this->options->resolve();
$this->assertTrue(is_callable($resolved['foo']));
}
public function testOverloadCannotBeEvaluatedLazilyWithoutFirstParamTypeHint()
{
$this->options->set('foo', 'bar');
$this->options->overload('foo', function ($object) {
return 'test';
});
$resolved = $this->options->resolve();
$this->assertTrue(is_callable($resolved['foo']));
}
public function testRemoveOptionAndNormalizer()
{
$this->options->set('foo1', 'bar');
$this->options->setNormalizer('foo1', function (Options $options) {
return '';
});
$this->options->set('foo2', 'bar');
$this->options->setNormalizer('foo2', function (Options $options) {
return '';
});
$this->options->remove('foo2');
$this->assertEquals(array('foo1' => ''), $this->options->resolve());
}
public function testReplaceOptionAndNormalizer()
{
$this->options->set('foo1', 'bar');
$this->options->setNormalizer('foo1', function (Options $options) {
return '';
});
$this->options->set('foo2', 'bar');
$this->options->setNormalizer('foo2', function (Options $options) {
return '';
});
$this->options->replace(array('foo1' => 'new'));
$this->assertEquals(array('foo1' => 'new'), $this->options->resolve());
}
public function testClearOptionAndNormalizer()
{
$this->options->set('foo1', 'bar');
$this->options->setNormalizer('foo1', function (Options $options) {
return '';
});
$this->options->set('foo2', 'bar');
$this->options->setNormalizer('foo2', function (Options $options) {
return '';
});
$this->options->clear();
$this->assertEmpty($this->options->resolve());
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="vendor/autoload.php"
>
<php>
<ini name="error_reporting" value="-1" />
</php>
<testsuites>
<testsuite name="Symfony OptionsResolver Component Test Suite">
<directory>./Tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./</directory>
<exclude>
<directory>./Resources</directory>
<directory>./Tests</directory>
<directory>./vendor</directory>
</exclude>
</whitelist>
</filter>
</phpunit>

View File

@ -0,0 +1,40 @@
CHANGELOG
=========
2.5.0
-----
* added support for PTY mode
* added the convenience method "mustRun"
* deprecation: Process::setStdin() is deprecated in favor of Process::setInput()
* deprecation: Process::getStdin() is deprecated in favor of Process::getInput()
* deprecation: Process::setInput() and ProcessBuilder::setInput() do not accept non-scalar types
2.4.0
-----
* added the ability to define an idle timeout
2.3.0
-----
* added ProcessUtils::escapeArgument() to fix the bug in escapeshellarg() function on Windows
* added Process::signal()
* added Process::getPid()
* added support for a TTY mode
2.2.0
-----
* added ProcessBuilder::setArguments() to reset the arguments on a builder
* added a way to retrieve the standard and error output incrementally
* added Process:restart()
2.1.0
-----
* added support for non-blocking processes (start(), wait(), isRunning(), stop())
* enhanced Windows compatibility
* added Process::getExitCodeText() that returns a string representation for
the exit code returned by the process
* added ProcessBuilder

View File

@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Process\Exception;
/**
* Marker Interface for the Process Component.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
interface ExceptionInterface
{
}

View File

@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Process\Exception;
/**
* InvalidArgumentException for the Process Component.
*
* @author Romain Neutron <imprec@gmail.com>
*/
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
{
}

View File

@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Process\Exception;
/**
* LogicException for the Process Component.
*
* @author Romain Neutron <imprec@gmail.com>
*/
class LogicException extends \LogicException implements ExceptionInterface
{
}

View File

@ -0,0 +1,54 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Process\Exception;
use Symfony\Component\Process\Process;
/**
* Exception for failed processes.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class ProcessFailedException extends RuntimeException
{
private $process;
public function __construct(Process $process)
{
if ($process->isSuccessful()) {
throw new InvalidArgumentException('Expected a failed process, but the given process was successful.');
}
$error = sprintf('The command "%s" failed.'."\n\nExit Code: %s(%s)\n\nWorking directory: %s",
$process->getCommandLine(),
$process->getExitCode(),
$process->getExitCodeText(),
$process->getWorkingDirectory()
);
if (!$process->isOutputDisabled()) {
$error .= sprintf("\n\nOutput:\n================\n%s\n\nError Output:\n================\n%s",
$process->getOutput(),
$process->getErrorOutput()
);
}
parent::__construct($error);
$this->process = $process;
}
public function getProcess()
{
return $this->process;
}
}

View File

@ -0,0 +1,69 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Process\Exception;
use Symfony\Component\Process\Process;
/**
* Exception that is thrown when a process times out.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class ProcessTimedOutException extends RuntimeException
{
const TYPE_GENERAL = 1;
const TYPE_IDLE = 2;
private $process;
private $timeoutType;
public function __construct(Process $process, $timeoutType)
{
$this->process = $process;
$this->timeoutType = $timeoutType;
parent::__construct(sprintf(
'The process "%s" exceeded the timeout of %s seconds.',
$process->getCommandLine(),
$this->getExceededTimeout()
));
}
public function getProcess()
{
return $this->process;
}
public function isGeneralTimeout()
{
return $this->timeoutType === self::TYPE_GENERAL;
}
public function isIdleTimeout()
{
return $this->timeoutType === self::TYPE_IDLE;
}
public function getExceededTimeout()
{
switch ($this->timeoutType) {
case self::TYPE_GENERAL:
return $this->process->getTimeout();
case self::TYPE_IDLE:
return $this->process->getIdleTimeout();
default:
throw new \LogicException(sprintf('Unknown timeout type "%d".', $this->timeoutType));
}
}
}

View File

@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Process\Exception;
/**
* RuntimeException for the Process Component.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}

View File

@ -0,0 +1,90 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Process;
/**
* Generic executable finder.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class ExecutableFinder
{
private $suffixes = array('.exe', '.bat', '.cmd', '.com');
/**
* Replaces default suffixes of executable.
*
* @param array $suffixes
*/
public function setSuffixes(array $suffixes)
{
$this->suffixes = $suffixes;
}
/**
* Adds new possible suffix to check for executable.
*
* @param string $suffix
*/
public function addSuffix($suffix)
{
$this->suffixes[] = $suffix;
}
/**
* Finds an executable by name.
*
* @param string $name The executable name (without the extension)
* @param string $default The default to return if no executable is found
* @param array $extraDirs Additional dirs to check into
*
* @return string The executable path or default value
*/
public function find($name, $default = null, array $extraDirs = array())
{
if (ini_get('open_basedir')) {
$searchPath = explode(PATH_SEPARATOR, ini_get('open_basedir'));
$dirs = array();
foreach ($searchPath as $path) {
// Silencing against https://bugs.php.net/69240
if (@is_dir($path)) {
$dirs[] = $path;
} else {
if (basename($path) == $name && is_executable($path)) {
return $path;
}
}
}
} else {
$dirs = array_merge(
explode(PATH_SEPARATOR, getenv('PATH') ?: getenv('Path')),
$extraDirs
);
}
$suffixes = array('');
if ('\\' === DIRECTORY_SEPARATOR) {
$pathExt = getenv('PATHEXT');
$suffixes = $pathExt ? explode(PATH_SEPARATOR, $pathExt) : $this->suffixes;
}
foreach ($suffixes as $suffix) {
foreach ($dirs as $dir) {
if (is_file($file = $dir.DIRECTORY_SEPARATOR.$name.$suffix) && ('\\' === DIRECTORY_SEPARATOR || is_executable($file))) {
return $file;
}
}
}
return $default;
}
}

View File

@ -0,0 +1,19 @@
Copyright (c) 2004-2015 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

Some files were not shown because too many files have changed in this diff Show More