plain2click()
Nov1
Nav ko te gari rakstīt. Nereti lietotāja ievadītajā tekstā nākas meklēt URL saites un pārtaisīt tās klikšķināmas. Lūk, strādājošs risinājums. Funkcija (otra tikai palīdz pirmajai
), kas tekstā sameklēs linkus un attiecīgi tos pārveidos par klikojamām saitēm vai e-pasta saitēm. Ja klikojamā saite būs links uz Youtube video, tad tā saite tiks pārvērsta par embed’u.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | function plain2click($str){ $str = preg_replace_callback("/(http(s)?\:\/\/|www\.)[a-z0-9\-\.]+\.[a-z]{2,4}(\/[a-z0-9\-\?=_&\+%#@\.\/]*)?/i", 'plain2clickCallback', $str); $str = preg_replace("/[a-z0-9\-\._]+@[a-z0-9\-\.]+\.[a-z]{2,4}/i", '<a href="mailto:$0">$0</a>', $str); return $str; } function plain2clickCallback($matches){ $ymatches = array(); if(preg_match("/http\:\/\/www\.youtube\.com\/watch\?v=([a-z0-9\-_]+)(&feature=[a-z]+)?/i", $matches[0], $ymatches)){ return '<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/'.$ymatches[1].'&hl=en_US&fs=1&"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/'.$ymatches[1].'&hl=en_US&fs=1&" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object>'; } $url = str_replace(array('http://', 'https://'), '', $matches[0]); return '<a rel="nofollow" href="http'.$matches[2].'://'.$url.'" onclick="window.open(this.href); return false;">'.$matches[0].'</a>'; } |
Ja kaut kas nestrādā vai strādā nepareizi, tad padod ziņu komentāros, man gan līdz šim viss bijis ok.
PHP.JS – malware detected
Jun0
Šodien saņēmu bug-report par kādu mājaslapu, kurā biju izmantojis PHP.JS default’o paku. Viss jau būtu ok, ja vien šis bug’s nebūtu tik interesants.
Lūk Avast anti-virus programma šajā pakā saskatījusi malware pazīmes. Līdz ar to, klientam tiek rodas vēlme pamest šo vietni, kas nevienam tā īsti nepatīk.
Nedaudz pagūglējot, nonācu pie tik ļoti negaidītās apgaismības (lasi – gaidītās) – manis veidotā vietne nebūt nav vienīgā, pret kuru iebilst šī anti-virus programma.
Risinājums ļoti vienkārš – nelietojam default paku, bet kādu citu no piedāvātajām, kura satur vajadzīgās funkcijas, jo izskatās, ka brīdinājums, par ļaundabīgu ietekmi, lido ārā tikai no šīs pakas.
Grūti pateikt, vairāk pat šaubos, ka šim ziņojumam ir kāds reāls pamats (lasi – potenciāli draudi datora veiksmīgai darbībai), bet nu man jādara viss, lai neviens klients nejustos apdalīts.
Tāpat arī šaubos, ka šīs nejaukās parādības varētu kaut kā ietekmēt projekta popularitāti, jo konkrētās programmas lietotāju loks nav pārāk plašs un problēmas risinājums ir diezgan elementārs. Protams, būtu krietni jaukāk, ja līdz šādu problēmu risināšanai vispār nebūtu jānonāk.
LABOJUMS. Tomēr izrādās, ka paku izvēle šeit nespēlē nekādu lomu. Izmanto kuru gribi, brīdinājuma logs tāpat lido acīs.
Vēlreiz par lappusēm…
Jun5
Nereti gadās situācijas, kad, piemēram, kaut kādu datu sarakstu jāsadala pa lappusēm. Vienreiz jau rakstīju, kāds, manuprāt, ir labākais risinājums lappušu skaita iegūšanai, šeit būs tāds kā turpinājums…
Ko darīt, ja lappušu skaits pārāk liels un visu lappušu linkus nav iespējams izvadīt? Protams, jāizvada tikai daļa no tām (arī pie tā raksta kāds jautāja par šādu algoritmu).
Nekā sarežģīta jau tur nav, bet vienmēr esmu rakstījis kaut kādu garu funkciju, kas rēķina sākuma ciparu un beigu ciparu, izejot no atvērtās lappuses. Pirms pāris nedēļām strādāju pie kāda foruma, kur radās šī pati problēma, taču šoreiz, dzerot aliņu, tika izdomāts algoritms, kurš aizņem, nevis 20, bet 2 rindiņas.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <? function draw_pages($count, $perpage, $offset = 5){ $pages = ceil($count/$perpage); if($pages<2){ return ''; } $r = '<ul class="pages">'; $page = (empty($_GET['p']) || $_GET['p']<2) ? 1 : $_GET['p']; $first = $page >= $offset+1 ? $page-$offset : 1; $last = $page <= $pages-$offset ? $page+$offset : $pages; if($page>1){ $r.= sprintf('<li><a href="?p=1">«</a></li><li><a href="?p=%d"><</a></li>', $page-1); } for($i=$first;$i<=$last;$i++){ $r.= sprintf('<li%s><a href="?p=%d">%d</a></li>', $page==$i ? ' class="active"' : '', $i, $i); } if($page<$pages){ $r.= sprintf('<li><a href="?p=%d">></a></li><li><a href="?p=%d">»</a></li>', $page+1, $pages); } $r.= '</ul>'; return $r; } ?> |
Atzīstu, ir tādas, lietas, kuras kādreiz tikušas izdomātas un tiek lietotas vēl šodien. Tomer, ja kaut ko mēģina uztaisīt no šodienas skata punkta, loģiski – tas sanāk daudz ātrāk un vienkāršāk.
Kalendārs PHP stilā.
Jun4
Protams, runa ir par vārda dienām. Vairākas reizes tas ir bijis vajadzīgs, katrreiz kaut kur meklēju, kopēju un beidzot tas piegriezās. Ņēmu kalendāru un drukāju (tas bija pagājšgad?), jo man šis kalendārs bija nepieciešams savā stilā. Derīgs čarsets, vārdi masīvā un vispār kārtība.
Ja tev arī vajag vārda dienu sarakstu, tad skripts lasot pilno rakstu.
Kā pasargāt sevi no SQL injekcijām
May10
Mūžam vecā lieta, kad kāds aizmirsis pārbaudīt datus, pirms tos liek SQL pieprasījumā, un kāds cits ir atklājis šo ievainojamību. Kā tad maksimāli izvairīties no šādām problēmām?
Ir redzētas dažādas datubāžu pieprasījumu veidošanas klases, bet tā īsti nav sanācis izmantot nevienu. Tad nu lūk, kādas īsti ir priekšrocības mysql_query() iebāzt kādā citā funkcijā? Tādas, ka tā cita funkcija var darīt vēl šo to, piemēram, palīdzēt debug’ot kodu. Precīzāk, saskaitīt nosūtīto pieprasījumu skaitu, izpildes laiku un visu pārējo, ko vēlies redzēt.
Bet ne par to ir runa. Neturēšu sveci zem pūra un parādīšu, manuprāt, nozīmīgāko, kas man neļauj aizmirst, par datu drošību. Kveriju sastādu pēc printf() principa, nevis līmējot stringus. Pieprasījuma veidošana izskatīsies šādi:
$db->query("SELECT id FROM `tabula` WHERE lauks1=%d AND laiks2=%s AND lauks3=%.2f OR lauks4=NOW()", $var1, $var2, $var3);
Tālāko varat iedomāties paši.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | function query($query){ if(func_num_args()>1){ $args = func_get_args(); $query = call_user_func_array(array($this, 'make_args'), $args); } $this->queries[] = $query; // etc return mysql_query($query); } function make_args(){ $args = func_get_args(); $arr = array($args[0]); foreach($args as $arg){ $arr[] = $arg==='' ? 'NULL' : ($this->is_numeric($arg) ? $arg : "'".mysql_real_escape_string($arg)."'"); // eskeipojam katru mainīgo, ja tas nepieciešams } return call_user_func_array('sprintf', $arr); } function is_numeric($what){ return (!preg_match('/^\-?\d+(\.\d+)?$/D', $what) || preg_match('/^0\d+$/D', $what)) ? false : true; } |
Arī update funkciju esmu uztaisījis savu, kur padodu tikai masīvu ar atjaunojamajiem laukiem
$db->update('tabula', $id, array( 'lauks1' => $var1, 'lauks2' => $var2, '~lauks3' => 'NOW()' // SQL'iskas funkcijas nevajag apstrādāt ));
Un konstruējot pieprasījumu, apstrādājam mainīgos
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | function update($table, (int)$id, $updates, $id_field = 'id'){ $update = array(); foreach($updates as $key=>$val){ list($field, $value) = $this->process_pair($key, $val); $update[] = "$field=$value"; } $update = implode(", ", $update); return $this->query("UPDATE `$table` SET $update WHERE $id_field=$id"); } function process_pair($field, $value){ $escape = true; if($field[0]=='~'){ // eskeipot mainīgo ? $escape = false; $field = substr($field, 1); } return array($field, ($value==='' ? 'NULL' : ($this->is_numeric($value) ? $value : "'".mysql_real_escape_string($value)."'"))); } |
Rezultātā praktiski visi mainīgie tiks eskeipoti automātiski un aizmirst par mysql_real_escape_string() lietošanu būs gandrīz neiespējami. Tāpat man patīk arī sintakse, kur nav jālipina kopā stringi.
Jāatzīst, ka diezgan pasen kaut kur jau manīju šādu risinājumu, taču biju piemirsis un pirms pāris mēnešiem atcerējos, nolēmu, ka toreiz manītā ideja man tīri labi patīk un nu jau kādu laiku pats lietoju praksē.
Personas koda un IBAN validācija
Apr4
Šie validācijas skripti gan vairāk attiecas uz mani pašu, jo tos tāpat var diezgan viegli atrast izmantojot tanti gūgli. Bet kāpēc man katru reizi meklēt, ja es varu tos pierakstīt un nākamreiz jau doties uz konkrētu vietu un nokopēt? (:
Personas kods:
1 2 3 4 5 6 | function validate_pk($pk){ $pk = preg_replace("/[^\d]/","",$pk); $calc = 1*$pk[0]+6*$pk[1]+3*$pk[2]+7*$pk[3]+9*$pk[4]+10*$pk[5]+5*$pk[6]+8*$pk[7]+4*$pk[8]+2*$pk[9]; $checksum = (1101-$calc)%11; return $checksum==$pk[10]; } |
IBAN (bankas konta numurs):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | function validate_iban($str,$reformat=true,$debug=false){ if($reformat){ $str = preg_replace('/[\W_]+/','',strtoupper($str)); } $errors = array(); if(4>strlen($str) || strlen($str)>34){ $errors[] = 'Invalid string length'; } if(is_numeric($str[0]) || is_numeric($str[1])){ $errors[] = 'Invalid chars at 0, 1'; } if(!is_numeric($str[2]) || !is_numeric($str[3])){ $errors[] = 'Invalid chars at 3, 4'; } $ibanReplaceChars = range('A','Z'); foreach(range(10,35) as $tempvalue){ $ibanReplaceValues[]=strval($tempvalue); } $tmpIBAN = substr($str,4).substr($str,0,4); $tmpIBAN = str_replace($ibanReplaceChars,$ibanReplaceValues,$tmpIBAN); $tmpValue = intval(substr($tmpIBAN,0,1)); for($i=1;$i<strlen($tmpIBAN);$i++){ $tmpValue*= 10; $tmpValue+= intval(substr($tmpIBAN,$i,1)); $tmpValue%= 97; } if($tmpValue!=1){ $errors[] = 'Invalid checksum'; } if(empty($errors)){ return true; } if($debug){ echo implode("\n", $errors); } return false; } |
Keša problēmu risināšana
Jan0
Lielāku portālu izmaiņu veikšanai nepieciešams, lai lietotāja interneta pārlūkprogramma pieprasītu atjaunotos datus, kā zināms, brouzeriem patīk tos pieglabāt – kešot (cache) -, tādejādi paātrinot lapas ielādi sev un samazinot trafika lielumu mums.
Protams, varam likt pārlūkam pieprasīt pilnīgi visu no jauna, taču, tas paildzinās lapas ielādes laiku un mūsu trafiku, kas nebūs no tiem labākajiem risinājumiem.
Bet kā likt pārlūkam pa jaunu pieprasīt tikai izmainītos failus? Principā, ideja ļoti vienkārša. Nedosim nekādus header’us, bet liksim domāt, ka šis fails agrāk nemaz nav redzēts.
Pārlūkam interesē pilna faila adrese, tātad tiek ņemtas vērā arī GET vērtības. Neizteicos korekti, bet doma bija – pamainam vērtību, kas seko aiz jautājuma zīmes, piemēram, /css/layout.css?xxx. Taču šo vērtību neģerēsim katru reizi randomā, jo šādā gadījumā fails atkal tiktu pieprasīts pie katras lapas ielādes. Kā mainīgo vērtību padosim faila izmaiņu veikšanas laiku.
<link rel="stylesheet" href="/css/layout.css?<?=filemtime($_SERVER['DOCUMENT_ROOT'].'/css/layout.css');?>" type="text/css" media="screen" />
Kodā tas neizskatīsies smuki (ja tas tevi neuztrauc, tad tālāk vari nelasīt), šoreiz palīdzīgu roku var sniegt mod_rewrite. Mainīgo vērtību varam padot, nevis caur jautājuma zīmi, bet gan kā mapi, piemēram, /css/jan27/layout.css.
<link rel="stylesheet" href="/css/<?=strtolower(date("Mj",filemtime($_SERVER['DOCUMENT_ROOT'].'/css/layout.css')));?>/layout.css" type="text/css" media="screen" />
Ierakstu dalīšana pa lappusēm
Jan21
Tēma šoreiz nav sareģīta, bet visur ir savs āķis.
Zināms, ka, lai izrēķinātu lappušu skaitu, jāzin kopējais ierakstu skaits, tātad divi pieprasījumi uz datubāzi:
- Iegūstam nepieciešamo skaitu ar ierakstiem
- Iegūstam kopējo ierakstu skaitu
Bet kur tad optimizācija? Un ja vēl nepieciešams liels skaits dinamiski ģenerētu filtru? Būs jāveido divi gandrīz identiski pieprasījumi – viens ar limitu, viens bez.
Es piedāvāju izmantot vienu no ne tik slavenajām MySQL funkcijām – SQL_CALC_FOUND_ROWS un FOUND_ROWS(). Pieprasījums un tā apstrāde, manā gadījumā, izskatīsies šādi:
1 2 3 4 5 6 7 8 9 10 11 12 | <? $perpage=10; // ieraksti vienā lappusē $_LIMIT=(!empty($_GET['p']) && $_GET['p']>1)?($_GET['p']*$perpage)-$perpage:0; // izrēķinam limita sākuma pozīciju, atkarīgā no atvērtās lappuses, kas padota kā ?p=X $_LIMIT.=", ".$perpage; $sql="SELECT SQL_CALC_FOUND_ROWS * FROM `tabula` LIMIT ".$_LIMIT; $res=mysql_query($sql); $count=mysql_fetch_object(mysql_query("SELECT FOUND_ROWS() as c")); while($row=mysql_fetch_object($res)){ var_dump($row); } $pages=splittopages($count->c,$perpage); ?> |
Funkcija, kura izrēķinās lappušu skaitu. Pieejamās lappuses tiek saliktas masīvā, lai būtu vienkāršāk izveidot pogas “Nākamā lappuse” un “Iepriekšējā lappuse” – atliek tikai pārbaudīt vai masīvā eksistē ieraksts ar atbilstošu atslēgu.
1 2 3 4 5 6 7 8 9 10 11 | <? function splittopages($count,$perpage){ $pagescount=ceil($count/$perpage); $pages=array(); if($pagescount<2){ return $pages; } for($i=1;$i<=$pagescount;$i++){ $pages[]=$i; } return $pages; } ?> |
Lappušu izvade.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | if($_GET['p']<2){ $_GET['p']=1; } // definēsim lappusi paši, ja tā vēl nav definēta $now=array_search($_GET['p'],$pages); // atvērtā lappuse $prev=$now-1; $next=$now+1; if(array_key_exists($prev,$pages)){ echo '<a href="?p='.$pages[$prev].'">Iepriekšējā lappuse</a>'; } foreach($pages as $value){ $active=$_GET['p']==$value?' class="active"':''; // atzīmējam atvērto lappusi echo '<a href="?p='.$value.'"'.$active.'>'.$value.'</a>'; } if(array_key_exists($next,$pages)){ echo '<a href="?p='.$pages[$next].'">Nākamā lappuse</a>'; } ?> |
Lielākais ieguvums izmantojot šādu risinājumu ir tāds, ka nav jāsūta vēl viens praktiski identisks pieprasījums uz datubāzi, ierakstu skaits jau tiek nokešots MySQL’ā.
Bez liekiem if’iem tiek pie nākamās un iepriekšējās lappuses.
Draudzīgas saites būvēšana
Dec2
Kā jau ierasts, runa ir par informācijas meklētājiem draudzīgām saitēm, izmantojot mod_rewrite moduli. Principā saites ģenerēšana vairumā gadījumu nav sarežģīts process, šī lieta tiek sarežģīta brīdī, kad izejot no vienas lapas, nepieciešams saražot lielu daudzumu citu saišu – visbiežāk meklēšana vai lapas karte.
Iepriekš savos rakstos esmu parādījis, kā nevajadzētu darīt, diemžēl, šoreiz tas izpaliks, jo rakstīšana ar savu jauno mini-datoru nebūt nav tik viegla. Biežāk izmantotais risinājums šai problēmai saistās ar pieprasījumu veidošanu uz datubāzi ciklā, patiesībā cikls ciklā – tiek atrasti vajadzīgie ieraksti datubāzē, ciklējot caur ierakstiem tiek izsaukta saites ģenerēšanas funkcija, kas arī satur ciklu.
Mans risinājums. Atkal pieņemam, ka strādājam ar klasisko koka struktūru.
+----+-----------+----------+----------+ | id | parent_id | name | url | +----+-----------+----------+----------+ | 1 | 0 | Sākums | home | +----+-----------+----------+----------+ | 2 | 0 | Kontakti | contacts | +----+-----------+----------+----------+ | 3 | 1 | Raksti | news | +----+-----------+----------+----------+ | 4 | 1 | Foto | photo | +----+-----------+----------+----------+ | 5 | 2 | Karte | map | +----+-----------+----------+----------+
Lai mums katru reizi nevajadzētu pieprasīt sadaļu sarakstu no jauna, saglabājam vajadzīgo masīvā, izejot no funkcijai padotās sadaļas id, izveidosim saiti.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <? function url($id){ global $urldata; if(!is_array($urldata)){ return false; }elseif(!array_key_exists($id,$urldata)){ return ""; } $url=$urldata[$id][0]; $url.=$urldata[$id][1]; return $url; } $urldata=array(); $sql="SELECT id, parent_id, url FROM `topics`"; $res=mysql_query($sql); while($row=mysql_fetch_object($res)){ $urldata[$row->id]=array($row->parent_id,$row->url); } ?> |
Pēc tam jau viss notiek vienkārši
Vizitkarte – vertikāli, horizontāli centrēta lapa
Dec2
Viens no šī laika “grūtākajiem” layout’iem – vertikāli un horizontāli centrēta lapa, visbiežāk vizitkartes tipa mājaslapa. Šīs problēmas risinājumi arī ir redzēti daudz un dažādi, liekākā daļa, diezgan, līki. Visvieglāk būtu izveidot 100%x100% tabulu ar atribūtiem align=”center” un valign=”middle”, bet priekškam lietot tabulu tur, kur tas nav vajadzīgs? Otrs biežāk pielietotais variants ir tabulas atdarināšana ar elementiem, proti, style=”display: table;” utt. Turklāt šādiem risinājumiem ir problēmas ar XHTML 1.0 Strict un pārlūku savietojamībām.
Parādīšu, kā to daru es, zinot centrējamā elementa augstumu.
CSS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | * { margin: 0; padding: 0; } html, body { height: 100%; } body { background-color: #fff; font-family: Arial; font-size: 12px; color: #000; text-align: justify; } #top { width: 100%; height: 50%; /* šis elements nebrauks virsū wrapper'im */ position: relative; z-index: -1; } #wrapper { margin: -150px auto 0 auto; /* puse no elementa augstuma */ padding: 30px; width: 440px; height: 240px; background-color: #eee; } |
HTML
1 2 3 4 5 6 7 8 9 10 11 12 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Mana lapa</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <link rel="stylesheet" href="/css/layout.css" type="text/css" media="screen" /> </head> <body> <div id="top"> </div> <div id="wrapper">Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. </div> </body> </html> |
Dabā to varam redzēt šeit.
Testēts uz Microsoft Internet Explorer 6, Microsoft Internet Explorer 7, Mozilla Firefix 3, Netscape Navigator 9, Apple Safari 3, Google Chrome, Opera 9.