Symfony parsing yaml files from subfolders - php

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);
}

Related

Find file inside phar with symfony finder

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.

How to use the PHP_CodeCoverage library directly?

I'm trying to get the PHP_CodeCoverage library working for the simplest possible case to create an HTML code coverage report, and failing. I have PHP and Xdebug installed. I would rather not specify the versions of those that I am using because I'm hoping there is something simple I am overlooking, but I will provide those details upon request. For now, suffice to say, I am using very new versions of them.
To make this as simple as possible, I distilled it down to 2 files and I still can't get it to do what I want. The 2 files are the automatically generated vendor/autoload.php created by composer when installing the coverage library, and the file that is using the library. This file is at project-root/src/CoverageTest.php. The autoload file is at project-root/vendor/autoload.php.
When I run php src/CoverageTest.php from the terminal, it does generate an HTML report. When I view the report, inside the Code Coverage table, there is only a single row with everything set to "n/a" or "0/0". It does not report any coverage as existing or missing. It doesn't say anything about specific files, classes, functions, or lines. To a large extent the code you see in this test file is the same as what appears on their README page. Here is the code:
<?php
require __DIR__.'/../vendor/autoload.php';
$coverage = new \SebastianBergmann\CodeCoverage\CodeCoverage;
$coverage->start('<name of test>');
class MathGenius
{
public function add($first, $second)
{
return $first + $second;
}
}
$guru = new MathGenius();
$sum = $guru->add(1, 1);
$coverage->stop();
$writer = new \SebastianBergmann\CodeCoverage\Report\Html\Facade;
$writer->process($coverage, __DIR__.'/../code-coverage-report');
It only reports on whitelisted files and directories. This is how I got the example I posted to work. Before creating the coverage object, I created a filter object, like so:
$filter = new \SebastianBergmann\CodeCoverage\Filter();
Since that example was only attempting to cover itself, I whitelisted that file with the filter like this:
$filter->addFileToWhitelist(__FILE__);
Then when creating the coverage object, you pass in the filter like so:
$coverage = new \SebastianBergmann\CodeCoverage\CodeCoverage(null, $filter);
Here is the full code for the fixed version of the code from the question:
<?php
require __DIR__.'/../vendor/autoload.php';
$filter = new \SebastianBergmann\CodeCoverage\Filter();
$filter->addFileToWhitelist(__FILE__);
$coverage = new \SebastianBergmann\CodeCoverage\CodeCoverage(null, $filter);
$coverage->start('<name of test>');
class MathGenius
{
public function add($first, $second)
{
return $first + $second;
}
}
$guru = new MathGenius();
$sum = $guru->add(1, 1);
$coverage->stop();
$writer = new \SebastianBergmann\CodeCoverage\Report\Html\Facade;
$writer->process($coverage, __DIR__.'/../code-coverage-report');

Packaging a PHP application

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

Placement of a function gives unknown problems

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.

using PHP.scandir() to scan files and then require_once them

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);

Categories