PHP.SU

Программирование на PHP, MySQL и другие веб-технологии
PHP.SU Портал     На главную страницу форума Главная     Помощь Помощь     Поиск Поиск     Поиск Яндекс Поиск Яндекс     Вакансии  Пользователи Пользователи


 Страниц (1): [1]   

> Описание: Разбор CLI-параметров в C-стиле
EuGen Администратор
Отправлено: 18 Февраля, 2013 - 15:43:03
Post Id


Профессионал


Покинул форум
Сообщений всего: 9097
Дата рег-ции: Июнь 2007  
Откуда: Berlin


Помог: 707 раз(а)




Приветствую, коллеги,
Думаю, всем нам достаточно часто приходится сталкиваться с запуском PHP-скриптов из CLI (command-line interface) режима. И нередко при этом бывает, что нужно передать скрипту параметры. В PHP есть встроенный массив $_SERVER, ключ argv которого будет содержать всю информацию о параметрах. Однако, если параметры запуска имеют динамичность - например, они генерируются другим скриптом, или же параметров много - возникает проблема с запоминанием очередности передачи параметров, а так же неочевидности назначения этих параметров для вызывающей стороны.
На самом деле, в *nix системах все это давно решено - параметры всех команд именованы и разбиваются на блоки "ключ-значение". Например,

- принимает, по сути, два параметра. Синтаксис таких команд может быть разнообразен, однако к общепринятым я бы отнес:
0. Короткое именование ключа. Синтаксис:

здесь K - имя ключа, обязательно состоящее из одного символа, а value - значение такого ключа, которое, впрочем, может и отсутствовать. В этом случае, как правило, ключ считается булевским и его добавление к параметрам запуска означает "включение опции", за которую он отвечает.
1. Длинное именование ключа. Синтаксис:

key - это длинное наименование ключа, value, как и для короткого ключа - значение.
2. Слияние коротких ключей. Для краткости записи множество коротких ключей допустимо сокращать в "1 слово". Это, однако, отличается от случая 1. - так как синтаксис будет:

- как видно, несмотря на то, что после разделителя идет целое слово, этот разделитель - одиночный, а не двойной, и потому это слово надлежит трактовать как множество ключей, составляющих его буквы.

Исходя из простых описаний выше, можно уже составить некоторый код, который бы помог в разборе переданных параметров. Однако все современные *nix-команды умеют распознавать синонимы своих ключей. Иными словами, некоторые ключи могут иметь синонимы, как краткие, так и полные. Чаще всего бывает, что для каждого краткого ключа есть его полный аналог. Это я так же учел в классе, который представляю ниже:
PHP:
скопировать код в буфер обмена
  1. class CLI_Parser
  2. {
  3.     const PARSER_DELIMITER_COMMAND  = ' ';
  4.     const PARSER_DELIMITER_VALUE    = '=';
  5.     const PARSER_MARKER_SHORTKEY    = '-';
  6.     const PARSER_MARKER_LONGKEY     = '--';
  7.     protected $_rgSynonyms  = null;
  8.     protected $_sData       = '';
  9.    
  10.     public function __construct($sData=null)
  11.     {
  12.         $this->_sData=isset($sData)?(string)$sData:join(' ', $_SERVER['argv']);
  13.     }
  14.     /**
  15.      *
  16.      * @param type $rgSynonyms - an array that contains synonyms.
  17.      * Each row of $rgSynonyms contains an array in format [synonym]=>array(key0,key1,...)
  18.      */
  19.     public function setSynonyms($rgSynonyms)
  20.     {
  21.         $this->_rgSynonyms=$rgSynonyms;
  22.     }
  23.     /**
  24.      *
  25.      * @param type $sData - optional string to parse
  26.      * @param type $bAllowSynonyms - including synonyms flag, true by default
  27.      * @return mixed
  28.      */
  29.     public function parseString($sData=null, $bAllowSynonyms=true)
  30.     {
  31.         $sData      = isset($sData)?(string)$sData:$this->_sData;
  32.         if(!$sData)
  33.         {
  34.             return null;
  35.         }
  36.         $rgResult   = array();
  37.         $rgCLI      = preg_split('/['.preg_quote(self::PARSER_DELIMITER_COMMAND).']+/', $sData, -1, PREG_SPLIT_NO_EMPTY);
  38.         foreach($rgCLI as $sCLI)
  39.         {
  40.             $rgValue= preg_split('/['.preg_quote(self::PARSER_DELIMITER_VALUE).']+/', $sCLI, -1, PREG_SPLIT_NO_EMPTY);
  41.             if(preg_match('/^'.preg_quote(self::PARSER_MARKER_LONGKEY).'(.+)$/', $rgValue[0], $rgMatches))
  42.             {
  43.                 $rgResult[$rgMatches[1]]=isset($rgValue[1])?$rgValue[1]:true;
  44.             }
  45.             elseif(preg_match('/^'.preg_quote(self::PARSER_MARKER_SHORTKEY).'(.+)$/', $rgValue[0], $rgMatches))
  46.             {
  47.                 foreach(str_split($rgMatches[1]) as $sKey)
  48.                 {
  49.                     $rgResult[$sKey]=isset($rgValue[1])?$rgValue[1]:true;
  50.                 };
  51.             }
  52.         }
  53.         if($bAllowSynonyms && isset($this->_rgSynonyms) && is_array($this->_rgSynonyms))
  54.         {
  55.             array_walk($this->_rgSynonyms, function($rgSynonym, $sKey) use (&$rgResult)
  56.             {
  57.                 foreach($rgSynonym as $sSynonym)
  58.                 {
  59.                     if(array_key_exists($sSynonym, $rgResult))
  60.                     {                        
  61.                         $rgResult[$sKey] = $rgResult[$sSynonym];
  62.                         array_walk($rgSynonym, function($sOldKey) use (&$rgResult)
  63.                         {
  64.                             unset($rgResult[$sOldKey]);
  65.                         });
  66.                         break;
  67.                     }
  68.                 }
  69.             });
  70.         }
  71.         return $rgResult;
  72.     }
  73. }

по-умолчанию разбор ведется из упомянутого уже массива $_SERVER, однако это не обязательно. Массив синонимов передается отдельно, и, если не задан, то не применяется. Кроме того, использование синонимов не влияет на те ключи, синонимы которых не определены - они будут сохранены "как есть". И еще один момент - массив синонимов в себе содержит так же приоритет того, какое значение ключа использовать. Иными словами, наивысший приоритет будет иметь синоним, который указан первым в списке. Пример использования:
PHP:
скопировать код в буфер обмена
  1. $rParser = new CLI_Parser();
  2. $rParser->setSynonyms(array(
  3.     'RegExp'    => array('regexp', 'r'),
  4.     'Size'      => array('s', 'size')
  5. ));
  6. var_dump($rParser->parseString());

- для запуска
CODE (bash):
скопировать код в буфер обмена
  1. php Parser.php --size=20G -garbage -s=285M -r=2 +garbage --data=/var/www --regexp

даст вывод:
CODE (bash):
скопировать код в буфер обмена
  1. array(7) {
  2.   ["g"]=>
  3.   bool(true)
  4.   ["a"]=>
  5.   bool(true)
  6.   ["b"]=>
  7.   bool(true)
  8.   ["e"]=>
  9.   bool(true)
  10.   ["data"]=>
  11.   string(8) "/var/www"
  12.   ["RegExp"]=>
  13.   bool(true)
  14.   ["Size"]=>
  15.   string(4) "285M"
  16. }


-----
Есть в мире две бесконечные вещи - это Вселенная и человеческая глупость. Но насчет первой .. я не уверен.
 
 Top
Мелкий Супермодератор
Отправлено: 18 Февраля, 2013 - 15:49:37
Post Id



Активный участник


Покинул форум
Сообщений всего: 11888
Дата рег-ции: Июль 2009  
Откуда: Россия, Санкт-Петербург


Помог: 614 раз(а)






-----
PostgreSQL DBA
 
 Top
EuGen Администратор
Отправлено: 18 Февраля, 2013 - 15:56:04
Post Id


Профессионал


Покинул форум
Сообщений всего: 9097
Дата рег-ции: Июнь 2007  
Откуда: Berlin


Помог: 707 раз(а)




Цитата:
Замечание: Для Windows-платформ эта функция не реализована.
- PHP<5.3
Плюс к этому, она имеет неочевидный интерфейс управления длинными именами параметров (которые еще нужно настраивать заранее, что затрудняет, например, возможность передачи каких угодно, а не предопределенных, параметров). Про синонимы и мечтать не приходится. Отказался.


-----
Есть в мире две бесконечные вещи - это Вселенная и человеческая глупость. Но насчет первой .. я не уверен.
 
 Top
Мелкий Супермодератор
Отправлено: 18 Февраля, 2013 - 16:01:28
Post Id



Активный участник


Покинул форум
Сообщений всего: 11888
Дата рег-ции: Июль 2009  
Откуда: Россия, Санкт-Петербург


Помог: 614 раз(а)




//ага, под виндой с 5.3 тоже есть
Но то, что не очень удобна в управлении - да, соглашусь.


-----
PostgreSQL DBA
 
 Top
EuGen Администратор
Отправлено: 18 Февраля, 2013 - 16:03:41
Post Id


Профессионал


Покинул форум
Сообщений всего: 9097
Дата рег-ции: Июнь 2007  
Откуда: Berlin


Помог: 707 раз(а)




Мелкий пишет:
Но то, что не очень удобна в управлении - да, соглашусь.

На самом деле, был вариант изначально и с ней (тогда это все было лишь обрывком кода, затем - функцией). Но - все выросло в те же preg_match и т.п. Так что смысл потерялся. Решил привести в порядок, добавил немного комментариев - и готово.


-----
Есть в мире две бесконечные вещи - это Вселенная и человеческая глупость. Но насчет первой .. я не уверен.
 
 Top
Страниц (1): [1]
Сейчас эту тему просматривают: 1 (гостей: 1, зарегистрированных: 0)
« Пользовательские функции »


Все гости форума могут просматривать этот раздел.
Только зарегистрированные пользователи могут создавать новые темы в этом разделе.
Только зарегистрированные пользователи могут отвечать на сообщения в этом разделе.
 



Powered by PHP  Powered By MySQL  Powered by Nginx  Valid CSS  RSS

 
Powered by ExBB FM 1.0 RC1. InvisionExBB