Update:
Oh boy, sometimes it is even easier than one thinks. There is already a function that counts substrings: substr_count().
So now it is just:
$number = substr_count($str, '</option>');
So if you say it could be a few hundred option tags, this solution is much better as it does not generate intermediate arrays.
Old answer: (is doing the same in a more complicated way)
Here is another hacky way:
The HTML string represents a select box that lets you select quantities. Thus, we can assume that the number of option tags represent the maximum number to choose from (in your example 3).
So we only have to count the number of option tags. We can do this using a combination of str_word_count() and array_count_values().
So assuming we have this HTML string (from your example):
$str = '<select name="products[1][quantity]" id="quantity-1"><option selected="selected" value="1">1</option><option value="2">2</option><option value="3">3</option></select>';
we can get the words be using str_word_count(). As there are opening and closing option tags, we are looking for closing tags for simplicity. Hence we have to tell the function to treat / also as word character:
str_word_count($str, 1, '/');
gives:
Array
(
[0] => select
[1] => name
[2] => products
[3] => quantity
[4] => id
[5] => quantity-
[6] => option
[7] => selected
[8] => selected
[9] => value
[10] => /option
[11] => option
[12] => value
[13] => /option
[14] => option
[15] => value
[16] => /option
[17] => /select
)
As we can see /option occurs 3 times.
Now we use array_count_values() to count them:
array_count_values(str_word_count($str, 1, '/'));
gives:
Array
(
[select] => 1
[name] => 1
[products] => 1
[quantity] => 1
[id] => 1
[quantity-] => 1
[option] => 3
[selected] => 2
[value] => 3
[/option] => 3
[/select] => 1
)
So we can get the number of option tags and therefore the highest number by simply applying:
$counter = array_count_values(str_word_count($str, 1, '/'));
$number = $counter['/option'];
Of course using a HTML parser would be better, but if you know that the function will always generate the HTML this way, counting the option tags should work.