How to output multi-dimensional array to CSV file in PHP? - php

I'm having a bit of an issue with converting my multidimensional array to a CSV file. The issue is that it also contains more arrays within the array.
I'm trying to convert the output of all the entries and attributes of an LDAP server into a CSV file.
A really similar question to mine can be found here: link
Essentially, I'll need the same output as the excel spreadsheet in that question, except for my fields. The issue is that not every LDAP entry has the same attributes. So some of the boxes will be empty.
So for instance I'll have
DN, ObjectClasses, Email, Phone Number, CN, UserID
cn=admin,dc=example,dc=com, top, email#gmail.com, 123-456-7890, training, 12345
But some of my arrays don't have all elements, so for instance, they are missing an email or phone number, but the rest of the elements do have the email or phone number, so I can't manually just enter all the attributes. (Also can't do this as I don't know all of the attributes that the user will need).
Here's my current output:
Amount of entries: 23dc=example,dc=com,objectclass,top,dcObject,organization,example.com,example,cn=admin,dc=example,dc=com,objectclass,simpleSecurityObject,organizationalRole,admin,"LDAP administrator",uid=newton,dc=example,dc=com,Newton,objectclass,inetOrgPerson,organizationalPerson,person,top,newton,newton#ldap.forumsys.com,"Isaac Newton",uid=einstein,dc=example,dc=com,objectclass,inetOrgPerson,organizationalPerson,person,top,"Albert Einstein",Einstein,einstein,einstein#ldap.forumsys.com,314-159-2653,uid=tesla,dc=example,dc=com,objectclass,inetOrgPerson,organizationalPerson,person,top,posixAccount,"Nikola Tesla",Tesla,tesla,tesla#ldap.forumsys.com,88888,99999,home,uid=galieleo,dc=example,dc=com,objectclass,inetOrgPerson,organizationalPerson,person,top,"Galileo Galilei",Galilei,galieleo,galieleo#ldap.forumsys.com,uid=euler,dc=example,dc=com,objectclass,inetOrgPerson,organizationalPerson,person,top,euler,Euler,"Leonhard Euler",euler#ldap.forumsys.com
Basically, it's just completely unorganized (don't mind the amount of entries: 23 part)
I was wondering how I could organize all my outputs?
Here's my current code:
<?php
session_start();
$ldaprdn = 'cn=read-only-admin,dc=example,dc=com';
$ldappass = 'password';
$ldapuri = "ldap.forumsys.com";
// Connecting to LDAP
$ldapconn = ldap_connect($ldapuri)
or die("That LDAP-URI was not parseable");
//We need to set the LDAP Protocol Version or else it isn't able to bind properly to the LDAP server.
ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3);
//We bind to the LDAP server using the previous credentials and location.
$ldapbind = ldap_bind($ldapconn, $ldaprdn, $ldappass);
//Gives where to search & what to search for
$dn = "dc=example,dc=com";
$filter = "(objectclass=*)";
//Saves the result into result variable
$result = ldap_search($ldapconn, $dn, $filter);
$info = ldap_get_entries($ldapconn, $result);
//Saves all the attributes of all entries into an array.
function cleanUpEntry( $entry ) {
$retEntry = array();
for ( $i = 0; $i < $entry["count"]; $i++ ) {
if (is_array($entry[$i])) {
$subtree = $entry[$i];
//This condition should be superfluous so just take the recursive call
//adapted to your situation in order to increase perf.
if ( ! empty($subtree['dn']) and ! isset($retEntry[$subtree['dn']])) {
$retEntry[$subtree['dn']] = cleanUpEntry($subtree);
}
else {
$retEntry[] = cleanUpEntry($subtree);
}
}
else {
$attribute = $entry[$i];
if ( $entry[$attribute]['count'] == 1 ) {
$retEntry[$attribute] = $entry[$attribute][0];
} else {
for ( $j = 0; $j < $entry[$attribute]['count']; $j++ ) {
$retEntry[$attribute][] = $entry[$attribute][$j];
}
}
}
}
return $retEntry;
}
echo "Amount of entries: ".(sizeof(cleanUpEntry($info)));
/*
//Prints all the elements in the array
echo "<pre>";
print_r(cleanUpEntry($info));
echo "</pre>";*/
function array_flatten($array) {
if (!is_array($array)) {
return FALSE;
}
$result = array();
foreach ($array as $key => $value) {
if (is_array($value)) {
$arrayList=array_flatten($value);
foreach ($arrayList as $listItem) {
$result[] = $listItem;
}
}
else {
$result[$key] = $value;
}
}
return $result;
}
function arrayToValues(array $data)
{
$output = array();
foreach ($data as $key => $item) {
if (is_array($item)) {
$output = array_merge($output, array($key), arrayToValues($item));
} else {
$output[] = $item;
}
}
return $output;
}
$csvData = implode(',', arrayToValues(cleanUpEntry($info)));
$csvExport = explode(',', $csvData);
header('Content-Type: application/csv');
header('Content-Disposition: attachment; filename="export.csv";');
$f = fopen('php://output', 'w');
fputcsv($f, arrayToValues($csvExport));
?>

Related

Incrementing the value in multidimensional array in php

I couldn't understand the multidimensional array in PHP properly. I have a CSV file having two columns as shown below:
I am trying to create an array of array, in which each key is a cataegory. However, the value of each key is an array. In this array, each key is company and value is the count of the product. See below the code:
<?php
//array contains value
function contains_value($my_array, $value_search){
foreach ($my_array as $key => $value) {
if ($value === $value_search)
return true;
}
return false;
}
//array contains key
function contains_key($my_array, $key_search){
foreach ($my_array as $key => $value) {
if ($key === $key_search)
return true;
}
return false;
}
$handle = fopen("product_list.csv", "r");
$products = array();
if ($handle) {
while (($line = fgets($handle)) !== false) {
$product = explode(",", $line);
$category = $product[0];
$company = $product[1];
if (contains_key($products, $category)) {
if (contains_value($products, $company)) {
//increase the count of category by 1
$products[$category][$company] = $products[$category][$company] + 1;
} else {
//append new company with count 1
array_push($products[$category], array(
$company,
1
));
}
} else {
//initialize new company with count 1
$products[$category] = array(
$company,
1
);
}
}
fclose($handle);
}
var_dump($products);
?>
I noticed that the var_dump($products) is not showing correction information. I am expecting following kind of result:
I haven't enough reputation to reply, but I think he need counts.
To complete the answer of Alive to Die, more something like this:
if (!array_key_exists($category, $products)) {
products[$category] = [];
}
if (!array_key_exists($company, $products[$category])) {
products[$category][$company] = 0;
}
++$results[$cataegory][$company];
But cleaner ;)
Edit:
If I remember well, his first idea was this:
$products[$category][] = $company;
The code is shorter. Maybe you can combine the two ideas.

Pulling NHL Standings from XML Table with PHP

I'm working on a project in which I pull various statistics about the NHL and inserting them into an SQL table. Presently, I'm working on the scraping phase, and have found an XML parser that I've implemented, but I cannot for the life of me figure out how to pull information from it. The table can be found here -> http://www.tsn.ca/datafiles/XML/NHL/standings.xml.
The parser supposedly generates a multi-dimmensional array, and I'm simply trying to pull all the stats from the "info-teams" section, but I have no idea how to pull that information from the array. How would I go about pulling the number of wins Montreal has? (Solely as an example for the rest of the stats)
This is what the page currently looks like -> http://mattegener.me/school/standings.php
here's the code:
<?php
$strYourXML = "http://www.tsn.ca/datafiles/XML/NHL/standings.xml";
$fh = fopen($strYourXML, 'r');
$dummy = fgets($fh);
$contents = '';
while ($line = fgets($fh)) $contents.=$line;
fclose($fh);
$objXML = new xml2Array();
$arrOutput = $objXML->parse($contents);
print_r($arrOutput[0]); //This print outs the array.
class xml2Array {
var $arrOutput = array();
var $resParser;
var $strXmlData;
function parse($strInputXML) {
$this->resParser = xml_parser_create ();
xml_set_object($this->resParser,$this);
xml_set_element_handler($this->resParser, "tagOpen", "tagClosed");
xml_set_character_data_handler($this->resParser, "tagData");
$this->strXmlData = xml_parse($this->resParser,$strInputXML );
if(!$this->strXmlData) {
die(sprintf("XML error: %s at line %d",
xml_error_string(xml_get_error_code($this->resParser)),
xml_get_current_line_number($this->resParser)));
}
xml_parser_free($this->resParser);
return $this->arrOutput;
}
function tagOpen($parser, $name, $attrs) {
$tag=array("name"=>$name,"attrs"=>$attrs);
array_push($this->arrOutput,$tag);
}
function tagData($parser, $tagData) {
if(trim($tagData)) {
if(isset($this->arrOutput[count($this->arrOutput)-1]['tagData'])) {
$this->arrOutput[count($this->arrOutput)-1]['tagData'] .= $tagData;
}
else {
$this->arrOutput[count($this->arrOutput)-1]['tagData'] = $tagData;
}
}
}
function tagClosed($parser, $name) {
$this->arrOutput[count($this->arrOutput)-2]['children'][] = $this->arrOutput[count($this- >arrOutput)-1];
array_pop($this->arrOutput);
}
}
?>
add this search function to your class and play with this code
$objXML = new xml2Array();
$arrOutput = $objXML->parse($contents);
// first param is always 0
// second is 'children' unless you need info like last updated date
// third is which statistics category you want for example
// 6 => the array you want that has wins and losses
print_r($arrOutput[0]['children'][6]);
//using the search function if key NAME is Montreal in the whole array
//result will be montreals array
$search_result = $objXML->search($arrOutput, 'NAME', 'Montreal');
//first param is always 0
//second is key name
echo $search_result[0]['WINS'];
function search($array, $key, $value)
{
$results = array();
if (is_array($array))
{
if (isset($array[$key]) && $array[$key] == $value)
$results[] = $array;
foreach ($array as $subarray)
$results = array_merge($results, $this->search($subarray, $key, $value));
}
return $results;
}
Beware
this search function is case sensitive it needs modifications like match to
a percentage the key or value changing capital M in montreal to lowercase will be empty
Here is the code I sent you working in action. Pulling the data from the same link you are using also
http://sjsharktank.com/standings.php
I have actually used the same exact XML file for my own school project. I used DOM Document. The foreach loop would get the value of each attribute of team-standing and store the values. The code will clear the contents of the table standings and then re-insert the data. I guess you could do an update statement, but this assumes you never did any data entry into the table.
try {
$db = new PDO('sqlite:../../SharksDB/SharksDB');
$db->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
} catch (Exception $e) {
echo "Error: Could not connect to database. Please try again later.";
exit;
}
$query = "DELETE FROM standings";
$result = $db->query($query);
$xmlDoc = new DOMDocument();
$xmlDoc->load('http://www.tsn.ca/datafiles/XML/NHL/standings.xml');
$searchNode = $xmlDoc->getElementsByTagName( "team-standing" );
foreach ($searchNode as $searchNode) {
$teamID = $searchNode->getAttribute('id');
$name = $searchNode->getAttribute('name');
$wins = $searchNode->getAttribute('wins');
$losses = $searchNode->getAttribute('losses');
$ot = $searchNode->getAttribute('overtime');
$points = $searchNode->getAttribute('points');
$goalsFor = $searchNode->getAttribute('goalsFor');
$goalsAgainst = $searchNode->getAttribute('goalsAgainst');
$confID = $searchNode->getAttribute('conf-id');
$divID = $searchNode->getAttribute('division-id');
$query = "INSERT INTO standings ('teamid','confid','divid','name','wins','losses','otl','pts','gf','ga')
VALUES ('$teamID','$confID','$divID','$name','$wins','$losses','$ot','$points','$goalsFor','$goalsAgainst')";
$result= $db->query($query);
}

Entity metadata wrapper

i'm getting error with metadata wrapper.
i have a field test => entity reference multiple which is a selection list.I get the following Error EntityMetadataWrapperException : Invalid data value given. Be sure it matches the required data type and format.
$account = entity_load_single('user', $user->uid);
$acc_wrapper = entity_metadata_wrapper('user', $account);
$list = $acc_wrapper->test->value();
$exists = FALSE;
if (!empty($list)) {
foreach ($list as $item) {
if ($item->nid == $form_state['storage']['node']->nid) {
$exists = TRUE;
break;
}
}
}
if (!$exists) {
if (!$list) {
$list = array();
$list[] = $form_state['storage']['node']->nid;
}
$acc_wrapper->test->set($list);
$acc_wrapper->save();
1rst quick tips
$account = entity_load_single('user', $user->uid);
$acc_wrapper = entity_metadata_wrapper('user', $account);
You don't need to load the entity unless you need it loaded after (Or it's already loaded). All you need is the id, and let entity_metadata_wrapper magic operate.
$acc_wrapper = entity_metadata_wrapper('user', $user->uid);
I think your error is here
if (!$list) {
$list = array();
$list[] = $form_state['storage']['node']->nid;
}
$list is always initiated because of "$list = $acc_wrapper->test->value();", so you never fullfill the condition, and then you are trying to set it back and save it (because you are missing a '}' )... Makes no sense...
Could try this version ?
$acc_wrapper = entity_metadata_wrapper('user', $user->uid);
$list = $acc_wrapper->test->value();
$exists = FALSE;
if (!empty($list)) {
foreach ($list as $item) {
if ($item->nid == $form_state['storage']['node']->nid) {
$exists = TRUE;
break;
}
}
}
if (!$exists && !$list) {
$list = array($form_state['storage']['node']->nid);
$acc_wrapper->test = $list;
$acc_wrapper->save();
}

Compare 2 text files and get internal id

I tried comparing 2 text files with data separated by -, in one case one file gets all data and in another case only has the data for change with the same id in this case this file it's called db_tmp.txt
The structure in both files it's this :
File txt ( the first it´s the id ) db/text.txt
1a34-Mark Jhonson
1a56-Jhon Smith
1a78-Mary Walter
The file for comparing, has for example the data for change, same id but different content - db_tmp.txt
1a56-Tom Tom
I created a function for comparing both files to detect if the same id and change exists:
<?php
$cron_file = file("db_tmp.txt");
$cron_compare = file("db/test.txt");
function cron($file_temp, $file_target)
{
for ($fte = 0; $fte < sizeof($file_temp); $fte++) {
$exp_id_tmp = explode("-", $file_temp[$fte]);
$cr_temp[] = "" . $exp_id_tmp[0] . "";
}
for ($ftt = 0; $ftt < sizeof($file_target); $ftt++) {
$exp_id_targ = explode("-", $file_target[$ftt]);
$cr_target[] = "" . $exp_id_targ[0] . "";
}
$diff = array_diff($cr_target, $cr_temp);
$it = 0;
foreach ($diff as $diff2 => $key) {
echo $diff2;
echo "--- $key";
print "<br>";
}
}
cron($cron_file, $cron_compare);
?>
If the same id exists in tmp, i must detect the entry in the other file and change to the value of the tmp, i try but can't get this to work, the function works but not for everything, if anyone can help me, that would be perfect, because i don´t know how continue this and save.
If you want to match according to id, a simple foreach would suffice, then just check during the loop if they have the same key. Consider this example:
// sample data from file('db_text.txt');
$contents_from_text = array('1a34-Mark Jhonson','1a56-Jhon Smith', '1a87-Mary Walter');
// reformat
$text = array();
foreach($contents_from_text as $element) {
list($key, $value) = explode('-', $element);
$text[$key] = $value;
}
$tmp = array();
// sample data from file('db_tmp.txt');
$contents_from_tmp = array('1a56-Tom Tom', '1a32-Magic Johnson', '1a23-Michael Jordan', '1a34-Larry Bird');
foreach($contents_from_tmp as $element) {
list($key, $value) = explode('-', $element);
$tmp[$key] = $value;
}
// compare
foreach($tmp as $key => $value) {
foreach($text as $index => $element) {
if($key == $index) {
$tmp[$key] = $element;
}
}
}
$contents_from_tmp = $tmp;
print_r($contents_from_tmp);
Sample Output

Issue Querying LDAP in PHP

I am currently working on a script that queries Active Directory to check if a user is a Domain Admin. The filter works correctly when I test it with ldp.exe. However, when I run the filter in php it does not return anything. However, just checking the SAM account returns correctly.
Thank you!
$ldap_host = "Example.internal";
$base_dn = "DC=Prefix,DC=Example,DC=internal";
$filter = "(&(sAMAccountName=test)(memberof=CN=Domain Admins,CN=Users,DC=Prefix,DC=Example,DC=internal))";
$ldap_user = "username";
$ldap_pass = "password";
$ldap_port = 3268;
$connect = ldap_connect( $ldap_host, $ldap_port)
or exit(">>Could not connect to LDAP server<<");
ldap_set_option($connect, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($connect, LDAP_OPT_REFERRALS, 0);
$bind = ldap_bind($connect, $ldap_user, $ldap_pass)
or exit(">>Could not bind to $ldap_host<<");
$read = ldap_search($connect, $base_dn, $filter)
or exit(">>Unable to search ldap server<<");
$info = ldap_get_entries($connect, $read);
echo $info["count"]." entries returned<p>";
$ii=0;
for ($i=0; $ii<$info[$i]["count"]; $ii++){
$data = $info[$i][$ii];
echo $data.": ".$info[$i][$data][0]."<br>";
}
ldap_close($connect);
?>
Based on the code, I'm assuming you are trying to walk the returned objects and their attributes in the for loop at the end. The problem is how you are iterating. Here's the returned data structure per the manual.
return_value["count"] = number of entries in the result
return_value[0] : refers to the details of first entry
return_value[i]["dn"] = DN of the ith entry in the result
return_value[i]["count"] = number of attributes in ith entry
return_value[i][j] = NAME of the jth attribute in the ith entry in the result
return_value[i]["attribute"]["count"] = number of values for
attribute in ith entry
return_value[i]["attribute"][j] = jth value of attribute in ith entry
Based on this code:
$ii=0;
for ($i=0; $ii<$info[$i]["count"]; $ii++){
$data = $info[$i][$ii];
echo $data.": ".$info[$i][$data][0]."<br>";
}
You are setting $i=0; and not iterating it so it's always 0, corresponding with the first entry in your returned array. You are actually walking through the attributes of the object, which is fine if you only ever expect 1 result back (I suspect that's not the case).
You might try the following function from the docs:
function cleanUpEntry( $entry ) {
$retEntry = array();
for ( $i = 0; $i < $entry['count']; $i++ ) {
if (is_array($entry[$i])) {
$subtree = $entry[$i];
//This condition should be superfluous so just take the recursive call
//adapted to your situation in order to increase perf.
if ( ! empty($subtree['dn']) and ! isset($retEntry[$subtree['dn']])) {
$retEntry[$subtree['dn']] = cleanUpEntry($subtree);
}
else {
$retEntry[] = cleanUpEntry($subtree);
}
}
else {
$attribute = $entry[$i];
if ( $entry[$attribute]['count'] == 1 ) {
$retEntry[$attribute] = $entry[$attribute][0];
} else {
for ( $j = 0; $j < $entry[$attribute]['count']; $j++ ) {
$retEntry[$attribute][] = $entry[$attribute][$j];
}
}
}
}
return $retEntry;
}
USAGE:
$info = ldap_get_entries($connect, $read);
$clean_info = Array();
foreach($info as $entry)
{
$clean_info[] = cleanUpEntry($entry);
}
print_r($clean_info);
Output:
array(256) {
["uid=doe,ou=People,dc=example,dc=net"]=>
array(3) {
["uid"]=>
string(4) "doe"
["cn"]=>
string(14) "John Doe"
["telephonenumber"]=>
string(4) "1234"
}
["uid=foo,ou=People,dc=example,dc=net"]=>
...
You may also consider using print_r($info) after calling ldap_get_entries() to see exactly what is in there.

Categories