-1

The Setup

This is to do specifically with Twitter Cards, but I'm looking for a more general (i.e. not Twitter Cards specific) solution, as many meta tags are namespaced.

Twitter cards are stored in the head of a web page, inside meta tags. Something like...

<meta name="twitter:site" content="... tag content ...">

When you run get_meta_tags() on a website that uses the cards, you'll get a return that looks something like...

[
    ... bunch of other meta tags ...

    "twitter:card" : "... tag content ...",
    "twitter:description" : "... tag content ...",
    "twitter:title" : "... tag content ...",
    "twitter:site" : "... tag content ...",
    "twitter:image" : "... tag content ...",
    "twitter:creator" : "... tag content ...",

    ... maybe some more tags ...
]

All the keys that have to do with the Twitter Card are namespaced - what I mean is that each of them is prefaced with twitter:.

Even if a page has Twitter Card tags present, not all the tags listed above will necessarily be there. Some times it's all of them, some times it's just a couple.

The Problem

Let's get us some meta tags...

$tagsList = @get_meta_tags($url);

Because you don't know for sure which tags will be present, it doesn't make sense to test for them all...

if(isset($tagsList['twitter:card'])) {
    // Do something
}

if(isset($tagsList['twitter:description'])) {
    // Do something
}

... and so on ...

This approach is also particularly ineffective if you do not know every possible tag name that could be present, or Twitter decides to change the current standard.

So instead, you loop through every tag...

$twitterList = array();

foreach($tagsList as $tagName => $tagCont) {

    if(strpos(strtolower($tagName), 'twitter:') === 0) {

        // root = 'twitter', sub = 'card' or 'description' or ...
        list($root, $sub) = explode(':', $tagName);
        
        $twitterList[] = array(
            'root' => $root,   // Don't really need this
            'sub' => $sub, 
            'content' => $tagCont
        );

    }

}

And this works a treat. It's accurate, and returns your Twitter Card tags in a nice list. But, if I only need the tags pertaining to the Twitter Card, why the heck should I have to busy myself with looping through ALL of them!?

The Question

Is there a quick way to access these namespaced keys, without looping through every meta tag that was returned from the page? I'm only interested in the twitter: ones, so I don't want to waste resources and time looping through every tag.

Something like...

$twitterTags = $tagsList['twitter:'];

where the output would be along the lines of...

$twitterTags : [
    'card' : 'card content', 
    'description' : 'desc content', 
    ... and so on ...
]

I reckon you might be able to use array_map(), which might shorten the number of lines of code used, but you still end up looping through every key.

Ok, I know it isn't necessarily necessary, but...

Unless a page has an unreasonable number of meta tags (bazillions of them), the time/resources difference between the brute-force loop and a more elegant solution will be negligible. But it's still an interesting exercise.

3
  • No there is no such functionality. You will have to either iterate over the list as you are doing or load the html using a DomDocument and traverse/search (with xpath) the DOM Structure to find the meta tags you are looking for. Commented Oct 20, 2017 at 9:36
  • @AronCederholm I found a prettier solution, that I've added to the question as an edit. I'm pretty sure it'll still be looping over the array, but it's a lot cleaner. Commented Oct 20, 2017 at 10:00
  • @Birrel If edit 1 is a solution you should move it to an answer. [\w\d\-\_]+ can be simplified to [-\w]+, these are the same. Commented Sep 6, 2022 at 17:08

2 Answers 2

2

No there is no such functionality, as get_meta_tags() is not designed for this (and certainly was designed prior to Twitter introducing their own set of meta tags).

You will have to either iterate over the list as you are doing (or use array_map() as you also mention yourself) or load the html using a DomDocument and traverse/search (with xpath) the DOM Structure to find the meta tags you are looking for.

In fact, the twitter:description naming scheme is just a naming scheme chosen by Twitter. It is not an actual namespace, nor is the tags part of any standard. If you want php to support this functionality of get_meta_tags you can try to propose it as a feature to add

Sign up to request clarification or add additional context in comments.

2 Comments

Bummer. I found a cleaner solution - that I've added to the original question as an edit - but I'm pretty sure it'll still be looping through the array, behind the scenes.
That was a beautiful solution, but it will still iterate over the array. preg_grep will iterate over the array of meta tags. The only way to avoid iterating over this array afterwards is by doing the check during parse time, which means you will have to setup a DomDocument, which won't be any faster.
-1

Thanks to Daniel Klein, I found this beauty...

function preg_grep_keys($pattern, $input, $flags = 0) {
    return array_intersect_key($input, array_flip(preg_grep($pattern, array_keys($input), $flags)));
}

Which is used in the following way...

$pattern = '/(twitter\:)([\w\d\-\_]+)/';

$twitterList = preg_grep_keys($pattern, $tagsList, $flags = 0);

And returns...

"twitterList": {
    "twitter:card": "... tag content ...",
    "twitter:description": "... tag content ...",
    "twitter:title": "... tag content ...",
    "twitter:site": "... tag content ...",
    "twitter:image": "... tag content ...",
    "twitter:creator": "... tag content ..."
}

What a gem! But, I'm unsure how array_intersect_key() and array_flip() go about their business. I'm probably still looping over the array. And surely array_keys() has to loop over the array?

It looks prettier than the foreach() above, anyway.


Originally posted as an edit to the question by the question author.

7 Comments

This is looping multiple times. array_keys(), preg_grep(), array_flip(), and array_intersect_key().
I'm not sure it "looks better". A loop that simply does if (str_starts_with($key, 'twitter:')) seems simpler.
Agreed, it's not a good answer! The question itself is nonsense, worrying about iterating over a dozen short strings "wasting resources" lol
I had closed it as a duplicate of this question until I noticed the "without looping" requirement, which is ridiculous but does make it a different question. (A requirement which was forgotten by OP when they posted this answer as an edit.)
I don't see how it's a duplicate even ignoring the looping issue. That question is about creating nested arrays based on a delimiter, this one is about filtering the keys based on a single prefix.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.