Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

j'apprends php 1

41 réponses
Avatar
Olivier Masson
Bonjour,

Dans le cadre de ma décision "j'apprends à coder proprement et
efficacement", j'ai 2.5 questions à vous poser (ça ne fait que commencer
:)).

Tout d'abord, pourquoi ne peut-on pas faire :
$a = (explode("?",$string))[0];
puisque explode renvoie un array ?

Ensuite, pourquoi ceci ne fonctionne pas :
array_flip($a);
ni même :
array_flip(&$a);
On est donc forcé d'écrire :
$a = array_flip($a);
?

La demi-question : Est-il (vraiment) utile de faire un unset dès que
l'on a plus besoin d'une variable si cette variable ne contient pas des
tonnes de données ?

Petite anecdote : hier en cherchant si qq un avait déjà codé ce que je
voulais, je suis tombé sur une solution.
Elle est publiée sur le site d'une agence web. Bon.
Cette solution fait 50 lignes, qui ressemblent à s'y méprendre à du
visualbasic codé par un enfant de 10ans.
Voyant ça, j'ai cherché de mon côté pour, au final, arriver à un
meilleur résultat en... 5 lignes (et 15 minutes de recherche dans le doc
php) !
Jusque là, vous me direz, c'est commun dans le monde du web. Sauf que
cette agence à créer son CMS en PHP :) (et là, c'est le drame).

Je ne veux pas devenir comme ça ! Alors je pose des questions :)
Et comme d'hab, c'est sympa de votre part d'y répondre.

10 réponses

1 2 3 4 5
Avatar
Olivier Miakinen
Le 06/11/2009 19:43, Olivier Masson a écrit :

if (count($del))



Si $del est un tableau vide, la boucle foreach ne sera de toutes façons
pas exécutée. Donc le test est inutile.



Ben non : Invalid argument supplied for foreach().



Ah ? Avec un tableau vide, vraiment ? Ou avec une valeur nulle ?

Fonctionne normalement avec ma condition inutile.



Peut-être parce que count(null) retourne 0, en revanche si j'en crois la
doc count(0) et count("") retournent 1.
Avatar
Olivier Masson
Olivier Miakinen a écrit :
Le 06/11/2009 19:43, Olivier Masson a écrit :
if (count($del))


Si $del est un tableau vide, la boucle foreach ne sera de toutes façons
pas exécutée. Donc le test est inutile.


Ben non : Invalid argument supplied for foreach().



Ah ? Avec un tableau vide, vraiment ? Ou avec une valeur nulle ?



Je pensais qu'en indiquant $del=array() dans la déclaration des
arguments (je sais pas si on dit ainsi), c'était ok mais non.


Fonctionne normalement avec ma condition inutile.



Peut-être parce que count(null) retourne 0, en revanche si j'en crois la
doc count(0) et count("") retournent 1.



Précisément. Je suis déçu car je pensais que array() était de type array
mais non, c'est null, c'est nul.
En fait, si ça avait été comme je pensais ci-dessus, il n'y aurait pas
eu besoin du if.
Avatar
Olivier Miakinen
Le 06/11/2009 21:03, Olivier Masson a écrit :

Ah ? Avec un tableau vide, vraiment ? Ou avec une valeur nulle ?



Je pensais qu'en indiquant $del=array() dans la déclaration des
arguments (je sais pas si on dit ainsi), c'était ok mais non.



C'est ok si tu ne passes pas de paramètre à la fonction. Ça ne l'est
plus si tu lui passes une valeur null.

Cf. <http://www.miakinen.net/tmp/test_array.php>

Source :
----------------------------------------------------------------------
<?php
header("Content-Type: text/plain; charset=UTF-8");

function changeQuery($add=array(),$del=array()) {
echo "add : "; var_dump($add);
echo "del : "; var_dump($del);
echo "n";
}

echo "changeQuery()n"; changeQuery();
echo "changeQuery(null)n"; changeQuery(null);
echo "changeQuery(null, null)n"; changeQuery(null, null);
echo "changeQuery(array(), null)n"; changeQuery(array(), null);
?>
----------------------------------------------------------------------

Résultat :
----------------------------------------------------------------------
changeQuery()
add : array(0) {
}
del : array(0) {
}

changeQuery(null)
add : NULL
del : array(0) {
}

changeQuery(null, null)
add : NULL
del : NULL

changeQuery(array(), null)
add : array(0) {
}
del : NULL
----------------------------------------------------------------------
Avatar
Bruno Desthuilliers
Olivier Miakinen a écrit :
Bonjour,

Je suis d'accord avec certaines de tes remarques, mais pas avec
d'autres.

Le 06/11/2009 16:41, Bruno Desthuilliers répondait à Olivier Masson :
function changeQuery($add=array(),$del=array()) {
parse_str(urldecode($_SERVER['QUERY_STRING']),$output);


1/ t'as pas déclaré ni initialisé $output avant.



Pas d'accord : c'est la fonction parse_str() qui initialise la valeur
(il n'y a pas de cas où la fonction puisse laisser cette valeur non
initialisée). Ta remarque me fait penser au genre de code suivant que
l'on voit quelquefois :
$variable = 0; // initialisation
$variable = ... résultat d'un calcul ...



Je ne pense pas que ce soit tout à fait similaire. Dans le cas qui nous
intéresse - et pour autant que je sache -, si tu passes un array à
parse_str, la fonction n'ira pas en allouer un nouveau. Mais bon, je
peux me tromper, j'ai pas été regarder l'implémentation !-)

Désolé, j'ai énormément de mal avec les variables qui apparaissent comme
ça de nulle part.

Quant à déclarer sans initialiser... ben c'est du PHP, hein !



No comment.

2/ et si je veux travailler sur une autre url ? (ou, plus exactement, la
querystring d'une autre url).



Ben ça, je suppose que le besoin ne s'en fait pas sentir dans ce code



Pas encore, peut-être - mais c'est quand même un cas d'utilisation plus
que courant (je parles pas expérience), et qui peut le plus peut le
moins... Un premier paramètre 'querystring' avec une valeur NULL par
défaut ne coûtait pas beaucoup plus cher et rendait immédiatement la
fonction bien plus générique.


(mais seul Olivier Masson pourrait le dire).

if (count($del))


Si $del est un tableau vide, la boucle foreach ne sera de toutes façons
pas exécutée. Donc le test est inutile.



Oui, entièrement d'accord.

foreach ($del as $value) {


ce sont plutôt des clés, non ?-)



Oui, mais là tu chipotes.



C'est une de mes grandes spécialités, en effet.

Les noms des clés sont aussi des valeurs...



Of course. Mais un bon nommage favorise la lisibilité du code.

mais oui, ok, j'ai vu le souriard.

if (isset($output[$value])) unset($output[$value]);


J'aime pas les conditionnelles incomplètes:

if (isset($output[$value])) {
unset($output[$value]);
}



D'accord et pas d'accord.

Je serais d'accord s'il l'avait écrit sur deux lignes :

if (isset($output[$value]))
unset($output[$value]);

Mais à mon sens il n'y a pas de danger quand c'est sur une seule ligne :

if (isset($output[$value])) unset($output[$value]);

le jour où tu veux ajouter un log, un trace ou quoi ou qu'est-ce, au
moins tu risques pas de manger une fermeture de bloc au passage...



Pour ma part, j'ajoute les accolades au moment de mettre ce code sur
deux lignes pour y ajouter l'instruction supplémentaire. Pas de risque
de se tromper.



Et pourtant, j'ai déjà vu le cas.

$result = array_merge($output,$add);


Attention, il est parfaitement valide d'avoir plusieurs fois le même
paramètre dans une querystring (paramètre multivalué). Avec array_merge,
tu aura un remplacement pur et simple.



Voui, m'enfin bon, avec l'ancien code les paramètres multivalués
n'étaient pas gérés non plus vu que tu ne pouvais pas dire quelle valeur
tu voulais remplacer : on peut donc supposer que ce cas n'est pas censé
se produire.



En l'occurrence, je me contentais d'attirer l'attention d'Olivier sur
une limitation de son code.

Cela dit, ajouter un commentaire pour expliquer les limitations pourrait
être utile.



+1

return http_build_query($result, '', '&amp;');
}


Manque quand même un truc : là tu renvoie une querystring, pas une URL
complète.





Accessoirement aussi: on peut vouloir générer une querystring pour une
autre raison que l'affichage dans une page. Il serait donc préférable -
en termes de généricité - de préciser (via un paramètre optionnel) si on
veut une entitée ou une vraie éperluette.

L'ancien code prenait une URL complète et renvoyait une URL complète, le
nouveau prend une query string



Même pas - hélas à mon avis.

et renvoie une query string : ça me
semble cohérent.



La fonction en elle-même est cohérente, mais au niveau fonctionnalité
elle n'est pas équivalente à l'original.

Le reste du traitement sera fait en dehors de cette
fonction, forcément.



CQFD.
Avatar
Bruno Desthuilliers
Olivier Masson a écrit :
Bruno Desthuilliers a écrit :

En PHP, c'est d'un intérêt limité (j'ai pas dit "inutile", hein...).
Ca a plus de sens dans un langage objet (un vrai, je veux dire), et -
en matière de développement web, avec un modèle d'exécution basé sur
un long running process.




Pourtant PHP5 est censé être pas mal objet non (et le 6 carrément donc) ?



Non. Les deux ont ajouté au modèle objet déjà existant dans PHP4, mais
ça ne fait pas de PHP un langage objet - au sens ou Python ou Ruby sont
des langages objet. En PHP, une chaine n'est pas un objet, un entier
n'est pas un objet, un array n'est pas un objet, une fonction n'est pas
un objet, etc etc.... Bref, PHP reste fondamentalement un langage
procédural.

Je doute que ce soit inutile tout de même (oui, tu l'as pas dit, ok.)



Non, je ne l'ai pas dit.



function changeQuery($add=array(),$del=array()) {
parse_str(urldecode($_SERVER['QUERY_STRING']),$output);



1/ t'a pas déclaré ni initialisé $output avant.




exact mais ça l'est en fait.



Oui, mais cette variable apparaît de nulle part, et on se demande d'où
elle vient. Ok, c'est un pattern courant en PHP, mais il heure
profondément ma sensibilité artistique !-)

2/ et si je veux travailler sur une autre url ? (ou, plus exactement,
la querystring d'une autre url).



Ben je veux pas :)



Tu parie que tu va en avoir besoin plus tôt que tu ne le crois ?-)

Comme je l'ai dit, c'est adapté à mon contexte.



Donc quand tu aura le besoin de la même fonction mais pouvant bosser sur
n'importe quelle querystring arbitraire, tu fera un copié-collé et tu
aura deux implémentations à maintenir en parallèle ?

Et j'en profite pour indiquer un argument qui ne me convainc pas dans la
POO : c'est extensible, réutilisable...



Pas forcément plus que du fonctionnel ou du procédural. Cet argument là,
c'est un peu de l'huile de serpent, n'est-ce pas.

Super ! Mais qd j'ai :
1/ des délais à tenir,
2/ des fonctions qui ne me serviront plus ou qui ne me serviront jamais
autrement,
je ne vois nullement l'intérêt de perdre du temps à rendre tout
parfaitement adaptable à tout contexte



Certaines fonctionnalités sont tellement spécifiques à un projet qu'il
serait effectivement inepte de vouloir les rendre générique. D'autre,
par contre, correspondent à des besoins récurrents, et il est parfois
très simple de les rendre tout de suite suffisament générique pour
couvrir la majorité des cas d'utilisation. C'est ici le cas, et
l'"effort" nécessaire est tellement dérisoire (deux lignes de code et 15
secondes de réflexion....) qu'il est AMHA dommage de ne pas l'avoir fait
immédiatement.

(c'est aussi valable pour du dev
non objet d'ailleurs.)



Tout à fait.



if (count($del))



Si $del est un tableau vide, la boucle foreach ne sera de toutes
façons pas exécutée. Donc le test est inutile.




Ben non : Invalid argument supplied for foreach().



Le code suivant fonctionne très bien chez moi:

<?php
function foo($bar=array()) {
foreach($bar as $baz) {
echo $baz;
}
}

foo();
?>


Fonctionne normalement avec ma condition inutile.



AMHA, tu a passé NULL en argument. Auquel cas - si c'est le "pattern"
d'utilisation recommendé - tu devrais tester sur "is_array", pas sur
"count". Mes deux centimes...


foreach ($del as $value) {



ce sont plutôt des clés, non ?-)



C'est vrai que ça fait couillon et peut nuire à la compréhension... mais
c'est comme ça (je vais qd même pas faire un flip juste pour pouvoir
lire les key !).



Je pensais juste au nommage. Effectivement, ce sont des valeurs, qui
correspondent à des clés dans le tableau représentant la querystring.
Personnellement, j'aurais utilisé "$param" ou "$name" pour éviter toute
ambiguité.


if (isset($output[$value])) unset($output[$value]);



J'aime pas les conditionnelles incomplètes:

if (isset($output[$value])) {
unset($output[$value]);
}




Je ne vois pas en quoi c'est incomplet.



cf plus bas.

A mon avis, question de style.



Tout à fait.

En tout cas, je fais toujours comme ça quand je sais qu'il n'y aura rien
de plus après la condition.
JE SAIS que ça peut ne pas plaire, mais les accolades de partout, ça me
gonfle et je ne trouve pas ça DU TOUT plus lisible.

A l'inverse, les
($a==='prout')?'do_this':(($b==='crotte')?'youpi':'piyou'))
ça m'insupporte.



Avec deux opérateurs ternaires enchainés, effectivement, ça devient pénible.

Il y a aussi une question de gout. Y'en a qui mettent des switch dès la
moindre condition, y'en a (y'en a ?) qui aiment les endif, etc.



Les "endXXX" sont surtout prévus pour le code "embarqué" dans du html -
où les accolades fermantes sont peu lisibles.

le jour où tu veux ajouter un log, un trace ou quoi ou qu'est-ce, au
moins tu risques pas de manger une fermeture de bloc au passage...





}



$result = array_merge($output,$add);



Attention, il est parfaitement valide d'avoir plusieurs fois le même
paramètre dans une querystring (paramètre multivalué). Avec
array_merge, tu aura un remplacement pur et simple.



Alors ça, c'est une TRES BONNE remarque même si dans ce que tu cites ça
ne pose nullement problème.
En effet, des index sont ajoutés aux queries (si j'ai
?nom[]=bob&nom[]=tom, j'obtiens ?nom[0]=bob&nom[1]=tom).
Mais ça coince pour le del car le unset n'est plus bon.


return http_build_query($result, '', '&amp;');
}



Manque quand même un truc : là tu renvoie une querystring, pas une URL
complète.



Ah ouais ça change tout et puis c'est balèze de reconstruire une url !
:)



Eh, tu postes ton code, je cherche la petite bête, hein ?-)

Je mentionnais surtout ça par "honnêteté" vis à vis de l'autre
magnifique (hahum...) example de code que nous avons si joyeusement
commenté - du point de vue fonctionnel, les deux codes ne sont pas
strictement équivalents. Quoique parler de "fonctionnel" pour l'autre
bout de code, c'est un peu exagéré !-)

(et en fait, c'est fait dans mes fonctions)


Avatar
Olivier Masson
Bruno Desthuilliers a écrit :
Olivier Masson a écrit :
Comme je l'ai dit, c'est adapté à mon contexte.



Donc quand tu aura le besoin de la même fonction mais pouvant bosser sur
n'importe quelle querystring arbitraire, tu fera un copié-collé et tu
aura deux implémentations à maintenir en parallèle ?


[...]
Certaines fonctionnalités sont tellement spécifiques à un projet qu'il
serait effectivement inepte de vouloir les rendre générique. D'autre,
par contre, correspondent à des besoins récurrents, et il est parfois
très simple de les rendre tout de suite suffisament générique pour
couvrir la majorité des cas d'utilisation. C'est ici le cas, et
l'"effort" nécessaire est tellement dérisoire (deux lignes de code et 15
secondes de réflexion....) qu'il est AMHA dommage de ne pas l'avoir fait
immédiatement.




Je comprends tout à fait ton raisonnement... qui est celui qui m'a jadis
fait perdre des heures. Car il est toujours très délicat de déterminer
ce qui pourra être utile, si toutefois c'est utile un jour. Mais
également des questions comme "faut-il que je sécurise toutes les
entrées ?", ce qui peut être très long à faire, alors que l'interface
(encore une fois, excuse-moi pour le vocabulaire pas forcément
approprié) ne sera *jamais* publique et que je suis pas encore assez
vieux pour m'auto-SQL-injecter :)
Donc dans le cas que tu cites, ok, mais moi j'ai la flemme d'ajouter un
argument à chaque fois. Et cette fonctionne est très utilisée dans mon
code. Donc je monte ma fonction pour que, par défaut, elle soit
argumentée pour le cas le plus classique ($_SERVER['REQUEST_URI'])




Eh, tu postes ton code, je cherche la petite bête, hein ?-)




Oui, oui, mais dans mon code je reconstruis l'url.
Là, j'ai posté le principe.
Mais tu as raison d'avoir tout relevé car le count à été remplacé par le
is_array et je vais modifié la fonction pour le $del.

J'imagine la tonne de commentaires négatifs si j'avais posté tout le bloc :)
Mais en fait, c'est comme ça que l'on devrait apprendre.
(Plutôt que de) En plus de lire des bouquins et des docs, je devrais
payer un 2 gars (un avis n'est jamais suffisant ;)) pour relire quelques
scripts et hop, on y passe 1 semaine. Mais ça va faire cher la formation !
Avatar
Olivier Miakinen
Le 05/11/2009 21:12, Mickaël Wolff a écrit :

Pas seulement. Si tu savais les horreurs que j'ai vu dans des applis de
gestion...


Comme l'usage des float pour gérer les flux monétaires ?



Trouvé sur le « daily WTF » francophone :
http://img.thedailywtf.com/images/fr/errors/bateau.JPG

--
Olivier Miakinen
Avatar
Eric Demeester
dans (in) fr.comp.lang.php, Olivier Miakinen <om+
ecrivait (wrote) :

Bonjour,

Le 05/11/2009 21:12, Mickaël Wolff a écrit :
> Comme l'usage des float pour gérer les flux monétaires ?

Trouvé sur le « daily WTF » francophone :
http://img.thedailywtf.com/images/fr/errors/bateau.JPG



Mouarf :)

Plus sérieusement, déterminer quel type de variable choisir en fonction
du contexte et du résultat attendu est très important quel que soit le
langage de programmation utilisé, mais ça l'est encore plus quand on
code avec un langage comme PHP, très faiblement typé, qui va accepter à
peu près n'importe quoi en entrée et s'adapter comme il peut (mais
généralement pas comme l'auteur du code le souhaiterait) pour retourner
un résultat.

Sans oublier la base de données, qui stocke les informations dans des
champs dont les caractéristiques (encodage et typage) doivent elles
aussi être définies précisément si on veut éviter les effets de bords.

Je pense utile de souligner ces points, parce que quand on débute en
programmation (ou en PHP, sujet de ce fil), on n'en tient souvent pas
compte par inexpérience, et on se trouve ensuite confronté à des
problèmes qui auraient pu être évités en suivant dès le départ quelques
de bon sens.

Mes deux centimes.

--
Eric
Avatar
Olivier Masson
Olivier Miakinen a écrit :
Le 05/11/2009 21:12, Mickaël Wolff a écrit :
Pas seulement. Si tu savais les horreurs que j'ai vu dans des applis de
gestion...


Comme l'usage des float pour gérer les flux monétaires ?



Trouvé sur le « daily WTF » francophone :
http://img.thedailywtf.com/images/fr/errors/bateau.JPG




Vous utilisez quoi pour les sous alors ?
Parce que moi, qd j'ai du float en base à exploiter, je garde bêtement
le float (bien évidemment, je n'affiche que 2 décimales et je passe par
printf).
Et quand c'est moi qui m'occupe de la base... je fais pareil :)

Mais bon, je ne gère pas les échanges de devises sur lesquelles un
cercle de gens puants (oups, HS) peuvent se faire un max sur un
changement de la 4eme décimales.
Avatar
Mickaël Wolff
Olivier Masson a écrit :

Vous utilisez quoi pour les sous alors ?
Parce que moi, qd j'ai du float en base à exploiter, je garde bêtement
le float (bien évidemment, je n'affiche que 2 décimales et je passe par
printf).
Et quand c'est moi qui m'occupe de la base... je fais pareil :)



Le problème provient du fait qu'il est impossible de repésenter un
nombre décimal non entier en nombre binaire exact. Ce qu'on sait faire,
c'est adopter une représentation du nombre. Le standad IEEE 754 et
successeurs proposent une représentation d'une précision acceptable pour
un utilisateur qui souhaite privilégier les performances du calcul au
détriment de l'exactitude.

Si dans le calcul d'une scène 3D l'imprécision n'est pas
problématique, dans les calculs de TVA cette approximation est
intolérable. Du moins pour l'URSSAF.

La plupart du temps on ne voit pas la différence. L'écart n'est
véritablement observable que lorsque le nombre d'opérations considérées
pour le calcul est importante, ou que somme deviennent importantes.


Mais bon, je ne gère pas les échanges de devises sur lesquelles un
cercle de gens puants (oups, HS) peuvent se faire un max sur un
changement de la 4eme décimales.



Concrètement, ANSI SQL propose un type précis : DECIMAL. MySQL le
supporte et garanti le comportement du DECIMAL. Du coup, je fais tout
les calculs dans MySQL.
Si tu n'as pas de bases de données, il existe des bibliothèques qui
fournissent cette fonctionnalité (au hasard, GMP qui est disponible dans
PHP).

--
Mickaël Wolff aka Lupus Michaelis
http://lupusmic.org
1 2 3 4 5