Adding files to a Tar Archive in PHP with different filenames - php

I'm currently using the Archive-Tar Pear extension for PHP to add a collection of files into a Tar Archive.
These files are stored on a filer with an extra extension
e.g.
filename.tgz.104850209.t or filename2.doc.2154395.t
I'd like to remove this extra extension while adding the files so that my Tar Archive would have the files: filename.tgz and filename2.doc
Is there a way of doing that without having to copy/rename the source files first before adding to the Archive?
Thanks,
Mark.

Archive_Tar in its latest version does not yet support such a functionality out of the box. Part of the functionality is in _addFile() and the other part in _addString().
Most easy is probably to extend from Archive_Tar and proxy all calls to _writeHeaderBlock() which is public, applying a map on the filename parameter so to rename it when written into headers.
class Patched_Archive_Tar extends Archive_Tar
{
var $renameMap = array();
function _writeHeaderBlock($p_filename, $p_size, $p_mtime=0, $p_perms=0,
$p_type='', $p_uid=0, $p_gid=0)
{
return parent::_writeHeaderBlock($this->_translateFilename($p_filename),
$p_size, $p_mtime=0, $p_perms=0,
$p_type='', $p_uid=0, $p_gid=0);
}
function _translateFilename($orignal)
{
$map = $this->renameMap;
if (isset($map[$orignal])) {
return $map[$orignal];
}
return $original;
}
}
Usage:
$obj = new Patched_Archive_Tar('dummy.tar'); // name of archive
$files = array('mystuff/ad.gif',
'mystuff/alcon.doc.t',
'mystuff/alcon.xls.t'); // files to store in archive
$obj->renameMap = array(
'mystuff/alcon.doc.t' => 'mystuff/alcon.doc',
'mystuff/alcon.xls.t' => 'mystuff/alcon.xls',
) // files to rename
if ($obj->create($files)) {
echo 'Created successfully!';
} else {
echo 'Error in file creation';
}
This is quick and dirty but hopefully worky. For something better see the function I noticed at the beginning _addFile() and _addString(), you basically want another one that is able to add a file (as with _addFile()) by specifiying the filename (as with _addString()).

Tried to edit #hakre's answer, but peer reviewers weren't having that.
To answer #user2248522's comment, I rewrote the class to use _writeHeader. Additionally, I added a block for any Windows users out there and fixed a couple spelling errors.
class Patched_Archive_Tar extends Archive_Tar
{
var $renameMap = array();
function _writeHeader($p_filename, $p_stored_filename)
{
return parent::_writeHeader($p_filename,
$this->_translateFilename($p_stored_filename));
}
function _translateFilename($orignal)
{
$map = $this->renameMap;
if (isset($map[$original])) {
return $map[$original];
}
//Need alter our map array to match the altered original on WIN systems
if (defined('OS_WINDOWS') && OS_WINDOWS) {
//Check for a proper array
if (!is_array($map)) return $original;
//Check each replacement rule
foreach($map as $needle => $replacement) {
if ($this->_translateWinPath($needle, true) == $original) {
return $replacement;
} //if()
} //foreach()
} //if()
return $original;
}
}
Usage:
$obj = new Patched_Archive_Tar('dummy.tar'); // name of archive
$files = array('mystuff/ad.gif',
'mystuff/alcon.doc.t',
'mystuff/alcon.xls.t'); // files to store in archive
$obj->renameMap = array(
'mystuff/alcon.doc.t' => 'mystuff/alcon.doc',
'mystuff/alcon.xls.t' => 'mystuff/alcon.xls',
) // files to rename
if ($obj->create($files)) {
echo 'Created successfully!';
} else {
echo 'Error in file creation';
}

Related

Multiple functions of the same name

I am looking to create an extension api for my web application.
Example extension file:
function echoCustomHeaders(){
echo '<li>Header Link</li>';
}
There would be several files similar to the example extension file (with the same function name, for user friendlyness when programming addons).
for($x=0;$x<count($extension_files);$x++){
//This would obviosely break when it gets to the second file, as functions cannot be declared twice in php
require_once($extension_files[$x]);
}
//some code later...
//this should call echoCustomHeaders() in ALL of the extension files, what code should I put here to make this happen?
echoCustomHeaders();
In case you are wondering about what the question is, read the comments in the code above and it should be fairly easy to see.
Return closures (lambda expressions) in your extension files as follows:
return function(){
echo '<li>Header Link</li>';
}
In PHP the include/require statement is really a function and therefore has a return value, hence you can collect those closures into an array:
$closures = array();
for($x=0;$x<count($extension_files);$x++){
$closures[$i]=include($extension_files[$x]);
}
// Do whatever you want with your closures, e.g. execute them:
foreach($closures as $closure) {
$closure();
}
ADDED CONTENT:
In the case if you would like to return multiple closures with each include, you may return an array of closures, indexed by the name of them:
return array(
'echoCustomHeaders' => function() {
echo '<li>Header Link</li>';
},
// ...
);
Then you can still execute some of them by their name:
$closureArray = array();
foreach($extension_files as $file) {
$closureArray[] = include($file);
}
foreach($closureArray as $closure) {
if(isset($closure['echoCustomHeaders'])) // Maybe there wasn't an echoCustomHeaders in each extension file ...
$closure['echoCustomHeaders']();
}
Maybe it would be a better idea to even separate the different kind of extension functions into distinct arrays:
$closureArray = array();
foreach($extension_files as $file) {
$functions = include($file);
foreach($functions as $name => $function) {
if(!isset($closureArray[$name]))
$closureArray[$name] = array();
$closureArray[$name][] = $function;
}
}
foreach($closureArray['echoCustomHeaders'] as $closure) {
$closure();
}
Another solution is to use a more object oriented way, and declare a new extension class in each extension file. However, if there would be no data sharing required between the extension methods in an extension file, then simply returning the functions as an array of closures is a more lightweight and cleaner solution in my opinion.
1.maybe you can use the new feature after php5.3:namespace http://www.php.net/manual/en/language.namespaces.php, then you can use the same name functions.
2.however you could think about the object oriented solution,for example,defined a base class who has a method echoCustomHeaders.

PHP include external method and class

I'm new to PHP and I have an issue I can't seem to fix or find a solution to.
I'm trying to create a helper function that will return an 'object' filled with information pulled from an XML file. This helper function, named functions.php contains a getter method which returns a 'class' object filled with data from an SVN log.xml file.
Whenever I try to import this file using include 'functions.php'; none of the code after that line runs the calling function's page is blank.
What am I doing wrong?
Here is what the functions.php helper method and class declaration looks like:
<?php
$list_xml=simplexml_load_file("svn_list.xml");
$log_xml=simplexml_load_file("svn_log.xml");
class Entry{
var $revision;
var $date;
}
function getEntry($date){
$ret = new Entry;
foreach ($log_xml->logentry as $logentry){
if ($logentry->date == $date){
$ret->date = $logentry->date;
$ret->author = $logentry->author;
}
}
return $ret;
}
I'm not sure what the point of having a separate helper function from the class is, personally I'd combine the two. Something like this
other-file.php
require './Entry.php';
$oLogEntry = Entry::create($date, 'svn_log.xml');
echo $oLogEntry->date;
echo $oLogEntry->revision;
Entry.php
class Entry
{
public $revision;
public $date;
public $author;
public static function create($date, $file) {
$ret = new Entry;
$xml = simplexml_load_file($file);
foreach($xml->logentry as $logentry) {
if($logentry->date == $date) {
$ret->date = $logentry->date;
$ret->author = $logentry->author;
$ret->revision = $logentry->revision;
}
}
return $ret;
}
}
EDIT
In light of the fact OP is new to PHP, I'll revise my suggestion completely. How about ditching the class altogether here? There's hardly any reason to use a class I can see at this point; let's take a look at using an array instead.
I might still move the simplexml_load_file into the helper function though. Would need to see other operations to merit keeping it broken out.
entry-helper.php
function getEntry($date, $file) {
$log_xml = simplexml_load_file($file);
$entry = array();
foreach($log_xml->logentry as $logentry) {
if($logentry->date == $date) {
$entry['date'] = $logentry->date;
$entry['author'] = $logentry->author;
$entry['revision'] = $logentry->revision;
}
}
return $entry;
}
other-file.php
require './entry.php';
$aLogEntry = Entry::create($date, 'svn_log.xml');
echo $aLogEntry['date'];
echo $aLogEntry['revision'];
EDIT
One final thought.. Since you're seemingly searching for a point of interest in the log, then copying out portions of that node, why not just search for the match and return that node? Here's what I mean (a return of false indicates there was no log from that date)
function getEntry($date, $file) {
$log_xml = simplexml_load_file($file);
foreach($log_xml->logentry as $logentry) {
if($logentry->date == $date) {
return $logentry;
return false;
}
Also, what happens if you have multiple log entries from the same date? This will only return a single entry for a given date.
I would suggest using XPATH. There you can throw a single, concise XPATH expression at this log XML and get back an array of objects for all the entries from a given date. What you're working on is a good starting point, but once you have the basics, I'd move to XPATH for a clean final solution.

Check if a file was included or loaded

Is there any elegant way to check if a file was included by using include/include_once/require/require_once or if the page was actually loaded directly? I'm trying to set up a testing file inside class files while I'm creating them.
I'm looking for something similar to Python's if __name__ == "__main__": technique. Without setting globals or constants.
Quoted from: How to know if php script is called via require_once()?
I was looking for a way to determine if a file have been included or called directly, all from within the file. At some point in my quest I passed through this thread. Checking various other threads on this and other sites and pages from the PHP manual I got enlightened and came up with this piece of code:
if (basename(__FILE__) == basename($_SERVER["SCRIPT_FILENAME"])) {
echo "called directly";
} else {
echo "included/required";
}
In essence it compares if the name of the current file (the one that
could be included) is the same as the file that is beeing executed.
Credit: #Interwebs Cowboy
you can do this by get_included_files — Returns an array with the names of included or required files and validate against __FILE__
I appreciate all the answers, but I didn't want to use any one's solution here, so I combined your ideas and got this:
<?php
// place this at the top of the file
if (count(get_included_files()) == 1) define ('TEST_SUITE', __FILE__);
// now I can even include bootstrap which will include other
// files with similar setups
require_once '../bootstrap.php'
// code ...
class Bar {
...
}
// code ...
if (defined('TEST_SUITE') && TEST_SUITE == __FILE__) {
// run test suite here
}
?>
if (defined('FLAG_FROM_A_PARENT'))
// Works in all scenarios but I personally dislike this
if (__FILE__ == get_included_files()[0])
// Doesn't work with PHP prepend unless calling [1] instead.
if (__FILE__ == $_SERVER['SCRIPT_FILENAME'])
// May break on Windows due to mixed DIRECTORY_SEPARATOR
if (basename(__FILE__) == basename($_SERVER['SCRIPT_FILENAME']))
// Doesn't work with files with the same basename but different paths
if (realpath(__FILE__) == realpath($_SERVER['SCRIPT_FILENAME']))
// Seems to do the trick as long as document root is properly configured
Note: On WAMP Servers virtual-hosts sometimes inherit the default document root setting, causing $_SERVER['DOCUMENT_ROOT'] to display wrong path.
<?php
if (__FILE__ == $_SERVER['SCRIPT_FILENAME'])
{
//file was navigated to directly
}
?>
Taken from mgutt's answer to a slightly different question here. It's important to note this doesn't work if the script is run from command line but other than that it functions exactly like python's
if __name__ == '__main__':
as far as I can tell
They is no way to separate them as include/include_once/require/require_once but php has get_included_files and get_required_files which is the same thing and only returns array of all included files. Its does not separate it if its required or included.
Example a.php
include 'b.php';
include_once 'c.php';
require 'd.php';
var_dump(get_required_files());
Output
array
0 => string '..\lab\stockoverflow\a.php' (length=46) <---- Returns current file
1 => string '..\lab\stockoverflow\b.php' (length=46)
2 => string '..\lab\stockoverflow\c.php' (length=46)
3 => string '..\lab\stockoverflow\d.php' (length=46)
But you can do something like
$inc = new IncludeManager($file);
var_dump($inc->find("b.php")); // Check if a file is included
var_dump($inc->getFiles("require_once")); // Get All Required Once
Class Used
class IncludeManager {
private $list = array();
private $tokens = array();
private $find;
private $file;
private $type = array(262 => "include",261 => "include_once",259 => "reguire",258 => "require_once");
function __construct($file) {
$this->file = $file;
$this->_parse();
}
private function _parse() {
$tokens = token_get_all(file_get_contents($this->file));
for($i = 0; $i < count($tokens); $i ++) {
if (count($tokens[$i]) == 3) {
if (array_key_exists($tokens[$i][0], $this->type)) {
$f = $tokens[$i + 1][0] == 371 ? $tokens[$i + 2][1] : $tokens[$i + 1][1];
$this->list[] = array("pos" => $i,"type" => $this->type[$tokens[$i][0]],"file" => trim($f, "\"\'"));
}
}
}
}
public function find($find) {
$finds = array_filter($this->list, function ($v) use($find) {
return $v['file'] == $find;
});
return empty($finds) ? false : $finds;
}
public function getList() {
return $this->list;
}
public function getFiles($type = null) {
$finds = array_filter($this->list, function ($v) use($type) {
return is_null($type) ? true : $type == $v['type'];
});
return empty($finds) ? false : $finds;
}
}
get_included_files() return array where 0 index mean first "included" file. Because direct run mean "include" in this terms, you can simple check first index for equality for __FILE__:
if(get_included_files()[0] == __FILE__){
do_stuff();
}
This can not work on PHP 4, because PHP 4 not add run file in this array.
Here's a different idea.
Just include the file whenever you need it.
Inside the include file you can decide whether it needs to include the contents:
<?php
if (defined("SOME_UNIQUE_IDENTIFIER_FOR_THIS_FILE"))
return;
define("SOME_UNIQUE_IDENTIFIER_FOR_THIS_FILE", 1);
// Rest of code goes here
Working solution:
$target_file = '/home/path/folder/file.php'; // or use __FILE__
if ($x=function($e){return str_replace(array('\\'), '/', $e);}) if(in_array( $x($target_file), array_map( $x , get_included_files() ) ) )
{
exit("Hello, already included !");
}
I don't think get_included_files is the perfect solution, what if your main script included some other scripts before the check? My suggestion is to check whether __FILE__ equals realpath($argv[1]):
<?php
require('phpunit/Autoload.php');
class MyTests extends PHPUnit_Framework_TestCase
{
// blabla...
}
if (__FILE__ == realpath($argv[0])) {
// run tests.
}
I took a similar approach to this issue when I cam across it. The solution I found was to load each file as needed in an include_once method. Hope this helps.
$FILES = get_included_files(); // Retrieves files included as array($FILE)
$FILE = __FILE__; // Set value of current file with absolute path
if(!in_array($FILE, $FILES)){ // Checks if file $FILE is in $FILES
include_once "PATH_TO_FILE"; // Includes file with include_once if $FILE is not found.
}
I have the following function established to check files loaded:
ARRAY_DUMP($FILES);
function ARRAY_DUMP($array){
echo "
<span style='font-size:12px;'>".date('h:i:s').":</span>
<pre style='font-size:12px;'>", print_r($array, 1), "</pre>
";
}
Output:
currentArray
(
[0] => /home/MY_DOMAIN/hardeen/index.php
[1] => /home/MY_DOMAIN/hardeen/core/construct.php
[2] => /home/MY_DOMAIN/hardeen/core/template.php
[3] => /home/MY_DOMAIN/hardeen/bin/tags.php
[4] => /home/MY_DOMAIN/hardeen/bin/systemFunction.php
)
It's sooo simple..
I have made something like this:
//code for file.php
if (!isset($file_included)){
echo "It was loaded!";
} else {
echo "It was included!";
}
//code for loader.php
//proves that atleast loader.php has loaded,
//not the file we targeted first..
$file_included = true;
include("../file.php");
And that's it.. as simple as in python.

Channel.Connect.Failed error NetConnection.Call.BadVersion

Okay - So I've been looking all over the place to try and correct this problem - But I keep finding different answers, and frankly, it is getting terribly frustrating trying to figure this out. Lemme post some code for you to look at:
PHP Script:
public function addNewCompany(CompanyVO $item)
{
$stmt = mysqli_prepare($this->connection,
"INSERT INTO `companies` ('companyName') VALUES (?);");
$this->throwExceptionOnError();
mysqli_bind_param($stmt, 's', $item->companyName);
$this->throwExceptionOnError();
mysqli_stmt_execute($stmt);
$this->throwExceptionOnError();
$autoid = mysqli_stmt_insert_id($stmt);
mysqli_stmt_free_result($stmt);
mysqli_close($this->connection);
return $autoid;
}
Portions of the MXML Main App:
protected function companysignupsheet1_addCompanyEventHandler(event:AddCompanyEvent):void
{
companyservicero.addNewCompany({Data:event.companyData});
}
<s:RemoteObject id="companyservicero"
source="CompanyServices"
destination="addNewCompany"
endpoint = "http://localhost/PHP_RO/public/gateway.php"
result="companyservicero_resultHandler(event)"
fault="companyservicero_faultHandler(event)"/>
A Part of code from Component:
protected function button_submitNewCompany_clickHandler(event:MouseEvent):void
{
var companyData11:CompanyVO = new CompanyVO();
companyData11.companyName = textinput_NewCompanyName.text;
var eventObject:AddCompanyEvent = new AddCompanyEvent("addCompanyEvent", companyData11);
dispatchEvent(eventObject);
}
The Event:
package events
{
import flash.events.Event;
import valueObjects.CompanyVO;
public class AddCompanyEvent extends Event
{
public var companyData:CompanyVO;
public function AddCompanyEvent(type:String, companyData:CompanyVO)
{
super(type);
this.companyData = companyData;
}
}
}
If I need to post more I will be happy to do so. Also - I know it is a bit overkill to try and just send the one text value in this fashion, but there will be much, much more that will go with it when I get it working - I just was trying to focus on where the problem is. Oh - and I don't know if it helps at all...But currently I can retrieve records from the mySQL database this is attached to (although I am not doing that via the RemoteObject way) - I can also add to the same table using the old drag-and-drop (Connect to Data/Services) functionality of an exact copy of the PHP above (although with the information hard coded in (I.E. the CompanyName=testtest)).
And to finish it all off - earlier when I didn't define the datatype for the argument:
public function addNewCompany($item){.....
for addNewCompany - it DID add a record in the database, although it was blank and it would still popup an error message with the whole Channel.Connect, etc..... And now in Zend Server's logs it is saying that the data is getting transferred in a stdClass wrapper and it is needed in CompanyVO datatype.
I am sooo frustrated with this all - I've been stuck with this type of problems for about 2-3 days now and I give up! PLEASE help. Thank you so much for your time and assistance!
-CS
EDIT - MORE INFO
GATEWAY.PHP
<?php
ini_set("display_errors", 1);
$dir = dirname(__FILE__);
$webroot = $_SERVER['DOCUMENT_ROOT'];
$configfile = "$dir/amf_config.ini";
$servicesdir = $dir.'/../services';
$librarydir = $dir.'/../library';
//default zend install directory
$zenddir = $webroot.'/ZendFramework/library';
//Load ini file and locate zend directory
if (file_exists($configfile)) {
$arr = parse_ini_file($configfile, true);
if (isset($arr['zend']['webroot'])) {
$webroot = $arr['zend']['webroot'];
$zenddir = $webroot.'/ZendFramework/library';
}
if (isset($arr['zend']['zend_path'])) {
$zenddir = $arr['zend']['zend_path'];
}
if (isset($arr['zend']['library'])) {
$librarydir = $arr['zend']['library'];
}
if (isset($arr['zend']['services'])) {
$servicesdir = $arr['zend']['services'];
}
}
// Setup include path
// add zend directory, library and services to include path
set_include_path(get_include_path()
.PATH_SEPARATOR.$zenddir
.PATH_SEPARATOR.$librarydir
.PATH_SEPARATOR.$servicesdir);
// Initialize Zend Framework loader
require_once 'Zend/Loader/Autoloader.php';
Zend_Loader_Autoloader::getInstance()->setFallbackAutoloader(true)- >suppressNotFoundWarnings(true);
// Load configuration
$default_config = new Zend_Config(array("production" => false), true);
$default_config->merge(new Zend_Config_Ini($configfile, 'zendamf'));
$default_config->setReadOnly();
$amf = $default_config->amf;
// Store configuration in the registry
Zend_Registry::set("amf-config", $amf);
// Initialize AMF Server
$server = new Zend_Amf_Server();
$server->setProduction($amf->production);
if (isset($amf->directories)) {
$dirs = $amf->directories->toArray();
foreach ($dirs as $dir) {
if ($dir == "./") {
$server->addDirectory($webroot);
} else
if (realpath("{$webroot}/{$dir}")) {
$server->addDirectory("{$webroot}/{$dir}");
} else
if (realpath($dir)) {
$server->addDirectory(realpath($dir));
}
}
}
// Initialize introspector for non-production
if (! $amf->production) {
$server->setClass('Zend_Amf_Adobe_Introspector', '',
array("config" => $default_config, "server" => $server));
$server->setClass('Zend_Amf_Adobe_DbInspector', '',
array("config" => $default_config, "server" => $server));
}
// Handle request
echo $server->handle();
AMF_CONFIG
[zend]
;set the absolute location path of webroot directory, example:
;Windows: C:\apache\www
;MAC/UNIX: /user/apache/www
webroot = "C:/Zend/Apache2/htdocs"
;set the absolute location path of zend installation directory, example:
;Windows: C:\apache\PHPFrameworks\ZendFramework\library
;MAC/UNIX: /user/apache/PHPFrameworks/ZendFramework/library
zend_path ="C:/Zend/Apache2/htdocs/.metadata/.plugins/org.zend.php.framework.resource/resources/ZendFramework-1/library"
library ="C:/Zend/Apache2/htdocs/PHP_RO/library"
services ="C:/Zend/Apache2/htdocs/PHP_RO/services"
[zendamf]
amf.production = false
amf.directories[]=PHP_RO/services
Channel.Connect.Failed error NetConnection.Call.BadVersion usually happens when PHP echoes an error or warning to the amf response. Flex gets an amf message appended with something like 'warning something went wrong on line X' and can't parse it.
Turn on the network monitor in Flash Builder and view the latest raw response. You will see the error formatted with html tags.

Restrict allowed file upload types PHP

Right now I have a function which takes my uploaded file, checks the extension, and if it matches an array of valid extensions it's processed. It's a contact list importer.
What I need to figure out is how to be sure that file (in this case a .csv) is actually what it says it is (ex. not an excel file that just got renamed as a .csv).
Our servers run PHP 5.2.13
Here's the current validation function I have
public static function validateExtension($file_name,$ext_array) {
$extension = strtolower(strrchr($file_name,"."));
$valid_extension="FALSE";
if (!$file_name) {
return false;
} else {
if (!$ext_array) {
return true;
} else {
foreach ($ext_array as $value) {
$first_char = substr($value,0,1);
if ($first_char <> ".") {
$extensions[] = ".".strtolower($value);
}
else {
$extensions[] = strtolower($value);
}
}
foreach ($extensions as $value) {
if ($value == $extension) {
$valid_extension = "TRUE";
}
}
if ($valid_extension==="TRUE") {
return true;
} else {
return false;
}
}
}
}
EDIT: I'm now trying to do
exec('file -ir '.$myFile)
When I run this command in terminal I'm given a usable response. When I run the same command through php, I'm given something different. Any ideas why? I've tried it with exec, passthru, shell_exec. And the server does not have safe mode running.
Forget extension checking, it's not reliable enough.
Also, I think traditional MIME magic sniffing will fail here, because there is no usable header (This is just my guess, though.)
In this specific case, I'd say it's feasible to take a quick peek at the contents, for example read the first ten lines or so. If they are all no longer than x bytes, and each line contains the same number of semicolons (or whatever your CSV parser takes as separators), it's a CSV file.

Categories