Notis: nästlade [QUOTE]- och [PHP]-taggar bygger ordentligt på höjden i forumet, så det blir snabbt väldigt mycket att scrolla när man delar upp det som jag gör… Men det får ni stå ut med!
Tillägg: om ni läser detta i SweClockers 6.0 så kanske ovanstående inte gäller…
Skrivet av anon186159:
Däremot så är det så mycket kod och jag vinner inget på att gå över till mysqli etc. att jag skippat det men tills nästa projekt ska jag definitivt uppgradera mig en level till!
Du vinner i prestanda (vilket troligen är helt försumbart, och inte ett argument här) och i säkerhet. Det är inte bara godhjärtade numismatiker som kommer använda din sida, utan även automatiserade skript från datorer världen över som konstant söker efter sårbarheter att utnyttja. Man får alltid väga in hotbild, men tja. Det är inte bortkastad tid att göra saker "rätt" vad gäller just databaskommunikation, vilket är det absolut vanligaste säkerhetsproblemet i PHP-kod världen över, skulle jag tro.
Ska du ändå radikalt skriva om koden så är det ett bra tillfälle. De ändringar som krävs är oftast bara att man lägger till rader och "låser" variabler till databassvar; i praktiken ger det överskådligare kod i slutändan. Det är troligen en av de saker som kommer ge mest tandagnisslan innan du får den "korrekt", men när väl polletten trillat ner så är det enkelt framöver.
Genomgång av filerna:
head.php
XHTML Transitional är lite daterat (XHTML var det "nya heta" runt år 2000). Det fungerar givetvis, men man har mycket att vinna i förenkling om man går över till HTML5, som är explicit designat för att vara bakåtkompatibelt till Hedenhöstiden. Om du tar bort en massa tecken och skriver ovanstående som:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
så har du helt plötsligt ett fullkomligt giltigt HTML5-dokument. Mindre kod, mer läsbart → bättre! Notera att vi inte behöver självstängande taggar (hela konceptet med självstängande taggar är egentligen inte ens applicerbart i "vanlig" HTML jämfört med XHTML), och att vi kunde använda en enklare syntax för att ange teckenkodning. Det finns liknande syntaxförenklingar på andra håll, men HTML5 är som sagt också bakåtkompatibelt. Det finns alltså saker att vinna på att uppdatera resterande kod, men egentligen inget att förlora på att behålla saker som de är, utan ändringarna kan ske mjukt efterhand.
`rel="shortcut icon"` är en antik kvarleva som bara finns med av bakåtkompatibilitetsskäl: skriv i stället bara `rel="icon"`, som gör samma sak. Och om du ändrar till HTML5 så behöver du inte den avslutande slashen, men det kommer gälla överallt nedan, så det upprepar jag inte igen.
Skrivet av intervet:
<link href="default.css" rel="stylesheet" type="text/css" media="all" />
I HTML5 så skriver man ekvivalent:
<link rel="stylesheet" href="default.css">
`media="all"` är defaultvärdet i HTML5 (tidigare var default `media="screen"`, men i praktiken tolkade alla läsare det på samma sätt som `media="all"` ändå…).
I HTML5 så noterade man också att alla stylesheets de facto är `type="text/css"`, så det behövs inte längre. Detsamma gäller `type="text/javascript"` i `<script>`-taggar.
Skrivet av intervet:
<!--[if IE 6]><link href="default_ie6.css" rel="stylesheet" type="text/css" /><![endif]-->
Behöver vi stödja IE6 längre? Stora aktörer som Google slutade med detta redan för fyra år sedan. Tid som läggs på att explivit stödja IE6 vore nog bättre lagd på andra saker.
Skrivet av intervet:
echo "
<script type=\"text/javascript\" src=\"development-bundle/jquery-1.10.2.js\"></script>
<script type=\"text/javascript\" src=\"js/tab.js\"></script>";}
För att undvika att behöva escapea citationstecken och annat så är det enklare att hoppa ur PHP-läget helt som visades tidigare (och som du gör på vissa ställen). Alternativt (men sämre, då en editor vanligen inte automatiskt kan tolka detta som HTML) kan man använda så kallad heredoc syntax för att skriva längre avsnitt av strängar; men just för HTML-kod är det nog enklast att gå ur PHP-läge.
Notera också det jag skrev om HTML5 ovan: `type="text/javascript"` behövs inte där.
Skrivet av intervet:
<h1><img src="images/logo.png" alt="coin" />Coin<span>Base</span></h1>
Ge `height` och `width` också: då kan en browser allokera rätt dimensioner direkt när koden läses, utan att behöva flytta på element igen när bilden laddats.
include.php
Bättre att ha en dedikerad användare med begränsade rättigheter; din myntdatabasanvändare behöver ju exempelvis inte kunna skapa nya användare och tabeller, eller ta bort hela databasen. En del i säkerhetsmodellen är att användare ska ha definierade roller efter behov.
Skrivet av intervet:
//FORCE SSL
//1 for on. 0 for off
$FORCE_SSL = "0";
Använd `true` och `false` i stället. Det säger mycket mer naturligt vad som menas. Detta gäller på fler ställen i koden där du använder liknande "flaggor".
`if` är egentligen bara intresserat av `true` eller `false`. Konstruktionen med `($FORCE_SSL == 1)` använder en inbyggd operator som jämför två variabler, och returnerar just `true` om de är lika, och `false` om de inte är det. Eftersom `1` implicit är `true` i PHP och `0` är `false` så kan det enklare skrivas:
Än enklare att följa blir det ju om man redan från börjat definierat `$FORCE_SSL = true;`, vilket ändå är vad man menar. `true` och `false` är inbyggda nyckelord i PHP.
Skrivet av intervet:
if ($FORCE_SSL) {
if($_SERVER["HTTPS"] != "on")
{
header("Location: https://" . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"]);
exit();
} }
Indenteringen rasar lite här, och konventionen för `if` ändras. Stycket borde snarare vara:
if ($FORCE_SSL) {
if ($_SERVER["HTTPS"] != "on") {
header("Location: https://" . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"]);
exit();
}
}
för att vara konsekvent, eller än enklare:
if ($FORCE_SSL && $_SERVER["HTTPS"] != "on") {
header("Location: https://" . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"]);
exit();
}
Notera också att det med "indentering" inte menas att man ska sitta och trycka på mellanslagstangenten 12 gånger varje gång man byter rad: en bra editor löser detta åt dig.
Gällande kontrollen i sig så säger manualen bara att `$_SERVER['HTTPS']` är "non-empty" om man kallat via HTTPS, så testet borde kanske snarare vara `if (empty($_SERVER['HTTPS']))` och inte strängjämförelse (notera dock att PHP via ISAPI på IIS (vilket sedan PHP 5.3.0 inte längre är supportat) av någon !"#¤% anledning returnerar "off" om det inte är satt).
För minnes skull kan man notera att det finns en liten fälla i att bygga egna kompletta URL:er som du gör om man exempelvis har servern på en icke-standard port för protokollet (se det för närvarande näst högst rankade, men mer korrekta, svaret från Timo Huovinen på Get the full URL in PHP [SO]), men i ditt fall fungerar det utan problem, så länge HTTPS-servern körs på port 443. Nämner det främst för att det kan ge oväntade resultat på utvecklingsservrar där man kanske använder icke-standardportar.
base_functions.php
Skrivet av intervet:
function getCountries()
{
include ('db_connect.php');
$query="SELECT sa FROM sa ORDER BY sa";
$result = mysql_query($query);
// Lets loop through the result:
while($row = mysql_fetch_object($result)) {
echo "<option value=\"$row->sa\">$row->sa</option>";
}
}
Indenteringen har försvunnit lite igen. `$query`-tilldelningen saknar mellanslag runt `=`. Du kan också kolla upp funktionen `include_once()`, och kanske ännu mer skillnaden mellan `include` och `require`. Här (och i resten av databasfunktionerna) skulle troligen `include_once` eller möjligen `require_once` passa bäst.
Skrivet av intervet:
function getAIDplusone()
{
include ('db_connect.php');
$rowSQL = mysql_query("SELECT MAX(A_ID) AS max FROM coin_bases");
$row = mysql_fetch_array( $rowSQL );
$largestNumber = $row['max'];
echo $largestNumber+1;
mysql_close();
}
Som ovan. Argumentet till `mysql_fetch_array()` bryter konventionen då det är omgivet av mellanslag, så bort med dem. Vissa rader avslutas med tabbtecken, vilket återigen inte är något problem i funktionalitetssynpunkt, men om man delar kod med andra så är detta inte så poppis, då det är lite slarvigt.
Dessutom: vad gör funktionen? Den returnerar nästa "lediga" ID i din databas — men din databas bör sköta tilldelningen av ID själv med hjälp av `auto_increment`! Du ska inte behöva bry dig om detta, så denna funktion borde inte behövas. Det är något som kan behövas tänkas om i databasdesignen här; kanske behövs ett inkrementellt ID för databasens skull, samt ett ID för "verkliga livets" skull, beroende på hur saker används.
För namngivning av funktioner finns två vanliga standarder: antingen `function_names_with_underscores` i gemena tecken, eller `functionNamesInCamelCase`. Vilken som är vanligast beror på språk och ofta preferens. I PHP är det senare vanligare. Det är fördelaktigt att välja en av dessa konventioner. Det finns liknande konventioner för variabelnamn, konstanter, etc., så enbart genom att se hur namnet är skrivet så kan hjärnan koppla till vad det är för objekt, vilket gör koden snabbare att förstå.
Snarare än att ha ett `echo`-uttryck i funktionen, så vore det mer naturligt med att du returnerar ett värde med `return` och skriver ut det med `echo` där det behövs, dvs att du i filen senare kallar `echo getAIDplusone();` (vilket också gör att du kan använda snabbkonstruktionen `<?=getAIDplusone()?>`). Annars borde funktionen snarare heta `echoAIDplusone()`, men det är en onaturlig konstruktion här.
Skrivet av intervet:
function getAIDplusoneroll()
Som ovan. Att döma av funktionen så funderar jag på om du har en tabell för både "mynt" och "rolls", där ID-fältet inte är unikt i tabellen, utan bara per typ..? I så fall bör du splitta dessa typer till två tabeller, eller låta ID-fältet vara unikt för båda (dvs om du har ett mynt med `id=5` så kan du inte ha en "roll" med `id=5`).
Databaser använder dessa unika ID:n för att kunna indexera en databas och snabbt och effektivt hitta rader, så de ska vara unika.
I "rena funktionsfiler" i PHP, dvs filer utan egen output, så rekommenderas det faktiskt att skippa den avslutande taggen. PHP-tolken avslutar ändå detta själv när filen tar slut, och att avsluta manuellt kan ge upphov till problem; se manualen och Why would one omit the close tag? [SO].
process_insert.php
Skrivet av intervet:
$action = mysql_real_escape_string($_REQUEST['do']);
switch($action){
case "add":
//Check for requiered values
Ja, `switch` passar väl in här. Det blir nog en tydlig förbättring vad gäller att kunna hålla koden ren. Konventionen ger att det borde vara ett mellanslag innan `{`, och det borde vara fyra mellanslags indentering, inte tre, för `case`. På nästa rad blandas mellanslag och tabbar för indentering, vilket aldrig ska göras. (((Stavning: "required"))).
Skrivet av intervet:
//Check for requiered values
if ($A_ID == '' || $row == '' || $year == '' || $quality == '' || $value == '' || $type == '' || $country == '---') {
Här är det återigen problemet med PHP:s lösa typning och vad som är "falskt". Vill du jämföra mot den tomma strängen så ska du använda `===`: här jämför du implicerade booleanska värden, så strängen 0 är faktiskt "lika med" den tomma strängen här, vilket inte är riktigt vad du vill. Du skulle med fördel kunna använda `empty()` som nämndes innan.
Skrivet av intervet:
header('Location: /insert.php?do=success');
En vanlig "konvention" är att man lägger till `exit()` efter `header()`. Det finns en viss säkerhetsaspekt i detta då inga knasigheter (företrädelsevis programmerarmisstag) ska kunna få skriptet att fortsätta efter denna punkt. "Hängslen och livrem". Du använder detta på vissa andra ställen (exempelvis i slutet av just denna fil), så i konsekvensens namn skulle det nog även vara på dessa ställen, så nära anropen som möjligt.
insert.php
Skrivet av intervet:
<ul class="tabs">
<li><a href="#tab1">Lägg till ett ark</a></li>
<li><a href="#tab2">Lägg till en rulle</a></li>
</ul>
<div id="tab1">
Ville bara flika in att indenteringen är lite bananas här :-).
Skrivet av intervet:
<form action="function/process_insert.php?do=add" method="post">
I stället för att blanda in en query-sträng i `action`-URL:en så är det nog tydligare enklare att lägga ett `hidden` fält i formuläret. Det finns faktiskt en "regel" gällande när man ska använda `POST`- eller `GET`-metoder i HTTP-specifikation: `POST` används om aktionen har sidoeffekter (exempelvis att lägga till/modifiera/radera saker i en databas), annars används `GET` (exempelvis för att kalla på en viss undersida, etc.). `GET` är alltså något som är vettigt att en användare ska kunna vilja lägga i sina bokmärken, men inte `POST`. I längre ordalag: HTTP 1.1 [W3C].
Skrivet av intervet:
<td><input name="A_ID" type="text" maxlength="5" /></td>
Om du går över till HTML5 så bör du också kolla upp vad attributet `required` kan göra för dina användare; kort sagt, så om det läggs till så kommer användarens webbläsare tydligt och direkt säga till användaren om något fält som är nödvändigt att fylla i, och inte skicka iväg formuläret förrän detta är fixat. Då slipper server behandla en förfrågan som ändå bara refuseras, och det blir en bättre upplevelse för användaren.
Skrivet av intervet:
<input name="is_roll" type="hidden" value="1" />
Du måste inte sätta `value="1"`, då du ju egentligen bara är intresserad av om variabeln överhuvudtaget är satt eller inte. Om du skippar `value` och kontrollerar `isset($_POST['is_roll'])` så får du samma effekt. Notera att om `value` saknas så har variabeln inget värde, men den är likväl satt. Skillnaden däremellan är bra att ha koll på, så att man även ser skillnaden mellan `isset()` och `empty()` fullt ut.
Jag skulle snarare döpt funktionen till `printCountryOptions()` eller något, om den nu faktiskt skriver ut ("print") saker och inte bara hämtar ("get") saker. Att kunna se direkt på funktionens namn vad man kan förvänta sig hända är också bra ur läsbarhetssynpunkt.
Skrivet av anon186159:
insert.php Var skriven i grunden som edit.php (det jag postade först). Och har som sagt försökt att plocka ur så mycket php kod jag har kunnat.
Det ser betydligt enklare ut att följa nu jämfört med innan, så jag skulle säga att det redan har gett resultat.
Skrivet av anon186159:
Kör XHTML 1.0 Transitional (om jag ska vara helt ärlig så har jag noll koll på vad man ska välja. Detta var default från Dreamweaver.) Bör jag välja någon annan? I övrigt så valideras min kod till Valid hos W3C Validator.
HTML5, dvs att egentligen inte ge någon specifik dialekt alls i sin doctype, är framtidssäkert, och enklare, rent ut sagt.
(XHTML Transitional var egentligen en rätt märklig standard, då den bara var tänkt att vara en övergång, men det är en annan diskussion.)
Skrivet av anon186159:
Och på tal om Dreamweaver. Finns det något bättre PHP programmeringsprogram? Med tanke på att det antyddes att det var något knas med min WYSIWYG (och jag använder faktiskt inte WYSIWYG, så Dreamweaver är kanske överflödigt). Förslag tas gärna emot.
Någon vass texteditor tycker jag är trevligt, och mer lär inte behövas för projekt i denna skala. Notepad++ är vanligt på Windows. Jag använder Vim.
Skrivet av anon186159:
Angående åäö så växte koden fram dynamiskt och precis i början var jag tvungen att hårdkoda dessa. Men efter jag lade till en ordentlig design så var det ju inte längre något problem. Ser dock ingen anledning till att ändra om alla åäö.
Nej, det är inga problem att ha kvar entities, utöver att det gör koden lite svårare att läsa. Med en "vass texteditor" som jag nämnde ovan så är det en snabb operation att ändra alla dessa en gång för alla med "sök och ersätt"-funktioner, men det är ingen prioritet :-) .
Skrivet av anon186159:
PHP klagar på undefined variable. Dessa är till för min check till min head.php för att slippa ropa in javascript i onödan. Är detta något jag måste bry mig om? Dock så tar det ju alltid emot att få upp felmeddelanden/varningar.
Varningar är till just för att man ska fixa dem, så det är helt rätt att inte bara "blunda". PHP klagar på att du refererar till variabler som inte har satts; som standard försöker PHP vara snäll och inte komplett dö, utan tolka dessa som en tom sträng och gå vidare med en varning. Vad du borde göra är att kontrollera med `isset()` ifall variabeln är satt — av funktionens natur så kommer den inte ge felmeddelanden om variabeln inte är satt, men däremot ett `false`. (Ett dåligt sätt att ta bort varningar är att tysta felmeddelanden med `@`: gör det ordentligt i stället.)
Skrivet av anon186159:
Sedan undrar jag hur man avgör när man ska använda == eller ===
Comparison Operators [PHP-manualen], How do the equality (== double equals) and identity (=== triple equals) comparison operators differ? [SO].
`===` jämför värde och typ. `==` jämför värde efter att PHP har kämpat med att omvandla värdena på båda sidor till samma typ. Eftersom PHP:s omvandlingsregler är rätt frikostiga så kan det senare ge just lustiga följder med strängen "0" och annat. `!==` är motsvarigheten till `!=`. Större än/mindre än-jämförelser leder till lite lustiga resultat som att `5 > "banan"` blir `true`, eftersom strängen "banan" booleanskt räknas som `true`, vilket motsvarar `1`, vilket är mindre än `5`. Jämför man bananer och heltal så har man dock större problem än PHP:s typning .