WN

WN (https://www.wn.se/forum/index.php)
-   Serversidans teknologier (https://www.wn.se/forum/forumdisplay.php?f=4)
-   -   PHP: require, dela upp funktioner? (https://www.wn.se/forum/showthread.php?t=1054925)

youheardit 2012-09-11 11:19

PHP: require, dela upp funktioner?
 
Hejsan, hade svårt att sätta rubrik så ursäkta det dåliga valet.

Har lite funderingar gällande import av filer i php (require, include osv).

Jag brukar bygga en "core" fil där jag har alla funktioner och klasser. Denna filen importeras i index.php oftast.

Men vore det inte bättre om man gjorde en fil/funktion eller klass? så man slipper importera onödiga funktioner som inte kommer till användning alltid.

Exempel: (denna filen ska skicka ett email genom en egengjord funktion)
PHP-kod:

require("send_mail.php"); //filen med send_mail(); funktionen och INGET annat!
send_mail("[email protected]"); 

istället för:
PHP-kod:

require("core.php"); //filen med ALLA funktioner till hemsidan
send_mail("[email protected]"); 

Tacksam för lite tips och tricks :) ha det!

Clarence 2012-09-11 11:43

Mitt första tips är att titta på OOP. Chansen att send_mail()-funktionen går att återanvända till ditt nästa projekt utan att skriva om den och få underhålla den i två projekt är liten om den ser ut som nu. Nästa gång vill du ha andra headers, gå direkt mot smtp osv. Sen 5 projekt in märker du att din mail inte alltid kommer in, så du vill logga alla mail som skickas. Då har du 5 ställen att gå in och ändra på. Detta löser en bra OOP-struktur åt dig.

Och det är även det enkla svaret på din fråga. Använd autoloading och lägg dina funktioner i klasser. För en snabb övergång kan du skapa klasser som Mail och bara ha en statisk funktion sendMail() i den. Så fort du försöker anropa Mail::sendMail() så kommer filen med klassen Mail att laddas och du slipper både manuellt inkludera filerna innan anropet och du laddar mer än du behöver (efter du läst på om cohesion och coupling).

Normalt sett brukar man också döpa filerna och därmed klasserna på ett sätt som gör att katalogträdet blir lite lättare att hitta filerna i också, liksom du får förklarande klassnamn. T ex kan du använda _ för att dela in det som t ex Mail_Transport_Smtp som hämtas från include/Mail/Transport/Smtp.php. Eller så lägger du Smtp-klassen i namespacet Vendor-name/Mail/Transport och får ungefär samma sökväg. Titta gärna på standarden PSR-0 om du ska börja använda autoloading (https://github.com/php-fig/fig-stand...epted/PSR-0.md).

coredev 2012-09-11 13:47

Ett generellt tips är att du inte vill ha för många require / includes i PHP eftersom det faktiskt stjäl tid. Försök hålla nere antalet, men kapsla gärna samtidigt in liknande funktioner i olika enheter (filer kan vara rätt för dig denna gång, i ett nytt projekt bör du ta Clarences råd och kika på OOP).

Clarence 2012-09-11 17:30

Citat:

Ursprungligen postat av coredev (Inlägg 20449941)
Ett generellt tips är att du inte vill ha för många require / includes i PHP eftersom det faktiskt stjäl tid. Försök hålla nere antalet, men kapsla gärna samtidigt in liknande funktioner i olika enheter (filer kan vara rätt för dig denna gång, i ett nytt projekt bör du ta Clarences råd och kika på OOP).

Samtidigt som detta kan vara sant kan det också vara tvärtom beroende på hur stor del av de inkluderade filerna som _kanske_ behövs faktiskt behövs vid varje request.

However, utvecklartid är dyrare än några procentenheter prestanda. Så att strukturera koden så att den ger färre buggar, är lättare att underhålla och arbeta vidare med (läs high cohesion, loose coupling) är viktigare än hur filerna laddas.

Björn 2012-09-11 22:39

Citat:

Ursprungligen postat av Clarence (Inlägg 20449923)
Mitt första tips är att titta på OOP. Chansen att send_mail()-funktionen går att återanvända till ditt nästa projekt utan att skriva om den och få underhålla den i två projekt är liten om den ser ut som nu. Nästa gång vill du ha andra headers, gå direkt mot smtp osv. Sen 5 projekt in märker du att din mail inte alltid kommer in, så du vill logga alla mail som skickas. Då har du 5 ställen att gå in och ändra på. Detta löser en bra OOP-struktur åt dig.

Och det är även det enkla svaret på din fråga. Använd autoloading och lägg dina funktioner i klasser. För en snabb övergång kan du skapa klasser som Mail och bara ha en statisk funktion sendMail() i den. Så fort du försöker anropa Mail::sendMail() så kommer filen med klassen Mail att laddas och du slipper både manuellt inkludera filerna innan anropet och du laddar mer än du behöver (efter du läst på om cohesion och coupling).

Normalt sett brukar man också döpa filerna och därmed klasserna på ett sätt som gör att katalogträdet blir lite lättare att hitta filerna i också, liksom du får förklarande klassnamn. T ex kan du använda _ för att dela in det som t ex Mail_Transport_Smtp som hämtas från include/Mail/Transport/Smtp.php. Eller så lägger du Smtp-klassen i namespacet Vendor-name/Mail/Transport och får ungefär samma sökväg. Titta gärna på standarden PSR-0 om du ska börja använda autoloading (https://github.com/php-fig/fig-stand...epted/PSR-0.md).

Bra post, väl förklarat! Kan inte annat än hålla med.

Conny Westh 2012-09-11 23:34

Om man väl använder require() så använd då require_once() i stället så laddas filen bara en gång....

Men annars tycker jag du helt ska glömma allt vad imperativa programspråk heter och göra som Clarence skriver att omedelbart gå över till Objektorienterad programmering (dvs OOP).

tartareandesire 2012-09-12 11:57

Citat:

Ursprungligen postat av ConnyWesth (Inlägg 20449981)
Om man väl använder require() så använd då require_once() i stället så laddas filen bara en gång....

Är varje prestandaförbättring viktig så är require bättre.

Conny Westh 2012-09-13 01:22

Citat:

Ursprungligen postat av tartareandesire (Inlägg 20450017)
Är varje prestandaförbättring viktig så är require bättre.

Det är ytterst sällan som prestandaoptimering är första fokus i användarinteraktiva applikationer.

Mer än 90-99 procent av datorns tid står den och väntar på att användaren ska utföra något, så att optimera användargränssnitt för hastighet är i normalfallet mindre viktigt (lägre prioriterat), det som däremot är viktigt att fokusera på är:

1) enkelhet att använda
2) eliminera felkällor vid utveckling
3) underlätta uppdatering/underhåll
4) Underlätta drift/administration

Punkt 3 och 4 ovan står normalt för 5/6 av totala kostnaden under ett systems livslängd.

Först om man upplever problem med prestanda kan man lägga ner tid på att optimera för hastighet. Optast behövs det inte alls hastightsoptimeras vid denna typ av applikationer.

Det är viktigt att man har en plan för vad man lägger ner kostnaden på för ett system.

Conny Westh 2012-09-13 05:00

Jag har testat lite OOP med PHP och autoloader som jag tänkte dela med mig av:

Först min testklass:

Kod:

<?php
// File: TestAutoloader.php
// Author: Conny Westh 2012-09-13
  require_once('/lib/autoloader.php');


  try
  {
      print "Test av autoloader.php\n";
      $c1 = new MyClass1();
      $c2 = new MyClass2();
  }
  catch (Exception $e)
  {
      print $e->getMessage();
  }
?>


Kod:

<?php

  class MyClass1
  {
    public static function run() { print "MyClass1 Works just fine...\n"; }
  }

  $className = 'MyClass1';
  $className::run();

?>

Kod:

<?php

  class MyClass2
  {
    public static function run() { print "MyClass2 Works just fine...\n"; }
  }

  $className = 'MyClass2';
  $className::run();

?>




Autoloader-klassen kan man lägga i en underkatalog som lämpligtvid heter LIB:
Kod:

<?php
// File: autoloader.php
// Modified by Conny Westh 2012-09-13
 class autoloader
{
 
    public static $loader;
 
    public static function init()
    {
        if (self::$loader == NULL)
            self::$loader = new self();
 
        return self::$loader;
    }
 
    public function __construct()
    {
        spl_autoload_register(array($this,'approot'));
        spl_autoload_register(array($this,'model'));
        spl_autoload_register(array($this,'helper'));
        spl_autoload_register(array($this,'controller'));
        spl_autoload_register(array($this,'library'));
    }
 
    public function approot($class)
    {
        set_include_path(get_include_path().PATH_SEPARATOR.'/');
        spl_autoload_extensions('.php');
        spl_autoload($class);
    }
 
    public function library($class)
    {
        set_include_path(get_include_path().PATH_SEPARATOR.'/lib/');
        spl_autoload_extensions('.php');
        spl_autoload($class);
    }
 
    public function controller($class)
    {
        $class = preg_replace('/_controller$/ui','',$class);
       
        set_include_path(get_include_path().PATH_SEPARATOR.'/controller/');
        spl_autoload_extensions('.php');
        spl_autoload($class);
    }
 
    public function model($class)
    {
        $class = preg_replace('/_model$/ui','',$class);
       
        set_include_path(get_include_path().PATH_SEPARATOR.'/model/');
        spl_autoload_extensions('.php');
        spl_autoload($class);
    }
 
    public function helper($class)
    {
        $class = preg_replace('/_helper$/ui','',$class);
 
        set_include_path(get_include_path().PATH_SEPARATOR.'/helper/');
        spl_autoload_extensions('.php');
        spl_autoload($class);
    }
 
}
 
//call
 autoloader::init();
 ?>


Clarence 2012-09-13 08:44

Citat:

Ursprungligen postat av ConnyWesth (Inlägg 20450068)
Jag har testat lite OOP med PHP och autoloader som jag tänkte dela med mig av:

--- klippt ---

Jag skulle rekommendera dig att fundera på att följa standarder. Jag vet att PHP är rätt nytt för dig så du kanske inte känner till dom.

PSR-0 har blivit en väl vedertagen standard för autoloading. Den ger dig interopabilitet mellan annan välskriven kod. https://github.com/php-fig/fig-stand...epted/PSR-0.md

Vidare är en full include_path baserad autoloading långsam samt mer error-prone och svåröveriktlig då du faktiskt måste åt en server-variabel för att se hela dess innebörd. Projektet blir alltså beroende av någon utanför projektet i sig. De flesta välskrivna projekt som har använt detta tidigare har använt en classmap generator för att lättare kunna överblicka och få bättre prestanda.

En exempel-impementation av en bra skriven PSR-0 autoloader kan du se på https://gist.github.com/221634 . Den är fullt produktionsduglig och är skriven av några av de främsta PHP-profilerna (både Zends och Symfonys huvudutvecklare finns representerade t ex).

Angående din kommentar om require_once så blir den inte alls gällande när du har en väl skriven autoloader. Det finns då längre ingen anledning att köra _once-funktionerna då andra användandet av klassen inte kommer försöka inkludera filen igen. Så en _once ger dig då ENDAST dubbla stat calls helt utan fördelar.

coredev 2012-09-13 11:50

Citat:

Ursprungligen postat av ConnyWesth (Inlägg 20450062)
Det är ytterst sällan som prestandaoptimering är första fokus i användarinteraktiva applikationer. Mer än 90-99 procent av datorns tid står den och väntar på att användaren ska utföra något, så att optimera användargränssnitt för hastighet är i normalfallet mindre viktigt (lägre prioriterat)...

Nope.

Det stämmer säkert när det gäller vanliga windowsapplikationer, men detta är en webbapplikation och där har du helt andra krav. Tar något mer än 2 sekunder så kan du räkna med att användaren laddar om sidan ELLER tröttnar - båda är rätt dåliga scenarion. Du riskerar dessutom att bli straffade i SERP:en om din webbplats snurrar långsamt. Utvecklar du webbapplikationer skall du ha ett högt fokus på just prestanda.

tartareandesire 2012-09-13 13:12

Citat:

Ursprungligen postat av coredev (Inlägg 20450087)
Nope.

Det stämmer säkert när det gäller vanliga windowsapplikationer, men detta är en webbapplikation och där har du helt andra krav. Tar något mer än 2 sekunder så kan du räkna med att användaren laddar om sidan ELLER tröttnar - båda är rätt dåliga scenarion. Du riskerar dessutom att bli straffade i SERP:en om din webbplats snurrar långsamt. Utvecklar du webbapplikationer skall du ha ett högt fokus på just prestanda.

Exakt. Driften är dessutom också en kostnad man måste räkna med + att miljötänk även bör finnas med inom webbutveckling. Det här handlar ju inte alls om optimering i efterhand heller vilket gör att det inte tar någon extra tid om man har koll på vad man gör.

dAEk 2012-09-13 18:03

Istället för att argumentera fram och tillbaka - kan ni inte göra mätningar från ett av era projekt och lägga upp resultaten här? CPU time, pages/sec och kanske något till mätvärde som kan vara intressant för att mäta skillnaden.

Eftersom jag inte kan PHP blir det en vild gissning och det är att skillnaden förmodligen är försvinnande liten.

tartareandesire 2012-09-13 21:09

Citat:

Ursprungligen postat av dAEk (Inlägg 20450143)
Istället för att argumentera fram och tillbaka - kan ni inte göra mätningar från ett av era projekt och lägga upp resultaten här? CPU time, pages/sec och kanske något till mätvärde som kan vara intressant för att mäta skillnaden.

Eftersom jag inte kan PHP blir det en vild gissning och det är att skillnaden förmodligen är försvinnande liten.

Skillnaden är i de flesta fall försvinnande liten, ja, men varje optimeringsmöjlighet är för trafikintensiva webbplatser mycket viktig. Många bäckar små. Detta är som sagt var också en så pass simpel optimeringsmöjlighet att det borde gå per automatik.

Att det är en skillnad mellan normalvarianten och _once är självförklarande av funktionaliteten i sig. Det finns en hel del skrivet på nätet redan med diverse tester. Den faktiska skillnaden varierar ju dock en del från en webbplats till en annan i likhet med de flesta andra optimeringar man kan göra.

SimonP 2012-09-14 22:56

Appropå prestanda, i de flesta fall är ju OOP att föredra, men i de fall där man skall processa mkt data kan OOP ge en märkbar prestandaförsämring. För några år sedan gjorde jag tester mellan vanlig kod och OOP kod. I koden nedan krypteras en 1k-sträng 1k gånger, dvs totalt 1 megabyte data. OOP-varianten ger en enorm prestandaförsämring, flera hundra procent, testade även nu på en obelastad server med följande resultat:
Kod:

Non OOP Result: 1.6096091270447 seconds
OOP Result: 4.6408619880676 seconds

Kod:

<?php

// Create 1k string
$initString="";
while (strlen($initString)<1024) $initString .="abcdef0123456789";

/*
---------------------------------------------------
 Non-OOP test
---------------------------------------------------
*/
$time_start = microtime(true);
for ($i=0; $i<1024; $i++)
{
        Non_OOP_test($initString,false);
}
$time_end = microtime(true);
$time = ($time_end - $time_start);
echo "<pre>Non OOP Result: $time seconds\n";

// Verification
$encrypted        = Non_OOP_test($initString,true);
$decrypted        = Non_OOP_test($encrypted,true);
echo "Encrypted: $encrypted\n";
echo "Decrypted: $decrypted\n</pre>";

/*
---------------------------------------------------
 OOP test
---------------------------------------------------
*/
$time_start = microtime(true);
for ($i=0; $i<1024; $i++)
{
        OOP_test($initString,false);
}
$time_end = microtime(true);
$time = ($time_end - $time_start);
echo "<pre>OOP Result: $time seconds\n";

// Verification
$encrypted        = OOP_test($initString,true);
$decrypted        = OOP_test($encrypted,true);
echo "Encrypted: $encrypted\n";
echo "Decrypted: $decrypted\n</pre>";



function OOP_test($instr, $return)
{
        $arc4 = new ARC4();
        $arc4->init("pAsSwOrD");
        $arc4->process($instr,1024);
        if ($return) return $instr;
}


function Non_OOP_test($instr, $return)
{
        global $x, $y, $state, $statebackup;
        $x = $y = 0;
        $state = array();
        $statebackup =array();
        arc4_init("pAsSwOrD");
        arc4_process($instr,1024);
        if ($return) return $instr;
}

//-------------------------------------------------------------------------------
// ARC4 Encryption class in PHP
//--------------------------------------------------------------------------------
class ARC4
{
  private $state = array();
  private $statebackup = array ();
  private $x = 0;
  private $y = 0;

  function init($key)  // init key schedule
  {
      $len= strlen($key);
      for ($this->x = 0; $this->x < 256; $this->x++) $this->state[$this->x] = $this->x;

      $this->y = 0;
      for ($this->x = 0; $this->x < 256; $this->x++)
      {
        $this->y = ($this->y + $this->state[$this->x] + ord($key[$this->x % $len])) % 256;
        $temp = $this->state[$this->x];
        $this->state[$this->x] = $this->state[$this->y];
        $this->state[$this->y] = $temp;
      }
       
      // Skip the first 256 bytes to avoid some weakness
      $temp="";
      for ($this->x = 0; $this->x < 256; $this->x++) $temp[$this->x]=chr($this->x);
      $this->process($temp,256);

      $this->x = $this->y = 0;
      $this->statebackup=$this->state;
    }

  function process(&$instr, $len)  // encrypt/decrypt raw ascii
  {
      for ($c= 0; $c < $len; $c++)
      {
        $this->x = ($this->x + 1) % 256;
        $this->y = ($this->y + $this->state[$this->x]) % 256;
        $temp = $this->state[$this->x];
        $this->state[$this->x] = $this->state[$this->y];
        $this->state[$this->y] = $temp;
        $instr[$c] = chr(ord($instr[$c]) ^ $this->state[($this->state[$this->x] + $this->state[$this->y]) % 256]);
      }
  }
}   
//--------------------------------------------------------------------------------------------------

       
function arc4_init($key)  // init key schedule
{
        global $x, $y, $state, $statebackup;
       
        $len= strlen($key);
  for ($x = 0; $x < 256; $x++) $state[$x] = $x;
  $y = 0;
  for ($x = 0; $x < 256; $x++)
  {
      $y = ($y + $state[$x] + ord($key[$x % $len])) % 256;
      $temp = $state[$x];
      $state[$x] = $state[$y];
      $state[$y] = $temp;
  }
   
  // Skip the first 256 bytes to avoid some weakness
  $temp="";
  for ($x = 0; $x < 256; $x++) $temp[$x]=chr($x);
  arc4_process($temp,256);

  $x = $y = 0;
  $statebackup=$state;
}


function arc4_process(&$instr, $len)  // encrypt/decrypt raw ascii
{
        global $x,$y,$state;
               
  for ($c= 0; $c < $len; $c++)
  {
      $x = ($x + 1) % 256;
      $y = ($y + $state[$x]) % 256;
      $temp = $state[$x];
      $state[$x] = $state[$y];
      $state[$y] = $temp;
      $instr[$c] = chr(ord($instr[$c]) ^ $state[($state[$x] + $state[$y]) % 256]);
  }
}

?>


Conny Westh 2012-09-15 12:54

Jag testkörde SimonPs kod (på en betydligt långsammare maskin) och fick följande resultat:

Kod:

Non OOP Result: 2.6286079883575 seconds
OOP Result: 5.3388919830322 seconds

Jag noterar att Simons Ratio (ImperativeTime/OOPtime) blev 2,88 medan min blev 2,03, det har m a o ganska stor betydelse vilken maskin man kör på.

Sen har det alltid varit så att OOP körningar tar något längre tid eftersom det är mer att göra för systemet. Det är inte av prestandaskäl man väljer OOP i stället för imperativa språk, det är för att kunna hantera komplexitet på ett bättre sätt och minska risken för buggar, återanvändbarhet av tidigare kod, samt därmed kortare utvecklingstid. Maskiner blir ständigt snabbare så hastigheten är inte det avgörande i de flesta fall. PHP ger i sig prestandaförluster i och med att det dessutom är interpreterande och inte kompilerande. Är hastigheten en kritisk faktor så väljer man ett annat språk.

SimonP 2012-09-15 14:10

Citat:

Ursprungligen postat av ConnyWesth (Inlägg 20450305)
Sen har det alltid varit så att OOP körningar tar något längre tid eftersom det är mer att göra för systemet. Det är inte av prestandaskäl man väljer OOP i stället för imperativa språk, det är för att kunna hantera komplexitet på ett bättre sätt och minska risken för buggar, återanvändbarhet av tidigare kod, samt därmed kortare utvecklingstid. Maskiner blir ständigt snabbare så hastigheten är inte det avgörande i de flesta fall. PHP ger i sig prestandaförluster i och med att det dessutom är interpreterande och inte kompilerande. Är hastigheten en kritisk faktor så väljer man ett annat språk.

Jag ville mest belysa prestandaproblem som OOP ger i vissa undantag. Jag har upplevt samma sak i kompilerande språk, t.ex C/C++, dvs. att OOP ger en försämring i prestanda vid intensiva anrop, inte lika mkt som PHP men ändå märkbar.

danjel 2012-09-16 18:53

Citat:

Ursprungligen postat av ConnyWesth (Inlägg 20450305)
.... PHP ger i sig prestandaförluster i och med att det dessutom är interpreterande och inte kompilerande. Är hastigheten en kritisk faktor så väljer man ett annat språk.

Nja, det stämmer inte. De flesta trafikintensiva använder någon slags bytecode cache för prestanda.


Vad gäller detta test, intressant vore se vilken skillnad det blir om man använder statisk metoder istället för instansiering. Förmodligen lite mindre minnesutnyttjande, vilket ju blir lite mer "rättvist" eftersom oop gör lite mer än funktionella koden om den instansierar

Conny Westh 2012-09-16 23:24

Citat:

Ursprungligen postat av danjel (Inlägg 20450436)
Nja, det stämmer inte. De flesta trafikintensiva använder någon slags bytecode cache för prestanda.


Vad gäller detta test, intressant vore se vilken skillnad det blir om man använder statisk metoder istället för instansiering. Förmodligen lite mindre minnesutnyttjande, vilket ju blir lite mer "rättvist" eftersom oop gör lite mer än funktionella koden om den instansierar

Nej, bytecode cache (som ju sker i runtime eller är det JIT Compilation du menar) kan aldrig mäta sig med kompilerad kod (som ju förkompilerar och optimerar koden).

Stark typning kan i många programspråk ge 50-100 gånger prestandaförbättring även i en interpreterande eller semikompilerad miljö.

Dock helt riktigt att statiska metoder ger betydande prestandaförbättring i OOP-sammanhang jämfört med instansiering, men det beror på vilket problem man ska lösa om man kan använda det eller inte.

danjel 2012-09-18 09:57

Med bytecode caches så kompileras och parsas koden endast vid första request. Så den overhead som JIT kompilering ger per default i PHP kan alltså undvikas.

Jag invände mot att du sa : om hastighet är en kritisk faktor så väljer man ett annat språk. Om man gör extremt stora beräkningar m.m i kod så är det klokt att inte använda php, t.ex motsvarande detta performance test i denna tråd. Där skulle asp.net eller cgi/c++ "vinna".
Det jag menar är att generellt väljer man inte bort php pga sämre prestanda i webblösningar.

Clarence 2012-09-18 11:03

Citat:

Ursprungligen postat av danjel (Inlägg 20450662)
Med bytecode caches så kompileras och parsas koden endast vid första request. Så den overhead som JIT kompilering ger per default i PHP kan alltså undvikas.

Jag invände mot att du sa : om hastighet är en kritisk faktor så väljer man ett annat språk. Om man gör extremt stora beräkningar m.m i kod så är det klokt att inte använda php, t.ex motsvarande detta performance test i denna tråd. Där skulle asp.net eller cgi/c++ "vinna".
Det jag menar är att generellt väljer man inte bort php pga sämre prestanda i webblösningar.

Med bytecode caches sparas en cachad bytecode, det är inte maskinkod. När man pratar om JIT-kompilation så brukar det vara precis samma situation, men resultatet är maskinkod istället för bytecode. Därför diskuteras det och utvecklas det JIT kompilatorer för PHP, för att det skulle ge en relevant prestandavinst.

Annars håller jag med dig om att man generellt sätt inte väljer bort PHP av prestandaskäl. Förutom vid väldigt speciella applikations-typer, t ex en chat eller annan websockets-hantering - och då är inte anledningen exekveringstiden.


Alla tider är GMT +2. Klockan är nu 06:34.

Programvara från: vBulletin® Version 3.8.2
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Svensk översättning av: Anders Pettersson