Nach Update von PHP 5.6 auf 7.4: "Warning: count(): Parameter must be an array or an object that implements Countable"

  • PHP

Es gibt 8 Antworten in diesem Thema. Der letzte Beitrag () ist von Marcus Gräfe.

    Nach Update von PHP 5.6 auf 7.4: "Warning: count(): Parameter must be an array or an object that implements Countable"

    Ich habe die PHP-Version für eine Software von 5.6 auf 7.4 aktualisiert und erhalte nun an einer Stelle folgende Fehlermeldung:

    Warning: count(): Parameter must be an array or an object that implements Countable

    Der entsprechende (stark vereinfachte) Code sieht wie folgt aus:

    PHP-Quellcode

    1. <?php
    2. $a = 'a';
    3. $b = 'b';
    4. test($a, $b);
    5. function test($x = array()) {
    6. if (count($x)) {
    7. echo 'Success';
    8. }
    9. }
    10. ?>

    Der Fehler tritt (in PHP 7) in Zeile 7 auf, in PHP 5 gar nicht, dort wird "Success" angezeigt.

    Die Frage lautet nun allerdings nicht direkt, wie ich den Fehler beheben kann, sondern was der Originalautor (die Software ist nicht von mir) sich bei dem Code gedacht hat. Kann das einer erkennen? Fragen kann ich ihn leider nicht. Also im Prinzip geht es um test($a, $b);. Warum macht(e) man sowas?
    Besucht auch mein anderes Forum:
    Das Amateurfilm-Forum
    Das ist eine gute Frage. Ich glaube das kann so nie nicht funktioniert haben.

    In GoLang bspw. kann man n-Params in ein Slice mappen lassen beim Call. -> In PHP glaube ich nicht.. Wäre mir neu.

    tehplayground.com/YdDWWNpO3n6tVVT8
    Wie du siehst, überschreibt er auch den init-Slice mit dem String "a" und alle Folgeparameter werden verworfen.

    Das einfachste wäre den Aufruf zu fixen.

    PHP-Quellcode

    1. ​test([$a, $b]);



    VG,
    Acr0most :thumbup:
    Wenn das Leben wirklich nur aus Nullen und Einsen besteht, dann laufen sicherlich genügen Nullen frei herum. :D
    Signature-Move 8o
    kein Problem mit privaten Konversationen zu Thema XY :thumbup:
    Danke für deine Antwort.

    Acr0most schrieb:

    Ich glaube das kann so nie nicht funktioniert haben

    Ich denke mir mittlerweile auch, dass an der Stelle fehlerhaft programmiert wurde. Allerdings ist der gesamte Rest der Software sehr sauber und ordentlich programmiert, daher würde mich das eigentlich wundern. Komisch ist einfach, dass es unter PHP 5 quasi funktioniert, unter PHP 7 gar nicht.

    Acr0most schrieb:

    Wie du siehst, überschreibt er auch den init-Slice mit dem String "a" und alle Folgeparameter werden verworfen.

    Das ist im Prinzip auch das Verhalten, was die Funktion unterm Strich trotzdem funktionierend gemacht hat. Im Code ist im Anschluss nämlich eine switch-case-Anweisung, die $x als String abfragt.

    Ich habe meinen Code nun wie folgt geändert (hier wieder am Beispiel meines vereinfachten Codes):

    PHP-Quellcode

    1. <?php
    2. $a = 'a';
    3. $b = 'b';
    4. test($a);
    5. function test($x) {
    6. if ($x != '') {
    7. echo 'Success';
    8. }
    9. }
    10. ?>

    Die Variable $b bzw. deren Wert wird nämlich auch gar nicht in der Funktion verwendet. Ich hoffe nur, dass ich nichts übersehen habe und die Software ihren Job tadellos verrichtet. Bisher tut sie das scheinbar.
    Besucht auch mein anderes Forum:
    Das Amateurfilm-Forum

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Marcus Gräfe“ ()

    Marcus Gräfe schrieb:

    if (count($x)) {


    Count gibt dir doch bestimmt die Anzahl der Elemente des Arrays zurück und bei einem String ein Char-Array (???) und in PHP ist doch bestimmt wie im klassischen c das 0 ein False und alle anderen Zahlen True und damit sagt die Funktion, dass mindesten 1 Character in dem String ist? Also strlen($x) > 0? Aber ich habe keine Ahnung von PHP, also kann das auch völlig falsch sein.

    Marcus Gräfe schrieb:

    if (count($x))


    Ich vermute, dass man hier einfach eine verkürzte Schreibweise gewählt hat, die für Strings (damals) UND Arrays funktioniert hat. In diesem Fall wäre es auf jeden Fall deutlich besser, dass Statement zu

    PHP-Quellcode

    1. is_array() && count() || is_string() && strlen()
    zu ändern, ohne den Parameter zu verändern.

    Dennoch ist es ziemlich stark verkürzt und so können wir nur spekulieren...

    Aber dies ist ein typisches Beispiel für die Wichtigkeit von Unit-Tests...

    Bluespide schrieb:

    bei einem String ein Char-Array (???)


    Nope. Leider (oder vllt. gut so?) nicht.

    PHP-Quellcode

    1. ​$x = "abcdef";
    2. echo count($x); // WARNING && OUTPUT: 1
    3. echo strlen($x); // OUTPUT: 9


    Dementsprechend wird die Validierung vorher nicht wie von @noob98 geschrieben stattgefunden haben. Komischerweise.
    Aber ohne den eigentlichen Code zu kennen wir es schwierig das zu hinterfragen.

    VG,
    Acr0most :thumbup:
    Wenn das Leben wirklich nur aus Nullen und Einsen besteht, dann laufen sicherlich genügen Nullen frei herum. :D
    Signature-Move 8o
    kein Problem mit privaten Konversationen zu Thema XY :thumbup:

    Acr0most schrieb:

    Aber ohne den eigentlichen Code zu kennen wir es schwierig das zu hinterfragen.

    Naja, eigentlich sagt der wahre Code nicht viel mehr als mein gekürzter Code. Hier der Originalcode ohne meine Modifikation:

    Die entsprechende Funktion

    PHP-Quellcode

    1. /**
    2. * Searches and processes time intervals expressed with a dashed range or an asterisk followed by a slash followed by a number.
    3. *
    4. * @param array $element
    5. */
    6. protected function getSlashedInterval($element = array()) {
    7. if (count($element)) {
    8. $scope = 0;
    9. $resultArr = array();
    10. switch ($element) {
    11. case 'startMinute':
    12. $scope = 59; // the scope within which the interval must be.
    13. $result = 0; // where the scope starts.
    14. break;
    15. case 'startHour':
    16. $scope = 23;
    17. $result = 0;
    18. break;
    19. case 'startDom':
    20. $scope = date('t', $this->timebase);
    21. $result = 1;
    22. break;
    23. case 'startMonth':
    24. $scope = 12;
    25. $result = 1;
    26. break;
    27. case 'startDow':
    28. $scope = 7;
    29. $result = 1;
    30. break;
    31. default:
    32. exit;
    33. }
    34. foreach ($this->cronjobsDataRaw[$element] as $key => $value) {
    35. if (is_array($value)) {
    36. $iterator = $value['1'];
    37. // the variant with the asterisk.
    38. if ($value['0'] == '*') {
    39. while ($result <= $scope) {
    40. $resultArr[] = $result;
    41. $result = $result + $iterator;
    42. }
    43. }
    44. // the variant with a dashed range.
    45. else if (is_array($value['0']) && count($value['0'])) {
    46. $result = $value['0']['0'];
    47. $upper = count($value['0']) - 1;
    48. $scope = $value['0'][$upper];
    49. while ($result <= $value['0'][$upper]) {
    50. $resultArr[] = $result;
    51. $result = $result + $iterator;
    52. }
    53. }
    54. $getrid = array_search($value, $this->cronjobsDataRaw[$element]);
    55. if (($getrid !== false) && is_array($this->cronjobsDataRaw[$element][$getrid])) unset($this->cronjobsDataRaw[$element][$getrid]);
    56. $this->cronjobsDataRaw[$element] = array_merge($this->cronjobsDataRaw[$element], $resultArr);
    57. }
    58. }
    59. }
    60. }


    Der Aufruf (in Zeile 37)

    PHP-Quellcode

    1. /**
    2. * Searches and processes time ranges expressed with a dash between two numbers (dashed range).
    3. *
    4. * @param string $element -- these are the entries representing months, hours etc.
    5. * @param integer $position -- there might be multiple entries for each month, hour etc.
    6. */
    7. protected function getDashedRange($element = '', $position = 0) {
    8. $i = 1;
    9. $insert = array();
    10. $data = array();
    11. // get the numbers between the dashes.
    12. if (is_array($this->cronjobsDataRaw[$element][$position])) {
    13. foreach ($this->cronjobsDataRaw[$element] as $key => $data) {
    14. if (count($data) > 1) {
    15. $count = ($data['1'] - $data['0']);
    16. if ($count) {
    17. $insert[] = intval($data['0']);
    18. while ($i < $count) {
    19. $insert[] = $data['0'] + $i;
    20. $i++;
    21. }
    22. $insert[] = intval($data['1']);
    23. }
    24. switch (count($data)) {
    25. case '2':
    26. // this is a normal dashed range.
    27. unset($this->cronjobsDataRaw[$element][$key]);
    28. $this->cronjobsDataRaw[$element] = array_merge($this->cronjobsDataRaw[$element], $insert);
    29. break;
    30. case '3':
    31. // this is a dashed range additionally containing a slashed interval.
    32. unset($this->cronjobsDataRaw[$element][$key]);
    33. $this->cronjobsDataRaw[$element][$key][] = $insert;
    34. $this->cronjobsDataRaw[$element][$key][] = $data['2'];
    35. // break down slashed interval.
    36. $this->getSlashedInterval($element, $key);
    37. break;
    38. }
    39. }
    40. }
    41. }
    42. }

    Besucht auch mein anderes Forum:
    Das Amateurfilm-Forum

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Marcus Gräfe“ ()

    ja also dann würde ich - wie du schon oben geschrieben hast, den zweiten Parameter einfach weglassen.

    Und der Verständlichkeit halber den Param $element nicht mit = array() initialisieren, sondern mit null oder Leerstring. Zusätzlich im Funktionskommentar bei @param das auf string (oder wenn du dich für null entscheidet, string|null) umschreiben.
    Wenn das Leben wirklich nur aus Nullen und Einsen besteht, dann laufen sicherlich genügen Nullen frei herum. :D
    Signature-Move 8o
    kein Problem mit privaten Konversationen zu Thema XY :thumbup: