I have this code that creates a ".zip" file and inside it a ".xml" file obtained from a string.
As seen in the example later I get your information and convert it to base64 and hash.
The code is functional.
What I want now is to use "ob_start()" so as not to have to create the ".zip" file, I don't know if someone could help me with a basic example, greetings...
<?php
$content = '<?xml version="1.0"?><Catalog><Book id="bk101"><Author>Garghentini, Davide</Author><Title>XML Developers Guide</Title><Genre>Computer</Genre><Price>44.95</Price><PublishDate>2000-10-01</PublishDate><Description>An in-depth look at creating applicationswith XML.</Description></Book><Book id="bk102"><Author>Garcia, Debra</Author><Title>Midnight Rain</Title><Genre>Fantasy</Genre><Price>5.95</Price><PublishDate>2000-12-16</PublishDate><Description>A former architect battles corporate zombies,an evil sorceress, and her own childhood to become queenof the world.</Description></Book></Catalog>';
$route = './temp/';
$name = 'facturaElectronicaCompraVenta.xml.zip';
$file = "{$route}{$name}";
// CREATE ZIP
$zp = gzopen($file,'w9');
gzwrite($zp,$content);
gzclose($zp);
// READ ZIP
$fp = fopen($file,'rb');
$binary = fread($fp,filesize($file));
$res = [
'archivo' => base64_encode($binary),
'hashArchivo' => hash('sha256',$binary),
];
print_r($res);
First of all, the output buffer (ob...) functions don't accomplish anything related to files, they only capture the script output (e.g., echo 'Hello, World!).
If you want to keep using gzopen(), perhaps you can just provide a stream wrapper pointing to anything that isn't a physical file (I haven't investigated that option) but it looks easier to just switch to gzencode().
Related
I have the contents of a file in a string. I need to pass this file to a function where the function is expecting the parameter to be the name of the file, not the contents. The obvious and probably simplest way to do this would be to write the contents to a temp file, then pass that file name to the function, and unlink the file once I'm finished.
However, I'm looking for a solution that doesn't involve writing the file out to the file system and then reading it back in. I've had a need for this in multiple cases, so I'm not looking for a work-around to a specific function, but more of a generic method that will work for any function expecting a file name (like file_get_contents(), for instance).
Here are some thoughts, but not sure how to pursue these yet:
Is it possible to write the contents somewhere in memory, and then
pass that to the function as a filename? Perhaps something using
php://memory.
Is it possible to write the contents to a pipe, then pass the name of the
pipe to the function?
I did a short proof-of-concept trying with php://memory as follows, but no luck:
$data = "This is some file data.\n";
file_put_contents( 'php://memory', $data );
echo file_get_contents( 'php://memory' );
Would be interested in knowing of good ways to address this. Googling hasn't come up with anything for me.
It mainly depends on what the target function does with the file name. If you're lucky, you can register your own stream wrapper:
stream_wrapper_register('demo', 'DemoStream');
$data = "This is some file data.\n";
$filename = 'demo://foo';
file_put_contents($filename, $data );
echo file_get_contents($filename);
Why not use a file in the /tmp/ directory? Like this:
<?php
$filename = '/tmp/mytmpfile';
$data = "This is some file.\n";
file_put_contents($filename, $data);
$result = file_get_contents($filename);
var_dump($result);
Well, as you say you don't want to use a file, you shouldn't use file_get_contents().
But you can achieve the same result by using stream_get_contents(), like this:
<?php
$data = "This is some file data.\n";
$handle = fopen('php://memory', 'r+'); // open an r/w handle to memory
fputs($handle, $data); // write the data
rewind($handle); // rewind the pointer
echo stream_get_contents($handle); // retrieve the contents
I have a .tar.gz file downloaded from an external API which we have to implement. It contains images for an object.
I'm not sure how they managed to compress it this way, but the files are basically prefixed with the "current directory". It looks like this in WinRAR:
And like this in 7-Zip, note the .tar first level, and "." second level:
-> ->
When calling
$file = 'archive.tar.gz';
$phar = new PharData($file, FilesystemIterator::CURRENT_AS_FILEINFO);
var_dump($phar->offsetGet('./12613_s_cfe3e73.jpg'));
I get the exception:
Cannot access phar file entry '/12613_s_cfe3e73.jpg' in archive '{...}/archive.tar.gz'
Calling a file which does not exist, e.g.:
var_dump($phar->offsetGet('non-existent.jpg'));
Or calling it without the directory seperator, e.g.:
var_dump($phar->offsetGet('12613_s_cfe3e73.jpg'));
I get a
Entry 12613_s_cfe3e73.jpg does not exist
Exception.
It is not possible to get the archive formatted differently. Does anyone have an idea how to solve this?
Ended up using Archive_Tar. There must be something wrong in the source code of PHP, though I don't think this is the "normal" way of packaging a .tar either.
Unfortunately I'm not very good at C, but it's probably in here (line 1214) or here.
This library seems to handle it just fine, using this example code:
$file = 'archive.tar.gz';
$zip = new Archive_Tar($file);
foreach ($zip->listContent() as $file) {
echo $file['filename'] . '<br>';
}
Result:
./12613_s_f3b483d.jpg
./12613_s_cfe3e73.jpg
./1265717_s_db141dc.jpg
./1265717_s_af5de56.jpg
./1265717_s_b783547.jpg
./1265717_s_35b11f9.jpg
./1265716_s_83ef572.jpg
./1265716_s_9ac2725.jpg
./1265716_s_c5af3e9.jpg
./1265716_s_c070da3.jpg
./1265715_s_4339e8a.jpg
Note the filenames are still prefixed with "./" just like they are in WinRAR.
If you want to stick to using PharData, i suggest a more conservative, two-step approach, where you first decompress the gz and then unarchive all files of the tar to a target folder.
// decompress gz archive to get "/path/to/my.tar" file
$gz = new PharData('/path/to/my.tar.gz');
$gz->decompress();
// unarchive all files from the tar to the target path
$tar = new PharData('/path/to/my.tar');
$tar->extractTo('/target/path');
But it looks like you want to select individual files from the tar.gz archive directly, right?
It should work using fopen() with a StreamReader (compress.zlib or phar) and selecting the individual file. Some examples:
$f = fopen("compress.zlib://http://some.website.org/my.gz/file/in/the/archive", "r");
$f = fopen('phar:///path/to/my.tar.gz//file/in/archive', 'r');
$filecontent = file_get_contents('phar:///some/my.tar.gz/some/file/in/the/archive');
Streaming should also work, when using Iterators:
$rdi = new RecursiveDirectoryIterator('phar:///path/to/my.tar.gz')
$rii = new RecursiveIteratorIterator($rdi, RecursiveIteratorIterator::CHILD_FIRST);
foreach ($rii as $splFileInfo){
echo file_get_contents($splFileInfo->getPathname());
}
The downside is that you have to buffer the stream and save it to file.
Its not a direct file extraction to a target folder.
I have a BASE64 string of a zip file that contains one single XML file.
Any ideas on how I could get the contents of the XML file without having to deal with files on the disk?
I would like very much to keep the whole process in the memory as the XML only has 1-5k.
It would be annoying to have to write the zip, extract the XML and then load it up and delete everything.
I had a similar problem, I ended up doing it manually.
https://www.pkware.com/documents/casestudies/APPNOTE.TXT
This extracts a single file (just the first one), no error/crc checks, assumes deflate was used.
// zip in a string
$data = file_get_contents('test.zip');
// magic
$head = unpack("Vsig/vver/vflag/vmeth/vmodt/vmodd/Vcrc/Vcsize/Vsize/vnamelen/vexlen", substr($data,0,30));
$filename = substr($data,30,$head['namelen']);
$raw = gzinflate(substr($data,30+$head['namelen']+$head['exlen'],$head['csize']));
// first file uncompressed and ready to use
file_put_contents($filename,$raw);
After some hours of research I think it's surprisingly not possible do handle a zip without a temporary file:
The first try with php://memory will not work, beacuse it's a stream that cannot be read by functions like file_get_contents() or ZipArchive::open(). In the comments is a link to the php-bugtracker for the lack of documentation of this problem.
There is a stream support ZipArchive with ::getStream() but as stated in the manual, it only supports reading operation on an opened file. So you cannot build a archive on-the-fly with that.
The zip:// wrapper is also read-only: Create ZIP file with fopen() wrapper
I also did some attempts with the other php wrappers/protocolls like
file_get_contents("zip://data://text/plain;base64,{$base64_string}#test.txt")
$zip->open("php://filter/read=convert.base64-decode/resource={$base64_string}")
$zip->open("php://filter/read=/resource=php://memory")
but for me they don't work at all, even if there are examples like that in the manual. So you have to swallow the pill and create a temporary file.
Original Answer:
This is just the way of temporary storing. I hope you manage the zip handling and parsing of xml on your own.
Use the php php://memory (doc) wrapper. Be aware, that this is only usefull for small files, because its stored in the memory - obviously. Otherwise use php://temp instead.
<?php
// the decoded content of your zip file
$text = 'base64 _decoded_ zip content';
// this will empty the memory and appen your zip content
$written = file_put_contents('php://memory', $text);
// bytes written to memory
var_dump($written);
// new instance of the ZipArchive
$zip = new ZipArchive;
// success of the archive reading
var_dump(true === $zip->open('php://memory'));
toster-cx had it right,you should award him the points, this is an example where the zip comes from a soap response as a byte array (binary), the content is an XML file:
$objResponse = $objClient->__soapCall("sendBill",array(parameters));
$fileData=unzipByteArray($objResponse->applicationResponse);
header("Content-type: text/xml");
echo $fileData;
function unzipByteArray($data){
/*this firts is a directory*/
$head = unpack("Vsig/vver/vflag/vmeth/vmodt/vmodd/Vcrc/Vcsize/Vsize/vnamelen/vexlen", substr($data,0,30));
$filename = substr($data,30,$head['namelen']);
$if=30+$head['namelen']+$head['exlen']+$head['csize'];
/*this second is the actua file*/
$head = unpack("Vsig/vver/vflag/vmeth/vmodt/vmodd/Vcrc/Vcsize/Vsize/vnamelen/vexlen", substr($data,$if,30));
$raw = gzinflate(substr($data,$if+$head['namelen']+$head['exlen']+30,$head['csize']));
/*you can create a loop and continue decompressing more files if the were*/
return $raw;
}
If you know the file name inside the .zip, just do this:
<?php
$xml = file_get_contents('zip://./your-zip.zip#your-file.xml');
If you have a plain string, just do this:
<?php
$xml = file_get_contents('compress.zlib://data://text/plain;base64,'.$base64_encoded_string);
[edit] Documentation is there: http://www.php.net/manual/en/wrappers.php
From the comments: if you don't have a base64 encoded string, you need to urlencode() it before using the data:// wrapper.
<?php
$xml = file_get_contents('compress.zlib://data://text/plain,'.urlencode($text));
[edit 2] Even if you already found a solution with a file, there's a solution (to test) I didn't see in your answer:
<?php
$zip = new ZipArchive;
$zip->open('data::text/plain,'.urlencode($base64_decoded_string));
$zip2 = new ZipArchive;
$zip2->open('data::text/plain;base64,'.urlencode($base64_string));
If you are running on Linux and have administration of the system. You could mount a small ramdisk using tmpfs, the standard file_get / put and ZipArchive functions will then work, except it does not write to disk, it writes to memory.
To have it permanently ready, the fstab is something like:
/media/ramdisk tmpfs nodev,nosuid,noexec,nodiratime,size=2M 0 0
Set your size and location accordingly so it suits you.
Using php to mount a ramdisk and remove it after using it (if it even has the privileges) is probably less efficient than just writing to disk, unless you have a massive number of files to process in one go.
Although this is not a pure php solution, nor is it portable.
You will still need to remove the "files" after use, or have the OS clean up old files.
They will of coarse not persist over reboots or remounts of the ramdisk.
if you want to read the content of a file from zip like and xml inside you shoud look at this i use it to count words from docx (wich is a zip )
if (!function_exists('docx_word_count')) {
function docx_word_count($filename)
{
$zip = new ZipArchive();
if ($zip->open($filename) === true) {
if (($index = $zip->locateName('docProps/app.xml')) !== false) {
$data = $zip->getFromIndex($index);
$zip->close();
$xml = new SimpleXMLElement($data);
return $xml->Words;
}
$zip->close();
}
return 0;
}
}
The idea comes from toster-cx is pretty useful to approach malformed zip files too!
I had one with missing data in the header, so I had to extract the central directory file header by using his method:
$CDFHoffset = strpos( $zipFile, "\x50\x4b\x01\x02" );
$CDFH = unpack( "Vsig/vverby/vverex/vflag/vmeth/vmodt/vmodd/Vcrc/Vcsize/Vsize/vnamelen/vexlen", substr( $zipFile, $CDFHoffset, 46 ) );
I'm writing a system for a browser application that will store some particular php scripts in a database and then pull them out and execute them when needed. At first I tried using exec() and piping to php the output of a script that got the scripts out of the database and printed them. This worked in one use case, but not all, and feels brittle anyway, so I'm looking for a better way.
I'm now attempting to accomplish this through use of a PHP file stream in memory. For instance:
$thing = <<<'TEST'
<?php
$thing = array();
print "Testing code in here.";
var_dump($thing);
?>
TEST;
$filename = "php://memory";
$fp = fopen($filename, "w+b");
fwrite($fp, $thing);
//rewind($fp);
fclose($fp);
include "php://memory";
However, nothing is printed when the script is executed. Is this even possible by this means, and if not, is there another way to do this? I'm trying to avoid having to write temporary files and read from them, as I'm sure accessing the filesystem would slow things down. Is there a URL I can provide to "include" so that it will read the memory stream as if it were a file?
I don't think eval() would do this, as, if I remember correctly, it's limited to a single line.
Also, please no "eval = include = hell" answers. Non-admin users do not have access to write the scripts stored in the database, I know that this needs special treatment over the life-cycle of my application.
You need to use stream_get_contents to read from the php://memory stream. You cannot include it directly.
eval() and include are actually pretty the same. So eval() works with multiple lines - just FYI. However, I would prefer include here, I always think it's faster. Maybe I'm wrong, no Idea.
However, I think you should debug your code, I don't see a reason per-se why it should not work. You might need to rewind the pointer (you have commented that), but what you should check first-hand is, that your PHP configuration allows to include URLs. I know that that setting prevents using of the data:// URIs, so you might have this enabled.
Also you can always try if PHP can open the memory by using file_get_contents and dumping out. This should give you the code. If not, you already made some mistake (e.g. no rewind or something similar).
Edit: I've not come that far (demo):
<?php
/**
* Include from “php://memory” stream
* #link https://stackoverflow.com/q/9944867/367456
*/
$thing = <<<TEST
<?php
\$thing = array();
print "Testing code in here.";
var_dump(\$thing);
TEST;
$filename = "php://memory";
$fp = fopen($filename, "w+b");
fwrite($fp, $thing);
rewind($fp);
var_dump(stream_get_contents($fp));
This is what I found out:
You should not close the "file". php://memory is a stream once closed it will disappear.
You need to access the $fp as stream than, which is not possible for include out of the box AFAIK.
You then would need to create a stream wrapper that maps a stream resource to a file name.
When you've done that, you can include a memory stream.
The PHP settings you need to check anyway. There are more than one, consult the PHP manual.
It might be easier to use the data URI (demo):
<?php
/**
* Include from “php://memory” stream
* #link https://stackoverflow.com/q/9944867/367456
*/
$thing = <<<TEST
<?php
\$thing = array();
print "Testing code in here.";
var_dump(\$thing);
TEST;
include 'data://text/plain;,'. urlencode($thing);
See as well: Include code from a PHP stream
If there is a way to include from php://memory then this is a serious vulnerability. While it has many uses, eval is used quite often with code obfuscation techniques to hide malicious code.
(Every tool is a weapon if you hold it right.)
With that said, there (thankfully) doesn't appear to be any obvious way to include from php://memory
I was wondering how to basically edit a .swf file using php, to change a single variable or to change more. How would I go about doing this? Is there a way to edit it without knowing machine code?
If there is an example of how to do this, where can I find it?
Thanks!
Or, if there is an easier way to go about doing this, please let me know!
take a look at libming
php documentation at http://docs.php.net/manual/en/book.ming.php
With Actionscript, it's very simple to load external data: XML and JSON are two standardized ways to do it, and both are easily generated by PHP. What exactly are you trying to do?
The question is old, but since it happens to coincide with what I've been working on, I figured I would put something together in case others find it useful. The solution works for AS3 only. It let you to change the values of instance variables and constants.
Suppose you have the following class:
package pl.krakow.rynek {
import flash.display.Sprite;
public class Advertisement extends Sprite {
private var title:String = 'Euro 2012 LIVE!';
/* ... */
}
}
You want the var title to be something else. The code for doing so is as follow:
<?php
require_once 'flaczki/classes.php';
// parse the SWF file, decoding only those tags needed by the injector
$input = fopen("input.swf", "rb");
$parser = new SWFParser;
$injector = new AS3ConstantInjector;
$swfFile = $parser->parse($input, $injector->getRequiredTags());
$classConstants = array(
'pl.krakow.rynek.Advertisement' => array(
'title' => 'Free Beer!'
)
);
// inject the values and reassemble the file
$injector->inject($swfFile, $classConstants);
$output = fopen($outPath, "wb");
$assembler = new SWFAssembler;
$assembler->assemble("output.swf", $swfFile);
?>
The code should be self-explanatory. The SWF file is first parsed, modifications are made, and the in-memory structure is saved to file. AS3ConstantInjector.inject() expects as the second argument an array of arrays keyed by the qualified names of the classes you wish to modify. The arrays themselves hold the new values for each class, with the key as the variable/constant name.
To see The variables in a SWF file, use AS3ConstantExtractor:
<?php
require_once 'flaczki/classes.php';
$input = fopen("button.swf", "rb");
$parser = new SWFParser;
$extractor = new AS3ConstantExtractor;
$swfFile = $parser->parse($input, $extractor->getRequiredTags());
$classConstants = $extractor->extract($swfFile);
print_r($classConstants);
?>
The Flaczki classes can be downloaded at http://code.google.com/p/flaczki/downloads/list
You can find out more about the Flaczki framework at the project development blog at http://flaczkojad.blogspot.com/
check out the SWF-library in php
Instead of thinking how to generate swf files, do the opposite and let the internal behavior depend on external logic in a php script. This way you never need to (re)compile your swf.