Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Klasa do ładowania plików
Forum PHP.pl > Forum > PHP
cojack
No więc tak, mam sobie klasę do ładowania plików, i gdy odpaliłem profilera php to zobaczyłem że dość duże obciążenie mam na tej klasie, bo z wszystkich wywołań metod wszystkie wywołania a jest ich 33 z tego co dobrze widzę, klasyfikują na 2 miejscu w stopniu najbardziej obciążającym system, no i szczerzę powiedziawszy to nie mam bladego pojęcia co jest tak mega super złego w tej klasie że takie obciążenie powoduje, oto kod:

  1. <?php
  2. /**
  3.  * BDT_Loader klasa odpowiedzialna za ładowanie plików
  4.  *
  5.  * @author Przemysław Czekaj <przemyslaw.czekaj@aichra.pl>
  6.  * @version 0.1
  7.  * @since 03.29.2010
  8.  * @package BDT
  9.  * @subpackage lib
  10.  * @charset utf8
  11.  **/
  12.  
  13. /**
  14.  * @author cojack
  15.  * @TODO Błędy w postaci GETTEXT
  16.  */
  17. class BDT_Loader {
  18.  
  19. private static $_path;
  20.  
  21. /**
  22.   * Konstruktor klasy
  23.   *
  24.   * @return void
  25.   */
  26. public static function initialize() {
  27. chdir( './..' );
  28. self::$_path = getcwd() . '/';
  29. }
  30.  
  31. /**
  32.   * Metoda przyjmuje argument jako tablice iteracyjną
  33.   * Adres do pliku powinien zaczynać się od ./ i uwzględnić
  34.   * zmianę ścieżki podczas wczytywania danych.
  35.   * Zachowywujemy się tak jakbyśmy byli w / tego fw.
  36.   *
  37.   * @access public
  38.   * @param array $files Tablica w postaci array ('plik', 'plik', 'plik');
  39.   * @return void
  40.   */
  41. public static function loadFile( $files = array() ) {
  42. foreach( $files as $file ) {
  43. if( self::_checkFile( $file ) ) {
  44. require_once( self::$_path.$file );
  45. }
  46. }
  47. }
  48.  
  49. /**
  50.   * Metoda wczytuje pojedyncze pliki XML
  51.   *
  52.   * @access public
  53.   * @param string $files Względna ścieżka do pliku
  54.   * @return object Xml object
  55.   */
  56. public function loadFileXML( $file ) {
  57. if( self::_checkFile( $file ) ) {
  58. libxml_use_internal_errors( TRUE );
  59. $sxe = new SimpleXMLElement( self::$_path.$file, NULL, TRUE );
  60. if( $sxe === FALSE ) {
  61. $errorMsg = 'Błąd w pliku XML: ' . $file . "<br />";
  62. $errorMsg .= 'Ścieżka: ' . self::$_path.$file . "<br />";
  63. foreach( libxml_get_errors() as $error ) {
  64. $errorMsg .= "<p>" . $error->message . "</p><br />";
  65. }
  66. trigger_error( $errorMsg , E_USER_WARNING );
  67. }
  68. }
  69. return $sxe;
  70. }
  71.  
  72. /**
  73.   * Metoda sprawdza czy dany plik jest możliwy do wczytania
  74.   *
  75.   * @param string $file Względna ścieżka do pliku
  76.   * @return boolean true, false
  77.   */
  78. private static function _checkFile( $file ) {
  79. if( is_file( self::$_path.$file ) ) {
  80. if( is_readable( self::$_path.$file ) ) {
  81. return TRUE;
  82. } else {
  83. trigger_error( 'Plik: '. $file . ' jest nie dla odczytu, sprawdź uprawnienia.' , E_USER_WARNING );
  84. }
  85. } else {
  86. trigger_error( 'Zła ścieżka i/lub nazwa dla pliku: '. $file . "\n" . 'Ścieżka: ' . self::$_path . $file , E_USER_WARNING );
  87. }
  88. return FALSE;
  89. }
  90. }


Gdyby ktoś mógł mi wskazać jakieś wskazówki by poprawić wydajność tej klasy, będę wdzięczny, pomysły z autoload zachować dla siebie, od razu mówię nie, nie i jeszcze raz nie, także bez nich.
wookieb
1) Nie latwiej zdefiniować nawet stałej która przechowuje ci getcwd() i dawać ją w potrzebnych miejscach? (że nie wspomnę o używaniu include_path)
  1. require_once ACTUAL_CWD.'plik.php';

Bo to co teraz masz nie ma sensu
2) Nie rozumiem, dlaczego pakujesz tutaj ładowanie xml
3) Korzystamy z wyjątków a nie trigger_errorów.
4) Klasa loadera wcale nie jest tak wolna. Czasem narzut pracy wynika po prostu z konieczności sparsowania , wykonania załadowanych plików przez php.
5)
Kod
   * @return  boolean  true, false

Facepalm :/
6)
Kod
* @return  object    Xml object

Typujemy silnie. Czyli nie "object" ale "SimpleXMLElement". Ma to ogromne znaczenie dla wszelkiego rodzaju IDE.
7) Nadal nie widzę sensu korzystania z metody _checkFile. Szczerze mówiąc przyda się tylko w środowisku developerskim gdzie sam na podstawie wywalonego przez php błędu będziesz wiedział co zrobić.
cojack
ad 1) nie używam stałych (DEFINE) w całym FW, taka filozofia
ad 2) bo to jest klasa do ładowania plików?
ad 3) odp jest w ad 6.
ad 4) Może i w tym właśnie tkwi problem, że musi php sparsować pozostałe pliki, przyjże się wywołaniom lepiej i spróbuję to jakoś odgadnąć.
ad 5) haha.gif
ad 6) Bez sensu byłoby stosowanie tutaj wyjątków, ponieważ kod byłby tak naszpikowany try {} catch {} że to się mija z celem, a błędy i tak gromadzę i pakuje w jedno miejsce, i tak czy siak używam trigger_error. A poza tym używam tej klasy też poza ciałem klas, także wyjątki w tym miejscu nie mają prawa bytu, gdyż ładuje w ten sposób klasy z których dana kasa dziedziczy, no i pomyśl sobie jakby to wyglądało.
ad 7) Z tym się zgodzę, będę miał to na uwadze, na pewno wprowadzę opcje sprawdzania typu środowiska przed wywołaniem _checkFile.
Pilsener
A ta klasa to ma być upload, parsowanie, jakiś model danych, czemu ma to służyć? Może efekt ma być diametralnie inny i nie ma sensu analizować kodu tej klasy, nie wiem na przykład, czy problem dotyczy includowania zwykłych plików czy xml? Jeśli to ma być jakiś autoloader to może warto przyjrzeć się, jak rozwiązano to w popularnych frameworkach?
erix
require_once jest wolniejsze od require, z tego co pamiętam. winksmiley.jpg

Poza tym, dlaczego dokonujesz tylu sprawdzań na poziomie systemu plików? Samo is_readable wystarczy, po co is_file?

Cytat
ad 1) nie używam stałych (DEFINE) w całym FW, taka filozofia

Hmm, filozofia w stylu kobiecej bo tak!, czy jakaś konkretna argumentacja? Nie wyobrażam sobie większego systemu bez choćby jednej stałej określającej katalog roboczy aplikacji.

Cytat
ad 2) bo to jest klasa do ładowania plików?

No niech Ci będzie, ale też tego nie rozumiem.

Cytat
ad 6) Bez sensu byłoby stosowanie tutaj wyjątków, ponieważ kod byłby tak naszpikowany try {} catch {} że to się mija z celem, a błędy i tak gromadzę i pakuje w jedno miejsce, i tak czy siak używam trigger_error. A poza tym używam tej klasy też poza ciałem klas, także wyjątki w tym miejscu nie mają prawa bytu, gdyż ładuje w ten sposób klasy z których dana kasa dziedziczy, no i pomyśl sobie jakby to wyglądało.

Takie myślenie jest bez sensu. Są wyjątki, zwiększają czytelność kodu i usprawniają wiele rzeczy, np:

  1. private static function _checkFile( $file ) {
  2. if( is_file( self::$_path.$file ) ) {
  3. if( is_readable( self::$_path.$file ) ) {
  4. return TRUE;
  5. } else {
  6. trigger_error( 'Plik: '. $file . ' jest nie dla odczytu, sprawdź uprawnienia.' , E_USER_WARNING );
  7. }
  8. } else {
  9. trigger_error( 'Zła ścieżka i/lub nazwa dla pliku: '. $file . "\n" . 'Ścieżka: ' . self::$_path . $file , E_USER_WARNING );
  10. }
  11. return FALSE;
  12. }


Na co tu tyle? Zrób sobie error_handler, który pluje wyjątkiem przy E_WARNING. PHP nie jest wielowątkowe, więc przy samym require_once wypluje wyjątek w dokładnie tym miejscu, co trzeba, a będziesz miał centralne zarządzanie odpowiednimi zdarzeniami.

Poza tym, żeby require_once wypluło błąd, to też musi sprawdzić, jaki on jest, czyli też sprawdza, czy plik da się odczytać, czy są do niego uprawnienia. Więc wywołanie is_readable jest - de facto - ponownym wykonaniem czegoś, co miało miejsce. Więc po co? winksmiley.jpg
cojack
No przeta mam error_handler ;] Ok, to już wiem co należy poprawić, dziękuwa erix, btw w calym FW nie ma ani jednej stałej zdefiniowanej przez DEFINE.

Cytat
require_once jest wolniejsze od require, z tego co pamiętam. winksmiley.jpg


może i jest wolniejsze, ale ja nie potrzebuje mieć 2 razy tego samego kodu w aplikacji.
erix
Cytat
No przeta mam error_handler ;]

Ale on nie wypluwa wyjątków i wszędzie musisz wykonywać 2x to samo. winksmiley.jpg

Cytat
może i jest wolniejsze, ale ja nie potrzebuje mieć 2 razy tego samego kodu w aplikacji.

A o cache'u, to słyszał? snitch.gif

Podpowiedź: static działa nie tylko w klasach. winksmiley.jpg
wookieb
Sorry erix ale mam NIE używać require_once bo require jest o 1 setną sekundą szybszy przy iluś tam wywołaniach i mam się męczyć o to, żeby kontrolować czy czasem nie dołączam tego samego pliku? No sorry.
erix
Cytat
żeby kontrolować czy czasem nie dołączam tego samego pliku?

Hmm, w swoich libach jestem świadomy, czy dołączyłem już, czy nie, ale to tak nawiasem. tongue.gif

I skoro już jesteśmy przy temacie:
  • autoloader ładuje w razie potrzeby
  • w funkcji ładującej możesz zrobić cache w zmiennej statycznej i sprawdzać via in_array. IMO będzie szybsze niż odwoływanie się do funkcji I/O
wookieb
Cytat(erix @ 24.08.2010, 10:04:26 ) *
  • w funkcji ładującej możesz zrobić cache w zmiennej statycznej i sprawdzać via in_array. IMO będzie szybsze niż odwoływanie się do funkcji I/O

Wiem, że w php-ie można spodziewać się różnych krzaków ale no sorry erix :/
  1.  
  2. class Loader
  3. {
  4. private static $loaded = array();
  5.  
  6. public static function load($file)
  7. {
  8. if (!in_array($file, self::$loaded))
  9. {
  10. require $file;
  11. self::$loaded[] = $file;
  12. }
  13. }
  14. }
  15.  
  16. $start = microtime(true);
  17. for ($i=0; $i<1000; $i++)
  18. {
  19. require_once 'test2.php';
  20. }
  21. echo microtime(true) - $start;
  22. echo '<br/>';
  23.  
  24. $start = microtime(true);
  25. for ($i=0; $i<1000; $i++)
  26. {
  27. Loader::load('test2.php');
  28. }
  29. echo microtime(true) - $start;
  30.  

Wynik
Kod
test0.0037469863891602
test0.050887823104858

Gdzie test jest zawartością pliku test2.php

Oczywiście, że require_once jest wolniejsze od require ale tylko przy pierwszym wywołaniu. I teraz pytanie. Czy warto się męczyć o zysk 1 setnej sekundy? I to w dodatku z ryzykiem wielu błędów (jeżeli nie korzystamy z autoloadera)? Nie sądzę.

Napomnę jeszczę taką kwestię iż nie zawsze korzystanie z funkcji autoloadera będzie ok.
Czasem w nagłówkach klas z łapy podaje się require_once i ścieżkę do pliku, żeby nie szukał jej autoloader.

Prosiłbym jeszcze kogoś o przetestowanie powyższego kodu z jakimś akceleratorem. Ponieważ APC mi nie działa na windowsie :/
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.