#!/usr/bin/php -q

<?php
/**
 * Mirror Tool
 *
 * Rsync a file or a folder from current local path to destination servers with 
 * the same path automatically, the current path is base on Linux's 
 * "pwd -P" command.
 *
 * @version     v1.2.0
 * @author      Nick Tsai <myintaer@gmail.com>
 * @filesource  PHP >= 5.4 (Support >= 5.0 if removing Short-Array-Syntax)
 * @param string $argv[1] File/directory in current path for rsync
 * @param string $argv[2] (Optional) Target servers group key of remoteServers
 * @example
 *  $ ~/mirror file.php      // Rsync file.php to servers with same path
 *  $ ~/mirror folderA       // Rsync whole folderA to servers
 *  $ ~/mirror ./            // Rsync current whole folder
 *  $ ~/mirror ./ stage      // Rsync to servers in stage group
 *  $ ~/mirror ./ prod       // Rsync to servers in prod group
 */


/* Configuration */

/**
 * @var array Distant server host list
 */
$config['remoteServers'] = [
    'default' => [
        '110.1.1.1',
        '110.1.2.1',
    ],
    'stage' => [
        '110.1.1.1',
    ],
    'prod' => [
        '110.1.2.1',
    ],
];

/**
 * @var string Remote server user
 */
$config['remoteUser'] = 'www-data';

/**
 * @var string Addition params of rsync command
 */
$config['rsyncParams'] = '-av --delete';

/**
 * @var int Seconds waiting of each rsync connections
 */
$config['sleepSeconds'] = 0;

/* /Configuration */


ob_implicit_flush();

// File input
$file = (isset($argv[1])) ? $argv[1] : NULL;

// Target server group list for rsync
$serverEnv = (isset($argv[2])) ? $argv[2] : 'default';

// Directory of destination same as source
$dir = trim(shell_exec("pwd -P"));

try {

    // File existence check
    if (strlen(trim($file))==0)
        throw new Exception('None of file input');
    
    // Check $argv likes asterisk
    if (isset($argv[3])) 
        throw new Exception('Invalid arguments input');

    /**
     * Validating file name input
     *
     * @var sstring $reg Regular patterns
     * @example
     *  \w\/    // folderA/
     *  \*      // * or *.*
     *  ^\/     // / or /etc
     *
     */
    $reg = '/(\w\/|\*|^\/)/';

    preg_match($reg,$file,$matches);

    if ($matches) {

        //print_r($matches);

        throw new Exception('Invalid file name input');
    }

    // Check for server list
    if (!isset($config['remoteServers'][$serverEnv]) 
        || !$config['remoteServers'][$serverEnv]) {

        throw new Exception("No server host in group: {$serverEnv}");
    }  
    
    // File or directory of source definition
    $this_file = $dir.'/'.$file;

    // Check for type of link
    if (is_link($this_file))
        throw new Exception('File input is symblic link');

    // Check for type of file / directory
    if (!is_file($this_file) && !is_dir($this_file) )
        throw new Exception('File input is not a file or directory');
    
    // Check for syntax if is PHP
    if ( preg_match("/\.php$/i",$file) 
        && !preg_match("/No syntax errors detected/i", shell_exec("php -l ".$this_file))  ) {

        throw new Exception('PHP syntax error!');
    }

    // Rsync each servers
    foreach ($config['remoteServers'][$serverEnv] as $key => $server) {

        // Info display
        echo '/* --- Process Start ---  */'."\n";
        echo '[Process]: '.($key+1)."\n";
        echo '[Group  ]: '.$serverEnv."\n";
        echo '[Server ]: '.$server."\n";
        echo '[User   ]: '.$config['remoteUser']."\n";


        /* Command builder */
           
        $cmd = 'rsync ' . $config['rsyncParams'];

        // Rsync shell command
        $cmd = sprintf("%s %s %s@%s:%s",
            $cmd,
            $file,
            $config['remoteUser'],
            $server,
            $dir 
        );

        echo '[Command]: '.$cmd."\n";
        
        // Shell execution
        $result = shell_exec($cmd);

        echo '[Message]: '."\n".$result;

        echo '/* --- /Process End ---  */'."\n";
        echo "\r\n";

        sleep($config['sleepSeconds']);
    }

    echo "\r\n";
    
} catch (Exception $e) {

    die('ERROR:'.$e->getMessage()."\n");
}
