Options for reading a remote directory - php

I have a script that displays images based on certain conditions. When none of the conditions are met, I want to randomly display one of the standard (backup) images. Those other images are on a remote server. I have read that you can't read a directory on a remote server, which makes sense.
Is my best bet to place a file into the remote server's image directory that outputs all of the image file names so I can parse it with the other server? Is there an easier way?
I prefer not to use FTP (http://php.net/manual/en/book.ftp.php).
What are my options for basically just getting the names of the images in that folder?
Thanks,
Ryan
UPDATE:
#mario's answer is lightweight and works like a charm. It is exactly the solution I thought I wanted, but after thinking about it some more, and reading that even #mario would do it differently, I decided to go with #bensiu's answer, because to me, control and security are more important than convenience. With #mario's method, it's very hard to know if the data you're getting is any good (lack of control) and you're exposing your directory / some server information (security). #bensiu's suggestion involves a second file (inconvenience), but provides the control and security I'm ultimately deciding to go with!
Thank you both!
-Ryan

I would prefer an exact and dedicated handler script like #bensiu pointed out.
But an alternative would be to read out a directory listing. A simple Apache generated mod_index listing would be sufficient for:
$html = file_get_contents("http://example.com/images/");
preg_match_all('/<a href="([-\w\d.]+\.(jpeg|png|gif))"/', $html, $uu);
$files = $uu[1];

I hope you at least have access to remote server...
You can place there script "A" that will do the job locally, return list of images in preffered format ( raw text, JSON, XML... ), and this script will be remotly called by curl form your server....
It also wise to make sure that when you call script "A" you at least passing some secret key to prevent unathorised access (not perfect solution but could be enought)

if you have PHP5 and the HTTP stream wrapper enabled on your server, it's very easy and simple to copy it to a local file:
copy('http://somedomain.com/file.jpeg', '/tmp/file.jpeg');
ome hosts disable copy() function then you can make your own -
<?php
function copyemz($file1,$file2){
$contentx =#file_get_contents($file1);
$openedfile = fopen($file2, "w");
fwrite($openedfile, $contentx);
fclose($openedfile);
if ($contentx === FALSE) {
$status=false;
}else $status=true;
return $status;
}
?>

Related

BIRT 4.5 - Disable saving connectstring inside report.rptdesign, PHP to assign connectstring

I am using Birt 4.5 and PHP/MYSQL.
I am able to run birt reports with php. I have enabled tomcat and copied 'birt-runtime-4_5_0/WebViewerExample' to tomcat/webapps and renamed it to birt.
So I can run birt viewer with php;
<?php
$fname = "report/test.rptdesign&__showtitle=false";
$dest = "http://localhost:8081/birt/frameset?__report=";
$dest .= $fname;
header("Location: $dest" );
?>
Above code is working fine. But report connectstring already saved in test.rptdesign file.
I want to remove DB login credentials from test.rptdesign file and assign it while report open with PHP.
I have tried with report parameters. But all the parameters will display on browser address-bar.
Is there any secure way to do this? This is very important when we need to change the database location. It is very hard to change the data source of each and every .rptdesign file.
Thank You,
Supun
I don't believe using report parameters to handle a database connection is the right way. In addition to the address-bar problem you mentionned, it will cause unexpected issues: for example you won't be able to use this database to feed the dataset of another report parameter.
With Tomcat the best approach is to externalize the database connection in a connection pool: easy, robust, and reports might run significantly faster.
Alternatively the datasource can be externalized in a BIRT library (.rptlibrary) and shared across all report-designs: thus only the library needs to be updated when the database location is changing.
I agree with Dominique that sending the database parameters via the query is most likely an inappropriate solution - and you've not given any explanation of whether this is a requirement of the system.
But it is quite trivial to proxy the request via PHP and decorate the URL with the required parameters, something like...
<?php
$_GET['__showtitle']=$_GET['__showtitle'] ? $_GET['__showtitle'] : 'false';
$_GET['__report']=$fname; // NB this should be NULL in your code!
$_GET['dbuser']='a_db_user';
$_GET['passwd']='s3cr3t';
$qry=http_build_query($_GET);
$url="http://localhost:8081/birt/frameset?" . $qry;
// if its simply returning HTML, then just....
$fin=fopen($url, 'r');
while ($l=fgets($fin)) {
print $l;
}
exit;
If the returned content contains relative links the you'll need to rewrite the output stream. If the content type is unusual or you want to project other headers (e.g. for caching) to the browser, then you'll need to use Curl, capture the headers and relay them.

Host file, log download, and host stats via PHP?

I'm a .NET guy attempting a PHP thing here, so am totally out of my comfort zone right now. What I THINK I want to do is to have 3 files:
download.php:
(a) contains a lookup of IDs to filenames (so download.php?file=11 querystring tells me I should host abc.zip)
(b) Some code to log this download to stats.log
(c) A couple header() calls and a readfile() call, similar to the answer to this question
stats.log: A simple log file that might look like the following example. This allows for logging to be accomplished by simply appending a line of text yet allows me to condense it from time to time.
abc.zip 1234
xyz.zip 4321
abc.zip 1
abc.zip 1
abc.zip 1
xyz.zip 1
abc.zip 1
stats.php: This is ultimately the PHP file that serves the stats. They can be real-time or near real-time, perhaps re-reading the file every minute and caching it or whatever. I don't really care and this won't be hit all that often but I do need to make sure that this isn't a stupidly expensive operation. This need not be a pretty page. Something so a human can easily read it is all that matters, so no fancy requirements there. For the above example of stats.log, I'd like this to serve something like the following:
abc.zip: 1238 downloads
xyz.zip: 4322 downloads
Ultimately, I don't want a database or any other systems involved in this. I only have FTP access to the server, so I can't really do much other than place scripts into the directory. I realize that I'll need to make sure that the script has write permissions to stats.txt, which is fine.
So my questions. I have a number of them but I believe they're all quite easy for somebody who knows PHP.
I think I have the hosting portion of download.php understood by setting headers and using readfile. However, how could I have a collection of key/value pairs representing file ids and filenames? If I were in .NET, I could do something like: var foo = new Dictionary<int, string> {{11, "abc.zip"}, {12, "xyz.zip"}} but I don't have a clue what this looks like in PHP.
How do I get querystrings? I need to pull from the URL "stuff/download.php?file=11" and take the 11 to grab my "abc.zip" out of my lookup collection.
How do I write the newline to my stats.log file?
How do I loop through my stats.log file in my stats.php script to count up and host these stats?
Bonus question: How do I cache the results from step 4 and only read the file once every minute/hour/or whatever?
I can probably fill in some gaps if somebody can answer at least most of these questions, but help sure would be appreciated! :)
1- You are looking for array e.g.
$files=array(11=>'abc.zip',
12=>'xyz.zip');
2- The Query String is accessed by the super global $_GET, so in your case $_GET['file'] holds that data you are interested in.
3,4,5
I would recommend storing the information JSON encoded. e.g.
$rawInfo=file_get_contents('stats.log');
$Info=json_decode($rawInfo,true);
if(isset($Info[$_GET['file']])){
$Info[$_GET['file']]++;
}else{
$Info[$_GET['file']]=1;
}
$rawInfo=json_encode($Info);
$h=fopen('stats.log','c');// If $h is false, you couldn't open the file
flock($h,LOCK_EX);// lock the file
$b=fwrite($h,$rawInfo);// if $b is not greater than 0, nothing was written
flock($h,LOCK_UN);
fclose($h);
//And then actually serve the file requested
This has the advantage of storing the information already in a useful format.
Whenever you fetch out the json_decodeed data, it is in the format of an array, which you will need to know how to handle.
stats.php might look something like this:
$rStats=file_get_contents('stats.log');
$Stats=json_decode($rStats,true);
foreach($Stats as $k=>$v){
echo $k.': '.$v.' download'.($v==1?'':'s');
}
I could have never done this so easily without #Shad's help in the accepted answer. As such, I wanted to post my final solution here that should work for practically anybody. This allows "direct links" (i.e. no 301/302 or other kinds of redirects) to function properly (right-click -> save as works too) while still logging downloads. NOTE that this is fairly "resource heavy" and some shared hosts may get upset with using something like this but as far as I can tell, this shouldn't really be a major drain. My files I'll be hosting are ~3-15MB and won't have a TON of downloads, so I'm not too worried about this in my scenario but if you use this solution, be very aware of this fact!
download.php:
<?php
$fileLookup = array(
0=>'subdir/test.zip',
1=>'another/sub/dir/test2.zip'
);
$currentRelativeFileName = $fileLookup[$_GET['file']];
$currentFileName = basename($currentRelativeFileName);
$rawInfo=file_get_contents('stats.log');
$Info=json_decode($rawInfo,true);
if(isset($Info[$currentFileName])){
$Info[$currentFileName]++;
}else{
$Info[$currentFileName]=1;
}
$rawInfo=json_encode($Info);
$h=fopen('stats.log','c');// If $h is false, you couldn't open the file
flock($h,LOCK_EX);// lock the file
$b=fwrite($h,$rawInfo);// if $b is not greater than 0, nothing was written
flock($h,LOCK_UN);
fclose($h);
header("Content-type: application/octet-stream");
header("Content-disposition: attachment; filename=" . $currentFileName);
readfile($currentRelativeFileName);
?>
stats.php:
<html>
<head>
<title>Here are my stats!</title>
</head>
<body>
<?php
$rStats=file_get_contents('stats.log');
if (strlen($rStats) > 5){
$Stats=json_decode($rStats,true);
foreach($Stats as $k=>$v){
echo $k.': '.$v.' download'.($v==1?'':'s') . '<br />';
}
}else{
echo 'No downloads';
}
?>
</body>

Creating a "two way" configuration file

I am writing a PHP application targeted at non-geeks, non-programmers. I need to create an option page with a bunch of "options" and then store those options...somewhere. Using a database application (MySQL/PostgreSQL/SQLite) is out of the question because it will require more configuration than the user needs to do (I don't want the user to do any kind of configuration if he doesn't want to). So the only solution left is to write the configuration to a configuration file. On the other hand, I also want that configuration file to be human-readable in case the user is a geek and he wants to edit the config file directly (or if he wants to edit the file remotely via SSH or any kind of reason...)
Here are the couple of potential solutions I found:
Using a JSON file...
...Retrieve the data from the file, using json_decode to convert the data, output it into HTML, retrieve any changes, encode back using json_encode, etc. You get the picture. There are a couple things that I don't like about this method, the main one being that the encoded JSON data using PHP will no be well formatted and very hard to edit without being reformatted beforehand.
Using an XML file
I won't describe that solution because I don't really like it either...and I don't know how to use XSLT and don't really want to learn...and because it's a pretty heavyweight solution, at least compared to the JSON solution. Correct me if I'm wrong.
Using an INI file
I love INI files, really I love them! I think they're really the most readable, and it's hard to mess up (ie: syntax errors). The problem with that solution is that there is no native way to write/edit an ini file. I found a topic showing a custom method to write one...that might be the solution I will adopt if I don't find anything better...
Using two files
That last solution seems as reasonable as the INI solution. In fact, I could use an INI file as "input" (the file that the user would edit if he wants to) and an XML/JSON file as output (the file that will be edited by PHP every time the user changes options using the web front-end). At this point, the best solution would be to ask the user to reload the configuration manually if he edited the config file directly, so that the "output" file is always up to date.
I know none of the solutions above are perfect, and that's why I created this topic to ask for advice. What is the best solution? Maybe (probably) I missed yet another solution.
One last thing: YAML isn't a valid solution because it's a lot easier to mess up the syntax if you're not used to it. PHP is not a solution either because editing PHP with PHP is a pain. PHP is only a good solution if I want to retrieve some configuration but not edit it directly via a web front-end.
ini
I'd write ini files, myself. As you said, the syntax is very simple, and that's what you want in a config file. The ini format's "key+value" pairing is exactly what you'd get with a database—without the database.
Related SO you may have seen already: create ini file, write values in PHP
Plus you can use parse_ini_file() to read it.
XML
XML isn't all that bad. It may be more work to write it (and may not be as clear to the user as an ini file), but reading it is really easy.
Create it:
<?php
// Create file
$xml = new SimpleXMLElement( '<?xml version="1.0" ?><config></config>' );
// Add stuff to it
$xml->addChild( 'option1' );
$xml->option1->addAttribute( 'first_name', 'billy' );
$xml->option1->addAttribute( 'middle_name', 'bob' );
$xml->option1->addAttribute( 'last_name', 'thornton' );
$xml->addChild( 'option2' );
$xml->option2->addAttribute( 'fav_dessert', 'cookies' );
// Save
$xml->asXML( 'config.xml' );
?>
Read it:
<?php
// Load
$config = new SimpleXMLElement( file_get_contents( 'config.xml' ) );
// Grab parts of option1
foreach( $config->option1->attributes() as $var )
{
echo $var.' ';
}
// Grab option2
echo 'likes '.$config->option2['fav_dessert'];
?>
Which gives you:
billy bob thornton likes cookies
Documentation for SimpleXML
SimpleXML Docs index
Basic Examples
Details on addChild() and addAttribute(), showing how to generate various XML structures (nested tags vs. attributes, for example)
I'd go with the ini. They're really not that hard to write. I personally hate XML. It's so bloated... even if the file size doesn't matter, it still makes me cringe at it's wordiness and the amount of typing I have to do. Plus, people are dumb. They won't close their tags.
The standard way would be XML files. They don't create that much overhead and are easily extensible. However, JSON files are the easiest on the programming end.
I'd rank my preference:
XML
JSON
ini (last resort)
Unless you have 1000+ options, you really shouldn't worry about the XML file size. The goal here is to keep things easy for the user. This means that whichever method you choose (JSON shouldn't be one of them in my opinion), it should be heavily documented at each config line.
Your two file solution brings me back to the days of sendmail config and makes me shudder.
I would just go with XML, it's self documenting to a point <Email>hi#hi.hi</Email>
Well, you could use PHP's serialize(), and although it is human readable, it isn't the most human readable thing there is. It's on the same level as JSON to implement.

Minimalist Wiki like script

I'm trying to find a simple wiki like script to setup a personal directory, browser favorites simply doesn't do anymore and i have lots of small files on my flash drive
Desired features
file upload
not bloated
works on a common webhost (aka php)
Thanks in advance
Google Sites functions as a Wiki, and you don't have to make your site public. You can upload files, but there's a 100MB limit for a site. The big advantage here (IMO) is the fact that you don't have to worry about setting up hosting at all.
It has a WYSIWYG editor, so that's a minus if you're really into some particular Wiki syntax, I guess.
Another (non-hosted) option would be TiddlyWiki, which is completely implemented in Javascript and meant to be used on a USB drive, primarily.
I recommend Dokuwiki. Uses text files instead of a database.
A couple of people have recommended Tiddlywiki. I've used it for a simple wiki setup... It's nice, fast, and easy.
You mentioned the need for web-access... Tiddlywiki combined with TiddlyHome is what I use for a simple, single-html-page wiki, which can be used both offline and online...
Another possibility is to use one of the new saving options listed at https://tiddlywiki.com/#Saving, including a Node.js or PHP server, or Google Drive/Dropbox/WebDAV (Sharepoint) integration.
I think this could help you find one that suits your needs: http://www.wikimatrix.org/
I found also this nice script: Hatta
Checkout LionWiki - http://lionwiki.0o.cz
It works from just one file and it's dead simple.
Here's one:
<?php function p($c){$r=preg_replace(array("~^ +([^\n]+)~m",'~^-\s+(.*)$~m',
"~-{4,}\r?\n~",'~(http(?:s)?)://([^\s]+)~i','~\n~'),array('<code>$1</code>','<li>$1',
'<hr>','<a href=$1://$2>$2</a>','<br>'),$c);preg_match_all('~([A-Z]\w+){2,}~',$r,
$x);foreach(array_unique($x[0])as$m){$r=str_replace($m,x($m)? "<a href=?$m>$m</a>":
"$m<a href=?e=$m>?</a>",$r);}return$r;}function f($f){#mkdir('wik');return
#file_get_contents("wik/$f.w");}function b($b){echo"<h1><a href=?$b>Backlinks $b"
."</a></h1><div id=c>";foreach(glob('wik/*.w')as$f){$f=substr($f,4,-2);if(strpos(
f($f),$b)!==false)echo"<a href=?$f>$f</a><br>";}echo"</div>";}function x($f){
return file_exists("wik/$f.w");}function e($p){$p=$p?$p:$_GET['e'];echo"<h1>Edit"
." $p</h1><form action='?$p' method=post><textarea name=c cols=50 rows=10>".f($p)
."</textarea><br><input type=submit value=Save>";}$p=preg_replace('~(e|b)=(.*)~'
,'',$_SERVER[QUERY_STRING]);$c=$_POST[c];$e=$_GET[e];$b=$_GET[b];if(!$p&&!$e)$p=
'MainPage';if($c){#file_put_contents("wik/$p.w",htmlspecialchars($c));header(
"Location: ?$p");}echo"<title>Wik Wiki</title>";if(!$e){if(!$b){echo x($p)?
"<h1><a href=?b=$p>$p</a></h1><div id=c>".p(f($p))."</div><hr><a href=?e=$p>"
."Edit</a> | <small>Modified: ".date('d.m.Y # H:i:s', #filemtime("wik/$p.w"))
:e($p);}else b($b);}else e($e);
It's from http://c2.com/cgi/wiki?WikWiki. It doesn't have file upload but you should be able to add it without bloating. I've used it as a private notepad and it works well, you just have to make sure the web server has write permissions on the wik directory

PHP variables maintaining an URL string

I'm not a PHP developer, but I'm currently hacking on an internal tool so my team can take advantage of its goodness. There's an index file that looks like so:
require( ($loader_path = "../../loaderapi/") . "loader.php" );
Used like this, $loader_path will retain its value within the loader.php file.
However, we want to access this API from our team's server like so:
require( ($loader_path = "http://remoteservername/loaderapi/") . "loader.php" );
In this case the $loader_path variable doesn't retain its value. I'm guessing it has something to do with it being a full blown URL, but I might be wrong. Any idea on how I can make this work, or why I can't do it this way?
If your accessing a PHP script over HTTP, only the output of that script is returned. So your script will try to interpret the output of that remote PHP script and not its source.
If there is a connection over the file system, you may want to try file://remoteservername/loaderapi/loader.php instead.
NO NO NO NO NO NO NO NO NO NO NO NO NO NO NO NO NO!
Remote file inclusion is a BAD idea, probably one of the biggest security flaws you can open up. Even for an internal tool this is not acceptable even if only purely for contributing bad habits.
PHP by default disables this behavior, and there is a broad push to have the ability to perform an include on a URL completely stripped from PHP (as there is no compelling reason to have this ability).
If you want to load shared resources, go through a shared file system drive (as in, don't use http, ftp, anything but file://) or better yet distribute copies of loader.php through a version control system. Loading from a single file resource opens you up to problems in the future of say a new dev overwriting loader.php and breaking everyone else's code.
There shouldn't be any real difference between the two; what you're doing is defining $loader_path, concatenating the loader.php, and passing that to require.
HOWEVER: you're defining the variable within the scope of a require, which will halt processing of the script of require fails.
Try replacing 'require' with 'include' and see if it retains the variable.
Also, note that if you are running your PHP server on a windows machine, and the php version is less than 4.3.0, neither 'require' nor 'include' can handle remote files : http://us.php.net/manual/en/function.include.php
Also, as noted before, if the .php lives on a remote server that parses php, you will not get code, but the result of the remote server processing the code. You'll either have to serve it up as a .txt file, or write php that, when processed, outputs valid php.
Have you tried splitting it into two lines:
$loader_path = "http://remoteservername/loaderapi/";
require( $loader_path . "loader.php" );
It's easier to read this way as well.
Simplify the code reading by simply putting everything on 3 lines:
$loader_path = "http://remoteservername/loaderapi/";
$page = "loader.php";
require($loader_path . $page );
Much clearer and it works.
why not just put it above the require statement? would make it easier to read too.
<?php
$loader_path = "../../folderName/"
require($loader_path . "filename")
?>

Categories