DataObject.php 166 KB

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