I want to create a Zend Controller for ACL management so my problem is: How can I get all Module names, Control names and Action names in a Zend application to build a ACL Control?
I use Zend_Navigation and if the resource don't exist in your ACL Zend_Navigation is thrown a exception. And I want to use a database to deny and allow access. So I must build the database first. And if I must do that by hand it's a pain to do that.
This may be an old question but this is how I am doing this...
// $front = Zend_Controller_Front::getInstance(); // use this line instead on a model class
$front = $this->getFrontController(); // this in controller
$acl = array();
foreach ($front->getControllerDirectory() as $module => $path) {
foreach (scandir($path) as $file) {
if (strstr($file, "Controller.php") !== false) {
include_once $path . DIRECTORY_SEPARATOR . $file;
$class = substr($file,0,strpos($file,".php"));
if (is_subclass_of($class, 'Zend_Controller_Action')) {
$controller = strtolower(substr($file, 0, strpos($file, "Controller")));
$methods = array();
foreach (get_class_methods($class) as $method) {
if (strstr($method,"Action") != false) {
array_push($methods,substr($method,0,strpos($method,"Action")));
}
}
}
$acl[$module][$controller] = $methods;
}
}
}
I have created a function that can get all the actions, controllers and modules from a zend application. Here it is:
$module_dir = substr(str_replace("\\","/",$this->getFrontController()->getModuleDirectory()),0,strrpos(str_replace("\\","/",$this->getFrontController()->getModuleDirectory()),'/'));
$temp = array_diff( scandir( $module_dir), Array( ".", "..", ".svn"));
$modules = array();
$controller_directorys = array();
foreach ($temp as $module) {
if (is_dir($module_dir . "/" . $module)) {
array_push($modules,$module);
array_push($controller_directorys, str_replace("\\","/",$this->getFrontController()->getControllerDirectory($module)));
}
}
foreach ($controller_directorys as $dir) {
foreach (scandir($dir) as $dirstructure) {
if (is_file($dir . "/" . $dirstructure)) {
if (strstr($dirstructure,"Controller.php") != false) {
include_once($dir . "/" . $dirstructure);
}
}
}
}
$default_module = $this->getFrontController()->getDefaultModule();
$db_structure = array();
foreach(get_declared_classes() as $c){
if(is_subclass_of($c, 'Zend_Controller_Action')){
$functions = array();
foreach (get_class_methods($c) as $f) {
if (strstr($f,"Action") != false) {
array_push($functions,substr($f,0,strpos($f,"Action")));
}
}
$c = strtolower(substr($c,0,strpos($c,"Controller")));
if (strstr($c,"_") != false) {
$db_structure[substr($c,0,strpos($c,"_"))][substr($c,strpos($c,"_") + 1)] = $functions;
}else{
$db_structure[$default_module][$c] = $functions;
}
}
}
}
I actually found the best way to have an easily available reflection reference was to recursively tokenise the correct directories and then build an xml document as a result. Caching the xml document for speed and using xpath for retrieving the data.
The plugin builds the reflection xml and caches it for later. I've taken this code out of its original implementation, so its more to give you a feel rather than copy and paste.
Of course, a database works just as well here. But if you're trying to limit your queries per page, a cached xml doc works pretty well.
class My_Reflection_Plugin extends My_Controller_Plugin_Abstract
{
public function routeShutdown(Zend_Controller_Request_Abstract $request)
{
$cache = $this -> getCacheManager() -> getCache('general');
if (!$xml = $cache->load("Reflection"))
{
$paths = array(
PATH_APPLICATION . "/Core",
PATH_SITE . "/Project"
);
foreach ($paths as $path)
{
$this -> inspectDir($path);
}
$cache -> save($this->getReflectionXML(), "Reflection");
}
else
{
$this -> getReflectionXML($xml);
}
}
private function inspectDir($path)
{
$rdi = new RecursiveDirectoryIterator($path);
$rii = new RecursiveIteratorIterator($rdi);
$filtered = new My_Reflection_Filter($rii);
iterator_apply($filtered, array($this, 'process'), array($filtered));
}
private function process($it = false)
{
$this -> getReflectionXML() -> addItem($it -> current());
return true;
}
}
Tokenisation happens inside the filter:
class My_Reflection_Filter extends FilterIterator
{
public function accept()
{
$file = $this->getInnerIterator()->current();
// If we somehow have something other than an SplFileInfo object, just
// return false
if (!$file instanceof SplFileInfo) {
return false;
}
// If we have a directory, it's not a file, so return false
if (!$file->isFile()) {
return false;
}
// If not a PHP file, skip
if ($file->getBasename('.php') == $file->getBasename()) {
return false;
}
// Resource forks are no good either.
if (substr($file->getBaseName(), 0, 2) == '._')
{
return false;
}
$contents = file_get_contents($file->getRealPath());
$tokens = token_get_all($contents);
$file->className = NULL;
$file->classExtends = NULL;
$file->classImplements = array();
$last = null;
while (count($tokens) > 0)
{
$token = array_shift($tokens);
if (!is_array($token))
{
continue;
}
list($id, $content, $line) = $token;
switch ($id)
{
case T_ABSTRACT:
case T_CLASS:
case T_INTERFACE:
$last = 'object';
break;
case T_EXTENDS:
$last = "extends";
break;
case T_IMPLEMENTS:
$last = "implements";
break;
case T_STRING:
switch ($last)
{
case "object":
$file -> className = $content;
break;
case "extends":
$file -> classExtends = $content;
break;
case "implements":
$file -> classImplements[] = $content;
break;
}
break;
case T_WHITESPACE:
// Do nothing, whitespace should be ignored but it shouldnt reset $last.
break;
default:
// If its not directly following a keyword specified by $last, reset last to nothing.
$last = null;
break;
}
}
return true;
}
}
Once you have your reflection xml populated with whatever information you need out of the class, your acl plugin can come after it and query that information with xpath.
I don't think there is a solution for this in Zend. You will have to do it yourself...
One way to do it, is to list all classes, and check if the classes extend (for example) the Zend_Controller_Action class...
check the php functions get_declared_classes and is_subclass_of
foreach(get_declared_classes() as $c){
if(is_subclass_of($c, 'Zend_Controller_Action')){
...
}
}
Related
I have folder structure like below in my web application.
How can I get the number of files in each folder recursively using php.
I'm working with Codeignaiter.
I haven't any idea for how to do this task.
I can't use scandir function because there are no physical
directory in path.Only files and folders save in database.
This is my database
Please any help needed.
Thank you.
Try this function by passing the path as a parameter in it:
function getFileCount($path) {
$size = 0;
$ignore = array('.','..','cgi-bin','.DS_Store');
$files = scandir($path);
foreach($files as $t) {
if(in_array($t, $ignore)) continue;
if (is_dir(rtrim($path, '/') . '/' . $t)) {
$size += getFileCount(rtrim($path, '/') . '/' . $t);
} else {
$size++;
}
}
return $size;
}
If you would like to get, say, all the *.php files in your project folder, recursively, you could use the following: Source
<?php
$Directory = new RecursiveDirectoryIterator('path/to/project/');
$Iterator = new RecursiveIteratorIterator($Directory);
$Regex = new RegexIterator($Iterator, '/^.+\.php$/i', RecursiveRegexIterator::GET_MATCH);
?>
$Regex will contain a single index array for each PHP file.
You can extend RecursiveArrayIterator to get kind of RecursiveDirectoryIterator but for your virtual filesystem:
class RecursiveVirtualDirectoryIterator extends RecursiveArrayIterator
{
private $files;
public function __construct($parentId, $array = [], $flags = 0)
{
$this->files = $array;
parent::__construct(
$this->getFilesByParentId($parentId),
$flags
);
}
private $children;
public function hasChildren()
{
$file = $this->current();
if ($file['is_file']) {
return false;
}
$this->children = $this->getFilesByParentId($file['id']);
return !empty($this->children);
}
private function getFilesByParentId($id)
{
return array_filter($this->files, function ($file) use ($id) {
return $file['parent_id'] === $id;
});
}
public function getChildren()
{
$file = $this->current();
return new static(
$file['id'],
$this->children,
$this->getFlags()
);
}
}
Then you can iterate over your array returned from databases using RecursiveIteratorIterator and count files for, say, topmost folders:
$iterator = new RecursiveIteratorIterator(
new RecursiveVirtualDirectoryIterator(0, $files),
RecursiveIteratorIterator::SELF_FIRST
);
$currentDirectoryName = null;
$filesCount = [];
foreach ($iterator as $file) {
if ($iterator->getDepth() === 0 && !$file['is_file']) {
$currentDirectoryName = $file['name'];
$filesCount[$currentDirectoryName] = 0;
continue;
}
$filesCount[$currentDirectoryName] += 1;
}
Here is working demo.
While Standard PHP Library (SPL) is poorly documented, it contains many useful things that save you from reinventing the wheel over and over again.
Is there any solution to download STANDARD-XML metadata from RETS using PHRETS?
Currently am able to extract each class metadata as an array using PHRETS function GetMetadataTable and combining & converting to XML format.
But then recently I found difference in single STANDARD-XML metadata(of entire resources and classes) and individual class metadata. Using metadata viewer service RETSMD.com(built on PHRETS) also, the class name getting from STANDARD-XML metadata is different and unable to view the details.
Note: I got the STANDARD-XML metadata via direct browser log-in using credentials, like this
http://rets.login.url/GetMetadata?Type=METADATA-TABLE&Format=STANDARD-XML&ID=0
Anyone faced the same? Is there any solution using PHP?
Thanks in Advance!
I got a solution by modifying PHRETS library.
Added a new function there with following code,
if (empty($this->capability_url['GetMetadata'])) {
die("GetServerInformation() called but unable to find GetMetadata location. Failed login?\n");
}
$optional_params['Type'] = 'METADATA-SYSTEM';
$optional_params['ID'] = '*';
$optional_params['Format'] = 'STANDARD-XML';
//request server information
$result = $this->RETSRequest($this->capability_url['GetMetadata'], $optional_params );
if (!$result) {
return false;
}
list($headers, $body) = $result;
$xml = $this->ParseXMLResponse($body);
Note: Main thing to note is,
$optional_params['ID'] = '*';
Should be '*' instead '0'
If anyone is still unable to retrieve STANDARD-XML data from the CREA DDF data feed using PhRETS v2.x.x, I created a fork to the ./src/Parsers/Search/OneX.php file. You can add the following protected methods to the end of the file:
protected function parseDDFStandardXMLData(&$xml)
{
// we can only work with an array
$property_details = json_decode(json_encode($xml), true);
$retn = array();
if(! empty($property_details['RETS-RESPONSE']['PropertyDetails'])) {
foreach($property_details['RETS-RESPONSE']['PropertyDetails'] as $property_array) {
$retn[] = $this->parseArrayElements(null, $property_array);
}
}
return $retn;
}
protected function parseArrayElements($parent_key, $element)
{
// three possible $element types
// 1. scalar value
// 2. sub-array
// 3. SimpleXMLElement Object
$retn = array();
if(is_object($element)) {
$element = json_decode(json_encode($element), true);
}
if(is_array($element)) {
foreach($element as $node_key => $node) {
$key = $node_key;
if(! empty($parent_key)) {
$key = $parent_key . '|' . $key;
}
if(is_array($node) || is_object($node)) {
$nodes = $this->parseArrayElements($key, $node);
if(!empty($nodes)) {
foreach($nodes as $k => $n) {
$retn[$k] = $n;
}
}
}else{
$retn[$key] = $node;
}
}
}else{
$retn[$parent_key] = $element;
}
return $retn;
}
protected function parseRecordFromArray(&$array, Results $rs)
{
$r = new Record;
foreach($rs->getHeaders() as $key => $name) {
$r->set($name, $array[$name]);
}
return $r;
}
Then replace the parseRecords() method with:
protected function parseRecords(Session $rets, &$xml, $parameters, Results $rs)
{
if (isset($xml->DATA)) {
foreach ($xml->DATA as $line) {
$rs->addRecord($this->parseRecordFromLine($rets, $xml, $parameters, $line, $rs));
}
}elseif (isset($xml->{"RETS-RESPONSE"}->PropertyDetails)) {
$data = $this->parseDDFStandardXMLData($xml);
if(! empty($data)) {
$fields_saved = false;
foreach ($data as $line) {
if(!$fields_saved) {
$rs->setHeaders(array_keys($line));
}
$rs->addRecord($this->parseRecordFromArray($line, $rs));
}
}
}
}
The line, }elseif (isset($xml->{"RETS-RESPONSE"}->PropertyDetails)) { in the latter method does the trick to identify the STANDARD-XML RETS-RESPONSE node and parse the data.
Hope this helps,
Cheers!
I am currently using this library https://github.com/php-poppler/php-poppler in addition to Poppler in order to convert PDF files to HTML. I installed the library via composer and unfortunately there is no documentation available except some guides about installation but still incomplete.
Given that this Main API usage:
$file = new Poppler\Process\PdfFile(...);
// Get pdf info
print_r($file->getInfo('test.pdf'));
// Get text content of pdf
echo $file->toText('test.pdf');
// Transform to html
$file->toHtml('test.pdf', '/path/for/html');
I can not even define what parameters should be given in $file = new Poppler\Process\PdfFile(...);
What I have tried:
<?php
include 'vendor/autoload.php';
use Poppler\Processor\PdfFile;
use Poppler\Driver\Pdfinfo;
use Poppler\Driver\Pdftohtml;
use Poppler\Driver\Pdftotext;
$a = new Pdfinfo;
$b = new Pdftohtml;
$c = new Pdftotext;
$file = new PdfFile($a,$b,$c);
print_r($file->getInfo('test.pdf'));
echo $file->toText('test.pdf');
$file->toHtml('test.pdf', 'Results');
?>
This gives an error:
Catchable fatal error: Argument 1 passed to Alchemy\BinaryDriver\AbstractBinary::__construct() must be an instance of Alchemy\BinaryDriver\ProcessBuilderFactoryInterface, none given
Here's PdfFile.php:
<?php
namespace Poppler\Processor;
use Poppler\Driver\Pdfinfo;
use Poppler\Driver\Pdftohtml;
use Poppler\Driver\Pdftotext;
use Poppler\Exception\FileNotFoundException;
class PdfFile
{
private $pdfinfo;
private $pdftotext;
private $pdftohtml;
public function __construct(Pdfinfo $pdfinfo, Pdftotext $pdftotext, Pdftohtml $pdftohtml)
{
$this->pdfinfo = $pdfinfo;
$this->pdftotext = $pdftotext;
$this->pdftohtml = $pdftohtml;
}
public function toText($inputfile, $toEncoding = 'UTF-8')
{
if (!file_exists($inputfile)) {
throw new FileNotFoundException("File $inputfile not found.");
}
$output = $this->pdftotext->command(array('-nopgbrk', $inputfile, '-'));
$fromEncoding = mb_detect_encoding($output);
if ($fromEncoding) {
return mb_convert_encoding($output, $toEncoding, $fromEncoding);
}
return mb_convert_encoding($output, $toEncoding);
}
public function toHtml($inputfile, $outputfile)
{
if (!file_exists($inputfile)) {
throw new FileNotFoundException("File $inputfile not found.");
}
$output = $this->pdftohtml->command(array($inputfile, $outputfile));
return $output;
}
public function getInfo($inputfile)
{
if (!file_exists($inputfile)) {
throw new FileNotFoundException("File $inputfile not found.");
}
$args = array($inputfile);
$output = $this->pdfinfo->command($args);
$info = array();
foreach (explode(PHP_EOL, $output) as $line) {
if (strpos($line, ': ') === false) {
continue;
}
$parts = explode(': ', $line);
$key = trim($parts[0]);
$value = trim($parts[1]);
$info[$key] = $value;
}
return $info;
}
}
I've been working on a method of including files recursively with the __autoload() function in php. This way, You could throw your class anywhere in the "classes" folder, and have them organized by sub directory, But the __autoload function would still be able to find them. This is what I've gotten so far, And was wondering if anyone might be able to help me simplify it so that it isn't so lengthy. It is completely functional currently, And works like a charm. I'm just trying to make it shorter.
<?php
function readRecursive($path){
if(!is_dir($path)){
return false;
}
$dir = glob($path ."/*");
$retArr = array();
foreach($dir as $f){
if(is_dir($f)){
$m = readRecursive($f);
foreach($m as $n){
$retArr[] = $n;
}
}else{
$retArr[] = $f;
}
}
return $retArr;
}
function endsWith($haystack, $needle){
return $needle === "" || substr($haystack, -strlen($needle)) === $needle;
}
/* Set up AutoLoading for object classes */
function __autoload($class_name){
$classes = readRecursive("classes");
foreach($classes as $class){
if(endsWith(strtolower($class), strtolower($class_name.".class.php"))){
include_once ($class);
}
}
}
?>
Here's my attempt at this autoload for you.
I've slightly modified Emil Condrea's Answer.
To start, I'll show you the file structure of my classes:
As you can see above, the classes are set into seperate files and such to show.
Now taking Emil's answer and slightly changing it:
(Provided the file name is something like "Class.php" as seen above in the file structure)
function getClasses($path) {
$files = array();
$dir_iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST);
foreach ($dir_iterator as $item) {
$pathname = $item->getPathName();
$filename = $item->getFileName();
if ($item->isDir()) {
getClasses($item);
} else {
$files[$filename] = $pathname;
}
}
return $files;
}
Will warrant a return array of files like the following [FILE_NAME] => [PATH_NAME]:
Array
(
[Error.php] => /home2/DERP/public_html/something.com/watch/model/Site/Error.php
[Form.php] => /home2/DERP/public_html/something.com/watch/model/Site/Form.php
[Site.php] => /home2/DERP/public_html/something.com/watch/model/Site/Site.php
[Db.php] => /home2/DERP/public_html/something.com/watch/model/Database/Db.php
[Db_pdo.php] => /home2/DERP/public_html/something.com/watch/model/Database/Db_pdo.php
[Session.php] => /home2/DERP/public_html/something.com/watch/model/Security/Session.php
[Auth.php] => /home2/DERP/public_html/something.com/watch/model/Security/Auth.php
[Input.php] => /home2/DERP/public_html/something.com/watch/model/Security/Input.php
[Postcode.php] => /home2/DERP/public_html/something.com/watch/model/Postcode.php
[Rep.php] => /home2/DERP/public_html/something.com/watch/model/User/Rep.php
[User.php] => /home2/DERP/public_html/something.com/watch/model/User/User.php
[Notifications.php] => /home2/DERP/public_html/something.com/watch/model/User/Notifications.php
[Log.php] => /home2/DERP/public_html/something.com/watch/model/Log/Log.php
[Hook.php] => /home2/DERP/public_html/something.com/watch/model/Hook.php
)
Now that would've been called by something like the following:
getClasses(realpath(dirname(__FILE__)) . '/model')
Allowing us to run an __autoload() like the following:
$model_classes = getClasses(realpath(dirname(__FILE__)) . '/model');
function __autoload($class_name) {
global $model_classes;
$filename = ucfirst($class_name) . '.php';
$model = $filename;
if (!isset($model_classes[$model])) {
// dead
return false;
} else {
// include first file (model)
include($model_classes[$model]);
}
}
Now
Obviously you shouldn't use global but to me it seemed a far better alternative to running the getClasses() function every single time within the __autoload() function.
If someone else has anything to add, feel free! I just tried my own little method on this and it works without fault!
Note: I was using file_exists() before and the above method is, in my opinion; a lot faster.
UPDATE
I had a brain wave just the other night and thought;
"Why not scan the application root and fetch all the php files then run a function to check if said file actually contains a class to make this as universal as possible.."
So I did a little research and found this nifty little function from php: token_get_all()
Now after a little digging through SO I found this answer: Determine class in file...
and after some modification, the getClasses() function now looks like this:
function getClasses($path) {
$files = array();
$dir_iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST);
foreach ($dir_iterator as $item) {
$pathname = $item->getPathName();
$filename = $item->getFileName();
if ($item->isDir()) {
get_classes($item);
} else {
if (substr($filename, -4) === '.php') {
if (get_php_classes(file_get_contents($pathname))) {
$files[$filename] = $pathname;
}
}
}
}
return $files;
}
With the addition of this new function from the above question:
function get_php_classes($php_code) {
$tokens = token_get_all($php_code);
$class_token = false;
foreach ($tokens as $token) {
if (is_array($token)) {
if ($token[0] == T_CLASS) {
$class_token = true;
} else if ($class_token && $token[0] == T_STRING) {
$classes[] = $token[1];
// $class_token = false;
}
}
}
return $class_token;
}
Now that allows you to simply run a $classes = getClasses(ROOTPATH) and itterate through them.
DOWNFALL: each of the classes will have to have unique class names and/or file names. Unless somebody could lend their hand at a modification to allow.
You can use
RecursiveDirectoryIterator to iterate through the directory recursively. This might simplify your function.
function getRecursive($path){
$files = array();
$dir_iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS),RecursiveIteratorIterator::SELF_FIRST);
foreach ($dir_iterator as $item) {
$subPath = $dir_iterator->getSubPathName();
if($item->isDir())
$files[$subPath] = array();
else
$files[$subPath][] = $subPath;
}
return $files;
}
i am actually working on this mentioned title. player show the list, its generating perfectly. but i am no where to make it actually play that file. i must be wrong some where.
i need advise fox. (ahh if i can attach the files.)
my class
class DecodDir
{
function getFiles($directory)
{
$all_files = array();
$handler = opendir($directory);
while($files=readdir($handler))
{
if($files!="." && $files!="..")
{
$all_files[]= $files;
}
}
closedir($handler);
return $all_files;
}
}
################# file where i am using this class *###############
<?php
include("decoddir.php");
$obj = new DecodDir();
$results = $obj->getFiles("mp3");
$total = count($results);
$string = "";
for($i=0; $i<$total; $i++){
$string .="
{
name:'$results[$i]',
mp3:'mp3/$results[$i]',
ogg:'$results[$i]'
},
";
}
?>
// its at the top of that html file (ofcorse with the php ext)
and below, this is where it is generating the playlist
var audioPlaylist = new Playlist("2", [
<?php echo $string; ?>
],
http://www.jplayer.org/latest/demo-02/ (the link from where i get jplayer) you can see the audio player with playlist.
(actually i don't know hot format the code in here stackoverflow)
thanks
Rafay
I have taken the liberty of re-factoring the code a bit for you. I don't know exactly what you are trying to do, but it will help to have the beginnings of a better class on your side.
<?php
class DecodDir
{
private
$directory,
$files;
public function __construct( $directory = null )
{
if ( ! is_null($directory) )
{
$this->setDirectory( $directory );
}
}
public function setDirectory( $directory )
{
$this->directory = $directory;
$this->files = null;
// TODO put some validation in here;
return $this;
}
public function getDirectory()
{
if ( is_null($this->directory) )
{
$this->directory = './';
}
return $this->directory;
}
private function getFiles()
{
if ( is_null($this->files) )
{
$this->files = array();
$handler = opendir( $this->getDirectory() );
while($files=readdir($handler))
{
if($files!="." && $files!="..")
{
$this->files[]= $files;
}
}
closedir($handler);
}
return $this->files;
}
public function getJson()
{
$list = array();
foreach ( $this->getFiles() as $filename )
{
$item = new stdClass();
$item->name = $filename;
$item->mp3 = "mp3/{$filename}";
$item->ogg = $filename;
$list[] = $item;
}
$json = json_encode( $list );
return $json;
}
public function countFiles()
{
return sizeof( $this->getFiles() );
}
}
$obj = new DecodDir( 'mp3' );
echo $obj->getJson();
I wrote the code at the following site to do what you are trying to do, I think:
http://jplaylister.yaheard.us/
Sadly, it doesn't currently collapse a song stored in multiple formats (mysong1.mp3, mysong1.ogg) into one playlist item, but otherwise it is pretty feature-complete and has lots of customizable options.
Hope this helps!