DataObject.php 172 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911
  1. <?php
  2. /**
  3. * Object Based Database Query Builder and data store
  4. *
  5. * For PHP versions 4,5 and 6
  6. *
  7. * LICENSE: This source file is subject to version 3.01 of the PHP license
  8. * that is available through the world-wide-web at the following URI:
  9. * http://www.php.net/license/3_01.txt. If you did not receive a copy of
  10. * the PHP License and are unable to obtain it through the web, please
  11. * send a note to license@php.net so we can mail you a copy immediately.
  12. *
  13. * @category Database
  14. * @package DB_DataObject
  15. * @author Alan Knowles <alan@akbkhome.com>
  16. * @copyright 1997-2006 The PHP Group
  17. * @license http://www.php.net/license/3_01.txt PHP License 3.01
  18. * @version CVS: $Id: DataObject.php 320069 2011-11-28 04:34:08Z alan_k $
  19. * @link http://pear.php.net/package/DB_DataObject
  20. */
  21. /* ===========================================================================
  22. *
  23. * !!!!!!!!!!!!! W A R N I N G !!!!!!!!!!!
  24. *
  25. * THIS MAY SEGFAULT PHP IF YOU ARE USING THE ZEND OPTIMIZER (to fix it,
  26. * just add "define('DB_DATAOBJECT_NO_OVERLOAD',true);" before you include
  27. * this file. reducing the optimization level may also solve the segfault.
  28. * ===========================================================================
  29. */
  30. /**
  31. * The main "DB_DataObject" class is really a base class for your own tables classes
  32. *
  33. * // Set up the class by creating an ini file (refer to the manual for more details
  34. * [DB_DataObject]
  35. * database = mysql:/username:password@host/database
  36. * schema_location = /home/myapplication/database
  37. * class_location = /home/myapplication/DBTables/
  38. * clase_prefix = DBTables_
  39. *
  40. *
  41. * //Start and initialize...................... - dont forget the &
  42. * $config = parse_ini_file('example.ini',true);
  43. * $options = &PEAR::getStaticProperty('DB_DataObject','options');
  44. * $options = $config['DB_DataObject'];
  45. *
  46. * // example of a class (that does not use the 'auto generated tables data')
  47. * class mytable extends DB_DataObject {
  48. * // mandatory - set the table
  49. * var $_database_dsn = "mysql://username:password@localhost/database";
  50. * var $__table = "mytable";
  51. * function table() {
  52. * return array(
  53. * 'id' => 1, // integer or number
  54. * 'name' => 2, // string
  55. * );
  56. * }
  57. * function keys() {
  58. * return array('id');
  59. * }
  60. * }
  61. *
  62. * // use in the application
  63. *
  64. *
  65. * Simple get one row
  66. *
  67. * $instance = new mytable;
  68. * $instance->get("id",12);
  69. * echo $instance->somedata;
  70. *
  71. *
  72. * Get multiple rows
  73. *
  74. * $instance = new mytable;
  75. * $instance->whereAdd("ID > 12");
  76. * $instance->whereAdd("ID < 14");
  77. * $instance->find();
  78. * while ($instance->fetch()) {
  79. * echo $instance->somedata;
  80. * }
  81. /**
  82. * Needed classes
  83. * - we use getStaticProperty from PEAR pretty extensively (cant remove it ATM)
  84. */
  85. require_once 'PEAR.php';
  86. /**
  87. * We are duping fetchmode constants to be compatible with
  88. * both DB and MDB2
  89. */
  90. define('DB_DATAOBJECT_FETCHMODE_ORDERED',1);
  91. define('DB_DATAOBJECT_FETCHMODE_ASSOC',2);
  92. /**
  93. * these are constants for the get_table array
  94. * user to determine what type of escaping is required around the object vars.
  95. */
  96. define('DB_DATAOBJECT_INT', 1); // does not require ''
  97. define('DB_DATAOBJECT_STR', 2); // requires ''
  98. define('DB_DATAOBJECT_DATE', 4); // is date #TODO
  99. define('DB_DATAOBJECT_TIME', 8); // is time #TODO
  100. define('DB_DATAOBJECT_BOOL', 16); // is boolean #TODO
  101. define('DB_DATAOBJECT_TXT', 32); // is long text #TODO
  102. define('DB_DATAOBJECT_BLOB', 64); // is blob type
  103. define('DB_DATAOBJECT_NOTNULL', 128); // not null col.
  104. define('DB_DATAOBJECT_MYSQLTIMESTAMP' , 256); // mysql timestamps (ignored by update/insert)
  105. /*
  106. * Define this before you include DataObjects.php to disable overload - if it segfaults due to Zend optimizer..
  107. */
  108. //define('DB_DATAOBJECT_NO_OVERLOAD',true)
  109. /**
  110. * Theses are the standard error codes, most methods will fail silently - and return false
  111. * to access the error message either use $table->_lastError
  112. * or $last_error = PEAR::getStaticProperty('DB_DataObject','lastError');
  113. * the code is $last_error->code, and the message is $last_error->message (a standard PEAR error)
  114. */
  115. define('DB_DATAOBJECT_ERROR_INVALIDARGS', -1); // wrong args to function
  116. define('DB_DATAOBJECT_ERROR_NODATA', -2); // no data available
  117. define('DB_DATAOBJECT_ERROR_INVALIDCONFIG', -3); // something wrong with the config
  118. define('DB_DATAOBJECT_ERROR_NOCLASS', -4); // no class exists
  119. define('DB_DATAOBJECT_ERROR_INVALID_CALL' ,-7); // overlad getter/setter failure
  120. /**
  121. * Used in methods like delete() and count() to specify that the method should
  122. * build the condition only out of the whereAdd's and not the object parameters.
  123. */
  124. define('DB_DATAOBJECT_WHEREADD_ONLY', true);
  125. /**
  126. *
  127. * storage for connection and result objects,
  128. * it is done this way so that print_r()'ing the is smaller, and
  129. * it reduces the memory size of the object.
  130. * -- future versions may use $this->_connection = & PEAR object..
  131. * although will need speed tests to see how this affects it.
  132. * - includes sub arrays
  133. * - connections = md5 sum mapp to pear db object
  134. * - results = [id] => map to pear db object
  135. * - resultseq = sequence id for results & results field
  136. * - resultfields = [id] => list of fields return from query (for use with toArray())
  137. * - ini = mapping of database to ini file results
  138. * - links = mapping of database to links file
  139. * - lasterror = pear error objects for last error event.
  140. * - config = aliased view of PEAR::getStaticPropery('DB_DataObject','options') * done for performance.
  141. * - array of loaded classes by autoload method - to stop it doing file access request over and over again!
  142. */
  143. $GLOBALS['_DB_DATAOBJECT']['RESULTS'] = array();
  144. $GLOBALS['_DB_DATAOBJECT']['RESULTSEQ'] = 1;
  145. $GLOBALS['_DB_DATAOBJECT']['RESULTFIELDS'] = array();
  146. $GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'] = array();
  147. $GLOBALS['_DB_DATAOBJECT']['INI'] = array();
  148. $GLOBALS['_DB_DATAOBJECT']['LINKS'] = array();
  149. $GLOBALS['_DB_DATAOBJECT']['SEQUENCE'] = array();
  150. $GLOBALS['_DB_DATAOBJECT']['LASTERROR'] = null;
  151. $GLOBALS['_DB_DATAOBJECT']['CONFIG'] = array();
  152. $GLOBALS['_DB_DATAOBJECT']['CACHE'] = array();
  153. $GLOBALS['_DB_DATAOBJECT']['OVERLOADED'] = false;
  154. $GLOBALS['_DB_DATAOBJECT']['QUERYENDTIME'] = 0;
  155. // this will be horrifically slow!!!!
  156. // these two are BC/FC handlers for call in PHP4/5
  157. if (!defined('DB_DATAOBJECT_NO_OVERLOAD')) {
  158. class DB_DataObject_Overload
  159. {
  160. function __call($method,$args)
  161. {
  162. $return = null;
  163. $this->_call($method,$args,$return);
  164. return $return;
  165. }
  166. function __sleep()
  167. {
  168. return array_keys(get_object_vars($this)) ;
  169. }
  170. }
  171. } else {
  172. class DB_DataObject_Overload {}
  173. }
  174. /*
  175. *
  176. * @package DB_DataObject
  177. * @author Alan Knowles <alan@akbkhome.com>
  178. * @since PHP 4.0
  179. */
  180. class DB_DataObject extends DB_DataObject_Overload
  181. {
  182. /**
  183. * The Version - use this to check feature changes
  184. *
  185. * @access private
  186. * @var string
  187. */
  188. var $_DB_DataObject_version = "1.11.3";
  189. /**
  190. * The Database table (used by table extends)
  191. *
  192. * @access private
  193. * @var string
  194. */
  195. var $__table = ''; // database table
  196. /**
  197. * The Number of rows returned from a query
  198. *
  199. * @access public
  200. * @var int
  201. */
  202. var $N = 0; // Number of rows returned from a query
  203. /* ============================================================= */
  204. /* Major Public Methods */
  205. /* (designed to be optionally then called with parent::method()) */
  206. /* ============================================================= */
  207. /**
  208. * Get a result using key, value.
  209. *
  210. * for example
  211. * $object->get("ID",1234);
  212. * Returns Number of rows located (usually 1) for success,
  213. * and puts all the table columns into this classes variables
  214. *
  215. * see the fetch example on how to extend this.
  216. *
  217. * if no value is entered, it is assumed that $key is a value
  218. * and get will then use the first key in keys()
  219. * to obtain the key.
  220. *
  221. * @param string $k column
  222. * @param string $v value
  223. * @access public
  224. * @return int No. of rows
  225. */
  226. function get($k = null, $v = null)
  227. {
  228. global $_DB_DATAOBJECT;
  229. if (empty($_DB_DATAOBJECT['CONFIG'])) {
  230. DB_DataObject::_loadConfig();
  231. }
  232. $keys = array();
  233. if ($v === null) {
  234. $v = $k;
  235. $keys = $this->keys();
  236. if (!$keys) {
  237. $this->raiseError("No Keys available for {$this->tableName()}", DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  238. return false;
  239. }
  240. $k = $keys[0];
  241. }
  242. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  243. $this->debug("$k $v " .print_r($keys,true), "GET");
  244. }
  245. if ($v === null) {
  246. $this->raiseError("No Value specified for get", DB_DATAOBJECT_ERROR_INVALIDARGS);
  247. return false;
  248. }
  249. $this->$k = $v;
  250. return $this->find(1);
  251. }
  252. /**
  253. * Get the value of the primary id
  254. *
  255. * While I normally use 'id' as the PRIMARY KEY value, some database use
  256. * {table}_id as the column name.
  257. *
  258. * To save a bit of typing,
  259. *
  260. * $id = $do->pid();
  261. *
  262. * @return the id
  263. */
  264. function pid()
  265. {
  266. $keys = $this->keys();
  267. if (!$keys) {
  268. $this->raiseError("No Keys available for {$this->tableName()}",
  269. DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  270. return false;
  271. }
  272. $k = $keys[0];
  273. if (empty($this->$k)) { // we do not
  274. $this->raiseError("pid() called on Object where primary key value not available",
  275. DB_DATAOBJECT_ERROR_NODATA);
  276. return false;
  277. }
  278. return $this->$k;
  279. }
  280. /**
  281. * build the basic select query.
  282. *
  283. * @access private
  284. */
  285. function _build_select()
  286. {
  287. global $_DB_DATAOBJECT;
  288. $quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
  289. if ($quoteIdentifiers) {
  290. $this->_connect();
  291. $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  292. }
  293. $tn = ($quoteIdentifiers ? $DB->quoteIdentifier($this->tableName()) : $this->tableName()) ;
  294. if (!empty($this->_query['derive_table']) && !empty($this->_query['derive_select']) ) {
  295. // this is a derived select..
  296. // not much support in the api yet..
  297. $sql = 'SELECT ' .
  298. $this->_query['derive_select']
  299. .' FROM ( SELECT'.
  300. $this->_query['data_select'] . " \n" .
  301. " FROM $tn " . $this->_query['useindex'] . " \n" .
  302. $this->_join . " \n" .
  303. $this->_query['condition'] . " \n" .
  304. $this->_query['group_by'] . " \n" .
  305. $this->_query['having'] . " \n" .
  306. ') ' . $this->_query['derive_table'];
  307. return $sql;
  308. }
  309. $sql = 'SELECT ' .
  310. $this->_query['data_select'] . " \n" .
  311. " FROM $tn " . $this->_query['useindex'] . " \n" .
  312. $this->_join . " \n" .
  313. $this->_query['condition'] . " \n" .
  314. $this->_query['group_by'] . " \n" .
  315. $this->_query['having'] . " \n";
  316. return $sql;
  317. }
  318. /**
  319. * find results, either normal or crosstable
  320. *
  321. * for example
  322. *
  323. * $object = new mytable();
  324. * $object->ID = 1;
  325. * $object->find();
  326. *
  327. *
  328. * will set $object->N to number of rows, and expects next command to fetch rows
  329. * will return $object->N
  330. *
  331. * if an error occurs $object->N will be set to false and return value will also be false;
  332. * if numRows is not supported it will
  333. *
  334. *
  335. * @param boolean $n Fetch first result
  336. * @access public
  337. * @return mixed (number of rows returned, or true if numRows fetching is not supported)
  338. */
  339. function find($n = false)
  340. {
  341. global $_DB_DATAOBJECT;
  342. if ($this->_query === false) {
  343. $this->raiseError(
  344. "You cannot do two queries on the same object (copy it before finding)",
  345. DB_DATAOBJECT_ERROR_INVALIDARGS);
  346. return false;
  347. }
  348. if (empty($_DB_DATAOBJECT['CONFIG'])) {
  349. DB_DataObject::_loadConfig();
  350. }
  351. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  352. $this->debug($n, "find",1);
  353. }
  354. if (!$this->__table) {
  355. // xdebug can backtrace this!
  356. trigger_error("NO \$__table SPECIFIED in class definition",E_USER_ERROR);
  357. }
  358. $this->N = 0;
  359. $query_before = $this->_query;
  360. $this->_build_condition($this->table()) ;
  361. $this->_connect();
  362. $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  363. $sql = $this->_build_select();
  364. foreach ($this->_query['unions'] as $union_ar) {
  365. $sql .= $union_ar[1] . $union_ar[0]->_build_select() . " \n";
  366. }
  367. $sql .= $this->_query['order_by'] . " \n";
  368. /* We are checking for method modifyLimitQuery as it is PEAR DB specific */
  369. if ((!isset($_DB_DATAOBJECT['CONFIG']['db_driver'])) ||
  370. ($_DB_DATAOBJECT['CONFIG']['db_driver'] == 'DB')) {
  371. /* PEAR DB specific */
  372. if (isset($this->_query['limit_start']) && strlen($this->_query['limit_start'] . $this->_query['limit_count'])) {
  373. $sql = $DB->modifyLimitQuery($sql,$this->_query['limit_start'], $this->_query['limit_count']);
  374. }
  375. } else {
  376. /* theoretically MDB2! */
  377. if (isset($this->_query['limit_start']) && strlen($this->_query['limit_start'] . $this->_query['limit_count'])) {
  378. $DB->setLimit($this->_query['limit_count'],$this->_query['limit_start']);
  379. }
  380. }
  381. $err = $this->_query($sql);
  382. if (is_a($err,'PEAR_Error')) {
  383. return false;
  384. }
  385. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  386. $this->debug("CHECK autofetchd $n", "find", 1);
  387. }
  388. // find(true)
  389. $ret = $this->N;
  390. if (!$ret && !empty($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid])) {
  391. // clear up memory if nothing found!?
  392. unset($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid]);
  393. }
  394. if ($n && $this->N > 0 ) {
  395. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  396. $this->debug("ABOUT TO AUTOFETCH", "find", 1);
  397. }
  398. $fs = $this->fetch();
  399. // if fetch returns false (eg. failed), then the backend doesnt support numRows (eg. ret=true)
  400. // - hence find() also returns false..
  401. $ret = ($ret === true) ? $fs : $ret;
  402. }
  403. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  404. $this->debug("DONE", "find", 1);
  405. }
  406. $this->_query = $query_before;
  407. return $ret;
  408. }
  409. /**
  410. * fetches next row into this objects var's
  411. *
  412. * returns 1 on success 0 on failure
  413. *
  414. *
  415. *
  416. * Example
  417. * $object = new mytable();
  418. * $object->name = "fred";
  419. * $object->find();
  420. * $store = array();
  421. * while ($object->fetch()) {
  422. * echo $this->ID;
  423. * $store[] = $object; // builds an array of object lines.
  424. * }
  425. *
  426. * to add features to a fetch
  427. * function fetch () {
  428. * $ret = parent::fetch();
  429. * $this->date_formated = date('dmY',$this->date);
  430. * return $ret;
  431. * }
  432. *
  433. * @access public
  434. * @return boolean on success
  435. */
  436. function fetch()
  437. {
  438. global $_DB_DATAOBJECT;
  439. if (empty($_DB_DATAOBJECT['CONFIG'])) {
  440. DB_DataObject::_loadConfig();
  441. }
  442. if (empty($this->N)) {
  443. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  444. $this->debug("No data returned from FIND (eg. N is 0)","FETCH", 3);
  445. }
  446. return false;
  447. }
  448. if (empty($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid]) ||
  449. !is_object($result = $_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid]))
  450. {
  451. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  452. $this->debug('fetched on object after fetch completed (no results found)');
  453. }
  454. return false;
  455. }
  456. $array = $result->fetchRow(DB_DATAOBJECT_FETCHMODE_ASSOC);
  457. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  458. $this->debug(serialize($array),"FETCH");
  459. }
  460. // fetched after last row..
  461. if ($array === null) {
  462. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  463. $t= explode(' ',microtime());
  464. $this->debug("Last Data Fetch'ed after " .
  465. ($t[0]+$t[1]- $_DB_DATAOBJECT['QUERYENDTIME'] ) .
  466. " seconds",
  467. "FETCH", 1);
  468. }
  469. // reduce the memory usage a bit... (but leave the id in, so count() works ok on it)
  470. unset($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid]);
  471. // we need to keep a copy of resultfields locally so toArray() still works
  472. // however we dont want to keep it in the global cache..
  473. if (!empty($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid])) {
  474. $this->_resultFields = $_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid];
  475. unset($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid]);
  476. }
  477. // this is probably end of data!!
  478. //DB_DataObject::raiseError("fetch: no data returned", DB_DATAOBJECT_ERROR_NODATA);
  479. return false;
  480. }
  481. // make sure resultFields is always empty..
  482. $this->_resultFields = false;
  483. if (!isset($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid])) {
  484. // note: we dont declare this to keep the print_r size down.
  485. $_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid]= array_flip(array_keys($array));
  486. }
  487. $replace = array('.', ' ');
  488. foreach($array as $k=>$v) {
  489. // use strpos as str_replace is slow.
  490. $kk = (strpos($k, '.') === false && strpos($k, ' ') === false) ?
  491. $k : str_replace($replace, '_', $k);
  492. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  493. $this->debug("$kk = ". $array[$k], "fetchrow LINE", 3);
  494. }
  495. $this->$kk = $array[$k];
  496. }
  497. // set link flag
  498. $this->_link_loaded=false;
  499. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  500. $this->debug("{$this->tableName()} DONE", "fetchrow",2);
  501. }
  502. if (($this->_query !== false) && empty($_DB_DATAOBJECT['CONFIG']['keep_query_after_fetch'])) {
  503. $this->_query = false;
  504. }
  505. return true;
  506. }
  507. /**
  508. * fetches all results as an array,
  509. *
  510. * return format is dependant on args.
  511. * if selectAdd() has not been called on the object, then it will add the correct columns to the query.
  512. *
  513. * A) Array of values (eg. a list of 'id')
  514. *
  515. * $x = DB_DataObject::factory('mytable');
  516. * $x->whereAdd('something = 1')
  517. * $ar = $x->fetchAll('id');
  518. * -- returns array(1,2,3,4,5)
  519. *
  520. * B) Array of values (not from table)
  521. *
  522. * $x = DB_DataObject::factory('mytable');
  523. * $x->whereAdd('something = 1');
  524. * $x->selectAdd();
  525. * $x->selectAdd('distinct(group_id) as group_id');
  526. * $ar = $x->fetchAll('group_id');
  527. * -- returns array(1,2,3,4,5)
  528. * *
  529. * C) A key=>value associative array
  530. *
  531. * $x = DB_DataObject::factory('mytable');
  532. * $x->whereAdd('something = 1')
  533. * $ar = $x->fetchAll('id','name');
  534. * -- returns array(1=>'fred',2=>'blogs',3=> .......
  535. *
  536. * D) array of objects
  537. * $x = DB_DataObject::factory('mytable');
  538. * $x->whereAdd('something = 1');
  539. * $ar = $x->fetchAll();
  540. *
  541. * E) array of arrays (for example)
  542. * $x = DB_DataObject::factory('mytable');
  543. * $x->whereAdd('something = 1');
  544. * $ar = $x->fetchAll(false,false,'toArray');
  545. *
  546. *
  547. * @param string|false $k key
  548. * @param string|false $v value
  549. * @param string|false $method method to call on each result to get array value (eg. 'toArray')
  550. * @access public
  551. * @return array format dependant on arguments, may be empty
  552. */
  553. function fetchAll($k= false, $v = false, $method = false)
  554. {
  555. // should it even do this!!!?!?
  556. if ($k !== false &&
  557. ( // only do this is we have not been explicit..
  558. empty($this->_query['data_select']) ||
  559. ($this->_query['data_select'] == '*')
  560. )
  561. ) {
  562. $this->selectAdd();
  563. $this->selectAdd($k);
  564. if ($v !== false) {
  565. $this->selectAdd($v);
  566. }
  567. }
  568. $this->find();
  569. $ret = array();
  570. while ($this->fetch()) {
  571. if ($v !== false) {
  572. $ret[$this->$k] = $this->$v;
  573. continue;
  574. }
  575. $ret[] = $k === false ?
  576. ($method == false ? clone($this) : $this->$method())
  577. : $this->$k;
  578. }
  579. return $ret;
  580. }
  581. /**
  582. * Adds a condition to the WHERE statement, defaults to AND
  583. *
  584. * $object->whereAdd(); //reset or cleaer ewhwer
  585. * $object->whereAdd("ID > 20");
  586. * $object->whereAdd("age > 20","OR");
  587. *
  588. * @param string $cond condition
  589. * @param string $logic optional logic "OR" (defaults to "AND")
  590. * @access public
  591. * @return string|PEAR::Error - previous condition or Error when invalid args found
  592. */
  593. function whereAdd($cond = false, $logic = 'AND')
  594. {
  595. // for PHP5.2.3 - there is a bug with setting array properties of an object.
  596. $_query = $this->_query;
  597. if (!isset($this->_query) || ($_query === false)) {
  598. return $this->raiseError(
  599. "You cannot do two queries on the same object (clone it before finding)",
  600. DB_DATAOBJECT_ERROR_INVALIDARGS);
  601. }
  602. if ($cond === false) {
  603. $r = $this->_query['condition'];
  604. $_query['condition'] = '';
  605. $this->_query = $_query;
  606. return preg_replace('/^\s+WHERE\s+/','',$r);
  607. }
  608. // check input...= 0 or ' ' == error!
  609. if (!trim($cond)) {
  610. return $this->raiseError("WhereAdd: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
  611. }
  612. $r = $_query['condition'];
  613. if ($_query['condition']) {
  614. $_query['condition'] .= " {$logic} ( {$cond} )";
  615. $this->_query = $_query;
  616. return $r;
  617. }
  618. $_query['condition'] = " WHERE ( {$cond} ) ";
  619. $this->_query = $_query;
  620. return $r;
  621. }
  622. /**
  623. * Adds a 'IN' condition to the WHERE statement
  624. *
  625. * $object->whereAddIn('id', $array, 'int'); //minimal usage
  626. * $object->whereAddIn('price', $array, 'float', 'OR'); // cast to float, and call whereAdd with 'OR'
  627. * $object->whereAddIn('name', $array, 'string'); // quote strings
  628. *
  629. * @param string $key key column to match
  630. * @param array $list list of values to match
  631. * @param string $type string|int|integer|float|bool cast to type.
  632. * @param string $logic optional logic to call whereAdd with eg. "OR" (defaults to "AND")
  633. * @access public
  634. * @return string|PEAR::Error - previous condition or Error when invalid args found
  635. */
  636. function whereAddIn($key, $list, $type, $logic = 'AND')
  637. {
  638. $not = '';
  639. if ($key[0] == '!') {
  640. $not = 'NOT ';
  641. $key = substr($key, 1);
  642. }
  643. // fix type for short entry.
  644. $type = $type == 'int' ? 'integer' : $type;
  645. if ($type == 'string') {
  646. $this->_connect();
  647. }
  648. $ar = array();
  649. foreach($list as $k) {
  650. settype($k, $type);
  651. $ar[] = $type == 'string' ? $this->_quote($k) : $k;
  652. }
  653. if (!$ar) {
  654. return $not ? $this->_query['condition'] : $this->whereAdd("1=0");
  655. }
  656. return $this->whereAdd("$key $not IN (". implode(',', $ar). ')', $logic );
  657. }
  658. /**
  659. * Adds a order by condition
  660. *
  661. * $object->orderBy(); //clears order by
  662. * $object->orderBy("ID");
  663. * $object->orderBy("ID,age");
  664. *
  665. * @param string $order Order
  666. * @access public
  667. * @return none|PEAR::Error - invalid args only
  668. */
  669. function orderBy($order = false)
  670. {
  671. if ($this->_query === false) {
  672. $this->raiseError(
  673. "You cannot do two queries on the same object (copy it before finding)",
  674. DB_DATAOBJECT_ERROR_INVALIDARGS);
  675. return false;
  676. }
  677. if ($order === false) {
  678. $this->_query['order_by'] = '';
  679. return;
  680. }
  681. // check input...= 0 or ' ' == error!
  682. if (!trim($order)) {
  683. return $this->raiseError("orderBy: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
  684. }
  685. if (!$this->_query['order_by']) {
  686. $this->_query['order_by'] = " ORDER BY {$order} ";
  687. return;
  688. }
  689. $this->_query['order_by'] .= " , {$order}";
  690. }
  691. /**
  692. * Adds a group by condition
  693. *
  694. * $object->groupBy(); //reset the grouping
  695. * $object->groupBy("ID DESC");
  696. * $object->groupBy("ID,age");
  697. *
  698. * @param string $group Grouping
  699. * @access public
  700. * @return none|PEAR::Error - invalid args only
  701. */
  702. function groupBy($group = false)
  703. {
  704. if ($this->_query === false) {
  705. $this->raiseError(
  706. "You cannot do two queries on the same object (copy it before finding)",
  707. DB_DATAOBJECT_ERROR_INVALIDARGS);
  708. return false;
  709. }
  710. if ($group === false) {
  711. $this->_query['group_by'] = '';
  712. return;
  713. }
  714. // check input...= 0 or ' ' == error!
  715. if (!trim($group)) {
  716. return $this->raiseError("groupBy: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
  717. }
  718. if (!$this->_query['group_by']) {
  719. $this->_query['group_by'] = " GROUP BY {$group} ";
  720. return;
  721. }
  722. $this->_query['group_by'] .= " , {$group}";
  723. }
  724. /**
  725. * Adds a having clause
  726. *
  727. * $object->having(); //reset the grouping
  728. * $object->having("sum(value) > 0 ");
  729. *
  730. * @param string $having condition
  731. * @access public
  732. * @return none|PEAR::Error - invalid args only
  733. */
  734. function having($having = false)
  735. {
  736. if ($this->_query === false) {
  737. $this->raiseError(
  738. "You cannot do two queries on the same object (copy it before finding)",
  739. DB_DATAOBJECT_ERROR_INVALIDARGS);
  740. return false;
  741. }
  742. if ($having === false) {
  743. $this->_query['having'] = '';
  744. return;
  745. }
  746. // check input...= 0 or ' ' == error!
  747. if (!trim($having)) {
  748. return $this->raiseError("Having: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
  749. }
  750. if (!$this->_query['having']) {
  751. $this->_query['having'] = " HAVING {$having} ";
  752. return;
  753. }
  754. $this->_query['having'] .= " AND {$having}";
  755. }
  756. /**
  757. * Adds a using Index
  758. *
  759. * $object->useIndex(); //reset the use Index
  760. * $object->useIndex("some_index");
  761. *
  762. * Note do not put unfiltered user input into theis method.
  763. * This is mysql specific at present? - might need altering to support other databases.
  764. *
  765. * @param string|array $index index or indexes to use.
  766. * @access public
  767. * @return none|PEAR::Error - invalid args only
  768. */
  769. function useIndex($index = false)
  770. {
  771. if ($this->_query === false) {
  772. $this->raiseError(
  773. "You cannot do two queries on the same object (copy it before finding)",
  774. DB_DATAOBJECT_ERROR_INVALIDARGS);
  775. return false;
  776. }
  777. if ($index=== false) {
  778. $this->_query['useindex'] = '';
  779. return;
  780. }
  781. // check input...= 0 or ' ' == error!
  782. if ((is_string($index) && !trim($index)) || (is_array($index) && !count($index)) ) {
  783. return $this->raiseError("Having: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
  784. }
  785. $index = is_array($index) ? implode(', ', $index) : $index;
  786. if (!$this->_query['useindex']) {
  787. $this->_query['useindex'] = " USE INDEX ({$index}) ";
  788. return;
  789. }
  790. $this->_query['useindex'] = substr($this->_query['useindex'],0, -2) . ", {$index}) ";
  791. }
  792. /**
  793. * Sets the Limit
  794. *
  795. * $boject->limit(); // clear limit
  796. * $object->limit(12);
  797. * $object->limit(12,10);
  798. *
  799. * Note this will emit an error on databases other than mysql/postgress
  800. * as there is no 'clean way' to implement it. - you should consider refering to
  801. * your database manual to decide how you want to implement it.
  802. *
  803. * @param string $a limit start (or number), or blank to reset
  804. * @param string $b number
  805. * @access public
  806. * @return none|PEAR::Error - invalid args only
  807. */
  808. function limit($a = null, $b = null)
  809. {
  810. if ($this->_query === false) {
  811. $this->raiseError(
  812. "You cannot do two queries on the same object (copy it before finding)",
  813. DB_DATAOBJECT_ERROR_INVALIDARGS);
  814. return false;
  815. }
  816. if ($a === null) {
  817. $this->_query['limit_start'] = '';
  818. $this->_query['limit_count'] = '';
  819. return;
  820. }
  821. // check input...= 0 or ' ' == error!
  822. if ((!is_int($a) && ((string)((int)$a) !== (string)$a))
  823. || (($b !== null) && (!is_int($b) && ((string)((int)$b) !== (string)$b)))) {
  824. return $this->raiseError("limit: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
  825. }
  826. global $_DB_DATAOBJECT;
  827. $this->_connect();
  828. $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  829. $this->_query['limit_start'] = ($b == null) ? 0 : (int)$a;
  830. $this->_query['limit_count'] = ($b == null) ? (int)$a : (int)$b;
  831. }
  832. /**
  833. * Adds a select columns
  834. *
  835. * $object->selectAdd(); // resets select to nothing!
  836. * $object->selectAdd("*"); // default select
  837. * $object->selectAdd("unixtime(DATE) as udate");
  838. * $object->selectAdd("DATE");
  839. *
  840. * to prepend distict:
  841. * $object->selectAdd('distinct ' . $object->selectAdd());
  842. *
  843. * @param string $k
  844. * @access public
  845. * @return mixed null or old string if you reset it.
  846. */
  847. function selectAdd($k = null)
  848. {
  849. if ($this->_query === false) {
  850. $this->raiseError(
  851. "You cannot do two queries on the same object (copy it before finding)",
  852. DB_DATAOBJECT_ERROR_INVALIDARGS);
  853. return false;
  854. }
  855. if ($k === null) {
  856. $old = $this->_query['data_select'];
  857. $this->_query['data_select'] = '';
  858. return $old;
  859. }
  860. // check input...= 0 or ' ' == error!
  861. if (!trim($k)) {
  862. return $this->raiseError("selectAdd: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
  863. }
  864. if ($this->_query['data_select']) {
  865. $this->_query['data_select'] .= ', ';
  866. }
  867. $this->_query['data_select'] .= " $k ";
  868. }
  869. /**
  870. * Adds multiple Columns or objects to select with formating.
  871. *
  872. * $object->selectAs(null); // adds "table.colnameA as colnameA,table.colnameB as colnameB,......"
  873. * // note with null it will also clear the '*' default select
  874. * $object->selectAs(array('a','b'),'%s_x'); // adds "a as a_x, b as b_x"
  875. * $object->selectAs(array('a','b'),'ddd_%s','ccc'); // adds "ccc.a as ddd_a, ccc.b as ddd_b"
  876. * $object->selectAdd($object,'prefix_%s'); // calls $object->get_table and adds it all as
  877. * objectTableName.colnameA as prefix_colnameA
  878. *
  879. * @param array|object|null the array or object to take column names from.
  880. * @param string format in sprintf format (use %s for the colname)
  881. * @param string table name eg. if you have joinAdd'd or send $from as an array.
  882. * @access public
  883. * @return void
  884. */
  885. function selectAs($from = null,$format = '%s',$tableName=false)
  886. {
  887. global $_DB_DATAOBJECT;
  888. if ($this->_query === false) {
  889. $this->raiseError(
  890. "You cannot do two queries on the same object (copy it before finding)",
  891. DB_DATAOBJECT_ERROR_INVALIDARGS);
  892. return false;
  893. }
  894. if ($from === null) {
  895. // blank the '*'
  896. $this->selectAdd();
  897. $from = $this;
  898. }
  899. $table = $this->tableName();
  900. if (is_object($from)) {
  901. $table = $from->tableName();
  902. $from = array_keys($from->table());
  903. }
  904. if ($tableName !== false) {
  905. $table = $tableName;
  906. }
  907. $s = '%s';
  908. if (!empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers'])) {
  909. $this->_connect();
  910. $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  911. $s = $DB->quoteIdentifier($s);
  912. $format = $DB->quoteIdentifier($format);
  913. }
  914. foreach ($from as $k) {
  915. $this->selectAdd(sprintf("{$s}.{$s} as {$format}",$table,$k,$k));
  916. }
  917. $this->_query['data_select'] .= "\n";
  918. }
  919. /**
  920. * Insert the current objects variables into the database
  921. *
  922. * Returns the ID of the inserted element (if auto increment or sequences are used.)
  923. *
  924. * for example
  925. *
  926. * Designed to be extended
  927. *
  928. * $object = new mytable();
  929. * $object->name = "fred";
  930. * echo $object->insert();
  931. *
  932. * @access public
  933. * @return mixed false on failure, int when auto increment or sequence used, otherwise true on success
  934. */
  935. function insert()
  936. {
  937. global $_DB_DATAOBJECT;
  938. // we need to write to the connection (For nextid) - so us the real
  939. // one not, a copyied on (as ret-by-ref fails with overload!)
  940. if (!isset($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
  941. $this->_connect();
  942. }
  943. $quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
  944. $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  945. $items = $this->table();
  946. if (!$items) {
  947. $this->raiseError("insert:No table definition for {$this->tableName()}",
  948. DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  949. return false;
  950. }
  951. $options = $_DB_DATAOBJECT['CONFIG'];
  952. $datasaved = 1;
  953. $leftq = '';
  954. $rightq = '';
  955. $seqKeys = isset($_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()]) ?
  956. $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()] :
  957. $this->sequenceKey();
  958. $key = isset($seqKeys[0]) ? $seqKeys[0] : false;
  959. $useNative = isset($seqKeys[1]) ? $seqKeys[1] : false;
  960. $seq = isset($seqKeys[2]) ? $seqKeys[2] : false;
  961. $dbtype = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn["phptype"];
  962. // nativeSequences or Sequences..
  963. // big check for using sequences
  964. if (($key !== false) && !$useNative) {
  965. if (!$seq) {
  966. $keyvalue = $DB->nextId($this->tableName());
  967. } else {
  968. $f = $DB->getOption('seqname_format');
  969. $DB->setOption('seqname_format','%s');
  970. $keyvalue = $DB->nextId($seq);
  971. $DB->setOption('seqname_format',$f);
  972. }
  973. if (PEAR::isError($keyvalue)) {
  974. $this->raiseError($keyvalue->toString(), DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  975. return false;
  976. }
  977. $this->$key = $keyvalue;
  978. }
  979. // if we haven't set disable_null_strings to "full"
  980. $ignore_null = !isset($options['disable_null_strings'])
  981. || !is_string($options['disable_null_strings'])
  982. || strtolower($options['disable_null_strings']) !== 'full' ;
  983. foreach($items as $k => $v) {
  984. // if we are using autoincrement - skip the column...
  985. if ($key && ($k == $key) && $useNative) {
  986. continue;
  987. }
  988. // Ignore INTEGERS which aren't set to a value - or empty string..
  989. if ( (!isset($this->$k) || ($v == 1 && $this->$k === ''))
  990. && $ignore_null
  991. ) {
  992. continue;
  993. }
  994. // dont insert data into mysql timestamps
  995. // use query() if you really want to do this!!!!
  996. if ($v & DB_DATAOBJECT_MYSQLTIMESTAMP) {
  997. continue;
  998. }
  999. if ($leftq) {
  1000. $leftq .= ', ';
  1001. $rightq .= ', ';
  1002. }
  1003. $leftq .= ($quoteIdentifiers ? ($DB->quoteIdentifier($k) . ' ') : "$k ");
  1004. if (is_object($this->$k) && is_a($this->$k,'DB_DataObject_Cast')) {
  1005. $value = $this->$k->toString($v,$DB);
  1006. if (PEAR::isError($value)) {
  1007. $this->raiseError($value->toString() ,DB_DATAOBJECT_ERROR_INVALIDARGS);
  1008. return false;
  1009. }
  1010. $rightq .= $value;
  1011. continue;
  1012. }
  1013. if (!($v & DB_DATAOBJECT_NOTNULL) && DB_DataObject::_is_null($this,$k)) {
  1014. $rightq .= " NULL ";
  1015. continue;
  1016. }
  1017. // DATE is empty... on a col. that can be null..
  1018. // note: this may be usefull for time as well..
  1019. if (!$this->$k &&
  1020. (($v & DB_DATAOBJECT_DATE) || ($v & DB_DATAOBJECT_TIME)) &&
  1021. !($v & DB_DATAOBJECT_NOTNULL)) {
  1022. $rightq .= " NULL ";
  1023. continue;
  1024. }
  1025. if ($v & DB_DATAOBJECT_STR) {
  1026. $rightq .= $this->_quote((string) (
  1027. ($v & DB_DATAOBJECT_BOOL) ?
  1028. // this is thanks to the braindead idea of postgres to
  1029. // use t/f for boolean.
  1030. (($this->$k === 'f') ? 0 : (int)(bool) $this->$k) :
  1031. $this->$k
  1032. )) . " ";
  1033. continue;
  1034. }
  1035. if (is_numeric($this->$k)) {
  1036. $rightq .=" {$this->$k} ";
  1037. continue;
  1038. }
  1039. /* flag up string values - only at debug level... !!!??? */
  1040. if (is_object($this->$k) || is_array($this->$k)) {
  1041. $this->debug('ODD DATA: ' .$k . ' ' . print_r($this->$k,true),'ERROR');
  1042. }
  1043. // at present we only cast to integers
  1044. // - V2 may store additional data about float/int
  1045. $rightq .= ' ' . intval($this->$k) . ' ';
  1046. }
  1047. // not sure why we let empty insert here.. - I guess to generate a blank row..
  1048. if ($leftq || $useNative) {
  1049. $table = ($quoteIdentifiers ? $DB->quoteIdentifier($this->tableName()) : $this->tableName());
  1050. if (($dbtype == 'pgsql') && empty($leftq)) {
  1051. $r = $this->_query("INSERT INTO {$table} DEFAULT VALUES");
  1052. } else {
  1053. $r = $this->_query("INSERT INTO {$table} ($leftq) VALUES ($rightq) ");
  1054. }
  1055. if (PEAR::isError($r)) {
  1056. $this->raiseError($r);
  1057. return false;
  1058. }
  1059. if ($r < 1) {
  1060. return 0;
  1061. }
  1062. // now do we have an integer key!
  1063. if ($key && $useNative) {
  1064. switch ($dbtype) {
  1065. case 'mysql':
  1066. case 'mysqli':
  1067. $method = "{$dbtype}_insert_id";
  1068. $this->$key = $method(
  1069. $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->connection
  1070. );
  1071. break;
  1072. case 'mssql':
  1073. // note this is not really thread safe - you should wrapp it with
  1074. // transactions = eg.
  1075. // $db->query('BEGIN');
  1076. // $db->insert();
  1077. // $db->query('COMMIT');
  1078. $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
  1079. $method = ($db_driver == 'DB') ? 'getOne' : 'queryOne';
  1080. $mssql_key = $DB->$method("SELECT @@IDENTITY");
  1081. if (PEAR::isError($mssql_key)) {
  1082. $this->raiseError($mssql_key);
  1083. return false;
  1084. }
  1085. $this->$key = $mssql_key;
  1086. break;
  1087. case 'pgsql':
  1088. if (!$seq) {
  1089. $seq = $DB->getSequenceName(strtolower($this->tableName()));
  1090. }
  1091. $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
  1092. $method = ($db_driver == 'DB') ? 'getOne' : 'queryOne';
  1093. $pgsql_key = $DB->$method("SELECT currval('".$seq . "')");
  1094. if (PEAR::isError($pgsql_key)) {
  1095. $this->raiseError($pgsql_key);
  1096. return false;
  1097. }
  1098. $this->$key = $pgsql_key;
  1099. break;
  1100. case 'ifx':
  1101. $this->$key = array_shift (
  1102. ifx_fetch_row (
  1103. ifx_query(
  1104. "select DBINFO('sqlca.sqlerrd1') FROM systables where tabid=1",
  1105. $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->connection,
  1106. IFX_SCROLL
  1107. ),
  1108. "FIRST"
  1109. )
  1110. );
  1111. break;
  1112. }
  1113. }
  1114. if (isset($_DB_DATAOBJECT['CACHE'][strtolower(get_class($this))])) {
  1115. $this->_clear_cache();
  1116. }
  1117. if ($key) {
  1118. return $this->$key;
  1119. }
  1120. return true;
  1121. }
  1122. $this->raiseError("insert: No Data specifed for query", DB_DATAOBJECT_ERROR_NODATA);
  1123. return false;
  1124. }
  1125. /**
  1126. * Updates current objects variables into the database
  1127. * uses the keys() to decide how to update
  1128. * Returns the true on success
  1129. *
  1130. * for example
  1131. *
  1132. * $object = DB_DataObject::factory('mytable');
  1133. * $object->get("ID",234);
  1134. * $object->email="testing@test.com";
  1135. * if(!$object->update())
  1136. * echo "UPDATE FAILED";
  1137. *
  1138. * to only update changed items :
  1139. * $dataobject->get(132);
  1140. * $original = $dataobject; // clone/copy it..
  1141. * $dataobject->setFrom($_POST);
  1142. * if ($dataobject->validate()) {
  1143. * $dataobject->update($original);
  1144. * } // otherwise an error...
  1145. *
  1146. * performing global updates:
  1147. * $object = DB_DataObject::factory('mytable');
  1148. * $object->status = "dead";
  1149. * $object->whereAdd('age > 150');
  1150. * $object->update(DB_DATAOBJECT_WHEREADD_ONLY);
  1151. *
  1152. * @param object dataobject (optional) | DB_DATAOBJECT_WHEREADD_ONLY - used to only update changed items.
  1153. * @access public
  1154. * @return int rows affected or false on failure
  1155. */
  1156. function update($dataObject = false)
  1157. {
  1158. global $_DB_DATAOBJECT;
  1159. // connect will load the config!
  1160. $this->_connect();
  1161. $original_query = $this->_query;
  1162. $items = $this->table();
  1163. // only apply update against sequence key if it is set?????
  1164. $seq = $this->sequenceKey();
  1165. if ($seq[0] !== false) {
  1166. $keys = array($seq[0]);
  1167. if (!isset($this->{$keys[0]}) && $dataObject !== true) {
  1168. $this->raiseError("update: trying to perform an update without
  1169. the key set, and argument to update is not
  1170. DB_DATAOBJECT_WHEREADD_ONLY
  1171. ". print_r(array('seq' => $seq , 'keys'=>$keys), true), DB_DATAOBJECT_ERROR_INVALIDARGS);
  1172. return false;
  1173. }
  1174. } else {
  1175. $keys = $this->keys();
  1176. }
  1177. if (!$items) {
  1178. $this->raiseError("update:No table definition for {$this->tableName()}", DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  1179. return false;
  1180. }
  1181. $datasaved = 1;
  1182. $settings = '';
  1183. $this->_connect();
  1184. $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  1185. $dbtype = $DB->dsn["phptype"];
  1186. $quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
  1187. $options = $_DB_DATAOBJECT['CONFIG'];
  1188. $ignore_null = !isset($options['disable_null_strings'])
  1189. || !is_string($options['disable_null_strings'])
  1190. || strtolower($options['disable_null_strings']) !== 'full' ;
  1191. foreach($items as $k => $v) {
  1192. // I think this is ignoring empty vlalues
  1193. if ((!isset($this->$k) || ($v == 1 && $this->$k === ''))
  1194. && $ignore_null
  1195. ) {
  1196. continue;
  1197. }
  1198. // ignore stuff thats
  1199. // dont write things that havent changed..
  1200. if (($dataObject !== false) && isset($dataObject->$k) && ($dataObject->$k === $this->$k)) {
  1201. continue;
  1202. }
  1203. // - dont write keys to left.!!!
  1204. if (in_array($k,$keys)) {
  1205. continue;
  1206. }
  1207. // dont insert data into mysql timestamps
  1208. // use query() if you really want to do this!!!!
  1209. if ($v & DB_DATAOBJECT_MYSQLTIMESTAMP) {
  1210. continue;
  1211. }
  1212. if ($settings) {
  1213. $settings .= ', ';
  1214. }
  1215. $kSql = ($quoteIdentifiers ? $DB->quoteIdentifier($k) : $k);
  1216. if (is_object($this->$k) && is_a($this->$k,'DB_DataObject_Cast')) {
  1217. $value = $this->$k->toString($v,$DB);
  1218. if (PEAR::isError($value)) {
  1219. $this->raiseError($value->getMessage() ,DB_DATAOBJECT_ERROR_INVALIDARG);
  1220. return false;
  1221. }
  1222. $settings .= "$kSql = $value ";
  1223. continue;
  1224. }
  1225. // special values ... at least null is handled...
  1226. if (!($v & DB_DATAOBJECT_NOTNULL) && DB_DataObject::_is_null($this,$k)) {
  1227. $settings .= "$kSql = NULL ";
  1228. continue;
  1229. }
  1230. // DATE is empty... on a col. that can be null..
  1231. // note: this may be usefull for time as well..
  1232. if (!$this->$k &&
  1233. (($v & DB_DATAOBJECT_DATE) || ($v & DB_DATAOBJECT_TIME)) &&
  1234. !($v & DB_DATAOBJECT_NOTNULL)) {
  1235. $settings .= "$kSql = NULL ";
  1236. continue;
  1237. }
  1238. if ($v & DB_DATAOBJECT_STR) {
  1239. $settings .= "$kSql = ". $this->_quote((string) (
  1240. ($v & DB_DATAOBJECT_BOOL) ?
  1241. // this is thanks to the braindead idea of postgres to
  1242. // use t/f for boolean.
  1243. (($this->$k === 'f') ? 0 : (int)(bool) $this->$k) :
  1244. $this->$k
  1245. )) . ' ';
  1246. continue;
  1247. }
  1248. if (is_numeric($this->$k)) {
  1249. $settings .= "$kSql = {$this->$k} ";
  1250. continue;
  1251. }
  1252. // at present we only cast to integers
  1253. // - V2 may store additional data about float/int
  1254. $settings .= "$kSql = " . intval($this->$k) . ' ';
  1255. }
  1256. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  1257. $this->debug("got keys as ".serialize($keys),3);
  1258. }
  1259. if ($dataObject !== true) {
  1260. $this->_build_condition($items,$keys);
  1261. } else {
  1262. // prevent wiping out of data!
  1263. if (empty($this->_query['condition'])) {
  1264. $this->raiseError("update: global table update not available
  1265. do \$do->whereAdd('1=1'); if you really want to do that.
  1266. ", DB_DATAOBJECT_ERROR_INVALIDARGS);
  1267. return false;
  1268. }
  1269. }
  1270. // echo " $settings, $this->condition ";
  1271. if ($settings && isset($this->_query) && $this->_query['condition']) {
  1272. $table = ($quoteIdentifiers ? $DB->quoteIdentifier($this->tableName()) : $this->tableName());
  1273. $r = $this->_query("UPDATE {$table} SET {$settings} {$this->_query['condition']} ");
  1274. // restore original query conditions.
  1275. $this->_query = $original_query;
  1276. if (PEAR::isError($r)) {
  1277. $this->raiseError($r);
  1278. return false;
  1279. }
  1280. if ($r < 1) {
  1281. return 0;
  1282. }
  1283. $this->_clear_cache();
  1284. return $r;
  1285. }
  1286. // restore original query conditions.
  1287. $this->_query = $original_query;
  1288. // if you manually specified a dataobject, and there where no changes - then it's ok..
  1289. if ($dataObject !== false) {
  1290. return true;
  1291. }
  1292. $this->raiseError(
  1293. "update: No Data specifed for query $settings , {$this->_query['condition']}",
  1294. DB_DATAOBJECT_ERROR_NODATA);
  1295. return false;
  1296. }
  1297. /**
  1298. * Deletes items from table which match current objects variables
  1299. *
  1300. * Returns the true on success
  1301. *
  1302. * for example
  1303. *
  1304. * Designed to be extended
  1305. *
  1306. * $object = new mytable();
  1307. * $object->ID=123;
  1308. * echo $object->delete(); // builds a conditon
  1309. *
  1310. * $object = new mytable();
  1311. * $object->whereAdd('age > 12');
  1312. * $object->limit(1);
  1313. * $object->orderBy('age DESC');
  1314. * $object->delete(true); // dont use object vars, use the conditions, limit and order.
  1315. *
  1316. * @param bool $useWhere (optional) If DB_DATAOBJECT_WHEREADD_ONLY is passed in then
  1317. * we will build the condition only using the whereAdd's. Default is to
  1318. * build the condition only using the object parameters.
  1319. *
  1320. * @access public
  1321. * @return mixed Int (No. of rows affected) on success, false on failure, 0 on no data affected
  1322. */
  1323. function delete($useWhere = false)
  1324. {
  1325. global $_DB_DATAOBJECT;
  1326. // connect will load the config!
  1327. $this->_connect();
  1328. $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  1329. $quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
  1330. $extra_cond = ' ' . (isset($this->_query['order_by']) ? $this->_query['order_by'] : '');
  1331. if (!$useWhere) {
  1332. $keys = $this->keys();
  1333. $this->_query = array(); // as it's probably unset!
  1334. $this->_query['condition'] = ''; // default behaviour not to use where condition
  1335. $this->_build_condition($this->table(),$keys);
  1336. // if primary keys are not set then use data from rest of object.
  1337. if (!$this->_query['condition']) {
  1338. $this->_build_condition($this->table(),array(),$keys);
  1339. }
  1340. $extra_cond = '';
  1341. }
  1342. // don't delete without a condition
  1343. if (($this->_query !== false) && $this->_query['condition']) {
  1344. $table = ($quoteIdentifiers ? $DB->quoteIdentifier($this->tableName()) : $this->tableName());
  1345. $sql = "DELETE ";
  1346. // using a joined delete. - with useWhere..
  1347. $sql .= (!empty($this->_join) && $useWhere) ?
  1348. "{$table} FROM {$table} {$this->_join} " :
  1349. "FROM {$table} ";
  1350. $sql .= $this->_query['condition']. $extra_cond;
  1351. // add limit..
  1352. if (isset($this->_query['limit_start']) && strlen($this->_query['limit_start'] . $this->_query['limit_count'])) {
  1353. if (!isset($_DB_DATAOBJECT['CONFIG']['db_driver']) ||
  1354. ($_DB_DATAOBJECT['CONFIG']['db_driver'] == 'DB')) {
  1355. // pear DB
  1356. $sql = $DB->modifyLimitQuery($sql,$this->_query['limit_start'], $this->_query['limit_count']);
  1357. } else {
  1358. // MDB2
  1359. $DB->setLimit( $this->_query['limit_count'],$this->_query['limit_start']);
  1360. }
  1361. }
  1362. $r = $this->_query($sql);
  1363. if (PEAR::isError($r)) {
  1364. $this->raiseError($r);
  1365. return false;
  1366. }
  1367. if ($r < 1) {
  1368. return 0;
  1369. }
  1370. $this->_clear_cache();
  1371. return $r;
  1372. } else {
  1373. $this->raiseError("delete: No condition specifed for query", DB_DATAOBJECT_ERROR_NODATA);
  1374. return false;
  1375. }
  1376. }
  1377. /**
  1378. * fetches a specific row into this object variables
  1379. *
  1380. * Not recommended - better to use fetch()
  1381. *
  1382. * Returens true on success
  1383. *
  1384. * @param int $row row
  1385. * @access public
  1386. * @return boolean true on success
  1387. */
  1388. function fetchRow($row = null)
  1389. {
  1390. global $_DB_DATAOBJECT;
  1391. if (empty($_DB_DATAOBJECT['CONFIG'])) {
  1392. $this->_loadConfig();
  1393. }
  1394. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  1395. $this->debug("{$this->tableName()} $row of {$this->N}", "fetchrow",3);
  1396. }
  1397. if (!$this->tableName()) {
  1398. $this->raiseError("fetchrow: No table", DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  1399. return false;
  1400. }
  1401. if ($row === null) {
  1402. $this->raiseError("fetchrow: No row specified", DB_DATAOBJECT_ERROR_INVALIDARGS);
  1403. return false;
  1404. }
  1405. if (!$this->N) {
  1406. $this->raiseError("fetchrow: No results avaiable", DB_DATAOBJECT_ERROR_NODATA);
  1407. return false;
  1408. }
  1409. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  1410. $this->debug("{$this->tableName()} $row of {$this->N}", "fetchrow",3);
  1411. }
  1412. $result = $_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid];
  1413. $array = $result->fetchrow(DB_DATAOBJECT_FETCHMODE_ASSOC,$row);
  1414. if (!is_array($array)) {
  1415. $this->raiseError("fetchrow: No results available", DB_DATAOBJECT_ERROR_NODATA);
  1416. return false;
  1417. }
  1418. $replace = array('.', ' ');
  1419. foreach($array as $k => $v) {
  1420. // use strpos as str_replace is slow.
  1421. $kk = (strpos($k, '.') === false && strpos($k, ' ') === false) ?
  1422. $k : str_replace($replace, '_', $k);
  1423. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  1424. $this->debug("$kk = ". $array[$k], "fetchrow LINE", 3);
  1425. }
  1426. $this->$kk = $array[$k];
  1427. }
  1428. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  1429. $this->debug("{$this->tableName()} DONE", "fetchrow", 3);
  1430. }
  1431. return true;
  1432. }
  1433. /**
  1434. * Find the number of results from a simple query
  1435. *
  1436. * for example
  1437. *
  1438. * $object = new mytable();
  1439. * $object->name = "fred";
  1440. * echo $object->count();
  1441. * echo $object->count(true); // dont use object vars.
  1442. * echo $object->count('distinct mycol'); count distinct mycol.
  1443. * echo $object->count('distinct mycol',true); // dont use object vars.
  1444. * echo $object->count('distinct'); // count distinct id (eg. the primary key)
  1445. *
  1446. *
  1447. * @param bool|string (optional)
  1448. * (true|false => see below not on whereAddonly)
  1449. * (string)
  1450. * "DISTINCT" => does a distinct count on the tables 'key' column
  1451. * otherwise => normally it counts primary keys - you can use
  1452. * this to do things like $do->count('distinct mycol');
  1453. *
  1454. * @param bool $whereAddOnly (optional) If DB_DATAOBJECT_WHEREADD_ONLY is passed in then
  1455. * we will build the condition only using the whereAdd's. Default is to
  1456. * build the condition using the object parameters as well.
  1457. *
  1458. * @access public
  1459. * @return int
  1460. */
  1461. function count($countWhat = false,$whereAddOnly = false)
  1462. {
  1463. global $_DB_DATAOBJECT;
  1464. if (is_bool($countWhat)) {
  1465. $whereAddOnly = $countWhat;
  1466. }
  1467. $t = clone($this);
  1468. $items = $t->table();
  1469. $quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
  1470. if (!isset($t->_query)) {
  1471. $this->raiseError(
  1472. "You cannot do run count after you have run fetch()",
  1473. DB_DATAOBJECT_ERROR_INVALIDARGS);
  1474. return false;
  1475. }
  1476. $this->_connect();
  1477. $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  1478. if (!$whereAddOnly && $items) {
  1479. $t->_build_condition($items);
  1480. }
  1481. $keys = $this->keys();
  1482. if (empty($keys[0]) && (!is_string($countWhat) || (strtoupper($countWhat) == 'DISTINCT'))) {
  1483. $this->raiseError(
  1484. "You cannot do run count without keys - use \$do->count('id'), or use \$do->count('distinct id')';",
  1485. DB_DATAOBJECT_ERROR_INVALIDARGS,PEAR_ERROR_DIE);
  1486. return false;
  1487. }
  1488. $table = ($quoteIdentifiers ? $DB->quoteIdentifier($this->tableName()) : $this->tableName());
  1489. $key_col = empty($keys[0]) ? '' : (($quoteIdentifiers ? $DB->quoteIdentifier($keys[0]) : $keys[0]));
  1490. $as = ($quoteIdentifiers ? $DB->quoteIdentifier('DATAOBJECT_NUM') : 'DATAOBJECT_NUM');
  1491. // support distinct on default keys.
  1492. $countWhat = (strtoupper($countWhat) == 'DISTINCT') ?
  1493. "DISTINCT {$table}.{$key_col}" : $countWhat;
  1494. $countWhat = is_string($countWhat) ? $countWhat : "{$table}.{$key_col}";
  1495. $r = $t->_query(
  1496. "SELECT count({$countWhat}) as $as
  1497. FROM $table {$t->_join} {$t->_query['condition']}");
  1498. if (PEAR::isError($r)) {
  1499. return false;
  1500. }
  1501. $result = $_DB_DATAOBJECT['RESULTS'][$t->_DB_resultid];
  1502. $l = $result->fetchRow(DB_DATAOBJECT_FETCHMODE_ORDERED);
  1503. // free the results - essential on oracle.
  1504. $t->free();
  1505. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  1506. $this->debug('Count returned '. $l[0] ,1);
  1507. }
  1508. return (int) $l[0];
  1509. }
  1510. /**
  1511. * sends raw query to database
  1512. *
  1513. * Since _query has to be a private 'non overwriteable method', this is a relay
  1514. *
  1515. * @param string $string SQL Query
  1516. * @access public
  1517. * @return void or DB_Error
  1518. */
  1519. function query($string)
  1520. {
  1521. return $this->_query($string);
  1522. }
  1523. /**
  1524. * an escape wrapper around DB->escapeSimple()
  1525. * can be used when adding manual queries or clauses
  1526. * eg.
  1527. * $object->query("select * from xyz where abc like '". $object->escape($_GET['name']) . "'");
  1528. *
  1529. * @param string $string value to be escaped
  1530. * @param bool $likeEscape escapes % and _ as well. - so like queries can be protected.
  1531. * @access public
  1532. * @return string
  1533. */
  1534. function escape($string, $likeEscape=false)
  1535. {
  1536. global $_DB_DATAOBJECT;
  1537. $this->_connect();
  1538. $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  1539. // mdb2 uses escape...
  1540. $dd = empty($_DB_DATAOBJECT['CONFIG']['db_driver']) ? 'DB' : $_DB_DATAOBJECT['CONFIG']['db_driver'];
  1541. $ret = ($dd == 'DB') ? $DB->escapeSimple($string) : $DB->escape($string);
  1542. if ($likeEscape) {
  1543. $ret = str_replace(array('_','%'), array('\_','\%'), $ret);
  1544. }
  1545. return $ret;
  1546. }
  1547. /* ==================================================== */
  1548. /* Major Private Vars */
  1549. /* ==================================================== */
  1550. /**
  1551. * The Database connection dsn (as described in the PEAR DB)
  1552. * only used really if you are writing a very simple application/test..
  1553. * try not to use this - it is better stored in configuration files..
  1554. *
  1555. * @access private
  1556. * @var string
  1557. */
  1558. var $_database_dsn = '';
  1559. /**
  1560. * The Database connection id (md5 sum of databasedsn)
  1561. *
  1562. * @access private
  1563. * @var string
  1564. */
  1565. var $_database_dsn_md5 = '';
  1566. /**
  1567. * The Database name
  1568. * created in __connection
  1569. *
  1570. * @access private
  1571. * @var string
  1572. */
  1573. var $_database = '';
  1574. /**
  1575. * The QUERY rules
  1576. * This replaces alot of the private variables
  1577. * used to build a query, it is unset after find() is run.
  1578. *
  1579. *
  1580. *
  1581. * @access private
  1582. * @var array
  1583. */
  1584. var $_query = array(
  1585. 'condition' => '', // the WHERE condition
  1586. 'group_by' => '', // the GROUP BY condition
  1587. 'order_by' => '', // the ORDER BY condition
  1588. 'having' => '', // the HAVING condition
  1589. 'useindex' => '', // the USE INDEX condition
  1590. 'limit_start' => '', // the LIMIT condition
  1591. 'limit_count' => '', // the LIMIT condition
  1592. 'data_select' => '*', // the columns to be SELECTed
  1593. 'unions' => array(), // the added unions,
  1594. 'derive_table' => '', // derived table name (BETA)
  1595. 'derive_select' => '', // derived table select (BETA)
  1596. );
  1597. /**
  1598. * Database result id (references global $_DB_DataObject[results]
  1599. *
  1600. * @access private
  1601. * @var integer
  1602. */
  1603. var $_DB_resultid;
  1604. /**
  1605. * ResultFields - on the last call to fetch(), resultfields is sent here,
  1606. * so we can clean up the memory.
  1607. *
  1608. * @access public
  1609. * @var array
  1610. */
  1611. var $_resultFields = false;
  1612. /* ============================================================== */
  1613. /* Table definition layer (started of very private but 'came out'*/
  1614. /* ============================================================== */
  1615. /**
  1616. * Autoload or manually load the table definitions
  1617. *
  1618. *
  1619. * usage :
  1620. * DB_DataObject::databaseStructure( 'databasename',
  1621. * parse_ini_file('mydb.ini',true),
  1622. * parse_ini_file('mydb.link.ini',true));
  1623. *
  1624. * obviously you dont have to use ini files.. (just return array similar to ini files..)
  1625. *
  1626. * It should append to the table structure array
  1627. *
  1628. *
  1629. * @param optional string name of database to assign / read
  1630. * @param optional array structure of database, and keys
  1631. * @param optional array table links
  1632. *
  1633. * @access public
  1634. * @return true or PEAR:error on wrong paramenters.. or false if no file exists..
  1635. * or the array(tablename => array(column_name=>type)) if called with 1 argument.. (databasename)
  1636. */
  1637. function databaseStructure()
  1638. {
  1639. global $_DB_DATAOBJECT;
  1640. // Assignment code
  1641. if ($args = func_get_args()) {
  1642. if (count($args) == 1) {
  1643. // this returns all the tables and their structure..
  1644. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  1645. $this->debug("Loading Generator as databaseStructure called with args",1);
  1646. }
  1647. $x = new DB_DataObject;
  1648. $x->_database = $args[0];
  1649. $this->_connect();
  1650. $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  1651. $tables = $DB->getListOf('tables');
  1652. class_exists('DB_DataObject_Generator') ? '' :
  1653. require_once 'DB/DataObject/Generator.php';
  1654. foreach($tables as $table) {
  1655. $y = new DB_DataObject_Generator;
  1656. $y->fillTableSchema($x->_database,$table);
  1657. }
  1658. return $_DB_DATAOBJECT['INI'][$x->_database];
  1659. } else {
  1660. $_DB_DATAOBJECT['INI'][$args[0]] = isset($_DB_DATAOBJECT['INI'][$args[0]]) ?
  1661. $_DB_DATAOBJECT['INI'][$args[0]] + $args[1] : $args[1];
  1662. if (isset($args[1])) {
  1663. $_DB_DATAOBJECT['LINKS'][$args[0]] = isset($_DB_DATAOBJECT['LINKS'][$args[0]]) ?
  1664. $_DB_DATAOBJECT['LINKS'][$args[0]] + $args[2] : $args[2];
  1665. }
  1666. return true;
  1667. }
  1668. }
  1669. if (!$this->_database) {
  1670. $this->_connect();
  1671. }
  1672. // if this table is already loaded this table..
  1673. if (!empty($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()])) {
  1674. return true;
  1675. }
  1676. // initialize the ini data.. if empt..
  1677. if (empty($_DB_DATAOBJECT['INI'][$this->_database])) {
  1678. $_DB_DATAOBJECT['INI'][$this->_database] = array();
  1679. }
  1680. if (empty($_DB_DATAOBJECT['CONFIG'])) {
  1681. DB_DataObject::_loadConfig();
  1682. }
  1683. // we do not have the data for this table yet...
  1684. // if we are configured to use the proxy..
  1685. if ( !empty($_DB_DATAOBJECT['CONFIG']['proxy']) ) {
  1686. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  1687. $this->debug("Loading Generator to fetch Schema",1);
  1688. }
  1689. class_exists('DB_DataObject_Generator') ? '' :
  1690. require_once 'DB/DataObject/Generator.php';
  1691. $x = new DB_DataObject_Generator;
  1692. $x->fillTableSchema($this->_database,$this->tableName());
  1693. return true;
  1694. }
  1695. // if you supply this with arguments, then it will take those
  1696. // as the database and links array...
  1697. $schemas = isset($_DB_DATAOBJECT['CONFIG']['schema_location']) ?
  1698. array("{$_DB_DATAOBJECT['CONFIG']['schema_location']}/{$this->_database}.ini") :
  1699. array() ;
  1700. if (isset($_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"])) {
  1701. $schemas = is_array($_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"]) ?
  1702. $_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"] :
  1703. explode(PATH_SEPARATOR,$_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"]);
  1704. }
  1705. $_DB_DATAOBJECT['INI'][$this->_database] = array();
  1706. foreach ($schemas as $ini) {
  1707. if (file_exists($ini) && is_file($ini)) {
  1708. $_DB_DATAOBJECT['INI'][$this->_database] = array_merge(
  1709. $_DB_DATAOBJECT['INI'][$this->_database],
  1710. parse_ini_file($ini, true)
  1711. );
  1712. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  1713. if (!is_readable ($ini)) {
  1714. $this->debug("ini file is not readable: $ini","databaseStructure",1);
  1715. } else {
  1716. $this->debug("Loaded ini file: $ini","databaseStructure",1);
  1717. }
  1718. }
  1719. } else {
  1720. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  1721. $this->debug("Missing ini file: $ini","databaseStructure",1);
  1722. }
  1723. }
  1724. }
  1725. // are table name lowecased..
  1726. if (!empty($_DB_DATAOBJECT['CONFIG']['portability']) && $_DB_DATAOBJECT['CONFIG']['portability'] & 1) {
  1727. foreach($_DB_DATAOBJECT['INI'][$this->_database] as $k=>$v) {
  1728. // results in duplicate cols.. but not a big issue..
  1729. $_DB_DATAOBJECT['INI'][$this->_database][strtolower($k)] = $v;
  1730. }
  1731. }
  1732. // now have we loaded the structure..
  1733. if (!empty($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()])) {
  1734. return true;
  1735. }
  1736. // - if not try building it..
  1737. if (!empty($_DB_DATAOBJECT['CONFIG']['proxy'])) {
  1738. class_exists('DB_DataObject_Generator') ? '' :
  1739. require_once 'DB/DataObject/Generator.php';
  1740. $x = new DB_DataObject_Generator;
  1741. $x->fillTableSchema($this->_database,$this->tableName());
  1742. // should this fail!!!???
  1743. return true;
  1744. }
  1745. $this->debug("Cant find database schema: {$this->_database}/{$this->tableName()} \n".
  1746. "in links file data: " . print_r($_DB_DATAOBJECT['INI'],true),"databaseStructure",5);
  1747. // we have to die here!! - it causes chaos if we dont (including looping forever!)
  1748. $this->raiseError( "Unable to load schema for database and table (turn debugging up to 5 for full error message)", DB_DATAOBJECT_ERROR_INVALIDARGS, PEAR_ERROR_DIE);
  1749. return false;
  1750. }
  1751. /**
  1752. * Return or assign the name of the current table
  1753. *
  1754. *
  1755. * @param string optinal table name to set
  1756. * @access public
  1757. * @return string The name of the current table
  1758. */
  1759. function tableName()
  1760. {
  1761. global $_DB_DATAOBJECT;
  1762. $args = func_get_args();
  1763. if (count($args)) {
  1764. $this->__table = $args[0];
  1765. }
  1766. if (!empty($_DB_DATAOBJECT['CONFIG']['portability']) && $_DB_DATAOBJECT['CONFIG']['portability'] & 1) {
  1767. return strtolower($this->__table);
  1768. }
  1769. return $this->__table;
  1770. }
  1771. /**
  1772. * Return or assign the name of the current database
  1773. *
  1774. * @param string optional database name to set
  1775. * @access public
  1776. * @return string The name of the current database
  1777. */
  1778. function database()
  1779. {
  1780. $args = func_get_args();
  1781. if (count($args)) {
  1782. $this->_database = $args[0];
  1783. } else {
  1784. $this->_connect();
  1785. }
  1786. return $this->_database;
  1787. }
  1788. /**
  1789. * get/set an associative array of table columns
  1790. *
  1791. * @access public
  1792. * @param array key=>type array
  1793. * @return array (associative)
  1794. */
  1795. function table()
  1796. {
  1797. // for temporary storage of database fields..
  1798. // note this is not declared as we dont want to bloat the print_r output
  1799. $args = func_get_args();
  1800. if (count($args)) {
  1801. $this->_database_fields = $args[0];
  1802. }
  1803. if (isset($this->_database_fields)) {
  1804. return $this->_database_fields;
  1805. }
  1806. global $_DB_DATAOBJECT;
  1807. if (!isset($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
  1808. $this->_connect();
  1809. }
  1810. if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()])) {
  1811. return $_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()];
  1812. }
  1813. $this->databaseStructure();
  1814. $ret = array();
  1815. if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()])) {
  1816. $ret = $_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()];
  1817. }
  1818. return $ret;
  1819. }
  1820. /**
  1821. * get/set an array of table primary keys
  1822. *
  1823. * set usage: $do->keys('id','code');
  1824. *
  1825. * This is defined in the table definition if it gets it wrong,
  1826. * or you do not want to use ini tables, you can override this.
  1827. * @param string optional set the key
  1828. * @param * optional set more keys
  1829. * @access public
  1830. * @return array
  1831. */
  1832. function keys()
  1833. {
  1834. // for temporary storage of database fields..
  1835. // note this is not declared as we dont want to bloat the print_r output
  1836. $args = func_get_args();
  1837. if (count($args)) {
  1838. $this->_database_keys = $args;
  1839. }
  1840. if (isset($this->_database_keys)) {
  1841. return $this->_database_keys;
  1842. }
  1843. global $_DB_DATAOBJECT;
  1844. if (!isset($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
  1845. $this->_connect();
  1846. }
  1847. if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()."__keys"])) {
  1848. return array_keys($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()."__keys"]);
  1849. }
  1850. $this->databaseStructure();
  1851. if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()."__keys"])) {
  1852. return array_keys($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()."__keys"]);
  1853. }
  1854. return array();
  1855. }
  1856. /**
  1857. * get/set an sequence key
  1858. *
  1859. * by default it returns the first key from keys()
  1860. * set usage: $do->sequenceKey('id',true);
  1861. *
  1862. * override this to return array(false,false) if table has no real sequence key.
  1863. *
  1864. * @param string optional the key sequence/autoinc. key
  1865. * @param boolean optional use native increment. default false
  1866. * @param false|string optional native sequence name
  1867. * @access public
  1868. * @return array (column,use_native,sequence_name)
  1869. */
  1870. function sequenceKey()
  1871. {
  1872. global $_DB_DATAOBJECT;
  1873. // call setting
  1874. if (!$this->_database) {
  1875. $this->_connect();
  1876. }
  1877. if (!isset($_DB_DATAOBJECT['SEQUENCE'][$this->_database])) {
  1878. $_DB_DATAOBJECT['SEQUENCE'][$this->_database] = array();
  1879. }
  1880. $args = func_get_args();
  1881. if (count($args)) {
  1882. $args[1] = isset($args[1]) ? $args[1] : false;
  1883. $args[2] = isset($args[2]) ? $args[2] : false;
  1884. $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()] = $args;
  1885. }
  1886. if (isset($_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()])) {
  1887. return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()];
  1888. }
  1889. // end call setting (eg. $do->sequenceKeys(a,b,c); )
  1890. $keys = $this->keys();
  1891. if (!$keys) {
  1892. return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()]
  1893. = array(false,false,false);
  1894. }
  1895. $table = $this->table();
  1896. $dbtype = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype'];
  1897. $usekey = $keys[0];
  1898. $seqname = false;
  1899. if (!empty($_DB_DATAOBJECT['CONFIG']['sequence_'.$this->tableName()])) {
  1900. $seqname = $_DB_DATAOBJECT['CONFIG']['sequence_'.$this->tableName()];
  1901. if (strpos($seqname,':') !== false) {
  1902. list($usekey,$seqname) = explode(':',$seqname);
  1903. }
  1904. }
  1905. // if the key is not an integer - then it's not a sequence or native
  1906. if (empty($table[$usekey]) || !($table[$usekey] & DB_DATAOBJECT_INT)) {
  1907. return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()] = array(false,false,false);
  1908. }
  1909. if (!empty($_DB_DATAOBJECT['CONFIG']['ignore_sequence_keys'])) {
  1910. $ignore = $_DB_DATAOBJECT['CONFIG']['ignore_sequence_keys'];
  1911. if (is_string($ignore) && (strtoupper($ignore) == 'ALL')) {
  1912. return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()] = array(false,false,$seqname);
  1913. }
  1914. if (is_string($ignore)) {
  1915. $ignore = $_DB_DATAOBJECT['CONFIG']['ignore_sequence_keys'] = explode(',',$ignore);
  1916. }
  1917. if (in_array($this->tableName(),$ignore)) {
  1918. return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()] = array(false,false,$seqname);
  1919. }
  1920. }
  1921. $realkeys = $_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()."__keys"];
  1922. // if you are using an old ini file - go back to old behaviour...
  1923. if (is_numeric($realkeys[$usekey])) {
  1924. $realkeys[$usekey] = 'N';
  1925. }
  1926. // multiple unique primary keys without a native sequence...
  1927. if (($realkeys[$usekey] == 'K') && (count($keys) > 1)) {
  1928. return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()] = array(false,false,$seqname);
  1929. }
  1930. // use native sequence keys...
  1931. // technically postgres native here...
  1932. // we need to get the new improved tabledata sorted out first.
  1933. // support named sequence keys.. - currently postgres only..
  1934. if ( in_array($dbtype , array('pgsql')) &&
  1935. ($table[$usekey] & DB_DATAOBJECT_INT) &&
  1936. isset($realkeys[$usekey]) && strlen($realkeys[$usekey]) > 1) {
  1937. return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()] = array($usekey,true, $realkeys[$usekey]);
  1938. }
  1939. if ( in_array($dbtype , array('pgsql', 'mysql', 'mysqli', 'mssql', 'ifx')) &&
  1940. ($table[$usekey] & DB_DATAOBJECT_INT) &&
  1941. isset($realkeys[$usekey]) && ($realkeys[$usekey] == 'N')
  1942. ) {
  1943. return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()] = array($usekey,true,$seqname);
  1944. }
  1945. // if not a native autoinc, and we have not assumed all primary keys are sequence
  1946. if (($realkeys[$usekey] != 'N') &&
  1947. !empty($_DB_DATAOBJECT['CONFIG']['dont_use_pear_sequences'])) {
  1948. return array(false,false,false);
  1949. }
  1950. // I assume it's going to try and be a nextval DB sequence.. (not native)
  1951. return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()] = array($usekey,false,$seqname);
  1952. }
  1953. /* =========================================================== */
  1954. /* Major Private Methods - the core part! */
  1955. /* =========================================================== */
  1956. /**
  1957. * clear the cache values for this class - normally done on insert/update etc.
  1958. *
  1959. * @access private
  1960. * @return void
  1961. */
  1962. function _clear_cache()
  1963. {
  1964. global $_DB_DATAOBJECT;
  1965. $class = strtolower(get_class($this));
  1966. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  1967. $this->debug("Clearing Cache for ".$class,1);
  1968. }
  1969. if (!empty($_DB_DATAOBJECT['CACHE'][$class])) {
  1970. unset($_DB_DATAOBJECT['CACHE'][$class]);
  1971. }
  1972. }
  1973. /**
  1974. * backend wrapper for quoting, as MDB2 and DB do it differently...
  1975. *
  1976. * @access private
  1977. * @return string quoted
  1978. */
  1979. function _quote($str)
  1980. {
  1981. global $_DB_DATAOBJECT;
  1982. return (empty($_DB_DATAOBJECT['CONFIG']['db_driver']) ||
  1983. ($_DB_DATAOBJECT['CONFIG']['db_driver'] == 'DB'))
  1984. ? $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->quoteSmart($str)
  1985. : $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->quote($str);
  1986. }
  1987. /**
  1988. * connects to the database
  1989. *
  1990. *
  1991. * TODO: tidy this up - This has grown to support a number of connection options like
  1992. * a) dynamic changing of ini file to change which database to connect to
  1993. * b) multi data via the table_{$table} = dsn ini option
  1994. * c) session based storage.
  1995. *
  1996. * @access private
  1997. * @return true | PEAR::error
  1998. */
  1999. function _connect()
  2000. {
  2001. global $_DB_DATAOBJECT;
  2002. if (empty($_DB_DATAOBJECT['CONFIG'])) {
  2003. $this->_loadConfig();
  2004. }
  2005. // Set database driver for reference
  2006. $db_driver = empty($_DB_DATAOBJECT['CONFIG']['db_driver']) ?
  2007. 'DB' : $_DB_DATAOBJECT['CONFIG']['db_driver'];
  2008. // is it already connected ?
  2009. if ($this->_database_dsn_md5 && !empty($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
  2010. // connection is an error...
  2011. if (PEAR::isError($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
  2012. return $this->raiseError(
  2013. $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->message,
  2014. $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->code, PEAR_ERROR_DIE
  2015. );
  2016. }
  2017. if (empty($this->_database)) {
  2018. $this->_database = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['database'];
  2019. $hasGetDatabase = method_exists($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5], 'getDatabase');
  2020. $this->_database = ($db_driver != 'DB' && $hasGetDatabase)
  2021. ? $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->getDatabase()
  2022. : $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['database'];
  2023. if (($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype'] == 'sqlite')
  2024. && is_file($this->_database)) {
  2025. $this->_database = basename($this->_database);
  2026. }
  2027. if ($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype'] == 'ibase') {
  2028. $this->_database = substr(basename($this->_database), 0, -4);
  2029. }
  2030. }
  2031. // theoretically we have a md5, it's listed in connections and it's not an error.
  2032. // so everything is ok!
  2033. return true;
  2034. }
  2035. // it's not currently connected!
  2036. // try and work out what to use for the dsn !
  2037. $options= $_DB_DATAOBJECT['CONFIG'];
  2038. // if the databse dsn dis defined in the object..
  2039. $dsn = isset($this->_database_dsn) ? $this->_database_dsn : null;
  2040. if (!$dsn) {
  2041. if (!$this->_database && !empty($this->__table)) {
  2042. $this->_database = isset($options["table_{$this->tableName()}"]) ? $options["table_{$this->tableName()}"] : null;
  2043. }
  2044. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  2045. $this->debug("Checking for database specific ini ('{$this->_database}') : database_{$this->_database} in options","CONNECT");
  2046. }
  2047. if ($this->_database && !empty($options["database_{$this->_database}"])) {
  2048. $dsn = $options["database_{$this->_database}"];
  2049. } else if (!empty($options['database'])) {
  2050. $dsn = $options['database'];
  2051. }
  2052. }
  2053. // if still no database...
  2054. if (!$dsn) {
  2055. return $this->raiseError(
  2056. "No database name / dsn found anywhere",
  2057. DB_DATAOBJECT_ERROR_INVALIDCONFIG, PEAR_ERROR_DIE
  2058. );
  2059. }
  2060. if (is_string($dsn)) {
  2061. $this->_database_dsn_md5 = md5($dsn);
  2062. } else {
  2063. /// support array based dsn's
  2064. $this->_database_dsn_md5 = md5(serialize($dsn));
  2065. }
  2066. if (!empty($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
  2067. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  2068. $this->debug("USING CACHED CONNECTION", "CONNECT",3);
  2069. }
  2070. if (!$this->_database) {
  2071. $hasGetDatabase = method_exists($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5], 'getDatabase');
  2072. $this->_database = ($db_driver != 'DB' && $hasGetDatabase)
  2073. ? $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->getDatabase()
  2074. : $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['database'];
  2075. if (($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype'] == 'sqlite')
  2076. && is_file($this->_database))
  2077. {
  2078. $this->_database = basename($this->_database);
  2079. }
  2080. }
  2081. return true;
  2082. }
  2083. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  2084. $this->debug("NEW CONNECTION TP DATABASE :" .$this->_database , "CONNECT",3);
  2085. /* actualy make a connection */
  2086. $this->debug(print_r($dsn,true) ." {$this->_database_dsn_md5}", "CONNECT",3);
  2087. }
  2088. // Note this is verbose deliberatly!
  2089. if ($db_driver == 'DB') {
  2090. /* PEAR DB connect */
  2091. // this allows the setings of compatibility on DB
  2092. $db_options = PEAR::getStaticProperty('DB','options');
  2093. require_once 'DB.php';
  2094. if ($db_options) {
  2095. $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5] = DB::connect($dsn,$db_options);
  2096. } else {
  2097. $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5] = DB::connect($dsn);
  2098. }
  2099. } else {
  2100. /* assumption is MDB2 */
  2101. require_once 'MDB2.php';
  2102. // this allows the setings of compatibility on MDB2
  2103. $db_options = PEAR::getStaticProperty('MDB2','options');
  2104. $db_options = is_array($db_options) ? $db_options : array();
  2105. $db_options['portability'] = isset($db_options['portability'] )
  2106. ? $db_options['portability'] : MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_FIX_CASE;
  2107. $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5] = MDB2::connect($dsn,$db_options);
  2108. }
  2109. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  2110. $this->debug(print_r($_DB_DATAOBJECT['CONNECTIONS'],true), "CONNECT",5);
  2111. }
  2112. if (PEAR::isError($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
  2113. $this->debug($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->toString(), "CONNECT FAILED",5);
  2114. return $this->raiseError(
  2115. "Connect failed, turn on debugging to 5 see why",
  2116. $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->code, PEAR_ERROR_DIE
  2117. );
  2118. }
  2119. if (empty($this->_database)) {
  2120. $hasGetDatabase = method_exists($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5], 'getDatabase');
  2121. $this->_database = ($db_driver != 'DB' && $hasGetDatabase)
  2122. ? $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->getDatabase()
  2123. : $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['database'];
  2124. if (($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype'] == 'sqlite')
  2125. && is_file($this->_database))
  2126. {
  2127. $this->_database = basename($this->_database);
  2128. }
  2129. }
  2130. // Oracle need to optimize for portibility - not sure exactly what this does though :)
  2131. return true;
  2132. }
  2133. /**
  2134. * sends query to database - this is the private one that must work
  2135. * - internal functions use this rather than $this->query()
  2136. *
  2137. * @param string $string
  2138. * @access private
  2139. * @return mixed none or PEAR_Error
  2140. */
  2141. function _query($string)
  2142. {
  2143. global $_DB_DATAOBJECT;
  2144. $this->_connect();
  2145. $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  2146. $options = $_DB_DATAOBJECT['CONFIG'];
  2147. $_DB_driver = empty($_DB_DATAOBJECT['CONFIG']['db_driver']) ?
  2148. 'DB': $_DB_DATAOBJECT['CONFIG']['db_driver'];
  2149. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  2150. $this->debug($string,$log="QUERY");
  2151. }
  2152. if (
  2153. strtoupper($string) == 'BEGIN' ||
  2154. strtoupper($string) == 'START TRANSACTION'
  2155. ) {
  2156. if ($_DB_driver == 'DB') {
  2157. $DB->autoCommit(false);
  2158. $DB->simpleQuery('BEGIN');
  2159. } else {
  2160. $DB->beginTransaction();
  2161. }
  2162. return true;
  2163. }
  2164. if (strtoupper($string) == 'COMMIT') {
  2165. $res = $DB->commit();
  2166. if ($_DB_driver == 'DB') {
  2167. $DB->autoCommit(true);
  2168. }
  2169. return $res;
  2170. }
  2171. if (strtoupper($string) == 'ROLLBACK') {
  2172. $DB->rollback();
  2173. if ($_DB_driver == 'DB') {
  2174. $DB->autoCommit(true);
  2175. }
  2176. return true;
  2177. }
  2178. if (!empty($options['debug_ignore_updates']) &&
  2179. (strtolower(substr(trim($string), 0, 6)) != 'select') &&
  2180. (strtolower(substr(trim($string), 0, 4)) != 'show') &&
  2181. (strtolower(substr(trim($string), 0, 8)) != 'describe')) {
  2182. $this->debug('Disabling Update as you are in debug mode');
  2183. return $this->raiseError("Disabling Update as you are in debug mode", null) ;
  2184. }
  2185. //if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 1) {
  2186. // this will only work when PEAR:DB supports it.
  2187. //$this->debug($DB->getAll('explain ' .$string,DB_DATAOBJECT_FETCHMODE_ASSOC), $log="sql",2);
  2188. //}
  2189. // some sim
  2190. $t= explode(' ',microtime());
  2191. $_DB_DATAOBJECT['QUERYENDTIME'] = $time = $t[0]+$t[1];
  2192. for ($tries = 0;$tries < 3;$tries++) {
  2193. if ($_DB_driver == 'DB') {
  2194. $result = $DB->query($string);
  2195. } else {
  2196. switch (strtolower(substr(trim($string),0,6))) {
  2197. case 'insert':
  2198. case 'update':
  2199. case 'delete':
  2200. $result = $DB->exec($string);
  2201. break;
  2202. default:
  2203. $result = $DB->query($string);
  2204. break;
  2205. }
  2206. }
  2207. // see if we got a failure.. - try again a few times..
  2208. if (!is_object($result) || !is_a($result,'PEAR_Error')) {
  2209. break;
  2210. }
  2211. if ($result->getCode() != -14) { // *DB_ERROR_NODBSELECTED
  2212. break; // not a connection error..
  2213. }
  2214. sleep(1); // wait before retyring..
  2215. $DB->connect($DB->dsn);
  2216. }
  2217. if (is_object($result) && is_a($result,'PEAR_Error')) {
  2218. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  2219. $this->debug($result->toString(), "Query Error",1 );
  2220. }
  2221. $this->N = false;
  2222. return $this->raiseError($result);
  2223. }
  2224. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  2225. $t= explode(' ',microtime());
  2226. $_DB_DATAOBJECT['QUERYENDTIME'] = $t[0]+$t[1];
  2227. $this->debug('QUERY DONE IN '.($t[0]+$t[1]-$time)." seconds", 'query',1);
  2228. }
  2229. switch (strtolower(substr(trim($string),0,6))) {
  2230. case 'insert':
  2231. case 'update':
  2232. case 'delete':
  2233. if ($_DB_driver == 'DB') {
  2234. // pear DB specific
  2235. return $DB->affectedRows();
  2236. }
  2237. return $result;
  2238. }
  2239. if (is_object($result)) {
  2240. // lets hope that copying the result object is OK!
  2241. $_DB_resultid = $GLOBALS['_DB_DATAOBJECT']['RESULTSEQ']++;
  2242. $_DB_DATAOBJECT['RESULTS'][$_DB_resultid] = $result;
  2243. $this->_DB_resultid = $_DB_resultid;
  2244. }
  2245. $this->N = 0;
  2246. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  2247. $this->debug(serialize($result), 'RESULT',5);
  2248. }
  2249. if (method_exists($result, 'numRows')) {
  2250. if ($_DB_driver == 'DB') {
  2251. $DB->expectError(DB_ERROR_UNSUPPORTED);
  2252. } else {
  2253. $DB->expectError(MDB2_ERROR_UNSUPPORTED);
  2254. }
  2255. $this->N = $result->numRows();
  2256. //var_dump($this->N);
  2257. if (is_object($this->N) && is_a($this->N,'PEAR_Error')) {
  2258. $this->N = true;
  2259. }
  2260. $DB->popExpect();
  2261. }
  2262. }
  2263. /**
  2264. * Builds the WHERE based on the values of of this object
  2265. *
  2266. * @param mixed $keys
  2267. * @param array $filter (used by update to only uses keys in this filter list).
  2268. * @param array $negative_filter (used by delete to prevent deleting using the keys mentioned..)
  2269. * @access private
  2270. * @return string
  2271. */
  2272. function _build_condition($keys, $filter = array(),$negative_filter=array())
  2273. {
  2274. global $_DB_DATAOBJECT;
  2275. $this->_connect();
  2276. $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  2277. $quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
  2278. $options = $_DB_DATAOBJECT['CONFIG'];
  2279. // if we dont have query vars.. - reset them.
  2280. if ($this->_query === false) {
  2281. $x = new DB_DataObject;
  2282. $this->_query= $x->_query;
  2283. }
  2284. foreach($keys as $k => $v) {
  2285. // index keys is an indexed array
  2286. /* these filter checks are a bit suspicious..
  2287. - need to check that update really wants to work this way */
  2288. if ($filter) {
  2289. if (!in_array($k, $filter)) {
  2290. continue;
  2291. }
  2292. }
  2293. if ($negative_filter) {
  2294. if (in_array($k, $negative_filter)) {
  2295. continue;
  2296. }
  2297. }
  2298. if (!isset($this->$k)) {
  2299. continue;
  2300. }
  2301. $kSql = $quoteIdentifiers
  2302. ? ( $DB->quoteIdentifier($this->tableName()) . '.' . $DB->quoteIdentifier($k) )
  2303. : "{$this->tableName()}.{$k}";
  2304. if (is_object($this->$k) && is_a($this->$k,'DB_DataObject_Cast')) {
  2305. $dbtype = $DB->dsn["phptype"];
  2306. $value = $this->$k->toString($v,$DB);
  2307. if (PEAR::isError($value)) {
  2308. $this->raiseError($value->getMessage() ,DB_DATAOBJECT_ERROR_INVALIDARG);
  2309. return false;
  2310. }
  2311. if ((strtolower($value) === 'null') && !($v & DB_DATAOBJECT_NOTNULL)) {
  2312. $this->whereAdd(" $kSql IS NULL");
  2313. continue;
  2314. }
  2315. $this->whereAdd(" $kSql = $value");
  2316. continue;
  2317. }
  2318. if (!($v & DB_DATAOBJECT_NOTNULL) && DB_DataObject::_is_null($this,$k)) {
  2319. $this->whereAdd(" $kSql IS NULL");
  2320. continue;
  2321. }
  2322. if ($v & DB_DATAOBJECT_STR) {
  2323. $this->whereAdd(" $kSql = " . $this->_quote((string) (
  2324. ($v & DB_DATAOBJECT_BOOL) ?
  2325. // this is thanks to the braindead idea of postgres to
  2326. // use t/f for boolean.
  2327. (($this->$k === 'f') ? 0 : (int)(bool) $this->$k) :
  2328. $this->$k
  2329. )) );
  2330. continue;
  2331. }
  2332. if (is_numeric($this->$k)) {
  2333. $this->whereAdd(" $kSql = {$this->$k}");
  2334. continue;
  2335. }
  2336. /* this is probably an error condition! */
  2337. $this->whereAdd(" $kSql = ".intval($this->$k));
  2338. }
  2339. }
  2340. /**
  2341. * classic factory method for loading a table class
  2342. * usage: $do = DB_DataObject::factory('person')
  2343. * WARNING - this may emit a include error if the file does not exist..
  2344. * use @ to silence it (if you are sure it is acceptable)
  2345. * eg. $do = @DB_DataObject::factory('person')
  2346. *
  2347. * table name can bedatabasename/table
  2348. * - and allow modular dataobjects to be written..
  2349. * (this also helps proxy creation)
  2350. *
  2351. * Experimental Support for Multi-Database factory eg. mydatabase.mytable
  2352. *
  2353. *
  2354. * @param string $table tablename (use blank to create a new instance of the same class.)
  2355. * @access private
  2356. * @return DataObject|PEAR_Error
  2357. */
  2358. static function factory($table = '')
  2359. {
  2360. global $_DB_DATAOBJECT;
  2361. // multi-database support.. - experimental.
  2362. $database = '';
  2363. if (strpos( $table,'/') !== false ) {
  2364. list($database,$table) = explode('.',$table, 2);
  2365. }
  2366. if (empty($_DB_DATAOBJECT['CONFIG'])) {
  2367. DB_DataObject::_loadConfig();
  2368. }
  2369. // no configuration available for database
  2370. if (!empty($database) && empty($_DB_DATAOBJECT['CONFIG']['database_'.$database])) {
  2371. $do = new DB_DataObject();
  2372. $do->raiseError(
  2373. "unable to find database_{$database} in Configuration, It is required for factory with database"
  2374. , 0, PEAR_ERROR_DIE );
  2375. }
  2376. /*
  2377. if ($table === '') {
  2378. if (is_a($this,'DB_DataObject') && strlen($this->tableName())) {
  2379. $table = $this->tableName();
  2380. } else {
  2381. return DB_DataObject::raiseError(
  2382. "factory did not recieve a table name",
  2383. DB_DATAOBJECT_ERROR_INVALIDARGS);
  2384. }
  2385. }
  2386. */
  2387. // does this need multi db support??
  2388. $cp = isset($_DB_DATAOBJECT['CONFIG']['class_prefix']) ?
  2389. explode(PATH_SEPARATOR, $_DB_DATAOBJECT['CONFIG']['class_prefix']) : '';
  2390. //print_r($cp);
  2391. // multiprefix support.
  2392. $tbl = preg_replace('/[^A-Z0-9]/i','_',ucfirst($table));
  2393. if (is_array($cp)) {
  2394. $class = array();
  2395. foreach($cp as $cpr) {
  2396. $ce = substr(phpversion(),0,1) > 4 ? class_exists($cpr . $tbl,false) : class_exists($cpr . $tbl);
  2397. if ($ce) {
  2398. $class = $cpr . $tbl;
  2399. break;
  2400. }
  2401. $class[] = $cpr . $tbl;
  2402. }
  2403. } else {
  2404. $class = $tbl;
  2405. $ce = substr(phpversion(),0,1) > 4 ? class_exists($class,false) : class_exists($class);
  2406. }
  2407. $rclass = $ce ? $class : DB_DataObject::_autoloadClass($class, $table);
  2408. // proxy = full|light
  2409. if (!$rclass && isset($_DB_DATAOBJECT['CONFIG']['proxy'])) {
  2410. DB_DataObject::debug("FAILED TO Autoload $database.$table - using proxy.","FACTORY",1);
  2411. $proxyMethod = 'getProxy'.$_DB_DATAOBJECT['CONFIG']['proxy'];
  2412. // if you have loaded (some other way) - dont try and load it again..
  2413. class_exists('DB_DataObject_Generator') ? '' :
  2414. require_once 'DB/DataObject/Generator.php';
  2415. $d = new DB_DataObject;
  2416. $d->__table = $table;
  2417. $ret = $d->_connect();
  2418. if (is_object($ret) && is_a($ret, 'PEAR_Error')) {
  2419. return $ret;
  2420. }
  2421. $x = new DB_DataObject_Generator;
  2422. return $x->$proxyMethod( $d->_database, $table);
  2423. }
  2424. if (!$rclass || !class_exists($rclass)) {
  2425. $dor = new DB_DataObject();
  2426. return $dor->raiseError(
  2427. "factory could not find class " .
  2428. (is_array($class) ? implode(PATH_SEPARATOR, $class) : $class ).
  2429. "from $table",
  2430. DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  2431. }
  2432. $ret = new $rclass();
  2433. if (!empty($database)) {
  2434. DB_DataObject::debug("Setting database to $database","FACTORY",1);
  2435. $ret->database($database);
  2436. }
  2437. return $ret;
  2438. }
  2439. /**
  2440. * autoload Class
  2441. *
  2442. * @param string|array $class Class
  2443. * @param string $table Table trying to load.
  2444. * @access private
  2445. * @return string classname on Success
  2446. */
  2447. function _autoloadClass($class, $table=false)
  2448. {
  2449. global $_DB_DATAOBJECT;
  2450. if (empty($_DB_DATAOBJECT['CONFIG'])) {
  2451. DB_DataObject::_loadConfig();
  2452. }
  2453. $class_prefix = empty($_DB_DATAOBJECT['CONFIG']['class_prefix']) ?
  2454. '' : $_DB_DATAOBJECT['CONFIG']['class_prefix'];
  2455. $table = $table ? $table : substr($class,strlen($class_prefix));
  2456. // only include the file if it exists - and barf badly if it has parse errors :)
  2457. if (!empty($_DB_DATAOBJECT['CONFIG']['proxy']) || empty($_DB_DATAOBJECT['CONFIG']['class_location'])) {
  2458. return false;
  2459. }
  2460. // support for:
  2461. // class_location = mydir/ => maps to mydir/Tablename.php
  2462. // class_location = mydir/myfile_%s.php => maps to mydir/myfile_Tablename
  2463. // with directory sepr
  2464. // class_location = mydir/:mydir2/: => tries all of thes locations.
  2465. $cl = $_DB_DATAOBJECT['CONFIG']['class_location'];
  2466. switch (true) {
  2467. case (strpos($cl ,'%s') !== false):
  2468. $file = sprintf($cl , preg_replace('/[^A-Z0-9]/i','_',ucfirst($table)));
  2469. break;
  2470. case (strpos($cl , PATH_SEPARATOR) !== false):
  2471. $file = array();
  2472. foreach(explode(PATH_SEPARATOR, $cl ) as $p) {
  2473. $file[] = $p .'/'.preg_replace('/[^A-Z0-9]/i','_',ucfirst($table)).".php";
  2474. }
  2475. break;
  2476. default:
  2477. $file = $cl .'/'.preg_replace('/[^A-Z0-9]/i','_',ucfirst($table)).".php";
  2478. break;
  2479. }
  2480. $cls = is_array($class) ? $class : array($class);
  2481. if (is_array($file) || !file_exists($file)) {
  2482. $found = false;
  2483. $file = is_array($file) ? $file : array($file);
  2484. $search = implode(PATH_SEPARATOR, $file);
  2485. foreach($file as $f) {
  2486. foreach(explode(PATH_SEPARATOR, '' . PATH_SEPARATOR . ini_get('include_path')) as $p) {
  2487. $ff = empty($p) ? $f : "$p/$f";
  2488. if (file_exists($ff)) {
  2489. $file = $ff;
  2490. $found = true;
  2491. break;
  2492. }
  2493. }
  2494. if ($found) {
  2495. break;
  2496. }
  2497. }
  2498. if (!$found) {
  2499. $dor = new DB_DataObject();
  2500. $dor->raiseError(
  2501. "autoload:Could not find class " . implode(',', $cls) .
  2502. " using class_location value :" . $search .
  2503. " using include_path value :" . ini_get('include_path'),
  2504. DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  2505. return false;
  2506. }
  2507. }
  2508. include_once $file;
  2509. $ce = false;
  2510. foreach($cls as $c) {
  2511. $ce = substr(phpversion(),0,1) > 4 ? class_exists($c,false) : class_exists($c);
  2512. if ($ce) {
  2513. $class = $c;
  2514. break;
  2515. }
  2516. }
  2517. if (!$ce) {
  2518. $dor = new DB_DataObject();
  2519. $dor->raiseError(
  2520. "autoload:Could not autoload " . implode(',', $cls) ,
  2521. DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  2522. return false;
  2523. }
  2524. return $class;
  2525. }
  2526. /**
  2527. * Have the links been loaded?
  2528. * if they have it contains a array of those variables.
  2529. *
  2530. * @access private
  2531. * @var boolean | array
  2532. */
  2533. var $_link_loaded = false;
  2534. /**
  2535. * Get the links associate array as defined by the links.ini file.
  2536. *
  2537. *
  2538. * Experimental... -
  2539. * Should look a bit like
  2540. * [local_col_name] => "related_tablename:related_col_name"
  2541. *
  2542. * @param array $new_links optional - force update of the links for this table
  2543. * You probably want to restore it to it's original state after,
  2544. * as modifying here does it for the whole PHP request.
  2545. *
  2546. * @return array|null
  2547. * array = if there are links defined for this table.
  2548. * empty array - if there is a links.ini file, but no links on this table
  2549. * false - if no links.ini exists for this database (hence try auto_links).
  2550. * @access public
  2551. * @see DB_DataObject::getLinks(), DB_DataObject::getLink()
  2552. */
  2553. function links()
  2554. {
  2555. global $_DB_DATAOBJECT;
  2556. if (empty($_DB_DATAOBJECT['CONFIG'])) {
  2557. $this->_loadConfig();
  2558. }
  2559. // have to connect.. -> otherwise things break later.
  2560. $this->_connect();
  2561. // alias for shorter code..
  2562. $lcfg = &$_DB_DATAOBJECT['LINKS'];
  2563. $cfg = $_DB_DATAOBJECT['CONFIG'];
  2564. if ($args = func_get_args()) {
  2565. // an associative array was specified, that updates the current
  2566. // schema... - be careful doing this
  2567. if (empty( $lcfg[$this->_database])) {
  2568. $lcfg[$this->_database] = array();
  2569. }
  2570. $lcfg[$this->_database][$this->tableName()] = $args[0];
  2571. }
  2572. // loaded and available.
  2573. if (isset($lcfg[$this->_database][$this->tableName()])) {
  2574. return $lcfg[$this->_database][$this->tableName()];
  2575. }
  2576. // loaded
  2577. if (isset($lcfg[$this->_database])) {
  2578. // either no file, or empty..
  2579. return $lcfg[$this->_database] === false ? null : array();
  2580. }
  2581. // links are same place as schema by default.
  2582. $schemas = isset($cfg['schema_location']) ?
  2583. array("{$cfg['schema_location']}/{$this->_database}.ini") :
  2584. array() ;
  2585. // if ini_* is set look there instead.
  2586. // and support multiple locations.
  2587. if (isset($cfg["ini_{$this->_database}"])) {
  2588. $schemas = is_array($cfg["ini_{$this->_database}"]) ?
  2589. $cfg["ini_{$this->_database}"] :
  2590. explode(PATH_SEPARATOR,$cfg["ini_{$this->_database}"]);
  2591. }
  2592. // default to not available.
  2593. $lcfg[$this->_database] = false;
  2594. foreach ($schemas as $ini) {
  2595. $links = isset($cfg["links_{$this->_database}"]) ?
  2596. $cfg["links_{$this->_database}"] :
  2597. str_replace('.ini','.links.ini',$ini);
  2598. // file really exists..
  2599. if (!file_exists($links) || !is_file($links)) {
  2600. if (!empty($cfg['debug'])) {
  2601. $this->debug("Missing links.ini file: $links","links",1);
  2602. }
  2603. continue;
  2604. }
  2605. // set to empty array - as we have at least one file now..
  2606. $lcfg[$this->_database] = empty($lcfg[$this->_database]) ? array() : $lcfg[$this->_database];
  2607. // merge schema file into lcfg..
  2608. $lcfg[$this->_database] = array_merge(
  2609. $lcfg[$this->_database],
  2610. parse_ini_file($links, true)
  2611. );
  2612. if (!empty($cfg['debug'])) {
  2613. $this->debug("Loaded links.ini file: $links","links",1);
  2614. }
  2615. }
  2616. if (!empty($_DB_DATAOBJECT['CONFIG']['portability']) && $_DB_DATAOBJECT['CONFIG']['portability'] & 1) {
  2617. foreach($lcfg[$this->_database] as $k=>$v) {
  2618. $nk = strtolower($k);
  2619. // results in duplicate cols.. but not a big issue..
  2620. $lcfg[$this->_database][$nk] = isset($lcfg[$this->_database][$nk])
  2621. ? $lcfg[$this->_database][$nk] : array();
  2622. foreach($v as $kk =>$vv) {
  2623. //var_Dump($vv);exit;
  2624. $vv =explode(':', $vv);
  2625. $vv[0] = strtolower($vv[0]);
  2626. $lcfg[$this->_database][$nk][$kk] = implode(':', $vv);
  2627. }
  2628. }
  2629. }
  2630. //echo '<PRE>';print_r($lcfg);exit;
  2631. // if there is no link data at all on the file!
  2632. // we return null.
  2633. if ($lcfg[$this->_database] === false) {
  2634. return null;
  2635. }
  2636. if (isset($lcfg[$this->_database][$this->tableName()])) {
  2637. return $lcfg[$this->_database][$this->tableName()];
  2638. }
  2639. return array();
  2640. }
  2641. /**
  2642. * generic getter/setter for links
  2643. *
  2644. * This is the new 'recommended' way to get get/set linked objects.
  2645. * must be used with links.ini
  2646. *
  2647. * usage:
  2648. * get:
  2649. * $obj = $do->link('company_id');
  2650. * $obj = $do->link(array('local_col', 'linktable:linked_col'));
  2651. *
  2652. * set:
  2653. * $do->link('company_id',0);
  2654. * $do->link('company_id',$obj);
  2655. * $do->link('company_id', array($obj));
  2656. *
  2657. * example function
  2658. *
  2659. * function company() {
  2660. * $this->link(array('company_id','company:id'), func_get_args());
  2661. * }
  2662. *
  2663. *
  2664. *
  2665. * @param mixed $link_spec link specification (normally a string)
  2666. * uses similar rules to joinAdd() array argument.
  2667. * @param mixed $set_value (optional) int, DataObject, or array('set')
  2668. * @author Alan Knowles
  2669. * @access public
  2670. * @return mixed true or false on setting, object on getting
  2671. */
  2672. function link($field, $set_args = array())
  2673. {
  2674. require_once 'DB/DataObject/Links.php';
  2675. $l = new DB_DataObject_Links($this);
  2676. return $l->link($field,$set_args) ;
  2677. }
  2678. /**
  2679. * load related objects
  2680. *
  2681. * Generally not recommended to use this.
  2682. * The generator should support creating getter_setter methods which are better suited.
  2683. *
  2684. * Relies on <dbname>.links.ini
  2685. *
  2686. * Sets properties on the calling dataobject you can change what
  2687. * object vars the links are stored in by changeing the format parameter
  2688. *
  2689. *
  2690. * @param string format (default _%s) where %s is the table name.
  2691. * @author Tim White <tim@cyface.com>
  2692. * @access public
  2693. * @return boolean , true on success
  2694. */
  2695. function getLinks($format = '_%s')
  2696. {
  2697. require_once 'DB/DataObject/Links.php';
  2698. $l = new DB_DataObject_Links($this);
  2699. return $l->applyLinks($format);
  2700. }
  2701. /**
  2702. * deprecited : @use link()
  2703. */
  2704. function getLink($row, $table = null, $link = false)
  2705. {
  2706. require_once 'DB/DataObject/Links.php';
  2707. $l = new DB_DataObject_Links($this);
  2708. return $l->getLink($row, $table === null ? false: $table, $link);
  2709. }
  2710. /**
  2711. * getLinkArray
  2712. * Fetch an array of related objects. This should be used in conjunction with a <dbname>.links.ini file configuration (see the introduction on linking for details on this).
  2713. * You may also use this with all parameters to specify, the column and related table.
  2714. * This is highly dependant on naming columns 'correctly' :)
  2715. * using colname = xxxxx_yyyyyy
  2716. * xxxxxx = related table; (yyyyy = user defined..)
  2717. * looks up table xxxxx, for value id=$this->xxxxx
  2718. * stores it in $this->_xxxxx_yyyyy
  2719. *
  2720. * @access public
  2721. * @param string $column - either column or column.xxxxx
  2722. * @param string $table - name of table to look up value in
  2723. * @return array - array of results (empty array on failure)
  2724. *
  2725. * Example - Getting the related objects
  2726. *
  2727. * $person = new DataObjects_Person;
  2728. * $person->get(12);
  2729. * $children = $person->getLinkArray('children');
  2730. *
  2731. * echo 'There are ', count($children), ' descendant(s):<br />';
  2732. * foreach ($children as $child) {
  2733. * echo $child->name, '<br />';
  2734. * }
  2735. *
  2736. */
  2737. function getLinkArray($row, $table = null)
  2738. {
  2739. require_once 'DB/DataObject/Links.php';
  2740. $l = new DB_DataObject_Links($this);
  2741. return $l->getLinkArray($row, $table === null ? false: $table);
  2742. }
  2743. /**
  2744. * unionAdd - adds another dataobject to this, building a unioned query.
  2745. *
  2746. * usage:
  2747. * $doTable1 = DB_DataObject::factory("table1");
  2748. * $doTable2 = DB_DataObject::factory("table2");
  2749. *
  2750. * $doTable1->selectAdd();
  2751. * $doTable1->selectAdd("col1,col2");
  2752. * $doTable1->whereAdd("col1 > 100");
  2753. * $doTable1->orderBy("col1");
  2754. *
  2755. * $doTable2->selectAdd();
  2756. * $doTable2->selectAdd("col1, col2");
  2757. * $doTable2->whereAdd("col2 = 'v'");
  2758. *
  2759. * $doTable1->unionAdd($doTable2);
  2760. * $doTable1->find();
  2761. *
  2762. * Note: this model may be a better way to implement joinAdd?, eg. do the building in find?
  2763. *
  2764. *
  2765. * @param $obj object|false the union object or false to reset
  2766. * @param optional $is_all string 'ALL' to do all.
  2767. * @returns $obj object|array the added object, or old list if reset.
  2768. */
  2769. function unionAdd($obj,$is_all= '')
  2770. {
  2771. if ($obj === false) {
  2772. $ret = $this->_query['unions'];
  2773. $this->_query['unions'] = array();
  2774. return $ret;
  2775. }
  2776. $this->_query['unions'][] = array($obj, 'UNION ' . $is_all . ' ') ;
  2777. return $obj;
  2778. }
  2779. /**
  2780. * The JOIN condition
  2781. *
  2782. * @access private
  2783. * @var string
  2784. */
  2785. var $_join = '';
  2786. /**
  2787. * joinAdd - adds another dataobject to this, building a joined query.
  2788. *
  2789. * example (requires links.ini to be set up correctly)
  2790. * // get all the images for product 24
  2791. * $i = new DataObject_Image();
  2792. * $pi = new DataObjects_Product_image();
  2793. * $pi->product_id = 24; // set the product id to 24
  2794. * $i->joinAdd($pi); // add the product_image connectoin
  2795. * $i->find();
  2796. * while ($i->fetch()) {
  2797. * // do stuff
  2798. * }
  2799. * // an example with 2 joins
  2800. * // get all the images linked with products or productgroups
  2801. * $i = new DataObject_Image();
  2802. * $pi = new DataObject_Product_image();
  2803. * $pgi = new DataObject_Productgroup_image();
  2804. * $i->joinAdd($pi);
  2805. * $i->joinAdd($pgi);
  2806. * $i->find();
  2807. * while ($i->fetch()) {
  2808. * // do stuff
  2809. * }
  2810. *
  2811. *
  2812. * @param optional $obj object |array the joining object (no value resets the join)
  2813. * If you use an array here it should be in the format:
  2814. * array('local_column','remotetable:remote_column');
  2815. * if remotetable does not have a definition, you should
  2816. * use @ to hide the include error message..
  2817. * array('local_column', $dataobject , 'remote_column');
  2818. * if array has 3 args, then second is assumed to be the linked dataobject.
  2819. *
  2820. * @param optional $joinType string | array
  2821. * 'LEFT'|'INNER'|'RIGHT'|'' Inner is default, '' indicates
  2822. * just select ... from a,b,c with no join and
  2823. * links are added as where items.
  2824. *
  2825. * If second Argument is array, it is assumed to be an associative
  2826. * array with arguments matching below = eg.
  2827. * 'joinType' => 'INNER',
  2828. * 'joinAs' => '...'
  2829. * 'joinCol' => ....
  2830. * 'useWhereAsOn' => false,
  2831. *
  2832. * @param optional $joinAs string if you want to select the table as anther name
  2833. * useful when you want to select multiple columsn
  2834. * from a secondary table.
  2835. * @param optional $joinCol string The column on This objects table to match (needed
  2836. * if this table links to the child object in
  2837. * multiple places eg.
  2838. * user->friend (is a link to another user)
  2839. * user->mother (is a link to another user..)
  2840. *
  2841. * optional 'useWhereAsOn' bool default false;
  2842. * convert the where argments from the object being added
  2843. * into ON arguments.
  2844. *
  2845. *
  2846. * @return none
  2847. * @access public
  2848. * @author Stijn de Reede <sjr@gmx.co.uk>
  2849. */
  2850. function joinAdd($obj = false, $joinType='INNER', $joinAs=false, $joinCol=false)
  2851. {
  2852. global $_DB_DATAOBJECT;
  2853. if ($obj === false) {
  2854. $this->_join = '';
  2855. return;
  2856. }
  2857. //echo '<PRE>'; print_r(func_get_args());
  2858. $useWhereAsOn = false;
  2859. // support for 2nd argument as an array of options
  2860. if (is_array($joinType)) {
  2861. // new options can now go in here... (dont forget to document them)
  2862. $useWhereAsOn = !empty($joinType['useWhereAsOn']);
  2863. $joinCol = isset($joinType['joinCol']) ? $joinType['joinCol'] : $joinCol;
  2864. $joinAs = isset($joinType['joinAs']) ? $joinType['joinAs'] : $joinAs;
  2865. $joinType = isset($joinType['joinType']) ? $joinType['joinType'] : 'INNER';
  2866. }
  2867. // support for array as first argument
  2868. // this assumes that you dont have a links.ini for the specified table.
  2869. // and it doesnt exist as am extended dataobject!! - experimental.
  2870. $ofield = false; // object field
  2871. $tfield = false; // this field
  2872. $toTable = false;
  2873. if (is_array($obj)) {
  2874. $tfield = $obj[0];
  2875. if (count($obj) == 3) {
  2876. $ofield = $obj[2];
  2877. $obj = $obj[1];
  2878. } else {
  2879. list($toTable,$ofield) = explode(':',$obj[1]);
  2880. $obj = is_string($toTable) ? DB_DataObject::factory($toTable) : $toTable;
  2881. if (!$obj || !is_object($obj) || is_a($obj,'PEAR_Error')) {
  2882. $obj = new DB_DataObject;
  2883. $obj->__table = $toTable;
  2884. }
  2885. $obj->_connect();
  2886. }
  2887. // set the table items to nothing.. - eg. do not try and match
  2888. // things in the child table...???
  2889. $items = array();
  2890. }
  2891. if (!is_object($obj) || !is_a($obj,'DB_DataObject')) {
  2892. return $this->raiseError("joinAdd: called without an object", DB_DATAOBJECT_ERROR_NODATA,PEAR_ERROR_DIE);
  2893. }
  2894. /* make sure $this->_database is set. */
  2895. $this->_connect();
  2896. $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  2897. /// CHANGED 26 JUN 2009 - we prefer links from our local table over the remote one.
  2898. /* otherwise see if there are any links from this table to the obj. */
  2899. //print_r($this->links());
  2900. if (($ofield === false) && ($links = $this->links())) {
  2901. // this enables for support for arrays of links in ini file.
  2902. // link contains this_column[] = linked_table:linked_column
  2903. // or standard way.
  2904. // link contains this_column = linked_table:linked_column
  2905. foreach ($links as $k => $linkVar) {
  2906. if (!is_array($linkVar)) {
  2907. $linkVar = array($linkVar);
  2908. }
  2909. foreach($linkVar as $v) {
  2910. /* link contains {this column} = {linked table}:{linked column} */
  2911. $ar = explode(':', $v);
  2912. // Feature Request #4266 - Allow joins with multiple keys
  2913. if (strpos($k, ',') !== false) {
  2914. $k = explode(',', $k);
  2915. }
  2916. if (strpos($ar[1], ',') !== false) {
  2917. $ar[1] = explode(',', $ar[1]);
  2918. }
  2919. if ($ar[0] != $obj->tableName()) {
  2920. continue;
  2921. }
  2922. if ($joinCol !== false) {
  2923. if ($k == $joinCol) {
  2924. // got it!?
  2925. $tfield = $k;
  2926. $ofield = $ar[1];
  2927. break;
  2928. }
  2929. continue;
  2930. }
  2931. $tfield = $k;
  2932. $ofield = $ar[1];
  2933. break;
  2934. }
  2935. }
  2936. }
  2937. /* look up the links for obj table */
  2938. //print_r($obj->links());
  2939. if (!$ofield && ($olinks = $obj->links())) {
  2940. foreach ($olinks as $k => $linkVar) {
  2941. /* link contains {this column} = array ( {linked table}:{linked column} )*/
  2942. if (!is_array($linkVar)) {
  2943. $linkVar = array($linkVar);
  2944. }
  2945. foreach($linkVar as $v) {
  2946. /* link contains {this column} = {linked table}:{linked column} */
  2947. $ar = explode(':', $v);
  2948. // Feature Request #4266 - Allow joins with multiple keys
  2949. $links_key_array = strpos($k,',');
  2950. if ($links_key_array !== false) {
  2951. $k = explode(',', $k);
  2952. }
  2953. $ar_array = strpos($ar[1],',');
  2954. if ($ar_array !== false) {
  2955. $ar[1] = explode(',', $ar[1]);
  2956. }
  2957. if ($ar[0] != $this->tableName()) {
  2958. continue;
  2959. }
  2960. // you have explictly specified the column
  2961. // and the col is listed here..
  2962. // not sure if 1:1 table could cause probs here..
  2963. if ($joinCol !== false) {
  2964. $this->raiseError(
  2965. "joinAdd: You cannot target a join column in the " .
  2966. "'link from' table ({$obj->__table}). " .
  2967. "Either remove the fourth argument to joinAdd() ".
  2968. "({$joinCol}), or alter your links.ini file.",
  2969. DB_DATAOBJECT_ERROR_NODATA);
  2970. return false;
  2971. }
  2972. $ofield = $k;
  2973. $tfield = $ar[1];
  2974. break;
  2975. }
  2976. }
  2977. }
  2978. // finally if these two table have column names that match do a join by default on them
  2979. if (($ofield === false) && $joinCol) {
  2980. $ofield = $joinCol;
  2981. $tfield = $joinCol;
  2982. }
  2983. /* did I find a conneciton between them? */
  2984. if ($ofield === false) {
  2985. $this->raiseError(
  2986. "joinAdd: {$obj->tableName()} has no link with {$this->tableName()}",
  2987. DB_DATAOBJECT_ERROR_NODATA);
  2988. return false;
  2989. }
  2990. $joinType = strtoupper($joinType);
  2991. // we default to joining as the same name (this is remvoed later..)
  2992. if ($joinAs === false) {
  2993. $joinAs = $obj->tableName();
  2994. }
  2995. $quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
  2996. $options = $_DB_DATAOBJECT['CONFIG'];
  2997. // not sure how portable adding database prefixes is..
  2998. $objTable = $quoteIdentifiers ?
  2999. $DB->quoteIdentifier($obj->tableName()) :
  3000. $obj->tableName() ;
  3001. $dbPrefix = '';
  3002. if (strlen($obj->_database) && in_array($DB->dsn['phptype'],array('mysql','mysqli'))) {
  3003. $dbPrefix = ($quoteIdentifiers
  3004. ? $DB->quoteIdentifier($obj->_database)
  3005. : $obj->_database) . '.';
  3006. }
  3007. // if they are the same, then dont add a prefix...
  3008. if ($obj->_database == $this->_database) {
  3009. $dbPrefix = '';
  3010. }
  3011. // as far as we know only mysql supports database prefixes..
  3012. // prefixing the database name is now the default behaviour,
  3013. // as it enables joining mutiple columns from multiple databases...
  3014. // prefix database (quoted if neccessary..)
  3015. $objTable = $dbPrefix . $objTable;
  3016. $cond = '';
  3017. // if obj only a dataobject - eg. no extended class has been defined..
  3018. // it obvioulsy cant work out what child elements might exist...
  3019. // until we get on the fly querying of tables..
  3020. // note: we have already checked that it is_a(db_dataobject earlier)
  3021. if ( strtolower(get_class($obj)) != 'db_dataobject') {
  3022. // now add where conditions for anything that is set in the object
  3023. $items = $obj->table();
  3024. // will return an array if no items..
  3025. // only fail if we where expecting it to work (eg. not joined on a array)
  3026. if (!$items) {
  3027. $this->raiseError(
  3028. "joinAdd: No table definition for {$obj->__table}",
  3029. DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  3030. return false;
  3031. }
  3032. $ignore_null = !isset($options['disable_null_strings'])
  3033. || !is_string($options['disable_null_strings'])
  3034. || strtolower($options['disable_null_strings']) !== 'full' ;
  3035. foreach($items as $k => $v) {
  3036. if (!isset($obj->$k) && $ignore_null) {
  3037. continue;
  3038. }
  3039. $kSql = ($quoteIdentifiers ? $DB->quoteIdentifier($k) : $k);
  3040. if (DB_DataObject::_is_null($obj,$k)) {
  3041. $obj->whereAdd("{$joinAs}.{$kSql} IS NULL");
  3042. continue;
  3043. }
  3044. if ($v & DB_DATAOBJECT_STR) {
  3045. $obj->whereAdd("{$joinAs}.{$kSql} = " . $this->_quote((string) (
  3046. ($v & DB_DATAOBJECT_BOOL) ?
  3047. // this is thanks to the braindead idea of postgres to
  3048. // use t/f for boolean.
  3049. (($obj->$k === 'f') ? 0 : (int)(bool) $obj->$k) :
  3050. $obj->$k
  3051. )));
  3052. continue;
  3053. }
  3054. if (is_numeric($obj->$k)) {
  3055. $obj->whereAdd("{$joinAs}.{$kSql} = {$obj->$k}");
  3056. continue;
  3057. }
  3058. if (is_object($obj->$k) && is_a($obj->$k,'DB_DataObject_Cast')) {
  3059. $value = $obj->$k->toString($v,$DB);
  3060. if (PEAR::isError($value)) {
  3061. $this->raiseError($value->getMessage() ,DB_DATAOBJECT_ERROR_INVALIDARG);
  3062. return false;
  3063. }
  3064. $obj->whereAdd("{$joinAs}.{$kSql} = $value");
  3065. continue;
  3066. }
  3067. /* this is probably an error condition! */
  3068. $obj->whereAdd("{$joinAs}.{$kSql} = 0");
  3069. }
  3070. if ($this->_query === false) {
  3071. $this->raiseError(
  3072. "joinAdd can not be run from a object that has had a query run on it,
  3073. clone the object or create a new one and use setFrom()",
  3074. DB_DATAOBJECT_ERROR_INVALIDARGS);
  3075. return false;
  3076. }
  3077. }
  3078. // and finally merge the whereAdd from the child..
  3079. if ($obj->_query['condition']) {
  3080. $cond = preg_replace('/^\sWHERE/i','',$obj->_query['condition']);
  3081. if (!$useWhereAsOn) {
  3082. $this->whereAdd($cond);
  3083. }
  3084. }
  3085. // nested (join of joined objects..)
  3086. $appendJoin = '';
  3087. if ($obj->_join) {
  3088. // postgres allows nested queries, with ()'s
  3089. // not sure what the results are with other databases..
  3090. // may be unpredictable..
  3091. if (in_array($DB->dsn["phptype"],array('pgsql'))) {
  3092. $objTable = "($objTable {$obj->_join})";
  3093. } else {
  3094. $appendJoin = $obj->_join;
  3095. }
  3096. }
  3097. // fix for #2216
  3098. // add the joinee object's conditions to the ON clause instead of the WHERE clause
  3099. if ($useWhereAsOn && strlen($cond)) {
  3100. $appendJoin = ' AND ' . $cond . ' ' . $appendJoin;
  3101. }
  3102. $table = $this->tableName();
  3103. if ($quoteIdentifiers) {
  3104. $joinAs = $DB->quoteIdentifier($joinAs);
  3105. $table = $DB->quoteIdentifier($table);
  3106. $ofield = (is_array($ofield)) ? array_map(array($DB, 'quoteIdentifier'), $ofield) : $DB->quoteIdentifier($ofield);
  3107. $tfield = (is_array($tfield)) ? array_map(array($DB, 'quoteIdentifier'), $tfield) : $DB->quoteIdentifier($tfield);
  3108. }
  3109. // add database prefix if they are different databases
  3110. $fullJoinAs = '';
  3111. $addJoinAs = ($quoteIdentifiers ? $DB->quoteIdentifier($obj->tableName()) : $obj->tableName()) != $joinAs;
  3112. if ($addJoinAs) {
  3113. // join table a AS b - is only supported by a few databases and is probably not needed
  3114. // , however since it makes the whole Statement alot clearer we are leaving it in
  3115. // for those databases.
  3116. $fullJoinAs = in_array($DB->dsn["phptype"],array('mysql','mysqli','pgsql')) ? "AS {$joinAs}" : $joinAs;
  3117. } else {
  3118. // if
  3119. $joinAs = $dbPrefix . $joinAs;
  3120. }
  3121. switch ($joinType) {
  3122. case 'INNER':
  3123. case 'LEFT':
  3124. case 'RIGHT': // others??? .. cross, left outer, right outer, natural..?
  3125. // Feature Request #4266 - Allow joins with multiple keys
  3126. $jadd = "\n {$joinType} JOIN {$objTable} {$fullJoinAs}";
  3127. //$this->_join .= "\n {$joinType} JOIN {$objTable} {$fullJoinAs}";
  3128. if (is_array($ofield)) {
  3129. $key_count = count($ofield);
  3130. for($i = 0; $i < $key_count; $i++) {
  3131. if ($i == 0) {
  3132. $jadd .= " ON ({$joinAs}.{$ofield[$i]}={$table}.{$tfield[$i]}) ";
  3133. }
  3134. else {
  3135. $jadd .= " AND {$joinAs}.{$ofield[$i]}={$table}.{$tfield[$i]} ";
  3136. }
  3137. }
  3138. $jadd .= ' ' . $appendJoin . ' ';
  3139. } else {
  3140. $jadd .= " ON ({$joinAs}.{$ofield}={$table}.{$tfield}) {$appendJoin} ";
  3141. }
  3142. // jadd avaliable for debugging join build.
  3143. //echo $jadd ."\n";
  3144. $this->_join .= $jadd;
  3145. break;
  3146. case '': // this is just a standard multitable select..
  3147. $this->_join .= "\n , {$objTable} {$fullJoinAs} {$appendJoin}";
  3148. $this->whereAdd("{$joinAs}.{$ofield}={$table}.{$tfield}");
  3149. }
  3150. return true;
  3151. }
  3152. /**
  3153. * autoJoin - using the links.ini file, it builds a query with all the joins
  3154. * usage:
  3155. * $x = DB_DataObject::factory('mytable');
  3156. * $x->autoJoin();
  3157. * $x->get(123);
  3158. * will result in all of the joined data being added to the fetched object..
  3159. *
  3160. * $x = DB_DataObject::factory('mytable');
  3161. * $x->autoJoin();
  3162. * $ar = $x->fetchAll();
  3163. * will result in an array containing all the data from the table, and any joined tables..
  3164. *
  3165. * $x = DB_DataObject::factory('mytable');
  3166. * $jdata = $x->autoJoin();
  3167. * $x->selectAdd(); //reset..
  3168. * foreach($_REQUEST['requested_cols'] as $c) {
  3169. * if (!isset($jdata[$c])) continue; // ignore columns not available..
  3170. * $x->selectAdd( $jdata[$c] . ' as ' . $c);
  3171. * }
  3172. * $ar = $x->fetchAll();
  3173. * will result in only the columns requested being fetched...
  3174. *
  3175. *
  3176. *
  3177. * @param array Configuration
  3178. * exclude Array of columns to exclude from results (eg. modified_by_id)
  3179. * links The equivilant links.ini data for this table eg.
  3180. * array( 'person_id' => 'person:id', .... )
  3181. * include Array of columns to include
  3182. * distinct Array of distinct columns.
  3183. *
  3184. * @return array info about joins
  3185. * cols => map of resulting {joined_tablename}.{joined_table_column_name}
  3186. * join_names => map of resulting {join_name_as}.{joined_table_column_name}
  3187. * count => the column to count on.
  3188. * @access public
  3189. */
  3190. function autoJoin($cfg = array())
  3191. {
  3192. //var_Dump($cfg);exit;
  3193. $pre_links = $this->links();
  3194. if (!empty($cfg['links'])) {
  3195. $this->links(array_merge( $pre_links , $cfg['links']));
  3196. }
  3197. $map = $this->links( );
  3198. //print_r($map);
  3199. $tabdef = $this->table();
  3200. // we need this as normally it's only cleared by an empty selectAs call.
  3201. $keys = array_keys($tabdef);
  3202. if (!empty($cfg['exclude'])) {
  3203. $keys = array_intersect($keys, array_diff($keys, $cfg['exclude']));
  3204. }
  3205. if (!empty($cfg['include'])) {
  3206. $keys = array_intersect($keys, $cfg['include']);
  3207. }
  3208. $selectAs = array();
  3209. if (!empty($keys)) {
  3210. $selectAs = array(array( $keys , '%s', false));
  3211. }
  3212. $ret = array(
  3213. 'cols' => array(),
  3214. 'join_names' => array(),
  3215. 'count' => false,
  3216. );
  3217. $has_distinct = false;
  3218. if (!empty($cfg['distinct']) && $keys) {
  3219. // reset the columsn?
  3220. $cols = array();
  3221. //echo '<PRE>' ;print_r($xx);exit;
  3222. foreach($keys as $c) {
  3223. //var_dump($c);
  3224. if ( $cfg['distinct'] == $c) {
  3225. $has_distinct = 'DISTINCT( ' . $this->tableName() .'.'. $c .') as ' . $c;
  3226. $ret['count'] = 'DISTINCT ' . $this->tableName() .'.'. $c .'';
  3227. continue;
  3228. }
  3229. // cols is in our filtered keys...
  3230. $cols = $c;
  3231. }
  3232. // apply our filtered version, which excludes the distinct column.
  3233. $selectAs = empty($cols) ? array() : array(array(array( $cols) , '%s', false)) ;
  3234. }
  3235. foreach($keys as $k) {
  3236. $ret['cols'][$k] = $this->tableName(). '.' . $k;
  3237. }
  3238. foreach($map as $ocl=>$info) {
  3239. list($tab,$col) = explode(':', $info);
  3240. // what about multiple joins on the same table!!!
  3241. $xx = DB_DataObject::factory($tab);
  3242. if (!is_object($xx) || !is_a($xx, 'DB_DataObject')) {
  3243. continue;
  3244. }
  3245. // skip columns that are excluded.
  3246. // we ignore include here... - as
  3247. // this is borked ... for multiple jions..
  3248. $this->joinAdd($xx, 'LEFT', 'join_'.$ocl.'_'. $col, $ocl);
  3249. if (!empty($cfg['exclude']) && in_array($ocl, $cfg['exclude'])) {
  3250. continue;
  3251. }
  3252. $tabdef = $xx->table();
  3253. $table = $xx->tableName();
  3254. $keys = array_keys($tabdef);
  3255. if (!empty($cfg['exclude'])) {
  3256. $keys = array_intersect($keys, array_diff($keys, $cfg['exclude']));
  3257. foreach($keys as $k) {
  3258. if (in_array($ocl.'_'.$k, $cfg['exclude'])) {
  3259. $keys = array_diff($keys, $k); // removes the k..
  3260. }
  3261. }
  3262. }
  3263. if (!empty($cfg['include'])) {
  3264. // include will basically be BASECOLNAME_joinedcolname
  3265. $nkeys = array();
  3266. foreach($keys as $k) {
  3267. if (in_array( sprintf($ocl.'_%s', $k), $cfg['include'])) {
  3268. $nkeys[] = $k;
  3269. }
  3270. }
  3271. $keys = $nkeys;
  3272. }
  3273. if (empty($keys)) {
  3274. continue;
  3275. }
  3276. // got distinct, and not yet found it..
  3277. if (!$has_distinct && !empty($cfg['distinct'])) {
  3278. $cols = array();
  3279. foreach($keys as $c) {
  3280. $tn = sprintf($ocl.'_%s', $c);
  3281. if ( $tn == $cfg['distinct']) {
  3282. $has_distinct = 'DISTINCT( ' . 'join_'.$ocl.'_'.$col.'.'.$c .') as ' . $tn ;
  3283. $ret['count'] = 'DISTINCT join_'.$ocl.'_'.$col.'.'.$c;
  3284. // var_dump($this->countWhat );
  3285. continue;
  3286. }
  3287. $cols[] = $c;
  3288. }
  3289. if (!empty($cols)) {
  3290. $selectAs[] = array($cols, $ocl.'_%s', 'join_'.$ocl.'_'. $col);
  3291. }
  3292. } else {
  3293. $selectAs[] = array($keys, $ocl.'_%s', 'join_'.$ocl.'_'. $col);
  3294. }
  3295. foreach($keys as $k) {
  3296. $ret['cols'][sprintf('%s_%s', $ocl, $k)] = $tab.'.'.$k;
  3297. $ret['join_names'][sprintf('%s_%s', $ocl, $k)] = sprintf('join_%s_%s.%s',$ocl, $col, $k);
  3298. }
  3299. }
  3300. // fill in the select details..
  3301. $this->selectAdd();
  3302. if ($has_distinct) {
  3303. $this->selectAdd($has_distinct);
  3304. }
  3305. foreach($selectAs as $ar) {
  3306. $this->selectAs($ar[0], $ar[1], $ar[2]);
  3307. }
  3308. // restore links..
  3309. $this->links( $pre_links );
  3310. return $ret;
  3311. }
  3312. /**
  3313. * Factory method for calling DB_DataObject_Cast
  3314. *
  3315. * if used with 1 argument DB_DataObject_Cast::sql($value) is called
  3316. *
  3317. * if used with 2 arguments DB_DataObject_Cast::$value($callvalue) is called
  3318. * valid first arguments are: blob, string, date, sql
  3319. *
  3320. * eg. $member->updated = $member->sqlValue('NOW()');
  3321. *
  3322. *
  3323. * might handle more arguments for escaping later...
  3324. *
  3325. *
  3326. * @param string $value (or type if used with 2 arguments)
  3327. * @param string $callvalue (optional) used with date/null etc..
  3328. */
  3329. function sqlValue($value)
  3330. {
  3331. $method = 'sql';
  3332. if (func_num_args() == 2) {
  3333. $method = $value;
  3334. $value = func_get_arg(1);
  3335. }
  3336. require_once 'DB/DataObject/Cast.php';
  3337. return call_user_func(array('DB_DataObject_Cast', $method), $value);
  3338. }
  3339. /**
  3340. * Copies items that are in the table definitions from an
  3341. * array or object into the current object
  3342. * will not override key values.
  3343. *
  3344. *
  3345. * @param array | object $from
  3346. * @param string $format eg. map xxxx_name to $object->name using 'xxxx_%s' (defaults to %s - eg. name -> $object->name
  3347. * @param boolean $skipEmpty (dont assign empty values if a column is empty (eg. '' / 0 etc...)
  3348. * @access public
  3349. * @return true on success or array of key=>setValue error message
  3350. */
  3351. function setFrom($from, $format = '%s', $skipEmpty=false)
  3352. {
  3353. global $_DB_DATAOBJECT;
  3354. $keys = $this->keys();
  3355. $items = $this->table();
  3356. if (!$items) {
  3357. $this->raiseError(
  3358. "setFrom:Could not find table definition for {$this->tableName()}",
  3359. DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  3360. return;
  3361. }
  3362. $overload_return = array();
  3363. foreach (array_keys($items) as $k) {
  3364. if (in_array($k,$keys)) {
  3365. continue; // dont overwrite keys
  3366. }
  3367. if (!$k) {
  3368. continue; // ignore empty keys!!! what
  3369. }
  3370. $chk = is_object($from) &&
  3371. (version_compare(phpversion(), "5.1.0" , ">=") ?
  3372. property_exists($from, sprintf($format,$k)) : // php5.1
  3373. array_key_exists( sprintf($format,$k), get_class_vars($from)) //older
  3374. );
  3375. // if from has property ($format($k)
  3376. if ($chk) {
  3377. $kk = (strtolower($k) == 'from') ? '_from' : $k;
  3378. if (method_exists($this,'set'.$kk)) {
  3379. $ret = $this->{'set'.$kk}($from->{sprintf($format,$k)});
  3380. if (is_string($ret)) {
  3381. $overload_return[$k] = $ret;
  3382. }
  3383. continue;
  3384. }
  3385. $this->$k = $from->{sprintf($format,$k)};
  3386. continue;
  3387. }
  3388. if (is_object($from)) {
  3389. continue;
  3390. }
  3391. if (empty($from[sprintf($format,$k)]) && $skipEmpty) {
  3392. continue;
  3393. }
  3394. if (!isset($from[sprintf($format,$k)]) && !DB_DataObject::_is_null($from, sprintf($format,$k))) {
  3395. continue;
  3396. }
  3397. $kk = (strtolower($k) == 'from') ? '_from' : $k;
  3398. if (method_exists($this,'set'. $kk)) {
  3399. $ret = $this->{'set'.$kk}($from[sprintf($format,$k)]);
  3400. if (is_string($ret)) {
  3401. $overload_return[$k] = $ret;
  3402. }
  3403. continue;
  3404. }
  3405. $val = $from[sprintf($format,$k)];
  3406. if (is_a($val, 'DB_DataObject_Cast')) {
  3407. $this->$k = $val;
  3408. continue;
  3409. }
  3410. if (is_object($val) || is_array($val)) {
  3411. continue;
  3412. }
  3413. $ret = $this->fromValue($k,$val);
  3414. if ($ret !== true) {
  3415. $overload_return[$k] = 'Not A Valid Value';
  3416. }
  3417. //$this->$k = $from[sprintf($format,$k)];
  3418. }
  3419. if ($overload_return) {
  3420. return $overload_return;
  3421. }
  3422. return true;
  3423. }
  3424. /**
  3425. * Returns an associative array from the current data
  3426. * (kind of oblivates the idea behind DataObjects, but
  3427. * is usefull if you use it with things like QuickForms.
  3428. *
  3429. * you can use the format to return things like user[key]
  3430. * by sending it $object->toArray('user[%s]')
  3431. *
  3432. * will also return links converted to arrays.
  3433. *
  3434. * @param string sprintf format for array
  3435. * @param bool||number [true = elemnts that have a value set],
  3436. * [false = table + returned colums] ,
  3437. * [0 = returned columsn only]
  3438. *
  3439. * @access public
  3440. * @return array of key => value for row
  3441. */
  3442. function toArray($format = '%s', $hideEmpty = false)
  3443. {
  3444. global $_DB_DATAOBJECT;
  3445. // we use false to ignore sprintf.. (speed up..)
  3446. $format = $format == '%s' ? false : $format;
  3447. $ret = array();
  3448. $rf = ($this->_resultFields !== false) ? $this->_resultFields :
  3449. (isset($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid]) ?
  3450. $_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid] : false);
  3451. $ar = ($rf !== false) ?
  3452. (($hideEmpty === 0) ? $rf : array_merge($rf, $this->table())) :
  3453. $this->table();
  3454. foreach($ar as $k=>$v) {
  3455. if (!isset($this->$k)) {
  3456. if (!$hideEmpty) {
  3457. $ret[$format === false ? $k : sprintf($format,$k)] = '';
  3458. }
  3459. continue;
  3460. }
  3461. // call the overloaded getXXXX() method. - except getLink and getLinks
  3462. if (method_exists($this,'get'.$k) && !in_array(strtolower($k),array('links','link'))) {
  3463. $ret[$format === false ? $k : sprintf($format,$k)] = $this->{'get'.$k}();
  3464. continue;
  3465. }
  3466. // should this call toValue() ???
  3467. $ret[$format === false ? $k : sprintf($format,$k)] = $this->$k;
  3468. }
  3469. if (!$this->_link_loaded) {
  3470. return $ret;
  3471. }
  3472. foreach($this->_link_loaded as $k) {
  3473. $ret[$format === false ? $k : sprintf($format,$k)] = $this->$k->toArray();
  3474. }
  3475. return $ret;
  3476. }
  3477. /**
  3478. * validate the values of the object (usually prior to inserting/updating..)
  3479. *
  3480. * Note: This was always intended as a simple validation routine.
  3481. * It lacks understanding of field length, whether you are inserting or updating (and hence null key values)
  3482. *
  3483. * This should be moved to another class: DB_DataObject_Validate
  3484. * FEEL FREE TO SEND ME YOUR VERSION FOR CONSIDERATION!!!
  3485. *
  3486. * Usage:
  3487. * if (is_array($ret = $obj->validate())) { ... there are problems with the data ... }
  3488. *
  3489. * Logic:
  3490. * - defaults to only testing strings/numbers if numbers or strings are the correct type and null values are correct
  3491. * - validate Column methods : "validate{ROWNAME}()" are called if they are defined.
  3492. * These methods should return
  3493. * true = everything ok
  3494. * false|object = something is wrong!
  3495. *
  3496. * - This method loads and uses the PEAR Validate Class.
  3497. *
  3498. *
  3499. * @access public
  3500. * @return array of validation results (where key=>value, value=false|object if it failed) or true (if they all succeeded)
  3501. */
  3502. function validate()
  3503. {
  3504. global $_DB_DATAOBJECT;
  3505. require_once 'Validate.php';
  3506. $table = $this->table();
  3507. $ret = array();
  3508. $seq = $this->sequenceKey();
  3509. $options = $_DB_DATAOBJECT['CONFIG'];
  3510. foreach($table as $key => $val) {
  3511. // call user defined validation always...
  3512. $method = "Validate" . ucfirst($key);
  3513. if (method_exists($this, $method)) {
  3514. $ret[$key] = $this->$method();
  3515. continue;
  3516. }
  3517. // if not null - and it's not set.......
  3518. if ($val & DB_DATAOBJECT_NOTNULL && DB_DataObject::_is_null($this, $key)) {
  3519. // dont check empty sequence key values..
  3520. if (($key == $seq[0]) && ($seq[1] == true)) {
  3521. continue;
  3522. }
  3523. $ret[$key] = false;
  3524. continue;
  3525. }
  3526. if (DB_DataObject::_is_null($this, $key)) {
  3527. if ($val & DB_DATAOBJECT_NOTNULL) {
  3528. $this->debug("'null' field used for '$key', but it is defined as NOT NULL", 'VALIDATION', 4);
  3529. $ret[$key] = false;
  3530. continue;
  3531. }
  3532. continue;
  3533. }
  3534. // ignore things that are not set. ?
  3535. if (!isset($this->$key)) {
  3536. continue;
  3537. }
  3538. // if the string is empty.. assume it is ok..
  3539. if (!is_object($this->$key) && !is_array($this->$key) && !strlen((string) $this->$key)) {
  3540. continue;
  3541. }
  3542. // dont try and validate cast objects - assume they are problably ok..
  3543. if (is_object($this->$key) && is_a($this->$key,'DB_DataObject_Cast')) {
  3544. continue;
  3545. }
  3546. // at this point if you have set something to an object, and it's not expected
  3547. // the Validate will probably break!!... - rightly so! (your design is broken,
  3548. // so issuing a runtime error like PEAR_Error is probably not appropriate..
  3549. switch (true) {
  3550. // todo: date time.....
  3551. case ($val & DB_DATAOBJECT_STR):
  3552. $ret[$key] = Validate::string($this->$key, VALIDATE_PUNCTUATION . VALIDATE_NAME);
  3553. continue;
  3554. case ($val & DB_DATAOBJECT_INT):
  3555. $ret[$key] = Validate::number($this->$key, array('decimal'=>'.'));
  3556. continue;
  3557. }
  3558. }
  3559. // if any of the results are false or an object (eg. PEAR_Error).. then return the array..
  3560. foreach ($ret as $key => $val) {
  3561. if ($val !== true) {
  3562. return $ret;
  3563. }
  3564. }
  3565. return true; // everything is OK.
  3566. }
  3567. /**
  3568. * Gets the DB object related to an object - so you can use funky peardb stuf with it :)
  3569. *
  3570. * @access public
  3571. * @return object The DB connection
  3572. */
  3573. function getDatabaseConnection()
  3574. {
  3575. global $_DB_DATAOBJECT;
  3576. if (($e = $this->_connect()) !== true) {
  3577. return $e;
  3578. }
  3579. if (!isset($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
  3580. $r = false;
  3581. return $r;
  3582. }
  3583. return $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  3584. }
  3585. /**
  3586. * Gets the DB result object related to the objects active query
  3587. * - so you can use funky pear stuff with it - like pager for example.. :)
  3588. *
  3589. * @access public
  3590. * @return object The DB result object
  3591. */
  3592. function getDatabaseResult()
  3593. {
  3594. global $_DB_DATAOBJECT;
  3595. $this->_connect();
  3596. if (!isset($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid])) {
  3597. $r = false;
  3598. return $r;
  3599. }
  3600. return $_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid];
  3601. }
  3602. /**
  3603. * Overload Extension support
  3604. * - enables setCOLNAME/getCOLNAME
  3605. * if you define a set/get method for the item it will be called.
  3606. * otherwise it will just return/set the value.
  3607. * NOTE this currently means that a few Names are NO-NO's
  3608. * eg. links,link,linksarray, from, Databaseconnection,databaseresult
  3609. *
  3610. * note
  3611. * - set is automatically called by setFrom.
  3612. * - get is automatically called by toArray()
  3613. *
  3614. * setters return true on success. = strings on failure
  3615. * getters return the value!
  3616. *
  3617. * this fires off trigger_error - if any problems.. pear_error,
  3618. * has problems with 4.3.2RC2 here
  3619. *
  3620. * @access public
  3621. * @return true?
  3622. * @see overload
  3623. */
  3624. function _call($method,$params,&$return) {
  3625. //$this->debug("ATTEMPTING OVERLOAD? $method");
  3626. // ignore constructors : - mm
  3627. if (strtolower($method) == strtolower(get_class($this))) {
  3628. return true;
  3629. }
  3630. $type = strtolower(substr($method,0,3));
  3631. $class = get_class($this);
  3632. if (($type != 'set') && ($type != 'get')) {
  3633. return false;
  3634. }
  3635. // deal with naming conflick of setFrom = this is messy ATM!
  3636. if (strtolower($method) == 'set_from') {
  3637. $return = $this->toValue('from',isset($params[0]) ? $params[0] : null);
  3638. return true;
  3639. }
  3640. $element = substr($method,3);
  3641. // dont you just love php's case insensitivity!!!!
  3642. $array = array_keys(get_class_vars($class));
  3643. /* php5 version which segfaults on 5.0.3 */
  3644. if (class_exists('ReflectionClass')) {
  3645. $reflection = new ReflectionClass($class);
  3646. $array = array_keys($reflection->getdefaultProperties());
  3647. }
  3648. if (!in_array($element,$array)) {
  3649. // munge case
  3650. foreach($array as $k) {
  3651. $case[strtolower($k)] = $k;
  3652. }
  3653. if ((substr(phpversion(),0,1) == 5) && isset($case[strtolower($element)])) {
  3654. trigger_error("PHP5 set/get calls should match the case of the variable",E_USER_WARNING);
  3655. $element = strtolower($element);
  3656. }
  3657. // does it really exist?
  3658. if (!isset($case[$element])) {
  3659. return false;
  3660. }
  3661. // use the mundged case
  3662. $element = $case[$element]; // real case !
  3663. }
  3664. if ($type == 'get') {
  3665. $return = $this->toValue($element,isset($params[0]) ? $params[0] : null);
  3666. return true;
  3667. }
  3668. $return = $this->fromValue($element, $params[0]);
  3669. return true;
  3670. }
  3671. /**
  3672. * standard set* implementation.
  3673. *
  3674. * takes data and uses it to set dates/strings etc.
  3675. * normally called from __call..
  3676. *
  3677. * Current supports
  3678. * date = using (standard time format, or unixtimestamp).... so you could create a method :
  3679. * function setLastread($string) { $this->fromValue('lastread',strtotime($string)); }
  3680. *
  3681. * time = using strtotime
  3682. * datetime = using same as date - accepts iso standard or unixtimestamp.
  3683. * string = typecast only..
  3684. *
  3685. * TODO: add formater:: eg. d/m/Y for date! ???
  3686. *
  3687. * @param string column of database
  3688. * @param mixed value to assign
  3689. *
  3690. * @return true| false (False on error)
  3691. * @access public
  3692. * @see DB_DataObject::_call
  3693. */
  3694. function fromValue($col,$value)
  3695. {
  3696. global $_DB_DATAOBJECT;
  3697. $options = $_DB_DATAOBJECT['CONFIG'];
  3698. $cols = $this->table();
  3699. // dont know anything about this col..
  3700. if (!isset($cols[$col]) || is_a($value, 'DB_DataObject_Cast')) {
  3701. $this->$col = $value;
  3702. return true;
  3703. }
  3704. //echo "FROM VALUE $col, {$cols[$col]}, $value\n";
  3705. switch (true) {
  3706. // set to null and column is can be null...
  3707. case ((!($cols[$col] & DB_DATAOBJECT_NOTNULL)) && DB_DataObject::_is_null($value, false)):
  3708. case (is_object($value) && is_a($value,'DB_DataObject_Cast')):
  3709. $this->$col = $value;
  3710. return true;
  3711. // fail on setting null on a not null field..
  3712. case (($cols[$col] & DB_DATAOBJECT_NOTNULL) && DB_DataObject::_is_null($value,false)):
  3713. return false;
  3714. case (($cols[$col] & DB_DATAOBJECT_DATE) && ($cols[$col] & DB_DATAOBJECT_TIME)):
  3715. // empty values get set to '' (which is inserted/updated as NULl
  3716. if (!$value) {
  3717. $this->$col = '';
  3718. }
  3719. if (is_numeric($value)) {
  3720. $this->$col = date('Y-m-d H:i:s', $value);
  3721. return true;
  3722. }
  3723. // eak... - no way to validate date time otherwise...
  3724. $this->$col = (string) $value;
  3725. return true;
  3726. case ($cols[$col] & DB_DATAOBJECT_DATE):
  3727. // empty values get set to '' (which is inserted/updated as NULl
  3728. if (!$value) {
  3729. $this->$col = '';
  3730. return true;
  3731. }
  3732. if (is_numeric($value)) {
  3733. $this->$col = date('Y-m-d',$value);
  3734. return true;
  3735. }
  3736. // try date!!!!
  3737. require_once 'Date.php';
  3738. $x = new Date($value);
  3739. $this->$col = $x->format("%Y-%m-%d");
  3740. return true;
  3741. case ($cols[$col] & DB_DATAOBJECT_TIME):
  3742. // empty values get set to '' (which is inserted/updated as NULl
  3743. if (!$value) {
  3744. $this->$col = '';
  3745. }
  3746. $guess = strtotime($value);
  3747. if ($guess != -1) {
  3748. $this->$col = date('H:i:s', $guess);
  3749. return $return = true;
  3750. }
  3751. // otherwise an error in type...
  3752. return false;
  3753. case ($cols[$col] & DB_DATAOBJECT_STR):
  3754. $this->$col = (string) $value;
  3755. return true;
  3756. // todo : floats numerics and ints...
  3757. default:
  3758. $this->$col = $value;
  3759. return true;
  3760. }
  3761. }
  3762. /**
  3763. * standard get* implementation.
  3764. *
  3765. * with formaters..
  3766. * supported formaters:
  3767. * date/time : %d/%m/%Y (eg. php strftime) or pear::Date
  3768. * numbers : %02d (eg. sprintf)
  3769. * NOTE you will get unexpected results with times like 0000-00-00 !!!
  3770. *
  3771. *
  3772. *
  3773. * @param string column of database
  3774. * @param format foramt
  3775. *
  3776. * @return true Description
  3777. * @access public
  3778. * @see DB_DataObject::_call(),strftime(),Date::format()
  3779. */
  3780. function toValue($col,$format = null)
  3781. {
  3782. if (is_null($format)) {
  3783. return $this->$col;
  3784. }
  3785. $cols = $this->table();
  3786. switch (true) {
  3787. case (($cols[$col] & DB_DATAOBJECT_DATE) && ($cols[$col] & DB_DATAOBJECT_TIME)):
  3788. if (!$this->$col) {
  3789. return '';
  3790. }
  3791. $guess = strtotime($this->$col);
  3792. if ($guess != -1) {
  3793. return strftime($format, $guess);
  3794. }
  3795. // eak... - no way to validate date time otherwise...
  3796. return $this->$col;
  3797. case ($cols[$col] & DB_DATAOBJECT_DATE):
  3798. if (!$this->$col) {
  3799. return '';
  3800. }
  3801. $guess = strtotime($this->$col);
  3802. if ($guess != -1) {
  3803. return strftime($format,$guess);
  3804. }
  3805. // try date!!!!
  3806. require_once 'Date.php';
  3807. $x = new Date($this->$col);
  3808. return $x->format($format);
  3809. case ($cols[$col] & DB_DATAOBJECT_TIME):
  3810. if (!$this->$col) {
  3811. return '';
  3812. }
  3813. $guess = strtotime($this->$col);
  3814. if ($guess > -1) {
  3815. return strftime($format, $guess);
  3816. }
  3817. // otherwise an error in type...
  3818. return $this->$col;
  3819. case ($cols[$col] & DB_DATAOBJECT_MYSQLTIMESTAMP):
  3820. if (!$this->$col) {
  3821. return '';
  3822. }
  3823. require_once 'Date.php';
  3824. $x = new Date($this->$col);
  3825. return $x->format($format);
  3826. case ($cols[$col] & DB_DATAOBJECT_BOOL):
  3827. if ($cols[$col] & DB_DATAOBJECT_STR) {
  3828. // it's a 't'/'f' !
  3829. return ($this->$col === 't');
  3830. }
  3831. return (bool) $this->$col;
  3832. default:
  3833. return sprintf($format,$this->col);
  3834. }
  3835. }
  3836. /* ----------------------- Debugger ------------------ */
  3837. /**
  3838. * Debugger. - use this in your extended classes to output debugging information.
  3839. *
  3840. * Uses DB_DataObject::DebugLevel(x) to turn it on
  3841. *
  3842. * @param string $message - message to output
  3843. * @param string $logtype - bold at start
  3844. * @param string $level - output level
  3845. * @access public
  3846. * @return none
  3847. */
  3848. function debug($message, $logtype = 0, $level = 1)
  3849. {
  3850. global $_DB_DATAOBJECT;
  3851. if (empty($_DB_DATAOBJECT['CONFIG']['debug']) ||
  3852. (is_numeric($_DB_DATAOBJECT['CONFIG']['debug']) && $_DB_DATAOBJECT['CONFIG']['debug'] < $level)) {
  3853. return;
  3854. }
  3855. // this is a bit flaky due to php's wonderfull class passing around crap..
  3856. // but it's about as good as it gets..
  3857. $class = (isset($this) && is_a($this,'DB_DataObject')) ? get_class($this) : 'DB_DataObject';
  3858. if (!is_string($message)) {
  3859. $message = print_r($message,true);
  3860. }
  3861. if (!is_numeric( $_DB_DATAOBJECT['CONFIG']['debug']) && is_callable( $_DB_DATAOBJECT['CONFIG']['debug'])) {
  3862. return call_user_func($_DB_DATAOBJECT['CONFIG']['debug'], $class, $message, $logtype, $level);
  3863. }
  3864. if (!ini_get('html_errors')) {
  3865. echo "$class : $logtype : $message\n";
  3866. flush();
  3867. return;
  3868. }
  3869. if (!is_string($message)) {
  3870. $message = print_r($message,true);
  3871. }
  3872. $colorize = ($logtype == 'ERROR') ? '<font color="red">' : '<font>';
  3873. echo "<code>{$colorize}<B>$class: $logtype:</B> ". nl2br(htmlspecialchars($message)) . "</font></code><BR>\n";
  3874. }
  3875. /**
  3876. * sets and returns debug level
  3877. * eg. DB_DataObject::debugLevel(4);
  3878. *
  3879. * @param int $v level
  3880. * @access public
  3881. * @return none
  3882. */
  3883. static function debugLevel($v = null)
  3884. {
  3885. global $_DB_DATAOBJECT;
  3886. if (empty($_DB_DATAOBJECT['CONFIG'])) {
  3887. DB_DataObject::_loadConfig();
  3888. }
  3889. if ($v !== null) {
  3890. $r = isset($_DB_DATAOBJECT['CONFIG']['debug']) ? $_DB_DATAOBJECT['CONFIG']['debug'] : 0;
  3891. $_DB_DATAOBJECT['CONFIG']['debug'] = $v;
  3892. return $r;
  3893. }
  3894. return isset($_DB_DATAOBJECT['CONFIG']['debug']) ? $_DB_DATAOBJECT['CONFIG']['debug'] : 0;
  3895. }
  3896. /**
  3897. * Last Error that has occured
  3898. * - use $this->_lastError or
  3899. * $last_error = PEAR::getStaticProperty('DB_DataObject','lastError');
  3900. *
  3901. * @access public
  3902. * @var object PEAR_Error (or false)
  3903. */
  3904. var $_lastError = false;
  3905. /**
  3906. * Default error handling is to create a pear error, but never return it.
  3907. * if you need to handle errors you should look at setting the PEAR_Error callback
  3908. * this is due to the fact it would wreck havoc on the internal methods!
  3909. *
  3910. * @param int $message message
  3911. * @param int $type type
  3912. * @param int $behaviour behaviour (die or continue!);
  3913. * @access public
  3914. * @return error object
  3915. */
  3916. function raiseError($message, $type = null, $behaviour = null)
  3917. {
  3918. global $_DB_DATAOBJECT;
  3919. if ($behaviour == PEAR_ERROR_DIE && !empty($_DB_DATAOBJECT['CONFIG']['dont_die'])) {
  3920. $behaviour = null;
  3921. }
  3922. $error = &PEAR::getStaticProperty('DB_DataObject','lastError');
  3923. // no checks for production here?....... - we log errors before we throw them.
  3924. DB_DataObject::debug($message,'ERROR',1);
  3925. if (PEAR::isError($message)) {
  3926. $error = $message;
  3927. } else {
  3928. require_once 'DB/DataObject/Error.php';
  3929. $dor = new PEAR();
  3930. $error = $dor->raiseError($message, $type, $behaviour,
  3931. $opts=null, $userinfo=null, 'DB_DataObject_Error'
  3932. );
  3933. }
  3934. // this will never work totally with PHP's object model.
  3935. // as this is passed on static calls (like staticGet in our case)
  3936. $_DB_DATAOBJECT['LASTERROR'] = $error;
  3937. if (isset($this) && is_object($this) && is_subclass_of($this,'db_dataobject')) {
  3938. $this->_lastError = $error;
  3939. }
  3940. return $error;
  3941. }
  3942. /**
  3943. * Define the global $_DB_DATAOBJECT['CONFIG'] as an alias to PEAR::getStaticProperty('DB_DataObject','options');
  3944. *
  3945. * After Profiling DB_DataObject, I discoved that the debug calls where taking
  3946. * considerable time (well 0.1 ms), so this should stop those calls happening. as
  3947. * all calls to debug are wrapped with direct variable queries rather than actually calling the funciton
  3948. * THIS STILL NEEDS FURTHER INVESTIGATION
  3949. *
  3950. * @access public
  3951. * @return object an error object
  3952. */
  3953. function _loadConfig()
  3954. {
  3955. global $_DB_DATAOBJECT;
  3956. $_DB_DATAOBJECT['CONFIG'] = &PEAR::getStaticProperty('DB_DataObject','options');
  3957. }
  3958. /**
  3959. * Free global arrays associated with this object.
  3960. *
  3961. *
  3962. * @access public
  3963. * @return none
  3964. */
  3965. function free()
  3966. {
  3967. global $_DB_DATAOBJECT;
  3968. if (isset($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid])) {
  3969. unset($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid]);
  3970. }
  3971. if (isset($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid])) {
  3972. unset($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid]);
  3973. }
  3974. // clear the staticGet cache as well.
  3975. $this->_clear_cache();
  3976. // this is a huge bug in DB!
  3977. if (isset($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
  3978. $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->num_rows = array();
  3979. }
  3980. if (is_array($this->_link_loaded)) {
  3981. foreach ($this->_link_loaded as $do) {
  3982. if (
  3983. !empty($this->{$do}) &&
  3984. is_object($this->{$do}) &&
  3985. method_exists($this->{$do}, 'free')
  3986. ) {
  3987. $this->{$do}->free();
  3988. }
  3989. }
  3990. }
  3991. }
  3992. /**
  3993. * Evaluate whether or not a value is set to null, taking the 'disable_null_strings' option into account.
  3994. * If the value is a string set to "null" and the "disable_null_strings" option is not set to
  3995. * true, then the value is considered to be null.
  3996. * If the value is actually a PHP NULL value, and "disable_null_strings" has been set to
  3997. * the value "full", then it will also be considered null. - this can not differenticate between not set
  3998. *
  3999. *
  4000. * @param object|array $obj_or_ar
  4001. * @param string|false $prop prperty
  4002. * @access private
  4003. * @return bool object
  4004. */
  4005. function _is_null($obj_or_ar , $prop)
  4006. {
  4007. global $_DB_DATAOBJECT;
  4008. $isset = $prop === false ? isset($obj_or_ar) :
  4009. (is_array($obj_or_ar) ? isset($obj_or_ar[$prop]) : isset($obj_or_ar->$prop));
  4010. $value = $isset ?
  4011. ($prop === false ? $obj_or_ar :
  4012. (is_array($obj_or_ar) ? $obj_or_ar[$prop] : $obj_or_ar->$prop))
  4013. : null;
  4014. $options = $_DB_DATAOBJECT['CONFIG'];
  4015. $null_strings = !isset($options['disable_null_strings'])
  4016. || $options['disable_null_strings'] === false;
  4017. $crazy_null = isset($options['disable_null_strings'])
  4018. && is_string($options['disable_null_strings'])
  4019. && strtolower($options['disable_null_strings'] === 'full');
  4020. if ( $null_strings && $isset && is_string($value) && (strtolower($value) === 'null') ) {
  4021. return true;
  4022. }
  4023. if ( $crazy_null && !$isset ) {
  4024. return true;
  4025. }
  4026. return false;
  4027. }
  4028. /**
  4029. * (deprecated - use ::get / and your own caching method)
  4030. */
  4031. static function staticGet($class, $k, $v = null)
  4032. {
  4033. $lclass = strtolower($class);
  4034. global $_DB_DATAOBJECT;
  4035. if (empty($_DB_DATAOBJECT['CONFIG'])) {
  4036. DB_DataObject::_loadConfig();
  4037. }
  4038. $key = "$k:$v";
  4039. if ($v === null) {
  4040. $key = $k;
  4041. }
  4042. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  4043. DB_DataObject::debug("$class $key","STATIC GET - TRY CACHE");
  4044. }
  4045. if (!empty($_DB_DATAOBJECT['CACHE'][$lclass][$key])) {
  4046. return $_DB_DATAOBJECT['CACHE'][$lclass][$key];
  4047. }
  4048. if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  4049. DB_DataObject::debug("$class $key","STATIC GET - NOT IN CACHE");
  4050. }
  4051. $obj = DB_DataObject::factory(substr($class,strlen($_DB_DATAOBJECT['CONFIG']['class_prefix'])));
  4052. if (PEAR::isError($obj)) {
  4053. $dor = new DB_DataObject();
  4054. $dor->raiseError("could not autoload $class", DB_DATAOBJECT_ERROR_NOCLASS);
  4055. $r = false;
  4056. return $r;
  4057. }
  4058. if (!isset($_DB_DATAOBJECT['CACHE'][$lclass])) {
  4059. $_DB_DATAOBJECT['CACHE'][$lclass] = array();
  4060. }
  4061. if (!$obj->get($k,$v)) {
  4062. $dor = new DB_DataObject();
  4063. $dor->raiseError("No Data return from get $k $v", DB_DATAOBJECT_ERROR_NODATA);
  4064. $r = false;
  4065. return $r;
  4066. }
  4067. $_DB_DATAOBJECT['CACHE'][$lclass][$key] = $obj;
  4068. return $_DB_DATAOBJECT['CACHE'][$lclass][$key];
  4069. }
  4070. /**
  4071. * autoload Class relating to a table
  4072. * (deprecited - use ::factory)
  4073. *
  4074. * @param string $table table
  4075. * @access private
  4076. * @return string classname on Success
  4077. */
  4078. function staticAutoloadTable($table)
  4079. {
  4080. global $_DB_DATAOBJECT;
  4081. if (empty($_DB_DATAOBJECT['CONFIG'])) {
  4082. DB_DataObject::_loadConfig();
  4083. }
  4084. $p = isset($_DB_DATAOBJECT['CONFIG']['class_prefix']) ?
  4085. $_DB_DATAOBJECT['CONFIG']['class_prefix'] : '';
  4086. $class = $p . preg_replace('/[^A-Z0-9]/i','_',ucfirst($table));
  4087. $ce = substr(phpversion(),0,1) > 4 ? class_exists($class,false) : class_exists($class);
  4088. $class = $ce ? $class : DB_DataObject::_autoloadClass($class);
  4089. return $class;
  4090. }
  4091. /* ---- LEGACY BC METHODS - NOT DOCUMENTED - See Documentation on New Methods. ---*/
  4092. function _get_table() { return $this->table(); }
  4093. function _get_keys() { return $this->keys(); }
  4094. }
  4095. // technially 4.3.2RC1 was broken!!
  4096. // looks like 4.3.3 may have problems too....
  4097. if (!defined('DB_DATAOBJECT_NO_OVERLOAD')) {
  4098. if ((phpversion() != '4.3.2-RC1') && (version_compare( phpversion(), "4.3.1") > 0)) {
  4099. if (version_compare( phpversion(), "5") < 0) {
  4100. overload('DB_DataObject');
  4101. }
  4102. $GLOBALS['_DB_DATAOBJECT']['OVERLOADED'] = true;
  4103. }
  4104. }