File system organization automation – moving thousands of files

I am trying to organize large thousands of files.
The task is not healthy if done by hand; moreover, it is prone to human error. So, I wrote an automated solution, using PHP and some of its file system calls.

The problem is described in the main function comments, here shared in this post. Imagine thousands of folders, each eventually containing a single specially named sub-folder (e.g. “_”) whose contents (files and directories) you want to move to its parent root. This was interesting to write because it shows the power of a language’s file system tools for automation in files organization.

Check the “before” and “after” images for the simplest example I managed to capture in a visual representation.

/*
 * 2019-07-04 started
 * 2019-07-04 first tested ok - test preserved in file "test_20190704.php"
 * learned: https://www.php.net/manual/en/class.splfileinfo.php
 * the idea is to move the contents in <some folder>\_\<contents> to <some folder>\<contents>
 * the "_" is the product of a rename "shortener" tool - it facilitates in avoiding very long paths that result from some jobs
 * however, some archives when unpacked will still have sub-folders inside the _ folder
 * and that organization creates problems to other tools of mine
 * this function should received a folder F path, of a folder which might contain a "_" named sub folder
 * then it will move _'s files and folders to the F's root
 *
 * @param string $pStrFolderPath : some folder path
 * @param string $pStrSpecificNameOfSubFolderWhoseContentsAreToBeMovedToParentFolder : optional, the name of the special folder, defaults to "_"
 * @param bool $pbFeedback : verbose activity, giving feedback to user? Defaults to true
 * @param bool $pbCaseSensitive : case sensitive in checking the folder? Default to false and is not relevant for folders with names like "_"
 * @return int : the number of successful ren/move operations
 */
function moveEverythingInFolderWithSpecificNameIfItExistsInPathToItsParentFolder (
    string $pStrFolderPath,
    string $pStrSpecificNameOfSubFolderWhoseContentsAreToBeMovedToParentFolder = "_",
    $pbFeedback=true,
    $pbCaseSensitive = false
)
{
    $bPathExists=file_exists($pStrFolderPath);
    $iSuccessfulRenamesCounter=$iFailedRenames=0;

    if ($pbFeedback){
        feedbackOnFunctionCall(
            __FUNCTION__,
            func_get_args()
        );
    }//if

    if ($bPathExists) {
        //dirsInDir does NOT return the dots dirs . and ..
        $dirsInDir = dirsInDir(
            $pStrFolderPath,
            false //not recursive
        );
        $iHowManyDirsInDir = count($dirsInDir);
        $bRightNumberOfDirs = ($iHowManyDirsInDir === 1); // $pStrSpecificNameOfSubFolderWhoseContentsAreToBeMovedToParentFolder

        //there should be only 1 dir if the move op is to be safe in preserving the contents' hierarchy
        if ($bRightNumberOfDirs) {
            $oSingleDirItemObject = $dirsInDir[0];
            $strDirName = $oSingleDirItemObject["fname"];

            $bFolderHasTheSearchedForName =
                $pbCaseSensitive ?
                    //sensitive
                    strcmp(
                        $strDirName,
                        $pStrSpecificNameOfSubFolderWhoseContentsAreToBeMovedToParentFolder
                    ) === 0

                    :
                    //insensitive
                    strcasecmp(
                        $strDirName,
                        $pStrSpecificNameOfSubFolderWhoseContentsAreToBeMovedToParentFolder
                    ) === 0;

            $oParentFolderPath = new SplFileInfo ($pStrFolderPath);
            $strParentDirectoryFullPath = $oParentFolderPath->getRealPath();

            if ($bFolderHasTheSearchedForName) {
                $strOldLocationOfSearchedFolder = $oSingleDirItemObject["realPath"];

                //dirItems is a function of mine, returning an assoc array representing each dir in a path, except the dot dirs (. and ..)
                $aItemsInSearchedFolder = dirItems(
                    $strOldLocationOfSearchedFolder,
                    "*",
                    false //not recursive
                );

                foreach ($aItemsInSearchedFolder as $itemAsAssocArray) {
                    $strOldLocationOfSearchedFolder = $itemAsAssocArray["realPath"];
                    $strItemName = $itemAsAssocArray["fname"];
                    $strNewLocationForSearchedFolderContents = $strParentDirectoryFullPath . "\\" . $strItemName;

                    $bRenameMoveItemOK = rename(
                        $strOldLocationOfSearchedFolder,
                        $strNewLocationForSearchedFolderContents
                    );
                    if ($bRenameMoveItemOK) {
                        $iSuccessfulRenamesCounter++;
                    }//if ok
                    else {
                        echo "Failed in moving $strOldLocationOfSearchedFolder to $strNewLocationForSearchedFolderContents" . PHP_EOL;
                        $iFailedRenames++;
                    }//else
                }//foreach item in folder

                $strFullPathOfSearchedFolder = $oSingleDirItemObject['realPath'];
                $aItemsInSearchedFolder = dirItems($strFullPathOfSearchedFolder);
                $bEmptyFolder = count($aItemsInSearchedFolder)===0;
                if ($bEmptyFolder){
                    $rmResult = rmdir($strFullPathOfSearchedFolder); //return not used, but it is a bool
                }//if
            }//if found the searched folder
        }//if there is exactly 1 folder in the path
    }//if path exists

    return $iSuccessfulRenamesCounter;
}//moveEverythingInFolderWithSpecificNameIfItExistsInPathToItsParentFolder



mover_after.png
https://arturmarques.com/wp/wp-content/uploads/2019/07/mover_after.png (image/png)

mover_after.png


mover_before.png
https://arturmarques.com/wp/wp-content/uploads/2019/07/mover_before.png (image/png)

mover_before.png

Technical Details

A quick solution to check if a string ends with…

Today I was coding a PHP solution to remove tracking data from URLs. To help in doing so, my approach to the problem requires being able to check if a string ends in another. PHP doesn’t seem to have a prebuilt “endsWith” function.
So, I wrote an auxiliary tool to do just that (check if a string ends in another), dumped it as static method to an “Util” class of mine (being static, no need to instantiate the class to use it), and gave it the bonus of being able to do the checking in a case sensitive fashion, or not.

    //_.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-
    /*
     * 2019-07-03
     * @param string $pStr : a string
     * @param string $pStrTermination : a string to be tested as a termination of the first
     * @return bool : true if indeed $pStrTermination is at the end of $pStr
     */
    public static function auxCheckIfStringsEndsWith(
        string $pStr, //some string
        string $pStrTermination, //some string that eventually is at the end of $pStr
        bool $pbCaseSensitive = false //by default, don't be case sensitive in checking
    ){
        $iLengthOfString = strlen($pStr);
        $iLengthOfTermination = strlen($pStrTermination);
        $bItCanBeATermination = $iLengthOfString>=$iLengthOfTermination;

        if ($bItCanBeATermination){
            $iPosWhereEventualTerminationStarts =
                $pbCaseSensitive ?
                strpos($pStr, $pStrTermination) //case sensitive checking
                :
                stripos($pStr, $pStrTermination); //case insensitive checking

            if ($iPosWhereEventualTerminationStarts!==false){
                //termination exists, but is it at the exact end of string?
                $bAtTheExactEnd = $iPosWhereEventualTerminationStarts === $iLengthOfString - $iLengthOfTermination;
                return $bAtTheExactEnd;
            }//if
        }//if
        return false;
    }//auxCheckIfStringsEndsWith

URLs "p1" 20190703 – 78 quality, diversified, international resources

I am an avid WWW surfer, with hundreds of websites visited each month, sometimes daily. I bookmark them all, at least for logging purposes. These posts having the "urls" category, capture what was on my browser on a specific date. I hope you enjoy some of these shared resources.


NHK DOC – Technological Unemployment

NHK World’s Documentary code-named “4001-331”, was my choice of programme for the last day of June 2019. It is about Artificial Intelligence (A.I.) and robots, and their effects in society as a whole, although more focused on employment. This high quality content puts nearly everything else on other networks to shame. It will probably teach you something. More importantly, it will make you think and spark a hunger to research and discuss some questions.

It is hard to pick “highlights” of the documentary; such is its constant high level.
It was great to see John Keynes’ “Economic Possibilities for our Grandchildren” depicted (actual images of that academic paper), where the economist writes about “technological unemployment”, something that many argue we are about to see in bigger relative terms than ever before.
I also recall two images of New York, taken 10 years part, to illustrate how fast horses disappeared from the workforce. In 1903, horses were everywhere on the roads; 10 years later, only cars were to be seen.
Despite these two strong representations of thinking and technological change, the main merit of the video is how full of good examples it is. The viewer is given short, but intensely rich segments that show A.I. and robots at work at strawberry picking, design fabrication, automatic convenience stores, replacing bank tellers, replacing couriers, etc.

Finally, NHK reports on one experiment of “universal income” which was running at Hamilton, Canada. That experiment does not end well, being suspend earlier than its intended original length. “Universal income” is a must, that society is only delaying. There will not be employment for all, at least with a decent wage. Unhappy people are sick people, much limited on their potential contributions. Just imagine everyone being given a minimum, regardless of other income streams. I believe that, on time, criminality would be lower, health problems (including metal health) would decline, and from these two factors alone, an enormous amount of expenses would disappear. Moreover, with people becoming more available to pursue higher, unique, even creative, activities, more and innovations would become available, eventually releasing civilization from the rat race that traps most.



nhk_doc_4001_331_01.png
https://arturmarques.com/wp/wp-content/uploads/2019/07/nhk_doc_4001_331_01.png (image/png)

nhk_doc_4001_331_01.png


nhk_doc_4001_331_03.png
https://arturmarques.com/wp/wp-content/uploads/2019/07/nhk_doc_4001_331_03.png (image/png)

nhk_doc_4001_331_03.png


nhk_doc_4001_331_04.jpg
https://arturmarques.com/wp/wp-content/uploads/2019/07/nhk_doc_4001_331_04.jpg (image/jpeg)

nhk_doc_4001_331_04.jpg

Technical Details
  • ./ups/nhk_doc_4001_331_01.png
    • attachment_id : 358
    • date_created_gmt : 2019-07-01 22:37:41
    • parent : 0
    • link : https://arturmarques.com/wp/wp-content/uploads/2019/07/nhk_doc_4001_331_01.png
    • title : nhk_doc_4001_331_01.png
    • caption :
    • description :
    • metadata :
      • width : 1928
      • height : 1205
      • file : 2019/07/nhk_doc_4001_331_01.png
      • sizes :
        • thumbnail :
          • file : nhk_doc_4001_331_01-150×150.png
          • width : 150
          • height : 150
          • mime-type : image/png
        • medium :
          • file : nhk_doc_4001_331_01-300×188.png
          • width : 300
          • height : 188
          • mime-type : image/png
        • medium_large :
          • file : nhk_doc_4001_331_01-768×480.png
          • width : 768
          • height : 480
          • mime-type : image/png
        • large :
          • file : nhk_doc_4001_331_01-1024×640.png
          • width : 1024
          • height : 640
          • mime-type : image/png
        • post-thumbnail :
          • file : nhk_doc_4001_331_01-672×372.png
          • width : 672
          • height : 372
          • mime-type : image/png
        • twentyfourteen-full-width :
          • file : nhk_doc_4001_331_01-1038×576.png
          • width : 1038
          • height : 576
          • mime-type : image/png
      • image_meta :
        • aperture : 0
        • credit :
        • camera :
        • caption :
        • created_timestamp : 0
        • copyright :
        • focal_length : 0
        • iso : 0
        • shutter_speed : 0
        • title :
        • orientation : 0
        • keywords :
      • type : image/png
      • thumbnail : https://arturmarques.com/wp/wp-content/uploads/2019/07/nhk_doc_4001_331_01-150×150.png
      • id : 358
      • file : nhk_doc_4001_331_01.png
      • url : https://arturmarques.com/wp/wp-content/uploads/2019/07/nhk_doc_4001_331_01.png
    • ./ups/nhk_doc_4001_331_03.png
      • attachment_id : 359
      • date_created_gmt : 2019-07-01 22:38:07
      • parent : 0
      • link : https://arturmarques.com/wp/wp-content/uploads/2019/07/nhk_doc_4001_331_03.png
      • title : nhk_doc_4001_331_03.png
      • caption :
      • description :
      • metadata :
        • width : 1928
        • height : 1205
        • file : 2019/07/nhk_doc_4001_331_03.png
        • sizes :
          • thumbnail :
            • file : nhk_doc_4001_331_03-150×150.png
            • width : 150
            • height : 150
            • mime-type : image/png
          • medium :
            • file : nhk_doc_4001_331_03-300×188.png
            • width : 300
            • height : 188
            • mime-type : image/png
          • medium_large :
            • file : nhk_doc_4001_331_03-768×480.png
            • width : 768
            • height : 480
            • mime-type : image/png
          • large :
            • file : nhk_doc_4001_331_03-1024×640.png
            • width : 1024
            • height : 640
            • mime-type : image/png
          • post-thumbnail :
            • file : nhk_doc_4001_331_03-672×372.png
            • width : 672
            • height : 372
            • mime-type : image/png
          • twentyfourteen-full-width :
            • file : nhk_doc_4001_331_03-1038×576.png
            • width : 1038
            • height : 576
            • mime-type : image/png
        • image_meta :
          • aperture : 0
          • credit :
          • camera :
          • caption :
          • created_timestamp : 0
          • copyright :
          • focal_length : 0
          • iso : 0
          • shutter_speed : 0
          • title :
          • orientation : 0
          • keywords :
        • type : image/png
        • thumbnail : https://arturmarques.com/wp/wp-content/uploads/2019/07/nhk_doc_4001_331_03-150×150.png
        • id : 359
        • file : nhk_doc_4001_331_03.png
        • url : https://arturmarques.com/wp/wp-content/uploads/2019/07/nhk_doc_4001_331_03.png
      • ./ups/nhk_doc_4001_331_04.jpg
        • attachment_id : 360
        • date_created_gmt : 2019-07-01 22:38:23
        • parent : 0
        • link : https://arturmarques.com/wp/wp-content/uploads/2019/07/nhk_doc_4001_331_04.jpg
        • title : nhk_doc_4001_331_04.jpg
        • caption :
        • description :
        • metadata :
          • width : 1928
          • height : 1205
          • file : 2019/07/nhk_doc_4001_331_04.jpg
          • sizes :
            • thumbnail :
              • file : nhk_doc_4001_331_04-150×150.jpg
              • width : 150
              • height : 150
              • mime-type : image/jpeg
            • medium :
              • file : nhk_doc_4001_331_04-300×188.jpg
              • width : 300
              • height : 188
              • mime-type : image/jpeg
            • medium_large :
              • file : nhk_doc_4001_331_04-768×480.jpg
              • width : 768
              • height : 480
              • mime-type : image/jpeg
            • large :
              • file : nhk_doc_4001_331_04-1024×640.jpg
              • width : 1024
              • height : 640
              • mime-type : image/jpeg
            • post-thumbnail :
              • file : nhk_doc_4001_331_04-672×372.jpg
              • width : 672
              • height : 372
              • mime-type : image/jpeg
            • twentyfourteen-full-width :
              • file : nhk_doc_4001_331_04-1038×576.jpg
              • width : 1038
              • height : 576
              • mime-type : image/jpeg
          • image_meta :
            • aperture : 0
            • credit :
            • camera :
            • caption :
            • created_timestamp : 0
            • copyright :
            • focal_length : 0
            • iso : 0
            • shutter_speed : 0
            • title :
            • orientation : 0
            • keywords :
          • type : image/jpeg
          • thumbnail : https://arturmarques.com/wp/wp-content/uploads/2019/07/nhk_doc_4001_331_04-150×150.jpg
          • id : 360
          • file : nhk_doc_4001_331_04.jpg
          • url : https://arturmarques.com/wp/wp-content/uploads/2019/07/nhk_doc_4001_331_04.jpg

        Did you know? You can NOT retrieve an input type file true path

        Did you know? You can NOT retrieve an input type file true path

        Did you know? In modern HTML/JS code, you can NOT retrieve the true full path of a selected file, via the "value" attribute of an input of type "file".

        So, if you wrote this EXAMPLE.HTML text file, saved it, loaded it on a browser, the output would be "c:\fakepath\your file" upon click, after any file selection.

        <input type="file" id="idFile">
        <input type="button" id="idButton" value="what is the selected path?">
        <script>
        document.getElementById("idButton").onclick = function(){
        alert(document.getElementById("idFile").value);
        }
        </script>

        Team AM League since Austria

        I started a Formula 1 “Fantasy League” named “Team AM League since Austria.” I am quite late to this F1 fantasy thing, and I am not quite sure I understand it or its purpose. For example, I built my team ahead of today’s Austria GP, and my picks scored big (including the top 3: Verstappen, Leclerc, Bottas), but last time I checked I had no points. Maybe that is because the results aren’t yet official, I don’t know.
        https://fantasy.formula1.com/leaderboards/league?league_id=158202

        I will not be sending emails, but anyone is welcome to join with the code 0ac53ce019. If you prefer, here is a direct link to subscribe:
        https://fantasy.formula1.com/join?league_code=0ac53ce019