Asked  7 Months ago    Answers:  5   Viewed   43 times

I like to know how can I get the hierarchy by the identifier Key NAME, just the values with PHP. I already had try some implode functions but without success. I appreciated any help. Thanks
This is an example :

$treeArray = (Array
(
    [0] => Array
        (
            [name] => S-ATLANTICO-1
            [id] => 1HIk_jh2GHo2VnBbUI8c3P9cADY4NnKQ5
            [parents] => 
            [children] => Array
                (
                    [0] => Array
                        (
                            [name] => TESTE
                            [id] => 1EYi_CF7gjANq_MPnUOkquJI609Jkhzf0
                            [parents] => 1HIk_jh2GHo2VnBbUI8c3P9cADY4NnKQ5
                        )

                    [1] => Array
                        (
                            [name] => SAPO
                            [id] => 1I8QxJiMa11U2s4ncPxyqfdCPk_6dQ9Tl
                            [parents] => 1HIk_jh2GHo2VnBbUI8c3P9cADY4NnKQ5
                            [children] => Array
                                (
                                    [0] => Array
                                        (
                                            [name] => SAPO-1
                                            [id] => 1KGzjcy79TCKp-c6T1Xxm5WqswXhqFlb7
                                            [parents] => 1I8QxJiMa11U2s4ncPxyqfdCPk_6dQ9Tl
                                        )

                                    [1] => Array
                                        (
                                            [name] => carlos.csv
                                            [id] => 1eHU_r5GJCXualVQMhurd6FwOoD_h3hHG
                                            [parents] => 1I8QxJiMa11U2s4ncPxyqfdCPk_6dQ9Tl
                                        )

                                    [2] => Array
                                        (
                                            [name] => logo-news_sa.png
                                            [id] => 16HnsOxzDEow710jNfda7Mtt-8qsLwSeG
                                            [parents] => 1I8QxJiMa11U2s4ncPxyqfdCPk_6dQ9Tl
                                        )

                                )

                        )

                    [2] => Array
                        (
                            [name] => DOCUMENTOS
                            [id] => 1viFriBa2GaSxiLG8nDYIDd-sFl545T31
                            [parents] => 1HIk_jh2GHo2VnBbUI8c3P9cADY4NnKQ5
                            [children] => Array
                                (
                                    [0] => Array
                                        (
                                            [name] => carlos-excel
                                            [id] => 16YyMy3F9QMzRT5XtCMVJygKDEVVSRpAVmAjh0XU3luY
                                            [parents] => 1viFriBa2GaSxiLG8nDYIDd-sFl545T31
                                        )

                                    [1] => Array
                                        (
                                            [name] => carlos-excel.xlsx
                                            [id] => 1GbJ9YmwuRiUmnT8jRJ9pLZI7acqO4eu-
                                            [parents] => 1viFriBa2GaSxiLG8nDYIDd-sFl545T31
                                        )

                                    [2] => Array
                                        (
                                            [name] => SAPO
                                            [id] => 1DRBPJHPxRSeaa7zC8yF-UgGbo9tpwOsOxAusfEuGtsY
                                            [parents] => 1viFriBa2GaSxiLG8nDYIDd-sFl545T31
                                        )

                                    [3] => Array
                                        (
                                            [name] => PRECIOS
                                            [id] => 1S0lNS7bKOK6wBxhCZcB7uaNBXA1GMqlxwtTdSBc4f9U
                                            [parents] => 1viFriBa2GaSxiLG8nDYIDd-sFl545T31
                                        )

                                )

                        )

                )

        )

)
);

This result would be ok: in array or print in screen line by line.

S-ATLANTICO-1/
S-ATLANTICO-1/TESTE
S-ATLANTICO-1/SAPO
S-ATLANTICO-1/SAPO/SAPO-1
S-ATLANTICO-1/SAPO/carlos.csv
S-ATLANTICO-1/SAPO/logo-news_sa.png
S-ATLANTICO-1/DOCUMENTOS
S-ATLANTICO-1/DOCUMENTOS/carlos-excel
S-ATLANTICO-1/DOCUMENTOS/carlos-excel.xlsx
S-ATLANTICO-1/DOCUMENTOS/SAPO
S-ATLANTICO-1/DOCUMENTOS/PRECIOS

 Answers

55

This should be something like this:

function rec($arr, $prefix ="") {
    if ($prefix != "") $prefix .= "/";
    foreach($arr as $e) {
        echo $prefix . $e['name'];
        if (!empty($e['children']))
            rec($e['children'], $prefix . $e['name']);
    }
}

I not on computer so this pseudo code only...

Saturday, May 29, 2021
 
hakre
answered 7 Months ago
39

When you set cell values individually, you have the option of setting the datatype explicitly, but when you use the fromArray() method, you don't have this option.

However, by default, PHP uses a default value binder to identify datatypes from the values passed, and set the cell datatype accordingly. This default behaviour is defined in a class /PHPExcel/Cell/DefaultValueBinder.php.

So you can create your own value binder, as described in the PHPExcel Documentation, that would set every value as a string datatype.

Something like:

class PHPExcel_Cell_MyColumnValueBinder extends PHPExcel_Cell_DefaultValueBinder implements PHPExcel_Cell_IValueBinder
{
    protected $stringColumns = [];

    public function __construct(array $stringColumnList = []) {
        // Accept a list of columns that will always be set as strings
        $this->stringColumns = $stringColumnList;
    }

    public function bindValue(PHPExcel_Cell $cell, $value = null)
    {
        // If the cell is one of our columns to set as a string...
        if (in_array($cell->getColumn(), $this->stringColumns)) {
            // ... then we cast it to a string and explicitly set it as a string
            $cell->setValueExplicit((string) $value, PHPExcel_Cell_DataType::TYPE_STRING);
            return true;
        }
        // Otherwise, use the default behaviour
        return parent::bindValue($cell, $value);
    }
}

// Instantiate our custom binder, with a list of columns, and tell PHPExcel to use it
PHPExcel_Cell::setValueBinder(new PHPExcel_Cell_MyColumnValueBinder(['A', 'B', 'C', 'E', 'F']));

$objPHPExcel = new PHPExcel();
$objPHPExcel->getActiveSheet()->fromArray($dataArray,null,"A2");
Friday, May 28, 2021
 
Wilk
answered 7 Months ago
20

There are many advantages in C# to using jagged arrays (array[][]). They actually will often outperform multidimensional arrays.

That being said, I would personally use a multidimensional or jagged array instead of a single dimensional array, as this matches the problem space more closely. Using a one dimensional array is adding complexity to your implementation that does not provide real benefits, especially when compared to a 2D array, as internally, it's still a single block of memory.

Thursday, August 5, 2021
 
ChriskOlson
answered 4 Months ago
80

What you are looking for is also called recursive directory traversing. Which means, you're going through all directories and list subdirectories and files in there. If there is a subdirectory it is traversed as well and so on and so forth - so it is recursive.

As you can imagine this is somewhat a common thing you need when you write a software and PHP supports you with that. It offers one RecursiveDirectoryIterator so that directories can be recursively iterated and the standard RecursiveIteratorIterator to do the traversal. You can then easily access all files and directories with a simple iteration, for example via foreach:

$rootpath = '.';
$fileinfos = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator($rootpath)
);
foreach($fileinfos as $pathname => $fileinfo) {
    if (!$fileinfo->isFile()) continue;
    var_dump($pathname);
}

This example first of all specifies the directory you want to traverse. I've been taking the current one:

$rootpath = '.';

The next line of code is a little bit long, it does instantiate the directory iterator and then the iterator-iterator so that the tree-like structure can be traversed in a single/flat loop:

$fileinfos = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator($rootpath)
);

These $fileinfos are then iterated with a simple foreach:

foreach($fileinfos as $pathname => $fileinfo) {

Inside of it, there is a test to skip all directories from being output. This is done by using the SplFileInfo object that is iterated over. It is provided by the recursive directory iterator and contains a lot of helpful properties and methods when working with files. You can as well for example return the file extension, the basename information about size and time and so on and so forth.

if (!$fileinfo->isFile()) continue;

Finally I just output the pathname that is the full path to the file:

var_dump($pathname);

An exemplary output would look like this (here on a windows operating system):

string(12) "..buildpath"
string(11) "..htaccess"
string(33) ".domxml-attacksattacks-xml.php"
string(38) ".domxml-attacksbillion-laughs-2.xml"
string(36) ".domxml-attacksbillion-laughs.xml"
string(40) ".domxml-attacksquadratic-blowup-2.xml"
string(40) ".domxml-attacksquadratic-blowup-3.xml"
string(38) ".domxml-attacksquadratic-blowup.xml"
string(22) ".domxmltree-dump.php"
string(25) ".domxpath-list-tags.php"
string(22) ".domxpath-search.php"
string(27) ".domxpath-text-search.php"
string(29) ".encrypt-decryptdecrypt.php"
string(29) ".encrypt-decryptencrypt.php"
string(26) ".encrypt-decrypttest.php"
string(13) ".favicon.ico"

If there is a subdirectory that is not accessible, the following would throw an exception. This behaviour can be controlled with some flags when instantiating the RecursiveIteratorIterator:

$fileinfos = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator('.'),
    RecursiveIteratorIterator::LEAVES_ONLY,
    RecursiveIteratorIterator::CATCH_GET_CHILD
);

I hope this was informative. You can also Wrap this up into a class of your own and you can also provide a FilterIterator to move the decision whether a file should be listed or not out of the foreach loop.


The power of the RecursiveDirectoryIterator and RecursiveIteratorIterator combination comes out of its flexibility. What was not covered above are so called FilterIterators. I thought I add another example that is making use of two self-written of them, placed into each other to combine them.

  • One is to filter out all files and directories that start with a dot (those are considered hidden files on UNIX systems so you should not give that information to the outside) and
  • Another one that is filtering the list to files only. That is the check that previously was inside the foreach.

Another change in this usage example is to make use of the getSubPathname() function that returns the subpath starting from the iteration's rootpath, so the one you're looking for.

Also I explicitly add the SKIP_DOTS flag which prevents traversing . and .. (technically not really necessary because the filters would filter those as well as they are directories, however I think it is more correct) and return as paths as UNIX_PATHS so the strings of paths are always unix-like paths regardless of the underlying operating system Which is normally a good idea if those values are requested via HTTP later as in your case:

$rootpath = '.';

$fileinfos = new RecursiveIteratorIterator(
    new FilesOnlyFilter(
        new VisibleOnlyFilter(
            new RecursiveDirectoryIterator(
                $rootpath,
                FilesystemIterator::SKIP_DOTS
                    | FilesystemIterator::UNIX_PATHS
            )
        )
    ),
    RecursiveIteratorIterator::LEAVES_ONLY,
    RecursiveIteratorIterator::CATCH_GET_CHILD
);

foreach ($fileinfos as $pathname => $fileinfo) {
    echo $fileinfos->getSubPathname(), "n";
}

This example is similar to the previous one albeit how the $fileinfos is build is a little differently configured. Especially the part about the filters is new:

    new FilesOnlyFilter(
        new VisibleOnlyFilter(
            new RecursiveDirectoryIterator($rootpath, ...)
        )
    ),

So the directory iterator is put into a filter and the filter itself is put into another filter. The rest did not change.

The code for these filters is pretty straight forward, they work with the accept function that is either true or false which is to take or to filter out:

class VisibleOnlyFilter extends RecursiveFilterIterator
{
    public function accept()
    {
        $fileName = $this->getInnerIterator()->current()->getFileName();
        $firstChar = $fileName[0];
        return $firstChar !== '.';
    }
}

class FilesOnlyFilter extends RecursiveFilterIterator
{
    public function accept()
    {
        $iterator = $this->getInnerIterator();

        // allow traversal
        if ($iterator->hasChildren()) {
            return true;
        }

        // filter entries, only allow true files
        return $iterator->current()->isFile();
    }
}

And that's it again. Naturally you can use these filters for other cases, too. E.g. if you have another kind of directory listing.

And another exemplary output with the $rootpath cut away:

test.html
test.rss
tests/test-pad-2.php
tests/test-pad-3.php
tests/test-pad-4.php
tests/test-pad-5.php
tests/test-pad-6.php
tests/test-pad.php
TLD/PSL/C/dkim-regdom.c
TLD/PSL/C/dkim-regdom.h
TLD/PSL/C/Makefile
TLD/PSL/C/punycode.pl
TLD/PSL/C/test-dkim-regdom.c
TLD/PSL/C/test-dkim-regdom.sh
TLD/PSL/C/tld-canon.h
TLD/PSL/generateEffectiveTLDs.php

No more .git or .svn directory traversal or listing of files like .builtpath or .project.


Note for FilesOnlyFilter and LEAVES_ONLY: The filter explicitly denies the use of directories and links based on the SplFileInfo object (only regular files that do exist). So it is a real filtering based on the file-system.
Another method to only get non-directory entries ships with RecursiveIteratorIterator because of the default LEAVES_ONLY flag (here used too in the examples). This flag does not work as a filter and is independent to the underlying iterator. It just specifies that the iteration should not return branchs (here: directories in case of the directory iterator).

Sunday, August 8, 2021
 
Aidan D
answered 4 Months ago
42

If you want $some_array['array_key'] to be an array of values, you have to initialize it as an array, like this:

$some_array['array_key'] = array('some string');

Only then can you use array_push() or the [] = notation:

$some_array['array_key'][] = 'another string';
Tuesday, August 17, 2021
 
pamelus
answered 4 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :
 
Share