database.php 99 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208
  1. <?php
  2. /*
  3. FusionPBX
  4. Version: MPL 1.1
  5. The contents of this file are subject to the Mozilla Public License Version
  6. 1.1 (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.mozilla.org/MPL/
  9. Software distributed under the License is distributed on an "AS IS" basis,
  10. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11. for the specific language governing rights and limitations under the
  12. License.
  13. The Original Code is FusionPBX
  14. The Initial Developer of the Original Code is
  15. Mark J Crane <[email protected]>
  16. Copyright (C) 2010 - 2022
  17. All Rights Reserved.
  18. Contributor(s):
  19. Mark J Crane <[email protected]>
  20. Luis Daniel Lucio Quiroz <[email protected]>
  21. */
  22. //define the database class
  23. if (!class_exists('database')) {
  24. class database {
  25. const TABLE_PREFIX = "v_";
  26. /**
  27. * Database connection
  28. * @access private
  29. * @var PDO object
  30. */
  31. public $db;
  32. /**
  33. * Driver to use.
  34. * @access public
  35. * @var string Can be pgsql, mysql, sqlite, odbc
  36. */
  37. public $driver;
  38. /**
  39. * Alias of driver.
  40. * @access public
  41. * @var string Can be pgsql, mysql, sqlite, odbc
  42. * @see $driver
  43. */
  44. public $type;
  45. /**
  46. * Host for database connection
  47. * @access public
  48. * @var string host name or IP address.
  49. */
  50. public $host;
  51. /**
  52. * Port number
  53. * @access public
  54. * @var int 1025 - 65534
  55. */
  56. public $port;
  57. /**
  58. * Database name
  59. * @access public
  60. * @var string
  61. */
  62. public $db_name;
  63. /**
  64. * Database security
  65. * @access public
  66. * @var boolean
  67. */
  68. public $db_secure;
  69. /**
  70. * Specifies the file name of the client SSL certificate
  71. * @access public
  72. * @var string full path
  73. */
  74. public $db_cert_authority;
  75. /**
  76. * Username used to connect
  77. * @access public
  78. * @var string
  79. */
  80. public $username;
  81. /**
  82. * Password used to connect
  83. * @access public
  84. * @var string
  85. */
  86. public $password;
  87. /**
  88. * Full path to file name.
  89. * @access public
  90. * @var string full path to file name
  91. */
  92. public $path;
  93. /**
  94. * Table name.
  95. * @access private
  96. * @var string sanitized
  97. */
  98. private $table;
  99. /**
  100. * Where clause(s) of an SQL statement.
  101. * <p>Array of arrays must be passed with each having the
  102. * following keys:
  103. * <ol><li>'name' - Any valid column name.</li>
  104. * <li>'operator' - Must be <b>one</b> of the following values: =, &gt;, &lt;, &gt;=, &lt;=, &lt;&gt;, !=</li>
  105. * <li>'value' - Value being matched</li></ol></p>
  106. * <p>Example Usage:</p>
  107. * <p><code>$db->where['SearchTerm'] = ['name'=>'MyColumn','operator'=>'=','value'=>'MySearchTerm'</code></p>
  108. * <p><code>$db->where['NextSearchTerm'] = ['name'=>'MyColumn','operator'=>'=','value'=>'MyOtherSearchTerm'</code></p>
  109. * <p>Below is equivalent to the above.</p>
  110. * <p><code>$db->where[0] = ['name'=>'MyColumn','operator'=>'=','value'=>'MyValue'</code></p>
  111. * <p><code>$db->where[1] = ['name'=>'MyColumn','operator'=>'=&gt;','value'=>'MyValue'</code></p>
  112. * @access public
  113. * @var array Two dimensional array of key value pairs
  114. * @see $order_by
  115. */
  116. public $where; //array
  117. /**
  118. * Order By clause(s) of an SQL statement.
  119. * <p>Array of arrays must be passed with each having the
  120. * following keys:
  121. * <ol><li>'name' - Any valid column name.</li>
  122. * <li>'operator' - Must be <b>one</b> of the following values: =, &gt;, &lt;, &gt;=, &lt;=, &lt;&gt;, !=</li>
  123. * <li>'value' - Value being matched</li></ol></p>
  124. * <p>Example Usage:</p>
  125. * <p><code>$db->where['SearchTerm'] = ['name'=>'MyColumn','operator'=>'=','value'=>'MySearchTerm'</code></p>
  126. * <p><code>$db->where['NextSearchTerm'] = ['name'=>'MyColumn','operator'=>'=','value'=>'MyOtherSearchTerm'</code></p>
  127. * <p>Below is equivalent to the above.</p>
  128. * <p><code>$db->where[0] = ['name'=>'MyColumn','operator'=>'=','value'=>'MyValue'</code></p>
  129. * <p><code>$db->where[1] = ['name'=>'MyColumn','operator'=>'=&gt;','value'=>'MyValue'</code></p>
  130. * @access private
  131. * @var array Two dimensional array of key value pairs
  132. * @see $where
  133. */
  134. public $order_by; //array
  135. /**
  136. * Ascending or Descending order.
  137. * @var string
  138. * @access public
  139. */
  140. public $order_type;
  141. /**
  142. * Numerical value to limit returned results.
  143. * @var int Used for 'LIMIT' in SQL statement.
  144. * @access public
  145. */
  146. public $limit;
  147. /**
  148. * Numerical value to offset returned results.
  149. * @var int Used for 'OFFSET' in SQL statement.
  150. * @access public
  151. */
  152. public $offset;
  153. /**
  154. * <p>Array of fields.</p>
  155. * <p>Fields are specified in 'name'=>'value' format.
  156. * <p>Used by {@link database::add() } and {@link database::update() }</p>
  157. * @access public
  158. * @var array Array of columns
  159. * @see database::add()
  160. * @see database::update()
  161. */
  162. public $fields;
  163. /**
  164. * Unknown property
  165. * @var unknown
  166. * @access public
  167. */
  168. public $count;
  169. /**
  170. * Unknown property
  171. * @var unknown
  172. * @access public
  173. */
  174. public $sql;
  175. /**
  176. * <p>Stores the result from the most recent query. The type will be based on what was requested.</p>
  177. * <p><b>NOTE:</b> If an error occurred on the last query the result is set to an empty string.</p>
  178. * @var mixed
  179. */
  180. private $result;
  181. /**
  182. * Stores the application built from the app_config files.
  183. * @var array
  184. * @see $apps
  185. * @access private
  186. */
  187. private static $apps = [];
  188. /**
  189. * Stores the application name making the request.
  190. * @var string App name making database request.
  191. * @access public
  192. */
  193. public $name;
  194. /**
  195. * Stores the application name making the request.
  196. * @var string App name making database request.
  197. * @see $app_uuid
  198. * @access public
  199. */
  200. public $app_name;
  201. /**
  202. * Stores the application UUID making the request.
  203. * @var string
  204. * @see $app_name
  205. * @access public
  206. */
  207. public $app_uuid;
  208. /**
  209. * <p>Stores the domain UUID making the request.</p>
  210. * <p>This is defaulted to the Session domain UUID.</p>
  211. * @access public
  212. * @uses $this->domain_uuid <br>Default value upon object creation
  213. * @var string Domain UUID making request.
  214. */
  215. public $domain_uuid;
  216. /**
  217. * <p>Stores the user UUID making the request.</p>
  218. * <p>This is defaulted to the Session domain UUID.</p>
  219. * @access public
  220. * @uses $this->user_uuid <br>Default value upon object creation
  221. * @var string Domain UUID making request.
  222. */
  223. public $user_uuid;
  224. /**
  225. * <p>Message for the query results.</p>
  226. * @var array Contains the message array after a query
  227. * @access private
  228. */
  229. public $message;
  230. /**
  231. * Config object used to get the database connection params
  232. * @var config
  233. */
  234. private $config;
  235. /**
  236. * SSL Mode used to connect to the database
  237. * @var string prefer or verify-ca. Default is 'prefer'
  238. */
  239. public $ssl_mode;
  240. /**
  241. * Singleton type class
  242. * @var database
  243. */
  244. private static $database;
  245. /**
  246. * Called when the object is created
  247. * @param array $params Optional
  248. */
  249. public function __construct(array $params = []) {
  250. //handle the config object
  251. if (isset($params['config'])) {
  252. $config = $params['config'];
  253. }
  254. else {
  255. $config = new config();
  256. }
  257. //driver and type point to the same value
  258. $this->driver = $config->get('database.0.type', 'pgsql');
  259. $this->driver = $config->get('database.0.type', 'pgsql');
  260. $this->type = $config->get('database.0.type', 'pgsql');
  261. $this->host = $config->get('database.0.host', '127.0.0.1');
  262. $this->port = $config->get('database.0.port', '5432');
  263. $this->username = $config->get('database.0.username', 'fusionpbx');
  264. $this->password = $config->get('database.0.password', 'fusionpbx');
  265. $this->db_name = $config->get('database.0.name', 'fusionpbx');
  266. $this->db_secure = $config->get('database.0.secure', '');
  267. $this->db_cert_authority = $config->get('database.0.cert_authority', '');
  268. $this->ssl_mode = $config->get('database.0.ssl_mode', '');
  269. //save the reference to the single instance of the config to this object
  270. $this->config = $config;
  271. //connect to the database now
  272. $this->connect();
  273. //use the session domain_uuid
  274. if (!isset($this->domain_uuid) && isset($_SESSION['domain_uuid'])) {
  275. $this->domain_uuid = $_SESSION['domain_uuid'];
  276. }
  277. //allow passed domain_uuid in the constructor to override the session domain
  278. if (isset($params['domain_uuid'])) {
  279. $this->domain_uuid = $params['domain_uuid'];
  280. }
  281. //allow passed user_uuid in the constructor to override the session user_uuid
  282. if (isset($params['user_uuid'])) {
  283. $this->user_uuid = $params['user_uuid'];
  284. } else {
  285. //try to determine the current user_uuid using the session
  286. $this->user_uuid = (!empty($_SESSION['user_uuid']) ? $_SESSION['user_uuid'] : null);
  287. }
  288. }
  289. /**
  290. * <p>Magic function called whenever a property is attempted to be set.</p>
  291. * <p>This is used to protect the values stored in the object properties.</p>
  292. * @param mixed $name Name of object property
  293. * @param mixed $value Value of property
  294. */
  295. public function __set($name,$value) {
  296. switch($name) {
  297. case 'name':
  298. case 'app_name':
  299. $this->app_name = self::sanitize($value);
  300. break;
  301. case 'message':
  302. if (is_array($value)) {
  303. $this->message = $value;
  304. } else {
  305. trigger_error('Message property must be set to array type', E_USER_ERROR);
  306. }
  307. break;
  308. case 'table':
  309. $this->table = self::sanitize($value);
  310. break;
  311. case 'db_name':
  312. $this->db_name = self::sanitize($value);
  313. break;
  314. case 'db':
  315. if ($name instanceof PDO) {
  316. $this->db = $value;
  317. } else {
  318. trigger_error('db property must be a PDO object!', E_USER_ERROR);
  319. }
  320. break;
  321. case 'count':
  322. break;
  323. case 'path':
  324. $value = realpath($value);
  325. if (file_exists($value)) {
  326. $this->path = $value;
  327. } else {
  328. trigger_error('Unable to find database path file!', E_USER_ERROR);
  329. }
  330. break;
  331. case 'db_cert_authority':
  332. if (!file_exists($value)) {
  333. trigger_error('db cert authority not found!', E_USER_WARNING);
  334. }
  335. $this->db_cert_authority = $value;
  336. break;
  337. case 'port':
  338. $value = (int)$value; // force cast to int
  339. if ($value > 1023 && $value < 65536) { $this->port = $value; } //valid values are 1024...65535
  340. else { trigger_error('Port not a valid range', E_USER_ERROR); }
  341. break;
  342. case 'app_uuid':
  343. case 'domain_uuid':
  344. if (is_uuid($value)) { $this->domain_uuid = $value; }
  345. break;
  346. case 'type':
  347. case 'driver':
  348. switch($value) {
  349. case 'pgsql':
  350. case 'mysql':
  351. case 'sqlite':
  352. case 'odbc':
  353. $this->type = $value;
  354. $this->driver = $value;
  355. break;
  356. default:
  357. trigger_error("Type/Driver must be set to pgsql,mysql,sqlite,odbc", E_USER_ERROR);
  358. break;
  359. }
  360. case 'offset':
  361. case 'limit':
  362. if (is_int($value)) {
  363. $this->$name = $value;
  364. } else {
  365. trigger_error('Offset or Limit not set to valid integer. Resetting to zero!', E_USER_WARNING);
  366. }
  367. break;
  368. case '':
  369. trigger_error('Database property must not be empty', E_USER_ERROR);
  370. break;
  371. case 'null':
  372. case null:
  373. trigger_error('Database property must not be null', E_USER_ERROR);
  374. break;
  375. case 'debug':
  376. $this->debug = $value;
  377. }
  378. }
  379. /**
  380. * Magic function called whenever a property is requested.
  381. * <p>If any case statement is removed then access to the variable will be removed.</p>
  382. * @param mixed $name object property
  383. * @return mixed
  384. */
  385. public function __get($name) {
  386. //remove any case statement below to remove access to the variable
  387. switch($name) {
  388. case 'name':
  389. return $this->app_name;
  390. case 'app_name':
  391. case 'app_uuid':
  392. case 'db':
  393. case 'db_cert_authority':
  394. case 'db_name':
  395. case 'db_secure':
  396. case 'domain_uuid':
  397. case 'driver':
  398. case 'fields':
  399. case 'host':
  400. case 'limit':
  401. case 'message':
  402. case 'offset':
  403. case 'order_by':
  404. case 'order_type':
  405. case 'password':
  406. case 'path':
  407. case 'port':
  408. case 'result':
  409. case 'sql':
  410. case 'table':
  411. case 'type':
  412. case 'username':
  413. case 'where':
  414. case 'debug':
  415. case 'count':
  416. return $this->count();
  417. default:
  418. trigger_error('Object property not available', E_USER_ERROR);
  419. }
  420. }
  421. /**
  422. * Returns the config object used to create this database object
  423. * @return config Config object
  424. */
  425. public function config(): config {
  426. return $this->config;
  427. }
  428. /**
  429. * <p>Connect to the database.</p>
  430. * <p>Database driver must be set before calling connect.</p>
  431. * <p>For types other than sqlite. Execution will stop on failure.</p>
  432. * @depends database::driver Alias of database::type.
  433. *
  434. */
  435. public function connect() {
  436. //get the database connection settings
  437. //$db_type = $conf['database.0.type'];
  438. //$db_host = $conf['database.0.host'];
  439. //$db_port = $conf['database.0.port'];
  440. //$db_name = $conf['database.0.name'];
  441. //$db_username = $conf['database.0.username'];
  442. //$db_password = $conf['database.0.password'];
  443. //debug info
  444. //echo "db type:".$db_type."\n";
  445. //echo "db host:".$db_host."\n";
  446. //echo "db port:".$db_port."\n";
  447. //echo "db name:".$db_name."\n";
  448. //echo "db username:".$db_username."\n";
  449. //echo "db password:".$db_password."\n";
  450. //echo "db path:".$db_path."\n";
  451. //echo "</pre>\n";
  452. //set defaults
  453. if (!isset($this->driver) && isset($db_type)) { $this->driver = $db_type; }
  454. if (!isset($this->type) && isset($db_type)) { $this->type = $db_type; }
  455. if (!isset($this->host) && isset($db_host)) { $this->host = $db_host; }
  456. if (!isset($this->port) && isset($db_port)) { $this->port = $db_port; }
  457. if (!isset($this->db_name) && isset($db_name)) { $this->db_name = $db_name; }
  458. if (!isset($this->db_secure) && isset($db_secure)) {
  459. $this->db_secure = $db_secure;
  460. }
  461. else {
  462. $this->db_secure = false;
  463. }
  464. if (!isset($this->username) && isset($db_username)) { $this->username = $db_username; }
  465. if (!isset($this->password) && isset($db_password)) { $this->password = $db_password; }
  466. if (!isset($this->path) && isset($db_path)) { $this->path = $db_path; }
  467. if ($this->driver == "sqlite") {
  468. if (empty($this->db_name)) {
  469. $server_name = $_SERVER["SERVER_NAME"];
  470. $server_name = str_replace ("www.", "", $server_name);
  471. $db_name_short = $server_name;
  472. $this->db_name = $server_name.'.db';
  473. }
  474. else {
  475. $db_name_short = $this->db_name;
  476. }
  477. $this->path = realpath($this->path);
  478. if (file_exists($this->path.'/'.$this->db_name)) {
  479. //connect to the database
  480. $this->db = new PDO('sqlite:'.$this->path.'/'.$this->db_name); //sqlite 3
  481. //PRAGMA commands
  482. $this->db->query('PRAGMA foreign_keys = ON;');
  483. $this->db->query('PRAGMA journal_mode = wal;');
  484. //add additional functions to SQLite so that they are accessible inside SQL
  485. //bool PDO::sqliteCreateFunction ( string function_name, callback callback [, int num_args] )
  486. $this->db->sqliteCreateFunction('md5', 'php_md5', 1);
  487. $this->db->sqliteCreateFunction('unix_timestamp', 'php_unix_timestamp', 1);
  488. $this->db->sqliteCreateFunction('now', 'php_now', 0);
  489. $this->db->sqliteCreateFunction('sqlitedatatype', 'php_sqlite_data_type', 2);
  490. $this->db->sqliteCreateFunction('strleft', 'php_left', 2);
  491. $this->db->sqliteCreateFunction('strright', 'php_right', 2);
  492. }
  493. else {
  494. echo "not found";
  495. }
  496. }
  497. if ($this->driver == "mysql") {
  498. try {
  499. //mysql pdo connection
  500. if (strlen($this->host) == 0 && empty($this->port)) {
  501. //if both host and port are empty use the unix socket
  502. $this->db = new PDO("mysql:host=$this->host;unix_socket=/var/run/mysqld/mysqld.sock;dbname=$this->db_name", $this->username, $this->password);
  503. }
  504. else {
  505. if (empty($this->port)) {
  506. //leave out port if it is empty
  507. $this->db = new PDO("mysql:host=$this->host;dbname=$this->db_name;", $this->username, $this->password, array(
  508. PDO::ATTR_ERRMODE,
  509. PDO::ERRMODE_EXCEPTION
  510. ));
  511. }
  512. else {
  513. $this->db = new PDO("mysql:host=$this->host;port=$this->port;dbname=$this->db_name;", $this->username, $this->password, array(
  514. PDO::ATTR_ERRMODE,
  515. PDO::ERRMODE_EXCEPTION
  516. ));
  517. }
  518. }
  519. }
  520. catch (PDOException $error) {
  521. print "error: " . $error->getMessage() . "<br/>";
  522. die();
  523. }
  524. }
  525. if ($this->driver == "pgsql") {
  526. //database connection
  527. try {
  528. if (!empty($this->host)) {
  529. if (empty($this->port)) { $this->port = "5432"; }
  530. if ($this->db_secure === true) {
  531. $this->db = new PDO("pgsql:host=$this->host port=$this->port dbname=$this->db_name user=$this->username password=$this->password sslmode=$this->ssl_mode sslrootcert=$this->db_cert_authority");
  532. }
  533. else {
  534. $this->db = new PDO("pgsql:host=$this->host port=$this->port dbname=$this->db_name user=$this->username password=$this->password");
  535. }
  536. }
  537. else {
  538. $this->db = new PDO("pgsql:dbname=$this->db_name user=$this->username password=$this->password");
  539. }
  540. }
  541. catch (PDOException $error) {
  542. print "error: " . $error->getMessage() . "<br/>";
  543. die();
  544. }
  545. }
  546. if ($this->driver == "odbc") {
  547. //database connection
  548. try {
  549. $this->db = new PDO("odbc:".$this->db_name, $this->username, $this->password);
  550. }
  551. catch (PDOException $e) {
  552. echo 'Connection failed: ' . $e->getMessage();
  553. }
  554. }
  555. }
  556. /**
  557. * Returns the table names from the database.
  558. * @return array tables
  559. * @depends connect()
  560. */
  561. public function tables() {
  562. $result = [];
  563. //connect to the database if needed
  564. if (!$this->db) {
  565. $this->connect();
  566. }
  567. if ($this->type == "sqlite") {
  568. $sql = "SELECT name FROM sqlite_master ";
  569. $sql .= "WHERE type='table' ";
  570. $sql .= "order by name;";
  571. }
  572. if ($this->type == "pgsql") {
  573. $sql = "select table_name as name ";
  574. $sql .= "from information_schema.tables ";
  575. $sql .= "where table_schema='public' ";
  576. $sql .= "and table_type='BASE TABLE' ";
  577. $sql .= "order by table_name ";
  578. }
  579. if ($this->type == "mysql") {
  580. $sql = "show tables";
  581. }
  582. if ($this->type == "mssql") {
  583. $sql = "SELECT * FROM sys.Tables order by name asc";
  584. }
  585. $prep_statement = $this->db->prepare(check_sql($sql));
  586. $prep_statement->execute();
  587. $tmp = $prep_statement->fetchAll(PDO::FETCH_NAMED);
  588. if ($this->type == "pgsql" || $this->type == "sqlite" || $this->type == "mssql") {
  589. if (is_array($tmp)) {
  590. foreach ($tmp as $row) {
  591. $result[]['name'] = $row['name'];
  592. }
  593. }
  594. }
  595. if ($this->type == "mysql") {
  596. if (is_array($tmp)) {
  597. foreach ($tmp as $row) {
  598. $table_array = array_values($row);
  599. $result[]['name'] = $table_array[0];
  600. }
  601. }
  602. }
  603. return $result;
  604. }
  605. /**
  606. * Returns table information from the database.
  607. * @return array table info
  608. * @depends connect()
  609. */
  610. public function table_info() {
  611. //public $db;
  612. //public $type;
  613. //public $table;
  614. //public $name;
  615. //connect to the database if needed
  616. if (!$this->db) {
  617. $this->connect();
  618. }
  619. //get the table info
  620. if (empty($this->table)) { return false; }
  621. if ($this->type == "sqlite") {
  622. $sql = "PRAGMA table_info(".$this->table.");";
  623. }
  624. if ($this->type == "pgsql") {
  625. $sql = "SELECT ordinal_position, ";
  626. $sql .= "column_name, ";
  627. $sql .= "data_type, ";
  628. $sql .= "column_default, ";
  629. $sql .= "is_nullable, ";
  630. $sql .= "character_maximum_length, ";
  631. $sql .= "numeric_precision ";
  632. $sql .= "FROM information_schema.columns ";
  633. $sql .= "WHERE table_name = '".$this->table."' ";
  634. $sql .= "and table_catalog = '".$this->db_name."' ";
  635. $sql .= "ORDER BY ordinal_position; ";
  636. }
  637. if ($this->type == "mysql") {
  638. $sql = "DESCRIBE ".$this->table.";";
  639. }
  640. if ($this->type == "mssql") {
  641. $sql = "SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '".$this->table."'";
  642. }
  643. $prep_statement = $this->db->prepare($sql);
  644. $prep_statement->execute();
  645. //set the result array
  646. return $prep_statement->fetchAll(PDO::FETCH_ASSOC);
  647. }
  648. /**
  649. * Checks if the table exists in the database.
  650. * <p><b>Note:</b><br>
  651. * Table name must be sanitized. Otherwise, a warning will be
  652. * emitted and false will be returned.</p>
  653. * @param type $table_name Sanitized name of the table to search for.
  654. * @return boolean Returns <i>true</i> if the table exists and <i>false</i> if it does not.
  655. * @depends connect()
  656. */
  657. public function table_exists ($table_name) {
  658. if (self::sanitize($table_name) != $table_name) {
  659. trigger_error('Table Name must be sanitized', E_USER_WARNING);
  660. return false;
  661. }
  662. //connect to the database if needed
  663. if (!$this->db) {
  664. $this->connect();
  665. }
  666. //if unable to connect to the database
  667. if (!$this->db) {
  668. $backtrace = debug_backtrace();
  669. echo "Connection Failed<br />\n";
  670. echo "line number ".__line__."<br />\n";
  671. echo "<pre>";
  672. print_r($backtrace);
  673. echo "</pre>";
  674. exit;
  675. }
  676. //query table store to see if the table exists
  677. $sql = "";
  678. if ($this->type == "sqlite") {
  679. $sql .= "SELECT * FROM sqlite_master WHERE type='table' and name='$table_name' ";
  680. }
  681. if ($this->type == "pgsql") {
  682. $sql .= "select * from pg_tables where schemaname='public' and tablename = '$table_name' ";
  683. }
  684. if ($this->type == "mysql") {
  685. $sql .= "SELECT TABLE_NAME FROM information_schema.tables WHERE table_schema = '".$this->db_name."' and TABLE_NAME = '$table_name' ";
  686. }
  687. $prep_statement = $this->db->prepare($sql);
  688. $prep_statement->execute();
  689. $result = $prep_statement->fetchAll(PDO::FETCH_NAMED);
  690. if (count($result) > 0) {
  691. return true; //table exists
  692. }
  693. else {
  694. return false; //table doesn't exist
  695. }
  696. }
  697. /**
  698. * Queries {@link database::table_info()} to return the fields.
  699. * @access public
  700. * @return array Two dimensional array
  701. * @depends table_info()
  702. */
  703. public function fields() {
  704. //public $db;
  705. //public $type;
  706. //public $table;
  707. //public $name;
  708. //initialize the array
  709. $result = [];
  710. //get the table info
  711. $table_info = $this->table_info();
  712. //set the list of fields
  713. if ($this->type == "sqlite") {
  714. if (is_array($table_info)) {
  715. foreach($table_info as $row) {
  716. $result[]['name'] = $row['name'];
  717. }
  718. }
  719. }
  720. if ($this->type == "pgsql") {
  721. if (is_array($table_info)) {
  722. foreach($table_info as $row) {
  723. $result[]['name'] = $row['column_name'];
  724. }
  725. }
  726. }
  727. if ($this->type == "mysql") {
  728. if (is_array($table_info)) {
  729. foreach($table_info as $row) {
  730. $result[]['name'] = $row['Field'];
  731. }
  732. }
  733. }
  734. if ($this->type == "mssql") {
  735. if (is_array($table_info)) {
  736. foreach($table_info as $row) {
  737. $result[]['name'] = $row['COLUMN_NAME'];
  738. }
  739. }
  740. }
  741. //return the result array
  742. return $result;
  743. }
  744. /**
  745. * Searches database using the following object properties:
  746. * <ol>
  747. * <li>table - sanitized name of the table {@see database::table}</li>
  748. * <li>where - where clause {@see database::where}</li>
  749. * <li>order_by - order_by clause {@see database::order_by}</li>
  750. * <li>limit - limit clause {@see database::limit}</li>
  751. * <li>offset - offset clause {@see database::offset}</li>
  752. * </ol>
  753. * @return boolean
  754. * @depends connect()
  755. */
  756. public function find() {
  757. //connect;
  758. //table;
  759. //where;
  760. //order_by;
  761. //limit;
  762. //offset;
  763. //connect to the database if needed
  764. if (!$this->db) {
  765. $this->connect();
  766. }
  767. //get data from the database
  768. $sql = "select * from ".$this->table." ";
  769. if ($this->where) {
  770. $i = 0;
  771. if (is_array($this->where)) {
  772. foreach($this->where as $row) {
  773. //sanitize the name
  774. $array['name'] = self::sanitize($array['name']);
  775. //validate the operator
  776. switch ($row['operator']) {
  777. case "<": break;
  778. case ">": break;
  779. case "<=": break;
  780. case ">=": break;
  781. case "=": break;
  782. case "<>": break;
  783. case "!=": break;
  784. default:
  785. //invalid operator
  786. return false;
  787. }
  788. //build the sql
  789. if ($i == 0) {
  790. //$sql .= 'where '.$row['name']." ".$row['operator']." '".$row['value']."' ";
  791. $sql .= 'where '.$row['name']." ".$row['operator']." :".$row['name']." ";
  792. }
  793. else {
  794. //$sql .= "and ".$row['name']." ".$row['operator']." '".$row['value']."' ";
  795. $sql .= "and ".$row['name']." ".$row['operator']." :".$row['name']." ";
  796. }
  797. //add the name and value to the params array
  798. $params[$row['name']] = $row['value'];
  799. //increment $i
  800. $i++;
  801. }
  802. }
  803. }
  804. if (is_array($this->order_by)) {
  805. $sql .= "order by ";
  806. $i = 1;
  807. if (is_array($this->order_by)) {
  808. foreach($this->order_by as $row) {
  809. //sanitize the name
  810. $row['name'] = self::sanitize($row['name']);
  811. //sanitize the order
  812. switch ($row['order']) {
  813. case "asc":
  814. break;
  815. case "desc":
  816. break;
  817. default:
  818. $row['order'] = '';
  819. }
  820. //build the sql
  821. if (count($this->order_by) == $i) {
  822. $sql .= $row['name']." ".$row['order']." ";
  823. }
  824. else {
  825. $sql .= $row['name']." ".$row['order'].", ";
  826. }
  827. //increment $i
  828. $i++;
  829. }
  830. }
  831. }
  832. //limit
  833. if (isset($this->limit) && is_numeric($this->limit)) {
  834. $sql .= "limit ".$this->limit." ";
  835. }
  836. //offset
  837. if (isset($this->offset) && is_numeric($this->offset)) {
  838. $sql .= "offset ".$this->offset." ";
  839. }
  840. $prep_statement = $this->db->prepare($sql);
  841. if ($prep_statement) {
  842. $prep_statement->execute($params);
  843. $array = $prep_statement->fetchAll(PDO::FETCH_ASSOC);
  844. unset($prep_statement);
  845. return $array;
  846. }
  847. else {
  848. return false;
  849. }
  850. }
  851. // Use this function to run complex queries
  852. public function execute($sql, $parameters = null, $return_type = 'all') {
  853. //connect to the database if needed
  854. if (!$this->db) {
  855. $this->connect();
  856. }
  857. //set the error mode
  858. $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  859. //run the query, and return the results
  860. try {
  861. $prep_statement = $this->db->prepare($sql);
  862. if (is_array($parameters)) {
  863. $prep_statement->execute($parameters);
  864. }
  865. else {
  866. $prep_statement->execute();
  867. }
  868. $message["message"] = "OK";
  869. $message["code"] = "200";
  870. $message["sql"] = $sql;
  871. if (is_array($parameters)) {
  872. $message["parameters"] = $parameters;
  873. }
  874. $this->message = $message;
  875. //return the results
  876. switch($return_type) {
  877. case 'all':
  878. return $prep_statement->fetchAll(PDO::FETCH_ASSOC);
  879. case 'row':
  880. return $prep_statement->fetch(PDO::FETCH_ASSOC);
  881. case 'column';
  882. return $prep_statement->fetchColumn();
  883. default:
  884. return $prep_statement->fetchAll(PDO::FETCH_ASSOC);
  885. }
  886. }
  887. catch(PDOException $e) {
  888. $message["type"] = 'error';
  889. $message["code"] = $e->getCode();
  890. $message["message"] = $e->getMessage();
  891. $message["sql"] = $sql;
  892. if (!empty($parameters)) {
  893. $message["parameters"] = $parameters;
  894. }
  895. $this->message = $message;
  896. return false;
  897. }
  898. }
  899. public function add() {
  900. //connect to the database if needed
  901. if (!$this->db) {
  902. $this->connect();
  903. }
  904. //sanitize the table name
  905. //$this->table = self::sanitize($this->table); // no longer needed
  906. //count the fields
  907. $field_count = count($this->fields);
  908. //add data to the database
  909. $sql = "insert into ".$this->table;
  910. $sql .= " (";
  911. $i = 1;
  912. if (is_array($this->fields)) {
  913. foreach($this->fields as $name => $value) {
  914. $name = self::sanitize($name);
  915. if (count($this->fields) == $i) {
  916. $sql .= $name." \n";
  917. }
  918. else {
  919. $sql .= $name.", \n";
  920. }
  921. $i++;
  922. }
  923. }
  924. $sql .= ") \n";
  925. $sql .= "values \n";
  926. $sql .= "(\n";
  927. $i = 1;
  928. if (is_array($this->fields)) {
  929. foreach($this->fields as $name => $value) {
  930. $name = self::sanitize($name);
  931. if ($field_count == $i) {
  932. if (isset($value) && $value != '') {
  933. //$sql .= "'".$value."' ";
  934. $sql .= ":".$name." \n";
  935. $params[$name] = trim($value);
  936. }
  937. else {
  938. $sql .= "null \n";
  939. }
  940. }
  941. else {
  942. if (isset($value) && $value != '') {
  943. //$sql .= "'".$value."', ";
  944. $sql .= ":".$name.", \n";
  945. $params[$name] = trim($value);
  946. }
  947. else {
  948. $sql .= "null, \n";
  949. }
  950. }
  951. $i++;
  952. }
  953. }
  954. $sql .= ")\n";
  955. //run the query, show exceptions
  956. $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  957. //reduce prepared statement latency
  958. if (defined('PDO::PGSQL_ATTR_DISABLE_PREPARES')) {
  959. $this->db->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true);
  960. }
  961. //prepare the sql and parameters and then run the query
  962. try {
  963. //$this->sql = $sql;
  964. //$this->db->exec($sql);
  965. $prep_statement = $this->db->prepare($sql);
  966. $prep_statement->execute($params);
  967. }
  968. catch(PDOException $e) {
  969. echo "<b>Error:</b><br />\n";
  970. echo "<table>\n";
  971. echo "<tr>\n";
  972. echo "<td>\n";
  973. echo $e->getMessage();
  974. echo "</td>\n";
  975. echo "</tr>\n";
  976. echo "</table>\n";
  977. }
  978. unset($sql, $prep_statement, $this->fields);
  979. }
  980. public function update() {
  981. //connect to the database if needed
  982. if (!$this->db) {
  983. $this->connect();
  984. }
  985. //sanitize the table name
  986. //$this->table = self::sanitize($this->table); // no longer needed
  987. //udate the database
  988. $sql = "update ".$this->table." set ";
  989. $i = 1;
  990. if (is_array($this->fields)) {
  991. foreach($this->fields as $name => $value) {
  992. $name = self::sanitize($name);
  993. if (count($this->fields) == $i) {
  994. if (!empty($name) && $value == null) {
  995. $sql .= $name." = null ";
  996. }
  997. else {
  998. //$sql .= $name." = '".$value."' ";
  999. $sql .= $name." = :".$name." ";
  1000. $params[$name] = trim($value);
  1001. }
  1002. }
  1003. else {
  1004. if (!empty($name) && $value == null) {
  1005. $sql .= $name." = null, ";
  1006. }
  1007. else {
  1008. //$sql .= $name." = '".$value."', ";
  1009. $sql .= $name." = :".$name.", ";
  1010. $params[$name] = trim($value);
  1011. }
  1012. }
  1013. $i++;
  1014. }
  1015. }
  1016. $i = 0;
  1017. if (is_array($this->where)) {
  1018. foreach($this->where as $row) {
  1019. //sanitize the name
  1020. $row['name'] = self::sanitize($row['name']);
  1021. //validate the operator
  1022. switch ($row['operator']) {
  1023. case "<": break;
  1024. case ">": break;
  1025. case "<=": break;
  1026. case ">=": break;
  1027. case "=": break;
  1028. case "<>": break;
  1029. case "!=": break;
  1030. default:
  1031. //invalid operator
  1032. return false;
  1033. }
  1034. //build the sql
  1035. if ($i == 0) {
  1036. //$sql .= $row['name']." ".$row['operator']." '".$row['value']."' ";
  1037. $sql .= "where ".$row['name']." ".$row['operator']." :".$row['name']." ";
  1038. }
  1039. else {
  1040. //$sql .= $row['name']." ".$row['operator']." '".$row['value']."' ";
  1041. $sql .= "and ".$row['name']." ".$row['operator']." :".$row['name']." ";
  1042. }
  1043. //add the name and value to the params array
  1044. $params[$row['name']] = $row['value'];
  1045. //increment $i
  1046. $i++;
  1047. }
  1048. }
  1049. //$this->db->exec(check_sql($sql));
  1050. $prep_statement = $this->db->prepare($sql);
  1051. $prep_statement->execute($params);
  1052. unset($prep_statement);
  1053. unset($this->fields);
  1054. unset($this->where);
  1055. unset($sql);
  1056. }
  1057. public function delete(array $array) {
  1058. //set the default value
  1059. $retval = true;
  1060. //return the array
  1061. if (!is_array($array)) { return false; }
  1062. //connect to the database if needed
  1063. if (!$this->db) {
  1064. $this->connect();
  1065. }
  1066. //set the message id
  1067. $m = 0;
  1068. //debug sql
  1069. //$this->debug["sql"] = true;
  1070. //set the message id
  1071. $m = 0;
  1072. //loop through the array
  1073. $checked = false;
  1074. $x = 0;
  1075. foreach ($array as $parent_name => $tables) {
  1076. if (is_array($tables)) {
  1077. foreach ($tables as $id => $row) {
  1078. //prepare the variables
  1079. $parent_name = self::sanitize($parent_name);
  1080. $parent_key_name = self::singular($parent_name)."_uuid";
  1081. //build the delete array
  1082. if (!empty($row['checked']) && $row['checked'] == 'true') {
  1083. //set checked to true
  1084. $checked = true;
  1085. //delete the child data
  1086. if (isset($row[$parent_key_name])) {
  1087. $new_array[$parent_name][$x][$parent_key_name] = $row[$parent_key_name];
  1088. }
  1089. //remove the row from the main array
  1090. unset($array[$parent_name][$x]);
  1091. }
  1092. //loop through the fields
  1093. foreach($row as $field_name => $field_value) {
  1094. //find the child tables
  1095. $y = 0;
  1096. if (is_array($field_value)) {
  1097. //prepare the variables
  1098. $child_name = self::sanitize($field_name);
  1099. $child_key_name = self::singular($child_name)."_uuid";
  1100. //loop through the child rows
  1101. foreach ($field_value as $sub_row) {
  1102. //build the delete array
  1103. if ($row['checked'] == 'true') {
  1104. //set checked to true
  1105. $checked = true;
  1106. //delete the child data
  1107. $new_array[$child_name][][$child_key_name] = $sub_row[$child_key_name];
  1108. //remove the row from the main array
  1109. unset($array[$parent_name][$x][$child_name][$y]);
  1110. }
  1111. //increment the value
  1112. $y++;
  1113. }
  1114. }
  1115. }
  1116. //increment the value
  1117. $x++;
  1118. }
  1119. }
  1120. }
  1121. //if not checked then copy the array to delete array
  1122. if (!$checked) {
  1123. $new_array = $array;
  1124. }
  1125. //get the current data
  1126. if (count($new_array) > 0) {
  1127. //build an array of tables, fields, and values
  1128. foreach($new_array as $table_name => $rows) {
  1129. foreach($rows as $row) {
  1130. foreach($row as $field_name => $field_value) {
  1131. $keys[$table_name][$field_name][] = $field_value;
  1132. }
  1133. }
  1134. }
  1135. //use the array to get a copy of the parent data before deleting it
  1136. foreach($new_array as $table_name => $rows) {
  1137. foreach($rows as $row) {
  1138. $table_name = self::sanitize($table_name);
  1139. $sql = "select * from ".self::TABLE_PREFIX.$table_name." ";
  1140. $i = 0;
  1141. foreach($row as $field_name => $field_value) {
  1142. if ($i == 0) { $sql .= "where "; } else { $sql .= "and "; }
  1143. $sql .= $field_name." in ( ";
  1144. $i = 0;
  1145. foreach($keys[$table_name][$field_name] as $field_value) {
  1146. $field_name = self::sanitize($field_name);
  1147. if ($i > 0) { $sql .= " ,"; }
  1148. $sql .= " :".$field_name."_".$i." ";
  1149. $i++;
  1150. }
  1151. $sql .= ") ";
  1152. $i = 0;
  1153. foreach($keys[$table_name][$field_name] as $field_value) {
  1154. $parameters[$field_name.'_'.$i] = $field_value;
  1155. $i++;
  1156. }
  1157. }
  1158. }
  1159. if (isset($field_value) && $field_value != '') {
  1160. $results = $this->execute($sql, $parameters, 'all');
  1161. unset($parameters);
  1162. if (is_array($results)) {
  1163. $old_array[$table_name] = $results;
  1164. }
  1165. }
  1166. }
  1167. //get relations array
  1168. $relations = self::get_relations($parent_name);
  1169. //add child data to the old array
  1170. foreach($old_array as $parent_name => $rows) {
  1171. //get relations array
  1172. $relations = self::get_relations($parent_name);
  1173. //loop through the rows
  1174. $x = 0;
  1175. foreach($rows as $row) {
  1176. if (is_array($relations)) {
  1177. foreach ($relations as $relation) {
  1178. if ($relation['key']['action']['delete'] == 'cascade') {
  1179. //set the child table
  1180. $child_table = $relation['table'];
  1181. //remove the v_ prefix
  1182. if (substr($child_table, 0, strlen(self::TABLE_PREFIX)) == self::TABLE_PREFIX) {
  1183. $child_table = substr($child_table, strlen(self::TABLE_PREFIX));
  1184. }
  1185. //get the child data
  1186. $sql = "select * from ".self::TABLE_PREFIX.$child_table." ";
  1187. $sql .= "where ".$relation['field']." = :".$relation['field'];
  1188. $parameters[$relation['field']] = $row[$relation['field']];
  1189. $results = $this->execute($sql, $parameters, 'all');
  1190. unset($parameters);
  1191. if (is_array($results) && $parent_name !== $child_table) {
  1192. $old_array[$parent_name][$x][$child_table] = $results;
  1193. }
  1194. //delete the child data
  1195. if (isset($row[$relation['field']]) && !empty($row[$relation['field']])) {
  1196. $sql = "delete from ".self::TABLE_PREFIX.$child_table." ";
  1197. $sql .= "where ".$relation['field']." = :".$relation['field'];
  1198. $parameters[$relation['field']] = $row[$relation['field']];
  1199. // $this->execute($sql, $parameters);
  1200. }
  1201. unset($parameters);
  1202. }
  1203. }
  1204. }
  1205. $x++;
  1206. }
  1207. }
  1208. }
  1209. //start the atomic transaction
  1210. $this->db->beginTransaction();
  1211. //delete the current data
  1212. foreach($new_array as $table_name => $rows) {
  1213. //echo "table: ".$table_name."\n";
  1214. foreach($rows as $row) {
  1215. if (permission_exists(self::singular($table_name).'_delete')) {
  1216. $sql = "delete from ".self::TABLE_PREFIX.$table_name." ";
  1217. $i = 0;
  1218. foreach($row as $field_name => $field_value) {
  1219. //echo "field: ".$field_name." = ".$field_value."\n";
  1220. if ($i == 0) { $sql .= "where "; } else { $sql .= "and "; }
  1221. $sql .= $field_name." = :".$field_name." ";
  1222. $parameters[$field_name] = $field_value;
  1223. $i++;
  1224. }
  1225. try {
  1226. $this->execute($sql, $parameters);
  1227. $message["message"] = "OK";
  1228. $message["code"] = "200";
  1229. $message["uuid"] = $id;
  1230. $message["details"][$m]["name"] = $this->app_name;
  1231. $message["details"][$m]["message"] = "OK";
  1232. $message["details"][$m]["code"] = "200";
  1233. //$message["details"][$m]["uuid"] = $parent_key_value;
  1234. $message["details"][$m]["sql"] = $sql;
  1235. $this->message = $message;
  1236. $m++;
  1237. unset($sql);
  1238. unset($statement);
  1239. }
  1240. catch(PDOException $e) {
  1241. $retval = false;
  1242. $message["message"] = "Bad Request";
  1243. $message["code"] = "400";
  1244. $message["details"][$m]["name"] = $this->app_name;
  1245. $message["details"][$m]["message"] = $e->getMessage();
  1246. $message["details"][$m]["code"] = "400";
  1247. $message["details"][$m]["sql"] = $sql;
  1248. $this->message = $message;
  1249. $m++;
  1250. }
  1251. unset($parameters);
  1252. } //if permission
  1253. } //foreach rows
  1254. } //foreach $array
  1255. //commit the atomic transaction
  1256. $this->db->commit();
  1257. //set the action if not set
  1258. $transaction_type = 'delete';
  1259. //log the transaction results
  1260. if (file_exists($_SERVER["PROJECT_ROOT"]."/app/database_transactions/app_config.php")) {
  1261. $sql = "insert into ".self::TABLE_PREFIX."database_transactions ";
  1262. $sql .= "(";
  1263. $sql .= "database_transaction_uuid, ";
  1264. if (isset($this->domain_uuid) && is_uuid($this->domain_uuid)) {
  1265. $sql .= "domain_uuid, ";
  1266. }
  1267. if (isset($this->user_uuid) && is_uuid($this->user_uuid)) {
  1268. $sql .= "user_uuid, ";
  1269. }
  1270. if (isset($this->app_uuid) && is_uuid($this->app_uuid)) {
  1271. $sql .= "app_uuid, ";
  1272. }
  1273. if (isset($this->app_name) && !empty($this->app_name)) {
  1274. $sql .= "app_name, ";
  1275. }
  1276. $sql .= "transaction_code, ";
  1277. $sql .= "transaction_address, ";
  1278. $sql .= "transaction_type, ";
  1279. $sql .= "transaction_date, ";
  1280. $sql .= "transaction_old, ";
  1281. $sql .= "transaction_new, ";
  1282. $sql .= "transaction_result ";
  1283. $sql .= ")";
  1284. $sql .= "values ";
  1285. $sql .= "(";
  1286. $sql .= "'".uuid()."', ";
  1287. if (isset($this->domain_uuid) && is_uuid($this->domain_uuid)) {
  1288. $sql .= "'".$this->domain_uuid."', ";
  1289. }
  1290. if (isset($this->user_uuid) && is_uuid($this->user_uuid)) {
  1291. $sql .= ":user_uuid, ";
  1292. }
  1293. if (isset($this->app_uuid) && is_uuid($this->app_uuid)) {
  1294. $sql .= ":app_uuid, ";
  1295. }
  1296. if (isset($this->app_name) && !empty($this->app_name)) {
  1297. $sql .= ":app_name, ";
  1298. }
  1299. $sql .= "'".$message["code"]."', ";
  1300. $sql .= ":remote_address, ";
  1301. $sql .= "'".$transaction_type."', ";
  1302. $sql .= "now(), ";
  1303. if (is_array($old_array)) {
  1304. $sql .= ":transaction_old, ";
  1305. }
  1306. else {
  1307. $sql .= "null, ";
  1308. }
  1309. if (is_array($new_array)) {
  1310. $sql .= ":transaction_new, ";
  1311. }
  1312. else {
  1313. $sql .= "null, ";
  1314. }
  1315. $sql .= ":transaction_result ";
  1316. $sql .= ")";
  1317. $statement = $this->db->prepare($sql);
  1318. if (isset($this->user_uuid) && is_uuid($this->user_uuid)) {
  1319. $statement->bindParam(':user_uuid', $this->user_uuid);
  1320. }
  1321. if (isset($this->app_uuid) && is_uuid($this->app_uuid)) {
  1322. $statement->bindParam(':app_uuid', $this->app_uuid);
  1323. }
  1324. if (isset($this->app_name) && !empty($this->app_name)) {
  1325. $statement->bindParam(':app_name', $this->app_name);
  1326. }
  1327. $statement->bindParam(':remote_address', $_SERVER['REMOTE_ADDR']);
  1328. if (is_array($old_array)) {
  1329. $old_json = json_encode($old_array, JSON_PRETTY_PRINT);
  1330. $statement->bindParam(':transaction_old', $old_json);
  1331. }
  1332. if (is_array($new_array)) {
  1333. $new_json = json_encode($new_array, JSON_PRETTY_PRINT);
  1334. $statement->bindParam(':transaction_new', $new_json);
  1335. }
  1336. $result = json_encode($this->message, JSON_PRETTY_PRINT);
  1337. $statement->bindParam(':transaction_result', $result);
  1338. $statement->execute();
  1339. unset($sql);
  1340. }
  1341. return $retval;
  1342. } //delete
  1343. /**
  1344. * Counts the number of rows.
  1345. * @return int Represents the number of counted rows or -1 if failed.
  1346. */
  1347. public function count() {
  1348. //connect to the database if needed
  1349. if (!$this->db) {
  1350. $this->connect();
  1351. }
  1352. //return if the table name is not set
  1353. if (empty($this->table)) {
  1354. return;
  1355. }
  1356. //sanitize the table name
  1357. //$this->table = self::sanitize($this->table); // no longer needed
  1358. //get the number of rows
  1359. $sql = "select count(*) as num_rows from ".$this->table." ";
  1360. $i = 0;
  1361. if (is_array($this->where)) {
  1362. foreach($this->where as $row) {
  1363. //sanitize the name
  1364. $row['name'] = self::sanitize($row['name']);
  1365. //validate the operator
  1366. switch ($row['operator']) {
  1367. case "<": break;
  1368. case ">": break;
  1369. case "<=": break;
  1370. case ">=": break;
  1371. case "=": break;
  1372. case "<>": break;
  1373. case "!=": break;
  1374. default:
  1375. //invalid operator
  1376. return -1;
  1377. }
  1378. //build the sql
  1379. if ($i == 0) {
  1380. $sql .= "where ".$row['name']." ".$row['operator']." :".$row['name']." ";
  1381. }
  1382. else {
  1383. $sql .= "and ".$row['name']." ".$row['operator']." :".$row['name']." ";
  1384. }
  1385. //add the name and value to the params array
  1386. $params[$row['name']] = $row['value'];
  1387. //increment $i
  1388. $i++;
  1389. }
  1390. }
  1391. //unset($this->where); //should not be objects resposibility
  1392. $prep_statement = $this->db->prepare($sql);
  1393. if ($prep_statement) {
  1394. if (!isset($params)) { $params = null; }
  1395. $prep_statement->execute($params);
  1396. $row = $prep_statement->fetch(PDO::FETCH_ASSOC);
  1397. if ($row['num_rows'] > 0) {
  1398. return $row['num_rows'];
  1399. }
  1400. else {
  1401. return 0;
  1402. }
  1403. }
  1404. unset($prep_statement);
  1405. } //count
  1406. /**
  1407. * Performs a select query on database using the <b>$sql</b> statement supplied.
  1408. * @param type $sql Valid SQL statement.
  1409. * @param type $parameters Value can be <i>array</i>, empty string, or <i>null</i>.
  1410. * @param type $return_type Values can be set to <i>all</i>, <i>row</i>, or <i>column</i>.
  1411. * @return mixed Returned values can be array, string, boolean, int, or false. This is dependent on <i>$return_type</i>.
  1412. */
  1413. public function select($sql, $parameters = '', $return_type = 'all') {
  1414. //connect to the database if needed
  1415. if (!$this->db) {
  1416. $this->connect();
  1417. }
  1418. //unable to connect to the database
  1419. if (!$this->db) {
  1420. echo "Connection Failed<br />\n";
  1421. echo "line number ".__line__."<br />\n";
  1422. exit;
  1423. }
  1424. //set the error mode
  1425. if ($this->db) {
  1426. $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  1427. }
  1428. //reduce prepared statement latency
  1429. if ($this->db && defined('PDO::PGSQL_ATTR_DISABLE_PREPARES')) {
  1430. $this->db->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true);
  1431. }
  1432. //execute the query and return the results
  1433. try {
  1434. $prep_statement = $this->db->prepare($sql);
  1435. if (is_array($parameters)) {
  1436. $prep_statement->execute($parameters);
  1437. }
  1438. else {
  1439. $prep_statement->execute();
  1440. }
  1441. $message["message"] = "OK";
  1442. $message["code"] = "200";
  1443. $message["sql"] = $sql;
  1444. if (is_array($parameters)) {
  1445. $message["parameters"] = $parameters;
  1446. }
  1447. $this->message = $message;
  1448. //return the results
  1449. switch($return_type) {
  1450. case 'all':
  1451. return $prep_statement->fetchAll(PDO::FETCH_ASSOC);
  1452. case 'row':
  1453. return $prep_statement->fetch(PDO::FETCH_ASSOC);
  1454. case 'column':
  1455. return $prep_statement->fetchColumn();
  1456. default:
  1457. return $prep_statement->fetchAll(PDO::FETCH_ASSOC);
  1458. }
  1459. }
  1460. catch(PDOException $e) {
  1461. $message["message"] = "Bad Request";
  1462. $message["code"] = "400";
  1463. $message["error"]["message"] = $e->getMessage();
  1464. $message["sql"] = $sql;
  1465. if (is_array($parameters)) {
  1466. $message["parameters"] = $parameters;
  1467. }
  1468. $this->message = $message;
  1469. return false;
  1470. }
  1471. } //select
  1472. /**
  1473. * Sets the object <i>$result</i> to sql array
  1474. * @param array $array Array containing the table name, uuid, SQL and where clause.
  1475. * @return database Returns the database object or null.
  1476. */
  1477. public function find_new(array $array) {
  1478. //connect to the database if needed
  1479. if (!$this->db) {
  1480. $this->connect();
  1481. }
  1482. //set the name
  1483. if (isset($array['name'])) {
  1484. $this->name = $array['name'];
  1485. }
  1486. //set the uuid
  1487. if (isset($array['uuid'])) {
  1488. $this->uuid = $array['uuid'];
  1489. }
  1490. //build the query
  1491. $sql = "SELECT * FROM ".self::TABLE_PREFIX . $this->name . " ";
  1492. if (isset($this->uuid)) {
  1493. //get the specific uuid
  1494. $sql .= "WHERE " . self::singular($this->name) . "_uuid = '" . $this->uuid . "' ";
  1495. } else {
  1496. //where
  1497. $i = 0;
  1498. if (isset($array['where'])) {
  1499. foreach ($array['where'] as $row) {
  1500. if (isset($row['operator'])) {
  1501. //validate the operator
  1502. switch ($row['operator']) {
  1503. case "<": break;
  1504. case ">": break;
  1505. case "<=": break;
  1506. case ">=": break;
  1507. case "=": break;
  1508. case "<>": break;
  1509. case "!=": break;
  1510. default:
  1511. //invalid operator
  1512. return null;
  1513. }
  1514. //build the sql
  1515. if ($i == 0) {
  1516. $sql .= "WHERE " . $row['name'] . " " . $row['operator'] . " :" . $row['value'] . " ";
  1517. } else {
  1518. $sql .= "AND " . $row['name'] . " " . $row['operator'] . " :" . $row['value'] . " ";
  1519. }
  1520. }
  1521. //add the name and value to the params array
  1522. $params[$row['name']] = $row['value'];
  1523. //increment $i
  1524. $i++;
  1525. }
  1526. }
  1527. //order by
  1528. if (isset($array['order_by'])) {
  1529. $array['order_by'] = self::sanitize($array['order_by']);
  1530. $sql .= "ORDER BY " . $array['order_by'] . " ";
  1531. }
  1532. //limit
  1533. if (isset($array['limit']) && is_numeric($array['limit'])) {
  1534. $sql .= "LIMIT " . $array['limit'] . " ";
  1535. }
  1536. //offset
  1537. if (isset($array['offset']) && is_numeric($array['offset'])) {
  1538. $sql .= "OFFSET " . $array['offset'] . " ";
  1539. }
  1540. }
  1541. //execute the query, and return the results
  1542. try {
  1543. $prep_statement = $this->db->prepare($sql);
  1544. $prep_statement->execute($params);
  1545. $message["message"] = "OK";
  1546. $message["code"] = "200";
  1547. $message["details"][$m]["name"] = $this->name;
  1548. $message["details"][$m]["message"] = "OK";
  1549. $message["details"][$m]["code"] = "200";
  1550. $message["details"][$m]["sql"] = $sql;
  1551. $this->message = $message;
  1552. $this->result = $prep_statement->fetchAll(PDO::FETCH_NAMED);
  1553. unset($prep_statement);
  1554. $m++;
  1555. } catch (PDOException $e) {
  1556. $message["message"] = "Bad Request";
  1557. $message["code"] = "400";
  1558. $message["details"][$m]["name"] = $this->name;
  1559. $message["details"][$m]["message"] = $e->getMessage();
  1560. $message["details"][$m]["code"] = "400";
  1561. $message["details"][$m]["sql"] = $sql;
  1562. $this->message = $message;
  1563. $this->result = '';
  1564. $m++;
  1565. }
  1566. return $this;
  1567. }
  1568. /**
  1569. * Stores the passed UUID in the object
  1570. * @param string $uuid A valid UUID must be passed
  1571. * @return database Returns this object
  1572. */
  1573. public function uuid(string $uuid) {
  1574. $this->uuid = $uuid;
  1575. return $this;
  1576. }
  1577. /**
  1578. * Copies records and appends <i>suffix</i> to the column <i>description</i> data
  1579. * @param array $array Three dimensional Array. The first dimension is the table name without the prefix 'v_'. Second dimension in the row value as int. Third dimension is the column name.
  1580. * @return bool Returns <b>true</b> on success and <b>false</b> on failure.
  1581. */
  1582. public function copy(array $array, $suffix = '(Copy)') {
  1583. //set default return value
  1584. $retval = false;
  1585. //return the array
  1586. if (!is_array($array)) { return $retval; }
  1587. //initialize array
  1588. $copy_array = [];
  1589. //set the message id
  1590. $m = 0;
  1591. //loop through the array
  1592. $x = 0;
  1593. foreach ($array as $parent_name => $tables) {
  1594. if (is_array($tables)) {
  1595. foreach ($tables as $id => $row) {
  1596. //prepare the variables
  1597. $parent_name = self::sanitize($parent_name);
  1598. $parent_key_name = self::singular($parent_name)."_uuid";
  1599. //build the copy array
  1600. if (!empty($row['checked']) && $row['checked'] == 'true') {
  1601. //set checked to true
  1602. $checked = true;
  1603. //copy the child data
  1604. if (!empty($row[$parent_key_name]) && is_uuid($row[$parent_key_name])) {
  1605. $copy_array[$parent_name][$x][$parent_key_name] = $row[$parent_key_name];
  1606. }
  1607. //remove the row from the main array
  1608. unset($array[$parent_name][$x]);
  1609. //loop through the fields
  1610. foreach($row as $field_name => $field_value) {
  1611. //find the child tables
  1612. if (is_array($field_value)) {
  1613. //prepare the variables
  1614. $child_name = self::sanitize($field_name);
  1615. $child_key_name = self::singular($child_name)."_uuid";
  1616. //loop through the child rows
  1617. $y = 0;
  1618. foreach ($field_value as $sub_row) {
  1619. //delete the child data
  1620. $copy_array[$child_name][][$child_key_name] = $sub_row[$child_key_name];
  1621. //remove the row from the main array
  1622. unset($array[$parent_name][$x][$child_name][$y]);
  1623. //increment the value
  1624. $y++;
  1625. }
  1626. }
  1627. }
  1628. }
  1629. //increment the value
  1630. $x++;
  1631. }
  1632. }
  1633. }
  1634. //get the current data
  1635. if (count($copy_array) > 0) {
  1636. //build an array of tables, fields, and values
  1637. foreach($copy_array as $table_name => $rows) {
  1638. foreach($rows as $row) {
  1639. foreach($row as $field_name => $field_value) {
  1640. $keys[$table_name][$field_name][] = $field_value;
  1641. }
  1642. }
  1643. }
  1644. //unset the array
  1645. unset($array);
  1646. //use the array to get a copy of the paent data before deleting it
  1647. foreach($copy_array as $table_name => $rows) {
  1648. foreach($rows as $row) {
  1649. $table_name = self::sanitize($table_name);
  1650. $sql = "select * from ".self::TABLE_PREFIX.$table_name." ";
  1651. $i = 0;
  1652. foreach($row as $field_name => $field_value) {
  1653. if ($i == 0) { $sql .= "where "; } else { $sql .= "and "; }
  1654. $sql .= $field_name." in ( ";
  1655. $i = 0;
  1656. foreach($keys[$table_name][$field_name] as $field_value) {
  1657. $field_name = self::sanitize($field_name);
  1658. if ($i > 0) { $sql .= " ,"; }
  1659. $sql .= " :".$field_name."_".$i." ";
  1660. $i++;
  1661. }
  1662. $sql .= ") ";
  1663. $i = 0;
  1664. foreach($keys[$table_name][$field_name] as $field_value) {
  1665. $parameters[$field_name.'_'.$i] = $field_value;
  1666. $i++;
  1667. }
  1668. }
  1669. }
  1670. $results = $this->execute($sql, $parameters, 'all');
  1671. unset($parameters);
  1672. if (is_array($results)) {
  1673. $array[$table_name] = $results;
  1674. }
  1675. }
  1676. //add child data to the old array
  1677. foreach($copy_array as $parent_name => $rows) {
  1678. //get relations array
  1679. $relations = self::get_relations($parent_name);
  1680. //loop through the rows
  1681. $x = 0;
  1682. foreach($rows as $row) {
  1683. if (is_array($relations)) {
  1684. foreach ($relations as $relation) {
  1685. //set the child table
  1686. $child_table = $relation['table'];
  1687. //remove the v_ prefix
  1688. if (substr($child_table, 0, strlen(self::TABLE_PREFIX)) == self::TABLE_PREFIX) {
  1689. $child_table = substr($child_table, strlen(self::TABLE_PREFIX));
  1690. }
  1691. //get the child data
  1692. $sql = "select * from ".self::TABLE_PREFIX.$child_table." ";
  1693. $sql .= "where ".$relation['field']." = :".$relation['field'];
  1694. $parameters[$relation['field']] = $row[$relation['field']];
  1695. $results = $this->execute($sql, $parameters, 'all');
  1696. unset($parameters);
  1697. if (is_array($results)) {
  1698. $array[$parent_name][$x][$child_table] = $results;
  1699. }
  1700. }
  1701. }
  1702. $x++;
  1703. }
  1704. }
  1705. }
  1706. //update the parent and child keys
  1707. $checked = false;
  1708. $x = 0;
  1709. foreach ($array as $parent_name => $tables) {
  1710. if (is_array($tables)) {
  1711. foreach ($tables as $id => $row) {
  1712. //prepare the variables
  1713. $parent_name = self::sanitize($parent_name);
  1714. $parent_key_name = self::singular($parent_name)."_uuid";
  1715. $parent_key_value = uuid();
  1716. //update the parent key id
  1717. $array[$parent_name][$x][$parent_key_name] = $parent_key_value;
  1718. //set enabled
  1719. if (array_key_exists(self::singular($parent_name).'_enabled', $array[$parent_name][$x])) {
  1720. $array[$parent_name][$x][self::singular($parent_name).'_enabled'] = $row[self::singular($parent_name).'_enabled'] === true || $row[self::singular($parent_name).'_enabled'] == 'true' ? 'true' : 'false';
  1721. }
  1722. else if (array_key_exists('enabled', $array[$parent_name][$x])) {
  1723. $array[$parent_name][$x]['enabled'] = $row['enabled'] === true || $row['enabled'] == 'true' ? 'true' : 'false';
  1724. }
  1725. //add copy to the description
  1726. if (array_key_exists(self::singular($parent_name).'_description', $array[$parent_name][$x])) {
  1727. $array[$parent_name][$x][self::singular($parent_name).'_description'] = trim($array[$parent_name][$x][self::singular($parent_name).'_description'].' '.$suffix);
  1728. }
  1729. else if (array_key_exists('description', $array[$parent_name][$x])) {
  1730. $array[$parent_name][$x]['description'] = trim($array[$parent_name][$x]['description'].' '.$suffix);
  1731. }
  1732. //loop through the fields
  1733. foreach($row as $field_name => $field_value) {
  1734. //find the child tables
  1735. $y = 0;
  1736. if (is_array($field_value)) {
  1737. //prepare the variables
  1738. $child_name = self::sanitize($field_name);
  1739. $child_key_name = self::singular($child_name)."_uuid";
  1740. //loop through the child rows
  1741. foreach ($field_value as $sub_row) {
  1742. //update the parent key id
  1743. $array[$parent_name][$x][$child_name][$y][$parent_key_name] = $parent_key_value;
  1744. //udpate the child key id
  1745. $array[$parent_name][$x][$child_name][$y][$child_key_name] = uuid();
  1746. //increment the value
  1747. $y++;
  1748. }
  1749. }
  1750. }
  1751. //increment the value
  1752. $x++;
  1753. }
  1754. }
  1755. }
  1756. //save the copy of the data
  1757. if (is_array($array) && count($array) > 0) {
  1758. $retval = $this->save($array);
  1759. unset($array);
  1760. }
  1761. return $retval;
  1762. } //end function copy
  1763. /**
  1764. * Toggles fields on a table using the <i>toggle_field</i> array values within the app object.
  1765. * @param array $array Three dimensional Array. The first dimension is the table name without the prefix 'v_'. Second dimension in the row value as int. Third dimension is the column name.
  1766. * @return bool Returns <b>true</b> on success and <b>false</b> on failure.
  1767. * @depends database::save()
  1768. * @depends database::get_apps()
  1769. */
  1770. public function toggle(array $array) {
  1771. //return the array
  1772. if (!is_array($array)) { return false; }
  1773. //set the message id
  1774. $m = 0;
  1775. //loop through the array
  1776. if (!empty($array) && is_array($array)) {
  1777. $x = 0;
  1778. foreach ($array as $parent_name => $tables) {
  1779. if (!empty($tables) && is_array($tables)) {
  1780. foreach ($tables as $id => $row) {
  1781. //prepare the variables
  1782. $parent_name = self::sanitize($parent_name);
  1783. $parent_key_name = self::singular($parent_name)."_uuid";
  1784. //build the toggle array
  1785. if (!empty($row['checked']) && $row['checked'] == 'true') {
  1786. //toggle the field value
  1787. //$toggle_array[$parent_name][$x][$parent_key_name] = $row[$parent_key_name];
  1788. $toggle_array[$parent_name][$x] = $row;
  1789. //remove the row from the main array
  1790. unset($array[$parent_name][$x]);
  1791. }
  1792. //loop through the fields
  1793. foreach($row as $field_name => $field_value) {
  1794. //find the child tables
  1795. $y = 0;
  1796. if (!empty($field_value) && is_array($field_value)) {
  1797. //prepare the variables
  1798. $child_name = self::sanitize($field_name);
  1799. $child_key_name = self::singular($child_name)."_uuid";
  1800. //loop through the child rows
  1801. foreach ($field_value as $sub_row) {
  1802. //build the delete array
  1803. if ($action == 'delete' && $sub_row['checked'] == 'true') {
  1804. //delete the child data
  1805. $delete_array[$child_name][$y][$child_key_name] = $sub_row[$child_key_name];
  1806. //remove the row from the main array
  1807. unset($array[$parent_name][$x][$child_name][$y]);
  1808. }
  1809. //increment the value
  1810. $y++;
  1811. }
  1812. }
  1813. }
  1814. //increment the value
  1815. $x++;
  1816. }
  1817. }
  1818. }
  1819. }
  1820. //unset the original array
  1821. unset($array);
  1822. //get the $apps array from the installed apps from the core and mod directories
  1823. if (count(self::$apps) == 0) {
  1824. self::get_apps();
  1825. }
  1826. //search through all fields to see if toggle field exists
  1827. foreach (self::$apps as $x => $app) {
  1828. if (!empty($app['db']) && is_array($app['db'])) {
  1829. foreach ($app['db'] as $y => $row) {
  1830. if (is_array($row['table']['name'])) {
  1831. $table_name = $row['table']['name']['text'];
  1832. }
  1833. else {
  1834. $table_name = $row['table']['name'];
  1835. }
  1836. if ($table_name === self::TABLE_PREFIX.$parent_name) {
  1837. if (is_array($row['fields'])) {
  1838. foreach ($row['fields'] as $field) {
  1839. if (isset($field['toggle'])) {
  1840. $toggle_field = $field['name'];
  1841. $toggle_values = $field['toggle'];
  1842. }
  1843. }
  1844. }
  1845. }
  1846. }
  1847. }
  1848. }
  1849. //if the toggle field and values are empty then set defaults
  1850. if (empty($toggle_field)) {
  1851. $toggle_field = self::singular($parent_name)."_enabled";
  1852. }
  1853. if (empty($toggle_values)) {
  1854. $toggle_values[] = 'true';
  1855. $toggle_values[] = 'false';
  1856. }
  1857. //get the current values from the database
  1858. foreach ($toggle_array as $table_name => $table) {
  1859. $x = 0;
  1860. foreach($table as $row) {
  1861. $child_name = self::sanitize($table_name);
  1862. $child_key_name = self::singular($child_name)."_uuid";
  1863. $array[$table_name][$x][$child_key_name] = $row[$child_key_name];
  1864. $array[$table_name][$x][$toggle_field] = ($row[$toggle_field] === $toggle_values[0]) ? $toggle_values[1] : $toggle_values[0];
  1865. $x++;
  1866. }
  1867. }
  1868. unset($toggle_array);
  1869. //save the array
  1870. return $this->save($array);
  1871. } //end function toggle
  1872. /**
  1873. * <p>Save an array to the database.</p>
  1874. * <p>Usage Example:<br><code>$database = new database();<br>$database->app_name = "MyApp";<br>$database->app_uuid = "12345678-1234-1234-1234-123456789abc";<br>$row = 0;<br>$array['mytable'][$row]['mycolumn'] = "myvalue";<br>if ($database->save($array)) { <br>&nbsp;&nbsp;echo "Saved Successfully.";<br> } else {<br>&nbsp;&nbsp;echo "Save Failed.";<br>}</code></p>
  1875. * @param array $array Three dimensional Array. The first dimension is the table name without the prefix 'v_'. Second dimension in the row value as int. Third dimension is the column name.
  1876. * @param bool $transaction_save
  1877. * @return returns and array wih result details
  1878. */
  1879. public function save(array &$array, bool $transaction_save = true) {
  1880. //prepare the values
  1881. $parent_field_names = [];
  1882. $child_field_names = [];
  1883. $this->message = [];
  1884. $parent_key_exists = false;
  1885. $parent_key_name = null;
  1886. $parent_key_value = null;
  1887. $child_key_exists = false;
  1888. $child_key_name = null;
  1889. $child_key_value = null;
  1890. $table_name = null;
  1891. $child_table_name = null;
  1892. //set default return value
  1893. $retval = true;
  1894. //return the array
  1895. if (!is_array($array)) { return false; }
  1896. //set the message id
  1897. $m = 0;
  1898. //build the json string from the array
  1899. $new_json = json_encode($array, JSON_PRETTY_PRINT);
  1900. //debug sql
  1901. //$this->debug["sql"] = true;
  1902. //connect to the database if needed
  1903. if (!$this->db) {
  1904. $this->connect();
  1905. }
  1906. //start the atomic transaction
  1907. $this->db->beginTransaction();
  1908. //loop through the array
  1909. if (is_array($array)) foreach ($array as $parent_name => $schema_array) {
  1910. if (is_array($schema_array)) foreach ($schema_array as $schema_id => $array) {
  1911. //set the variables
  1912. $table_name = self::TABLE_PREFIX.$parent_name;
  1913. $parent_key_name = self::singular($parent_name)."_uuid";
  1914. $parent_key_name = self::sanitize($parent_key_name);
  1915. //if the uuid is set then set parent key exists and value
  1916. //determine if the parent_key_exists
  1917. $parent_key_exists = false;
  1918. if (isset($array[$parent_key_name])) {
  1919. $parent_key_value = $array[$parent_key_name];
  1920. $parent_key_exists = true;
  1921. }
  1922. else {
  1923. if (isset($this->uuid)) {
  1924. $parent_key_exists = true;
  1925. $parent_key_value = $this->uuid;
  1926. }
  1927. else {
  1928. $parent_key_value = uuid();
  1929. }
  1930. }
  1931. //allow characters found in the uuid only.
  1932. $parent_key_value = self::sanitize($parent_key_value);
  1933. //get the parent field names
  1934. $parent_field_names = array();
  1935. if (is_array($array)) {
  1936. foreach ($array as $key => $value) {
  1937. if (!is_array($value)) {
  1938. $parent_field_names[] = self::sanitize($key);
  1939. }
  1940. }
  1941. }
  1942. //determine action update or delete and get the original data
  1943. if ($parent_key_exists) {
  1944. $sql = "SELECT ".implode(", ", $parent_field_names)." FROM ".$table_name." ";
  1945. $sql .= "WHERE ".$parent_key_name." = '".$parent_key_value."'; ";
  1946. $prep_statement = $this->db->prepare($sql);
  1947. if ($prep_statement) {
  1948. //get the data
  1949. try {
  1950. $prep_statement->execute();
  1951. $result = $prep_statement->fetchAll(PDO::FETCH_ASSOC);
  1952. }
  1953. catch(PDOException $e) {
  1954. echo $sql."<br />\n";
  1955. echo 'Caught exception: '. $e->getMessage()."<br /><br />\n";
  1956. echo $sql. "<br /><br />\n";
  1957. exit;
  1958. }
  1959. //set the action
  1960. if (count($result) > 0) {
  1961. $action = "update";
  1962. $old_array[$parent_name] = $result;
  1963. }
  1964. else {
  1965. $action = "add";
  1966. }
  1967. }
  1968. unset($prep_statement, $result);
  1969. }
  1970. else {
  1971. $action = "add";
  1972. }
  1973. //add a record
  1974. if ($action == "add") {
  1975. if (permission_exists(self::singular($parent_name).'_add')) {
  1976. $params = array();
  1977. $sql = "INSERT INTO ".$table_name." ";
  1978. $sql .= "(";
  1979. if (!$parent_key_exists) {
  1980. $sql .= $parent_key_name.", ";
  1981. }
  1982. if (is_array($array)) {
  1983. foreach ($array as $array_key => $array_value) {
  1984. if (!is_array($array_value)) {
  1985. $array_key = self::sanitize($array_key);
  1986. if ($array_key != 'insert_user' &&
  1987. $array_key != 'insert_date' &&
  1988. $array_key != 'update_user' &&
  1989. $array_key != 'update_date') {
  1990. $sql .= $array_key.", ";
  1991. }
  1992. }
  1993. }
  1994. }
  1995. $sql .= "insert_date, ";
  1996. $sql .= "insert_user ";
  1997. $sql .= ") ";
  1998. $sql .= "VALUES ";
  1999. $sql .= "(";
  2000. if (!$parent_key_exists) {
  2001. $sql .= "'".$parent_key_value."', ";
  2002. }
  2003. if (is_array($array)) {
  2004. foreach ($array as $array_key => $array_value) {
  2005. if (!is_array($array_value)) {
  2006. if ($array_key != 'insert_user' &&
  2007. $array_key != 'insert_date' &&
  2008. $array_key != 'update_user' &&
  2009. $array_key != 'update_date') {
  2010. if (!isset($array_value) || $array_value == '') {
  2011. $sql .= "null, ";
  2012. }
  2013. elseif ($array_value === "now()") {
  2014. $sql .= "now(), ";
  2015. }
  2016. elseif ($array_value === "user_uuid()") {
  2017. $sql .= ':'.$array_key.", ";
  2018. $params[$array_key] = $this->user_uuid ?? null;
  2019. }
  2020. elseif ($array_value === "remote_address()") {
  2021. $sql .= ':'.$array_key.", ";
  2022. $params[$array_key] = $_SERVER['REMOTE_ADDR'];
  2023. }
  2024. else {
  2025. $array_value = $array_value ?? '';
  2026. $sql .= ':'.$array_key.", ";
  2027. $params[$array_key] = trim($array_value);
  2028. }
  2029. }
  2030. }
  2031. }
  2032. }
  2033. $sql .= "now(), ";
  2034. $sql .= ":insert_user ";
  2035. $sql .= ");";
  2036. //add insert user parameter
  2037. $params['insert_user'] = $this->user_uuid ?? null;
  2038. //set the error mode
  2039. $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  2040. //reduce prepared statement latency
  2041. if (defined('PDO::PGSQL_ATTR_DISABLE_PREPARES')) {
  2042. $this->db->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true);
  2043. }
  2044. //run the query and return the results
  2045. try {
  2046. //$this->db->query(check_sql($sql));
  2047. $prep_statement = $this->db->prepare($sql);
  2048. $prep_statement->execute($params);
  2049. unset($prep_statement);
  2050. $message["message"] = "OK";
  2051. $message["code"] = "200";
  2052. $message["uuid"] = $parent_key_value;
  2053. $message["details"][$m]["name"] = $this->app_name;
  2054. $message["details"][$m]["message"] = "OK";
  2055. $message["details"][$m]["code"] = "200";
  2056. $message["details"][$m]["uuid"] = $parent_key_value;
  2057. $message["details"][$m]["sql"] = $sql;
  2058. if (is_array($params)) {
  2059. $message["details"][$m]["params"] = $params;
  2060. }
  2061. unset($params);
  2062. $this->message = $message;
  2063. $m++;
  2064. }
  2065. catch(PDOException $e) {
  2066. $retval = false;
  2067. $message["message"] = "Bad Request";
  2068. $message["code"] = "400";
  2069. $message["details"][$m]["name"] = $this->app_name;
  2070. $message["details"][$m]["message"] = $e->getMessage();
  2071. $message["details"][$m]["code"] = "400";
  2072. $message["details"][$m]["array"] = $array;
  2073. $message["details"][$m]["sql"] = $sql;
  2074. if (is_array($params)) {
  2075. $message["details"][$m]["params"] = $params;
  2076. }
  2077. unset($params);
  2078. $this->message = $message;
  2079. $m++;
  2080. }
  2081. unset($sql);
  2082. }
  2083. else {
  2084. $retval = false;
  2085. $message["name"] = $this->app_name;
  2086. $message["message"] = "Forbidden, does not have '".self::singular($parent_name)."_add'";
  2087. $message["code"] = "403";
  2088. $message["line"] = __line__;
  2089. $this->message[] = $message;
  2090. $m++;
  2091. }
  2092. }
  2093. //edit a specific uuid
  2094. if ($action == "update") {
  2095. if (permission_exists(self::singular($parent_name).'_edit')) {
  2096. //parent data
  2097. $params = array();
  2098. $sql = "UPDATE ".$table_name." SET ";
  2099. if (is_array($array)) {
  2100. foreach ($array as $array_key => $array_value) {
  2101. if (!is_array($array_value) && $array_key != $parent_key_name) {
  2102. $array_key = self::sanitize($array_key);
  2103. if (!isset($array_value) || (isset($array_value) && $array_value === '')) {
  2104. $sql .= $array_key." = null, ";
  2105. }
  2106. elseif ($array_value === "now()") {
  2107. $sql .= $array_key." = now(), ";
  2108. }
  2109. elseif ($array_value === "user_uuid()") {
  2110. $sql .= $array_key." = :".$array_key.", ";
  2111. $params[$array_key] = $this->user_uuid ?? null;
  2112. }
  2113. elseif ($array_value === "remote_address()") {
  2114. $sql .= $array_key." = :".$array_key.", ";
  2115. $params[$array_key] = $_SERVER['REMOTE_ADDR'];
  2116. }
  2117. else {
  2118. $sql .= $array_key." = :".$array_key.", ";
  2119. $params[$array_key] = trim($array_value);
  2120. }
  2121. }
  2122. }
  2123. }
  2124. //add the modified date and user
  2125. $sql .= "update_date = now(), ";
  2126. $sql .= "update_user = :update_user ";
  2127. $params['update_user'] = $this->user_uuid ?? null;
  2128. //add the where with the parent name and value
  2129. $sql .= "WHERE ".$parent_key_name." = '".$parent_key_value."'; ";
  2130. $sql = str_replace(", WHERE", " WHERE", $sql);
  2131. //add update user parameter
  2132. $params['update_user'] = $this->user_uuid ?? null;
  2133. //set the error mode
  2134. $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  2135. //reduce prepared statement latency
  2136. if (defined('PDO::PGSQL_ATTR_DISABLE_PREPARES')) {
  2137. $this->db->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true);
  2138. }
  2139. //run the query and return the results
  2140. try {
  2141. $prep_statement = $this->db->prepare($sql);
  2142. $prep_statement->execute($params);
  2143. //$this->db->query(check_sql($sql));
  2144. $message["message"] = "OK";
  2145. $message["code"] = "200";
  2146. $message["uuid"] = $parent_key_value;
  2147. $message["details"][$m]["name"] = $this->app_name;
  2148. $message["details"][$m]["message"] = "OK";
  2149. $message["details"][$m]["code"] = "200";
  2150. $message["details"][$m]["uuid"] = $parent_key_value;
  2151. $message["details"][$m]["sql"] = $sql;
  2152. if (is_array($params)) {
  2153. $message["details"][$m]["params"] = $params;
  2154. }
  2155. unset($params);
  2156. $this->message = $message;
  2157. $m++;
  2158. unset($sql);
  2159. }
  2160. catch(PDOException $e) {
  2161. $retval = false;
  2162. $message["message"] = "Bad Request";
  2163. $message["code"] = "400";
  2164. $message["details"][$m]["name"] = $this->app_name;
  2165. $message["details"][$m]["message"] = $e->getMessage();
  2166. $message["details"][$m]["code"] = "400";
  2167. $message["details"][$m]["sql"] = $sql;
  2168. if (is_array($params)) {
  2169. $message["details"][$m]["params"] = $params;
  2170. }
  2171. unset($params);
  2172. $this->message = $message;
  2173. $m++;
  2174. }
  2175. }
  2176. else {
  2177. $retval = false;
  2178. $message["message"] = "Forbidden, does not have '".self::singular($parent_name)."_edit'";
  2179. $message["code"] = "403";
  2180. $message["line"] = __line__;
  2181. $this->message = $message;
  2182. $m++;
  2183. }
  2184. }
  2185. //unset the variables
  2186. unset($sql, $action);
  2187. //child data
  2188. if (is_array($array)) {
  2189. foreach ($array as $key => $value) {
  2190. if (is_array($value)) {
  2191. $child_table_name = self::TABLE_PREFIX.$key;
  2192. $child_table_name = self::sanitize($child_table_name);
  2193. foreach ($value as $id => $row) {
  2194. //prepare the variables
  2195. $child_name = self::singular($key);
  2196. $child_name = self::sanitize($child_name);
  2197. $child_key_name = $child_name."_uuid";
  2198. //determine if the parent key exists in the child array
  2199. $parent_key_exists = false;
  2200. if (!isset($array[$parent_key_name])) {
  2201. $parent_key_exists = true;
  2202. }
  2203. //determine if the uuid exists
  2204. $uuid_exists = false;
  2205. if (is_array($row)) foreach ($row as $k => $v) {
  2206. if ($child_key_name == $k) {
  2207. if (strlen($v) > 0) {
  2208. $child_key_value = trim($v);
  2209. $uuid_exists = true;
  2210. break;
  2211. }
  2212. }
  2213. else {
  2214. $uuid_exists = false;
  2215. }
  2216. }
  2217. //allow characters found in the uuid only
  2218. if (isset($child_key_value)) {
  2219. $child_key_value = self::sanitize($child_key_value);
  2220. }
  2221. //get the child field names
  2222. $child_field_names = array();
  2223. if (is_array($row)) {
  2224. foreach ($row as $k => $v) {
  2225. if (!is_array($v) && $k !== 'checked') {
  2226. $child_field_names[] = self::sanitize($k);
  2227. }
  2228. }
  2229. }
  2230. //determine sql update or delete and get the original data
  2231. if ($uuid_exists) {
  2232. $sql = "SELECT ". implode(", ", $child_field_names)." FROM ".$child_table_name." ";
  2233. $sql .= "WHERE ".$child_key_name." = '".$child_key_value."'; ";
  2234. try {
  2235. $prep_statement = $this->db->prepare($sql);
  2236. if ($prep_statement) {
  2237. //get the data
  2238. $prep_statement->execute();
  2239. $child_array = $prep_statement->fetch(PDO::FETCH_ASSOC);
  2240. //set the action
  2241. if (is_array($child_array)) {
  2242. $action = "update";
  2243. }
  2244. else {
  2245. $action = "add";
  2246. }
  2247. //add to the parent array
  2248. if (is_array($child_array)) {
  2249. $old_array[$parent_name][$schema_id][$key][] = $child_array;
  2250. }
  2251. }
  2252. unset($prep_statement);
  2253. }
  2254. catch(PDOException $e) {
  2255. echo $sql."<br />\n";
  2256. echo 'Caught exception: '. $e->getMessage()."<br /><br />\n";
  2257. echo $sql. "<br /><br />\n";
  2258. exit;
  2259. }
  2260. }
  2261. else {
  2262. $action = "add";
  2263. }
  2264. //update the child data
  2265. if ($action == "update") {
  2266. if (permission_exists($child_name.'_edit')) {
  2267. $sql = "UPDATE ".$child_table_name." SET ";
  2268. if (is_array($row)) {
  2269. foreach ($row as $k => $v) {
  2270. if (!is_array($v) && ($k != $parent_key_name || $k != $child_key_name)) {
  2271. $k = self::sanitize($k);
  2272. if (!isset($v) || (isset($v) && $v == '')) {
  2273. $sql .= $k." = null, ";
  2274. }
  2275. elseif ($v === "now()") {
  2276. $sql .= $k." = now(), ";
  2277. }
  2278. elseif ($v === "user_uuid()") {
  2279. $sql .= $k." = :".$k.", ";
  2280. $params[$k] = $this->user_uuid ?? null;
  2281. }
  2282. elseif ($v === "remote_address()") {
  2283. $sql .= $k." = :".$k.", ";
  2284. $params[$k] = $_SERVER['REMOTE_ADDR'];
  2285. }
  2286. else {
  2287. $sql .= $k." = :".$k.", ";
  2288. $params[$k] = isset($v) ? trim($v) : null;
  2289. }
  2290. }
  2291. }
  2292. }
  2293. //add the modified date and user
  2294. $sql .= "update_date = now(), ";
  2295. $sql .= "update_user = :update_user ";
  2296. $params['update_user'] = $this->user_uuid ?? null;
  2297. //add the where with the parent name and value
  2298. $sql .= "WHERE ".$parent_key_name." = '".$parent_key_value."' ";
  2299. $sql .= "AND ".$child_key_name." = '".$child_key_value."'; ";
  2300. $sql = str_replace(", WHERE", " WHERE", $sql);
  2301. //set the error mode
  2302. $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  2303. //reduce prepared statement latency
  2304. if (defined('PDO::PGSQL_ATTR_DISABLE_PREPARES')) {
  2305. $this->db->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true);
  2306. }
  2307. //$prep_statement->bindParam(':domain_uuid', $this->domain_uuid );
  2308. try {
  2309. //$this->db->query(check_sql($sql));
  2310. $prep_statement = $this->db->prepare($sql);
  2311. $prep_statement->execute($params);
  2312. unset($prep_statement);
  2313. $message["details"][$m]["name"] = $key;
  2314. $message["details"][$m]["message"] = "OK";
  2315. $message["details"][$m]["code"] = "200";
  2316. $message["details"][$m]["uuid"] = $child_key_value;
  2317. $message["details"][$m]["sql"] = $sql;
  2318. if (is_array($params)) {
  2319. $message["details"][$m]["params"] = $params;
  2320. }
  2321. unset($params);
  2322. $this->message = $message;
  2323. $m++;
  2324. }
  2325. catch(PDOException $e) {
  2326. $retval = false;
  2327. if ($message["code"] == "200") {
  2328. $message["message"] = "Bad Request";
  2329. $message["code"] = "400";
  2330. }
  2331. $message["details"][$m]["name"] = $key;
  2332. $message["details"][$m]["message"] = $e->getMessage();
  2333. $message["details"][$m]["code"] = "400";
  2334. $message["details"][$m]["sql"] = $sql;
  2335. if (is_array($params)) {
  2336. $message["details"][$m]["params"] = $params;
  2337. }
  2338. unset($params);
  2339. $this->message = $message;
  2340. $m++;
  2341. }
  2342. }
  2343. else {
  2344. $retval = false;
  2345. $message["name"] = $child_name;
  2346. $message["message"] = "Forbidden, does not have '".$child_name."_edit'";
  2347. $message["code"] = "403";
  2348. $message["line"] = __line__;
  2349. $this->message = $message;
  2350. $m++;
  2351. }
  2352. } //action update
  2353. //add the child data
  2354. if ($action == "add") {
  2355. if (permission_exists($child_name.'_add')) {
  2356. //determine if child or parent key exists
  2357. $child_key_name = $child_name.'_uuid';
  2358. $parent_key_exists = false;
  2359. $child_key_exists = false;
  2360. if (is_array($row)) {
  2361. foreach ($row as $k => $v) {
  2362. if ($k == $parent_key_name) {
  2363. $parent_key_exists = true;
  2364. }
  2365. if ($k == $child_key_name) {
  2366. $child_key_exists = true;
  2367. $child_key_value = trim($v);
  2368. }
  2369. }
  2370. }
  2371. if (!isset($child_key_value) || $child_key_value == '') {
  2372. $child_key_value = uuid();
  2373. }
  2374. //build the insert
  2375. $sql = "INSERT INTO ".$child_table_name." ";
  2376. $sql .= "(";
  2377. if (!$parent_key_exists) {
  2378. $sql .= self::singular($parent_key_name).", ";
  2379. }
  2380. if (!$child_key_exists) {
  2381. $sql .= self::singular($child_key_name).", ";
  2382. }
  2383. if (is_array($row)) {
  2384. foreach ($row as $k => $v) {
  2385. if (!is_array($v)) {
  2386. $k = self::sanitize($k);
  2387. if ($k != 'insert_user' &&
  2388. $k != 'insert_date' &&
  2389. $k != 'update_user' &&
  2390. $k != 'update_date') {
  2391. $sql .= $k.", ";
  2392. }
  2393. }
  2394. }
  2395. }
  2396. $sql .= "insert_date, ";
  2397. $sql .= "insert_user ";
  2398. $sql .= ") ";
  2399. $sql .= "VALUES ";
  2400. $sql .= "(";
  2401. if (!$parent_key_exists) {
  2402. $sql .= "'".$parent_key_value."', ";
  2403. }
  2404. if (!$child_key_exists) {
  2405. $sql .= "'".$child_key_value."', ";
  2406. }
  2407. if (is_array($row)) {
  2408. foreach ($row as $k => $v) {
  2409. if (!is_array($v)) {
  2410. if ($k != 'insert_user' &&
  2411. $k != 'insert_date' &&
  2412. $k != 'update_user' &&
  2413. $k != 'update_date') {
  2414. if (!isset($v) || strlen($v) == 0) {
  2415. $sql .= "null, ";
  2416. }
  2417. elseif ($v === "now()") {
  2418. $sql .= "now(), ";
  2419. }
  2420. elseif ($v === "user_uuid()") {
  2421. $sql .= ':'.$k.", ";
  2422. $params[$k] = $this->user_uuid ?? null;
  2423. }
  2424. elseif ($v === "remote_address()") {
  2425. $sql .= ':'.$k.", ";
  2426. $params[$k] = $_SERVER['REMOTE_ADDR'];
  2427. }
  2428. else {
  2429. $k = self::sanitize($k);
  2430. if ($k != 'insert_user' &&
  2431. $k != 'insert_date' &&
  2432. $k != 'update_user' &&
  2433. $k != 'update_date') {
  2434. $sql .= ':'.$k.", ";
  2435. $params[$k] = trim($v);
  2436. }
  2437. }
  2438. }
  2439. }
  2440. }
  2441. }
  2442. $sql .= "now(), ";
  2443. $sql .= ":insert_user ";
  2444. $sql .= ");";
  2445. //add insert user parameter
  2446. $params['insert_user'] = $this->user_uuid ?? null;
  2447. //set the error mode
  2448. $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  2449. //reduce prepared statement latency
  2450. if (defined('PDO::PGSQL_ATTR_DISABLE_PREPARES')) {
  2451. $this->db->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true);
  2452. }
  2453. //run the query and return the results
  2454. try {
  2455. $prep_statement = $this->db->prepare($sql);
  2456. $prep_statement->execute($params);
  2457. unset($prep_statement);
  2458. $message["details"][$m]["name"] = $key;
  2459. $message["details"][$m]["message"] = "OK";
  2460. $message["details"][$m]["code"] = "200";
  2461. $message["details"][$m]["uuid"] = $child_key_value;
  2462. $message["details"][$m]["sql"] = $sql;
  2463. if (is_array($params)) {
  2464. $message["details"][$m]["params"] = $params;
  2465. }
  2466. unset($params);
  2467. $this->message = $message;
  2468. $m++;
  2469. }
  2470. catch(PDOException $e) {
  2471. $retval = false;
  2472. if ($message["code"] == "200") {
  2473. $message["message"] = "Bad Request";
  2474. $message["code"] = "400";
  2475. }
  2476. $message["details"][$m]["name"] = $key;
  2477. $message["details"][$m]["message"] = $e->getMessage();
  2478. $message["details"][$m]["code"] = "400";
  2479. $message["details"][$m]["sql"] = $sql;
  2480. if (is_array($params)) {
  2481. $message["details"][$m]["params"] = $params;
  2482. }
  2483. unset($params);
  2484. $this->message = $message;
  2485. $m++;
  2486. }
  2487. }
  2488. else {
  2489. $retval = false;
  2490. $message["name"] = $child_name;
  2491. $message["message"] = "Forbidden, does not have '".$child_name."_add'";
  2492. $message["code"] = "403";
  2493. $message["line"] = __line__;
  2494. $this->message = $message;
  2495. $m++;
  2496. }
  2497. } //action add
  2498. //unset the variables
  2499. unset($sql, $action, $child_key_name, $child_key_value);
  2500. } // foreach value
  2501. } //is array
  2502. } //foreach array
  2503. }
  2504. } // foreach schema_array
  2505. } // foreach main array
  2506. //save the message
  2507. $this->message = $message;
  2508. //commit the atomic transaction
  2509. $this->db->commit();
  2510. //set the action if not set
  2511. if (empty($action)) {
  2512. if (!empty($old_array)) {
  2513. $transaction_type = 'update';
  2514. }
  2515. else {
  2516. $transaction_type = 'add';
  2517. }
  2518. }
  2519. else {
  2520. $transaction_type = $action;
  2521. }
  2522. //log the transaction results
  2523. if ($transaction_save && file_exists($_SERVER["PROJECT_ROOT"]."/app/database_transactions/app_config.php")) {
  2524. try {
  2525. //get the domain_uuid
  2526. $domain_uuid = '';
  2527. foreach ($old_array as $data_array) {
  2528. foreach ($data_array as $row) {
  2529. if (!empty($row['domain_uuid'])) {
  2530. $domain_uuid = $row['domain_uuid'];
  2531. }
  2532. }
  2533. }
  2534. //insert the transaction into the database
  2535. $sql = "insert into ".self::TABLE_PREFIX."database_transactions ";
  2536. $sql .= "(";
  2537. $sql .= "database_transaction_uuid, ";
  2538. if (isset($domain_uuid) && is_uuid($domain_uuid)) {
  2539. $sql .= "domain_uuid, ";
  2540. }
  2541. if (isset($this->user_uuid) && is_uuid($this->user_uuid)) {
  2542. $sql .= "user_uuid, ";
  2543. }
  2544. if (isset($this->app_uuid) && is_uuid($this->app_uuid)) {
  2545. $sql .= "app_uuid, ";
  2546. }
  2547. if (isset($this->app_name) && !empty($this->app_name)) {
  2548. $sql .= "app_name, ";
  2549. }
  2550. $sql .= "transaction_code, ";
  2551. $sql .= "transaction_address, ";
  2552. $sql .= "transaction_type, ";
  2553. $sql .= "transaction_date, ";
  2554. $sql .= "transaction_old, ";
  2555. $sql .= "transaction_new, ";
  2556. $sql .= "transaction_result ";
  2557. $sql .= ")";
  2558. $sql .= "values ";
  2559. $sql .= "(";
  2560. $sql .= "'".uuid()."', ";
  2561. if (isset($domain_uuid) && is_uuid($domain_uuid)) {
  2562. $sql .= ":domain_uuid, ";
  2563. }
  2564. if (isset($this->user_uuid) && is_uuid($this->user_uuid)) {
  2565. $sql .= ":user_uuid, ";
  2566. }
  2567. if (isset($this->app_uuid) && is_uuid($this->app_uuid)) {
  2568. $sql .= ":app_uuid, ";
  2569. }
  2570. if (isset($this->app_name) && !empty($this->app_name)) {
  2571. $sql .= ":app_name, ";
  2572. }
  2573. $sql .= "'".$message["code"]."', ";
  2574. $sql .= ":remote_address, ";
  2575. $sql .= "'".$transaction_type."', ";
  2576. $sql .= "now(), ";
  2577. if (!empty($old_array)) {
  2578. $sql .= ":transaction_old, ";
  2579. }
  2580. else {
  2581. $sql .= "null, ";
  2582. }
  2583. if (!empty($array)) {
  2584. $sql .= ":transaction_new, ";
  2585. }
  2586. else {
  2587. $sql .= "null, ";
  2588. }
  2589. $sql .= ":transaction_result ";
  2590. $sql .= ")";
  2591. $statement = $this->db->prepare($sql);
  2592. if (isset($domain_uuid) && is_uuid($domain_uuid)) {
  2593. $statement->bindParam(':domain_uuid', $domain_uuid);
  2594. }
  2595. if (isset($this->user_uuid) && is_uuid($this->user_uuid)) {
  2596. $statement->bindParam(':user_uuid', $this->user_uuid);
  2597. }
  2598. if (isset($this->app_uuid) && is_uuid($this->app_uuid)) {
  2599. $statement->bindParam(':app_uuid', $this->app_uuid);
  2600. }
  2601. if (isset($this->app_name) && !empty($this->app_name)) {
  2602. $statement->bindParam(':app_name', $this->app_name);
  2603. }
  2604. $statement->bindParam(':remote_address', $_SERVER['REMOTE_ADDR']);
  2605. if (!empty($old_array)) {
  2606. $old_json = json_encode($old_array, JSON_PRETTY_PRINT);
  2607. $statement->bindParam(':transaction_old', $old_json);
  2608. }
  2609. if (!empty($new_json)) {
  2610. $statement->bindParam(':transaction_new', $new_json);
  2611. }
  2612. $message = json_encode($this->message, JSON_PRETTY_PRINT);
  2613. $statement->bindParam(':transaction_result', $message);
  2614. $statement->execute();
  2615. unset($sql);
  2616. }
  2617. catch(PDOException $e) {
  2618. echo $e->getMessage();
  2619. exit;
  2620. }
  2621. }
  2622. return $this->message;
  2623. } //save method
  2624. /**
  2625. * Converts a plural English word to singular.
  2626. * @param string $word English word
  2627. * @return string Singular version of English word
  2628. * @internal Moved to class to conserve resources.
  2629. */
  2630. public static function singular(string $word) {
  2631. //"-es" is used for words that end in "-x", "-s", "-z", "-sh", "-ch" in which case you add
  2632. if (substr($word, -2) == "es") {
  2633. if (substr($word, -4) == "sses") { // eg. 'addresses' to 'address'
  2634. return substr($word,0,-2);
  2635. }
  2636. elseif (substr($word, -3) == "ses") { // eg. 'databases' to 'database' (necessary!)
  2637. return substr($word,0,-1);
  2638. }
  2639. elseif (substr($word, -3) == "ies") { // eg. 'countries' to 'country'
  2640. return substr($word,0,-3)."y";
  2641. }
  2642. elseif (substr($word, -3, 1) == "x") {
  2643. return substr($word,0,-2);
  2644. }
  2645. elseif (substr($word, -3, 1) == "s") {
  2646. return substr($word,0,-2);
  2647. }
  2648. elseif (substr($word, -3, 1) == "z") {
  2649. return substr($word,0,-2);
  2650. }
  2651. elseif (substr($word, -4, 2) == "sh") {
  2652. return substr($word,0,-2);
  2653. }
  2654. elseif (substr($word, -4, 2) == "ch") {
  2655. return substr($word,0,-2);
  2656. }
  2657. else {
  2658. return rtrim($word, "s");
  2659. }
  2660. }
  2661. else {
  2662. return rtrim($word, "s");
  2663. }
  2664. }
  2665. /**
  2666. * Gets the $apps array from the installed apps from the core and mod directories and writes it to self::$apps overwriting previous values.
  2667. * @uses $_SERVER['DOCUMENT_ROOT'] Global variable
  2668. * @uses PROJECT_PATH Global variable
  2669. * @return null Does not return any values
  2670. * @internal Moved to class to conserve resources.
  2671. */
  2672. public static function get_apps() {
  2673. //get the $apps array from the installed apps from the core and mod directories
  2674. $config_list = glob($_SERVER["DOCUMENT_ROOT"] . PROJECT_PATH . "/*/*/app_config.php");
  2675. $x = 0;
  2676. if (is_array($config_list)) {
  2677. foreach ($config_list as $config_path) {
  2678. include($config_path);
  2679. $x++;
  2680. }
  2681. }
  2682. self::$apps = $apps;
  2683. }
  2684. /**
  2685. * Returns the depth of an array
  2686. * @param array $array Reference to array
  2687. * @return int Depth of array
  2688. * @internal Moved to class to conserve resources.
  2689. */
  2690. public static function array_depth(array &$array) {
  2691. $depth = 0;
  2692. if (is_array($array)) {
  2693. $depth++;
  2694. foreach ($array as $value) {
  2695. if (is_array($value)) {
  2696. $depth = self::array_depth($value) + 1;
  2697. }
  2698. }
  2699. }
  2700. return $depth;
  2701. }
  2702. /**
  2703. * Searches through all fields to see if domain_uuid exists
  2704. * @param string $name
  2705. * @uses self::$apps directly
  2706. * @return boolean <b>true</b> on success and <b>false</b> on failure
  2707. * @see database::get_apps()
  2708. */
  2709. public static function domain_uuid_exists($name) {
  2710. //get the $apps array from the installed apps from the core and mod directories
  2711. if (count(self::$apps) == 0) {
  2712. self::get_apps();
  2713. }
  2714. //search through all fields to see if domain_uuid exists
  2715. foreach (self::$apps as $x => &$app) {
  2716. if (is_array($app['db'])) {
  2717. foreach ($app['db'] as $y => $row) {
  2718. if (is_array($row['table']['name'])) {
  2719. $table_name = $row['table']['name']['text'];
  2720. }
  2721. else {
  2722. $table_name = $row['table']['name'];
  2723. }
  2724. if ($table_name === self::TABLE_PREFIX.$name) {
  2725. if (is_array($row['fields'])) {
  2726. foreach ($row['fields'] as $field) {
  2727. if ($field['name'] == "domain_uuid") {
  2728. return true;
  2729. }
  2730. } //foreach
  2731. } //is array
  2732. }
  2733. } //foreach
  2734. } //is array
  2735. } //foreach
  2736. //not found
  2737. return false;
  2738. }
  2739. /**
  2740. * Get Relations searches through all fields to find relations
  2741. * @param string $schema Table name
  2742. * @return array Returns array or false
  2743. * @internal Moved to class to conserve resources.
  2744. */
  2745. public static function get_relations($schema) {
  2746. //remove the v_ prefix
  2747. if (substr($schema, 0, strlen(self::TABLE_PREFIX)) == self::TABLE_PREFIX) {
  2748. $schema = substr($schema, strlen(self::TABLE_PREFIX));
  2749. }
  2750. //sanitize the values
  2751. $schema = self::sanitize($schema);
  2752. //get the apps array
  2753. $config_list = [];
  2754. $directories = ["core", "app"];
  2755. $applications = [$schema, self::singular($schema)];
  2756. foreach ($directories as $directory) {
  2757. foreach ($applications as $application) {
  2758. $path = $_SERVER["DOCUMENT_ROOT"] . PROJECT_PATH . "/$directory/$application/app_config.php";
  2759. $app_config_files = glob($path);
  2760. if ($app_config_files !== false) {
  2761. $config_list = array_merge($config_list, $app_config_files);
  2762. }
  2763. }
  2764. }
  2765. $x = 0;
  2766. foreach ($config_list as $config_path) {
  2767. include($config_path);
  2768. $x++;
  2769. }
  2770. //search through all fields to find relations
  2771. if (!empty($apps) && is_array($apps)) {
  2772. foreach ($apps as $x => $app) {
  2773. foreach ($app['db'] as $y => $row) {
  2774. foreach ($row['fields'] as $z => $field) {
  2775. if (!empty($field['deprecated']) && $field['deprecated'] != "true") {
  2776. if (!empty($field['key']['type']) && $field['key']['type'] == "foreign") {
  2777. if ($row['table']['name'] == self::TABLE_PREFIX.$schema || $field['key']['reference']['table'] == self::TABLE_PREFIX.$schema) {
  2778. //get the field name
  2779. if (!empty($field['name']) && is_array($field['name'])) {
  2780. $field_name = trim($field['name']['text']);
  2781. }
  2782. else {
  2783. $field_name = trim($field['name']);
  2784. }
  2785. //build the array
  2786. $relations[$i]['table'] = $row['table']['name'];
  2787. $relations[$i]['field'] = $field_name;
  2788. $relations[$i]['key']['type'] = $field['key']['type'];
  2789. $relations[$i]['key']['table'] = $field['key']['reference']['table'];
  2790. $relations[$i]['key']['field'] = $field['key']['reference']['field'];
  2791. if (isset($field['key']['reference']['action'])) {
  2792. $relations[$i]['key']['action'] = $field['key']['reference']['action'];
  2793. }
  2794. //increment the value
  2795. $i++;
  2796. }
  2797. }
  2798. }
  2799. unset($field_name);
  2800. }
  2801. }
  2802. }
  2803. }
  2804. //return the array
  2805. if (!empty($relations) && is_array($relations)) {
  2806. return $relations;
  2807. } else {
  2808. return false;
  2809. }
  2810. }
  2811. /**
  2812. * Returns a sanitized string value safe for database or table name.
  2813. * @param string $value To be sanitized
  2814. * @return string Sanitized using preg_replace('#[^a-zA-Z0-9_\-]#', '')
  2815. * @see preg_replace()
  2816. */
  2817. public static function sanitize(string $value) {
  2818. return preg_replace('#[^a-zA-Z0-9_\-]#', '', $value);
  2819. }
  2820. /**
  2821. * Returns a new connected database object.<br>
  2822. * <p>This allows a shortcut for a common syntax. For more information
  2823. * on how the connection happens see {@link database::__construct()} and
  2824. * {@link database::connect()}</p>
  2825. * <p><b>Usage:</b><br>
  2826. * <code>&nbsp; $database_object = database::new();</code></p>
  2827. * @return database new instance of database object already connected
  2828. * @see database::__construct()
  2829. * @see database::connect()
  2830. */
  2831. public static function new(array $params = []) {
  2832. if (self::$database === null) {
  2833. self::$database = new database($params);
  2834. self::$database->connect();
  2835. }
  2836. return self::$database;
  2837. }
  2838. } //class database
  2839. } //!class_exists
  2840. //addtitional functions for sqlite
  2841. if (!function_exists('php_md5')) {
  2842. function php_md5($string) {
  2843. return md5($string);
  2844. }
  2845. }
  2846. if (!function_exists('php_unix_time_stamp')) {
  2847. function php_unix_time_stamp($string) {
  2848. return strtotime($string);
  2849. }
  2850. }
  2851. if (!function_exists('php_now')) {
  2852. function php_now() {
  2853. return date("Y-m-d H:i:s");
  2854. }
  2855. }
  2856. if (!function_exists('php_left')) {
  2857. function php_left($string, $num) {
  2858. return substr($string, 0, $num);
  2859. }
  2860. }
  2861. if (!function_exists('php_right')) {
  2862. function php_right($string, $num) {
  2863. return substr($string, (strlen($string)-$num), strlen($string));
  2864. }
  2865. }
  2866. /*
  2867. //example usage
  2868. //find
  2869. require_once "resources/classes/database.php";
  2870. $database = new database;
  2871. $database->domain_uuid = $_SESSION["domain_uuid"];
  2872. $database->type = $db_type;
  2873. $database->table = "v_extensions";
  2874. $where[0]['name'] = 'domain_uuid';
  2875. $where[0]['value'] = $_SESSION["domain_uuid"];
  2876. $where[0]['operator'] = '=';
  2877. $database->where = $where;
  2878. $order_by[0]['name'] = 'extension';
  2879. $database->order_by = $order_by;
  2880. $database->order_type = 'desc';
  2881. $database->limit = '2';
  2882. $database->offset = '0';
  2883. $database->find();
  2884. print_r($database->result);
  2885. //insert
  2886. require_once "resources/classes/database.php";
  2887. $database = new database;
  2888. $database->domain_uuid = $_SESSION["domain_uuid"];
  2889. $database->table = "v_ivr_menus";
  2890. $fields[0]['name'] = 'domain_uuid';
  2891. $fields[0]['value'] = $_SESSION["domain_uuid"];
  2892. echo $database->count();
  2893. */
  2894. ?>