note 62073 added to function.strtok

From: Date: Sat, 18 Feb 2006 21:49:39 +0000
Subject: note 62073 added to function.strtok
Groups: php.notes 
Request: Send a blank email to php-notes+get-104000@lists.php.net to get a copy of this message
This function takes a string and returns an array with words (delimited by spaces), also taking into account quotes, doublequotes, backticks and backslashes (for escaping stuff). So $string = "cp 'my file' to Judy's file"; var_dump(parse_cli($string)); would yield: array(4) { [0]=> string(2) "cp" [1]=> string(7) "my file" [2]=> string(5) "to" [3]=> string(11) "Judy's file" } Way it works, runs through the string character by character, for each character looking up the action to take, based on that character and its current $state. Actions can be (one or more of) adding the character/string to the current word, adding the word to the output array, and changing or (re)storing the state. For example a space will become part of the current 'word' (or 'token') if $state is 'doublequoted', but it will start a new token if $state was 'unquoted'. I was later told it's a "tokeniser using a finite state automaton". Who knew :-) <?php #_____________________ # parse_cli($string) / function parse_cli($string) { $state = 'space'; $previous = ''; // stores current state when encountering a backslash (which changes $state to 'escaped', but has to fall back into the previous $state afterwards) $out = array(); // the return value $word = ''; $type = ''; // type of character // array[states][chartypes] => actions $chart = array( 'space' => array('space'=>'', 'quote'=>'q', 'doublequote'=>'d', 'backtick'=>'b', 'backslash'=>'ue', 'other'=>'ua'), 'unquoted' => array('space'=>'w ', 'quote'=>'a', 'doublequote'=>'a', 'backtick'=>'a', 'backslash'=>'e', 'other'=>'a'), 'quoted' => array('space'=>'a', 'quote'=>'w ', 'doublequote'=>'a', 'backtick'=>'a', 'backslash'=>'e', 'other'=>'a'), 'doublequoted' => array('space'=>'a', 'quote'=>'a', 'doublequote'=>'w ', 'backtick'=>'a', 'backslash'=>'e', 'other'=>'a'), 'backticked' => array('space'=>'a', 'quote'=>'a', 'doublequote'=>'a', 'backtick'=>'w ', 'backslash'=>'e', 'other'=>'a'), 'escaped' => array('space'=>'ap', 'quote'=>'ap', 'doublequote'=>'ap', 'backtick'=>'ap', 'backslash'=>'ap', 'other'=>'ap')); for ($i=0; $i<=strlen($string); $i++) { $char = substr($string, $i, 1); $type = array_search($char, array('space'=>' ', 'quote'=>'\'', 'doublequote'=>'"', 'backtick'=>'`', 'backslash'=>'\\')); if (! $type) $type = 'other'; if ($type == 'other') { // grabs all characters that are also 'other' following the current one in one go preg_match("/[ \'\"\`\\\]/", $string, $matches, PREG_OFFSET_CAPTURE, $i); if ($matches) { $matches = $matches[0]; $char = substr($string, $i, $matches[1]-$i); // yep, $char length can be > 1 $i = $matches[1] - 1; }else{ // no more match on special characters, that must mean this is the last word! // the .= hereunder is because we *might* be in the middle of a word that just contained special chars $word .= substr($string, $i); break; // jumps out of the for() loop } } $actions = $chart[$state][$type]; for($j=0; $j<strlen($actions); $j++) { $act = substr($actions, $j, 1); if ($act == ' ') $state = 'space'; if ($act == 'u') $state = 'unquoted'; if ($act == 'q') $state = 'quoted'; if ($act == 'd') $state = 'doublequoted'; if ($act == 'b') $state = 'backticked'; if ($act == 'e') { $previous = $state; $state = 'escaped'; } if ($act == 'a') $word .= $char; if ($act == 'w') { $out[] = $word; $word = ''; } if ($act == 'p') $state = $previous; } } if (strlen($word)) $out[] = $word; return $out; } ?> ---- Server IP: 194.109.193.119 Probable Submitter: 82.73.144.213 ---- Manual Page -- http://www.php.net/manual/en/function.strtok.php Edit -- http://master.php.net/manage/user-notes.php?action=edit+62073 Delete: added to the manual -- http://master.php.net/manage/user-notes.php?action=delete+62073&report=yes&reason=added+to+the+manual Delete: bad code -- http://master.php.net/manage/user-notes.php?action=delete+62073&report=yes&reason=bad+code Delete: spam -- http://master.php.net/manage/user-notes.php?action=delete+62073&report=yes&reason=spam Delete: useless -- http://master.php.net/manage/user-notes.php?action=delete+62073&report=yes&reason=useless Delete: non-english -- http://master.php.net/manage/user-notes.php?action=delete+62073&report=yes&reason=non-english Delete: already in docs -- http://master.php.net/manage/user-notes.php?action=delete+62073&report=yes&reason=already+in+docs Delete: other reasons -- http://master.php.net/manage/user-notes.php?action=delete+62073&report=yes Reject -- http://master.php.net/manage/user-notes.php?action=reject+62073&report=yes Search -- http://master.php.net/manage/user-notes.php

« previous php.notes (#104000) next »