How to reduce code duplication - php

As I'm not familiar with PHP I wonde how do I reduce code duplication here? Both methods are doing exactly the same thing here... except the part where the string is extracted (filemtime and basename) and joined.
private function modified_hash( $files ) {
$joined = "";
foreach ( $files as $file ) {
$joined .= filemtime( $file );
}
return $this->checksum( $joined );
}
private function filename_hash( $files ) {
$joined = "";
foreach ( $files as $file ) {
$joined .= basename( $file );
}
return $this->checksum( $joined );
}

Instead of two functions, declare a unified function with an argument for a crucial callback/function name $func_name:
/**
* Gets joined files hash
*
* #param $files an array of file paths
* #param $func_name callback name
* #return mixed
*/
private function getFilesHash($files, callable $func_name) {
$joined = "";
foreach ($files as $file) {
$joined .= call_user_func($func_name, $file);
}
return $this->checksum($joined);
}
Usage:
$fileHash = getFilesHash($files, 'basename');
Used functions:
call_user_func

I guess my version is way bigger then Romans, but as an OOP problem I think this is a possible solution too:
<?php
interface HashInterface
{
public function hash();
}
class ModifiedHash implements HashInterface
{
public function hash($file)
{
return filemtime($file);
}
}
class FileNameHash implements HashInterface
{
public function hash($file)
{
return basename($file);
}
}
class SomeClient
{
private $hashType;
public function setHashType(HashInterface $hashType)
{
$this->hashType = $hashType;
}
private function doHash( $files ) {
$joined = "";
foreach ( $files as $file ) {
$joined .= $this->hashType->hash( $file );
}
return $this->checksum( $joined );
}
}
$client = new SomeClient();
$files = ???;
// Want a ModifiedHash?
$client->setHashType(new ModifiedHash());
$data = $client->doHash($files);
// Want a FileNameHash?
$client->setHashType(new FileNameHash());
$data = $client->doHash($files);
Sorry for the confusing class or method names. I hope you got the idea.

Related

How to get the number of files in the folder recursive in php

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.

Array return at the end one value

what is the best way to put parents value in the same array ( Concate ) as this code just return one value
public function divisionParent($name)
{
$path = array();
$path[] = $name;
$div = CsiCategory::where('name', $name)->first();
$parent_id = $div->parent_id;
if ($parent_id != 0) {
$name = CsiCategory::where('id', $parent_id)->first();
$this->divisionParent($name->name);
}
return $path;
}
Something like this maybe?
public function divisionParent($name, $path = [])
{
// Append to $path arr.
array_push($path, $name);
// Get division by name.
$div = CsiCategory::where('name', $name)->first();
// Check for existing parent division by id.
if (isset($div)) {
if ($div->parent_id != 0) {
$divParent = CsiCategory::where('id', $div->parent_id)->first();
}
}
return isset($divParent) ? $this->divisionParent($divParent->name, $path) : $path;
}

Reading files from multiple directions

I am trying to make a script that reads .sql files from multiple directories in a certain folder, and then to execute them into the database.
How would I make my code below able to read multiple folders. [These folders will be added/removed in future so I need it to just automatic find any folder without any partciular names.
So for example I have a folder called "mysqls" and inside that folder I have other folders such as 'text1', 'test2'. Test1 and test2 have different .sql files in that I want to be excuted.
<?php
$dirf = 'mysqls';
$dir = scandir($dirf);
unset($dir['0']);
unset($dir['1']);
foreach($dir as $file) {
$sql = file_get_contents($dirf . '/'. $file);
$qr = $dbh->exec($sql);
}
?>
$directory = new RecursiveDirectoryIterator('mysqls');
// Filter out "dot" folders
$filter = new DirnameFilter($directory, '/^(?!\.)/');
// Filter sql files
$filter = new FilenameFilter($filter, '/(?:sql)$/i');
foreach(new RecursiveIteratorIterator($filter) as $file) {
$sql = file_get_contents($file);
$qr = $dbh->exec($sql);
}
abstract class FilesystemRegexFilter extends RecursiveRegexIterator {
protected $regex;
public function __construct(RecursiveIterator $it, $regex) {
$this->regex = $regex;
parent::__construct($it, $regex);
}
}
class FilenameFilter extends FilesystemRegexFilter {
// Filter files against the regex
public function accept() {
return ( ! $this->isFile() || preg_match($this->regex, $this->getFilename()));
}
}
class DirnameFilter extends FilesystemRegexFilter {
// Filter directories against the regex
public function accept() {
return ( ! $this->isDir() || preg_match($this->regex, $this->getFilename()));
}
}
Perhaps something like this using RecursiveIteratorIterator()
<?php
function scan_for_sql_files($path){
$itdir = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS),
\RecursiveIteratorIterator::SELF_FIRST,
\RecursiveIteratorIterator::CATCH_GET_CHILD);
$files = array();
foreach ($itdir as $path=>$dir){
if ($dir->isFile()){
$ext = substr(strtolower($path), -3);
if($ext == 'sql'){
$files[] = array('path' => $path);
}
}
}
return $files;
}
//get files
$files = scan_for_sql_files('mysqls');
//Do sql
foreach($files as $file){
$sql = file_get_contents($file['path']);
$qr = $dbh->exec($sql);
}
?>
$iterator = new RecursiveDirectoryIterator('mysqls');
foreach($iterator as $fileinfo){
if('sql' == $fileinfo->getExtension()){
$sql = file_get_contents($fileinfo->getPathname());
$qr = $dbh->exec($sql);
}
}

CakePhp: Url based internationalization

I've a small problem with my internationalization:
I want to have some url looking like this: http://mywebsite/eng/controller/action/params...
I found this http://nuts-and-bolts-of-cakephp.com/2008/11/28/cakephp-url-based-language-switching-for-i18n-and-l10n-internationalization-and-localization/
This is working nice most of time. But I've one case where this hasn't the expected result.
When I'm using $this->Html->link with named parameters, I don't get my nice structure, but something like http://mywebsite/controller/action/paramX:aaa/paramxY:bbb/language:eng
I think this is a routing problem, but I can't figure what is going wrong?
Thank you very much
This is because cakephp doens't find a route in routes.php that corresponds to this link. In other words, you'll have to define this route in the routes.php file
Router::connect('/:language/:controller/:action/:paramX/:paramY');
Once this set, $this->Html->link will output a nice url
I finally did this:
I created a custom CakeRoute, in this cakeRoute, I override the "match" url and the _writeUrl method.
Now every thing is working like a charm :)
For those which are interessted by the route class:
<?php
class I18nRoute extends CakeRoute {
/**
* Constructor for a Route
* Add a regex condition on the lang param to be sure it matches the available langs
*
* #param string $template Template string with parameter placeholders
* #param array $defaults Array of defaults for the route.
* #param string $params Array of parameters and additional options for the Route
* #return void
* #access public
*/
public function __construct($template, $defaults = array(), $options = array()) {
//$defaults['language'] = Configure::read('Config.language');
$options = array_merge((array)$options, array(
'language' => join('|', Configure::read('Config.languages'))
));
parent::__construct($template, $defaults, $options);
}
/**
* Attempt to match a url array. If the url matches the route parameters + settings, then
* return a generated string url. If the url doesn't match the route parameters false will be returned.
* This method handles the reverse routing or conversion of url arrays into string urls.
*
* #param array $url An array of parameters to check matching with.
* #return mixed Either a string url for the parameters if they match or false.
* #access public
*/
public function match($url) {
if (empty($url['language'])) {
$url['language'] = Configure::read('Config.language');
}
if (!$this->compiled()) {
$this->compile();
}
$defaults = $this->defaults;
if (isset($defaults['prefix'])) {
$url['prefix'] = $defaults['prefix'];
}
//check that all the key names are in the url
$keyNames = array_flip($this->keys);
if (array_intersect_key($keyNames, $url) != $keyNames) {
return false;
}
$diffUnfiltered = Set::diff($url, $defaults);
$diff = array();
foreach ($diffUnfiltered as $key => $var) {
if ($var === 0 || $var === '0' || !empty($var)) {
$diff[$key] = $var;
}
}
//if a not a greedy route, no extra params are allowed.
if (!$this->_greedy && array_diff_key($diff, $keyNames) != array()) {
return false;
}
//remove defaults that are also keys. They can cause match failures
foreach ($this->keys as $key) {
unset($defaults[$key]);
}
$filteredDefaults = array_filter($defaults);
//if the difference between the url diff and defaults contains keys from defaults its not a match
if (array_intersect_key($filteredDefaults, $diffUnfiltered) !== array()) {
return false;
}
$passedArgsAndParams = array_diff_key($diff, $filteredDefaults, $keyNames);
list($named, $params) = Router::getNamedElements($passedArgsAndParams, $url['controller'], $url['action']);
//remove any pass params, they have numeric indexes, skip any params that are in the defaults
$pass = array();
$i = 0;
while (isset($url[$i])) {
if (!isset($diff[$i])) {
$i++;
continue;
}
$pass[] = $url[$i];
unset($url[$i], $params[$i]);
$i++;
}
/*
//still some left over parameters that weren't named or passed args, bail.
//We don't want this behavior, we use most of args for the matching, and if we have more, we just allow them as parameters
if (!empty($params)) {
return false;
}*/
//check patterns for routed params
if (!empty($this->options)) {
foreach ($this->options as $key => $pattern) {
if (array_key_exists($key, $url) && !preg_match('#^' . $pattern . '$#', $url[$key])) {
return false;
}
}
}
return $this->_writeUrl(array_merge($url, compact('pass', 'named')));
}
function _writeUrl($params) {
if (isset($params['prefix'], $params['action'])) {
$params['action'] = str_replace($params['prefix'] . '_', '', $params['action']);
unset($params['prefix']);
}
if (is_array($params['pass'])) {
$params['pass'] = implode('/', $params['pass']);
}
$instance =& Router::getInstance();
$separator = $instance->named['separator'];
if (!empty($params['named']) && is_array($params['named'])) {
$named = array();
foreach ($params['named'] as $key => $value) {
$named[] = $key . $separator . $value;
}
$params['pass'] = $params['pass'] . '/' . implode('/', $named);
}
$out = $this->template;
$search = $replace = array();
foreach ($this->keys as $key) {
$string = null;
if (isset($params[$key])) {
$string = $params[$key];
} elseif (strpos($out, $key) != strlen($out) - strlen($key)) {
$key .= '/';
}
$search[] = ':' . $key;
$replace[] = $string;
}
$out = str_replace($search, $replace, $out);
if (strpos($this->template, '*')) {
$out = str_replace('*', $params['pass'], $out);
}
$out = str_replace('//', '/', $out);
//Modified part: allows us to print unused parameters
foreach($params as $key => $value){
$found = false;
foreach($replace as $repValue){
if($value==$repValue){
$found=true;
break;
}
}
if(!$found && !empty($value)){
$out.="/$key:$value";
}
}
return $out;
}
}
And you can set the route like this:
Router::connect('/:language/:controller/*', array(), array('routeClass' => 'I18nRoute'));

Generating playlist for jplayer in php

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!

Categories