vfsstream paths and realpath - php

I'm experimenting with vfsStream for unit testing filesystem interactions and have very quickly run into a major hurdle. One of the validation checks the code under test does is execute realpath() on a supplied input path to test that it's an actual path and not nonsense. However, realpath always fails on a vfsstream path.
The following code demonstrates the problem outside of any particular class.
$content = "It rubs the lotion on its skin or else it gets the hose again";
$url = vfsStream::url ('test/text.txt');
file_put_contents ($url, $content);
var_dump ($url);
var_dump (realpath ($url));
var_dump (file_get_contents ($url));
The output is as follows:
string(27) "vfs://FileClassMap/text.txt"
bool(false)
string(61) "It rubs the lotion on its skin or else it gets the hose again"
Obviously the vfsStream created the file and wrote the given content to it, but I can't verify that the path to it is correct with realpath. As realpath is being used inside the actual code I need a way of working about this.
I really don't think removing realpath is a sensible approach because it performs an important function inside the code, and eliminating an important check just to make the code testable seems a pretty poor solution. I could also put an if around the test to make it possible to disable it for testing purposes, but again I don't think that's a good idea either. Also I'd hate to have to do that at every point in the code where I might make a call to realpath (). The third option would be to set up a RAM disk for filesystem unit tests, but that's not ideal either. You have to clean up after yourself (which is what vfsstream is supposed to help you avoid the need for) and how to actually do it will differ from OS to OS, so the unit tests would cease to be OS agnostic.
So is there a way to get a vfsstream path in a format that actually works with realpath?
For completeness, the following is the code fragment from the class I'm trying to actually test.
if (($fullPath = realpath ($unvalidatedPath))
&& (is_file ($fullPath))
&& (is_writable ($fullPath))) {
A refactoring to the following (as per potential solution 2) allows me to test with vfsStream, but I think it could be problematic in production:
// If we can get a canonical path then do so (realpath can fail on URLs, stream wrappers, etc)
$fullPath = realpath ($unvalidatedPath);
if (false === $fullPath) {
$fullPath = $unvalidatedPath;
}
if ((is_file ($fullPath))
&& (is_writable ($fullPath))) {

If you use namespaces you can override the realpath function only in the test class. I always use canonical paths in my vfsStream testcases, beause i don't want to test the realpath() function itself.
namespace my\namespace;
/**
* Override realpath() in current namespace for testing
*
* #param string $path the file path
*
* #return string
*/
function realpath($path)
{
return $path;
}
Good described here: http://www.schmengler-se.de/en/2011/03/php-mocking-built-in-functions-like-time-in-unit-tests/

I opened a bug with an implementation of Sebkrueger's method on vfsStream: https://github.com/bovigo/vfsStream/issues/207
Waiting for their feedback, here is my working realpath():
/**
* This function overrides the native realpath($url) function, removing
* all the "..", ".", "///" of an url. Contrary to the native one,
*
* #param string $url
* #param string|bool The cleaned url or false if it doesn't exist
*/
function realpath(string $url)
{
preg_match("|^(\w+://)?(/)?(.*)$|", $url, $matches);
$protocol = $matches[1];
$root = $matches[2];
$rest = $matches[3];
$split = preg_split("|/|", $rest);
$cleaned = [];
foreach ($split as $item) {
if ($item === '.' || $item === '') {
// If it's a ./ then it's nothing (just that dir) so don't add/delete anything
} elseif ($item === '..') {
// Remove the last item added since .. negates it.
$removed = array_pop($cleaned);
} else {
$cleaned[] = $item;
}
}
$cleaned = $protocol.$root.implode('/', $cleaned);
return file_exists($cleaned) ? $cleaned : false;
}

Related

How to remove a directory in wordpress using Filesystem_API

I am using WordPress 4.8.1
I am trying to remove a directory from theme setting, but neither getting any error nor any success.
Even PHP error log has not logged data about it, My WP_DEBUG is on.
$wp_filesystem_base = WP_Filesystem_Base();
$wp_filesystem_base->rmdir(dirname($file_path), true);
The class is not undefined for sure as I could have got some error, still I used a class_exists to check it and it is available where I am using.
please let me know, if I am doing it wrong or need any thing more about the issue.
Since a WordPress' core methods working answer is missing here, I propose this complete solution that sums up all the other aswers.
require_once ( ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php' );
require_once ( ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php' );
$fileSystemDirect = new WP_Filesystem_Direct(false);
$fileSystemDirect->rmdir($dir, true);
Summary:
You can't use WP_Filesystem_Base because does not really implement the method rmdir; it's just an interface.
You must require class-wp-filesystem-base.php because it's a dependency of WP_Filesystem_Direct
You can always use your own rrmdir as someone proposed, but you don't have to add additional code to be maintained when a feature is already available in core;
The false argument passed to the constructor is just because the $arg param is needed but not used as explained in official WP's documentation.
I guess you need to use WP_Filesystem_Direct class
WP_Filesystem_Base::rmdir implementation:
public function rmdir( $path, $recursive = false ) {
return false;
}
You can see, that this code is not working with real FS :)
So, WP_Filesystem_Direct::rmdir actually do the job.
I got it to work like this:
/**
* Deletes a directory, using the WordPress Filesystem API
*
* #param string $path
* #return void
* #author Rasso Hilber <mail#rassohilber.com>
*/
function delete_directory(string $path) {
// make it work from the frontend, as well
require_once ABSPATH . 'wp-admin/includes/file.php';
// this variable will hold the selected filesystem class
global $wp_filesystem;
// this function selects the appropriate filesystem class
WP_Filesystem();
// finally, you can call the 'delete' function on the selected class,
// which is now stored in the global '$wp_filesystem'
$wp_filesystem->delete($path, true);
}
Finally I ended with using a core php function, I believe it is a workaround, but will be useful for someone for who the above code or
$wp_filesystem_direct = WP_Filesystem_Direct();
$wp_filesystem_direct->rmdir(dirname($file_path), true);
will not work. You can use this
function rrmdir($dir) {
if (is_dir($dir)) {
$objects = scandir($dir);
foreach ($objects as $object) {
if ($object != "." && $object != "..") {
if (filetype($dir."/".$object) == "dir") self::rrmdir($dir."/".$object); else unlink($dir."/".$object);
}
}
reset($objects);
rmdir($dir);
}
}
This works for me:
require_once ( ABSPATH . '\wp-admin\includes\class-wp-filesystem-direct.php' );
$fileSystemDirect = new WP_Filesystem_Direct(false);
$fileSystemDirect->rmdir($dir, true);
Or simply:
WP_Filesystem();
global $wp_filesystem;
$wp_filesystem->rmdir($dir, true);

PHP file include script not fully working on LINUX machines

I have a lot of functions and classes that I have included in my website.
With the help of stackoverflow I recieved a script that automaticly includes all files in a folder and its subfolders: PHP: Automatic Include
When testing the script it always worked and I never had any problems with it.
But recently when switching from a windows server to a linux server it gives problems with extension of classes.
PHP Fatal error: Class 'AcumulusExportBase' not found in path/functions/classes/Acumulus/AcumulusExportWorkshop.php on line 3, referer: pagesite/?page_id=346
AcumulusExportWorkshop extends from AcumulusExportBase.
This all fully works on windows but refuses to work on linux.
I can fix this creating a include_once 'AcumulusExportBase.php'; but if there is a better solution it all seems unnecessary and annyoing work.
The code I use is the following:
load_folder(dirname(__FILE__));
function load_folder($dir, $ext = '.php') {
if (substr($dir, -1) != '/') { $dir = "$dir/"; }
if($dh = opendir($dir)) {
$files = array();
$inner_files = array();
while($file = readdir($dh)) {
if($file != "." and $file != ".." and $file[0] != '.') {
if(is_dir($dir . $file)) {
$inner_files = load_folder($dir . $file);
if(is_array($inner_files)) $files = array_merge($files, $inner_files);
} else {
array_push($files, $dir . $file);
}
}
}
closedir($dh);
foreach ($files as $file) {
if (is_file($file) and file_exists($file)) {
$lenght = strlen($ext);
if (substr($file, -$lenght) == $ext && $file != 'loader.php') { require_once($file); }
}
}
}
}
Can anyone tell me how it is that windows has no problems with extension classes and linux does? Also is there a fix for the problem without having to manual include the base classes?
Have you verified that AcumulusExportBase is included before AcumulusExportWorkshop under Linux? PHP is sensitive to the order of imports.
Both other answers are correct (and I've upvoted them both). Your problem will be the order the files are loaded (see Mark's response) and the recursion is also wrong (see KIKO).
However there is a better way of doing what you want: use an autoloader. http://php.net/manual/en/language.oop5.autoload.php
First time is confusing, but once you've grasped it, it's a lovely way of loading files.
Basically you say "If I need class X and it's not loaded, then load file Y.php".
If you're being super-lazy and don't want to specify each class then you can say "If I need class X and it's not loaded, run through the directory structure looking for a file called X.php and load that, my class will be in there." You can mix in what you have above to do this.
This way, you can load AcumulusExportWorkshop first, and then it looks for AcumulusExportBase afterwards and runs happily.
And, more beneficially, you only load what you need. If you never need the class, it never gets loaded.
I would like to answer your question, but regretably I do not have a Windows PHP server installed. I can however look at, and test, your code. The first thing I notice is the malformed recursion. To get the 'inner_files', recursion is used, which is fine, but this requires your function to return a value, namely the array of files. It does not. Furthermore, although you're using 'require_once', this is called on each recursion, meaning you try to include 'deep' files many times. In short: It's time to somewhat simplify your code.
load_folder(dirname(__FILE__));
function load_folder($dir,$ext = '.php')
{
if (substr($dir,-1) != '/') $dir = $dir.'/';
if ($handle = opendir($dir))
{
while($file = readdir($handle))
{
if (($file != '.') && ($file != '..') && ($file[0] != '.'))
{
if (is_dir($dir.$file)) load_folder($dir.$file,$ext);
else
{
if ((substr($file,-strlen($ext)) == $ext) &&
($file != 'loader.php') &&
file_exists($dir.$file)) require_once($dir.$file);
}
}
}
closedir($handle);
}
}
This works under linux, and performs the same task. I corrected the fact that $ext was missing from internal load_folder().
My advise is to never blindly copy code you find on the internet. Always check it, and then check again. Make sure you understand how it work. If you do not your projects will be littered with bug and impossible for anyone to maintain.
As Robbie stated, both of the other answers are correct, and an autoloader is the ideal solution.
An autoloader may seem (at first) to be slightly more complicated, but it presents benefits that are genuinely significant and make it well worth using.
Here are a few of them:
You do not need to manually include or require files.
You do not need to worry about files being loaded in the correct sequence - the interpreter will load any dependencies automatically. (In your case, the differences in the Windows and Linux operating system exposed this weakness in the existing code)
You can avoid loading files that are not needed.
The interpreter does not need to parse unnecessary classes and code.
Here are some things you should know about autoloaders:
You can have as many autoloaders as you need and want - they are stored in a stack and executed in sequence. If the first one does not load the class, the next one is used, and so on, until the class is loaded or there are no more autoloaders to try.
An autoloader is a callable - either a method of a class, or a function.
Exactly how you implement the autoloader is up to you - so if your project has a specific directory structure that relates to the class type or hierarchy, you can instruct it to look in specific directories, making it more efficient.
Most of us like to keep our classes in separate files. This makes it easier to find the classes we are interested in, and keeps the files smaller, which makes them easier to understand.
PHP does not enforce any kind of naming convention when it comes to the names of the files we use, but most developers prefer to save Classes in files with file names that relate to the class name.
The autoloader feature assumes that there is a way to load the correct file when presented with the file name. So a good practice is to have a simple way of generating the file name from the class name - the simplest is to use the class name as the file name.
Here is my preferred autoloader - which I have adapted from code by Jess Telford that I found online when I was learning PHPUnit - (http://jes.st/2011/phpunit-bootstrap-and-autoloading-classes/)
class ClassDirectoryAutoLoader {
static private $classNamesDirectory = array();
public static function crawlDirectory($directory) {
$dir = new DirectoryIterator($directory);
foreach ($dir as $file) {
self::addClassesAndCrawlDirectories($file);
}
}
private static function addClassesAndCrawlDirectories($file){
if (self::isRealDirectory($file)) {
self::crawlDirectory($file->getPathname());
} elseif (self::isAPhpFile($file)) {
self::saveClassFilename($file);
}
}
private static function isRealDirectory($file){
// ignore links, self and parent
return $file->isDir() && !$file->isLink() && !$file->isDot();
}
private static function isAPhpFile($file){
//ends in .php
return substr($file->getFilename(), -4) === '.php';
}
private static function saveClassFilename($file){
//assumes that the filename is the same as the classname
$className = substr($file->getFilename(), 0, -4);
self::registerClass($className, $file->getPathname());
}
public static function registerClass($className, $fileName) {
self::$classNamesDirectory[$className] = $fileName;
}
public static function loadClass($className) {
if (isset(self::$classNamesDirectory[$className])) {
require_once(self::$classNamesDirectory[$className]);
}
}
}
$classDir = dirname(__FILE__) . '/../classes'; // replace with the root directory for your class files
ClassDirectoryAutoLoader::crawlDirectory($classDir);
spl_autoload_register(array('ClassDirectoryAutoLoader', 'loadClass'));
What this code does is
Recurse through the directories (from the classDir), looking for .php files.
Builds an associative array that maps the classname to the full filename.
Registers an autoloader (the loadClass method).
When the interpreter tries to instantiate a class that is not defined, it will run this autoloader, which will:
Check if the file is stored in the associative array.
Require the file if it is found.
I like this autoloader because:
It's simple.
It's general - you can use it in virtually any project that follows a few simple conventions (see below).
It only crawls the directory tree once, not every time a new class is instantiated.
It only requires the files that are needed.
The loadClass method is super-efficient, simple performing a lookup and a require.
This code makes some assumptions:
All of the classes are stored in a specific directory.
All of the files in that directory contain classes.
The file name exactly matches the class name.
There are no side effects from requiring a file (i.e. the file contains only a class definition, no procedural code).
Breaking these assumptions will break this autoloader.
These are the conventions you need to follow to make use of this autoloader:
Keep all classes under a single directory.
Only class definition files under the class directory.
Make a seperate file for each public class.
Name each file after the class it contains.
No procedural code with side effects in these class definition files.
Well, I am building a System which uses an auto-loader, So here's what I made:
function endswith($string,$tidbit){
// Length of string
$strlen = strlen($string);
// Length of ending
$tidlen = strlen($tidbit);
// If the tidbit is of the right length (less than or equal to the length of the string)
if($tidlen <= $strlen){
// Substring requires a place to start the copying
$tidstart = $strlen - $tidlen;
// Get $tidlen characters off the end of $string
$endofstring = substr($string, $tidstart, $tidlen);
// If the $tidbit matches the end of the string
$ret = ($endofstring == $tidbit);
return $ret;
} else {
// Failure
return -1;
}
}
// Working
function ush_load_path($path) {
if (is_dir($path)) {
if (is_file($path . '/' . (explode('/', $path)[count(explode('/', $path)) - 1]) . '.inc')) {
require_once $path . '/' . (explode('/', $path)[count(explode('/', $path)) - 1]) . '.inc';
}
ush_load_path_recursive($path);
// If it is a file
} else if (is_file($path)) {
require_once $path;
// Failure
} else {
return false;
}
}
function ush_load_path_recursive($path) {
// Directory RESOURCE
$path_dir = opendir($path);
// Go through the entries of the specified directory
while (false != ($entry = readdir($path_dir))) {
if ($entry != '.' && $entry != '..') {
// Create Full Path
$path_ext = $path . '/' . $entry;
// Development
if (is_dir($path_ext)) {
ush_load_path_recursive($path_ext);
} else if (is_file($path_ext)) {
if (ush_is_phplib($path_ext)) {
print $path_ext . '<br />';
require_once $path_ext;
} else {
// Do nothing
}
}
}
}
}
// Working
function ush_is_phplib($path) {
return endswith($path, '.inc');
}
Can you do a print_r($files) after the closedir($dh); and before the foreach so we could see which files are actually being loaded and in which order?
load_folder(dirname(__FILE__));
function load_folder($dir, $ext = '.php') {
if (substr($dir, -1) != '/') { $dir = "$dir/"; }
clearstatcache(); // added to clear path cache
if($dh = opendir($dir)) {
$files = array();
$inner_files = array();
while($file = readdir($dh)) {
if($file != "." and $file != ".." and $file[0] != '.') {
if(is_dir($dir . $file)) {
$inner_files = load_folder($dir . $file);
if(is_array($inner_files)) $files = array_merge($files, $inner_files);
} else {
array_push($files, $dir . $file);
}
}
}
closedir($dh);
clearstatcache($dir); // added to clear path cache
foreach ($files as $file) {
if (is_file($file) and file_exists($file)) {
$lenght = strlen($ext);
if (substr($file, -$lenght) == $ext && $file != 'loader.php') { require_once($file); }
}
}
}
}
It seems that clearstatcache($path) must be called before any file-handling functions on the symlink'd dir. Php isn't caching symlink'd dirs properly.
I might be missing some point here, but it gives me the creeps just seeing that script...I am making the assumption that you call that function with a folder where you keep all your php function files and what not, which are all included, even if only one of those files is needed for the actual script to work.
Am I missing something here, or is this how it is working? If not, I am mislead by the description and function code.
If this is really what you are doing, there are better ways of including the needed files, without all those unnecessary inclusions.
I have a class that handles all my script loading. All I have to do is register the loading function:
spl_autoload_register('Modulehandler::Autoloader');
Then, whenever a new file is required, PHP will use my function to lookup the file.
Here is the function itself:
static function Autoloader($className) {
$files = array($className);
$lowerClass = strtolower($className);
if (strcmp($className, $lowerClass) != 0) $files[] = $lowerClass;
foreach (self::$modules as $moduleName => $module) {
foreach ($files as $className) {
$file = "{$module}/classes/{$className}.php";
if (file_exists($file)) {
require $file;
break;
}
}
}
}
This class has a little more to itself than just the loading, as I also have the ability to add Modules to the loader, so I only search the folders from the included modules, allowing some performance gain over the alternative of searching through all the modules. Besides that, there is the obvious benefit of only including the necessary files.
I hope this fits into what you need, and helps you out.
Have you checked whether that AcumulusExportBase is properly included in AcumulusExportWorkshop ?
And please keep in mind that Linux is very much case sensitive so the file name should in proper case.
LIKE a.JPG can be called a.jpg in windows but in LINUX we need to maintain the proper case.
The primary difference is the type of filesystem. When you use Mac, or Windows they are both not case sensitive, but Linux actually treats the filename "Capitalized.php" as "CAPITALIZED.PHP"
That is why most popular frameworks have lower cased filenames.

How to remove a previously added script file in a Zend Framework controller?

Is it possible to remove a script that was previously added like this?
$this->view->headScript()->appendFile("/js/my.js");
The situation here is that in my Bootstrap.php several JavaScript files are appended like in the above example but in a particular controller I want one particular JavaScript file not to be loaded. Is there a way to remove it during the initiation of the controller?
I am looking for something like this.
$this->view->headScript()->removeFile("/js/my.js");
This works, but really isn't a good practice but rather for exceptional purposes. I recommend trying to not load the unwanted scripts in the first place.
It is possible to remove scripts subsequently with a function like this one.
/**
* Removes a previously appended script file.
*
* #param string $src The source path of the script file.
* #return boolean Returns TRUE, if the removal has been a success.
*/
public function removeScript($src) {
$headScriptContainer = Zend_View_Helper_Placeholder_Registry::getRegistry()
->getContainer("Zend_View_Helper_HeadScript");
$iter = $headScriptContainer->getIterator();
$success = FALSE;
foreach ($iter as $k => $value) {
if(strpos($value->attributes["src"], $src) !== FALSE) {
$iter->offsetUnset($k);
$success = TRUE;
}
}
Zend_View_Helper_Placeholder_Registry::getRegistry()
->setContainer("Zend_View_Helper_HeadScript",$headScriptContainer);
return $success;
}
Note that the strpos function is used here. This will remove every script that has $src in its path. Of course you can change that to your needs.
It's an old question, but here is an alternative:
I used the minifyHeadScript and changed his helper adding this custom function:
/**
* Removes all script file.
*
*/
public function clearAllScripts(){
$this->getContainer()->exchangeArray(array());
$container = $this->getContainer();
Zend_View_Helper_Placeholder_Registry::getRegistry()->setContainer("Zend_View_Helper_HeadScript", $container);
}

Is it wrong to use the return statement this way?

I've got this autoloader method which is used to include class files as needed.
public static function autoloader($className) {
$fileExists = false;
foreach(array(LIBS_PATH, CONTROLLERS_PATH, MODELS_PATH) as $path) {
// Check if the class file exists and is readable
if(is_readable($classPath = $path . '/' . $className . '.php')) {
// Include the file
require($classPath);
$fileExists = true;
break;
}
}
if($fileExists === false) {
exit('The application cannot continue as it is not able to locate a required class.');
}
}
That works fine but I was thinking is this way better:
public static function autoloader($className) {
foreach(array(LIBS_PATH, CONTROLLERS_PATH, MODELS_PATH) as $path) {
// Check if the class file exists and is readable
if(is_readable($classPath = $path . '/' . $className . '.php')) {
// Include the file
require($classPath);
return;
}
}
exit('The application cannot continue as it is not able to locate a required class.');
}
As you can see I'm using the return statement in the middle of the loop to terminate the rest of the function because the class file has been included and the method has done its job.
Which way is the best way to break out of the loop if a match has been found? I'm just unsure about using the return statement because I always associated that with returning some value from a method.
Return can be used to simply break out of a method/function, and this use is perfectly legal. The real question to ask is what impact do you feel it has on readability?
There are different schools of thought on early returns.
One school maintains a single entry/exit point, stating that it makes the code easier to read since one can be assured that logic at the bottom of the code will be reached.
Another school states that the first school is outdated, and this is not as pressing a concern, especially if one maintains shorter method/function lengths.
A medium exists between the two where a trade-off between an early return and convoluted logic is present.
It comes down to your judgment.
No, it is not wrong to use return this way.
At least look at manual:
http://php.net/manual/en/function.autoload.php
void __autoload ( string $class )
void means that it should not return anything.
But it is not an error.
And also better use require_once when including class definitions.

Creating a folder when I run file_put_contents()

I have uploaded a lot of images from the website, and need to organize files in a better way.
Therefore, I decide to create a folder by months.
$month = date('Yd')
file_put_contents("upload/promotions/".$month."/".$image, $contents_data);
after I tried this one, I get error result.
Message: file_put_contents(upload/promotions/201211/ang232.png): failed to open stream: No such file or directory
If I tried to put only file in exist folder, it worked. However, it failed to create a new folder.
Is there a way to solve this problem?
file_put_contents() does not create the directory structure. Only the file.
You will need to add logic to your script to test if the month directory exists. If not, use mkdir() first.
if (!is_dir('upload/promotions/' . $month)) {
// dir doesn't exist, make it
mkdir('upload/promotions/' . $month);
}
file_put_contents('upload/promotions/' . $month . '/' . $image, $contents_data);
Update: mkdir() accepts a third parameter of $recursive which will create any missing directory structure. Might be useful if you need to create multiple directories.
Example with recursive and directory permissions set to 777:
mkdir('upload/promotions/' . $month, 0777, true);
modification of above answer to make it a bit more generic, (automatically detects and creates folder from arbitrary filename on system slashes)
ps previous answer is awesome
/**
* create file with content, and create folder structure if doesn't exist
* #param String $filepath
* #param String $message
*/
function forceFilePutContents ($filepath, $message){
try {
$isInFolder = preg_match("/^(.*)\/([^\/]+)$/", $filepath, $filepathMatches);
if($isInFolder) {
$folderName = $filepathMatches[1];
$fileName = $filepathMatches[2];
if (!is_dir($folderName)) {
mkdir($folderName, 0777, true);
}
}
file_put_contents($filepath, $message);
} catch (Exception $e) {
echo "ERR: error writing '$message' to '$filepath', ". $e->getMessage();
}
}
i have Been Working on the laravel Project With the Crud Generator and this Method is not Working
#aqm so i have created my own function
PHP Way
function forceFilePutContents (string $fullPathWithFileName, string $fileContents)
{
$exploded = explode(DIRECTORY_SEPARATOR,$fullPathWithFileName);
array_pop($exploded);
$directoryPathOnly = implode(DIRECTORY_SEPARATOR,$exploded);
if (!file_exists($directoryPathOnly))
{
mkdir($directoryPathOnly,0775,true);
}
file_put_contents($fullPathWithFileName, $fileContents);
}
LARAVEL WAY
Don't forget to add at top of the file
use Illuminate\Support\Facades\File;
function forceFilePutContents (string $fullPathWithFileName, string $fileContents)
{
$exploded = explode(DIRECTORY_SEPARATOR,$fullPathWithFileName);
array_pop($exploded);
$directoryPathOnly = implode(DIRECTORY_SEPARATOR,$exploded);
if (!File::exists($directoryPathOnly))
{
File::makeDirectory($directoryPathOnly,0775,true,false);
}
File::put($fullPathWithFileName,$fileContents);
}
I created an simpler answer from #Manojkiran.A and #Savageman. This function can be used as drop-in replacement for file_put_contents. It doesn't support context parameter but I think should be enough for most cases. I hope this helps some people. Happy coding! :)
function force_file_put_contents (string $pathWithFileName, mixed $data, int $flags = 0) {
$dirPathOnly = dirname($pathWithFileName);
if (!file_exists($dirPathOnly)) {
mkdir($dirPathOnly, 0775, true); // folder permission 0775
}
file_put_contents($pathWithFileName, $data, $flags);
}
Easy Laravel solution:
use Illuminate\Support\Facades\File;
// If the directory does not exist, it will be create
// Works recursively, with unlimited number of subdirectories
File::ensureDirectoryExists('my/super/directory');
// Write file content
File::put('my/super/directory/my-file.txt', 'this is file content');
I wrote a function you might like. It is called forceDir(). It basicaly checks whether the dir you want exists. If so, it does nothing. If not, it will create the directory. A reason to use this function, instead of just mkdir, is that this function can create nexted folders as well.. For example ('upload/promotions/januari/firstHalfOfTheMonth'). Just add the path to the desired dir_path.
function forceDir($dir){
if(!is_dir($dir)){
$dir_p = explode('/',$dir);
for($a = 1 ; $a <= count($dir_p) ; $a++){
#mkdir(implode('/',array_slice($dir_p,0,$a)));
}
}
}

Categories