Parse BBCode in array - php

I am trying to call a function from BBCode(like WordPress shortcodes). but I didn't find any code to do that, only I found HTML tag parser like:
[bold]Bold text[/bold]
->
<b>Bold text</b>
But I want to save it as an array, for example:
[date format="j M, Y" type="jalali"]
to something like this:
array(
'date' => array(
'format' => 'j M, Y',
'type' => 'jalali'
)
)
*Edited
I made a code to do this (sorry if my English is bad):
[date format="Y/m/d" type="jalali"] =>
Step 1: Get code between "[" and "]":
date format="Y/m/d" type="jalali"
Step 2: Explode space in the code:
$code = array('date', 'format="Y/m/d"', 'type="jalali"')
Step 3: Get shortcode name(offset 0 of $code) and get
difference($code excluded offset 0):
$name = 'date'
$attr = array('format="Y/m/d"', 'type="jalali"')
Step 4: Now I have attributes and code name. But the problem is if
put space in attributes value it will explode that too:
[date format="j M, Y" type="jalali"] =>
$code = array('date', 'format="j', 'M,', ' Y"', 'type="jalali"');
Now how can I fix this or get name and attributes with regex or anything else?

You can try this using regex
$code = '[date format="j M, Y" type="jalali"]';
preg_match_all("/\[([^\]]*)\]/", $code, $matches);
$codes = [];
foreach($matches[1] as $match) {
// Normalize quotes into double quotes
$match = str_replace("'",'"',$match);
// Split by space but ignore inside of double quotes
preg_match_all('/(?:[^\s+"]+|"[^"]*")+/',$match,$tokens);
$parsed = [];
$prevToken = '';
foreach($tokens[0] as $token) {
if(strpos($token,'=') !== false) {
if($prevToken !== '') {
$parts = explode('=',$token);
$parsed[$prevToken][$parts[0]] = trim($parts[1],'"\'');
}
} else {
$parsed[$token] = [];
$prevToken = $token;
}
}
$codes[] = $parsed;
}
var_dump($codes);
Result:
array(1) {
[0]=>
array(1) {
["date"]=>
array(2) {
["format"]=>
string(6) "j M, Y"
["type"]=>
string(6) "jalali"
}
}
}

Related

Removing first part of string?

Trying to edit urls in array.
[0] => https://www.proud-web.jp/mansion/b115110/https://www.proud-web.jp/module/structure/outline/BukkenOutline.xphp?code_no=011244
[1] => https://www.proud-web.jp/mansion/p-ebisuminami88/https://www.proud-web.jp/module/structure/outline/BukkenOutline.xphp?code_no=011205
As you see urls are like this. trying to remove first url and contain the second.
expected result is like:
https://www.proud-web.jp/module/structure/outline/BukkenOutline.xphp?code_no=011244
https://www.proud-web.jp/module/structure/outline/BukkenOutline.xphp?code_no=011205
what I tried is right below. In that way I can only remove the second. But how can I fix this code to remove first url in the string not the second.
$result = [];
foreach($setLinks as $key) {
array_push($result, current(explode("/h", $key)));
}
You can use foreach followed by explode to get split the string w.r.t /https. Below is the code:
$array = ['https://www.proud-web.jp/mansion/b115110/https://www.proud-web.jp/module/structure/outline/BukkenOutline.xphp?code_no=011244','https://www.proud-web.jp/mansion/p-ebisuminami88/https://www.proud-web.jp/module/structure/outline/BukkenOutline.xphp?code_no=011205'];
$result = [];
foreach($array as $arr){
$getUrl = explode('/https', $arr);
array_push($result, 'https' . $getUrl[1]);
}
print_r($result);
I would separate the task in 3 subtasks.
First one being to capture the protocol using regex in example (the protocol of that url could be https, http, ftp ...)
Then, capture the url itself, splitting the string using :// as delimiter
Finally, rebuild protocol . "://" . url
In example :
<?php
$array =
[
'https://www.proud-web.jp/mansion/b115110/https://www.proud-web.jp/module/structure/outline/BukkenOutline.xphp?code_no=011244',
'https://www.proud-web.jp/mansion/p-ebisuminami88/https://www.proud-web.jp/module/structure/outline/BukkenOutline.xphp?code_no=011205',
'http://www.example.com/home/http://something',
'http://www.example.com/https/ftp://something',
'https://nothing.to.capture'
];
$result = array();
/*
* matches a slash -> \/
* followed by letters -> ([a-z]*)
* followed by :// -> :\/\/
* and capture the letters -> (the parenthesis)
* it can match, in example : something/mycustomprotocol://somethingelse
*/
$pattern = "/\/([a-z]*):\/\//i";
foreach($array as $item) {
preg_match_all($pattern, $item, $matches);
if (count($matches) > 0)
{
$urls = explode("://", $item, 3);
if (count($urls) > 2)
{
$protocol = $matches[1][0];
$result[] = $protocol . "://" . $urls[2];
}
}
}
var_dump($result);
Output
array(4) {
[0]=>
string(83) "https://www.proud-web.jp/module/structure/outline/BukkenOutline.xphp?code_no=011244"
[1]=>
string(83) "https://www.proud-web.jp/module/structure/outline/BukkenOutline.xphp?code_no=011205"
[2]=>
string(16) "http://something"
[3]=>
string(15) "ftp://something"
}
try this
unset($setLinks[0]);
foreach($setLinks as $key) {
echo $key;
}
This is what I would do:
$urls = [
'https://www.proud-web.jp/mansion/b115110/https://www.proud-web.jp/module/structure/outline/BukkenOutline.xphp?code_no=011244',
'https://www.proud-web.jp/mansion/p-ebisuminami88/https://www.proud-web.jp/module/structure/outline/BukkenOutline.xphp?code_no=011205'
];
foreach($urls as &$url){
$url = 'http'.preg_split('/^.+?\/http/', $url, 2, PREG_SPLIT_NO_EMPTY)[0];
}
print_r($urls);
Output
Array
(
[0] => https://www.proud-web.jp/module/structure/outline/BukkenOutline.xphp?code_no=011244
[1] => https://www.proud-web.jp/module/structure/outline/BukkenOutline.xphp?code_no=011205
)
Sandbox
I set it up so that it would handle both HTTP and HTTPS
You could use preg_replace to remove the leading text:
foreach ($setLinks as &$value) {
$value = preg_replace('#^.+(https?://.*)$#', '$1', $value);
}
print_r($setLinks);
Output:
Array (
[0] => https://www.proud-web.jp/module/structure/outline/BukkenOutline.xphp?code_no=011244
[1] => https://www.proud-web.jp/module/structure/outline/BukkenOutline.xphp?code_no=011205
)
Demo on 3v4l.org
i miss understand your question .kindly try it
<?php $quest = array("https://www.proud-web.jp/mansion/b115110/https://www.proud-web.jp/module/structure/outline/BukkenOutline.xphp?code_no=011244",
"https://www.proud-web.jp/mansion/p-ebisuminami88/https://www.proud-web.jp/module/structure/outline/BukkenOutline.xphp?code_no=011205");
foreach($quest as $q )
{
$allquest = explode("/https",$q);
echo "https".$allquest[1];
}
?>

PHP: String to multidimensional array

(Sorry for my bad English)
I have a string that I want to split into an array.
The corner brackets are multiple nested arrays.
Escaped characters should be preserved.
This is a sample string:
$string = '[[["Hello, \"how\" are you?","Good!",,,123]],,"ok"]'
The result structure should look like this:
array (
0 =>
array (
0 =>
array (
0 => 'Hello, \"how\" are you?',
1 => 'Good!',
2 => '',
3 => '',
4 => '123',
),
),
1 => '',
2 => 'ok',
)
I have tested it with:
$pattern = '/[^"\\]*(?:\\.[^"\\]*)*/s';
$return = preg_match_all($pattern, $string, null);
But this did not work properly. I do not understand these RegEx patterns (I found this in another example on this page).
I do not know whether preg_match_all is the correct command.
I hope someone can help me.
Many Thanks!!!
This is a tough one for a regex - but there is a hack answer to your question (apologies in advance).
The string is almost a valid array literal but for the ,,s. You can match those pairs and then convert to ,''s with
/,(?=,)/
Then you can eval that string into the output array you are looking for.
For example:
// input
$str1 = '[[["Hello, \\"how\\" are you?","Good!",,,123]],,"ok"]';
// replace , followed by , with ,'' with a regex
$pattern = '/,(?=,)/';
$replace = ",''";
$str2 = preg_replace($pattern, $replace, $str1);
// eval updated string
$arr = eval("return $str2;");
var_dump($arr);
I get this:
array(3) {
[0]=>
array(1) {
[0]=>
array(5) {
[0]=>
string(21) "Hello, "how" are you?"
[1]=>
string(5) "Good!"
[2]=>
string(0) ""
[3]=>
string(0) ""
[4]=>
int(123)
}
}
[1]=>
string(0) ""
[2]=>
string(2) "ok"
}
Edit
Noting the inherent dangers of eval the better option is to use json_decode with the code above e.g.:
// input
$str1 = '[[["Hello, \\"how\\" are you?","Good!",,,123]],,"ok"]';
// replace , followed by , with ,'' with a regex
$pattern = '/,(?=,)/';
$replace = ',""';
$str2 = preg_replace($pattern, $replace, $str1);
// eval updated string
$arr = json_decode($str2);
var_dump($arr);
If you can edit the code that serializes the data then it's a better idea to let the serialization be handled using json_encode & json_decode. No need to reinvent the wheel on this one.
Nice cat btw.
You might want to use a lexer in combination with a recursive function that actually builds the structure.
For your purpose, the following tokens have been used:
\[ # opening bracket
\] # closing bracket
".+?(?<!\\)" # " to ", making sure it's not escaped
,(?!,) # a comma, not followed by a comma
\d+ # at least one digit
,(?=,) # a comma followed by a comma
The rest is programming logic, see a demo on ideone.com. Inspired by this post.
class Lexer {
protected static $_terminals = array(
'~^(\[)~' => "T_OPEN",
'~^(\])~' => "T_CLOSE",
'~^(".+?(?<!\\\\)")~' => "T_ITEM",
'~^(,)(?!,)~' => "T_SEPARATOR",
'~^(\d+)~' => "T_NUMBER",
'~^(,)(?=,)~' => "T_EMPTY"
);
public static function run($line) {
$tokens = array();
$offset = 0;
while($offset < strlen($line)) {
$result = static::_match($line, $offset);
if($result === false) {
throw new Exception("Unable to parse line " . ($line+1) . ".");
}
$tokens[] = $result;
$offset += strlen($result['match']);
}
return static::_generate($tokens);
}
protected static function _match($line, $offset) {
$string = substr($line, $offset);
foreach(static::$_terminals as $pattern => $name) {
if(preg_match($pattern, $string, $matches)) {
return array(
'match' => $matches[1],
'token' => $name
);
}
}
return false;
}
// a recursive function to actually build the structure
protected static function _generate($arr=array(), $idx=0) {
$output = array();
$current = 0;
for($i=$idx;$i<count($arr);$i++) {
$type = $arr[$i]["token"];
$element = $arr[$i]["match"];
switch ($type) {
case 'T_OPEN':
list($out, $index) = static::_generate($arr, $i+1);
$output[] = $out;
$i = $index;
break;
case 'T_CLOSE':
return array($output, $i);
break;
case 'T_ITEM':
case 'T_NUMBER':
$output[] = $element;
break;
case 'T_EMPTY':
$output[] = "";
break;
}
}
return $output;
}
}
$input = '[[["Hello, \"how\" are you?","Good!",,,123]],,"ok"]';
$items = Lexer::run($input);
print_r($items);
?>

Parsing a string separated by semicolon

How can I parse this string
name:john;phone:12345;website:www.23.com;
into becoming like this
$name = "john";
$phone = "12345"
.....
because I want to save the parameter in one table column, I see joomla using this method to save the menu/article parameter.
Something like this(explode() is the way):
$string = 'name:john;phone:12345;website:www.23.com';
$array = explode(';',$string);
foreach($array as $a){
if(!empty($a)){
$variables = explode(':',$a);
$$variables[0] = $variables[1];
}
}
echo $name;
Working example
Please note: String must be like this, variable_name:value;variable_name2:value and the variable_name or variable cant contain ; or :
Here's how I'd do it:
Use explode() and split the string with ; as the delimiter.
Loop through the result array and explode() by :
Store the second part in a variable and push it into the result array
Optionally, if you want to convert the result array back into a string, you can use implode()
Code:
$str = 'name:john;phone:12345;website:www.23.com;';
$parts = explode(';', $str);
foreach ($parts as $part) {
if(isset($part) && $part != '') {
list($item, $value) = explode(':', $part);
$result[] = $value;
}
}
Output:
Array
(
[0] => john
[1] => 12345
[2] => www.23.com
)
Now, to get these values into variables, you can simply do:
$name = $result[0];
$phone = $result[1];
$website = $result[2];
Demo!
Use explode()
explode — Split a string by string
Description
Returns an array of strings, each of which is a substring of string formed by splitting it on boundaries formed by the string delimiter.
<?php
$string = "name:john;phone:12345;website:www.23.com;";
$pieces = explode(";", $string);
var_dump($pieces);
?>
Output
array(4) {
[0]=>
string(9) "name:john"
[1]=>
string(11) "phone:12345"
[2]=>
string(18) "website:www.23.com"
[3]=>
string(0) ""
}
DEMO
try this
<?php
$str = "name:john;phone:12345;website:www.23.com";
$array=explode(";",$str);
if(count($array)!=0)
{
foreach($array as $value)
{
$data=explode(":",$value);
echo $data[0]." = ".$data[1];
echo "<br>";
}
}
?>

How to parse console command strings in PHP with quotes

For my game I'm coding a console that sends messages via AJAX and then receives output from the server.
For example, an input would be:
/testmessage Hello!
However, I would also need to parse the quotes e.g.:
/testmessage "Hello World!"
However, since I am simply exploding the string with spaces, PHP sees "Hello and World!" as separate parameters. How do I make PHP think that "Hello World!" is one parameter?
Right now I'm using the following code to parse the command:
// Suppose $inputstring = '/testmessage "Hello World!"';
$inputstring = substr($inputstring, 1);
$parameters = explode(" ", $inputstring);
$command = strtolower($parameters[0]);
switch ($command) {
case "testmessage":
ConsoleDie($parameters[1]);
break;
}
Thank you in advance.
This code will do what you want:
$params = preg_split('/(".*?")/', '/testmessage "Hello World!" 1 2 3', -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
$realParams = array();
foreach($params as $param)
{
$param = trim($param);
if ($param == '')
continue;
if (strpos($param, '"') === 0)
$realParams = array_merge($realParams, array(trim($param, '"')));
else
$realParams = array_merge($realParams, explode(' ', $param));
}
unset($params);
print_r($realParams);
that print:
array(5) {
[0]=>
string(12) "/testmessage"
[1]=>
string(14) "Hello World!"
[2]=>
string(1) "1"
[3]=>
string(1) "2"
[4]=>
string(1) "3"
}
Note: As you can see the first parameter is the command
Hope this code is more 'understandable'
$input = $inputstring = '/testmessage "Hello World!" "single phrase" level two';
// find the parameters surrounded with quotes, grab only the value (remove "s)
preg_match_all('/"(.*?)"/', $inputstring, $quotes);
// for each parameters with quotes, put a 'placeholder' like {{1}}, {{2}}
foreach ($quotes[1] as $key => $value) {
$inputstring = str_replace($value, "{{{$key}}}", $inputstring);
}
// then separate by space
$parameters = explode(" ", $inputstring);
// replace the placeholders {{1}} with the original value
foreach ($parameters as $key => $value) {
if (preg_match('{{(\d+)}}', $value, $matches)) {
$parameters[$key] = $quotes[1][$matches[1]];
}
}
// here you go
print_r($parameters);
I may not have understood you fully, but if you are assuming that the first word is always a command word, and anything following is 'one parameter' you could do the following
$inputstring = substr($inputstring, 1);
$parameters = explode(" ", $inputstring);
// shift the first element off the array i.e. the command
$command = strtolower(array_shift($parameters));
// Glue the rest of the array together
$input_message = implode($parameters);
switch ($command) {
case "testmessage":
ConsoleDie($input_message);
break;
}
You can use the Symfony Console Component which offers a secure and clean way to get console inputs.
For your use case you should do:
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputArgument;
$input = new ArgvInput(null, new InputDefinition(array(
new InputArgument('message', InputArgument::REQUIRED)
)));
$parameters = $input->getArguments(); // $parameters['message'] contains the first argument

How to pull numbers from a string with curly brackets?

I have a test string which is something like this:
digit{digit}digit
I want to break this string into 3 variables. For example, 40{1}2 should be split into 40 1 2. The string could be as big as 2034{345}1245. I assume regex would be the best way to split this string.
Here's what I have so far:
$productID = preg_match('/(.*?){/', $product);
$productOptionID = preg_match('/{(.*?)}/', $product);
$optionValueID = preg_match('/}(.*?)/', $product);
No need for regular expressions here:
$str = '40{1}2';
sscanf($str, '%d{%d}%d', $part_1, $part_2, $part_3);
// $part_1 would equal: 40
// $part_2 would equal: 1
// $part_3 would equal: 2
With this method, the variables are already typecast to integers.
Try this instead:
preg_match('/^(\d+)\{(\d+)\}(\d+)$/', '123{456}789', $matches)
$productId = $matches[1];
$productOptionId = $matches[2];
$productValueId = $matches[3];
How about preg_split :
$str = '123{456}789';
$arr = preg_split("/[{}]/", $str);
print_r($arr);
output:
Array
(
[0] => 123
[1] => 456
[2] => 789
)
I would personally create a simple function that can manage the process of fetching the data from the string like so:
function processID($string)
{
$result = array();
$c = 0;
for($i = 0; $i < strlen($string); $i++)
{
if(!isset($result[$c]))
{
$result[$c] = "";
}
if($string[$i] == "{" || $string[$i] == "}")
{
$c++;
continue;
}
$result[$c] .= $string[$i];
}
return $result;
}
and then just use like:
$result = processID("2034{345}1245");
The outputted result would be like so:
array(3) {
[0]=>
string(4) "2034"
[1]=>
string(3) "345"
[2]=>
string(4) "1245"
}
and a working example can be found here: http://codepad.org/7k5tAzuy

Categories