Kirjautuminen

Haku

Tehtävät

Keskustelu: Nettisivujen teko: Muuttujien aktivointi merkkijonosta

Sivun loppuun

pistemies [02.04.2008 15:38:34]

#

Minulla on tällainen array.

Array
(
    [server] => $this->_tpl_vars['serv']
    [username] => $this->_tpl_vars['usr']
    [password] => $this->_tpl_vars['pass']
    [database] => $this->_tpl_vars['db']
)

Miten saan nuo muuttujinen arvoista _tpl_vars arrayn tulostumaan muuna kuin tuollaisena merkkijonona?
Eval herjaa tuota merkkijonoa.

pistemies [02.04.2008 16:42:12]

#

Sain tämän tällätavoin "epävirallisesti":

        $serv = str_replace('$this->_tpl_vars[\'','',$server);
        $serv = str_replace('\']','',$serv);
        $usr =  str_replace('$this->_tpl_vars[\'','',$user);
        $usr = str_replace('\']','',$usr);
        $pss = str_replace('$this->_tpl_vars[\'','',$pass);
        $pss = str_replace('\']','',$pss);
        $db = str_replace('$this->_tpl_vars[\'','',$dbase);
        $db = str_replace('\']','',$db);
// tulostus
echo $this->_tpl_vars[$serv];

Jos jollakin on selkeämpi ratkaisu, niin se on tervetullut.

Niko [02.04.2008 17:51:47]

#

mikset suoraan vain

$db = $this->_tpl_vars['db']

jne.?

eval vois olla hyödyllinen

Short Php [02.04.2008 18:51:55]

#

Tai..

$serv = preg_replace('/\$this->_tpl_vars[\'(.*?)\\']/i', "$1", $jtn);
$usr = preg_replace('/\$this->_tpl_vars[\'(.*?)\\']/i', "$1", $jtn);
$pss = preg_replace('/\$this->_tpl_vars[\'(.*?)\\']/i', "$1", $jtn);
$db = preg_replace('/\$this->_tpl_vars[\'(.*?)\\']/i', "$1", $jtn);

Metabolix [02.04.2008 18:54:21]

#

Jos olet varma muuttujien sisällöstä, voit tosiaan käyttää eval-funktiota:

<?php
$s = array('moi' => '$juttu->muuttuja["jotain"]');
$s = eval('return "{'.$s['moi'].'}";');
echo $s;
?>

Tämä on kuitenkin vaarallista, jos käyttäjä pääsee syöttämään dataa, joten harkitse tarkoin.

pistemies [02.04.2008 19:28:46]

#

Niko kirjoitti:

mikset suoraan vain

$db = $this->_tpl_vars['db']

jne.?

eval vois olla hyödyllinen

Juttu on sellainen, että tuolla ekaviestin arraylla otetaan selville muuttujan nimi, joka voi vapaasti vaihdella käyttäjän mukaan. Kyse on lisäominaisuuden tekemisestä smarty-luokkaan.

Tähän liittyvä juttu on tällä hetkellä tämän näköinen:
http://www.pm-netti.com/index.php?id=scriptit&koodi=38

Kuten mainitsin, eval ei huoli tätä merkkijonoa:

$this->_tpl_vars['serv']

ps. ShortPhp:n koodi herjaa ] -merkkiä.

tsuriga [02.04.2008 21:57:03]

#

Itsellä ainakin heräsi semmoinen kysymys, että miksi templatessa tarvitsisi yhdistää tietokantaan? Eikös templaten tarkoitus esittää jo valmiiksi muodostettu data, ei niinkään käsitellä dataa.

pistemies [02.04.2008 23:11:02]

#

Ehkä tämä yhdistämishomma on tarpeeton.
Esitettävä data voi tulla myös mysql-tietokannasta. Enemmänkin tietokanta-kyselyn tulostaminen templateen olisi paikallaan,eikö?
Toisaalta sen saa jo nytkin, mutta ei suoraan, kun ensin tulostaa sen mysqlin sisällön taulukkoon ja käy läpi smartyn foreachilla.

tsuriga [03.04.2008 02:52:18]

#

Tietokantakyselyn tuloksien. Zend Frameworkilla väsäilen ja mulla on näkymissä esim. näin:

<ul>
<?php foreach ($this->messages as $message) :?>
 <li><?php echo $this->escape($message->content); ?></li>
<?php endforeach; ?>
</ul>

$this->messages on iteraattori, joka palauttelee rivejä tietokannasta. Eli templateissa kyllä sallitaan taulukon iterointi kun tarkoituksena on muotoilla haettu data, ei muokata sitä. Tavoitteena on siis pitää sisältö ja esitysasu erillään. Tarkoitit tuossa varmaan "tallentaa MySQL:n palauttaman sisällön taulukkomuuttujaan--".

Hieman aiheeseen liittyen, jouduin tuuppaamaan Tidyn toistaiseksi layout-templateen kun en vielä keksinyt, mistä muokata tuota $content-muuttujaa:

<?php
$config = array(
                 'indent'       => true,
                 'output-xhtml' => true
               );
$tidy = new Tidy();

$tidy->parseString( $this->layout()->content, $config, 'utf8' );
?>
  <?php echo $tidy; ?>

Ps. Ei muistu suomenkieliset vastikkeet mieleen ei millään, vaan eipä jaksa ottaa pulttia moisesta.

pistemies [03.04.2008 10:42:46]

#

tsuriga kirjoitti:

Tarkoitit tuossa varmaan "tallentaa MySQL:n palauttaman sisällön taulukkomuuttujaan--".

Joo, periaatteessa näin. Tulee vaan käytettyä sanaa "tulostaa" muuhunkin, kuin tulostaa ruudulle näkyviin. ( Käyttäväthän monet tekstinkäsittelyohjelmatkin tulostuksen yhteydessä mahdollisuuden valita toiminto "tulosta tiedostoon". )

Mutta kiitos vihjeistä. Olen vasta aloitellut tätä smartya, siinä on vielä paljon opittavaa.

Wizard [03.04.2008 20:22:24]

#

Katselin tuota Pekan MySQL oliota ja kiinnitin huomiota siihen, että tulee return jos salasana tai tietokannan nimi puuttuvat? Jos minulta kysytään, niin tuossa on ongelma, koska:

1) salasanaa ei aina tarvita, on useita tilanteita joissa IP:n perusteella tunnistetaan ja pelkkä käyttäjänimi silloin riittää
2) samaa yhteyttä voidaan käyttää useasti eli tietokannan nimeä voidaan vaihdella samalla yhteydellä

MySQLi yhteyksissä taas tietokannan nimi on PAKOLLINEN.

Tuo luokka on aika hyvä esimerkki siitä kuinka rakennetaan avaruusraketti yhdessä metodissa. Käytännössä tuo tietokannan valinta pitäisi tehdä toisessa metodissa ja tuo avattu yhteys pitäisi hyödyntää uudelleen private muuttujalla luokan sisällä jne. Lisäksi luokan sisäinen yhteyksien hallinta olisi muutenkin hyvä olla jolloin sillä voidaan hallita olemassa olevia yhteyksiä yleensäkin sekä tehdä esim. multi-DB olio handler.

-W-

pistemies [03.04.2008 22:30:53]

#

Totta haastat, Wizard :)
Saattaa että muutan tuon jossakin vaiheessa erilaiseksi, jonka tarkoitus on pelkästään tulostaa dataa valituista sarakkeista. Yhteyden voi luoda erikseen php-skriptinä. Pitää harkita.

Wizard [04.04.2008 08:27:21]

#

Koodi voisi olla jotain tämän näköistä (tosin tämä on aika purkkaa vielä ja heikosti tyypitettyä kaiken kaikkiaan):

<?php

/**
 * Database Connector for MySQL
 *
 * Open connection(s) to MySQL & get result
 *
 * @author			Jari Tuomoja, 2008
 * @license 		GPL
 * @version 		1.0.0
 * @since 			Class Available since Release 1.0.0
 *
 */
 class Db {

	// Declare variables
	private $connections = array();		// Current open connections
	private $connID = false;			// Current connection ID

	/**
	 * Init class
	 */
	 public function __construct() {
		if(version_compare(PHP_VERSION, '5.0.0') < "0"):
			echo 'Your PHP version is not 5.x.x or bigger';
		endif;
	}

	/**
	 * Close connections when closing class
	 */
	 public function __destruct() {
		if($this->connections):
			$max = count($this->connections);
			$counter = 1;
			reset($this->connections);

			while($counter <= $max):
				$key = key($this->connections);

				$this->closeConnection($this->connections[$key]['connID']);

				$counter ++;
			endwhile;
		endif;
	}

	/**
	 * Execute query
	 */
	 public function getResult($server, $username, $password, $db, $query) {
		// Open DB connection
		// Select DB
		// Execute query
		if($this->openConnection($server, $username, $password) && $this->selectDb($db)):
			return mysql_query($query, $this->connID);
		else:
			return false;
		endif;
	}


	/**
	 * Open DB connection
	 *
	 * @param string $server
	 * @param string $server
	 * @param string $server
	 * @return resource
	 */
	 private function openConnection($server, $username, $password) {
		if($server && ctype_alnum($username)):
			if($this->connectionHandler($server, $username)):
				return is_resource($this->connID);
			else:
				$this->connID = mysql_connect($server, $username, $password, true);
				$this->saveHandler($server, $username, $this->connID);
				return is_resource($this->connID);
			endif;
		endif;

		return false;
	}

	/**
	 * Select DB
	 *
	 * @param string $name
	 * @param resource $connID
	 * @return boolean
	 */
	 private function selectDb($name = false) {
		if($name && is_resource($this->connID)):
			return mysql_select_db($name, $this->connID);
		endif;

		return false;
	}

	/**
	 * Close connection
	 */
	 private function closeConnection($connID) {
		return mysql_close($connID);
	}

	/**
	 * Connection handler
	 *
	 * @param string $server
	 * @param string $username;
	 */
	 private function connectionHandler($server, $username) {
		if($this->connections):
			$max = count($this->connections);
			$counter = 1;
			reset($this->connections);

			while($counter <= $max):
				$key = key($this->connections);

				if($this->connections[$key]['server'] == $server && $this->connections[$key]['username'] == $username):
					$this->connID = $this->connections[$key]['connID'];
					return true;
				endif;

				$counter ++;
			endwhile;
		endif;

		return false;
	}

	/**
	 * Save new handler
	 */
	 private function saveHandler($server, $username, $connID) {
		$this->connections[] = array('server'=>$server, 'username'=>$username, 'connID'=>$connID);
	}
}

?>

Kulutin aikaa tähän 8 minuuttia joten virheitä saattaa olla. On vielä hieman kohmelossa näin aamutuimaan....

-W-

Wizard [04.04.2008 10:34:36]

#

while loopeissa on virhe: sieltä puuttuu kahdesta metodista rivi:

next($this->connections);

...heti jälkeen $counter ++;


Sitä se teettää kun ei ole edes aamukahvia kunnolla saanut...

pistemies [06.04.2008 22:27:21]

#

Kiitos mielenkiinnosta.
Saattaa olla, että sinulta meni 8 minuuutia hukkaan ainakin mitä tähän smartyyn tulee.
Epäilen, että tuota ei saa yhteensopivaksi smartyn Smarty_Compiler-luokan kanssa. En ole asiasta varma, mutta otaksun näin. Se on pääsyy tuolle lausunnolleni.
Pienempi homma on se, että yhteydenotto-skripti täytyy tulostaa stringinä tpl-tiedostosta tehtyyn "käännös-tiedostoon", koska ohjelma suorittaa ne skriptit, mitä kyseisessä tiedostossa on. Tuo return $output palauttaa tuon stringin ja smartyn ohjelma hoitaa loput.

Wizard [07.04.2008 08:47:23]

#

En tunne smartyä, mutta ainakin Zend Frameworkissa toimii mainiosti. Tuo koodinpätkä on itse asiassa menossa omaan koulutusmateriaaliin kun satun kouluttamaan PHP:ta ja nimenomaan olio-ohjelmointia MVC-mallilla. Eli sinänsä ei mennyt hukkaan vaan korjaan nuo bugit tuosta kun testaan tuota.

Mikä Smarty itse asiassa on ja mitä se tekee? Onko se MVC Framework kuten Zend vai jotain muuta, mitä?

ajv [07.04.2008 09:05:34]

#

@Wizard:
tuo sinun koodisi vilisee tosiaan purkahtavia virityksiä. Miksi et esim. käytä foreach()-silmukkaa taulukon läpikäymiseen vaan while-key-viritelmää. Muutenkin tuon käyttäminen koulutusmateriaalina on hieman kyseenalaista, koska tuosta ei kokeneempikaan PHP-ohjelmoija ota ihan heti selkoa.

@Pekka Mansikka:
Sulta tainnut mennä Smartyn ja template-tiedostojen perimmäinen idea vähän ohi, vai miksi haluat templatessa yhdistää tietokantaan? Templatet pitävät tosiaan sen html:n erillään itse koodista, niin että layout-suunnittelija / sunnuntaikoodari voi niitä suht turvallisesti käpistellä, kun taas sisältö niihin template-tiedostoihin luodaan nimenomaan siellä koodin puolella ja useimmiten se tulee suoraan tietokannasta.

Wizard [07.04.2008 09:27:34]

#

ajv kirjoitti:

@Wizard:
tuo sinun koodisi vilisee tosiaan purkahtavia virityksiä. Miksi et esim. käytä foreach()-silmukkaa taulukon läpikäymiseen vaan while-key-viritelmää. Muutenkin tuon käyttäminen koulutusmateriaalina on hieman kyseenalaista, koska tuosta ei kokeneempikaan PHP-ohjelmoija ota ihan heti selkoa.

Foreach silmukkaa en käytä tarkoituksella tuossa sen takia, että se olisi muodossa foreach($taulukko as $key=>$val) ja pyrin tarkoituksella välttämään tuota ylimääräistä muuttujaa $val. While on ihan yhtä nopea tuossa tarkoituksessa noiden ylimääräisten funktioiden käyttämisestä huolimatta. Taulukoiden koko on kuitenkin tuossa silmukassa max muutamasta rivistä 10-20 riviin (aika iso ympäristö saa olla jo tuossa määrässä).


Ja tuo luokka on itse asiassa hyvin selkeä ja siinä ei harjoiteta avaruusraketin rakentamista yhdessä metodissa. Se mitä taas tarkoittaa määritelmä "kokenut ohjelmoija", jää minulle hämärän peittoon, koska jokainen nykyinen ja ex-työkaveri osaavat lukea tuota ja heillä on kaikilla PHP ohjelmointikokemusta 5+ vuotta. ;)


Mutta kerro mitä et ymmärrä, niin selitän toki. Tässä kuitenkin lyhyt selitys:

Kaikki pyynnöt ohjataan yhteen public functioon luokassa eli ->getResult($param1, $param2...)

Tämä metodi ohjaa sitten pyyntöjä luokan sisällä private metodeille jotka ovat tarkoituksella suljettuja. Näin ollen vain yksi luokka pystyy käpistelemään tietokantayhteyksiä, tässä tapauksessa MySQL. Kun tuo luokka yhdistetään läjään muita tietokanta adaptereita kuten MySQLi, MS SQL, PostgreSQL ja Oracle, niin on helpompaa hallita noita kaikkia asioita yhdestä luokasta. Virheiden selvittelykin on aika paljon helpompaa muuten - ihan vain noin kokemuksesta voin sanoa multi-DB luokkien kanssa pelanneena.


Tuo esimerkki on suorituskyvyn kannalta ihan ok: peruspalvelin pystyy avaamaan ja käsittelemään jotakuinkin 200-250 yhteyttä sekunnissa MVC-mallissa kun jokainen pyyntö käsittää jotakuinkin 25-30 SQL kyselyä. Vasteajat on jotakuinkin < 0.5 sekuntia. Luokan suorituskyky ei ole siis huono vaikka tuossa onkin hieman vielä viilaamista eli tuo ei ihan sellaista suorituskykyä varmasti anna.


Mutta kerro siis mikä luokassa on sinusta huonoa? Otan mielelläni opiksi jos osaat kertoa KONKREETTISIA asioita joita korjata etkä tyydy vain arvostelemaan.


-W-

ajv [07.04.2008 09:55:07]

#

No kyllä minä tuon toimintaperiaatteen sisälsin pienen tutkinnan jälkeen.

Siis ihan tuo connectionhandler toimisi minun mielestäni näin ilman turhia apumuuttujia:

<?php
if($this->connections):
	foreach($this->connections as $connection):
		if($connection['server'] == $server && $connection['username'] == $username):
			$this->connID = $connection['connID'];
			return true;
		endif;
	endforeach;
endif;
?>

Ja OK, otan palautettani sen verran takaisin, että vaikka PHP onkin tuttu kieli, niin en ole ikinä tuollaista tietokantaluokkaa tarvinnut. Omat projektit tosiaan ovat sen verran pienempiä, että niissä käytetään yhtä tietokantaa kerrallaan jolloin tietokantaluokan toimintaperiaate on aivan erilainen.

Edit vielä tarkennukseksi: Nyt kun tosiaan sisälsin tuon idean (enkä yrittänyt mielessäni istuttaa sitä oman tietokantaluokkani tilalle projektiin), niin onhan tuo luokan toiminta ihan selkeä.

kayttaja-2791 [07.04.2008 10:08:49]

#

Wizard kirjoitti:

Ja tuo luokka on itse asiassa hyvin selkeä ja siinä ei harjoiteta avaruusraketin rakentamista yhdessä metodissa. Se mitä taas tarkoittaa määritelmä "kokenut ohjelmoija", jää minulle hämärän peittoon, koska jokainen nykyinen ja ex-työkaveri osaavat lukea tuota ja heillä on kaikilla PHP ohjelmointikokemusta 5+ vuotta. ;)

ajv varmaan suhteutti "kokeneen" oppilaisiisi. Tosin voivathan hekin voineet olla vuosikausia alalla tuotantotyössä käsittelemässä juuri näitä asioita, en tiedä ketä oletkaan opettamassa.

Ehkä koodiin voisi kuitenkin nakella vähän enemmän kommentteja jos tuo opetustarkoitukseen on menossa. Kyllähän tuosta selvän sai, mutta olisihan se luku huomattavasti nopeampaa kun olisi muutama selventävä kommentti että mitä esim. ko. lohkolla haetaan.

ps. selectDb-metodissa näyttäisi olevan epäloogisuuksia. Dokumentointi on väärin parametrien osalta, ja name -muuttujan oletusarvon alustus on täysin turhaa jos muuttujan on kerran pakko olla syötetty.

Jos suorituskyvystä puhutaan niin hyvä tietysti olisi ottaa yhteys vasta viime hetkellä, eli juuri ennen ensimmäistä tietokantasuoritusta. Tosin tälläisten kikkailujen käyttö opetusmateriaalissa on tietysti vähän harmaalla alueella, johtaa helposti tilanteeseen missä koodin monimutkaistaminen vain vaikeuttaa koodin lukemista ja asian ymmärtämistä.

pistemies [07.04.2008 10:09:53]

#

@ajv:
Kiitos vastineesta. Muutan tuota tosiaan jossakin vaiheessa. Mahdollisesti skriptin nimi (mysql) tulee sitten myös muuttumaan.
Olen käyttänyt smartyä kovin vähän. Siksi kaipaisin myös vähän kommentteja, mistä alueesta toi smarty kaipaisi vielä lisäominaisuuksia.
Ps. Olen muuttanut tuon skriptin tilaksi "uusi", joten se ei näy enää skriptien listauksessa, mutta tuo suora linkki edelleen toimii.

@wizard:
Kyllä tuo luokka minusta on aika selkeä toiminnaltaan. Siinä on hiukan javan kaltainen rakenne. Javassahan myös yksi metodi tulostaa koko luokan ja mahdolliset alilokat.

Wizard [07.04.2008 10:19:14]

#

JTS kirjoitti:

ajv varmaan suhteutti "kokeneen" oppilaisiisi. Tosin voivathan hekin voineet olla vuosikausia alalla tuotantotyössä käsittelemässä juuri näitä asioita, en tiedä ketä oletkaan opettamassa.

Opetan ihmisiä jotka ovat koodanneet PHP:lla 2-> vuotta eli perusasiat pitäisi olla jotenkin hanskassa. Käytännössä opetan olioita sekä MVC-mallia ja rajatusti sitten skaalautuvuuten sekä isoihin järjestelmiin liittyvistä erityisongelmista PHP -kielellä.

JTS kirjoitti:

Ehkä koodiin voisi kuitenkin nakella vähän enemmän kommentteja jos tuo opetustarkoitukseen on menossa. Kyllähän tuosta selvän sai, mutta olisihan se luku huomattavasti nopeampaa kun olisi muutama selventävä kommentti että mitä esim. ko. lohkolla haetaan.

Totta turiset. Tuo onkin se 8 minuutin viritelmä ja jäi nuo kommentoinnit vajaiksi. Esimerkissä ne sitten on kyllä kohdallaan kun olen korjannut noita ongelmia tuolta.

JTS kirjoitti:

ps. selectDb-metodissa näyttäisi olevan epäloogisuuksia. Dokumentointi on väärin parametrien osalta, ja name -muuttujan oletusarvon alustus on täysin turhaa jos muuttujan on kerran pakko olla syötetty.

Itse asiassa ongelma on jo ->getResult metodissa. Sieltä puuttuu alustukset ja kaikissa private metodeissa alustuksia ei sitten tarvita. Tuo ->selectDb on väärin dokumentoitu, mutta se johtuu siitä, että korjasin esimerkkiä ja sitten unohdin korjata dokumentointia. Nyt en pääse sitä enää korjaamaankaan.

->getResult on myös multi-DB adapterina protected eikä public ja luokka pitäisi alustaa 'class Db_Mysql extends Db' eikä kuten nykyisin. Mutta kyseessähän on vain rautalanka havannointi malli, ei niinkään täydellinen kympin suoritus.

Tuosta luokasta puuttuu myös kokonaan tuo query metodi joka on sen verran iso, että en alkanut sitä puljaamaan tuohon vaan yritin vain luoda yhteyksien hallintaan liittyvän luokan. Query luokka kuitenkin muuttujien tarkistuksineen kuuluu MySQL adapterissa tietysti kyseiseen luokkaan joko laajentavana tai sitten samaan sätökseen. Se on hieman sitten jo makuasia enemmänkin. Jos olisi laajentava, niin $this->connID joutuisi romukoppaan ja tuota luokkaa pitäisi muuttaa jonkin verran.

JTS kirjoitti:

Jos suorituskyvystä puhutaan niin hyvä tietysti olisi ottaa yhteys vasta viime hetkellä, eli juuri ennen ensimmäistä tietokantasuoritusta. Tosin tälläisten kikkailujen käyttö opetusmateriaalissa on tietysti vähän harmaalla alueella, johtaa helposti tilanteeseen missä koodin monimutkaistaminen vain vaikeuttaa koodin lukemista ja asian ymmärtämistä.

Käytännössä tuo tehdään niin. Tuota luokkaa kutsutaan __autoload metodilla kun tarvitaan dataa tietokannasta. Termi onDemand on tässä oikea.

-W-


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta