ASP.NET features – ViewState

Solīts ir un paliek solīts – rakstām par ASP.Net atribūtiku. Šoreiz – par ViewState.
Jau iepriekš pieņemu, ka lasītājs ir iepazinies ar rakstu par ASP.Net pamatiem un par ASP.Net koda izpildes modeli

Īsumā, kas ir ViewState:

  • veids, kā ASP.Net uzglabā informāciju par WebFormas stāvokli, informāciju nesaglabājot sesijas mainīgajos
  • izstrādātājam – datu masīvs, kurā var ievietot datus un tos nolasīt neatkarīgi no WebFormas dzīves cikla. Tāpat arī – iespēja vienu reizi dinamiski uzstādīt kādas kontroles kādu atribūtu un pēc tam ļaut par atribūta vērtību parūpēties pašam ASP.Net
  • pārlūkprogrammai – burtu virknīte, hidden lauks HTML dokumentā, kas jāpārsūta atpakaļ serverim.

Vēlreiz parunāsim par ASP.Net WebFormu dzīves ciklu. Piemēram, esam atvēruši lapu http://localhost/default.aspx Šajā lapā ir viens teksta ievades lauks txtName un viena poga. Nospiežot pogu, tiek izpildīta HTTP POST operācija, datus atkal pārsūtot uz http://localhost/default.aspx, tas ir, lapa tos pārsūta “pati uz sevi”. Saņemtos datus apstrādā ASP.Net, ģenerē atjaunotu WebFormas izskatu un to nosūta uz pārlūku.

Saņemtajos formas POST datos parādās lauka txtName vērtība, kādu to ir ievadījis vai izmainījis lietotājs. Taču, lai varētu korekti apstrādāt lauka vērtības izmaiņas, nepieciešams salīdzināt šī lauka vērtību pirms to ļāva lietotājam mainīt un tad, kad saņemtas lietotāja veiktās izmaiņas, tas ir, nepieciešams zināt, kāda bija lauka vērtība, kad lapa iepriekšējo reizi tika sūtīta lietotājam. Tieši šādam mērķim arī kalpo ViewState – tajā ir atrodamas dažādu informācijas lauku “vecās versijas”. Tādējādi ASP.Net, procesējot lapu http://localhost/default.aspx, var salīdzināt, ka šobrīd aktuālā txtName vērtība ir “ArčibaldsKronins”, taču pēc ViewState datiem laukā txtName bijis ierakstīts “VitālijsMētra” un, pamatojoties uz to, apstrādāt txtName_TextChanged notikumu.

Jāatzīmē, ka katra lapas kontrole var ViewState saglabāt dažādus savus atribūtus. Piemēram, visas klases, kas mantotas no Web.UI.WebControls.WebControl klases, ViewState informācijā saglabā Enabled, AccessKey, TabIndex, ToolTip, Visible un papildus tam vēl specifiski kontrolei piemītošas īpašības, piemēram, Web.UI.WebControls.Label papildus tam saglabā arī Text atribūtu.

To, vai kāda ekrāna kontrole vispār saglabā savus datus ViewState vai nē, nosaka no System.Web.UI.Control klases mantotā atribūta EnableViewState vērtība. Ja EnableViewState=False, kontrole savu informāciju ViewState datos nesaglabā. Ja True, saglabā visu šim tipam atbilstošo informāciju (bieži vien pat par daudz). Par to, kā samazināt ViewState apjomu, raksta daudzi, ir vērts iepazīties ar Microsoft ieteikumiem aplikācijas stāvokļa saglabāšanai.

Lai saprastu tālāk, jāpaskatās, kā izskatās ViewState dati no HTML viedokļa. Tas ir slēpts datu lauks, kura vērtība apstrādāta ar Base-64 algoritmu

< input type="hidden" 
name="__VIEWSTATE" 
value="dDw5NDE3OTg0NzE7Oz6gEYYV9S+G2OiSz/XslX9Ha+7t5Q==" />

Atkodējot datus, var iegūt pa pusei lasāmu datu virkni, kas satur informāciju par visām kontrolēm, kas izmanto ViewState. Piemēram, manis demonstrētais ViewState ir atkodējams uz šādu t<941798471;;> †õ/†Ųč’Ļõģ•Gkīķå. Nav sevišķi izteiksmīgs teksts, taču dators saprot. Pēdējie baiti ir informācija pašam ASP.Net, lai varētu pārbaudīt, vai dati nav modificēti pēc nosūtīšanas klientam, pirmie – satur svarīgo informāciju par ekrāna kontroļu atribūtiem.

Kā redzams no atkodētajiem datiem, ViewState saglabātā informācija nav piekārtota ekrāna kontrolēm pēc to identifikatoriem vai nosaukumiem, bet gan pēc secības, kādā kontroles novietotas lapā. Tieši šī iemesla dēļ ir svarīgi, ka ekrāna kontroļu kolekcija nav tikusi mainīta kopš iepriekšējās lapas ielādes.
Ja atšķiras kontroļu skaits vai tips, ASP.Net ģenerēs kļūdas paziņojumu par nekorektiem ViewState datiem. Šī īpatnība nerada problēmas gadījumā, kad visas ekrāna kontroles ir sagatavotas .aspx lapā un kontroļu kolekcija ir nemainīga visā WebFormas dzīves laikā, bet problēma aktualizējas brīdī, kad kontroles tiek pievienotas lapai dinamiski.

Piemēram, ja veidosim WebFormu, kas parāda uz ekrāna kāda faila EXIF atribūtus un gribēsim ļaut tos visus vienlaicīgi mainīt uz ekrāna – katram EXIF laukam būs jāveido atsevišķa TextBox kontrole, kurā jāparāda esošā vērtība un jāļauj ievadīt citus datus. Tādējādi šo formu nevarēs sagatavot jau iepriekš un kontroļu skaits būs atšķirīgs.

Līdz ar to, lai pirmo reizi parādītu visus EXIF laukus, veidosim jaunu TextBox objektu:

    Dim oTxt As New Web.UI.WebControls.TextBox
    oTxt.ID = "txtResolution"
    oTxt.Text = oSomeObj.Resolution 'šis ir objekts, no kura "lasām" EXIF datus

un pievienosim šo objektu ekrāna kontroļu kolekcijai ar kodu Page.Controls.Add(oTxt).
Taču nākamajā lapas ielādes reizē, kad gribēsim izmantot ViewState datus, mums jāizveido attiecīgais TextBox objekts un jāļauj tam patstāvīgi nolasīt savu vērtību no ViewState. Objekta veidošanu ierakstām Page_Load metodē, kas izpildās pirms ViewState datu lasīšanas:

Private Sub Page_Load _
            (ByVal s As System.Object, ByVal e As System.EventArgs) _
             Handles MyBase.Load
    'izņemot gadījumu, ja lapa ielādējas pirmo reizi - tad jāizpilda augstāk aprakstītā procedūra
    If Not Page.IsPostBack() Then
        Dim oTxt As New Web.UI.WebControls.TextBox
        oTxt.ID = "txtResolution"
    Else
        'izsaukums uz augstāk aprakstīto procedūru, kas nolasa datus no paša faila
    End If
End Sub

Jādomā, ka problēma šeit jau ir jūtama – kā zināt, kā saucas ekrāna kontroles, kas jāveido, lai tās pašas no sevis nolasītu datus. Piemērā esmu izmantojis nosaukumu “txtResolution”, jo pieņemu, ka gandrīz katrā EXIF galvenē būs dati par attēla izšķirtspēju, taču ko darīt, ja datus ielādējam patiešām dinamiski?

Viens no risinājumiem (apkārtceļiem, protams), kādu esmu lietojis, ir – savākt visu pirmajā reizē izveidoto kontroļu ID-us vienā garā String tipa mainīgajā, atdalītā, piemēram ar “#” simboliem. Un, kad dinamiskā ģenerēšana beigusies, šo String noglabāt turpat ViewState datos:

Viewstate.Add("ControlNames", sControlNames)

Pēc tam, nākamo reizi ielādējot lapu, pārbaudām, vai ViewState satur mainīgo ControlNames. Ja satur – sadalām šo String pa atsevišķiem nosaukumiem, analizējam nosaukumus, pēc analīzes rezultātiem veidojam kontroles. Piemēram, no šāda te ControlNames satura – “txtResolution#chkMakePublic” var secināt, ka jāveido teksta ievades lauks ar nosaukumu txtResolution un ķekškaste ar nosaukumu chkMakePublic.

Kāpēc jālieto šāds apkārtceļš? Lai atkārtoti nelasītu datus no paša faila (pieņemsim, ka fiziskā faila atribūtu lasīšana ir resursietilpīga procedūra). Ja datus varam atkārtoti lasīt no paša faila, nav nepieciešams izmantot ViewState, jo var vienkārši salīdzināt lietotāja piesūtītos datus ar oriģinālajiem.

Ja pieļaujams darbam izmantot SessionState datus jeb “sesijas mainīgos”, lielu daļu no ViewState var pārcelt uz SessionState, tādējādi atslogojot klienta-servera saziņas datu kanālu, bet vairāk noslogojot serveri.

Jāatceras, ka daudzas no Microsoft piedāvātajām datu ievades kontrolēm strādās jocīgi vai nestrādās nemaz, ja ViewState tām būs izslēgts. Piemēram, ļoti ērtā DataGrid kontrole intensīvi izmanto ViewState un pie izslēgta ViewState darbojas slikti. Taču tieši šī kontrole saglabā ļoti daudz datu ViewState masīvā. No tā varam viegli secināt, ka ViewState būs labs risinājums gadījumos, kad klienta-servera komunikācija ir viegla un saziņas kanāls ir plats (piemēram, iekšējā tīkla risinājumos), bet ar to jāuzmanās tad, kad datu plūsmas apjoms ir svarīgs.

Starp citu – ViewState datos var saglabāt, šķiet, jebkuru objektu, kas atbalsta ISerializable interfeisu, piemēram, DataSet, taču ļoti jāuzmanās, vai dotais datu tips netiek serializēts nejēdzīgi lielā bināru datu virknē.

Stulbas beigas?

Piemērs no dzīves: ko dara nekārtīgs programmētājs, ja vajadzīga funkcija, kas “sakabina” kopā divus faila pilnās takas fragmentus? Raksta funkciju

Function getFullPath(ByVal spRoot As String, ByVal spSub As String) As String
    Return spRoot & "/" & spSub
End Function

Viss būtu labi, bet gadījumos , ja spRoot jau beidzas ar “/” simbolu, izveidosies neglīta taka ar divām slīpsvītrām pēc kārtas. Tāpēc kārtīgāks programmētājs cenšas notīrīt liekos “/” no Root beigām:

Function getFullPath(ByVal spRoot As String, ByVal spSub As String) As String
    'korekti ir nemainīt parametra vērtību funkcijā - nokopēsim
    Dim sRoot As String = spRoot

    'kamēr vien rinda beidzas ar /, dzēšam tos nost
    While Right(sRoot, 1) = "/"
        'ja būs atkārtoti / beigās, notīrīs visus
        sRoot = Left(spRoot, Len(sRoot) - 1)
    End While

    Return sRoot & "/" & spSub
End Function

Ir jau labi, tikai darba daudz. Un vispār – kāpēc tā darīt, ja ir izveidota String klases funkcija TrimEnd. Lietojot to, varam atkal funkcijas būtību ierakstīt vienā rindiņā:

Function getFullPath(ByVal spRoot As String, ByVal spSub As String) As String
    Return spRoot.TrimEnd("/"c) & "/" & spSub
End Function

TrimEnd un TrimStart funkcijām parametros var nodot sarakstu ar dažādiem simboliem, kurus mēģināt nodzēst no rindas beigām. Piemēram, ja gribam pārliecināties, ka apakšdirektorijas daļa spSub patiesībā nav līdzīga šai “../../../../windows/system32/”, varam nodzēst arī punkta simbolus no otrā parametra abu parametru savienojamajiem galiem.

Function getFullPath(ByVal spRoot As String, ByVal spSub As String) As String
    Return spRoot.TrimEnd("/"c, "/"c) & "/" & spSub.TrimStart("."c, "/"c)
End Function

Cīņa par drošu kodu? Nu, tā laikam būs.

P.S. Uzmanīgi, ja kāds no parametriem būs Nothing, funkcija metīs kļūdu!

ASP.NET koda modelis

Šajā rakstā tiek pieņemts, ka lasītājs ir iepazinies ar ievadrakstu par ASP.Net

Šis ir īss izklāsts latviešu valodā no MSDN izklāsta par ASP.Net koda modeli

ASP.Net koda modelis ir viens no pirmajiem soļiem pretim Longhorn paredzēto aplikāciju izstrādes tehnikai, kur aplikācijas izskats tiek nodalīts no koda, kas apstrādā notikumus. Tādējādi “layouts” un “eventi” ir koda ziņā divi dažādi faili, kuru saturs aplikācijas izpildes laikā tiek ar “maģiskās mērces” palīdzību saķibināts vienotā baitu plūsmā.
Būtībā tiek risināta problēma, kuras dēļ, piemēram PHP skriptu gadījumā radīti tādi “template dzinēji” kā Smarty, tikai šeit problēmas risinājums iešūts pašā dzinējā.

ASP.Net lapas pamatā vienmēr būs koda klase System.Web.UI.Page. Šajā klasē implementētas visas darbam pamatā nepieciešamās metodes (piemēram, RenderControl, Validate un citas), pieejamas datu kolekcijas un svarīgākie objekti, kas atbalsta HTTP ideoloģiju(piemēram Request, Response, ViewState), klase realizē pietiekami sarežģītu notikumu modeli (pieejami notikumi, piemēram Init, Load, PreRender, DataBinding un citi)

Sākot veidot jaunu ASP.Net lapu, jāveido klase, kas mantota no Page klases. Tas ir, ja vēlamies izveidot primitīvu webformu uz kuras varētu atrasties viens nerediģējama teksta klucītis, kods ir šāds:

Public Class MyPage
    Inherits Web.UI.Page
    Protected MyLabel As Web.UI.WebControls.Label
End Class

Failu, kas satur šādu klasi, iespējams kompilēt, lietojot parasto Visual Basic.net kompilatoru vbc.exe un rezultātā iegūt *.dll failu, ko dēvē par code-behind bibliotēku.

Viegli pamanīt, ka iepriekš minētajā kodā nav informācijas par to, kāds izskatīsies Label objekts. WebFormas vizualizācija tiek veidota kā atsevišķs fails, kura paplašinājums parasti ir *.aspx. Šāda faila pirmā rinda ir norāde interpretatoram, kur meklēt no kādas klases mantota dotā webforma:

< %@ Page Language="vb" AutoEventWireup="false" Inherits="MyApp.MyPage"%>

Mazliet īpatnēji šajā kontekstā šķiet izmantot terminu mantošana, jo šādi sanāk, ka teksta fails manto koda klasi, bet tā to dēvēt izvēlējušies tehnoloģijas arhitekti. Ir iespējams vienai koda klasei piekārtot vairākas izskata lapas, bet ne otrādi.

Webformas layout fails tiek veidots kā HTML un ASP iezīmju maisījums, lietojot speciālas birkas kontroļu ievietošanai. Mūsu piemērs izskatās šādi (atvainojiet par liekajām atstarpēm birku sākumā, ir nelielas problēmas ar manu publicēšanas sistēmu):

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
  <body>
  <form id="Form1" method="post" runat="server">
    <asp:Label id="MyLabel" runat="server"></asp:Label>
  </form>
  </body>
</HTML>

Šeit svarīgi ir tas, ka asp:Label ID atribūts sakrīt ar MyPage koda klases atribūta nosaukumu. Šādā veidā webformas lietotāja interfeisa kontrole tiek piesaistīta kodam, kas ar to darbojas. Ja ASP.Net lapa tiek veidota ar MS Visual Studio .Net, formas kontroļu saderību ar klases atribūtiem tiek nodrošināta automātiski.

Kas tad notiek tālāk. Rezultātā esam ieguvuši divus atsevišķus failus – mypage.aspx, kur definēts lapas izskats un myapp.dll, kurā implementētas lapas loģika. Izveidojam Internet Information Server pieejamu direktoriju uz cietā diska, piemēram, c:\inetpub\wwwroot\myapp\. Direktorijā ievietojam tikai .aspx failu un izveidojam papildus apakšdirektoriju bin, kurā ievietojam .dll failu.

IIS konfigurācijā izveidojam jaunu aplikāciju direktorijai c:\inetpub\wwwroot\myapp\ (kā to izdarīt).
Šajā brīdī IIS zina, ka visi .aspx faili, kas atrodas šajā direktorijā, turpmāk jāapstrādā ar ASP.Net ISAPI filtru jeb interpretatoru, kas tiks ielādēts no C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\aspnet_isapi.dll (Tā arī tā manis minētā *maģiskā mērce*).

Kad lietotājs pieslēdzas tīmekļa serverim un pieprasa mūsu izstrādāto lapu (http://localhost/myapp/mypage.aspx) , IIS process sevī ielādē (ja vēl nav ielādējis) aspnet_isapi.dll bibliotēku un tai palūdz procesēt lapu mypage.aspx. Interpretators noskaidro, ka mypage.aspx ir mantota no koda klases MyPage, kas atrodama bibliotēkā myapp.dll. Rezultātā IIS procesā tiek ielādēta arī myapp.dll bibliotēka’. Tālāk tiek izpildīts kods, kas galu galā atgriež rezultātu IIS procesam, kurš to nodod atpakaļ lietotājam.

Koda izpildes laikā lapa ‘izdzīvo’ savudzīves ciklu. Lai imitētu Windows formas darba stilu, tiek veiktas daudzas lietotājam nezināmas darbības, piemēram, lapā saliktas datu ievades kontroles, to vērtības atjaunotas uz sākotnējām vai tādām, uz kādām lietotājs tās pamainījis pirms kādas pogas nospiešanas, tiek apstrādāti notikumi kā “nospiesta poga A” vai “nomainījies izvēlētais ieraksts listboxī” un citi.

Skaists un pilnīgs lapas dzīves cikla apraksts atrodams 15Seconds rakstā, bet īsumā, kas notiek:

  • Objektu un kontroļu inicializēšana
  • ViewState datu atjaunošana (par šo stāstīšu nākamajā rakstā)
  • PostBack datu apstrāde- tas, ko lietotājs ir pamainījis formā
  • Tiek pacelti izmaiņu notikumi (onclick, onselectedindexchanged etc)
  • Tiek ģenerēts HTML
  • Tiek ģenerēts jaunais ViewState

Pēc tam lietotājs atkal kaut ko pamaina WebFormā, katra darbība pārsūta datus uz serveri, un atkal notiek šāda pat procedūra. Tādējādi jebkuras pogas nospiešana patiesībā pārsūta datus atkārtoti uz serveri, tur tiek apstrādāts šīs pogas onClick notikums un ģenerēts jauns, droši vien jau citāds HTML kods. Visu tekstu ievades lauku dati saglabājas, jo arī tie tiek pārsūtīti uz serveri, bet, atšķirībā no PHP aplikācijām, izstrādātājam ne obligāti būs jārūpējas par to, lai no POST (vai GET) datiem atjaunotu ievades lauka stāvokli – to izdarīs ASP.net.

ASP.Net – sākumam

Ir tāds bieži dzirdēts vārds ASP.Net, ko katrs saprot pa savam. Cits uzskata, ka tā ir programmēšanas valoda (patiesībā nav vis), cits tajā saskata tālāku attīstību no Microsoft Active Server Pages (un tā arī ir), cits atzīst par nopietni apskatāmu kandidatūru web aplikācijas risinājumiem (līdzvērtīgu PHP vai ColdFusion), cits spēj vien kārtējo reizi nospļaudīties “Ak, tas [pīiii] maikrosofts”.

Iegūstot zināmu darba pieredzi ar ASP.Net tehnoloģiju, esmu sapratis, ka tas patiešām ir sava veida turpinājums “vecajam ASP”, kas veidots tā, lai tīmekļa aplikāciju izstrāde būtu līdzīga Windows aplikāciju izstrādei (kontroles, to dzīves cikls u.c.) Parādījies jēdziens “web forma“, kas tuvināts Windows formām – ir pat izveidoti GUI redaktori, ar kuriem strādājot nav jūtama liela atšķirība starp WebFormu un Windows formu (ne tikai VisualStudio.net, bet arī, piemēram SharpDevelop) .

ASP.Net web formu izstrādātājs dizaina laikā teorētiski darbojas tikai ar kontroļu izkārtojumu uz “formas”, nav jāuztraucas par to, kāds HTML kods tiks ģenerēts šai webformai (tas tiešām tiek ģenerēts atkarībā no lietotāja aģenta, kas lapu pieprasījis – piemēram, mobilās ierīces saņem pavisam citādu kodu nekā FireFox pārlūks. Savukārt MS Internet Explorer saņem kodu, kas “pārbāzts” ar Microsoft papildinājumiem HTML4.0 standartam. Manuprāt, korekts risinājums, ja citi pārlūki netiek ar to traucēti).

Pateicoties pietiekami interesantajai arhitektūrai, ASP.Net ir labi piemērots mērogojamu aplikāciju izstrādei, savukārt tas, ka tehniski sīkumi ir paslēpti pašā ASP.Net infrastruktūrā, ļauj vairāk koncentrēties uz biznesa loģikas implementēšanu. No otras puses, tieši sīkumu paslēpšana ļauj darboties diletantiski, kā rezultātā rodas jautājumi kā, piemēram “Kā es varu norādīt faila upload dialogam, kurus faila tipus drīkst izvēlēties?”

Protams, jāsaprot arī tos, kuri par šo tehnoloģiju izsakās nievājoši – tās sarežģītības dēļ rodas pārpratumi, kuru pamatā visbiežāk tas, ka kāds rīks tiek lietots citiem mērķiem nekā tas sākotnēji paredzēts. Tādēļ, izstrādājot ASP.Net aplikācijas, ir tomēr jāizprot WWW būtība, HTTP protokola īpatnības (sesijas, datu pārsūtīšana u.c.) un rūpīgi jālasa dokumentācija, kuras gan šajā gadījumā netrūkst.

Cits bieži minēts arguments ir, ka ASP.Net ģenerē W3C standartiem neatbilstošu HTML kodu. Tā ir taisnība, taču, cik zināms ASP.Net 2.0 šī vaina būs labota un tiks ģenerēts tīrs XHTML. Tik jājautā, vai tas būs efektīvi, ja zinām, ka Microsoft parasti neaizraujas ar kompakta HTML ģenerēšanu?

ASP.Net darbojas uz serveriem, kur instalēts .Net Framework un lieliski sadarbojas ar Internet Information Server. IIS nebūt nav vienīgā iespēja, ASP.Net var darbināt arī caur Apache 2.0, ja tam papildus tiek izmantots Cassini modulis vai Mono projekta ietvaros izstrādāto Apache moduli mod_mono. Strādājot ar IIS, protams, ir ērti pieejama visu veidu lietotāju autorizācija (Basic/Windows/Digest), kas citu webserveru gadījumā, šķiet, varētu nebūt tik vienkārša.

Par RSS

Šodien pamanīju pavisam jauku lietu un nodomāju – kāpēc gan tā nedarīt?

Tātad, špikojam no Microsoft Software Developer Network mājas lapas RSS plūsmām. Katrā XML failā, kas tiek piedāvāts kā plūsma (piemēram, šis), ir iekļauta XSL direktīva, kas viņu gadījumā izskatās šādi:

<?xml-stylesheet type='text/xsl' href='rsspretty.xsl' version='1.0'?>

Savukārt pats rsspretty.xsl fails ņem un transformē RSS plūsmas datus lietotājam baudāmā izskatā: pieliek dokumenta sākumu, kurā pastāsta, kas tā ir par plūsmu un kāds ir tās saturs, pēc tam izdrukā plūsmā esošos rakstus secīgi, parādot description, autoru, publicēšanas datumu.

Rezultātā, ja RSS plūsmas fails tiek atvērts ar vizuālu tīmekļa pārlūku, kas spēj rādīt XSL transformētus XML failus, parādās gandrīz normāla lapa. Ja RSS plūsmas failu atver nevizuāls aģents – tas darīs, kas nu tam jādara.

Modulus

Rakstot par nesmukajiem datumiem, biju izmantojis šādu izteiksmi: $sundayEveningSpan = ((($now["wday"]-1)%7)*24*60*60);. Rezultātā mainīgais $sundayEveningSpan tiek uzstādīts (tam vajadzētu tikt uzstādītam) uz sekunžu skaitu starp šodienas plkst. 00:00 un šīs pirmdienas 00:00. Vārdu sakot, tiek aprēķināts, cik sekundes pagājušas šajā nedēļā.

Kā redzams, izteiksmē parādās operators %, kas atgriež izteiksmes $now["wday"]-1 vērtību pēc moduļa 7. Tas darīts tāpēc, ka PHP funkcijas GetDate() atgrieztajā masīvā wday lauks atgriež “Numeric representation of the day of the week: 0 (for Sunday) through 6 (for Saturday)”. Tātad svētdiena ir 0-tā diena, sestiena ir 6. diena. Tā kā man (un arī daudziem citiem latviešiem) nedēļa sākas ar pirmdienu, no dienas numura bija jāatņem vieninieks pēc moduļa 7, lai iegūtu pareizo piekārtojumu: pirmdienai 0, svētdienai (0-1) mod 7, kas vienāds ar 6 klasiskajā moduļu aritmētikā.

Tomēr izrādījās, ka šādi mana funkcija darbojas nekorekti. Izpētot tuvāk, secināju, ka PHP valodai arī ir savas īpatnības – modulis no negatīva skaitļa ir negatīvs. Tas arī oficiāli ir dokumentēts modulus operatora aprakstā. Tādēļ -1 % 7==-6, nevis 6, kā bija sagaidāms.

Lai novērstu šīs PHP īpatnības sekas, aizstājam -1 ar +6 (kas ir kongruenti pēc moduļa 7) un turpmāk nedēļas sākumu aprēķinām šādi: $sundayEveningSpan = ((($now["wday"]+6)%7)*24*60*60);.

Kā rāda testi, šāda pat problēma novērojama arī Visual Basic.Net un, ļoti iespējams, arī citās .Net valodās. Kā sacīts MSDN, “The result of x Mod y is the value produced by x – (x \ y) * y”. Tas nozīmē, ka gadījumā, kad pats x ir negatīvs, arī atgrieztais rezultāts būs negatīvs.

Tā, lūk. Atliek vien secināt, ka programmēšanas valodu veidotājiem ir vienalga, vai tiek saglabāta multiplikatīvās grupas pēc moduļa N un gredzena īpašība, kas tik svarīga ir matemātiķiem. Un tas, protams, ir tikai normāli – ja vajadzēs, vienmēr varēsim pieskaitīt 6 tā vietā, lai atņemtu 1.

Nesmukie laiki

Šodien mani kaunināja, ka es nesmuki datumus rakstot. Ka “02.06.2005. 07:21:22” patiesībā cilvēkam neko neizsaka. Cita lieta, ja paziņotu vienkārši “vakar” – tas jau daudz labāk izklausoties. Taisnība jau arī ir – cilvēks nav dators, cilvēkam BIOS-ā nav iešūts kalendārs.

Tāpēc funkcija, kas UNIX timestampam atgriež elementāros vārdiskos aprakstus. Tā sacīt, izkrāso pati, ja gribi citādi:

function TextInfo($spTime){
    $todayMidNight = mktime(0, 0, 0, date("m"), date("d"), date("y"));
    $yesterdayMidNight = $todayMidNight - 24*60*60;


    $nowTime = mktime();        //brīdis, pret kuru rēķinās
    $now = getdate($nowTime); //aprēķina brīža dati sadalīti pa "detaļām"

    //timestamp pagājušās svētdienas beigām
    $sundayEveningSpan = ((($now["wday"]+6)%7)*24*60*60); 

    $then = getdate($spTime); //laiks, pret kuru rēķinās sadalīts pa "detaļām"

    //vai sakrīt ar šodienu
    if ( ($now["mday"]==$then["mday"]) && 
        ($now["mon"]==$then["mon"]) && 
        ($now["year"]==$then["year"])) {
    if ($then["hours"] &lt; 6) return "šonakt"; //noticis šajā naktī
    if ($then["hours"] &lt; 10) return "šorīt"; //noticis rīta pusē
    return "šodien";  //citādi vienkārši informējam, ka šodien 
    }

    //ir noticis vakardien
    if ( ($todayMidNight &gt; $spTime) && ($spTime >= $yesterdayMidNight)) {
    if ( abs( $now["hours"] - $then["hours"] ) &lt; 3) return "vakar ap šo laiku";  //ja stundas līdzīgas pašreizējām
    if ($then["hours"] &lt; 6) return "vakar naktī"; //noticis vakar no rīta agri
    if ($then["hours"] &lt; 10) return "vakar no rīta"; 
    if ($then["hours"] &lt; 12) return "vakar priekšpusdienā"; 
    return "vakar";
    }

    //ir noticis šonedēļ
    if (( $todayMidNight > $spTime) &&  ($todayMidNight- $sundayEveningSpan < $spTime )) {
    return "šonedēļ";
    }

    //ir noticis pagājušajā nedēļā
    if (( $todayMidNight- $sundayEveningSpan > $spTime) && 
        ( $todayMidNight- $sundayEveningSpan - 24*60*60*7 &lt; $spTime )) {
    return "pagājušajā nedēļā";
    }

    //ja senāk par pagājušo nedēļu, vai nav senāk par gadu?
    $tSpan = $nowTime - $spTime;
    if (floor($tspan /(24*60*60*365) )>0) return " pirms vairāk nekā gada";

    //aizmirstam īsos un garos mēnešus
    $tSpanMonths = floor( $tSpan / (24*60*60*30) );     
    if ($tSpanMonths &gt; 0) {
    //starp 1 un 2 mēnešiem
    if ($tSpanMonths == 1) {
        $days = floor(($tSpan-24*60*60*30)/(24*60*60));
        if ( ($days!=1) && ($days!=21) && ($days!=31)) 
        return "pirms mēneša un ".$days." dienām";
                            Else
        return "pirms mēneša un ".$days." dienas";
        }
    return "pirms vairāk nekā ".$tSpanMonths." mēnešiem";
    } else {
    //jāskatās, vai bija tas pats mēnesis, vai iepriekšējais
    if ($then["mon"]==$now["mon"]) return "šajā mēnesī"; else return "pagājušajā mēnesī";
    }
}

Parametros Unix timestamps.

Daudzrindiņu links

Ja jūs zinātu, kāaaaa man ir apnicis cīnīties ar to, ka e-pasta klientprogramma neatpazīst garas www adreses. Piemēram, vēstules tekstā ir adrese, kuras pirmo rindiņu MSOutlook rāda zilu un pasvītrotu (jo atpazīst kā adresi), bet otro rāda melnu un nepazīst:

http://www.google.lv/search?sourceid=navclient&ie=UTF-8&rls=GGLD,GGLD:2004-4
7,GGLD:en&q=photo+workshop

Lai apskatītos, kas tur iekšā, pārkopēju adresi uz Notepad, tur skrienu cauri rindām, velku visu kopā… garlaicīgi.

Cīnījos ar to, izveidojot bookmarkletu, kas dara šādi:

var s,w=window;
s=w.clipboardData.getData("Text").replace(/\r|\n|\s/g,"");
if(s.indexOf("http://")>-1) 
  w.location.href=s

Tas ir, paņemam no Windows starpliktuves (clipboard) datus, kas tur šobrīd ir, izdzēšam visas rindas beigu pazīmes, ja palikušais teksts satur http://, tad mēģinām navigēt uz šo lapu. Un viss.

Savelkot to visu kopā, pie favorītiem jāpieliek šāds links:
javascript:(function x(){var s,w=window;s=w.clipboardData.getData(“Text”).replace(/\r|\n|\s/g,””);if(s.indexOf(“http://”)>-1) w.location.href=s});x()
Internet Explorer gan dusmosies, ka tas neesot atpazīts protokols, bet nu ko lai dara… bookmarkleti vispār ir “hacks”.

Ja gribas šo lietiņu izmantot, var doties uz manu bookmarkletu lapu un darīt, kā tur rakstīts.