I am working on a php site that needs to search a set of files with any combination of search fields.
The possible search fields are
id, year, building, lastname, firstname, birthdate
The folder structure and file names are as such
/year/building/file.pdf
The filenames contain the data to search
id_lastname_firstname_MM_dd_yy.pdf
I have everything working on the site except this part. Originally I only had ID, year, and building and I was able to do if's to check for every possibility of combinations. Now there is way more combinations so it much more complex.
I was thinking nested if and in_array or such, but there has to be a better way. I just learning my way around php.
I would like to be able to search with any combination of fields. I can change the filenames if it helps.
I started with something like this
function search($transcripts, $studentid=null, $year=null, $building=null, $last=null, $first=null, $birthdate=null){
$ext = '.pdf';
date_default_timezone_set('America/Los_Angeles');
$dir_iterator = new RecursiveDirectoryIterator("../transcripts");
$iterator = new RecursiveIteratorIterator($dir_iterator, RecursiveIteratorIterator::SELF_FIRST);
foreach ($iterator as $file) {
if ($file->isFile()){
$path = explode('\\',$file->getPath());
$fname = explode('_', $file->getBasename($ext));
if($path[1] == $year){
if($path[2] == $building){
if(in_array($last, $fname, true)){
if((in_array($first, $fname, true)){
if((in_array($birthdate
Originally I had seperate functions depending on which fields where filed in.
function bldStuSearch($building, $studentid, $transcripts){
$ext = '.pdf';
date_default_timezone_set('America/Los_Angeles');
$dir_iterator = new RecursiveDirectoryIterator("../transcripts");
$iterator = new RecursiveIteratorIterator($dir_iterator, RecursiveIteratorIterator::SELF_FIRST);
foreach ($iterator as $file) {
$results = explode('\\',$file->getPath());
//var_dump($results);
if (($file->isFile()) && ($file->getBasename($ext)==$studentid) && ($results[2] == $building)){
//echo substr($file->getPathname(), 27) . ": " . $file->getSize() . " B; modified " . date("Y-m-d", $file->getMTime()) . "\n";
$results = explode('\\',$file->getPath());
//var_dump($results);
//$building = $results[2];
$year = $results[1];
//$studentid = $file->getBasename($ext);
array_push($transcripts, array($year, $building, $studentid));
//var_dump($transcripts);
//$size += $file->getSize();
//echo '<br>';
}
}
//echo "\nTotal file size: ", $size, " bytes\n";
if (empty($transcripts))
{
header('Location: index.php?error=2'); exit();
}
return $transcripts;
}
Now I am trying to have one search function to check for any combination? Any idea that would at least put in the right direction?
Thanks.
So I had an idea about doing a scoring system but then dismissed it. I came back to it and found a way to make it work using a weighted scoring system.
This allows the search to be super flexible and maintain being portable, not requiring a database for the metadata and using the filename as the search data without having to search each PDF. I am using A-Pdf splitter to split the PDF into separate files and add the metadata to the filename.
I hope someone some day finds this useful for other searches. I am really happy the way this turned out.
I will post the entire code when I am done on http://github.com/friedcircuits
One thing I should change is to use named keys for the arrays.
Here is the resulting code. Right now the birthdate has to be entered as m-d-yyyy to match.
function search($transcripts, $studentid=null, $year=null, $building=null, $last=null, $first=null, $birthdate=null){
$ext = '.pdf';
$bldSearch = false;
date_default_timezone_set('America/Los_Angeles');
if (($building == null) AND ($year == null)){ $searchLocation = "../transcripts";}
elseif (($year != null) AND ($building != null)){$searchLocation = "../transcripts/".$year."/".$building;}
elseif ($year != null) {$searchLocation = "../transcripts/".$year;}
elseif ($building != null) {
$searchLocation = "../transcripts/";
$bldSearch = true;
}
else{$searchLocation = "../transcripts";}
$dir_iterator = new RecursiveDirectoryIterator($searchLocation);
$iterator = new RecursiveIteratorIterator($dir_iterator, RecursiveIteratorIterator::SELF_FIRST);
$score = 0;
foreach ($iterator as $file) {
if ($file->isFile()){
//Fix for slashes changing direction depending on search path
$path = str_replace('/','\\', $file->getPath());
$path = explode('\\',$path);
$fname = explode('_', $file->getBasename($ext));
//var_dump($path);
//echo "<br>";
//var_dump($fname);
//echo "<br>";
//fix for different search paths
if($path[1] == "transcripts"){
$pYear = $path[2];
$pbuilding = $path[3];
}
else{
$pYear = $path[1];
$pbuilding = $path[2];
}
if ($bldSearch == true){
if ($building != $pbuilding) {continue;}
}
//$fname[1] = #strtolower($fname[1]);
//$fname[2] = #strtolower($fname[2]);
if($fname[0] == $studentid){
$yearS = $pYear;
$buildingS = $pbuilding;
$studentidS = $fname[0];
$lastS = $fname[1];
$firstS = $fname[2];
$birthdateS = $fname[3];
array_push($transcripts, array($yearS, $buildingS, $studentidS, $lastS, $firstS, $birthdateS));
continue;
}
if($pYear == $year){
$score += 1;
}
if($path[2] == $building){
$score += 1;
}
if(#strpos(#strtolower($fname[1]),$last) !== false){
$score += 3;
}
if(#strpos(strtolower($fname[2]), $first) !== false){
$score += 3;
}
if($fname[3] == $birthdate){
$score += 3;
}
//echo $score." ";
if ($score > 2) {
$yearS = $pYear;
$buildingS = $pbuilding;
$studentidS = $fname[0];
$lastS = $fname[1];
$firstS = $fname[2];
$birthdateS = $fname[3];
array_push($transcripts, array($yearS, $buildingS, $studentidS, $lastS, $firstS, $birthdateS));
//var_dump($transcripts);
}
}
$score = 0;
}
if (empty($transcripts))
{
header('Location: index.php?error=2'); exit();
}
return $transcripts;}
Related
Im trying to get the number of class and methods in a specific directory which contain sub folder and scan through them. So far I can only count the number of files.
$ite=new RecursiveDirectoryIterator("scanME");
//keyword search
$classWords = array('class');
$functionWords = array('function');
//Global Counts
$bytestotal=0;
$nbfiles=0;
$classCount = 0;
$methodCount = 0;
foreach (new RecursiveIteratorIterator($ite) as $filename=>$cur) {
$filesize=$cur->getSize();
$bytestotal+=$filesize;
if(is_file($cur))
{
$nbfiles++;
foreach ($classWords as $classWord) {
$fileContents = file_get_contents($cur);
$place = strpos($fileContents, $classWord);
if (!empty($place)) {
$classCount++;
}
}
foreach($functionWords as $functionWord) {
$fileContents = file_get_contents($cur);
$place = strpos($fileContents, $functionWord);
if (!empty($place)) {
$methodCount++;
}
}
}
}
EDIT: I manage to count the keyword class and function but the problem is it only concatenate for each file. Eg: I have 2 class in one file it will just count 1. How do I count for each keyword in a file?
The only time you define $classContents is at the top where you're attempting to get the contents of the directory:
$classContents = file_get_contents('scanMeDir');
You should be getting the contents of each file while looping through the RecursiveDirectoryIterator results. (You also don't need to create a new iterator instance):
foreach ($ite as $filename => $cur) {
$classContents = file_get_contents($filename);
...
}
using token instead of keyword is the better solution for this
$bytestotal=0;
$nbfiles=0;
$fileToString;
$token;
$pathInfo;
$classCount = 0;
$methodCount = 0;
foreach (new RecursiveIteratorIterator($ite) as $filename=>$cur) {
$filesize=$cur->getSize();
$bytestotal+=$filesize;
if(is_file($cur))
{
$nbfiles++;
$fileToString = file_get_contents($cur);
$token = token_get_all($fileToString);
$tokenCount = count($token);
//Class Count
$pathInfo = pathinfo($cur);
if ($pathInfo['extension'] === 'php') {
for ($i = 2; $i < $tokenCount; $i++) {
if ($token[$i-2][0] === T_CLASS && $token[$i-1][0] === T_WHITESPACE && $token[$i][0] === T_STRING ) {
$classCount++;
}
}
} else {
error_reporting(E_ALL & ~E_NOTICE);
}
//Method Count
for ($i = 2; $i < $tokenCount; $i++) {
if ($token[$i-2][0] === T_FUNCTION && $token[$i-1][0] === T_WHITESPACE && $token[$i][0] === T_STRING) {
$methodCount++;
}
}
}
}
I have a PHP code that is supposed to save values fro a CSV file into a MySql database table. Everything works fine except that only the first row of the CSV is added. Here's the code:
<?php
public function saveProductsFromCsv($productId, $val) {
$productId = (int) $productId;
$data = array();
$fieldNames = $this->_config->getCsvColumnNames();
array_shift($fieldNames);
$numberOfFields = count($fieldNames);
$lines = explode("\n", $val);
foreach ($lines as $line) {
$line = trim($line);
if (empty($line))
continue;
$values = explode(',', $line);
if (count($values) != $numberOfFields){
throw new Exception();
return;
}
$make = trim($values[0]);
$model = trim($values[1]);
$yearFrom = (int) $values[2];
$yearTo = (int) $values[3];
$engine = trim($values[4]);
if ($yearFrom > 0){
if ($yearFrom < 1950){
$yearFrom = 1950;
} elseif ($yearFrom > 2030){
$yearFrom = 2030;
}
}
if ($yearTo > 0){
if ($yearTo < 1950){
$yearTo = 1950;
} elseif ($yearTo > 2030){
$yearTo = 2030;
}
}
$data[] = array($productId, $make, $model, $yearFrom, $yearTo, $engine);
}
if (count($data) > 0){
$this->saveValues($data);
}
}
public function saveValues($data)
{
$valuesStr = '';
foreach ($data as $values){
$cell = '';
foreach ($values as $value)
$cell .= ",'" . esc_sql(trim($value)). "'";
$valuesStr .= ($valuesStr != '' ? ',' : '') . "(NULL{$cell})";
}
$this->_wpdb->query("INSERT IGNORE INTO {$this->_mainTable} VALUES {$valuesStr}");
}
?>
Below is the CSV:
product_sku,make,model,year_from,year_to,engine
63118,Toyota,FJ Cruiser,2000,2008,V6 4.7L 2UZ-FE 20R/22R 1st Gen FJ Cruiser
28216,Toyota,GX470,1992,1997,V8 4.7L 2UZ-FE GX470
62687,Toyota,Land Cruiser,1998,2007,V8 4.7L 2UZ-FE 100-Series
28485,Toyota,Land Cruiser,2007,2018,V8 5.7L 3UR-FE 200-Series
Incidentally, if I use this:
product_sku,make,model,year_from,year_to,engine
63118,Toyota,FJ Cruiser,2000,2008,engine1
28216,Toyota,GX470,1992,1997,engine2
62687,Toyota,Land Cruiser,1998,2007,engine3
28485,Toyota,Land Cruiser,2007,2018,engine4
everything gets inserted fine. I think the problem is with the last column because if I use the same values in the last column in the second or third columns, everything works fine.
Never mind, I've solved it. The problem was with the encoding of the CSV file. I changed it to UTF-8 and everything is working fine.
I'm trying to make a recursive function to get all the directories and sub directories from my ftp server in an array.
I tried a lot of functions I've found on the web. The one that works best for me is this one:
public function getAllSubDirFiles() {
$dir = array(".");
$a = count($dir);
$i = 0;
$depth = 20;
$b = 0;
while (($a != $b) && ($i < $depth)) {
$i++;
$a = count($dir);
foreach ($dir as $d) {
$ftp_dir = $d . "/";
$newdir = ftp_nlist($this->connectionId, $ftp_dir);
foreach ($newdir as $key => $x) {
if ((strpos($x, ".")) || (strpos($x, ".") === 0)) {
unset($newdir[$key]);
} elseif (!in_array($x, $dir)) {
$dir[] = $x;
}
}
}
$b = count($dir);
}
return $dir ;
}
The problem with this function is it wont allow the directory to have a "." in it's name and every file that is located in the root directory will be considered a directory as well. So I adjusted the function and got this:
public function getAllSubDirFiles($ip, $id, $pw) {
$dir = array(".");
$a = count($dir);
$i = 0;
$depth = 20;
$b =0;
while (($a != $b) && ($i < $depth)) {
$i++;
$a = count($dir);
foreach ($dir as $d) {
$ftp_dir = $d . "/";
$newdir = ftp_nlist($this->connectionId, $ftp_dir);
foreach ($newdir as $key => $x) {
if (!is_dir('ftp://'.$id.':'.$pw.'#'.$ip.'/'.$x)) {
unset($newdir[$key]);
} elseif (!in_array($x, $dir)) {
$dir[] = $x;
}
}
}
$b = count($dir);
}
return $dir ;
}
This works pretty good but and gives the result I want. but it's so slow it's unusable.
I also tried working with ftp_rawlist but it has the same drawback of being horribly slow.
public function getAllSubDirFiles() {
$dir = array(".");
$a = count($dir);
$i = 0;
$depth = 20;
$b = 0;
while (($a != $b) && ($i < $depth)) {
$i++;
$a = count($dir);
foreach ($dir as $d) {
$ftp_dir = $d . "/";
$newdir = $this->getFtp_rawlist('/' . $ftp_dir);
foreach ($newdir as $key => $x) {
$firstChar = substr($newdir[$key][0], 0, 1);
$a = 8;
while ($a < count($newdir[$key])) {
if ($a == 8) {
$fileName = $ftp_dir . '/' . $newdir[$key][$a];
} else {
$fileName = $fileName . ' ' . $newdir[$key][$a];
}
$a++;
}
if ($firstChar != 'd') {
unset($newdir[$key]);
} elseif (!in_array($fileName, $dir)) {
$dir[] = $fileName;
}
}
}
$b = count($dir);
}
return $dir;
}
public function getFtp_rawlist($dir) {
$newArr = array();
$arr = ftp_rawlist($this->connectionId, $dir);
foreach ($arr as $value) {
$stringArr = explode(" ", $value);
$newArr[] = array_values(array_filter($stringArr));
}
return $newArr;
}
I've been stuck on this problem for the last couple of days and I'am getting desperate. If any one has any suggestion please let me know
If your server supports MLSD command and you have PHP 7.2 or newer, you can use ftp_mlsd function:
function ftp_mlsd_recursive($ftp_stream, $directory)
{
$result = [];
$files = ftp_mlsd($ftp_stream, $directory);
if ($files === false)
{
die("Cannot list $directory");
}
foreach ($files as $file)
{
$name = $file["name"];
$filepath = $directory . "/" . $name;
if (($file["type"] == "cdir") || ($file["type"] == "pdir"))
{
// noop
}
else if ($file["type"] == "dir")
{
$result = array_merge($result, ftp_mlsd_recursive($ftp_stream, $filepath));
}
else
{
$result[] = $filepath;
}
}
return $result;
}
If you do not have PHP 7.2, you can try to implement the MLSD command on your own. For a start, see user comment of the ftp_rawlist command:
https://www.php.net/manual/en/function.ftp-rawlist.php#101071
If you cannot use MLSD, you will particularly have problems telling if an entry is a file or folder. While you can use the ftp_size trick, calling ftp_size for each entry can take ages.
But if you need to work against one specific FTP server only, you can use ftp_rawlist to retrieve a file listing in a platform-specific format and parse that.
The following code assumes a common *nix format.
function ftp_nlst_recursive($ftp_stream, $directory)
{
$result = [];
$lines = ftp_rawlist($ftp_stream, $directory);
if ($lines === false)
{
die("Cannot list $directory");
}
foreach ($lines as $line)
{
$tokens = preg_split("/\s+/", $line, 9);
$name = $tokens[8];
$type = $tokens[0][0];
$filepath = $directory . "/" . $name;
if ($type == 'd')
{
$result = array_merge($result, ftp_nlst_recursive($ftp_stream, $filepath));
}
else
{
$result[] = $filepath;
}
}
return $result;
}
For DOS format, see: Get directory structure from FTP using PHP.
I've build an OOP FTP Client library that's can help you on this a lot, using just this code you can retrieve a list of only the directories with addition useful information like (chmod, last modified time, size ...).
The code :
// Connection
$connection = new FtpConnection("localhost", "foo", "12345");
$connection->open();
// FtpConfig
$config = new FtpConfig($connection);
$config->setPassive(true);
$client = new FtpClient($connection);
$allFolders =
// directory, recursive, filter
$client->listDirectoryDetails('/', true, FtpClient::DIR_TYPE);
// Do whatever you want with the folders
This code a variation of Martin Prikryl one. It is slower but do not have any failures with whitespaces. Use this code only if you have any problems with the code above.
function ftp_list_files_recursive($ftp_stream, $path){
$lines = ftp_nlist($ftp_stream, $path);
$result = array();
foreach ($lines as $line) {
if (ftp_size($ftp_stream, $line) == -1) {
$result = array_merge($result, ftp_list_files_recursive($ftp_stream, $line));
}
else{
$result[] = $line;
}
}
return $result;
}
I have on my linux machine such folder tree structure:
/dir/yyyy/mm/dd/HH
e.g.:
/dir/2014/03/01/08
/dir/2014/03/20/09
/dir/2014/03/01/10
/dir/2014/08/01/10
/dir/2014/12/15/10
/dir/2015/01/01/14
I'd like to get in php what path is the oldest, like this:
The oldest path is: 2014-03-01 08
The newest path is: 2015-01-01 14
How it can be done?
It could be written better but it works
$paths = array(
'/dir/2014/03/01/08',
'/dir/2014/03/20/09',
'/dir/2014/03/01/10',
'/dir/2014/08/01/10',
'/dir/2014/12/15/10',
'/dir/2015/01/01/14',
);
$dates = array();
foreach($paths as $path)
{
$matches = array();
preg_match('#([^\/]+?)\/([^\/]+?)\/([^\/]+?)\/([^\/]+?)\/([^\/]+)#', $path, $matches);
$dates[$path] = strtotime(sprintf("%s-%s-%s %s:00:00", $matches[2], $matches[3], $matches[4], $matches[5]));
}
asort($dates);
$dates = array_keys($dates);
$oldest = array_shift($dates);
$newest = array_pop($dates);
It changes date find by regex to unixtimestamp then sorts it and returns top and bottom value of sorted array.
Little bit like Pascal style)
<?php
$oldest = '';
$newest = '';
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('./dir/'));
foreach ($iterator as $file => $object) {
if ($iterator->getDepth() === 4) {
$name = $object->getPath();
if ($name > $newest) {
$newest = $name;
}
if (empty($oldest) or $name < $oldest) {
$oldest = $name;
}
}
}
var_export([$oldest, $newest]);
Result:
array (
0 => './dir/2014/03/01/08',
1 => './dir/2015/01/01/14',
)
All you have to do is loop through each folder and find the directory that has the lowest number. If you have the file paths stored in a database, it can be easier, but from your question it seems like you want to search the folders.
<?php
$base = 'dir';
$yearLowest = lowestDir($base);
$monthLowest = lowestDir($yearLowest);
$dayLowest = lowestDir($monthLowest);
echo $dayLowest;
function lowestDir($dir) {
$lowest = null;
$handle = opendir($dir);
while(($name = readdir($handle))) {
if($name == '.' || $name == '..') {
continue;
}
if(is_dir($dir.'/'.$name) && ($lowest == null || $name < $lowest)) {
$lowest = $name;
}
}
closedir($handle);
return $dir.'/'.$lowest;
}
?>
I'm taking data from an excel using phpexcel, like this:
$number = $objPHPExcel->getActiveSheet()->getCell('A3')->getValue();
number is clearly a number, okay? so, after that I want to know if $number exists on the word $elem:
if(strpos($elem,$number) !== false) //está?
{
$answer = true;
}
the problem is that when I test it with this data, the $answer is true, and it shouldn't be:
$number = 11001456
$elem = '10001033.jpg'
So... what's wrong here?
PD: I'm going to post the entire code so you can see it, If I try to do (string)$number then the code crashes, it exceeds time execution.... (using cakephp)
The important thing is located at the function SearchPhoto... you will see the strpos there...
public function admin_load() //esto sirve para cargar un excel...
{
if($this->request->is('post'))
{
$data = $this->request->data;
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);
define('EOL',(PHP_SAPI == 'cli') ? PHP_EOL : '<br />');
date_default_timezone_set('Europe/London');
/** Include PHPExcel_IOFactory */
require_once WWW_ROOT . '/excelWorker/Classes/PHPExcel/IOFactory.php';
echo date('H:i:s') , " Load from Excel2007 file" , EOL;
$callStartTime = microtime(true);
$objPHPExcel = PHPExcel_IOFactory::load($data['People']['excel']['tmp_name']);
$dir = WWW_ROOT . "img/photos";
$files = scandir($dir);
$batchPeople = array();
for($i = 2; $i <= $data['People']['num']; $i++)
{
$batchPeople[$i-2]['People']['fullname'] = $objPHPExcel->getActiveSheet()->getCell('B'.$i)->getValue();
$batchPeople[$i-2]['People']['floor'] = $objPHPExcel->getActiveSheet()->getCell('C'.$i)->getValue();
$batchPeople[$i-2]['People']['country'] = $objPHPExcel->getActiveSheet()->getCell('D'.$i)->getValue();
$batchPeople[$i-2]['People']['day'] = $objPHPExcel->getActiveSheet()->getCell('F'.$i)->getValue();
$batchPeople[$i-2]['People']['month'] = $objPHPExcel->getActiveSheet()->getCell('G'.$i)->getValue();
$batchPeople[$i-2]['People']['photo'] = $this->SearchPhoto($objPHPExcel->getActiveSheet()->getCell('A'.$i)->getValue(),$files);
}
// $this->People->saveMany($batchPeople);
}
}
function SearchPhoto($number, $array)
{
$answer = '';
$getOut = false;
foreach($array as $elem)
{
if(strcmp($elem,'.') != 0 && strcmp($elem,'..') != 0)
if(strpos($elem,$number) !== false) //está?
{
echo 'coinciden--> '. $number . ',' . $elem;
echo '<br>';
$answer = $elem;
$getOut = true;
}
if($getOut)
break;
}
return $answer;
}
This should work for you:
(BTW: You use $number in the code and not $num)
<?php
$num = 11001456;
$elem = "10001033.jpg";
if(strpos($elem, (string)$num) !== false) {
echo "yes";
}
?>
For more information about strpos() look into the manual: http://php.net/manual/en/function.strpos.php
And a quote from there:
needle:
If needle is not a string, it is converted to an integer and applied as the ordinal value of a character.
echo chr($number); // p
echo strpos($elem, 'p'); // 10 (which is p in $elem)
I ended up using preg_match to solve my problem... I still don't know what's wrong with using strpos... because it works on all the sites I made expect this particular case!
In case anyone wondering what's the exact solution:
$text = $B;
$pattern = '/'.$A.'/';
preg_match($pattern,$text,$matches);
if(isset($matches[0]))
$answer = true;