I want to return an error message when two classes provided by the user/ developer don't exist.
core/model/Conan.php,
namespace core\model;
class Conan
{
var $bodyBuild = 'extremely muscular';
var $birthDate = 'before history';
var $skill = 'fighting';
public function methodConan()
{
return 'The methodConan from namespace core\model';
}
}
local/model/Conan.php,
namespace local\model;
class Conan
{
var $bodyBuild = 'very skinny';
var $birthDate = '1963';
var $skill = 'comedy';
public function methodConan()
{
return 'The methodConan from namespace local\model';
}
}
index.php,
define ('WEBSITE_DOCROOT', str_replace('\\', '/', dirname(__FILE__)).'/');
include 'core/helper/BaseClassAutoloader.php';
// Autoload the core & local classes.
$autoloader = new BaseClassAutoloader([
'local/model/',
'core/model/'
]);
if (class_exists('\foo\model\Conan'))
{
echo 'from local';
}
else
{
if(class_exists('\boo\model\Conan'))
{
echo 'from core';
}
else
{
echo 'both don\'t exist';
}
}
I suppose to get 'both don\'t exist' as the result, I get this error instead,
Fatal error: Cannot redeclare class local\model\Conan in
C:\wamp\www...\local\model\Conan.php
on line 8
It doesn't really make sense!
Is it something wrong with my autoload below??
autoload class,
class BaseClassAutoloader
{
public function __construct($directory)
{
$this->directory = $directory;
spl_autoload_register(array($this,'getClass'));
}
private function getClass($class_name)
{
if(is_array($this->directory)): $mainDirectories = $this->directory;
else: $mainDirectories = array($this->directory); endif;
$subDirectories = [];
$namespace = "\\";
$isNamespace = false;
$parts = explode($namespace, $class_name);
if(strpos($class_name, $namespace) !== false)
{
$isNamespace = true;
}
$fileNameName = end($parts).'.php';
foreach($mainDirectories as $pathDirectory)
{
$iterator = new RecursiveIteratorIterator
(
new RecursiveDirectoryIterator(WEBSITE_DOCROOT.$pathDirectory), // Must use absolute path to get the files when ajax is used.
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $fileObject)
{
if ($fileObject->isDir())
{
$pathnameReplace = str_replace('\\', '/', $fileObject->getPathname());
$array = explode("/",$pathnameReplace);
$folder = end($array);
if($folder === '.' || $folder === '..') {continue;}
$subDirectories[] = preg_replace('~.*?(?=core|local)~i', '', str_replace('\\', '/', $fileObject->getPathname())) .'/';
}
}
}
$merged_directories = array_merge($mainDirectories,$subDirectories);
foreach($merged_directories as $pathDirectory)
{
if(file_exists(WEBSITE_DOCROOT.$pathDirectory.$fileNameName))
{
include WEBSITE_DOCROOT.$pathDirectory.$fileNameName;
if($isNamespace === false) if (class_exists($class_name)) break;
}
}
}
}
try using require_once instead of include
try using include_once instead of include at line 62 oh BaseClassAutoLoader.php.
For future googlers, If you don't control the files inclusion process like say inside a framework, and you need an urget fix you can just wrap the class definition inside a check, and it will prevent php from throwing the above error.
if (!class_exists('AwesomCLass')) {
class AwesomCLass extends ParentClass {
// Do awesome stuff here all day long
}
}
For those who are getting fatal error while using class_exists can use a alternative , i found it useful.
$classname = 'Myclass';
if( class_exists( $classname ) ) {
echo "class exists";
} else {
echo "class not exists";
}
instead, we can use as given below
$classname = 'Myclass';
if( in_array($classname, get_declared_classes() ) ) {
echo "class exists";
} else {
echo "class not exists";
}
Related
Test.php
<?php
$a = 'D:/mydomain/Slim/Lib/Table.php';
$b = '\Slim\Lib\Table';
foreach (array($a, $b) as $value)
{
if (file_exists($value))
{
echo "file_exist";
include_once($value);
new Table();
}
else if (class_exists($value))
{
echo "class_exist";
$class = new $value();
}
else
{
echo "error";
}
}
?>
and D:/mydomain/Slim/Lib/Table.php
<?php
class Table {
function hello()
{
echo "test";
}
function justTest()
{
echo "just test";
}
}
?>
When im execute test.php in browser the output result is:
file_exist
Fatal error: Cannot redeclare class Table in D:/mydomain/Slim/Lib/Table.php on line 2
if statement for class_exist is not trigger. namespace \Slim\Lib\Table is never exist.
The second, optional, parameter of class_exists is bool $autoload = true, so it tries to autoload this class. Try to change this call to class_exists( $value, false) See the manual.
The first if can be changed to:
if(!class_exists($value) && file_exists($file)
actually there are other problems:
$a = 'D:/mydomain/Slim/Lib/Table.php';
$b = 'Table'; //Since you don't have a namespace in the Table class...
//This ensures that the class and table are a pair and not checked twice
foreach (array($a=>$b) as $file=>$value)
{
if (!class_exists($value) && file_exists($file))
{
echo "file_exist";
include_once($file);
$class = new $value();
}
else if (class_exists($value))
{
echo "class_exist";
$class = new $value();
}
else
{
echo "error";
}
}
<?php
extract($_REQUEST);
if(isset($_POST['submit']))
{
$get_folder = $_POST['url'];
$q = mysql_query("insert into test (url) values ('$url')");
if($q)
{
copydir("test",$get_folder);
function copydir($source,$destination)
{
if(!is_dir($destination))
{
$oldumask = umask(0);
mkdir($destination, 01777);
umask($oldumask);
}
$dir_handle = #opendir($source) or die("Unable to open");
while ($file = readdir($dir_handle))
{
if($file!="." && $file!=".." && !is_dir("$source/$file")) //if it is file
copy("$source/$file","$destination/$file");
if($file!="." && $file!=".." && is_dir("$source/$file")) //if it is folder
copydir("$source/$file","$destination/$file");
}
closedir($dir_handle);
}
}
}
?>
this is my code ...it shows Fatal error: Call to undefined function copydir() in C:\xampp\htdocs\mywork\creating-folder\1.php on line 14. But when i copy from copydir("test",$get_folder); to closedir($dir_handle); in separate file it works perfectly but instead of $get_folder need to give some static name
Use copy().
Note that this function does support directories out of the box. A function from one of the comments on the linked documentation page might help:
<?php
function recurse_copy($src,$dst) {
$dir = opendir($src);
#mkdir($dst);
while(false !== ( $file = readdir($dir)) ) {
if (( $file != '.' ) && ( $file != '..' )) {
if ( is_dir($src . '/' . $file) ) {
recurse_copy($src . '/' . $file,$dst . '/' . $file);
}
else {
copy($src . '/' . $file,$dst . '/' . $file);
}
}
}
closedir($dir);
}
?>
// Will copy foo/test.php to bar/test.php
// overwritting it if necessary
copy('foo/test.php', 'bar/test.php');
This works:
foo();
function foo() { ... }
This won't:
if (...) {
foo();
function foo() { ... }
}
This will:
if (...) {
function foo() { ... }
foo();
}
In general, you're required to declare the function before you call it. The exception is with plain, globally defined functions as in the first example; those are being handled right in the parsing step before execution. Since your function declaration is inside an if statement and thereby conditional, the if condition and thereby the whole code need to be evaluated first. And while the code is being evaluated, you're trying to call a function which hasn't been declared yet.
I wrote recursive PHP function for folder deletion. I wonder, how do I modify this function to delete all files and folders in webhosting, excluding given array of files and folder names (for ex. cgi-bin, .htaccess)?
BTW
to use this function to totally remove a directory calling like this
recursive_remove_directory('path/to/directory/to/delete');
to use this function to empty a directory calling like this:
recursive_remove_directory('path/to/full_directory',TRUE);
Now the function is
function recursive_remove_directory($directory, $empty=FALSE)
{
// if the path has a slash at the end we remove it here
if(substr($directory,-1) == '/')
{
$directory = substr($directory,0,-1);
}
// if the path is not valid or is not a directory ...
if(!file_exists($directory) || !is_dir($directory))
{
// ... we return false and exit the function
return FALSE;
// ... if the path is not readable
}elseif(!is_readable($directory))
{
// ... we return false and exit the function
return FALSE;
// ... else if the path is readable
}else{
// we open the directory
$handle = opendir($directory);
// and scan through the items inside
while (FALSE !== ($item = readdir($handle)))
{
// if the filepointer is not the current directory
// or the parent directory
if($item != '.' && $item != '..')
{
// we build the new path to delete
$path = $directory.'/'.$item;
// if the new path is a directory
if(is_dir($path))
{
// we call this function with the new path
recursive_remove_directory($path);
// if the new path is a file
}else{
// we remove the file
unlink($path);
}
}
}
// close the directory
closedir($handle);
// if the option to empty is not set to true
if($empty == FALSE)
{
// try to delete the now empty directory
if(!rmdir($directory))
{
// return false if not possible
return FALSE;
}
}
// return success
return TRUE;
}
}
Try something like this:
$it = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator('%yourBaseDir%'),
RecursiveIteratorIterator::CHILD_FIRST
);
$excludeDirsNames = array();
$excludeFileNames = array('.htaccess');
foreach($it as $entry) {
if ($entry->isDir()) {
if (!in_array($entry->getBasename(), $excludeDirsNames)) {
try {
rmdir($entry->getPathname());
}
catch (Exception $ex) {
// dir not empty
}
}
}
elseif (!in_array($entry->getFileName(), $excludeFileNames)) {
unlink($entry->getPathname());
}
}
I'm using this function to delete the folder with all files and subfolders:
function removedir($dir) {
if (substr($dir, strlen($dir) - 1, 1) != '/')
$dir .= '/';
if ($handle = opendir($dir)) {
while ($obj = readdir($handle)) {
if ($obj != '.' && $obj != '..') {
if (is_dir($dir . $obj)) {
if (!removedir($dir . $obj))
return false;
}
else if (is_file($dir . $obj)) {
if (!unlink($dir . $obj))
return false;
}
}
}
closedir($handle);
if (!#rmdir($dir))
return false;
return true;
}
return false;
}
$folder_to_delete = "folder"; // folder to be deleted
echo removedir($folder_to_delete) ? 'done' : 'not done';
Iirc I got this from php.net
You could provide an extra array parameter $exclusions to recursive_remove_directory(), but you'll have to pass this parameter every time recursively.
Make $exclusions global. This way it can be accessed in every level of recursion.
I'm looping over all the files in a directory. Now I want to get all the functions and classes defined in each of them. From there, I can examine them further using the ReflectionClass. I can't figure out how to get all the functions and classes defined in a file though.
ReflectionExtension looks the closest to what I want, except my files aren't part of an extension. Is there some class or function I'm overlooking?
Great question. get_declared_classes and get_defined_functions could be a good starting point. You would have to take note of what classes / functions are already defined when trying to determine what's in a given file.
Also, not sure what your end goal is here, but tools such as PHP Depend or PHP Mess Detector may do something similar to what you want. I'd recommend checking them out as well.
This is the best I could come up with (courtesy):
function trimds($s) {
return rtrim($s,DIRECTORY_SEPARATOR);
}
function joinpaths() {
return implode(DIRECTORY_SEPARATOR, array_map('trimds', func_get_args()));
}
$project_dir = '/path/to/project/';
$ds = array($project_dir);
$classes = array();
while(!empty($ds)) {
$dir = array_pop($ds);
if(($dh=opendir($dir))!==false) {
while(($file=readdir($dh))!==false) {
if($file[0]==='.') continue;
$path = joinpaths($dir,$file);
if(is_dir($path)) {
$ds[] = $path;
} else {
$contents = file_get_contents($path);
$tokens = token_get_all($contents);
for($i=0; $i<count($tokens); ++$i) {
if(is_array($tokens[$i]) && $tokens[$i][0] === T_CLASS) {
$i += 2;
$classes[] = $tokens[$i][1];
}
}
}
}
} else {
echo "ERROR: Could not open directory '$dir'\n";
}
}
print_r($classes);
Wish I didn't have to parse out the files and loop over all the tokens like this.
Forgot the former solutions prevents me from using reflection as I wanted. New solution:
$project_dir = '/path/to/project/';
$ds = array($project_dir);
while(!empty($ds)) {
$dir = array_pop($ds);
if(($dh=opendir($dir))!==false) {
while(($file=readdir($dh))!==false) {
if($file[0]==='.') continue;
$path = joinpaths($dir,$file);
if(is_dir($path)) {
$ds[] = $path;
} else {
try{
include_once $path;
}catch(Exception $e) {
echo 'EXCEPTION: '.$e->getMessage().PHP_EOL;
}
}
}
} else {
echo "ERROR: Could not open directory '$dir'\n";
}
}
foreach(get_declared_classes() as $c) {
$class = new ReflectionClass($c);
$methods = $class->getMethods();
foreach($methods as $m) {
$dc = $m->getDocComment();
if($dc !== false) {
echo $class->getName().'::'.$m->getName().PHP_EOL;
echo $dc.PHP_EOL;
}
}
}
there is a project which I need to extend. All classes are in seperate files, I need to extend some of the classes without rewriting existing code in other files. My idea was to use namespaces but I fail. Here is an example:
I've renamed the original A.php class file to A_Original.php:
class A
{
public function hello()
{
echo "hello world from Class A\n";
}
}
Then created a new A.php:
namespace AOriginal {
include 'A_Original.php';
}
namespace {
class A
{
public function hello()
{
echo "hello world from Class A Extended\n";
}
}
}
This fails because on including the original A_Original.php file the class is dumped to the global scope (thus ignoring the namespace command).
I can not modify the existing code inthe A_Original.php file, but renaming is ok.
The other project files (whic I cannot modify) use a require "A.php".
How to accomplish this?
You can extend a class without modifying its existing behaviour:
class A {
public function foo(){
}
}
class MySubClassOfA extends A {
public function bar(){
}
}
You can add your own methods to MySubClassOfA, i.e. bar(). You can call the foo method on MySubClassOfA and it's behaviour is the same, unless you define a method called foo in MySubClassOfA.
I guess that you have no choice but add the single line of "namespace xxx;" code on top of all your files. The following PHP CLI script may be useful.
<?php
function convert($namespace, $srcdir, $dstdir)
{
try
{
$files = glob("$srcdir/{*,.*}", GLOB_BRACE);
if ( ! file_exists($dstdir) && ! mkdir($dstdir) )
{
throw new Exception("Cannot create directory {$dstdir}");
}
if ( ! is_dir($dstdir) )
{
throw new Exception("{$dstdir} is not a directory");
}
foreach ( $files as $f )
{
extract(pathinfo($f)); // then we got $dirname, $basename, $filename, $extension
if ( $basename == '.' || $basename == '..' )
{
continue;
}
if ( is_dir($f) )
{
$d = $dstdir. substr($f, strlen($srcdir));
convert($namespace, $f, $d);
continue;
}
print "processing {$f} ... ";
if ( ($s = file_get_contents($f)) === FALSE )
{
throw new Exception("Error reading $f");
}
if ( preg_match("/^\s*namespace\s+\S+;/m", $s) )
{
print "already has namespace, skip";
}
else
{
$lines = preg_split("/(\n|\r\n)/", $s);
$output = array();
$matched = FALSE;
foreach ( $lines as $s )
{
$output[] = $s;
// check if this is a PHP code?
if ( ! $matched && preg_match('/<(\?(php )*|%)/', $s) )
{
$matched = TRUE;
print "insert namespace ... ";
$output[] = "namespace {$namespace};";
}
}
if ( file_put_contents("{$dstdir}/{$basename}" , implode("\n", $output)) === FALSE )
{
throw new Exception("Cannot save file {$dstdir}/{$basename}");
}
if ( ! $matched )
{
print ("not a PHP file, skip.");
}
else
{
print "done!";
}
}
print "\n";
}
}
catch (Exception $e)
{
print 'Error: '. $e->getMessage() .' ('. $e->getCode() .')' ."\n";
}
}
extract($_SERVER);
if ( $argc < 4 )
{
?>
Usage: php -F <?=$argv[0]?> <namespace> <source_dir(s)> <dst_dir>
Convert PHP code to be namespace-aware
<?
return;
}
else
{
for ( $i = 2; $i < $argc - 1; $i++ )
{
convert($argv[1], $argv[$i], $argv[$argc-1]);
}
}
?>
How about eval()?
New A.php
$lines = file('a_original.php');
array_unshift($lines, 'namespace AO;?>');
$string = implode(chr(13).chr(10), $lines);
eval($string);
class A extends AO\A
{
public function hello()
{
parent::hello();
echo "hello world from Class A Extended\n";
}
}