How to reverse a Unicode string - php

It was hinted in a comment to an answer to this question that PHP can not reverse Unicode strings.
As for Unicode, it works in PHP
because most apps process it as
binary. Yes, PHP is 8-bit clean. Try
the equivalent of this in PHP: perl
-Mutf8 -e 'print scalar reverse("ほげほげ")' You will get garbage,
not "げほげほ". – jrockway
And unfortunately it is correct that PHPs unicode support atm is at best "lacking". This will hopefully change drastically with PHP6.
PHPs MultiByte functions does provide the basic functionality you need to deal with unicode, but it is inconsistent and does lack a lot of functions. One of these is a function to reverse a string.
I of course wanted to reverse this text for no other reason then to figure out if it was possible. And I made a function to accomplish this enormous complex task of reversing this Unicode text, so you can relax a bit longer until PHP6.
Test code:
$enc = 'UTF-8';
$text = "ほげほげ";
$defaultEnc = mb_internal_encoding();
echo "Showing results with encoding $defaultEnc.\n\n";
$revNormal = strrev($text);
$revInt = mb_strrev($text);
$revEnc = mb_strrev($text, $enc);
echo "Original text is: $text .\n";
echo "Normal strrev output: " . $revNormal . ".\n";
echo "mb_strrev without encoding output: $revInt.\n";
echo "mb_strrev with encoding $enc output: $revEnc.\n";
if (mb_internal_encoding($enc)) {
echo "\nSetting internal encoding to $enc from $defaultEnc.\n\n";
$revNormal = strrev($text);
$revInt = mb_strrev($text);
$revEnc = mb_strrev($text, $enc);
echo "Original text is: $text .\n";
echo "Normal strrev output: " . $revNormal . ".\n";
echo "mb_strrev without encoding output: $revInt.\n";
echo "mb_strrev with encoding $enc output: $revEnc.\n";
} else {
echo "\nCould not set internal encoding to $enc!\n";
}

here's another approach using regex:
function utf8_strrev($str){
preg_match_all('/./us', $str, $ar);
return implode(array_reverse($ar[0]));
}

Here's another way. This seems to work without having to specify an output encoding (tested with a couple of different mb_internal_encodings):
function mb_strrev($text)
{
return join('', array_reverse(
preg_split('~~u', $text, -1, PREG_SPLIT_NO_EMPTY)
));
}

Grapheme functions handle UTF-8 string more correctly than mbstring and PCRE functions/ Mbstring and PCRE may break characters. You can see the defference between them by executing the following code.
function str_to_array($string)
{
$length = grapheme_strlen($string);
$ret = [];
for ($i = 0; $i < $length; $i += 1) {
$ret[] = grapheme_substr($string, $i, 1);
}
return $ret;
}
function str_to_array2($string)
{
$length = mb_strlen($string, "UTF-8");
$ret = [];
for ($i = 0; $i < $length; $i += 1) {
$ret[] = mb_substr($string, $i, 1, "UTF-8");
}
return $ret;
}
function str_to_array3($string)
{
return preg_split('//u', $string, -1, PREG_SPLIT_NO_EMPTY);
}
function utf8_strrev($string)
{
return implode(array_reverse(str_to_array($string)));
}
function utf8_strrev2($string)
{
return implode(array_reverse(str_to_array2($string)));
}
function utf8_strrev3($string)
{
return implode(array_reverse(str_to_array3($string)));
}
// http://www.php.net/manual/en/function.grapheme-strlen.php
$string = "a\xCC\x8A" // 'LATIN SMALL LETTER A WITH RING ABOVE' (U+00E5)
."o\xCC\x88"; // 'LATIN SMALL LETTER O WITH DIAERESIS' (U+00F6)
var_dump(array_map(function($elem) { return strtoupper(bin2hex($elem)); },
[
'should be' => "o\xCC\x88"."a\xCC\x8A",
'grapheme' => utf8_strrev($string),
'mbstring' => utf8_strrev2($string),
'pcre' => utf8_strrev3($string)
]));
The result is here.
array(4) {
["should be"]=>
string(12) "6FCC8861CC8A"
["grapheme"]=>
string(12) "6FCC8861CC8A"
["mbstring"]=>
string(12) "CC886FCC8A61"
["pcre"]=>
string(12) "CC886FCC8A61"
}
IntlBreakIterator can be used since PHP 5.5 (intl 3.0);
function utf8_strrev($str)
{
$it = IntlBreakIterator::createCodePointInstance();
$it->setText($str);
$ret = '';
$pos = 0;
$prev = 0;
foreach ($it as $pos) {
$ret = substr($str, $prev, $pos - $prev) . $ret;
$prev = $pos;
}
return $ret;
}

The answer
function mb_strrev($text, $encoding = null)
{
$funcParams = array($text);
if ($encoding !== null)
$funcParams[] = $encoding;
$length = call_user_func_array('mb_strlen', $funcParams);
$output = '';
$funcParams = array($text, $length, 1);
if ($encoding !== null)
$funcParams[] = $encoding;
while ($funcParams[1]--) {
$output .= call_user_func_array('mb_substr', $funcParams);
}
return $output;
}

Another method:
function mb_strrev($str, $enc = null) {
if(is_null($enc)) $enc = mb_internal_encoding();
$str = mb_convert_encoding($str, 'UTF-16BE', $enc);
return mb_convert_encoding(strrev($str), $enc, 'UTF-16LE');
}

It is easy utf8_strrev( $str ). See the relevant source code of my Library that I copied below:
function utf8_strrev( $str )
{
return implode( array_reverse( utf8_split( $str ) ) );
}
function utf8_split( $str , $split_length = 1 )
{
$str = ( string ) $str;
$ret = array( );
if( pcre_utf8_support( ) )
{
$str = utf8_clean( $str );
$ret = preg_split('/(?<!^)(?!$)/u', $str );
// \X is buggy in many recent versions of PHP
//preg_match_all( '/\X/u' , $str , $ret );
//$ret = $ret[0];
}
else
{
//Fallback
$len = strlen( $str );
for( $i = 0 ; $i < $len ; $i++ )
{
if( ( $str[$i] & "\x80" ) === "\x00" )
{
$ret[] = $str[$i];
}
else if( ( ( $str[$i] & "\xE0" ) === "\xC0" ) && ( isset( $str[$i+1] ) ) )
{
if( ( $str[$i+1] & "\xC0" ) === "\x80" )
{
$ret[] = $str[$i] . $str[$i+1];
$i++;
}
}
else if( ( ( $str[$i] & "\xF0" ) === "\xE0" ) && ( isset( $str[$i+2] ) ) )
{
if( ( ( $str[$i+1] & "\xC0" ) === "\x80" ) && ( ( $str[$i+2] & "\xC0" ) === "\x80" ) )
{
$ret[] = $str[$i] . $str[$i+1] . $str[$i+2];
$i = $i + 2;
}
}
else if( ( ( $str[$i] & "\xF8" ) === "\xF0" ) && ( isset( $str[$i+3] ) ) )
{
if( ( ( $str[$i+1] & "\xC0" ) === "\x80" ) && ( ( $str[$i+2] & "\xC0" ) === "\x80" ) && ( ( $str[$i+3] & "\xC0" ) === "\x80" ) )
{
$ret[] = $str[$i] . $str[$i+1] . $str[$i+2] . $str[$i+3];
$i = $i + 3;
}
}
}
}
if( $split_length > 1 )
{
$ret = array_chunk( $ret , $split_length );
$ret = array_map( 'implode' , $ret );
}
if( $ret[0] === '' )
{
return array( );
}
return $ret;
}
function utf8_clean( $str , $remove_bom = false )
{
$regx = '/([\x00-\x7F]|[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3})|./s';
$str = preg_replace( $regx , '$1' , $str );
if( $remove_bom )
{
$str = utf8_str_replace( utf8_bom( ) , '' , $str );
}
return $str;
}
function utf8_str_replace( $search , $replace , $subject , &$count = 0 )
{
return str_replace( $search , $replace , $subject , $count );
}
function utf8_bom( )
{
return "\xef\xbb\xbf";
}
function pcre_utf8_support( )
{
static $support;
if( !isset( $support ) )
{
$support = #preg_match( '//u', '' );
//Cached the response
}
return $support;
}

Related

php variable interpolation inside variable [duplicate]

Python has a feature called template strings.
>>> from string import Template
>>> s = Template('$who likes $what')
>>> s.substitute(who='tim', what='kung pao')
'tim likes kung pao'
I know that PHP allows you to write:
"Hello $person"
and have $person substituted, but the templates can be reused in various sections of the code?
You can use template strings like this:
$name = "Maria";
$info["last_name"] = "Warner";
echo "Hello {$name} {$info["last_name"]}";
This will echo Hello Maria Warner.
You could also use strtr:
$template = '$who likes $what';
$vars = array(
'$who' => 'tim',
'$what' => 'kung pao',
);
echo strtr($template, $vars);
Outputs:
tim likes kung pao
I think there are a bunch of ways to do this... but this comes to mind.
$search = array('%who%', '%what_id%');
$replace = array('tim', 'kung pao');
$conference_target = str_replace(
$search,
$replace,
"%who% likes %what%"
);
Ha, we even had one in our framework using vsprintf:
class Helper_StringFormat {
public static function sprintf($format, array $args = array()) {
$arg_nums = array_slice(array_flip(array_keys(array(0 => 0) + $args)), 1);
for ($pos = 0; preg_match('/(?<=%)\(([a-zA-Z_]\w*)\)/', $format, $match, PREG_OFFSET_CAPTURE, $pos);) {
$arg_pos = $match[0][2];
$arg_len = strlen($match[0][0]);
$arg_key = $match[1][0];
if (! array_key_exists($arg_key, $arg_nums)) {
user_error("sprintfn(): Missing argument '${arg_key}'", E_USER_WARNING);
return false;
}
$format = substr_replace($format, $replace = $arg_nums[$arg_key] . '$', $arg_pos, $arg_len);
$pos = $arg_pos + strlen($replace);
}
return vsprintf($format, array_values($args));
}
}
Which looks like it came from the sprintf page
This allows for calls like:
sprintfn('second: %(second)s ; first: %(first)s', array(
'first' => '1st',
'second'=> '2nd'
));
UPDATE
Here is an update to do what you want... not fully tested though
class Helper_StringFormat {
public static function sprintf($format, array $args = array()) {
$arg_nums = array_slice(array_flip(array_keys(array(0 => 0) + $args)), 1);
for ($pos = 0; preg_match('/(?<=%)\(([a-zA-Z_][\w\s]*)\)/', $format, $match, PREG_OFFSET_CAPTURE, $pos);) {
$arg_pos = $match[0][1];
$arg_len = strlen($match[0][0]);
$arg_key = $match[1][0];
if (! array_key_exists($arg_key, $arg_nums)) {
user_error("sprintfn(): Missing argument '${arg_key}'", E_USER_WARNING);
return false;
}
$format = substr_replace($format, $replace = $arg_nums[$arg_key] . '$', $arg_pos, $arg_len);
$pos = $arg_pos + strlen($replace); // skip to end of replacement for next iteration
}
return vsprintf($format, array_values($args));
}
}
$str = "%(my var)s now work with a slight %(my var2)s";
$repl = array("my var" => "Spaces", "my var2" => "modification.");
echo Helper_StringFormat::sprintf($str, $repl);
OUTPUT
Spaces now work with a slight modification.
Another more simple approach would be this:
$s = function ($vars) {
extract($vars);
return "$who likes $what";
};
echo $s(['who' => 'Tim', 'what' => 'King Pao']); // Tim likes King Pao
And yes, PHPStorm will complain...
I personally most like sprintf (or vsprintf, for an array of arguments). It places them in the intended order, coerces types as needed, and has a lot more advanced features available.
Example:
$var = sprintf("%s costs %.2f dollars", "Cookies", 1.1);
This will result in the value Cookies cost 1.10 dollars.
There's an entire family of printf functions for different use cases, all listed under "See Also".
Very versatile: same methods for providing variables, array components, function results, etc.
I made a function to do what you want. i made it "quck-and-dirty" because i have not much time to refactorize it, maybe i upload it to my github.
EDIT: a bug correction...
Use it like
formattemplatter(
'$who likes $what'
, array(
'who' => 'Tim'
, 'what' => 'Kung Pao'
)
);
Variables can be [a-zA-Z0-9_] only.
function formattemplater($string, $params) {
// Determine largest string
$largest = 0;
foreach(array_keys($params) as $k) {
if(($l=strlen($k)) > $largest) $largest=$l;
}
$buff = '';
$cp = false; // Conditional parenthesis
$ip = false; // Inside parameter
$isp = false; // Is set parameter
$bl = 1; // buffer length
$param = ''; // current parameter
$out = ''; // output string
$string .= '!';
for($sc=0,$c=$oc='';isset($string{$sc});++$sc,++$bl) {
$c = $string{$sc};
if($ip) {
$a = ord($c);
if(!($a == 95 || ( // underscore
($a >= 48 && $a <= 57) // 0-9
|| ($a >= 65 && $a <= 90) // A-Z
|| ($a >= 97 && $a <= 122) // a-z
)
)) {
$isp = isset($params[$buff]);
if(!$cp && !$isp) {
trigger_error(
sprintf(
__FUNCTION__.': the parameter "%s" is not defined'
, $buff
)
, E_USER_ERROR
);
} elseif(!$cp || $isp) {
$out .= $params[$buff];
}
$isp = $isp && !empty($params[$buff]);
$oc = $buff = '';
$bl = 0;
$ip = false;
}
}
if($cp && $c === ')') {
$out .= $buff;
$cp = $isp = false;
$c = $buff = '';
$bl = 0;
}
if(($cp && $isp) || $ip)
$buff .= $c;
if($c === '$' && $oc !== '\\') {
if($oc === '(') $cp = true;
else $out .= $oc;
$ip = true;
$buff = $c = $oc = '';
$bl = 0;
}
if(!$cp && $bl > $largest) {
$buff = substr($buff, - $largest);
$bl = $largest;
}
if(!$ip && ( !$cp || ($cp && $isp))) {
$out .= $oc;
if(!$cp) $oc = $c;
}
}
return $out;
}
Just for the sake of completeness: there is also Heredoc.
$template = fn( $who, $what ) => <<<EOT
$who likes $what
EOT;
echo( $template( 'tim', 'kung pao' ) );
Outputs:
tim likes kung pao
Sidenotes:
You get highlighting in your favourite language (if properly configured). Just substitute EOT (from the sample above) with whatever you like (e.c. HTML, SQL, PHP, ...).
Escape arrays with curly braces {$data['who']}. Accessing objekts like $data->who works without braces.
Arrow functions like fn($a)=>$a are available since PHP 7.4. You can write function($a){return $a;} if you are using PHP<7.4.

how to convert string to array character

The first line of code is converted to the second code. I can use the padding methods, but I want to get the solution from the shortest path and convert it quickly. I will use the code field in the sql select section
$it = "(a(am,bam),b(dam,cam))";
$to = "a.am, a.bam, b.dam, b.cam";
Try following code:
$it = "(a(am,bam),b(dam,cam))";
function iDelimiter( $str ) {
$count = 0;
$str = str_split( $str );
foreach( $str as &$value ) {
if( $value == "(" ) {
$count++;
} elseif( $value == ")" ) {
$count--;
}
if( $value == "," && ! $count ) {
$value = "|";
}
}
return explode( "|", implode( $str ) );
}
function iParser( $str, $_prefix = "" ) {
preg_match( "/^((?!(\(|\))).)*\((.*)\)$/", $str, $m );
if( count( $m ) < 4 ) {
return ( strlen( $_prefix ) ? $_prefix . "." : '' ) . $str;
}
$prefix = ( strlen( $_prefix ) ? $_prefix . "." : '' ) . $m[1];
$str = $m[3];
if( strpos( $str, "(" ) === false ) {
$return = explode( ",", $str );
$pad = preg_filter( '/^/', strlen( $prefix ) ? $prefix . '.' : '', $return );
return implode( $pad, "," );
} else {
$str = iDelimiter( $str );
$return = array_map( 'iParser', $str, array_pad( array(), count( $str ), $prefix ) );
return implode( $return, ", " );
}
}
print_r( iParser( $it ) );
It parse every depth of parentheses. Just pass your string to iParser function.
Try this:
<?php
function str2Arr( $s, $r, $str) {
$str = trim( str_replace( $s, $r, $str ));
return explode(" ", $str);
}
$it = "(a(am,bam),b(dam,cam))";
//$to = "a.am, a.bam, b.dam, b.cam";
$search = ['),', '(', ')', ',', 'a a', 'b d', 'ba', 'c'];
$replace =[' ', ' ', ' ', ' ', 'a.a','b.d', 'a.ba', 'b.c'];
var_dump( implode(", ",( str2Arr( $search, $replace, $it) ) ) );
See demo
Without using a regex one may achieve the specified conversion by using str_replace() which uses an array of characters to be replaced by another array of characters when found in the subject string. The non-alphabetical characters are each replaced with blank space and the substrings are replaced so that each starts with an "a" or "b" as appropriate followed by a period and the rest of the substring.
Kill me now. There's got to be a better way, but my eyes are bleeding.
It probably would have worked much better had I started with a regex, but my regex skills are rusty.
I kept pounding your data with a hammer until the square peg went through the round hole.
<?php
$it = "(a(am,bam),b(dam,cam))";
$it = substr($it, 1, -1);
//$to = "a.am, a.bam, b.dam, b.cam";
$final = [];
$test = explode("),", $it);
foreach($test as $section) {
$letter = substr($section, 0, 1);
$remainder = substr($section, 2);
if(strpos($remainder, ")")) {
$remainder = substr($remainder, 0, strpos($remainder, ")"));
}
echo $letter . " ".$remainder."\n";
foreach(explode(",", $remainder) as $fragment) {
array_push($final, $letter.".".$fragment);
}
}
var_dump($final);
var_dump(implode($final, ", "));
Yields
a am,bam
b dam,cam
array(4) {
[0]=>
string(4) "a.am"
[1]=>
string(5) "a.bam"
[2]=>
string(5) "b.dam"
[3]=>
string(5) "b.cam"
}
string(22) "a.am, a.bam, b.dam, b.cam"

Check if a word can be created from a random letter string using PHP

<?php
$randomstring = 'raabccdegep';
$arraylist = array("car", "egg", "total");
?>
Above $randomstring is a string which contain some alphabet letters.
And I Have an Array called $arraylist which Contain 3 Words Such as 'car' , 'egg' , 'total'.
Now I need to check the string Using the words in array and print if the word can be created using the string.
For Example I need an Output Like.
car is possible.
egg is not possible.
total is not possible.
Also Please Check the repetition of letter. ie, beep is also possible. Because the string contains two e. But egg is not possible because there is only one g.
function find_in( $haystack, $item ) {
$match = '';
foreach( str_split( $item ) as $char ) {
if ( strpos( $haystack, $char ) !== false ) {
$haystack = substr_replace( $haystack, '', strpos( $haystack, $char ), 1 );
$match .= $char;
}
}
return $match === $item;
}
$randomstring = 'raabccdegep';
$arraylist = array( "beep", "car", "egg", "total");
foreach ( $arraylist as $item ) {
echo find_in( $randomstring, $item ) ? " $item found in $randomstring." : " $item not found in $randomstring.";
}
This should do the trick:
<?php
$randomstring = 'raabccdegep';
$arraylist = array("car", "egg", "total");
foreach($arraylist as $word){
$checkstring = $randomstring;
$beMade = true;
for( $i = 0; $i < strlen($word); $i++ ) {
$char = substr( $word, $i, 1 );
$pos = strpos($checkstring, $char);
if($pos === false){
$beMade = false;
} else {
substr_replace($checkstring, '', $i, 1);
}
}
if ($beMade){
echo $word . " is possible \n";
} else {
echo $word . " is not possible \n";
}
}
?>

Truncating with ascii symbols

i've a problem with the truncate php function..
<?php
print_r(truncate ('cia???☺☻♥♀♂☼•◘○♠♣xas?????!!!!----'));
function truncate($text) {
$length = 100;
$ending = '...';
$exact = true;
$considerHtml = false;
$stripTags = false;
$wordsLenght = 20;
$textArray = explode ( " ", $text );
foreach ( $textArray as $key => $word ) {
if (strlen ( $word ) > $wordsLenght) {
$truncatedWord = substr ( $word, 0, $wordsLenght );
$textArray [$key] = $truncatedWord . "[...]";
}
}
$text = implode ( " ", $textArray );
// end truncate long word
if (strlen ( $text ) <= $length) {
return $text;
} else {
$truncate = substr ( $text, 0, $length - mb_strlen ( $ending, 'UTF-8' ) );
}
}
// if the words shouldn't be cut in the middle...
if (! $exact) {
// ...search the last occurance of a space...
$spacepos = strrpos ( $truncate, ' ' );
if (isset ( $spacepos )) {
// ...and cut the text in this position
$truncate = substr ( $truncate, 0, $spacepos );
}
}
// add the defined ending to the text
$truncate .= $ending;
if ($considerHtml) {
// close all unclosed html-tags
foreach ( $open_tags as $tag ) {
$truncate .= '';
}
}
return $truncate;
The problem, whit the string o given, is that the truncate function doesn't work well with unicode symbols...
The result is this:
cia???☺☻♥♀��[...]
Is there a way to split correctly?
I tried in different ways but none of them works correctly... I'm going out of mind :)

Does PHP have a feature like Python's template strings?

Python has a feature called template strings.
>>> from string import Template
>>> s = Template('$who likes $what')
>>> s.substitute(who='tim', what='kung pao')
'tim likes kung pao'
I know that PHP allows you to write:
"Hello $person"
and have $person substituted, but the templates can be reused in various sections of the code?
You can use template strings like this:
$name = "Maria";
$info["last_name"] = "Warner";
echo "Hello {$name} {$info["last_name"]}";
This will echo Hello Maria Warner.
You could also use strtr:
$template = '$who likes $what';
$vars = array(
'$who' => 'tim',
'$what' => 'kung pao',
);
echo strtr($template, $vars);
Outputs:
tim likes kung pao
I think there are a bunch of ways to do this... but this comes to mind.
$search = array('%who%', '%what_id%');
$replace = array('tim', 'kung pao');
$conference_target = str_replace(
$search,
$replace,
"%who% likes %what%"
);
Ha, we even had one in our framework using vsprintf:
class Helper_StringFormat {
public static function sprintf($format, array $args = array()) {
$arg_nums = array_slice(array_flip(array_keys(array(0 => 0) + $args)), 1);
for ($pos = 0; preg_match('/(?<=%)\(([a-zA-Z_]\w*)\)/', $format, $match, PREG_OFFSET_CAPTURE, $pos);) {
$arg_pos = $match[0][2];
$arg_len = strlen($match[0][0]);
$arg_key = $match[1][0];
if (! array_key_exists($arg_key, $arg_nums)) {
user_error("sprintfn(): Missing argument '${arg_key}'", E_USER_WARNING);
return false;
}
$format = substr_replace($format, $replace = $arg_nums[$arg_key] . '$', $arg_pos, $arg_len);
$pos = $arg_pos + strlen($replace);
}
return vsprintf($format, array_values($args));
}
}
Which looks like it came from the sprintf page
This allows for calls like:
sprintfn('second: %(second)s ; first: %(first)s', array(
'first' => '1st',
'second'=> '2nd'
));
UPDATE
Here is an update to do what you want... not fully tested though
class Helper_StringFormat {
public static function sprintf($format, array $args = array()) {
$arg_nums = array_slice(array_flip(array_keys(array(0 => 0) + $args)), 1);
for ($pos = 0; preg_match('/(?<=%)\(([a-zA-Z_][\w\s]*)\)/', $format, $match, PREG_OFFSET_CAPTURE, $pos);) {
$arg_pos = $match[0][1];
$arg_len = strlen($match[0][0]);
$arg_key = $match[1][0];
if (! array_key_exists($arg_key, $arg_nums)) {
user_error("sprintfn(): Missing argument '${arg_key}'", E_USER_WARNING);
return false;
}
$format = substr_replace($format, $replace = $arg_nums[$arg_key] . '$', $arg_pos, $arg_len);
$pos = $arg_pos + strlen($replace); // skip to end of replacement for next iteration
}
return vsprintf($format, array_values($args));
}
}
$str = "%(my var)s now work with a slight %(my var2)s";
$repl = array("my var" => "Spaces", "my var2" => "modification.");
echo Helper_StringFormat::sprintf($str, $repl);
OUTPUT
Spaces now work with a slight modification.
Another more simple approach would be this:
$s = function ($vars) {
extract($vars);
return "$who likes $what";
};
echo $s(['who' => 'Tim', 'what' => 'King Pao']); // Tim likes King Pao
And yes, PHPStorm will complain...
I personally most like sprintf (or vsprintf, for an array of arguments). It places them in the intended order, coerces types as needed, and has a lot more advanced features available.
Example:
$var = sprintf("%s costs %.2f dollars", "Cookies", 1.1);
This will result in the value Cookies cost 1.10 dollars.
There's an entire family of printf functions for different use cases, all listed under "See Also".
Very versatile: same methods for providing variables, array components, function results, etc.
I made a function to do what you want. i made it "quck-and-dirty" because i have not much time to refactorize it, maybe i upload it to my github.
EDIT: a bug correction...
Use it like
formattemplatter(
'$who likes $what'
, array(
'who' => 'Tim'
, 'what' => 'Kung Pao'
)
);
Variables can be [a-zA-Z0-9_] only.
function formattemplater($string, $params) {
// Determine largest string
$largest = 0;
foreach(array_keys($params) as $k) {
if(($l=strlen($k)) > $largest) $largest=$l;
}
$buff = '';
$cp = false; // Conditional parenthesis
$ip = false; // Inside parameter
$isp = false; // Is set parameter
$bl = 1; // buffer length
$param = ''; // current parameter
$out = ''; // output string
$string .= '!';
for($sc=0,$c=$oc='';isset($string{$sc});++$sc,++$bl) {
$c = $string{$sc};
if($ip) {
$a = ord($c);
if(!($a == 95 || ( // underscore
($a >= 48 && $a <= 57) // 0-9
|| ($a >= 65 && $a <= 90) // A-Z
|| ($a >= 97 && $a <= 122) // a-z
)
)) {
$isp = isset($params[$buff]);
if(!$cp && !$isp) {
trigger_error(
sprintf(
__FUNCTION__.': the parameter "%s" is not defined'
, $buff
)
, E_USER_ERROR
);
} elseif(!$cp || $isp) {
$out .= $params[$buff];
}
$isp = $isp && !empty($params[$buff]);
$oc = $buff = '';
$bl = 0;
$ip = false;
}
}
if($cp && $c === ')') {
$out .= $buff;
$cp = $isp = false;
$c = $buff = '';
$bl = 0;
}
if(($cp && $isp) || $ip)
$buff .= $c;
if($c === '$' && $oc !== '\\') {
if($oc === '(') $cp = true;
else $out .= $oc;
$ip = true;
$buff = $c = $oc = '';
$bl = 0;
}
if(!$cp && $bl > $largest) {
$buff = substr($buff, - $largest);
$bl = $largest;
}
if(!$ip && ( !$cp || ($cp && $isp))) {
$out .= $oc;
if(!$cp) $oc = $c;
}
}
return $out;
}
Just for the sake of completeness: there is also Heredoc.
$template = fn( $who, $what ) => <<<EOT
$who likes $what
EOT;
echo( $template( 'tim', 'kung pao' ) );
Outputs:
tim likes kung pao
Sidenotes:
You get highlighting in your favourite language (if properly configured). Just substitute EOT (from the sample above) with whatever you like (e.c. HTML, SQL, PHP, ...).
Escape arrays with curly braces {$data['who']}. Accessing objekts like $data->who works without braces.
Arrow functions like fn($a)=>$a are available since PHP 7.4. You can write function($a){return $a;} if you are using PHP<7.4.

Categories