Autor | Zpráva | ||
---|---|---|---|
Suta Profil |
Jelikož začínám s postupným průzkumem OOP v php, rád bych se zeptal, zda-li je začlenění běžně používaných funkcí do třídy dobrý či špatný nápad. Konkrétněji:
Aktuálně používaný styl zápisu: function codeClean() {}; function imageResize() {}; // jiné běžně používané funkce Nahradit za: Class myCoreLibrary { public function codeClean() {}; public function imageResize() {}; // jiné běžně používané funkce } $myCoreLibrary = new myCoreLibrary(); $myCoreLibrary->codeClean(); Přemýšlím totiž nad přehledností mého současného kódu, kdy moje aktuální vlastní knihovna obsahuje několik set funkcí. Převod do objektového modelu by mi přinesl zpřehlednění např. v tom, že můžu jednotlivé metody rozčlenit podle jejich zaměření, tedy můžu vytvořit objekty (knihovny) typu: $myCoreLibrary = new myCoreLibrary(); $myMysqlLibrary = new myMysqlLibrary(); $myValidateLibrary = new myValidateLibrary(); $myValidateLibrary->validateEmail($email); Poznámka: uvedené názvy metod jsou vytvořeny pouze jako ukázka. Druhý dotaz pak směřuje k tomu, zda-li použití druhého způsobu (vytvoření instance a používání metod) je pro server náročnější, než-li definice a volání klasických funkcí. Problém totiž můžu vyřešit i prostým přejmenováním současných funkcí: function validateLibrary_validateEmail(); function imgLibrary_imageResize(); Předem díky za jakoukoliv radu, jak říkám, s OOP v php se začínám seznamovat. |
||
tiso Profil |
Začnem od konca:
1) s objektami je spojená väčšia réžia ako s obyčajnými funkciami. 2) existujú aj statické triedy, v niektorých prípadoch nepotrebuješ vytvárať nový objekt, stačí si zavolať konkrétnu metódu. 3) ďalšia hierarchická úroveň usporiadania je menný priestor (namespace) |
||
Suta Profil |
#3 · Zasláno: 21. 1. 2013, 18:11:31
Reaguji na tisa:
Můžeš prosím blíže popsat body 2 a 3? |
||
tiso Profil |
#4 · Zasláno: 21. 1. 2013, 18:45:27
2) http://php.net/manual/en/language.oop5.static.php, prípadne http://studium.vos-sps-jicin.cz/oop/index.php?page=staticke a http://www.pcforum.sk/staticke-metody-v-oop-a-kedy-ich-pouzit-vt105702.html
3) http://php.net/manual/en/language.namespaces.php, prípadne http://www.mirin.cz/blog/jmenne-prostory-namespaces-v-php-53 |
||
Suta Profil |
#5 · Zasláno: 21. 1. 2013, 19:47:40
Díky, prostuduju.
|
||
Suta Profil |
Reaguji na tisa:
Všechny odkazy jsem pečlivě prostudoval, díky informaci o statických metodách jsem se dostal i na jiné tutoriály. Používání metod zapouzdřených v třídě bez vytváření instance je skvělá věc. Co mi však ještě chybí je vytváření dalších "podobjektů". Tedy něco na způsob: MyLibrary::Core::Log::logMessage(); Např. v javascriptu by struktura vypadala následovně: var MyLibrary = { Core: { Log: { logMessage: function(message) { console.log(message); } }, nextCoreObjectOrMethod: function(){} } }; Podle dosud načerpaných znalostí nyní dokážu zavolat pouze statickou metodu třídy. class Commons { public static function createMysqlConnection() { } } class Log { public static function logMessage($message) { } } Commons::createMysqlConnection(); // vytvoří připojení k databázi Log::logMessage("Logovací zpráva"); // zaloguje zprávu Nevím však, jak docílit toho, když bych chtěl, aby třída Log byla dostupná pod třídou Commons, tedy volání: Commons::Log::logMessage(); Předem díky. |
||
martin1312 Profil |
#7 · Zasláno: 22. 1. 2013, 01:01:45
Suta:
neviem, či presne rozumiem tomu, čo chceš, ale skús si pozrieť dedičnosť. |
||
Suta Profil |
martin1312:
O dědičnosti vím, dosud jsme se však bavili o volání metod bez toho, aniž bych musel vytvářet instanci objektu. Jde tedy nějakým způsobem docílit volání Commons::Log::logMessage(); ?
|
||
tiso Profil |
#9 · Zasláno: 22. 1. 2013, 11:20:18
Suta: „Jde tedy nějakým způsobem docílit volání...“
Nejde, ani to v PHP nedáva zmysel. Ale podobným mechanizmom sú práve tie menné priestory (namespace). - Že voláš metódu zo svojho menného priestoru: \Commons\Log::logMessage(); |
||
Suta Profil |
Reaguji na tisa:
Chápu, díky za vysvětlení. Tady je jeden z prvních pokusů (převod do OOP). Pravděpodobně se nikomu nebude chtít kód studovat, za případné letmé proletění a varování před špatným stylem návrhu však budu vděčný. <?php session_start(); define("MYSQL_SERVER", "localhost"); // případně "192.168.1.3" define("MYSQL_USER", "root"); define("MYSQL_PASSWORD", ""); define("MYSQL_DB", "zav_system_3"); class Commons { private static $mysqlConnection; public static function createMysqlConnection() { /** * vytvoří připojení k databázi */ // připojení k serveru self::$mysqlConnection = @mysql_connect(MYSQL_SERVER,MYSQL_USER,MYSQL_PASSWORD); // výběr databáze mysql_select_db(MYSQL_DB); // nastavení kódování if(function_exists("mysql_set_charset")) mysql_set_charset("utf8"); else mysql_query("SET NAMES utf8"); // nastavení časového pásma pro MySQL server mysql_query("SET time_zone = 'Europe/Bratislava'"); } public static function getMysqlConnection() { // vrátí spojení s databází return self::$mysqlConnection; } public static function getMysqlConnectionStatus() { // informace, zda bylo spojení vytvořeno úspěšně return self::$mysqlConnection === FALSE ? "error" : "success"; } public static function destroyAllSessions() { /** * Odstraní veškeré session relace */ // Initialize the session. // If you are using session_name("something"), don't forget it now! if(!isset($_SESSION)) { session_start(); } // Unset all of the session variables. $_SESSION = array(); // If it's desired to kill the session, also delete the session cookie. // Note: This will destroy the session, and not just the session data! if (ini_get("session.use_cookies")) { $params = session_get_cookie_params(); setcookie(session_name(), '', time() - 42000, $params["path"], $params["domain"], $params["secure"], $params["httponly"] ); } // Finally, destroy the session. session_destroy(); } public static function get_GET($string) { // http://blog.zdenekvecera.cz/item/jak-na-to-sql-injection-magic_quotes_gpc-addslashes-a-stripslashes /** * Metoda ošetří data předaná metodou GET */ $string = isset($_GET[$string]) ? $_GET[$string] : ""; if($string === 0) return 0; return self::codeClean($string); } public static function get_POST($string) { // http://blog.zdenekvecera.cz/item/jak-na-to-sql-injection-magic_quotes_gpc-addslashes-a-stripslashes /** * Metoda ošetří data předaná metodou POST */ $string = isset($_POST[$string]) ? $_POST[$string] : ""; if($string === 0) return 0; return self::codeClean($string); } public static function get_POST_DATA($string) { // http://blog.zdenekvecera.cz/item/jak-na-to-sql-injection-magic_quotes_gpc-addslashes-a-stripslashes /** * Metoda ošetří data předaná metodou POST a již uložená v objektu $POST_DATA */ global $Zav; $string = isset($Zav->POST_DATA->{$string}) ? $Zav->POST_DATA->{$string} : ""; if($string === 0) return 0; return self::codeClean($string); } public static function codeClean($var) { if($var == "") return ""; if (is_array($var)) { foreach($var as $key => $val) { $output[$key] = self::codeClean($val); } } else { //$var = strip_tags(trim($var)); $var = trim($var); if (get_magic_quotes_gpc()) { $var = stripslashes($var); } $var = mysql_real_escape_string($var); } return $var; } public static function getMicrotime() { return round(microtime(true) * 1000); } } class Log{ private static $info = array(); public static function reset() { self::$info = array(); } public static function logMessage($string) { self::$info[] = Commons::getMicrotime() . ": " . $string; } public static function getLogResult() { return self::$info; } } class Zav { public $ACTION_TYPE; public $POST_DATA; public $RETURN_ARRAY = array(); public $NOW; public $isConnectionSuccessful = false; public $rootPath; // cesta ke kořenu webu public $isServer; // informace o umístění stránek (server vs localhost public function __construct() { //server //$jsonStrData = isset($_POST['data']) ? stripslashes ($_POST['data']) : ""; //localhost // viz POST data a metoda json_decode $jsonStrData = isset($_POST['data']) ? $_POST['data'] : ""; $this->ACTION_TYPE = Commons::get_POST("actionType"); $this->POST_DATA = json_decode($jsonStrData); // převod na objekt, uvedením true v druhém parametru by proběhl převod na array $this->RETURN_ARRAY["data"] = array(); $this->NOW = strtotime("now"); // cesta ke kořenu webu $this->rootPath = $_SERVER['DOCUMENT_ROOT']; // C:/wamp/www/ // informace o umístění stránek (server vs localhost) $this->isServer = strtoupper(SubStr($this->rootPath,0,3)) == "C:/" ? false : true; } } Commons::createMysqlConnection(); $Zav = new Zav(); Log::logMessage("Připojení k databázi - " . Commons::getMysqlConnectionStatus()); $Zav->RETURN_ARRAY["textStatus"] = "testStatus"; $Zav->RETURN_ARRAY["data"]["postData"] = $Zav->POST_DATA; $Zav->RETURN_ARRAY["data"]["log"] = Log::getLogResult(); echo json_encode($Zav->RETURN_ARRAY); exit(); ?> |
||
span Profil * |
#11 · Zasláno: 23. 1. 2013, 15:47:55
Je zbytočne vytvárať objekt ak z neho bude nakoniec použitá len jedna metóda.
K metódam tried sa dá pristupovať ako k statickým aj v obyčajných triedach nie len v statických (scope operator ::). Hierarchicky sa dajú písať aj rozšírenia tried (extends). A veľmi užitočné metódy sú konstruktory (_costruct()). Konštanty sa dajú deklarovať aj v rámci tried (const RESET = 'Reset';). |
||
Someone Profil |
#12 · Zasláno: 23. 1. 2013, 15:52:52
Suta:
Když už je to celé takto objektově, tak proč místo mysql_* nepoužiješ objekt Mysqli ? mysql_* funkce jsou od PHP 5.5.0 DEPRECATED a budou se odstraňovat. |
||
Tori Profil |
#13 · Zasláno: 23. 1. 2013, 16:40:04
Nejvíc mi padlo do oka:
Nelíbí se mi zabalení všeho do jedné třídy jen proto, abyste to mohl volat přes tuto třídu - na tohle jsou vhodnější jmenné prostory: Zav\Common\Log::logMessage('text'); Uvedený příklad v JS by se tak zapsal hlavně proto, že v JavaScriptu jmenné prostory neexistují (pokud vím) a simulují se například tímhle způsobem. V PHP ale takový návrh porušuje princip jediné zodpovědnosti (tj. že třída / metoda / funkce se stará plně a výhradně o jednu věc). U vás se to projevilo např. vzájemně nesouvisejícími metodami třídy Common , anebo používání databázového escapování řetězce v kontextu čtení dat z $_POST .
S tím souvisí další důležitá věc v OOP - zapouzdření. Metody by měly být takové černé skříňky; možná dostanou nějaký vstup, něco udělají (okolní kód nepotřebuje vědět jakým způsobem) a možná vrátí nějakou hodnotu. Rozhodně by neměly spoléhat na existenci nějakých globálních proměnných (používání global ) nebo konstant (mysql_connect - přihlašovací údaje předávejte databázové třídě transparentně, např. jako parametr konstruktoru).
|
||
Suta Profil |
Reaguji na uživatele span:
„Hierarchicky sa dajú písať aj rozšírenia tried (extends).“ I v těch třídách, které nebudu používat pro vytváření instancí, ale budu pouze volat jejich metody? Reaguji na uživatele Someone: „Když už je to celé takto objektově, tak proč místo mysql_* nepoužiješ objekt Mysqli ?“ Ano, to je pravda, přechod na mysqli plánuji. Reaguji na uživatele Tori: „na tohle jsou vhodnější jmenné prostory: Zav\Common\Log::logMessage('text');“ Když já jsem jmenným prostorům předevčírem věnoval několik hodin, a vůbec se mi nepodařilo tuto funkcionalitu rozchodit. Můžeš mi, prosím, napsat úplně jednoduchý příklad definice tříd a definici jmenného(jmenných) prostorů tak, abych docílit např. tebou popsaného volání? ( Zav\Common\Log::logMessage('text'); )
„Rozhodně by neměly spoléhat na existenci nějakých globálních proměnných (používání global) nebo konstant (mysql_connect - přihlašovací údaje předávejte databázové třídě transparentně, např. jako parametr konstruktoru).“ S první částí souhlasím (v kódu skutečně používám v metodě základní třídy globální proměnnou, přepracuji..). S tou druhou si však již nejsem jistý.. Vždy jsem byl zvyklý definovat připojení k databázi zcela nahoře v kódu, při použití na jiném projektu vždy stačilo přepsat jinými údaji.. Opravdu si myslíš, že je chybné používat pro připojení k databázi údaje uložené pomocí define? Jde mi totiž i o to, že bych byl rád, kdyby třída Commons obsahovala základní důležité funkce (metody) a proměnné (vlastnosti). A např. spojení s databází nebudu vytvářet vždy.. Pouze tehdy, když budu vědět, že budu později s databází pracovat.. Tudíž např. volání Commons:createMysqlConnection() uskutečním právě pouze v této situaci. A abych nemusel zadávat jako parametry údaje pro připojení k databázi, použije si je třída sama z definované konstanty.
|
||
span Profil * |
#15 · Zasláno: 23. 1. 2013, 23:31:32
[#14] Suta
<?php class Common { const VERSION = '0.0.1'; public $MemoryLimit = 1048576; public $PostMaxSize = 1048576; public $UploadMaxFilesize = 1048576; public function __construct() { $this->UploadMaxFilesize = (integer)ini_get('upload_max_filesize')*1048576; $this->PostMaxSize = (integer)ini_get('post_max_size')*1048576; $this->MemoryLimit = (integer)ini_get('memory_limit')*1048576; } } class Ui extends Common { const VERSION = '0.0.1'; public function __construct() { Common::__construct(); Ui::Render(); } public function Render() { ob_start(); print ' Upload Max Filesize: '.$this->UploadMaxFilesize; print ' Post Max Size: '.$this->PostMaxSize; print ' Memory Limit: '.$this->MemoryLimit; print ' Common version: '.Common::VERSION; print ' Ui version: '.Ui::VERSION; ob_end_flush(); } } $Ui = new Ui(); ?> |
||
Tori Profil |
Suta:
„Opravdu si myslíš, že je chybné používat pro připojení k databázi údaje uložené pomocí define?“ Jak je ukládáte, to je (v tomhle kontextu) jedno. Šlo mi o odstranění závislosti: pokud změníte způsob ukládání přístupových údajů, musíte v [#10] změnit i DB třídu - to není dobré. Třída má být nezávislá na svém okolí. Porovnejte tyhle dva případy: class DB { public static function createMysqlConnection() { self::$mysqlConnection = @mysql_connect(MYSQL_SERVER,MYSQL_USER,MYSQL_PASSWORD); mysql_select_db(MYSQL_DB); if(function_exists("mysql_set_charset")) mysql_set_charset("utf8"); else mysql_query("SET NAMES utf8"); mysql_query("SET time_zone = 'Europe/Bratislava'"); } } class DB { protected $options = array(); protected $connection; public function __construct(array $opts) { // nějaké výchozí hodnoty: if (!isset($opts['charset'])) $opts['charset'] = 'utf8'; $this->options = $opts; } protected function connect() { $this->connection = mysql_connect($this->options['server'], $this->options['user'], $this->options['password']); if ($this->connection === false) throw new RuntimeException("Nelze připojit k databázi. Popis chyby: " . mysql_error()); // U ostatních volání mysql_* by taky bylo ošetření chyb. Teď ho ale vynechávám, zjednodušený příklad stačí. mysql_select_db($this->options['database']); if (function_exists("mysql_set_charset")) mysql_set_charset($this->options['charset']); else mysql_query("SET NAMES ".$this->options['charset']); mysql_query("SET time_zone = '" . $this->options['timezone'] . "'"); } } „A např. spojení s databází nebudu vytvářet vždy.. Pouze tehdy, když budu vědět, že budu později s databází pracovat“ Další možnost je nechat to úplně na třídě DB. Připojení se vytvoří automaticky při prvním volání metody query: // ... pokračování předchozího příkladu public function query($sql) { if (!$this->connection) $this->connect(); // provedení SQL dotazu ... } |
||
Suta Profil |
Reaguji na uživatele Tori:
Ano, v obou případech máte pravdu (dobrá, budeme si vykat), vámi uvedený způsob je lepší. Z příkladu se jistě poučím a vlastní kód poupravím. V mém konkrétním případě jde však spíše o to, že knihovnu používám pouze já sám a nepředpokládám, že bych kdy považoval za nutné měnit zaběhlý způsob definování přístupových údajů přes define (používám stále stejně již několik let ve všech projektech, které jsem kdy dělal). |
||
Tori Profil |
„nepředpokládám, že bych kdy považoval za nutné měnit zaběhlý způsob definování přístupových údajů přes define“
Co když budeš někdy potřebovat pracovat se dvěma databázemi paralelně? Já jsem zvyklá vykat, ale nečekám to od ostatních; takže si klidně tykejme, jestli ti to víc vyhovuje. :) |
||
Suta Profil |
Reaguji na uživatele Tori:
Dobře. Můžu ještě poprosit o pomoc s definicí jmenných prostorů? (citace z [#14]) Suta: > „na tohle jsou vhodnější jmenné prostory: Zav\Common\Log::logMessage('text');“ > Když já jsem jmenným prostorům předevčírem věnoval několik hodin, a vůbec se mi nepodařilo tuto funkcionalitu rozchodit. Můžeš mi, prosím, napsat úplně jednoduchý příklad definice tříd a definici jmenného(jmenných) prostorů tak, abych docílit např. tebou popsaného volání? (Zav\Common\Log::logMessage('text');) |
||
Tori Profil |
Soubor Log.php:
<?php namespace Zav\Commons; class Log { public static function message() { } } Volání ve skriptu bez namespace: Zav\Commons\Log::message(); // nebo \Zav\Commons\Log::message(); Možnosti volání ve skriptu s namespace: <?php namespace NějakýNS; \Zav\Commons\Log::message(); // bez úvodního lomítka nefunguje, přeložilo by se na NějakýNS\Zav\Commons\Log Zav\Commons\Log::message(); <?php namespace NějakýJiný; use \Zav\Commons; Commons\Log::message(); <?php namespace NějakýJiný; use \Zav\Commons\Log; Log::message(); <?php namespace Zav\Commons; use \A\B\CokolivJiného; Log::message(); // tohle nebude fungovat, přeložilo by se na Zav\Commons\Commons\Log Commons\Log::message(); <?php namespace Zav\Commons; use Zav\Commons; Log::message(); // ale tady už to funguje, použije se "Commons" z části use Commons\Log::message(); <?php namespace NějakýJiný; use \Zav\Commons; use \Zav\Commons as Foo; // importovaný NS má alias Foo\Log::message(); // pokud není alias zadaný, použije se jako alias název poslední části Commons\Log::message(); Je to hodně podobné způsobu, jakým přistupuješ k souborům v jiných složkách, akorát nemůžeš použít adresáře . a .. , ale buď
a) adresovat "od kořene" (tj. s lomítkem na začátku), nebo b) pokud cílový NS je potomkem aktuálního, adresovat relativně k aktuálnímu NS c) importovat cílový NS - nejdřív napíšeš alias importovaného NS, pak relativní cestu ke třídě U všech příkladů použití chybí nějaké require './Log.php'; , předpokládala jsem autoloading.
U těch importů záleží na tom, jaké další třídy a odkud budeš v tom skriptu potřebovat (myslím tím rozdíl v tom, jestli použiješ use \Zav\Commons nebo use \Zav\Commons\Log nebo obojí). Odkazuju na zajímavé vlákno na fóru Nette.
|
||
Suta Profil |
Tori:
Díky moc, hodně jsi pomohla. |
||
Časová prodleva: 11 let
|
0