1
\$\begingroup\$

I'm looking at improving my PHP knowledge and wondering if anybody has any tips on improving and optimising my function below?

<?php
        function urlCheck($string = '')
        {
            $searches = array('one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven', 'twelve', 'thirteen');
            $serverUrl = $_SERVER['REQUEST_URI'];
            $getContents = file_get_contents('php://input');
            $queryJoin = $serverUrl . $getContents;
            foreach ($searches as $search) {
                if (strpos(strtolower(urldecode($queryJoin)), strtolower($search)) !== false) {
                    return true;
                }
            }
            return false;
        }
        
        print_r(urlCheck());
\$\endgroup\$
2
  • 3
    \$\begingroup\$ I'm not convinced that I have enough context here. How about some sample input? Do you need word boundaries to ensure that you are matching a whole word? Is this hypothetical code? \$\endgroup\$ Commented Jul 13, 2021 at 13:18
  • \$\begingroup\$ For clarity $getContents = file_get_contents('php://input'); is only aiming to get query string variables, right? or is it intended to include POST data also? \$\endgroup\$ Commented Jul 14, 2021 at 17:48

2 Answers 2

3
\$\begingroup\$

If you replace

            $queryJoin = $serverUrl . $getContents;

with

            $queryJoin = strtolower(urldecode($serverUrl . $getContents));

You can change

                if (strpos(strtolower(urldecode($queryJoin)), strtolower($search)) !== false) {

to

                if (strpos($queryJoin, strtolower($search)) !== false) {

and save a strtolower call and urldecode call for every iteration.

For that matter, if the keys are constants, as they are here, you could simply drop the strtolower on $searches as well. Just make sure that the values are lower case.

Alternatively, consider making a check class.

class URL_Checker {

    protected $searches;

    public function __construct($searches) {
        $this->searches = array_map('strtolower', $searches);
    }

    public function check($string) {
        $haystack = strtolower(urldecode($string));
        foreach ($this->searches as $search) {
            if (false !== strpos($haystack, $search)) {
                return true;
            }
        }

        return false;
    }

}

That would work better if you are calling the function multiple times for the same search keys.

Just as a matter of good style, it would be better to separate loading the data from checking it. This class would be reusable for any data string. Or you could load the body of the request once and reuse it multiple times. Your original form would allow neither input nor processing reuse.

It's possible that replacing the foreach with an array_reduce might be more efficient on average. You'd have to run metrics. It would tend to improve the worst case at the cost of the best case; that might be a useful tradeoff regardless. The worst case being when the search keys do not appear so you have to check all of them.

\$\endgroup\$
0
\$\begingroup\$

Unless the request has an encoding issue, $_SERVER['QUERY_STRING'] could likely be used when assigning $getContents instead of reading from the raw input (I.e. file_get_contents('php://input')).

If you want to optimize for readability then consider generating the values in the array $searches using PHP's built-in number formatter with the formatter NumberFormatter::SPELLOUT. The range() function can also be used to create the array from 1 to 13. Then if the list of numbers needs to change you can easily modify the parameters to that function.

$nf = new NumberFormatter('en_US', NumberFormatter::SPELLOUT);
foreach(range(1, 13) as $i) {
    $search = $nf->format($i);
    //look for $search in $queryJoin
}
\$\endgroup\$

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.