I have a section in my code which uses file_get_contents to grab the url from the given web page. I also have a section in my code which scans the titles in each link value in my array.
I want end up having an array similar to this :
Array(
Google => array(
[title] => Google
[link] => http://www.google.com
)
)
but no values are saved to my array, even though i can't detect any errors
$links = Array();
$URL = 'http://www.theqlick.com'; // change it for urls to grab
$file = file_get_contents($URL);
// grabs the urls from URL
if( strlen( $file )>0 ) {
$links[] = preg_match_all( "/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/", $file, $links);
}
function Titles() {
global $links;
$str = implode('',array_map('file_get_contents',$links));
error_reporting(E_ERROR | E_PARSE);
$titles = Array();
if( strlen( $str )>0 ) {
$titles[] = preg_match_all( "/\<title\>(.*)\<\/title\>/", $str, $title );
return $title;
return $links;
}
}
$newArray = array();
$j = 0;
foreach( $links as $key => $val ){
$newArray[$key] = array( 'link' => $val, 'title' => $title[1][$j++]);
}
print_r($newArray);
The following code does not seem to return anything
$links[] = preg_match_all( "/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/", $file, $links);
Try the following
$links = Array();
$URL = 'http://www.theqlick.com'; // change it for urls to grab
$file = file_get_contents($URL);
// grabs the urls from URL
if (strlen($file) > 0) {
$links[] = preg_match_all('#\bhttps?://[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/))#', $file, $links);
}
var_dump($links);
Output
array
0 =>
array
0 => string 'http://www.w3.org/TR/xhtmll/DTD/xhtmll-transitional.dtd' (length=55)
1 => string 'http://www.w3.org/1999/xhtml' (length=28)
2 => string 'http://www.theqlick.com' (length=23)
3 => string 'http://www.theqlick.com' (length=23)
1 =>
array
0 => string 'd' (length=1)
1 => string 'l' (length=1)
2 => string 'm' (length=1)
3 => string 'm' (length=1)
2 => int 4
Related
I have this HTML in $string:
$string = '<p>random</p>
Test 1 (target1)
<br>
Test 2 (target1)
<br>
Test 3 (skip)
// etc
';
And I have a few terms in $array:
$array = array(
'(target1)',
'(target2)'
);
How can I search through $string to find all terms in $array and grab the content of the <a> tag that precedes it?
So I end up with the following results:
$results = array(
array(
'text' => 'Test 1',
'needle' => 'target1'
),
array(
'text' => 'Test 2',
'needle' => 'target1'
)
);
I will give you an answer using javascript, but php can do the same thing.
You can search through the array 1 string at a time, and finish once no results are found and you have reached the end of your array.
target1Match = s.match(/<.+?>(.+?)<\/.+?> *\(target1\)/);
// target1Match is now [Test 1 (target1), Test 1]
target1Match = target1Match[1];
target2Match = s.match(/<.+?>(.+?)<\/.+?> *\(target2\)/);
// target1Match is now [Test 2 (target2), Test 2]
target2Match = target2Match[1];
You build the regex using variables for "target1 and 2"
matching multiple targets and specific tag
s.match(/<a.+?>(.+?)<\/a> *\((target1|target2)\)/);
Using preg_match_all():
// Assuming your HTML as $str, your terms as $terms
$results = [];
foreach ($terms as $t) {
// Get content of <a> tag preceeding the term
preg_match_all('/<a ?.*>(.*)<\/a>\s+' . preg_quote($t) . '/', $str, $matches);
//Then insert into your result array
foreach ($matches[1] as $m) {
$results[] = [
'text' => $m,
'needle' => $t
];
}
}
Output:
// echo '<pre>' . print_r($results, true) . '</pre>';
Array
(
[0] => Array
(
[text] => Test 1
[needle] => (target1)
)
[1] => Array
(
[text] => Test 2
[needle] => (target1)
)
)
See also: preg_quote()
I'm in the JayBlanchard camp. Here's a solution that rightly uses DomDocument & Xpath with a dynamically generated query to target <a> tags that are immediately followed by text that contains one of the qualifying needles.
For the sample needles, this is the generated query:
//a[following-sibling::text()[1][contains(.,'(target1)') or contains(.,'(target2)')]]
Code: (Demo)
$html = '<p>random</p>
Test 1 (skip)
<br>
Test 2 (target1)
<br>
Test 3 (target1)
<br>
Test 4 (skip)
<br>
Test 5 (target2)
<br>
Test 6 (skip)
';
$needles = [
'(target1)',
'(target2)'
];
$contains = array_reduce($needles, function($carry, $needle) {
return $carry .= ($carry !== null ? ' or ' : '') . "contains(.,'$needle')";
});
$matches = [];
$dom=new DOMDocument;
$dom->loadHTML($html);
$xpath = new DOMXPath($dom);
foreach ($xpath->query("//a[following-sibling::text()[1][$contains]]") as $node) {
$matches[] = ["text" => $node->nodeValue, "needle" => trim($node->nextSibling->nodeValue)];
}
var_export($matches);
Output:
array (
0 =>
array (
'text' => 'Test 2',
'needle' => '(target1)',
),
1 =>
array (
'text' => 'Test 3',
'needle' => '(target1)',
),
2 =>
array (
'text' => 'Test 5',
'needle' => '(target2)',
),
)
This is quite basic, but I am missing a puzzle piece.
I have a multidimensional PHP array that - among other things - contains some strings. I would like to translate special strings in this array based on a translation table or array in PHP.
$r = array(
0 => 'something',
1 => array(
'othertext' => '1000 {{animals}} and {{cars}}',
'anytext' => '400 {{cars}}',
)
);
In $r, now I would like to replace {{animals}} with another string that is stored in a separate array.
Here it is:
$translations = array(
'animals' => array('Tiere','animaux','bestie'),
'cars' => array('Autos','voitures','macchine'),
);
Now let's set the language / column we want to look up
$langId = 0;
And now, take $r, look for all key that are wrapped in {{}}, look them up in $translations and replace them with key[$langId], so in return we get:
$r = array(
0 => 'something',
1 => array(
'othertext' => '1000 Tiere',
'anytext' => '400 Autos',
)
);
ehm... how's that done?
PS: the marker {{}} is random, could be anything robust
I was able to get the output you expected using the following code. Try it and tell me if it worked for you or not:
<?php
$r = array(
0 => 'something',
1 => array(
'othertext' => '1000 {{animals}} and {{cars}}',
'anytext' => '400 {{cars}}',
)
);
$translations = array(
'animals' => array('Tiere','animaux','bestie'),
'cars' => array('Autos','voitures','macchine'),
);
$langId = 0;
$pattern = "/\{\{[a-zA-Z]+\}\}/";
for($t=0; $t<count($r); $t++) {
$row = $r[$t];
if(!is_array($row))
continue;
foreach($row as $key=>$value) {
if(preg_match_all($pattern, $value, $match, PREG_SET_ORDER)) {
for($i = 0; $i < count($match); $i++) {
//remove {{ & }} to get key
$k = substr($match[$i][0], 2, strlen($match[$i][0])-4);
$replacer = $translations[$k][$langId];
$value = str_replace($match[$i][0], $replacer, $value);
$r[$t][$key] = $value;
}
}
}
}
?>
This seems like a simple task, but I haven't been able to find a way to do this. I have an output of data stored as a string like this:
$rawData = "array('first' => '$first', 'middle' => '$middle', 'last' => '$last')";
What I simply need to do is convert it to this array:
$arrData = array('first' => "$first", 'middle' => "$middle", 'last' => "$last");
The closest I have been able to get is this shown below with print_r results:
$Data = explode(',', $rawData);
Array
(
[0] => 'first' => '$first'
[1] => 'middle' => '$middle'
[2] => 'last' => '$last'
)
What I need is this:
Array
(
[first] => $first
[middle] => $middle
[last] => $last
)
Must be something very easy I have overlooked. Please help.
Depending on where you get the string from, you may use eval. It's highly recommended to not use this function, whenever the contents of that string may be influenced directly or indirectly by a user, see hint at http://php.net/manual/en/function.eval.php. And it must contain valid PHP syntax, thus putting it into a try/catch block is necessary:
$evalArray = false;
$first = 'a';
$middle = 'b';
$last = 'c';
$rawData = "array('first' => '$first', 'middle' => '$middle', 'last' => '$last')";
try {
$evalArray = eval($rawData);
} catch ( $e )
{
echo "parsing failed " . $e
}
print_r($evalArray );
This does what you want ( i think ), even if its not pretty
<?php
$arr = array();
$first = 'one';
$middle = 'two';
$last = 'three';
$rawData = "array('first' => '$first', 'middle' => '$middle', 'last' => '$last')";
$arrData = explode( ',', ( str_replace( array( 'array(\'', '=>', '\')', '\'' ), array( '', '%##%', '', '' ), $rawData ) ) );
foreach( $arrData as $val ) {
$v = explode( '%##%', $val );
$arr[trim($v[0])] = trim($v[1]);
}
echo '<pre>' . print_r( $arr, true ) . '</pre>';
?>
I have strings like the following that I have to split:
Other,CODSITE,Items::getCodCdeCli+Address::getNameAddress
Other,CODSITE,Items::getCodCdeCli
Items::getCode+Address::getName,CODSITE+Items::getSample,Items::getItemID
Other, CODSITE, CODSITE2
Into:
array(
array(
0 => 'Other',
1 => 'CODSITE',
2 => array(
'Items' => 'getCodCdeCli',
'Address' => 'getNameAddress'
)
),
//...
)
Each comma involve new information, if we have a '+' we need to append both data. If we have '::' we need to get first part as key of the result information.
For beginning this solution I have tried to split this on comma:
$re = "/([^,]+)/";
$str = "Other,CODSITE,Items::getCodCdeCli+Address::getNameAddress";
preg_match_all($re, $str, $matches);
for now with this regex I have this:
array (size=2)
0 =>
array (size=3)
0 => string 'Other' (length=5)
1 => string 'CODSITE' (length=7)
2 => string 'Items::getCodCdeCli+Address::getNameAddress' (length=43)
1 =>
array (size=3)
0 => string 'Other' (length=5)
1 => string 'CODSITE' (length=7)
2 => string 'Items::getCodCdeCli+Address::getNameAddress' (length=43)
Which is wrong. I have same result twice.. and line 2 => [...] is not split (which is normal with my regex)
One way of doing it in single pass is by using array_combine function like this:
$str = 'Other,CODSITE,Items::getCodCdeCli+Address::getNameAddress';
if ( preg_match_all('~(?|[,+]([^,+]+)::([^,+]+)|([^,]+))~', $str, $m) )
print_r( array_combine ( $m[1], $m[2] ) );
Output:
Array
(
[Other] =>
[CODSITE] =>
[Items] => getCodCdeCli
[Address] => getNameAddress
)
Does it need to be regex? It might be possible to achieve this with a couple of explodes and foreach loops:
$str = 'Other,CODSITE,Items::getCodCdeCli+Address::getNameAddress';
//new entry per comma (,)
$results = explode(',',$str);
//check each entry for array information
foreach($results as &$result) {
if(strpos($result,'+') !== FALSE) {
//explode array information
$bits1 = explode('+',$result);
$result = array();
foreach($bits1 as &$subresult) {
//format array information into key => value pairs
if(strpos($subresult,'::') !== FALSE) {
$bits = explode('::',$subresult);
$result[$bits[0]] = $bits[1];
}
}
}
}
var_dump($results);
/**
* array (size=3)
* 0 => string 'Other' (length=5)
* 1 => string 'CODSITE' (length=7)
* 2 => array (size=2)
* 'Items' => string 'getCodCdeCli' (length=12)
* 'Address' => string 'getNameAddress' (length=14)
*/
With the help of #Richard Parnaby-King answer. This is the solution, in fact no regex is needed even if i'm sure we can use it for the same result.
$lines = array(
0 => 'Other,CODSITE,Items::getCodCdeCli+Address::getNameAddress',
2 => 'Other,CODSITE,Items::getCodCdeCli',
3 => 'Items::getCode+Address::getName,CODSITE+Items::getSample,Items::getItemID',
4 => 'Other, CODSITE, CODSITE2',
);
foreach ($lines as $input) {
$informations = explode(',', $input);
$result = array();
foreach ($informations as $information) {
if(strpos($information, '+') !== FALSE) {
$classes = explode('+',$information);
$temp = array();
foreach($classes as $subresult) {
if(strpos($subresult,'::') !== FALSE) {
$classAndMethod = explode('::',$subresult);
$temp[$classAndMethod[0]] = $classAndMethod[1];
} else {
$temp[] = trim($subresult);
}
}
$result[] = $temp;
} elseif (strpos($information, '::') !== FALSE) {
$classAndMethod = explode('::',$information);
$result[][$classAndMethod[0]] = $classAndMethod[1];
} else {
$result[] = trim($information);
}
}
var_dump($result);
}
It works !
I'm not even sure how to begin wording this question, but basically, I have an array, that looks like this:
Array
(
[0] => /
[1] => /404/
[2] => /abstracts/
[3] => /abstracts/edit/
[4] => /abstracts/review/
[5] => /abstracts/view/
[6] => /admin/
[7] => /admin/ads/
[8] => /admin/ads/clickcounter/
[9] => /admin/ads/delete/
[10] => /admin/ads/edit/
[11] => /admin/ads/list/
[12] => /admin/ads/new/
[13] => /admin/ads/sponsordelete/
[14] => /admin/ads/sponsoredit/
[15] => /admin/ads/sponsornew/
[16] => /admin/ads/stats/
[17] => /admin/boilerplates/
[18] => /admin/boilerplates/deleteboiler/
[19] => /admin/boilerplates/editboiler/
[20] => /admin/boilerplates/newboilerplate/
[21] => /admin/calendar/event/add/
[22] => /admin/calendar/event/copy/
)
And I need to 'reduce' / 'process' it into an array that looks like this:
Array
(
[''] => Array()
['404'] => Array()
['abstracts'] => Array
(
[''] => Array()
['edit'] => Array()
['review'] => Array()
['view'] => Array()
)
['admin'] => Array
(
['ads'] => Array
(
[''] => Array()
['clickcounter'] => Array()
['delete'] =>Array()
['edit'] => Array()
)
)
.....
.....
)
That, if manually initialized would look something like this:
$urlTree = array( '' => array(),
'404' => array(),
'abstracts'=> array( '' => array(),
'edit' => array(),
'review'=> array(),
'view' => array() ),
'admin' => array( 'ads'=> array( '' => array(),
'clickcounter'=> array(),
'delete' => array(),
'edit' => array() ) )
);
I usually stray away from asking straight up for a chunk of code on SO, but does anyone perhaps have any advice / code that can traverse my array and convert it to a hierarchy?
EDIT: Here is the bit I have right now, which, I know is pitifully small, I'm just blanking out today it seems.
function loadUrlData()
{
// hold the raw data, /blah/blah/
$urlData = array();
$res = sql::query( "SELECT DISTINCT(`url`) FROM `pages` ORDER BY `url` ASC" );
while( $row = sql::getarray( $res ) )
{
$urlData[] = explode( '/', substr( $row['url'], 1, -1 ) );
}
// populated, eventually, with the parent > child data
$treeData = array();
// a url
foreach( $urlData as $k=> $v )
{
// the url pieces
foreach( $v as $k2=> $v2 )
{
}
}
// $treeData eventually
return $urlData;
}
Looks rather easy. You want to loop through all lines (foreach), split them into parts (explode), loop through them (foreach) and categorize them.
Since you don't like asking for a chunk of code, I won't provide any.
Update
A very nice way to solve this is to reference the $urlTree (use &), loop through every part of the URL and keep updating a variable like $currentPosition to the current part in the URL tree. Because you use &, you can simply edit the array directly while still using a simple variable.
Update 2
This might work:
// a url
foreach( $urlData as $k=> $v )
{
$currentSection = &$treeData;
// the url pieces
foreach( $v as $k2=> $v2 )
{
if (!isset($currentSection[$v2])) {
$currentSection[$v2] = array();
}
$currentSection = &$currentSection[$v2];
}
}
I know you didn't ask for a chunk of code, but I'd just call this a petit serving:
$map = array();
foreach($urls as $url) {
$folders = explode('/', trim($url, '/'));
applyChain($map, $folders, array());
}
function applyChain(&$arr, $indexes, $value) { //Here's your recursion
if(!is_array($indexes)) {
return;
}
if(count($indexes) == 0) {
$arr = $value;
} else {
applyChain($arr[array_shift($indexes)], $indexes, $value);
}
}
It's fairly simple. We separate each url into its folders (removing trailing and leading slashes) and then work our way down the array chain until we reach the folder mentioned in the URL. Then we place a new empty array there and continue to the next URL.
My version:
$paths = array(
0 => '/',
1 => '/404/',
2 => '/abstracts/',
3 => '/abstracts/edit/',
4 => '/abstracts/review/',
5 => '/abstracts/view/',
6 => '/admin/',
7 => '/admin/ads/',
// ....
);
$tree = array();
foreach($paths as $path){
$tmp = &$tree;
$pathParts = explode('/', rtrim($path, '/'));
foreach($pathParts as $pathPart){
if(!array_key_exists($pathPart, $tmp)){
$tmp[$pathPart] = array();
}
$tmp = &$tmp[$pathPart];
}
}
echo json_encode($tree, JSON_PRETTY_PRINT);
https://ideone.com/So1HLm
http://ideone.com/S9pWw
$arr = array(
'/',
'/404/',
'/abstracts/',
'/abstracts/edit/',
'/abstracts/review/',
'/abstracts/view/',
'/admin/',
'/admin/ads/',
'/admin/ads/clickcounter/',
'/admin/ads/delete/',
'/admin/ads/edit/',
'/admin/ads/list/',
'/admin/ads/new/',
'/admin/ads/sponsordelete/',
'/admin/ads/sponsoredit/',
'/admin/ads/sponsornew/',
'/admin/ads/stats/',
'/admin/boilerplates/',
'/admin/boilerplates/deleteboiler/',
'/admin/boilerplates/editboiler/',
'/admin/boilerplates/newboilerplate/',
'/admin/calendar/event/add/',
'/admin/calendar/event/copy/');
$result = array();
foreach ($arr as $node) {
$result = magic($node, $result);
}
var_dump($result);
function magic($node, $tree)
{
$path = explode('/', rtrim($node, '/'));
$original =& $tree;
foreach ($path as $node) {
if (!array_key_exists($node, $tree)) {
$tree[$node] = array();
}
if ($node) {
$tree =& $tree[$node];
}
}
return $original;
}
<?php
$old_array = array("/", "/404/", "/abstracts/", "/abstracts/edit/", "/abstracts/review/", "/rrl/");
$new_array = array();
foreach($old_array as $woot) {
$segments = explode('/', $woot);
$current = &$new_array;
for($i=1; $i<sizeof($segments); $i++) {
if(!isset($current[$segments[$i]])){
$current[$segments[$i]] = array();
}
$current = &$current[$segments[$i]];
}
}
print_r($new_array);
?>
You might consider converting your text to a JSON string, then using json_decode() to generate the structure.