I have the following code to find files in a directory called robofile-templates in my source tree.
Now I am packaging the app into a phar file.
Can I use similar code to iterate over the files contained in the phar file or do I have to rewrite it?
protected function getTemplateRoboFiles() {
$base = dirname( __DIR__ );
$templates = [];
$finder = new Finder();
$finder->directories()->in( $base )->name( 'robofile-templates' );
foreach ( $finder as $directory ) {
$fileFinder = new Finder();
$fileFinder->files()->in( $directory->getRealPath() )->name( '*.php' );
foreach ( $fileFinder as $file ) {
$templates[ $file->getBasename( '.php' ) ] = $file->getRealPath();
}
}
return $templates;
}
EDIT: Interesting enough, opendir / readdir work inside the PHAR. I could resort to use those basic file functions, but I think Symfony file finder is built on the same basic commands?
Symfony finder is working as expected, also finding files in phars.
My problem was on another part of the process: I was building the phar using composer install and my composer.lock was not up to data. As the file I was trying to find in the archive, was in a composer module, it was just not actually included in the phar.
So who is stumbling across this: Better check if your file is really packaged into the phar.
Related
I'm trying to parse all specific .yml files from Folders subfolders. Like lets say I have Modules folder with test & example subfolder. Both test and example have .yml file I want to parse.
Currently I have:
$yaml = new Parser();
$yml_array = $yaml->parse(file_get_contents(__DIR__.'/path/to/file.yml'));
This works but would be pain in the ass hardcode everytime when new subfolder with .yml file is added.
Can someone help me out and teach me how to do it smarter or give tip?
Thanks for your time
You can use the DirectoryLoader: a recursive loader to go through directories. As example:
// Load recursively all YAML configuration files in services directories
$fileLocator = new FileLocator(__DIR__);
$loader = new DirectoryLoader($container, $fileLocator);
$loader->setResolver(new LoaderResolver(array(
new YamlFileLoader($container, $fileLocator),
$loader,
)));
$loader->load('/services/');
More info in this article.
Hope this help
Thanks Matteo for helpful answer, it didn't really fit into my needs, but helped.
I found following solution:
$yaml = new Parser();
$finder = new Finder();
$parsedData = array();
$tmp = $finder->files()->in(__DIR__.'/../path/to/folder')->name('test.yml');
foreach ($tmp as $t)
{
$x = $yaml->parse(file_get_contents($t));
array_push($parsedData, $x);
}
I am trying to create a .phar file from my web application. Following the php documentation's example I tried the following for this purpose.
<?php
$srcRoot = __DIR__ . "/../app";
$buildRoot = __DIR__ . "/../build";
$p = new Phar("$buildRoot/build.phar", 0, 'build.phar');
$p->buildFromIterator(
new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($srcRoot)
),
$srcRoot
);
However I got the following error. I dont have any idea about the error. What is wrong with the code?
PHP Fatal error: Uncaught exception 'UnexpectedValueException' with message
'Iterator RecursiveIteratorIterator returned a path "D:\site\app" that is
not in the base directory "D:\site\app"'
in D:\site\tools\create-phar.php:7
The source of the problem is that RecursiveDirectoryIterator also lists the dot files - . and ...
When iterating over /path/to/foo it also lists /path/to/foo/. and /path/to/foo/.. which goes to the parent directory - outside the base directory.
Thus you have to prevent the inclusion of the ".." files, which is most easily achieved with FilesystemIterator::SKIP_DOTS as second parameter to DirectoryIterator:
new RecursiveDirectoryIterator($srcRoot, FilesystemIterator::SKIP_DOTS)
(#cweiske -- I just realized you beat me to it, I'll make sure to refresh the page next time, my sincerest apologies!)
You need just a slight edit to skip over the unix paths /. and /..:
<?php
$srcRoot = __DIR__ . "/../app";
$buildRoot = __DIR__ . "/../build";
$p = new Phar("$buildRoot/build.phar", 0, 'build.phar');
$p->buildFromIterator(
new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($srcRoot, FilesystemIterator::SKIP_DOTS)
),
$srcRoot
);
Box makes it very easy to create PHAR archives from your source files. Basically you first add a configuration file and then you can create your PHAR file by simply invoking box build on the command line.
There is a great blog post by Matthieu Moquet describing how he used Box to simplify the distribution of a PHP Cli application. Personally, I've also been using Box for a CLI application, however the official project description is not limited to CLI applications, but rather summarizes the Box project as:
An application for building and managing Phars.
The Box project provides the PHAR build script and takes care of setting all paths correctly, so this might solve your problem.
The solution proposed by #cweiske is brilliant. In some situations, however, you may need the ability to add more directory path exclusions. Have a look at this example that excludes any references to the .git directory, using FilterIterator:
// create phar
$p = new Phar($pharFile, 0, $pharFile);
// produce iteration which excludes any references to values assigned to $excludes
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS)
);
$filter = new class($iterator) extends FilterIterator {
public static $excludes = [];
public function accept()
{
$item = $this->current();
$actual = 0;
foreach (self::$excludes as $exclude) {
$actual += (int) boolval(strpos($item->getPath(), $exclude));
}
return ($actual === 0);
}
};
$filter::$excludes = ['.git'];
$p->buildFromIterator($filter, $path);
Have a look at php.ini file to check the value of phar.readonly field.
It must be 0 to create phar archive.
Ref: http://php.net/manual/en/phar.configuration.php#ini.phar.readonly
I have this function that creates an array of a directories output.
public function getDirContents( $path )
{
$dir = new RecursiveDirectoryIterator( $path, FilesystemIterator::SKIP_DOTS );
// Flatten the recursive iterator, folders come before their files
$it = new RecursiveIteratorIterator( $dir, RecursiveIteratorIterator::SELF_FIRST );
// Maximum depth is 1 level deeper than the base folder
$it->setMaxDepth(1);
$results = array();
// Basic loop displaying different messages based on file or folder
foreach ($it as $contents)
{
$fileInfo = array();
if ( $contents->isFile() )
{
$fileInfo['dir'] = $it->getSubPath();
$fileInfo['file'] = $contents->getFilename();
$results[] = $fileInfo;
}
}
return $results;
}
I have this in a class that I created and it works as expected. I have an older script that I haven't rewritten to use this class. so I placed this function (minus the public) in another file that contains random common functions and include the file into my old script. However, when I call it I get the error
PHP Fatal error: Class 'FilesystemIterator' not found in /aaa/bbb/ccc/ddd/common.php on line 745
If I remove the FilesystemIterator::SKIP_DOTS it works fine except I guess it loses that functionality. I'm curious why this is happening.
Other things that may or may not explain what could be occuring.
The class that I use it in successfully is part of the main site.
The script where it errs is actually a subdomain.
I'm using php 5.3 (single file)
-- This is bluehost's config for picking a version of php and this is how mine is set up.
PHP 5.3 (Single php.ini) Same as PHP 5.3, but all subdirectories will use ~/public_html/php.ini
This is why I was under the impression that my subdirectory (subdomain) would use that file.
The httaccess in the subdomain vs the main are not the same.
I'm not sure if those tidbits help but I'm at lose and I'm fairly new at this.
Any help would be appreciated.
I've been doing a lot of digging around and have ended up with a huge headache and no success. I am trying to use the Zend Amazon Service (within my Codeigniter framework) to fetch information about a book using its ISBN. I initially tried it with Zend 1.12 but I kept getting an error about missing parameter AssociateTag. I am now trying with Zend 2.0 but still getting problems.
This is the code I am using within my controller:
set_include_path(get_include_path() . PATH_SEPARATOR . 'site/libraries');
require_once 'Zend2/Loader/StandardAutoloader.php';
$autoloader = Zend_Loader_Autoloader::getInstance();
$amazon = new Zend_Service_Amazon('[apikey]', 'US', '[secretkey]');
$item = $amazon->itemLookup('B0000A432X');
I get the following error:
Fatal error: Class 'Zend_Loader_Autoloader' not found.
My questions are:
How can I load the correct autoloader?
Where do I include the associate tag in the parameters? There is no mention of it in the outdated zend user guide
Zend 2.0 doesn't even seem to have the amazon service files or even the /service folder. What does this mean?
Note: The demo included with zendservice-amazon doesn't work as is. Requests must include your App ID, Secret Key, and Associate Tag which the demo script doesn't do by default. It took me a while to figure this out, without these, all queries throw an exception saying the HTTP response status was 400. Unfortunately the exception doesn't have the response body which says what parameters were missing.
Here is some code that will get you started with ZF2 and ZendService\Amazon.
To start, let me outline the directory structure of where I will be putting files for this example:
testing
|-Zend
|---Crypt
|---Escaper
|---Http
|---I18n
|---Loader
|----+AutoloaderFactory.php
|----+... more files
|----+StandardAutoloader.php
|-----Exception
|---Stdlib
|---Uri
|---Validator
|-ZendRest
|-ZendService
|---Amazon
|-----Authentication
|-------Exception
|-----Ec2
|-------Exception
|-----Exception
|-----S3
|-------Exception
|-----SimpleDb
|-------Exception
|-----Sqs
|-------Exception
|----+AbstractAmazon.php
|----+...more files
|----+SimilarProduct.php
|-+test.php
The gist is I have created a directory called testing where we will place the ZF2 autoloader as well as the Amazon classes and their dependencies. Under testing is a Zend folder that contains the autoloader (in Loader) and also a ZendService folder where the Amazon service goes.
First, we need to get a copy of the autoloader from ZF2. Part of the reason you were having trouble is because it looks like you are using the ZF1 autoloader which is incompatible with ZF2. To get the Autoloader from ZF2, you can download the latest ZF2 package and copy the Loader directory from ZendFramework-2.0.x/library/Zend/ into the Zend folder we created in the testing directory.
Now that we have the autoloader files, let's grab the Amazon service files. I'll write a detailed answer on how to use Composer to get the latest package, but for now I'll explain how to get it manually. To get the full list of available ZF2 packages, load up the JSON file at http://packages.zendframework.com/packages.json In it find, zendframework/zendservice-amazon, determine the highest version available from the list, and grab the corresponding dist. EDIT As of 11th Jul 2013, this is the latest zendservice-amazon package.
From the library directory in ZendService_Amazon-2.0.2.zip, copy the entire ZendService directory into the testing directory. You now have the ZF2 Amazon service files.
Next, take care of the dependencies. From the ZF2 library, copy the directories, Crypt, Escaper, Http, I18n, Json, Stdlib, Uri, and Validator into the Zend directory inside testing.
You will also need the ZendRest package. Copy the ZendRest folder from library from the ZendRest package to testing/ZendRest.
Now for some code. Create test.php inside the testing folder with the following contents:
<?php
require_once './Zend/Loader/StandardAutoloader.php';
$autoloader = new Zend\Loader\StandardAutoloader(array(
'namespaces' => array(
'Zend' => dirname(__FILE__) . '/Zend',
'ZendRest' => dirname(__FILE__) . '/ZendRest',
'ZendService' => dirname(__FILE__) . '/ZendService',
),
'fallback_autoloader' => true));
$autoloader->register();
$tag = 'prdesign-20'; // replace with your Amazon app ID
$appId = '1JT2V3QNEHDAMKYR5F02'; // replace w/ your access key from https://portal.aws.amazon.com/gp/aws/securityCredentials
$secretKey = 'Qgjeiw39f8UNzjJgeerrgDs1a193du/v7djDAtn/x';
$query = new ZendService\Amazon\Query($appId, 'US', $secretKey);
$query->Category('Books')->Keywords('PHP')->AssociateTag($tag);
$result = $query->search();
foreach($result as $item): ?>
<div class="item">
<?php echo $item->Title ?>
by <?php if (is_array($item->Author)): ?>
<?php echo implode(', ', $item->Author) ?>
<?php else: ?>
<?php echo $item->Author ?>
<?php endif; ?>
</div>
<?php endforeach; ?>
First, we require_once the StandardAutoloader class from ZF2. Once the autoloader is registered, this is the only class you have to manually include.
Next, we create a new autoloader and pass some options. This tells the autoloader where classes in the namespace Zend and ZendService are located. We tell the autoloader they live in the respective folders in our current directory. Change dirname(__FILE__) to the correct path as needed. The fallback_autoloader option tells the autoloader to look for classes of any namespace or vendor in the include_path.
Calling $autoloader->register(); then actually registers the autoloader we set up within PHP. Setting up the autoloader is now finished.
The next 3 lines define some required parameters for the API.
The next 3 lines are straightforward, we now create a new instance of ZendService\Amazon\Query and pass our Amazon app ID and secret key. We then build the query by specifying to search in the Books Category and set the Keywords to be PHP. We also add the required AssociateTag. Finally, we execute the search.
I haven't yet used ZendService\Amazon yet so I can't provide detailed instructions on using the class but the included demo script should get you started with sending basic queries to Amazon and processing the results.
Hope that helps.
i have use Amazon AWS S3 bucket along with Zend Framework. A simple example is photo upload to amazon AWS Bucket.In you application/config/application.ini file
;AWSAccessKeyId= "AccessKey"
;AWSSecretKey= "SecretKey"
;AWSS3BucketName = "zoshare-images"
;AWSS3GetImageUrl = "http://[name].s3.amazonaws.com/[folder]/"
Lets say you want to upload an image to bucket, below is sample code of controller
public function updateAction()
{
if($_FILES["fuPic"]["size"] > 0 || $_FILES["fuPre"]["size"] > 0)
{
$config = Zend_Registry::get('config');
$s3 = new Zend_Service_Amazon_S3($config['AWSAccessKeyId'],$config['AWSSecretKey']);
$bucketName = $config['AWSS3BucketName'];
if($_FILES["fuPic"]["size"] == 0)
unset($_FILES["fuPic"]);
else if($_FILES["fuPre"]["size"] == 0)
unset($_FILES["fuPre"]);
$s3->removeObject($bucketName."/[folder]/".$campaignModel->getCampaignId().'_pic_'.$campaign_olddata->getPicture());
$s3->removeObject($bucketName."/[folder]/".$campaignModel->getCampaignId().'_pre_'.$campaign_olddata->getPreview());
if (isset($_FILES["fuPic"]))
{
$filename = $campaignModel->getCampaignId().'_pic_'.$_FILES["fuPic"]['name'];
$s3->putObject($bucketName."/[folder]/".$filename, file_get_contents($_FILES["fuPic"]["tmp_name"]),
array(Zend_Service_Amazon_S3::S3_ACL_HEADER =>
Zend_Service_Amazon_S3::S3_ACL_PUBLIC_READ));
$data['picture'] = $_FILES["fuPic"]['name'];
}
if (isset($_FILES["fuPre"]))
{
$filename = $campaignModel->getCampaignId().'_pre_'.$_FILES["fuPre"]['name'];
$s3->putObject($bucketName."/[folder]/".$filename, file_get_contents($_FILES["fuPre"]["tmp_name"]),
array(Zend_Service_Amazon_S3::S3_ACL_HEADER =>
Zend_Service_Amazon_S3::S3_ACL_PUBLIC_READ));
$data['preview'] = $_FILES["fuPre"]['name'];
}
}
}
Happy Coding !!!!
this is my third question so far on stackoverflow :D
i am defining files and their location on my first_run.php files,
the files that i define here is those files containing classes, helper functions
and any other files required
at early development, this first_run.php contains only a few lines of codes
but the line is increasing gradually as i add some new classes or new files to be included
and since i group the file's location inside a particular folder, i figure that maybe i can scan the folder, put the name of the files retrieved into an array and then loop the require_once, so that i dont have to edit first_run.php every time i add a new file inside the folder.
my fisrt approach is using scandir()
before:
defined('INC_PATH') ? null : define('INC_PATH', SITE_ROOT.DS.'includes');
defined('MEMBERS') ? null : define('MEMBERS', INC_PATH.DS.'models'.DS.'members');
require_once(MEMBERS.DS.'member.php');
require_once(MEMBERS.DS.'phone.php');
require_once(MEMBERS.DS.'profile.php');
require_once(MEMBERS.DS.'talent.php');
require_once(MEMBERS.DS.'profile_picture.php');
require_once(MEMBERS.DS.'audio.php');
require_once(MEMBERS.DS.'video.php');
require_once(MEMBERS.DS.'gallery.php');
require_once(MEMBERS.DS.'statistik.php');
require_once(MEMBERS.DS.'inbox.php');
require_once(MEMBERS.DS.'comment.php');
require_once(MEMBERS.DS.'picked_stat.php');
require_once(MEMBERS.DS.'log.php');
after is something like:
$member_files = scandir(MEMBERS);
foreach($member_files as $member_file):
require_once(MEMBERS.DS.$member_file);
endforeach;
i havent try the 'after' code though.
is this possible?? or is there any other approach?? or should i just leave it that way (keep adding the lines without scanning the files)
thanks in advance
Consider using Autoloading instead.
With autoloading, you do not have to bother with including files at all. Whenever you instantiate a new class that is not known to PHP at that point, PHP will trigger the registered autoload function. The function includes the required files then. This way, you only load what you need when you need it, which should increase performance.
Simple example with PHP5.3
spl_autoload_register(function($className) {
include "/path/to/lib/and/$className.php";
});
$foo = new Foo;
When you call new Foo, the registered autoload function will try to include the class from /path/to/lib/and/Foo.php. It is advisable to use a classname convention, like f.i. PEAR, to make finding files easier and to cut down on the amount of include_paths.
For additional security and speed, you can provide a more sophisticated Autoloader that uses an array to map from classname to filename, thus making sure only files that actually are part of your application can get included.
Further reading:
http://weierophinney.net/matthew/archives/245-Autoloading-Benchmarks.html
It's possible, but not recommended, like what if somebody could create a php file on that directory, you'll end up including it, besides, you can't predict the inclusion order.
Try this instead:
$includes=array(
'member',
'phone',
'profile',
'talent',
);
foreach($includes as $fname) {
require_once(MEMBERS.DS.$fname. '.php');
}
If you were using classes, consider using autoloading, as #Gordon suggested. And if you werent using classes, consider using them :)
At a first glance your code could work, although you have to ignore "." and ".." in the foreach loop. Plus I'd check, if the file ends with ".php":
$member_files = scandir(MEMBERS.DS);
foreach($member_files as $member_file) {
// Ignore non php files and thus ".." & "."
if (!preg_match('/\.php$/', $member_file) {
continue;
}
require_once(MEMBERS.DS.$member_file);
}
create 2 functions
function GetFiles($directory,$exempt = array('.','..','.ds_store','.svn'),&$files = array()) {
$handle = opendir($directory);
while(false !== ($resource = readdir($handle))){
if(!in_array(strtolower($resource),$exempt)){
if(is_dir($directory.$resource.'/'))
array_merge($files, self::GetFiles($directory.$resource.'/',$exempt,$files));
else
$files[] = $directory.$resource;
}
}
closedir($handle);
return $files;
}
function Autoload($includes = array()){
$files = array();
foreach($includes as $directory)
$files[] = self::GetFiles($directory);
foreach($files as $k=>$v){
foreach($v as $k1=>$v1)
require_once($v1);
}
}
to use it:
$includes = array(
'C:WWW/project/helpers/',
'C:WWW/project/lang/',
'C:WWW/project/lib/',
'C:WWW/project/mod/',
);
Autoload($includes);