Situation
I have a relatively short php code I found and tweaked that includes a random html file from my 'randomizer' folder into my page.
Here is the code
<?php
error_reporting(0);
function random_file($string){
return ((file_exists($string))&&(preg_match('#(\.html)$#i',$string))) ? true : false ;
}
define('OUTPUT_TYPE','text');
define('RANDOM_FILES_FOLDER','randomizer/');
$my_array = Array();
$my_dir = RANDOM_FILES_FOLDER ;
if ($dir = #opendir("$my_dir")) {
while (($file = readdir($dir)) !== false) {
if ($file != "." && $file != ".." && !is_dir($my_dir.$file))
{
switch(OUTPUT_TYPE):
case'text':
if(random_file($my_dir.$file)){
$my_array[] = $file;
}
break;
default:
break;
endswitch;
}
}
closedir($dir);
}
if(count($my_array)>0){
$random_number = rand(0, count($my_array)-1);
$random_file = $my_array[$random_number];
switch(OUTPUT_TYPE):
case'text':
include($my_dir.$random_file);
break;
default:
break;
endswitch;
}
?>
Question
It does what it is supposed to do (perhaps someone can trim/optimize that code for me) but I have only a few files to randomize and I don't want the same file to appear twice when I refresh or open the page a day after.
I think cookies may be the answer, but not sure how to do anything with them.
Can anyone write a piece code to add to mine to do that or provide a code that has all those attributes? keep in mind it must include files at random from a folder, I don't want the code from those files on my actual page code for CMS purposes
Keep in mind I am a PHP and Javascript beginner with VERY basic knowledge, so please dumb it down for me.
Thanks!
Very rough:
session_start();
$dir = 'randomizer/';
if (empty($_SESSION['files'])) {
$_SESSION['files'] = array_filter(scandir($dir), function ($file) use ($dir) {
return is_file($dir . $file) && preg_match('#(\.html)$#i', $file);
});
shuffle($_SESSION['files']);
}
include $dir . array_shift($_SESSION['files']);
Keep a list of all candidate files in the session, use them one by one. That way all files will be displayed once in random order before the cycle starts again. Only not recommended if the list is very long.
It's worth noting that the array_filter callback syntax requires PHP 5.3.
This is not the perfect way to do this but it will work(intentionally simple):
Include this after the line $random_file = $my_array[$random_number];
$oldFile = ''
if(!empty($_COOKIE['oldfilename']) {
$oldFile = $_COOKIE['oldfilename'];
}
while ($oldFile == $random_file) {
$random_number = rand(0, count($my_array)-1);
$random_file = $my_array[$random_number];
}
setcookie('oldfilename', $random_file);
Related
I want to grab the first file in a directory, without touching/grabbing all the other files. The filename is unknown.
One very short way could be this, using glob:
$file = array_slice(glob('/directory/*.jpg'), 0, 1);
But if there are a lot of files in that directory, there will be some overhead.
Other ways are answers to this question - but all involve a loop and are also longer then the glob example:
PHP: How can I grab a single file from a directory without scanning entire directory?
Is there a very short and efficient way to solve this?
Probably not totally efficient, but if you only want the FIRST jpg that appears, then
$dh = opendir('directory/');
while($filename = readdir($dh)) {
if (substr($filename, -4) == '.jpg')) {
break;
}
}
Well this is not totally a one-liner, but it is a way to go I believe:
$result = null;
foreach(new FilesystemIterator('directory/') as $file)
{
if($file->isFile() && $file->getExtension() == 'jpg') {
$result = $file->getPathname();
break;
}
}
but why don't you wrap it in a function and use it like get_first_file('directory/') ? It will be a nice and short!
This function will get the first filename of any type.
function get_first_filename ($dir) {
$d = dir($dir);
while ($f = $d->read()){
if (is_file($dir . '/' . $f)) {
$d->close();
return $f;
}
}
}
I've been working with a handy php script, which scans my site and spits out links to all the pages it finds. The problem is, it's also scanning my 'includes' folder, which contains all my .inc.php files.
Obviously I'd rather it ignore this folder when scanning the site, but for the life of me I can't see how to edit the script to tell it to do so.
The script is:
<?php
// starting directory. Dot means current directory
$basedir = ".";
// function to count depth of directory
function getdepth($fn){
return (($p = strpos($fn, "/")) === false) ? 0 : (1 + getdepth(substr($fn, $p+1)));
}
// function to print a line of html for the indented hyperlink
function printlink($fn){
$indent = getdepth($fn); // get indent value
echo "<li class=\"$indent\"><a href=\"$fn\">"; //print url
$handle = fopen($fn, "r"); //open web page file
$filestr = fread($handle, 1024); //read top part of html
fclose($handle); //clos web page file
if (preg_match("/<title>.+<\/title>/i",$filestr,$title)) { //get page title
echo substr($title[0], 7, strpos($title[0], '/')-8); //print title
} else {
echo "No title";
}
echo "</a></li><br>\n"; //finish html
}
// main function that scans the directory tree for web pages
function listdir($basedir){
if ($handle = #opendir($basedir)) {
while (false !== ($fn = readdir($handle))){
if ($fn != '.' && $fn != '..'){ // ignore these
$dir = $basedir."/".$fn;
if (is_dir($dir)){
listdir($dir); // recursive call to this function
} else { //only consider .html etc. files
if (preg_match("/[^.\/].+\.(htm|html|php)$/",$dir,$fname)) {
printlink($fname[0]); //generate the html code
}
}
}
}
closedir($handle);
}
}
// function call
listdir($basedir); //this line starts the ball rolling
?>
Now I can see where the script is told to ignore certain files, and I've tried appending:
&& $dir != 'includes'
...to it in numerous places, but my php knowledge is simply too shaky to know exactly how to integrate that code into the script.
If anyone can help, then you'd be saving me an awfully large headache. Cheers.
Add it this line:
if ($fn != '.' && $fn != '..'){ // ignore these
so it's
if ($fn != '.' && $fn != '..' && $fn != 'includes'){ // ignore these
Your path needs to be absolute. Add it at the top of listdir
function listdir($basedir){
if($basedir == '/path/to/includes') {
return;
} [...]
This also makes sure that only one includes folder will be ignored.
<?
$dir=scandir('/home/crusty/www/crusty.bshellz.pl/htdocs/404/');
foreach($dir as $file){
if($file!='.' && $file!='..' && $file!='index.php'){
$choice=$dir[rand(0, count($dir) - 1)];
include($choice);
}
}
?>
I have a little problem with that code. Of course it is working on some files but it is still trying to include index.php, .. and .
Can sameone help me with solving it?
Split Your code into two parts: first one to prepare array of good files; second to include random file:
$allfiles = scandir('/home/crusty/www/crusty.bshellz.pl/htdocs/404/');
$goodfiles = array();
foreach ($allfiles as $file) {
if($file!='.' && $file!='..' && $file!='index.php'){
$goodfiles[] = $file;
}
}
$choicenfile = $goodfiles[rand(0, count($goodfiles) - 1)];
// As I understant You want to include only one file, not all;
include($choicenfile);
Now You can even extract this code to methods or functions
You have to supply the full path, your trying to include the file from the location of the script.
Change this:
include($choice);
to:
include('/home/crusty/www/crusty.bshellz.pl/htdocs/404/'.$choice);
I wouldn't do it this way, but it should work.
I'm not shure if you want to include all files in randomized order or just one random file of the given folder, so I have included both in the solution - just delete what you don't need:
function filter_includes($incfile) {
return !in_array($incfile, array(".", "..", "index.php"));
}
$dirPath = '/home/crusty/www/crusty.bshellz.pl/htdocs/404/';
$dir = array_filter(scandir($dirPath), "filter_includes");
// include all files in randomized order
shuffle($dir);
foreach($dir as $file) {
include($dirPath . $file);
}
// include one random file
include($dirPath . $dir[rand(0, count($dir) - 1)]);
What is the point of the rand in $choice=$dir[rand(0, count($dir) - 1)];?
Because right now it's just including a random file in your array.
You should change your code to something like:
$dir=scandir('/home/crusty/www/crusty.bshellz.pl/htdocs/404/');
foreach($dir as $file){
if($file!='.' && $file!='..' && $file!='index.php'){
include($file);
}
}
Is there a better way to check if a dir is empty than parsing it?
Don't think so. Shortest/quickest way I can think of is the following, which should work as far as I can see.
function dir_is_empty($path)
{
$empty = true;
$dir = opendir($path);
while($file = readdir($dir))
{
if($file != '.' && $file != '..')
{
$empty = false;
break;
}
}
closedir($dir);
return $empty;
}
This should only go through a maximum of 3 files. The two . and .. and potentially whatever comes next. If something comes next, it's not empty, and if not, well then it's empty.
Not really, but you can try to delete it. If it fails, its not empty (or you just can't delete it ;))
function dirIsEmpty ($dir) {
return rmdir($dir) && mkdir($dir);
}
Update:
It seems, that the answer, that takes the condition "without parsing" into account, doesn't find much friends ;)
function dirIsEmpty ($dir) {
return count(glob("$dir/**/*")) === 0:
}
Note, that this assumes, that the directory and every subdirectory doesn't contain any hidden file (starting with a single .).
The following code loads all .php files found in the specified folder (defined separately). Is there a way to put this into an array to simplify the code?
Only a couple of variables change but essentially the code repeats several times.
// The General Files
$the_general = opendir(FRAMEWORK_GENERAL);
while (($the_general_files = readdir($the_general)) !== false) {
if(strpos($the_general_files,'.php')) {
include_once(FRAMEWORK_GENERAL . $the_general_files);
}
}
closedir($the_general);
// The Plugin Files
$the_plugins = opendir(FRAMEWORK_PLUGINS);
while (($the_plugins_files = readdir($the_plugins)) !== false) {
if(strpos($the_plugins_files,'.php')) {
include_once(FRAMEWORK_PLUGINS . $the_plugins_files);
}
}
closedir($the_plugins);
There are several more sections which call different folders.
Any help is greatly appreciated.
Cheers,
James
I nicer way to do this would to use glob(). And make it into a function.
function includeAllInDirectory($directory)
{
if (!is_dir($directory)) {
return false;
}
// Make sure to add a trailing slash
$directory = rtrim($directory, '/\\') . '/';
foreach (glob("{$directory}*.php") as $filename) {
require_once($directory . $filename);
}
return true;
}
This is fairly simple. See arrays and foreach.
$dirs = array(FRAMEWORK_GENERAL, FRAMEWORK_PLUGINS, );
foreach ($dirs as $dir) {
$d = opendir($dir);
while (($file = readdir($d)) !== false) {
if(strpos($file,'.php')) {
include_once($dir . $file);
}
}
closedir($d);
}
A better idea might be lazy loading via __autoload or spl_autoload_register, including all the .php files in a directory might seem like a good idea now, but not when your codebase gets bigger.
Your code should be layed out in an easy to understand heirarchy, rather than putting them all in one directory so they can be included easily. Also, if you dont need all of the code in the files in every request you are wasting resources.
Check http://php.net/manual/en/language.oop5.autoload.php for an easy example.
This can be done pretty tightly:
$dirs = array(FRAMEWORK_GENERAL, FRAMEWORK_PLUGINS);
foreach($dirs as $dir) {
if (!is_dir($dir)) { continue; }
foreach (glob("$dir/*.php") as $filename) {
include($filename);
}
}
Put that in a function where $dirs comes in as a param and use freely.