Getting function names from php file with preg_match_all - php

I have a file test.php:
public function editAction() {
//...
}
public function editGroupAction() {
//...
}
This is my code:
$source = "test.php";
$fh = fopen($source,'r');
while ($line = fgets($fh)) {
preg_match_all('/function(.*?)Action()/', $line, $matches);
var_dump($matches);
}
I want to get the functions that end with Action, but the result is empty. How do I get a result like this:
edit
editGroup

Your code can be simplified to this:
$fileName = 'test.php';
$fileContent = file_get_contents($fileName);
preg_match_all('/function(.*?)Action()/', $fileContent, $matches);
$functions = $matches[1];
Result ($functions):
Array
(
[0] => edit
[1] => editGroup
)
Following is your code with some changes...
First, check if anything was found, if so, add that to an array. Here is the working code:
$source = "test.php";
$fh = fopen($source,'r');
$m = array();
while ($line = fgets($fh)) {
if(preg_match_all('/function(.*?)Action()/', $line, $matches)){
$m[] = $matches[1][0];
}
}
Result ($m):
Array
(
[0] => edit
[1] => editGroup
)
Since preg_match_all returns the number of full pattern matches, you can use the return to check if anything was found. If you get a hit, add the wanted value to an array so you can get it later.
You were getting some empty results because not all lines will match ;)
Sidenote: As mentioned, you'll end up with something like string(5) " edit" (notice the white space). I don't know preg, so I can't fix it for you. What I can do is suggest you to change to $functions = array_map('trim', $matches[1]);

Not sure if that's what you want, but you should escape parentheses in regexp.
So here's your code with minor modifications:
<?php
$content = "public function editAction() public function editGroupAction()";
preg_match_all('/function(.*?)Action\(\)/', $content, $matches);
echo '<pre>';
var_dump($matches);
echo '</pre>';
?>
And yes, result is not empty :)

Related

How do I search a file and return an array of the results and line number in PHP?

How do I search a file and return an array of the results so that I can use it in a collection in PHP?
So, for example, say I have a .txt file with something like:
hellohello
hihi
heywhats up
hello hey whats up
hello
And I want to search for all occurrences with hello and its line number, then return it as an array so I can use it in a data collector.
So, it would return the line number and line, like:
$results = array
(
array('1', 'hellohello'),
array('4', 'hello hey whats up'),
array('5', 'hello'),
);
My idea is to us file_get_contents.
So, for example..
$file = 'example.txt';
function get_file($file) {
$file = file_get_contents($file);
return $file;
}
function searchFile($search_str) {
$matches = preg_match('/$search_str/i', get_file($file);
return $matches;
}
As an alternative, you could also use file() function so it reads the entire file into an array. Then you could just loop, then search. Rough example:
$file = 'example.txt';
$search = 'hello';
$results = array();
$contents = file($file);
foreach($contents as $line => $text) {
if(stripos($text, $search) !== false) {
$results[] = array($line+1, $text);
}
}
print_r($results);
Sidenote: stripos() is just an example, you could still use your other way/preference to search the needle for that particular line.

substr() function for array PHP

I have a code that will produce array of strings.... now my problem is i need to substr each result of the array but i think array is not allowed to be used in substr...
please help:
CODE:
<?php
$file = 'upload/filter.txt';
$searchfor = $_POST['search'];
$btn = $_POST['button'];
$sum = 0;
if($btn == 'search') {
//prevents the browser from parsing this as HTML.
header('Content-Type: text/plain');
// get the file contents, assuming the file to be readable (and exist)
$contents = file_get_contents($file);
// escape special characters in the query
$pattern = preg_quote($searchfor, '/');
// finalise the regular expression, matching the whole line
$pattern = "/^.*$pattern.*\$/m";
// search, and store all matching occurences in $matches
if(preg_match_all($pattern, $contents, $matches)){
echo "Found matches:\n";
$result = implode("\n", $matches[0]);
echo $result;
}
else{
echo "No matches found";
}
}
?>
The $matches there is the array... i need to substr each result of the $matches
you can use array_walk:
function fcn(&$item) {
$item = substr(..do what you want here ...);
}
array_walk($matches, "fcn");
Proper use of array_walk
array_walk( $matches, substr(your area));
Array_map accepts several arrays
array_map(substr(your area), $matches1, $origarray2);
in your case
array_map(substr(your area), $matches);
Read more:
array_map
array_walk
To find a sub string in an array I use this function on a production site, works perfectly.
I convert the array to a collection because it's easier to manage.
public function substrInArray($substr, Array $array) {
$substr = strtolower($substr);
$array = collect($array); // convert array to collection
return $body_types->map(function ($array_item) {
return strtolower($array_item);
})->filter(function ($array_item) use ($substr) {
return substr_count($array_item, $substr);
})->keys()->first();
}
This will return the key from the first match, it's just an example you can tinker. Returns null if nothing found.

Regex extract variables from [shortcode]

After migrating some content from WordPress to Drupal, I've got som shortcodes that I need to convert:
String content:
Irrelevant tekst...
[sublimevideo class="sublime"
poster="http://video.host.com/_previews/600x450/sbx-60025-00-da-ANA.png"
src1="http://video.host.com/_video/H.264/LO/sbx-60025-00-da-ANA.m4v"
src2="(hd)http://video.host.com/_video/H.264/HI/sbx-60025-00-da-ANA.m4v"
width="560" height="315"]
..more irrelevant text.
I need to find all variables within the shortcode [sublimevideo ...] and turn it into an array:
Array (
class => "sublime"
poster => "http://video.host.com/_previews/600x450/sbx-60025-00-da-FMT.png"
src1 => "http://video.host.com/_video/H.264/LO/sbx-60025-00-da-FMT.m4v"
src2 => "(hd)http://video.host.com/_video/H.264/HI/sbx-60025-00-da-FMT.m4v"
width => "560"
height => "315"
)
And preferably handle multiple instances of the shortcode.
I guess it can be done with preg_match_all() but I've had no luck.
This will give you what you want.
$data = 'Irrelevant tekst... [sublimevideo class="sublime" poster="http://video.host.com/_previews/600x450/sbx-60025-00-da-ANA.png" src1="http://video.host.com/_video/H.264/LO/sbx-60025-00-da-ANA.m4v" src2="(hd)http://video.host.com/_video/H.264/HI/sbx-60025-00-da-ANA.m4v" width="560" height="315"] ..more irrelevant text.';
$dat = array();
preg_match("/\[sublimevideo (.+?)\]/", $data, $dat);
$dat = array_pop($dat);
$dat= explode(" ", $dat);
$params = array();
foreach ($dat as $d){
list($opt, $val) = explode("=", $d);
$params[$opt] = trim($val, '"');
}
print_r($params);
In anticipation of the next challenge you will face with processing short codes you can use preg_replace_callback to replace the short tag data with it's resultant markup.
$data = 'Irrelevant tekst... [sublimevideo class="sublime" poster="http://video.host.com/_previews/600x450/sbx-60025-00-da-ANA.png" src1="http://video.host.com/_video/H.264/LO/sbx-60025-00-da-ANA.m4v" src2="(hd)http://video.host.com/_video/H.264/HI/sbx-60025-00-da-ANA.m4v" width="560" height="315"] ..more irrelevant text.';
function processShortCode($matches){
// parse out the arguments
$dat= explode(" ", $matches[2]);
$params = array();
foreach ($dat as $d){
list($opt, $val) = explode("=", $d);
$params[$opt] = trim($val, '"');
}
switch($matches[1]){
case "sublimevideo":
// here is where you would want to return the resultant markup from the shorttag call.
return print_r($params, true);
}
}
$data = preg_replace_callback("/\[(\w+) (.+?)]/", "processShortCode", $data);
echo $data;
You could use the following RegEx to match the variables:
$regex = '/(\w+)\s*=\s*"(.*?)"/';
I would suggest to first match the sublimevideo shortcode and get that into a string with the following RegEx:
$pattern = '/\[sublimevideo(.*?)\]/';
To get the correct array keys I used this code:
// $string is string content you specified
preg_match_all($regex, $string, $matches);
$sublimevideo = array();
for ($i = 0; $i < count($matches[1]); $i++)
$sublimevideo[$matches[1][$i]] = $matches[2][$i];
This returns the following array: (the one that you've requested)
Array
(
[class] => sublime
[poster] => http://video.host.com/_previews/600x450/sbx-60025-00-da-ANA.png
[src1] => http://video.host.com/_video/H.264/LO/sbx-60025-00-da-ANA.m4v
[src2] => (hd)http://video.host.com/_video/H.264/HI/sbx-60025-00-da-ANA.m4v
[width] => 560
[height] => 315
)
This is my interpretation, I come from a WordPress background and tried to recreate the setup for a custom php project.
It'll handle things like [PHONE] [PHONE abc="123"] etc
The only thing it falls flat on is the WordPress style [HERE] to [HERE]
Function to build a list of available shortcodes
// Setup the default global variable
function create_shortcode($tag, $function)
{
global $shortcodes;
$shortcodes[$tag] = $function;
}
define shortcodes individually, e.g. [IFRAME url="https://www.bbc.co.uk"]:
/**
* iframe, allows the user to add an iframe to a page with responsive div wrapper
*/
create_shortcode('IFRAME', function($atts) {
// ... some validation goes here
// The parameters that can be set in the shortcode
if (empty($atts['url'])) {
return false;
}
return '
<div class="embed-responsive embed-responsive-4by3">
<iframe class="embed-responsive-item" src="' . $atts['url'] . '">
</iframe>
</div>';
});
Then when you want to pass a block of html via the shortcode handling do... handle_shortcodes($some_html_with_shortcodes);
function handle_shortcodes($content)
{
global $shortcodes;
// Loop through all shortcodes
foreach($shortcodes as $key => $function){
$matches = [];
// Look for shortcodes, returns an array of ALL matches
preg_match_all("/\[$key([^_^\]].+?)?\]/", $content, $matches, PREG_UNMATCHED_AS_NULL);
if (!empty($matches))
{
$i = 0;
$full_shortcode = $matches[0];
$attributes = $matches[1];
if (!empty($attributes))
{
foreach($attributes as $attribute_string) {
// Decode the values (e.g. " to ")
$attribute_string = htmlspecialchars_decode($attribute_string);
// Find all the query args, looking for `arg="anything"`
preg_match_all('/\w+\=\"(.[^"]+)\"/', $attribute_string, $query_args);
$params = [];
foreach ($query_args[0] as $d) {
// Split the
list($att, $val) = explode('=', $d, 2);
$params[$att] = trim($val, '"');
}
$content = str_replace($full_shortcode[$i], $function($params), $content);
$i++;
}
}
}
}
return $content;
}
I've plucked these examples from working code so hopefully it's readable and doesn't have any extra functions exclusive to our setup.
As described in this answer, I'd suggest letting WordPress do the work for you using the get_shortcode_regex() function.
$pattern = get_shortcode_regex();
preg_match_all("/$pattern/",$wp_content,$matches);
This will give you an array that is easy to work with and shows the various shortcodes and affiliated attributes in your content. It isn't the most obvious array format, so print it and take a look so you know how to manipulate the data you need.

PHP batch file renaming

I have a bunch of files named like this...
full-file(1).jpg
full-file(10).jpg
full-file(11).jpg
full-file(12).jpg
full-file(2).jpg
etc...
I'm trying to figure out the most efficient way to rename all these files using PHP so that they get renamed like this...
full-file0001.jpg
full-file0010.jpg
full-file0011.jpg
full-file0012.jpg
full-file0002.jpg
I've got as far as reading all the files from a folder and looping through them but not sure about the best way to remove the brackets and make the number 4 digits with leading 0.
$image_files = get_files($thumbpath);
foreach($image_files as $index=>$file) {
echo $file;
}
Use a regular expression to get the digit, and then zero-pad it using sprintf():
$image_files = get_files($thumbpath);
foreach($image_files as $index=>$file) {
// Capture \d+ into $matches[1]
preg_match('/\((\d+)\)/', $file, $matches);
// Pad it with %04d in sprintf()
$newfile = sprintf("full-file%04d.jpg", $matches[1]);
}
Example:
php > $file = 'full-file(12).jpg';
php > preg_match('/\((\d+)\)/', $file, $matches);
php > $newfile = sprintf("full-file%04d.jpg", $matches[1]);
php > echo $newfile;
// full-file0012.jpg
Update (for more flexible filenames):
To please the downvoter I can only assume wanted more flexible filenames, expand the regular expression:
$image_files = get_files($thumbpath);
foreach($image_files as $index=>$file) {
preg_match('/([^(]+)\((\d+)\)(.+)/', $file, $matches);
$newfile = sprintf("%s%04d%s", $matches[1], $matches[2], $matches[3]);
// And rename the file
if (!rename($file, $newfile)) {
echo "Could not rename $file.\n";
}
else echo "Successfully renamed $file to $newfile\n";
}
The pattern matches first, everything up to the the first ( with ([^(]+), followed by the number via (\d+), and everything remaining via (.*).
You can use a mixture of REGEXP (remove brackets) and string padding (to force four digits).
Note I use a replacement callback to do both operations in one place.
$files = array(
'full-file(1).jpg',
'full-file(10).jpg',
'full-file(11).jpg',
'full-file(12).jpg',
'full-file(2).jpg'
);
function pr_callback($match) {
return str_pad($match[1], 4, '0', STR_PAD_LEFT);
}
foreach($files as $file)
echo preg_replace_callback('/\((\d+)\)/', pr_callback, $file).'<br />';
Outputs:
full-file0001.jpg
full-file0010.jpg
full-file0011.jpg
full-file0012.jpg
full-file0002.jpg
I haven't seen anyone recommend sscanf() yet.
<?php
$files = array(
"full-file(1).jpg",
"full-file(10).jpg",
"full-file(11).jpg",
"full-file(12).jpg",
"full-file(2).jpg",
);
foreach ($files as $file) {
$n = sscanf($file, "full-file(%d).jpg");
printf("full-file%04d.jpg\n", $n[0]);
}
returns:
full-file0001.jpg
full-file0010.jpg
full-file0011.jpg
full-file0012.jpg
full-file0002.jpg
This only works if "full-file" is the actual name of your file, of course. sscanf() is not a regex parser, it merely extracts data using printf()-style format strings ... though it does do some more advanced format recognition than is documented at http://php.net/sscanf . If you need to handle other filenames, you can extend the format string:
<?php
$files = array(
"this-file(1).jpg",
"full-file(10).jpg",
"foo(11).jpg",
"blarg(12).jpg",
"full-file(2).jpg",
);
foreach ($files as $file) {
$n = sscanf($file, "%[a-z-](%d).jpg");
printf("%s%04d.jpg\n", $n[0], $n[1]);
}
returns:
this-file0001.jpg
full-file0010.jpg
foo0011.jpg
blarg0012.jpg
full-file0002.jpg
Assuming your files aren't actually called full-file(0000).jpg:
<?php
$arr = array('full-file(1).jpg',
'full-file(10).jpg',
'full-file(11).png',
'full-file(12).jpg',
'full-file(2).gif',
'abc(12345).jpg',
'def(99).jpg',
'xzy-file(100).jpg');
function format_filename($matches){
return $matches[1].sprintf("%04d",$matches[3]).'.'.$matches[5];
}
function process_array(&$value){
$value = preg_replace_callback('/^(.*?)(\()(\d+)(\)).(jpg|png|gif)/','format_filename',$value);
}
array_walk($arr,'process_array');
print_r($arr);
/*
Array
(
[0] => full-file0001.jpg
[1] => full-file0010.jpg
[2] => full-file0011.png
[3] => full-file0012.jpg
[4] => full-file0002.gif
[5] => abc12345.jpg
[6] => def0099.jpg
[7] => xzy-file0100.jpg
)
*/
?>
Use code:
preg_match('/^(.*?)\((\d+)\)(.*)$/', $name, $m);
$name = sprintf("%s%04d%s", $m[1], $m[2], $m[3]);
See and test it here.
You will need str-pad(). A sample soon...
EDIT 1: solution using str_pad and preg_replace_callback.
OBS: Anonymous functions only in php5.3+.
foreach ($image_files as $file)
{
$o = preg_replace_callback(
"|\((\d+)\)|", function($matches)
{
$r = str_pad($matches[1], 4, '0', STR_PAD_LEFT);
return $r;
}
, $file);
echo $o . "\n";
}

list all defined constants from a file in php

I have some php files that includes some language constants
define("_SEARCH","Search");
define("_LOGIN","Login");
define("_WRITES","writes");
define("_POSTEDON","Posted on");
define("_NICKNAME","Nickname");
now I need to read each file and list all constants and their values
and to return an output like this :
constant name :
value is :
so I think there should be function to list all defined constants of a given php file.
I'm aware of functions like token_get_all or get_defined_constants but i wasn't able to do it.
If the files do contain nothing but define statements, you can use get_defined_constants:
function getUserDefinedConstants() {
$constants = get_defined_constants(true);
return (isset($constants['user']) ? $constants['user'] : array());
}
$constantsBeforeInclude = getUserDefinedConstants();
include('file.php');
$constantsAfterInclude = getUserDefinedConstants();
$newConstants = array_diff_assoc($constantsAfterInclude, $constantsBeforeInclude);
What it does is basically: get_defined_constants(true) gives us an array of arrays with all available constants, sorted by sections (core, user, ..) - the array under the key 'user' gives us all user-defined constants that we defined in our php code using define, up to that point. array_diff_assoc gives us the difference between this array before and after the file got included.. and that is exactly a list of all constants that got defined in that specific file (as long as there is none of the declarations a duplicate, meaning a constant with that exact name has been defined before - but this would cause an error anyway).
this is the php script you need:
<?php
//remove comments
$Text = php_strip_whitespace("your_constants_file.php");
$Text = str_replace("<?php","",$Text);
$Text = str_replace("<?","",$Text);
$Text = str_replace("?>","",$Text);
$Lines = explode(";",$Text);
$Constants = array();
//extract constants from php code
foreach ($Lines as $Line) {
//skip blank lines
if (strlen(trim($Line))==0) continue;
$Line = trim($Line);
//skip non-definition lines
if (strpos($Line,"define(")!==0) continue;
$Line = str_replace("define(\"","",$Line);
//get definition name & value
$Pos = strpos($Line,"\",\"");
$Left = substr($Line,0,$Pos);
$Right = substr($Line,$Pos+3);
$Right = str_replace("\")","",$Right);
$Constants[$Left] = $Right;
}
echo "<pre>";
var_dump($Constants);
echo "</pre>";
?>
The result will be something similar to this:
array(5) {
["_SEARCH"]=>
string(6) "Search"
["_LOGIN"]=>
string(5) "Login"
["_WRITES"]=>
string(6) "writes"
["_POSTEDON"]=>
string(9) "Posted on"
["_NICKNAME"]=>
string(8) "Nickname"
}
Late to the game here but I had a similar issue. You could use an include() substitute/wrapper function that logs constants in an accessible global array.
<?php
function include_build_page_constants($file) {
global $page_constants ;
$before = get_defined_constants(true);
include_once($file);
$after = get_defined_constants(true);
if ( isset($after['user']) ) {
if ( isset($before['user']) ) {
$current = array_diff_assoc($after['user'],$before['user']);
}else{
$current = $after['user'];
}
$page_constants[basename($file)]=$current;
}
}
include_and_build_page_constants('page1.php');
include_and_build_page_constants('page2.php');
// test the array
echo '<pre>'.print_r($page_constants,true).'</pre>';
?>
This will result in something like:
Array
(
[page1.php] => Array
(
[_SEARCH] => Search
[_LOGIN] => Login
[_WRITES] => writes
[_POSTEDON] => Posted on
[_NICKNAME] => Nickname
)
[page2.php] => Array
(
[_THIS] => Foo
[_THAT] => Bar
)
)
Assuming that you want to do this on runtime, you should take a look at PHP Reflection, specifically at the ReflectionClass::getConstants() which lets you do exactly what you seem to want.
I too had the same problem. I went from jondinham's suggestion, but I prefer to use regex, as it is a bit easier to control and flexible. Here's my version of the solution:
$text = php_strip_whitespace($fileWithConstants);
$text = str_replace(array('<?php', '<?', '?>'), '', $text);
$lines = explode(";", $text);
$constants = array();
//extract constants from php code
foreach ($lines as $line) {
//skip blank lines
if (strlen(trim($line)) == 0)
continue;
preg_match('/^define\((\'.*\'|".*"),( )?(.*)\)$/', trim($line), $matches, PREG_OFFSET_CAPTURE);
if ($matches) {
$constantName = substr($matches[1][0], 1, strlen($matches[1][0]) - 2);
$constantValue = $matches[3][0];
$constants[$constantName] = $constantValue;
}
}
print_r($constants);

Categories