Everything in this answer is based on my reading of the code here and.
I did not write this, I did not go through it with the debugger, this is only my interpretation.
It seems that the intention was strict mode to check if the string as a whole was valid for encoding, while lax mode would allow a subsequence that could be part of a valid string. For example, if a string ends with what should be the first byte of a multibyte character, it will not match in strict mode, but it will still match UTF-8 in loose mode.
However, there seems to be an error *, where in lax mode, in some cases, only the first byte of the string is checked.
Example:
Byte 0xf8 not allowed anywhere in UTF-8. When placed at the beginning of a line, mb_detect_encoding() correctly returns false for it regardless of which mode is used.
$str = "\xf8foo"; var_dump( mb_detect_encoding($str, 'UTF-8'), // bool(false) mb_detect_encoding($str, 'UTF-8', true) // bool(false) );
But while the leading byte can occur anywhere in the UTF-8 sequence, lax mode returns UTF-8.
$str = "foo\xf8"; var_dump( mb_detect_encoding($str, 'UTF-8'), // string(5) "UTF-8" mb_detect_encoding($str, 'UTF-8', true) // bool(false) );
So, although your ISO-8859-1 'áéóú' not valid UTF-8, the first byte "\xe1" may occur in UTF-8 and mb_detect_encoding() erroneously returns the line as such.
* I opened a report for this at https://bugs.php.net/bug.php?id=72933