How to extract the body part of a function? - php

The function extractBody() extracts the body part of a function:
$data = '
<?php
function my_function($param){
if($param === true){
// This is true
}else if($param === false){
// This is false
}else{
// This is not
}
}
?>
';
function extractBody($functionName, $data) {
$c = preg_match_all("/function\s+".$functionName."\s*\((?<param>[^\)]*)\)\s*(?<body>\{(?:[^{}]+|(?&body))*\})/", $data, $matches);
return $c > 0 ? $matches['body'] : null;
}
$body =extractBody("my_function", $data);
var_dump($body);
result: The variable $body contains
if($param === true){
// This is true
}else if($param === false){
// This is false
}else{
// This is not
}
Now I need a second function to work with lambda functions (function is assigned to a variable)
$data2 = '
<?php
$my_function = function($param){
if($param === true){
// This is true
}else if($param === false){
// This is false
}else{
// This is not
}
}
?>
';
function extractBody2($functionName, $data) {
$c = preg_match_all("/".$functionName."\s+=\s+function\s+\s*\((?<param>[^\)]*)\)\s*(?<body>\{(?:[^{}]+|(?&body))*\})/", $data, $matches);
return $c > 0 ? $matches['body'] : null;
}
$body2 =extractBody2("my_function", $data2);
var_dump($body2);
Unfortunately, I'm not a regex specialist and I get NULL back.
I think the error must be somewhere here: "/".$functionName."\s+=\s+
regex101 didn't reveal any issues though.

This works for me:
function extractBody2($functionName, $data) {
$c = preg_match_all("/\\$".$functionName."\s+=\s+function\s*\((?<param>[^\)]*)\)\s*(?<body>\{(?:[^{}]+|(?&body))*\})/", $data, $matches);
return $c > 0 ? $matches['body'] : null;
}

Related

PHP shorthand if echo else assign variable

I want to write this in one line if possible:
if ($some_var === true) {
$return .= $input;
} else {
echo $input;
}
Obviously I don't want this:
if ($some_var === true) { $return .= $input; } else { echo $input; }
but a shorter version of it.
I looked at other answers but I only find the echo (expression) ? true : false; statements. I don't want to echo on the true, only on the false.
$some_var = true;
$input = 'abc';
$return = '123';
echo ($some_var === true) ? ($return .= $input) : ($input);

preg_match - For sure there is a better way to search for these characters

So, I want to check the users-input, if it contains some of these characters:
" ' < >
I hope someone can show me a better way with less code
Thanks!
I used preg_match, but i just managed it with 4 nested if's.
/*Checks if the given value is valid*/
private function checkValidInput($input)
{
/*If there is no " */
if(preg_match('/"/', $input) == false)
{
/*If there is no ' */
if(preg_match("/'/", $input) == false)
{
/*If there is no <*/
if(preg_match("/</", $input) == false)
{
/*If there is no >*/
if(preg_match("/>/", $input) == false)
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
else
{
return false;
}
}
else
{
return false;
}
}
You could create a regex class
preg_match('#["\'<>]#', $input);
Edit:
If you need to check for all characters then use strpos() with for loop
function checkInput($val) {
$contains = true;
$required = "<>a";
for($i = 0, $count = strlen($required); $i < $count ; ++$i) {
$contains = $contains && false !== strpos($val, $required[$i]);
}
return $contains;
}
var_dump(checkInput('abcd<>a')); // true
var_dump(checkInput('abcd>a')); // false, doesn't contain <

How to repeat the same action for diffrent variables

<!-- language: php -->
<?php
// test variables
$l1 = "http://youtube.com/channel/";
$l2 = "http://youtube.com/channel/";
$l3 = "http://youtube.com/channel/";
$l4 = "http://youtube.com/channel/";
$fl = "http://youtube.com/channel/";
//set error false as default
$error = "false";
//check if variables are ready for use, if they are, add them to `$l` array
//I do each check as a seperate line, as it looks cleaner than 1 long if statement.
$l = [];
if(!empty($l1)) $l[] = $l1;
if(!empty($l2)) $l[] = $l2;
if(!empty($l3)) $l[] = $l3;
if(!empty($l4)) $l[] = $l4;
if(!empty($fl)) $l[] = $fl;
foreach($l as $key => $value) {
//1 line ternary is cleaner than if/else statetmnt
$errorKey = $key < 9? "0{$key}" : $key;
//each row by default has no error
$hasError = 0;
//check if this a valid url
if(!preg_match('|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i', $value)) {
$error = "true";
$hasError = 1;
}
if($hasError) {
//store error in array, to loop through later
$errors[] = $errorKey;
}
}
$search = '?sub_confirmation=1';
$searchUrl = "youtube.com/channel";
if (strpos($l, $searchUrl) !== false && strpos($l, $search) === false) {
$l = $value."".$search;
}
if($error == "false") {
echo $l1;
echo $l2;
echo $l3;
echo $l4;
echo $fl;
}
// deliver the error message
//Check if $error has been set to true at any point
if($error == "true") {
//loop through error array, echo error message if $errorNumber matches.
//at this point we KNOW there was an error at some point, no need to use a switch really
foreach($errors as $errorNumber) {
echo "Something went wrong here $errorNumber :o";
}
}
?>
Hello, my problem is at the end of the code where the strpos function is, so basically I want to check every url, once if it contains a certain url, and then add something to the end if it is so. But I don't want to repeat an if statement 4 times($fl variable doesn't has to be checked), I am quite new in all that so I hope somebody can help me, I tought about a switch statement but I guess there is a better way. And if I put it in the foreach aboth, it doesn't applies on the certain variables, only on the value variable.
You can assign $value by reference using this foreach header (notice the & in front of $value):
foreach($l as $key => &$value) {
By doing this every change you do to $value will also be done to the corresponding value in the $l array.
Then at the end of the foreach loop you put this code:
if (strpos($value, $searchUrl) !== false && strpos($value, $search) === false) {
$value .= $search;
}
So your final foreach loop should look like this:
foreach($l as $key => &$value) {
//1 line ternary is cleaner than if/else statetmnt
$errorKey = $key < 9? "0{$key}" : $key;
//each row by default has no error
$hasError = 0;
//check if this a valid url
if(!preg_match('|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i', $value)) {
$error = "true";
$hasError = 1;
}
if($hasError) {
//store error in array, to loop through later
$errors[] = $errorKey;
}
$search = '?sub_confirmation=1';
$searchUrl = "youtube.com/channel";
if (strpos($value, $searchUrl) !== false && strpos($value, $search) === false) {
$value .= $search;
}
}
You can read more about using references in foreach loops here: PHP: foreach
Edit:
To apply the changes not only to the elements of the $l array, but also to the original variables $l1, $l2 and so on, you should assign the elements to your array as references too:
$l = [];
if(!empty($l1)) $l[] = &$l1;
if(!empty($l2)) $l[] = &$l2;
if(!empty($l3)) $l[] = &$l3;
if(!empty($l4)) $l[] = &$l4;
if(!empty($fl)) $l[] = &$fl;
Personally, I think this is a good candidate for moving to a class. To be honest I'm not 100% sure what you are doing but will try to convert your code to a class.
class L {
public $raw = null;
public $modified = null;
public $error = false;
// create the class
public function __construct($data=null) {
$this->raw = $data;
// Check the raw passed in data
if ($data) {
$this->isUrl();
}
// If there was no error, check the data
if (! $this->error) {
$this->search();
}
}
// Do something ?
public function debug() {
echo '<pre>';
var_dump($this);
echo '</pre>';
}
public function getData() {
return ($this->modified) ? : $this->raw;
}
private function isUrl() {
$this->error = (! preg_match('|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i', $this->raw));
}
// Should a failed search also be an error?
private function search() {
if ($this->raw) {
if ( (strpos($this->raw, "youtube.com/channel") !== false) &&
(strpos($this->raw, "?sub_confirmation=1") === false) ) {
$this->modified = $this->raw ."?sub_confirmation=1";
}
}
}
}
// Test data
$testList[] = "test fail";
$testList[] = "https://youtube.com/searchFail";
$testList[] = "https://youtube.com/channel/success";
$testList[] = "https://youtube.com/channel/confirmed?sub_confirmation=1";
// Testing code
foreach($testList as $key=>$val) {
$l[] = new L($val);
}
foreach($l as $key=>$val) {
// Check for an error
if ($val->error) {
$val->debug();
} else {
echo '<pre>'.$val->getData().'</pre>';
}
}
And the output would be:
object(L)#1 (3) {
["raw"]=>
string(9) "test fail"
["modified"]=>
NULL
["error"]=>
bool(true)
}
https://youtube.com/searchFail
https://youtube.com/channel/success?sub_confirmation=1
https://youtube.com/channel/confirmed?sub_confirmation=1

String starts with a defined set of words

How to know if a given string starts with a defied set of words?
$allowed = array("foo", "bar");
pseudocode:
$boolean = somefunction($allowed,'food');
$boolean should be TRUE
function doesStringStartWith($string, $startWithOptions)
{
foreach($startWithOptions as $option)
{
if(substr($string, 0, strlen($option)) == $option) // comment this for case-insenstive
// uncomment this for case-insenstive: if(strtolower(substr($string, 0, strlen($option))) == strtolower($option))
{
return true;
}
}
return false;
}
$result = doesStringStartWith('food', array('foo', 'bar'));
function somefunction($allowed, $word) {
$result = array_filter(
$allowed,
function ($value) use ($word) {
return strpos($word, $value) === 0;
}
);
return (boolean) count($result);
}
$boolean = somefunction($allowed,'food');
If you know that all of your prefixes are the same length you could do this:
if ( in_array( substr($input,0,3), $allowed ) {
// your code
}
I came up with the following function:
function testPos($allowed,$s) {
$a = 0;
while($a < count($allowed)) {
if(strpos($s,$allowed[$a]) === 0) {
return true;
}
$a++;
}
}
Now you can try:
$allowed = array('foo','bar');
echo testPos($allowed,'food');

Own implementation of StringTokenizer returns unexcepted (endless) output

I am implementing my own StringTokenizer class in php, because the strtok function can only handle one opened tokenizer at the same time.
With
Hello;this;is;a;text
it works perfectly.
The output is:
**Hello**
**this**
**is**
**a**
**text**
But with
Hello;this;is;a;text;
it outputs:
**Hello**
**this**
**is**
**a**
**text**
****
****
<endless loop>
But I except the following output:
**Hello**
**this**
**is**
**a**
**text**
****
See my code below and please correct me:
class StringTokenizer
{
private $_str;
private $_chToken;
private $_iPosToken = 0;
private $_bInit;
public function __construct($str, $chToken)
{
if (empty($str) && empty($chToken))
{
throw new Exception('String and the token char variables cannot be empty.');
}
elseif(empty($chToken) && !empty($str))
{
throw new Exception('Missing parameter: Token char cannot be empty.');
}
elseif(!empty($chToken) && empty($str))
{
throw new Exception('Missing parameter: String cannot be empty.');
}
elseif(!empty($chToken) && !empty($str) && is_string($str) && strlen($chToken) >= 0)
{
$this->_str = $str;
$this->_chToken = $chToken;
$this->_bInit = true;
}
else
{
throw new Exception('TypeError: Illegal call to __construct from class StringTokenizer.');
}
}
public function next()
{
if ($this->_iPosToken === false)
{
return false;
}
if ($this->_bInit === true && (strlen($this->_str) - 1) > $this->_iPosToken)
{
$iCh1stPos = strpos($this->_str, $this->_chToken, $this->_iPosToken) + 1;
$this->_iPosToken = $iCh1stPos;
$this->_bInit = false;
return substr($this->_str, 0, $this->_iPosToken - 1);
}
elseif ($this->_bInit === false && (strlen($this->_str) - 1) > $this->_iPosToken)
{
$iCh1stPos = $this->_iPosToken;
$iCh2ndPos = strpos($this->_str, $this->_chToken, $this->_iPosToken);
if ($iCh2ndPos === false)
{
$this->_iPosToken = false;
return substr($this->_str, $iCh1stPos);
}
else
{
$this->_iPosToken = $iCh2ndPos + 1;
return substr($this->_str, $iCh1stPos, $iCh2ndPos - $iCh1stPos);
}
}
}
public function hasNext()
{
return strpos($this->_str, $this->chToken, $this->_iPosToken) === false ? false : true;
}
}
$strText = 'Hello;this;is;a;text';
$tokenizer = new StringTokenizer($strText, ';');
$tok = $tokenizer->Next();
while ($tok !== false)
{
echo '**' . $tok . '**' . PHP_EOL;
$tok = $tokenizer->next();
}
exit(0);
The problem with the third condition in the next() is this. String length is 26 and the last character match is 26 which you represent with the _iPosToken. so the condition in the 3rd if is false and the block never executes for the last semicolon.
A function in php returns NULL not FALSE by default.source
and the while never terminates at the bottom of the code.
So you have two options here. change the condition in the 3rd if to (strlen($this->_str)) >= $this->_iPosToken
OR
add a 4th condtion which returns false, as shown below.
public function next()
{
if ($this->_iPosToken === false)
{
return false;
}
if ($this->_bInit === true && (strlen($this->_str) - 1) > $this->_iPosToken)
{
$iCh1stPos = strpos($this->_str, $this->_chToken, $this->_iPosToken) + 1;
$this->_iPosToken = $iCh1stPos;
$this->_bInit = false;
return substr($this->_str, 0, $this->_iPosToken - 1);
}
elseif ($this->_bInit === false && (strlen($this->_str)-1 ) > $this->_iPosToken)
{
$iCh1stPos = $this->_iPosToken;
echo $this->_iPosToken;
$iCh2ndPos = strpos($this->_str, $this->_chToken, $this->_iPosToken);
if ($iCh2ndPos === FALSE) // You can chuck this if block. I put a echo here and //it never executed.
{
$this->_iPosToken = false;
return substr($this->_str, $iCh1stPos);
}
else
{
$this->_iPosToken = $iCh2ndPos + 1;
return substr($this->_str, $iCh1stPos, $iCh2ndPos - $iCh1stPos);
}
}
else return false;
}
Why do you like reinvent the wheel ?
You can use explode function, and then implements Iterator pattern in this tokenizer, i think it's an good approach.
http://php.net/explode
http://br1.php.net/Iterator
Example
<?php
class StringTokenizer implements Iterator
{
private $tokens = [];
private $position = 0;
public function __construct($string, $separator)
{
$this->tokens = explode($separator, $string);
}
public function rewind()
{
$this->position = 0;
}
public function current()
{
return $this->tokens[$this->position];
}
public function next()
{
++ $this->position;
}
public function key()
{
return $this->position;
}
public function valid()
{
return isset($this->tokens[$this->position]);
}
}
And using it:
$tokenizer = new StringTokenizer('h;e;l;l;o;', ';');
while($tokenizer->valid()) {
printf('**%s**', $tokenizer->current());
$tokenizer->next();
}

Categories