How to get list of functions that are declared in a php file
You can get a list of currently defined function by using get_defined_functions():
$arr = get_defined_functions();
var_dump($arr['user']);
Internal functions are at index internal while user-defined function are at index user.
Note that this will output all functions that were declared previous to that call. Which means that if you include() files with functions, those will be in the list as well. There is no way of separating functions per-file other than making sure that you do not include() any file prior to the call to get_defined_functions().
If you must have the a list of functions per file, the following will attempt to retrieve a list of functions by parsing the source.
function get_defined_functions_in_file($file) {
$source = file_get_contents($file);
$tokens = token_get_all($source);
$functions = array();
$nextStringIsFunc = false;
$inClass = false;
$bracesCount = 0;
foreach($tokens as $token) {
switch($token[0]) {
case T_CLASS:
$inClass = true;
break;
case T_FUNCTION:
if(!$inClass) $nextStringIsFunc = true;
break;
case T_STRING:
if($nextStringIsFunc) {
$nextStringIsFunc = false;
$functions[] = $token[1];
}
break;
// Anonymous functions
case '(':
case ';':
$nextStringIsFunc = false;
break;
// Exclude Classes
case '{':
if($inClass) $bracesCount++;
break;
case '}':
if($inClass) {
$bracesCount--;
if($bracesCount === 0) $inClass = false;
}
break;
}
}
return $functions;
}
Use at your own risk.
You can use get_defined_functions() before and after you include the file, and look at what gets added to the array the second time. Since they appear to be in order of definition, you can just use the index like this:
$functions = get_defined_functions();
$last_index = array_pop(array_keys($functions['user']));
// Include your file here.
$functions = get_defined_functions();
$new_functions = array_slice($functions['user'], $last_index);
You can use the get_defined_functions() function to get all defined function in your current script.
See: http://www.php.net/manual/en/function.get-defined-functions.php
If you want to get the functions defined in another file, you can try using http://www.php.net/token_get_all like this:
$arr = token_get_all(file_get_contents('anotherfile.php'));
Then you can loop through to find function tokens with the symbols defined. The list of tokens can be found http://www.php.net/manual/en/tokens.php
If you're not worried about catching some commented out ones, this might be the simplest way:
preg_match_all('/function (\w+)/', file_get_contents(__FILE__), $m);
var_dump($m[1]);
I wrote this small function to return the functions in a file.
https://gist.github.com/tonylegrone/8742453
It returns a simple array of all the function names. If you're calling it in the particular file you want to scan, you can just use the following:
$functions = get_functions_in_file(__FILE__);
I solved this problem with array_diff
$funcs = get_defined_functions()["user"];
require_once 'myFileWithNewFunctions.php'; // define function testFunc() {} here
var_dump( array_values( array_diff(get_defined_functions()["user"], $funcs) ) )
// output: array[ 0 => "test_func"]
Update
To get the "real" functions name try this
foreach($funcsDiff AS $newFunc) {
$func = new \ReflectionFunction($newFunc);
echo $func->getName(); // testFunc
}
Well for what ever reason if you need to do this I show you:
Example file: Functions.php (I just wrote some random shit does not Mather it works with everything)
<?php
// gewoon een ander php script. door het gebruiken van meerdere includes kun je gemakkelijk samen aan één app werken
function johannes($fnaam, $mode, $array){
switch ($mode) {
case 0:
echo "
<center>
<br><br><br><br><br>
he johannes!<br><br>
klik hier voor random text:<br>
<input type='submit' value='superknop' id='btn' action='randomding' level='0' loadloc='randomshit' />
<p id='randomshit'></p>
</center>
";
break;
case 1:
echo "bouw formulier";
break;
case 2:
echo "verwerk formulier";
break;
default:
echo "[Error: geen mode gedefinieerd voor functie '$fnaam'!]";
}
}
function randomding($fnaam, $mode, $array){
$randomar = array('He pipo wat mot je!','bhebhehehehe bheeeee. rara wie ben ik?','poep meloen!','He johannes, wat doeeeeee je? <input type="text" name="doen" class="deze" placeholder="Wat doe je?" /> <input type="button" value="vertellen!" id="btn" action="watdoetjho" level="0" loadloc="hierzo" kinderen="deze"> <p id="hierzo"></p>','knopje de popje opje mopje','abcdefghijklmnopqrstuvwxyz, doe ook nog mee','Appien is een **p!!!!!! hahhahah<br><br><br><br> hahaha','Ik weet eiegelijk niks meer te verzinnen','echt ik weet nu niks meer','nou oke dan[[][(!*($##&*$*^éäåðßð','he kijk een microboat: <br> <img src="https://encrypted-tbn2.gstatic.com/images?q=tbn:ANd9GcS_n8FH6xzf24kEc31liZF6ULHCn2IINFurlFZ_G0f0_F4sLTi74w" alt="microboat" />');
$tellen = count($randomar);
$tellen--;
$rand = rand(0, $tellen);
echo $randomar[$rand];
}
function watdoetjho($fnaam, $mode, $array){
$dit = $array['doen'];
echo "Johannes is: $dit";
}
?>
We have 3 functions inside here:
johannes
randomding
watdoetjho
But we also call these functions:
count
rand
If we use get_defined_functions we will get all functions inside scope of script. Yes the PHP functions are separated from the user declared ones but still we want from a specific file.
If we use token_get_all we will receive a large amount of data we need to separate first.
I found these number which we need to make some connections inside the array to match the user functions. Otherwise we will have a list including all of php functions which are called upon.
The numbers are:
334 (List of all the user declared functions)
307 (List of all the function names)
If we filter the array to take all the 334 elements we have this:
334 -|- function -|- 5
But we need the function name and all the array elements are in relation with the 3rd value. So now we need to filter all the array elements which match the 3rd value (5) and the constant number 307.
This will give us something like this:
307 -|- johannes -|- 5
Now in PHP it will look like this:
<?php
error_reporting(E_ALL ^ E_NOTICE); // Or we get these undefined index errors otherwise use other method to search array
// Get the file and get all PHP language tokens out of it
$arr = token_get_all(file_get_contents('Functions.php'));
//var_dump($arr); // Take a look if you want
//The array where we will store our functions
$functions = array();
// loop trough
foreach($arr as $key => $value){
//filter all user declared functions
if($value[0] == 334){
//Take a look for debug sake
//echo $value[0] .' -|- '. $value[1] . ' -|- ' . $value[2] . '<br>';
//store third value to get relation
$chekid = $value[2];
}
//now list functions user declared (note: The last check is to ensure we only get the first peace of information about the function which is the function name, else we also list other function header information like preset values)
if($value[2] == $chekid && $value[0] == 307 && $value[2] != $old){
// just to see what happens
echo $value[0] .' -|- '. $value[1] . ' -|- ' . $value[2] . '<br>';
$functions[] = $value[1];
$old = $chekid;
}
}
?>
Result in this case is:
307 -|- johannes -|- 5
307 -|- randomding -|- 31
307 -|- watdoetjho -|- 43
To get list of defined and used functions in code, with small useful file manager, you can use code below. Enjoy!
if ((!function_exists('check_password'))||(!check_password()) ) exit('Access denied.'); //...security!
echo "<html><body>";
if (!$f) echo nl2br(htmlspecialchars('
Useful parameters:
$ext - allowed extensions, for example: ?ext=.php,css
$extI - case-insensitivity, for example: ?extI=1&ext=.php,css
'));
if (($f)&&(is_readable($_SERVER['DOCUMENT_ROOT'].$f))&&(is_file($_SERVER['DOCUMENT_ROOT'].$f))) {
echo "<h3>File <strong>$f</strong></h3>\n";
if(function_exists('get_used_functions_in_code')) {
echo '<h3>Used:</h3>';
$is=get_used_functions_in_code(file_get_contents($_SERVER['DOCUMENT_ROOT'].$f));
sort($is);
$def=get_defined_functions();
$def['internal']=array_merge($def['internal'], array('__halt_compiler', 'abstract', 'and', 'array', 'as', 'break', 'callable', 'case', 'catch', 'class', 'clone', 'const', 'continue', 'declare', 'default', 'die', 'do', 'echo', 'else', 'elseif', 'empty', 'enddeclare', 'endfor', 'endforeach', 'endif', 'endswitch', 'endwhile', 'eval', 'exit', 'extends', 'final', 'finally', 'for', 'foreach', 'function', 'global', 'goto', 'if', 'implements', 'include', 'include_once', 'instanceof', 'insteadof', 'interface', 'isset', 'list', 'namespace', 'new', 'or', 'print', 'private', 'protected', 'public', 'require', 'require_once', 'return', 'static', 'switch', 'throw', 'trait', 'try', 'unset', 'use', 'var', 'while'));
foreach ($def['user'] as &$e) $e=strtolower($e); unset($e);
foreach ($is as &$e) if (!in_array(strtolower($e), $def['internal'], TRUE)) $e='<span style="color: red">'.$e.'</span>'; unset($e); //user-defined functions will be red
echo implode('<br />'.PHP_EOL,$is);
}
else echo "Error: missing function 'get_used_functions_in_code' !";
if(function_exists('get_defined_functions_in_code')) {
echo '<br /><h3>Defined:</h3>';
$is=get_defined_functions_in_code(file_get_contents($_SERVER['DOCUMENT_ROOT'].$f));
sort($is);
echo implode('<br />',$is);
}
else echo "Error: missing function 'get_defined_functions_in_code' !";
}
/*
File manager
*/
else {
if (!function_exists('select_icon')) {
function select_icon($name) {$name = pathinfo($name); return '['.$name["extension"].']';}
}
if($ext) $extensions=explode(',',strrev($ext));
if(!$f) $f=dirname($_SERVER['PHP_SELF']);
echo "<h3>Dir ".htmlspecialchars($f)."</h3><br />\n<table>";
$name=scandir($_SERVER['DOCUMENT_ROOT'].$f);
foreach($name as $name) {
if (!($fileOK=(!isset($extensions)))) {
foreach($extensions as $is) if (!$fileOK) $fileOK=((strpos(strrev($name),$is)===0)||($extI &&(stripos(strrev($name),$is)===0)));
}
$is=is_dir($fullName=$_SERVER['DOCUMENT_ROOT']."$f/$name");
if ($is || $fileOK) echo '<tr><td>'.select_icon($is ? 'x.folder' : $name).' </td><td> '.($is ? '[' : '').''.htmlspecialchars($name).''.($is ? ']' : '').'</td>';
if ($is) echo '<td> </td><td> </td>';
elseif ($fileOK) echo '<td style="text-align: right"> '.number_format(filesize($fullName),0,"."," ").' </td><td> '.date ("Y.m.d (D) H:i",filemtime($fullName)).'</td>';
if ($is || $fileOK) echo '</tr>'.PHP_EOL;
}
echo "\n</table>\n";
}
echo "<br /><br />".date ("Y.m.d (D) H:i")."</body></html>";
return;
/********************************************************************/
function get_used_functions_in_code($source) {
$tokens = token_get_all($source);
$functions = array();
$thisStringIsFunc = 0;
foreach($tokens as $token) {
if(($token[0]!=T_WHITESPACE)&&((!is_string($token))||($token[0]!='('))) unset($func);
if((is_array($token))&&(in_array($token[0],array(T_EVAL,T_EXIT,T_INCLUDE,T_INCLUDE_ONCE,T_LIST,T_REQUIRE,T_REQUIRE_ONCE,T_RETURN,T_UNSET)))) {$token[0]=T_STRING;$thisStringIsFunc=1;}
switch($token[0]) {
case T_FUNCTION: $thisStringIsFunc=-1;
break;
case T_STRING:
if($thisStringIsFunc>0) {
if (!in_array(strtoupper($token[1]),$functionsUp)) {$functions[]=$token[1];$functionsUp[]=strtoupper($token[1]);}
$thisStringIsFunc = 0;
} elseif ($thisStringIsFunc+1>0) {
$func = $token[1];
} else $thisStringIsFunc = 0;
break;
case '(':if($func) if(!in_array(strtoupper($func),$functionsUp)) {$functions[]=$func;$functionsUp[]=strtoupper($func);}
}
}
return $functions;
}
/********************************************/
function get_defined_functions_in_code($source) {
$tokens = token_get_all($source);
... then Andrew code (get_defined_functions_in_file) (https://stackoverflow.com/a/2197870/9996503)
}
Finding string (eg. Function names) in a file is simple with regex.
Just read the file and parse the content using preg_match_all.
I wrote a simple function to get list of functions in a file.
https://gist.github.com/komputronika/f92397b4f60870131ef52930faf09983
$a = functions_in_file( "mylib.php" );
The simplest thing (after I saw #joachim answer) is to use get_defined_functions and then only watch for the 'user' key (which contains an array of user-defined methods)
This is the code that helped me solved the problem
<?php
//Just to be sure it's empty (as it should be)
$functions = get_defined_functions();
print_r($functions['user']);
//Load the needed file
require_once '/path/to/your/file.php';
//display the functions loaded
$functions2 = get_defined_functions();
print_r($functions2['user']);
To retrieve also the params definition: (Source in comments)
Array
(
[0] => function foo ( &&bar, &big, [$small = 1] )
[1] => function bar ( &foo )
[2] => function noparams ( )
[3] => function byrefandopt ( [&$the = one] )
)
$functions = get_defined_functions();
$functions_list = array();
foreach ($functions['user'] as $func) {
$f = new ReflectionFunction($func);
$args = array();
foreach ($f->getParameters() as $param) {
$tmparg = '';
if ($param->isPassedByReference()) $tmparg = '&';
if ($param->isOptional()) {
$tmparg = '[' . $tmparg . '$' . $param->getName() . ' = ' . $param->getDefaultValue() . ']';
} else {
$tmparg.= '&' . $param->getName();
}
$args[] = $tmparg;
unset ($tmparg);
}
$functions_list[] = 'function ' . $func . ' ( ' . implode(', ', $args) . ' )' . PHP_EOL;
}
print_r($functions_list);
do include to the file and try this :
$functions = get_defined_functions();
print_r($functions['user']);
Related
What would be the best way to do this?
I'm given a template with some things in it like {:HELLO-WORLD:} tags in it.
I'm also given an array like:
Array
(
[0] => Array
(
[Name] => {:HELLO-WORLD:}
[Plugin] => "<?php return 'Hello World'; ?>"
[Settings] =>
)
)
What can I do to make sure {:HELLO-WORLD:} gets replaced with the output of Hello World?
I am currently attempting:
private function PluginReplacer($arr, $str){
$gsCt = count($arr);
$kv = array();
for ($i=0;$i<$gsCt;++$i){
$kv[$arr[$i]['Name']] = $arr[$i]['Plugin'];
}
return str_replace(array_keys($kv), $this->EvalCode(array_values($kv)), $str);
}
// Eval Some Code
private function EvalCode($var){
require_once('plugins.php');
$pr = new CloudCMSPluginRunner();
$pr->Code = $var;
$pr->SitePath = GetSiteAssetsPath($this->SiteID);
$pr->RunIt();
echo $pr->Error;
}
<?php
class CloudCMSPluginRunner {
public $Code = '';
public $Error = '';
public $SitePath = '';
private $DoNotAllow = array('echo', 'eval', 'phpinfo', '/`/', 'chmod', 'chown', 'umask', 'shell_exec',
'exec', 'escapeshellcmd', 'proc_open', 'proc_terminate', 'proc_get_status',
'passthru', 'proc_nice', 'system', 'escapeshellarg', 'ob_start', 'ob_end_clean',
'ob_get_clean', 'session_start', 'putenv', 'header', 'sleep', 'uwait', 'ini_set',
'error_reporting', 'chgrp', 'basename', 'clearstatcache', 'copy', 'delete',
'dirname', 'disk_free_space', 'disk_total_space', 'diskfreespace', 'fclose',
'feof', 'fflush', 'fgetc', 'fgetcsv', 'fgets', 'fgetss', 'file_exists', 'file_get_contents',
'file_put_contents', 'file', 'fileatime', 'filectime', 'filegroup', 'fileinode', 'filemtime',
'fileowner', 'fileperms', 'filesize', 'filetype', 'flock', 'fnmatch', 'fopen', 'fpassthru',
'fputcsv', 'fputs', 'fread', 'fscanf', 'fseek', 'fstat', 'ftell', 'ftruncate', 'fwrite', 'glob',
'is_dir', 'is_executable', 'is_file', 'is_link', 'is_readable', 'is_uploaded_file', 'is_writeable',
'is_writable', 'lchgrp', 'lchown', 'link', 'linkinfo', 'lstat', 'mkdir', 'move_uploaded_file',
'parse_ini_file', 'parse_ini_string', 'pathinfo', 'pclose', 'popen', 'readfile', 'readlink',
'realpath_cache_get', 'realpath_cache_size', 'realpath', 'rename', 'rewind', 'rmdir', 'set_file_buffer',
'stat', 'symlink', 'tempnam', 'tmpfile', 'touch', 'unlink', 'chdir', 'chroot', 'closedir', 'dir',
'getcwd', 'opendir', 'readdir', 'rewinddir', 'scandir', 'dio_close', 'dio_fcntl', 'dio_open', 'dio_read',
'dio_seek', 'dio_stat', 'dio_tcsetattr', 'dio_truncate', 'dio_write', 'finfo_buffer', 'finfo_close',
'finfo_file', 'finfo_open', 'finfo_set_flags', 'mime_content_type', 'inotify_add_watch', 'inotify_init',
'inotify_queue_len', 'inotify_read', 'inotify_rm_watch', 'setproctitle', 'setthreadtitle', 'xattr_get',
'xattr_list', 'xattr_remove', 'xattr_set', 'xattr_supported');
public function RunIt(){
$valid = $this->CheckIt();
if($valid){
eval($this->Code);
}else{
// code is invalid
$this->Error = 'The code in this plugin is invalid.';
return null;
}
}
private function CheckIt(){
$ret = false;
ob_start(); // Catch potential parse error messages
$code = eval('if(0){' . "\n" . $this->Code . "\n" . '}');
ob_end_clean();
$ret = ($code !== false);
// run a check against the dissallowed
$ret = (stripos($this->Code , $this->DoNotAllow) !== false);
// make sure any path is there's and there's alone
$ret = (stripos($this->Code , $this->SitePath) !== false);
return $ret;
}
}
?>
But nothing is happenning... in fact the page I am attempting to run this on blanks out (meaning there is an error happenning)
You're generating code formatted as:
eval("function GetPageWeAreOn(){$p=explode('/',$_SERVER['REQUEST_URI']);return $p[1];}");
What's happening is that PHP is interpreting the variables wrongly - instead of passing them in to the eval'ed function, it's interpolating them first.
I've avoided the error by escaping them:
eval("function GetPageWeAreOn(){\$p=explode('/',\$_SERVER['REQUEST_URI']);return \$p[1];}");
You can avoid the need for escaping by putting your string to be eval'ed into single quotes, too - that doesn't try to interpolate variables:
eval('function GetPageWeAreOn(){$p=explode("/",$_SERVER["REQUEST_URI"]);return $p[1];}');
i am working on php i have dynamic array i need to get the array result store in some variable i encounter the error :array to string conversion
coding
<?php
require_once('ag.php');
class H
{
var $Voltage;
var $Number;
var $Duration;
function H($Voltage=0,$Number=0,$Duration=0)
{
$this->Voltage = $Voltage;
$this->Number = $Number;
$this->Duration = $Duration;
}}
//This will be the crossover function. Is just the average of all properties.
function avg($a,$b) {
return round(($a*2+$b*2)/2);
}
//This will be the mutation function. Just increments the property.
function inc($x)
{
return $x+1*2;
}
//This will be the fitness function. Is just the sum of all properties.
function debug($x)
{
echo "<pre style='border: 1px solid black'>";
print_r($x);
echo '</pre>';
}
//This will be the fitness function. Is just the sum of all properties.
function total($obj)
{
return $obj->Voltage*(-2) + $obj->Number*2 + $obj->Duration*1;
}
$asma=array();
for($i=0;$i<$row_count;$i++)
{
$adam = new H($fa1[$i],$fb1[$i],$fcc1[$i]);
$eve = new H($fe1[$i],$ff1[$i],$fg1[$i]);
$eve1 = new H($fi1[$i],$fj1[$i],$fk1[$i]);
$ga = new GA();
echo "Input";
$ga->population = array($adam,$eve,$eve1);
debug($ga->population);
$ga->fitness_function = 'total'; //Uses the 'total' function as fitness function
$ga->num_couples = 5; //4 couples per generation (when possible)
$ga->death_rate = 0; //No kills per generation
$ga->generations = 10; //Executes 100 generations
$ga->crossover_functions = 'avg'; //Uses the 'avg' function as crossover function
$ga->mutation_function = 'inc'; //Uses the 'inc' function as mutation function
$ga->mutation_rate = 20; //10% mutation rate
$ga->evolve(); //Run
echo "BEST SELECTED POPULATION";
debug(GA::select($ga->population,'total',3)); //The best
$array=array((GA::select($ga->population,'total',3))); //The best }
?>
<?php
$comma_separated = implode(",", $array);
echo $comma_separated; // lastname,email,phone
?
>
i apply implode function but its not working
it display the error of : Array to string conversion in C:\wamp\www\EMS3\ge.php on line 146 at line $r=implode($rt,",");
<script>
if ( ($textboxB.val)==31.41)
{
</script>
<?php echo "as,dll;g;h;'islamabad"; ?>
<script>} </script>
You are running your java script code in PHP, I havent implemented your code just checked and found this bug.You can get the value by submitting the form also
---------------------------- Answer For your Second updated question------------------------
<?php
$array = array(
"name" => "John",
"surname" => "Doe",
"email" => "j.doe#intelligence.gov"
);
$comma_separated = implode(",", $array); // You can implode them with any character like i did with ,
echo $comma_separated; // lastname,email,phone
?>
I'm really stumped on how Twitter expects users of its API to convert the plaintext tweets it sends to properly linked HTML.
Here's the deal: Twitter's JSON API sends this set of information back when you request the detailed data for a tweet:
{
"created_at":"Wed Jul 18 01:03:31 +0000 2012",
"id":225395341250412544,
"id_str":"225395341250412544",
"text":"This is a test tweet. #boring #nbc http://t.co/LUfDreY6 #skronk #crux http://t.co/VpuMlaDs #twitter",
"source":"web",
"truncated":false,
"in_reply_to_status_id":null,
"in_reply_to_status_id_str":null,
"in_reply_to_user_id":null,
"in_reply_to_user_id_str":null,
"in_reply_to_screen_name":null,
"user": <REDACTED>,
"geo":null,
"coordinates":null,
"place":null,
"contributors":null,
"retweet_count":0,
"entities":{
"hashtags":[
{
"text":"boring",
"indices":[22,29]
},
{
"text":"skronk",
"indices":[56,63]
}
],
"urls":[
{
"url":"http://t.co/LUfDreY6",
"expanded_url":"http://www.twitter.com",
"display_url":"twitter.com",
"indices":[35,55]
},
{
"url":"http://t.co/VpuMlaDs",
"expanded_url":"http://www.example.com",
"display_url":"example.com",
"indices":[70,90]
}
],
"user_mentions":[
{
"screen_name":"nbc",
"name":"NBC",
"id":26585095,
"id_str":"26585095",
"indices":[30,34]
},
{
"screen_name":"crux",
"name":"Z. D. Smith",
"id":407213,
"id_str":"407213",
"indices":[64,69]
},
{
"screen_name":"twitter",
"name":"Twitter",
"id":783214,
"id_str":"783214",
"indices":[91,99]
}
]
},
"favorited":false,
"retweeted":false,
"possibly_sensitive":false
}
The interesting parts, for this question, are the text element and the entries in the hashtags, user_mentions, and urls arrays. Twitter is telling us where in the text element the hastags, mentions, and urls appear with the indices arrays... so here's the crux of the question:
How do you use those indices arrays?
You can't just use them straight up by looping over each link element with something like substr_replace, since replacing the first link element in the text will invalidate all the index values for subsequent link elements. You also can't use substr_replace's array functionality, since it only works when you give it an array of strings for the first arg, rather than a single string (I've tested this. The results are... strange).
Is there some function that can simultaneously replace multiple index-delimited substrings in a single string with different replacement strings?
All you have to do to use the indices twitter provides straight up with a simple replace is collect the replacements you want to make and then sort them backwards. You can probably find a more clever way to build $entities, I wanted them optional anyway, so I KISS as far as that went.
Either way, my point here was just to show that you don't need to explode the string and character count and whatnot. Regardless of how you do it, all you need to to is start at the end and work to the beginning of the string, and the index twitter has is still valid.
<?php
function json_tweet_text_to_HTML($tweet, $links=true, $users=true, $hashtags=true)
{
$return = $tweet->text;
$entities = array();
if($links && is_array($tweet->entities->urls))
{
foreach($tweet->entities->urls as $e)
{
$temp["start"] = $e->indices[0];
$temp["end"] = $e->indices[1];
$temp["replacement"] = "<a href='".$e->expanded_url."' target='_blank'>".$e->display_url."</a>";
$entities[] = $temp;
}
}
if($users && is_array($tweet->entities->user_mentions))
{
foreach($tweet->entities->user_mentions as $e)
{
$temp["start"] = $e->indices[0];
$temp["end"] = $e->indices[1];
$temp["replacement"] = "<a href='https://twitter.com/".$e->screen_name."' target='_blank'>#".$e->screen_name."</a>";
$entities[] = $temp;
}
}
if($hashtags && is_array($tweet->entities->hashtags))
{
foreach($tweet->entities->hashtags as $e)
{
$temp["start"] = $e->indices[0];
$temp["end"] = $e->indices[1];
$temp["replacement"] = "<a href='https://twitter.com/hashtag/".$e->text."?src=hash' target='_blank'>#".$e->text."</a>";
$entities[] = $temp;
}
}
usort($entities, function($a,$b){return($b["start"]-$a["start"]);});
foreach($entities as $item)
{
$return = substr_replace($return, $item["replacement"], $item["start"], $item["end"] - $item["start"]);
}
return($return);
}
?>
Ok so I needed to do exactly this and I solved it. Here is the function I wrote. https://gist.github.com/3337428
function parse_message( &$tweet ) {
if ( !empty($tweet['entities']) ) {
$replace_index = array();
$append = array();
$text = $tweet['text'];
foreach ($tweet['entities'] as $area => $items) {
$prefix = false;
$display = false;
switch ( $area ) {
case 'hashtags':
$find = 'text';
$prefix = '#';
$url = 'https://twitter.com/search/?src=hash&q=%23';
break;
case 'user_mentions':
$find = 'screen_name';
$prefix = '#';
$url = 'https://twitter.com/';
break;
case 'media':
$display = 'media_url_https';
$href = 'media_url_https';
$size = 'small';
break;
case 'urls':
$find = 'url';
$display = 'display_url';
$url = "expanded_url";
break;
default: break;
}
foreach ($items as $item) {
if ( $area == 'media' ) {
// We can display images at the end of the tweet but sizing needs to added all the way to the top.
// $append[$item->$display] = "<img src=\"{$item->$href}:$size\" />";
}else{
$msg = $display ? $prefix.$item->$display : $prefix.$item->$find;
$replace = $prefix.$item->$find;
$href = isset($item->$url) ? $item->$url : $url;
if (!(strpos($href, 'http') === 0)) $href = "http://".$href;
if ( $prefix ) $href .= $item->$find;
$with = "$msg";
$replace_index[$replace] = $with;
}
}
}
foreach ($replace_index as $replace => $with) $tweet['text'] = str_replace($replace,$with,$tweet['text']);
foreach ($append as $add) $tweet['text'] .= $add;
}
}
It's an edge case but the use of str_replace() in Styledev's answer could cause issues if one entity is contained within another. For example, "I'm a genius! #me #mensa" could become "I'm a genius! #me #mensa" if the shorter entity is substituted first.
This solution avoids that problem:
<?php
/**
* Hyperlinks hashtags, twitter names, and urls within the text of a tweet
*
* #param object $apiResponseTweetObject A json_decoded() one of these: https://dev.twitter.com/docs/platform-objects/tweets
* #return string The tweet's text with hyperlinks added
*/
function linkEntitiesWithinText($apiResponseTweetObject) {
// Convert tweet text to array of one-character strings
// $characters = str_split($apiResponseTweetObject->text);
$characters = preg_split('//u', $apiResponseTweetObject->text, null, PREG_SPLIT_NO_EMPTY);
// Insert starting and closing link tags at indices...
// ... for #user_mentions
foreach ($apiResponseTweetObject->entities->user_mentions as $entity) {
$link = "https://twitter.com/" . $entity->screen_name;
$characters[$entity->indices[0]] = "<a href=\"$link\">" . $characters[$entity->indices[0]];
$characters[$entity->indices[1] - 1] .= "</a>";
}
// ... for #hashtags
foreach ($apiResponseTweetObject->entities->hashtags as $entity) {
$link = "https://twitter.com/search?q=%23" . $entity->text;
$characters[$entity->indices[0]] = "<a href=\"$link\">" . $characters[$entity->indices[0]];
$characters[$entity->indices[1] - 1] .= "</a>";
}
// ... for http://urls
foreach ($apiResponseTweetObject->entities->urls as $entity) {
$link = $entity->expanded_url;
$characters[$entity->indices[0]] = "<a href=\"$link\">" . $characters[$entity->indices[0]];
$characters[$entity->indices[1] - 1] .= "</a>";
}
// ... for media
foreach ($apiResponseTweetObject->entities->media as $entity) {
$link = $entity->expanded_url;
$characters[$entity->indices[0]] = "<a href=\"$link\">" . $characters[$entity->indices[0]];
$characters[$entity->indices[1] - 1] .= "</a>";
}
// Convert array back to string
return implode('', $characters);
}
?>
Jeff's solution worked well with English text but it got broken when the tweet contained non-ASCII characters. This solution avoids that problem:
mb_internal_encoding("UTF-8");
// Return hyperlinked tweet text from json_decoded status object:
function MakeStatusLinks($status)
{$TextLength=mb_strlen($status['text']); // Number of UTF-8 characters in plain tweet.
for ($i=0;$i<$TextLength;$i++)
{$ch=mb_substr($status['text'],$i,1); if ($ch<>"\n") $ChAr[]=$ch; else $ChAr[]="\n<br/>"; // Keep new lines in HTML tweet.
}
if (isset($status['entities']['user_mentions']))
foreach ($status['entities']['user_mentions'] as $entity)
{$ChAr[$entity['indices'][0]] = "<a href='https://twitter.com/".$entity['screen_name']."'>".$ChAr[$entity['indices'][0]];
$ChAr[$entity['indices'][1]-1].="</a>";
}
if (isset($status['entities']['hashtags']))
foreach ($status['entities']['hashtags'] as $entity)
{$ChAr[$entity['indices'][0]] = "<a href='https://twitter.com/search?q=%23".$entity['text']."'>".$ChAr[$entity['indices'][0]];
$ChAr[$entity['indices'][1]-1] .= "</a>";
}
if (isset($status['entities']['urls']))
foreach ($status['entities']['urls'] as $entity)
{$ChAr[$entity['indices'][0]] = "<a href='".$entity['expanded_url']."'>".$entity['display_url']."</a>";
for ($i=$entity['indices'][0]+1;$i<$entity['indices'][1];$i++) $ChAr[$i]='';
}
if (isset($status['entities']['media']))
foreach ($status['entities']['media'] as $entity)
{$ChAr[$entity['indices'][0]] = "<a href='".$entity['expanded_url']."'>".$entity['display_url']."</a>";
for ($i=$entity['indices'][0]+1;$i<$entity['indices'][1];$i++) $ChAr[$i]='';
}
return implode('', $ChAr); // HTML tweet.
}
Here is an updated answer that works with Twitter's new Extended Mode. It combines the answer by #vita10gy and the comment by #Hugo (to make it utf8 compatible), with a few minor tweaks to work with the new api values.
function utf8_substr_replace($original, $replacement, $position, $length) {
$startString = mb_substr($original, 0, $position, "UTF-8");
$endString = mb_substr($original, $position + $length, mb_strlen($original), "UTF-8");
$out = $startString . $replacement . $endString;
return $out;
}
function json_tweet_text_to_HTML($tweet, $links=true, $users=true, $hashtags=true) {
// Media urls can show up on the end of the full_text tweet, but twitter doesn't index that url.
// The display_text_range indexes show the actual tweet text length.
// Cut the string off at the end to get rid of this unindexed url.
$return = mb_substr($tweet->full_text, $tweet->display_text_range[0],$tweet->display_text_range[1]);
$entities = array();
if($links && is_array($tweet->entities->urls))
{
foreach($tweet->entities->urls as $e)
{
$temp["start"] = $e->indices[0];
$temp["end"] = $e->indices[1];
$temp["replacement"] = " <a href='".$e->expanded_url."' target='_blank'>".$e->display_url."</a>";
$entities[] = $temp;
}
}
if($users && is_array($tweet->entities->user_mentions))
{
foreach($tweet->entities->user_mentions as $e)
{
$temp["start"] = $e->indices[0];
$temp["end"] = $e->indices[1];
$temp["replacement"] = " <a href='https://twitter.com/".$e->screen_name."' target='_blank'>#".$e->screen_name."</a>";
$entities[] = $temp;
}
}
if($hashtags && is_array($tweet->entities->hashtags))
{
foreach($tweet->entities->hashtags as $e)
{
$temp["start"] = $e->indices[0];
$temp["end"] = $e->indices[1];
$temp["replacement"] = " <a href='https://twitter.com/hashtag/".$e->text."?src=hash' target='_blank'>#".$e->text."</a>";
$entities[] = $temp;
}
}
usort($entities, function($a,$b){return($b["start"]-$a["start"]);});
foreach($entities as $item)
{
$return = utf8_substr_replace($return, $item["replacement"], $item["start"], $item["end"] - $item["start"]);
}
return($return);
}
Here is a JavaScript version (using jQuery) of vita10gy's solution
function tweetTextToHtml(tweet, links, users, hashtags) {
if (typeof(links)==='undefined') { links = true; }
if (typeof(users)==='undefined') { users = true; }
if (typeof(hashtags)==='undefined') { hashtags = true; }
var returnStr = tweet.text;
var entitiesArray = [];
if(links && tweet.entities.urls.length > 0) {
jQuery.each(tweet.entities.urls, function() {
var temp1 = {};
temp1.start = this.indices[0];
temp1.end = this.indices[1];
temp1.replacement = '' + this.display_url + '';
entitiesArray.push(temp1);
});
}
if(users && tweet.entities.user_mentions.length > 0) {
jQuery.each(tweet.entities.user_mentions, function() {
var temp2 = {};
temp2.start = this.indices[0];
temp2.end = this.indices[1];
temp2.replacement = '#' + this.screen_name + '';
entitiesArray.push(temp2);
});
}
if(hashtags && tweet.entities.hashtags.length > 0) {
jQuery.each(tweet.entities.hashtags, function() {
var temp3 = {};
temp3.start = this.indices[0];
temp3.end = this.indices[1];
temp3.replacement = '#' + this.text + '';
entitiesArray.push(temp3);
});
}
entitiesArray.sort(function(a, b) {return b.start - a.start;});
jQuery.each(entitiesArray, function() {
returnStr = substrReplace(returnStr, this.replacement, this.start, this.end - this.start);
});
return returnStr;
}
You can then use this function like so ...
for(var i in tweetsJsonObj) {
var tweet = tweetsJsonObj[i];
var htmlTweetText = tweetTextToHtml(tweet);
// Do something with the formatted tweet here ...
}
Regarding vita10gy's helpful json_tweet_text_to_HTML(), I found a tweet that it could not format correctly: 626125868247552000.
This tweet has a nonbreaking space in it. My solution was to replace the first line of the function with the following:
$return = str_replace("\xC2\xA0", ' ', $tweet->text);
Performing a str_replace() on is covered here.
I am using an array which allows me to prevent particular subfolders from being included in a script that generates an audio playlist. It looks like this:
$disallowed=array('..', '.', 'simon', 'steve');
I would like to find a way of disallowing any folders which are empty, or, perhaps, better still, disallowing folders that do not have any MP3s in them (in case the script interprets folders as not empty if they contain system files which are automatically generated).
I included the full script below so that you can see how the disallowed array fits in.
Would someone be able to help with this?
Thanks,
Nick
<?php
/*
* =====================
* FUNctions
* =====================
*/
/*
* array subvalue sort -- from: http://www.firsttube.com/read/sorting-a-multi-dimensional-array-with-php/
*
* this function lets me sort the multidimensional array containing song/artist information by the file modified time, a subvalue
*/
function subval_sort($a,$subkey) {
foreach($a as $k=>$v) {
$b[$k] = strtolower($v[$subkey]);
}
arsort($b); //change this to 'asort' for ascending
foreach($b as $key=>$val) {
$c[] = $a[$key];
}
return $c;
}
/*
* function written to clean up my messy code (too many slashes ... display '/' as '»' (>>) for user friendliness )
*/
function clean($dirty, $type='general', $debug=false){
//debug
if($debug==true)
echo'<br />value before clean: '.$dirty.' (first character: '.substr($dirty, 0, 1).')';
/*
* General cleaning -- remove '/' at front and end
*/
if(substr($dirty, 0, 1)=='/'){
//echo'<br />found leading /';
$dirty=substr($dirty, 1, strlen($dirty)-1);
}
if(substr($dirty, -1)=='/')
$dirty=substr($dirty, 0, strlen($dirty)-1);
//prepare the subfolder display information by type
if($type=='link')
$dirty=str_replace(array('//','»'), array('/', '/'), $dirty);
else if($type=='display')
$dirty=str_replace(array('///','//','/'), array('»','»', '»'), $dirty);
else
$dirty=str_replace('»', '/', $dirty);
if($debug==true)echo' | after clean: '.$dirty;
//return
return $dirty;
}
function makelink($linkme, $debug=false){
$link=str_replace('»', '/', $linkme);
$link=str_replace('//', '/', $link);
return $link;
}
function recursiveGetSongs($directory, $fileinfo, $useID3, $getID3, $parent=null, $debug, $filtered=null){
/*
* configure function here:
*
* _usage_
* > the disallowed array should include any folders or files you explicitely don't want displayed
* > the allowedfiletypes array should include any file extentions you want to play
*/
$disallowed=array('..', '.', 'simon', 'steve');
$allowedfiletypes=array('mp3');
if($filtered!=null){
$disallowed=array_merge((array)$filtered, (array)$disallowed);
}
//simple error fix
if($directory=='./')
$directory='.';
//debug
if ($debug==true)echo'Dir to open: '.$directory;
//open directory
$dir = opendir($directory);
while ($read = readdir($dir))
{
//if ( !in_array($read, $disallowed) AND ( $filter!=null AND in_array($read, $filter) ) )
if ( !in_array($read, $disallowed) )
{
if($debug==true)echo $read.'<br />';
//if is not dir, handle file
if ( !is_dir($directory.'/'.$read) ){
if($debug==true)echo '^^ not dir | dir: '.$directory.'<br />';
if( in_array(substr($read, -3, 3), $allowedfiletypes) ){
if($useID3==TRUE){
//store id3 info
$FullFileName = realpath($directory.'/'.$read);
if($debug==TRUE)echo'<br />FFN » '.$FullFileName;
$ThisFileInfo = $getID3->analyze($FullFileName);
getid3_lib::CopyTagsToComments($ThisFileInfo);
$fileinfo[$read]['artist']=$ThisFileInfo['comments_html']['artist'][0];
$fileinfo[$read]['title']=$ThisFileInfo['comments_html']['title'][0];
$fileinfo[$read]['album']=$ThisFileInfo['comments_html']['album'][0];
$fileinfo[$read]['filename']=$ThisFileInfo['filename'];
$fileinfo[$read]['modified']=date ("YmdHis", filemtime($directory.'/'.$read));
if($debug==true)
echo "<br />$read was last modified: " . date ("YmdHis", filemtime($directory.'/'.$read));
$fileinfo[$read]['path']=$directory.'/'.$read;
if($debug==true)echo'<span style="margin-left: 10px;">path:'.$fileinfo[$read]['path'].' > fn: '.$fileinfo[$read]['filename'].'</span><br /><br />';
if($parent!=null)
$fileinfo[$read]['from']=str_replace(array('./', '//', '/'), array('', '»', '»'), $directory); // was =$parent
else
$fileinfo[$read]['from']='root'; //testing this
if($debug==true)echo'<br />'.$fileinfo[$read]['from'].'<br />';
//debug
//echo$ThisFileInfo['filename'].'<br />';
}
else{
//store filename
$fileinfo[$fileinfo['count']]['path']=$directory.'/'.$read;
$fileinfo[$fileinfo['count']]['fn']=$read;
if($parent!=null)
$fileinfo[$fileinfo['count']]['from']=str_replace(array('./', '//', '/'), array('', '»', '»'), $directory);
$fileinfo[$fileinfo['count']]['modified']=date ("YmdHis", filemtime($directory.'/'.$read));
//$fileinfo[$fileinfo['count']]=date ("YmdHis", filemtime($directory.'/'.$read));
}
//inc counter
$fileinfo['count']=$fileinfo['count']+1; // had ++ and it didn't work
}
else
;//do nothing
}
//else, must be a folder (as determined above), recurse folder
else{
//debug
if($debug==true)echo '^^ DIR<br />';
//capture subfolders in case they are needed
if($parent!='')$fileinfo['folders'].=$parent.'»'.$read.'|';
else $fileinfo['folders'].=$read.'|';
$fileinfo['folderpaths'].=$directory.'/|';
$fileinfo=recursiveGetSongs($directory.'/'.$read, $fileinfo, $useID3, $getID3, $parent.'/'.$read, $debug, $filtered);
}
}
}
closedir($dir);
return $fileinfo;
}
?>
Use glob('somefolder/*.mp3') to test if there are mp3s in a folder. The function will return an array, so if it's empty the folder does not contain any mp3 files.
You could try glob() since it's flexible and a little bit more secure than building up a 'valid array'.
foreach (glob($dir . "/*.mp3") as $filename) {
echo "$filename size " . filesize($filename) . "\n";
}
I am currently building PHP class that generates web form for flexibility and localization issues. I am having difficulty assigning a key and value for dropdown input; for some reason the foreach seems does not get array variable ($country_list). Here is my code that I am having difficulty.
require_once('_include/country_list.php');
//drop down form class
class DropDown
{
function __construct ($form, $field_label, $field_name, $field_desc, $dropdown_data, $locale){
$this->form = $form;
$this->field_label = $field_label;
$this->field_name = $field_name;
$this->filed_desc = $filed_desc;
$this->dropdown_data = $dropdown_data;
$this->locale = $locale;
}
function getNotRequiredData(){
global $notReqArry; //http://stackoverflow.com/questions/1415577/accessing-variables-and-methods-outside-of-class-definitions
return $notReqArry[$this->locale];
}
function getValue(){
return $_POST[$this->field_name];
}
function dropdown(){
$selecedVal = $this->getValue();
$select_start = "<select name=\"$this->field_name\"><option value=\"\">$this->filed_desc</option>";
foreach ($this->dropdown_data as $key=>$value){
$selected = ($key == $selecedVal ? 'selected' : '');
$options = sprintf('<option value="%s" %s >%s</option>',$key,$selected,$value);
print $options;
}
$select_end = "</select>";
return $select_start . $options . $select_end;
}
function getLabel(){
$non_req = $this->getNotRequiredData();
$req = in_array($this->field_name, $non_req) ? '' : '*';
return $this->field_label ? $req . $this->field_label : '';
}
function __toString(){
$label = $this->getLabel();
$field = $this->dropdown();
return $label.$field;
}
}
function generateForm ($lang,$country_list){
switch($lang)
{
case 'en-US':
//create EN web form
echo $textField = new TextField($form, 'Note', 'form_note', '2', '20', '250', 'en-US');
//echo $textField_js = new JsTextField($textField, 'onkeyup', 'return checklength(this,contact_max_warning)', 'Characters typed:');
echo $dropDown = new DropDown ($form, 'Country', 'form_country', '--Select Country--', $country_list, 'en-US');
break;
case 'fr-FR':
//create FR web form
break;
case 'de-DE':
//create DE web form
break;
case 'ja-JP':
//create JA web form
break;
default:
//create default web form
print('foooo');
};
}
<form id="frm_verification" action="<?=$_SERVER['SCRIPT_NAME']?>" method="POST">
<?
$lang='en-US';
echo generateForm ($lang,$country_list);
?>
<p>
<input type="button" name="reset" value="Reset">
<input type="submit" name="submit" value="Submit">
</p>
</form>
and the array from country_list.php is like:
$country_main = array (
//Main 5 countries at first
//Those 5 countries are direct market of my previous company. you can modify whatever you want.
"US" => "United States",
"UK" => "United Kingdom",
"DE" => "Germany",
"FR" => "France",
"JP" => "Japan",
);
It does not work currently and I have check the error log, and it says:
"PHP Fatal error: Cannot access empty property in..."
I am sure that this is from my misunderstanding of class and its var or foreach statement.
Please help me to fix this; I really need help. (Yeah, I am really new on PHP)
foreach ($this->dropdown_data as $key->$value){
should read
foreach ($this->dropdown_data as $key=>$value){
Notice the change from -> to => in $key=>$value. Otherwise your code is trying to access property value in object key which is obviously invalid since key is not an object.
The variable you're referencing for your list of countries is misnamed:
echo generateForm ($lang,$country_list);
should be
echo generateForm ($lang,$country_main);
as that's how it's defined in country_list.php.